blob: 2d62b25a1691418b9d6e570cc136ddb91b678271 [file] [log] [blame]
#!/bin/env python
COPYRIGHT = """\
/*
* Copyright 2021 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sub license, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
"""
import os
import re
import ply.lex as lex
import ply.yacc as yacc
# Libraries
libraries = {}
# LEXER
keywords = {
'__debugbreak': 'KW_DEBUGBREAK',
'alignas': 'KW_ALIGNAS',
'args': 'KW_ARGS',
'atomic': 'KW_ATOMIC',
'atomic_return': 'KW_ATOMIC_RETURN',
'const': 'KW_CONST',
'control': 'KW_CONTROL',
'define': 'KW_DEFINE',
'dispatch': 'KW_DISPATCH',
'dispatch_indirect': 'KW_DISPATCH_INDIRECT',
'goto': 'KW_GOTO',
'if': 'KW_IF',
'kernel': 'KW_KERNEL',
'kernel_module': 'KW_KERNEL_MODULE',
'import': 'KW_IMPORT',
'library': 'KW_LIBRARY',
'links': 'KW_LINKS',
'load_dword': 'KW_LOAD_DWORD',
'load_qword': 'KW_LOAD_QWORD',
'metakernel': 'KW_METAKERNEL',
'module': 'KW_MODULE',
'not': 'KW_NOT',
'offsetof': 'KW_OFFSETOF',
'postsync': 'KW_POSTSYNC',
'print': 'KW_PRINT',
'semaphore_wait': 'KW_SEMAPHORE_WAIT',
'shiftof': 'KW_SHIFTOF',
'sizeof': 'KW_SIZEOF',
'store_dword': 'KW_STORE_DWORD',
'store_qword': 'KW_STORE_QWORD',
'store_timestamp': 'KW_STORE_TIMESTAMP',
'struct': 'KW_STRUCT',
'unsigned': 'KW_UNSIGNED',
'while': 'KW_WHILE'
}
ops = {
'&&': 'OP_LOGICAL_AND',
'||': 'OP_LOGICAL_OR',
'==': 'OP_EQUALEQUAL',
'!=': 'OP_NOTEQUAL',
'<=': 'OP_LESSEQUAL',
'>=': 'OP_GREATEREQUAL',
'<<': 'OP_LSHIFT',
'>>': 'OP_RSHIFT'
}
tokens = [
'INT_LITERAL',
'STRING_LITERAL',
'OP',
'IDENTIFIER'
] + list(keywords.values()) + list(ops.values())
def t_INT_LITERAL(t):
r'(0x[a-fA-F0-9]+|\d+)'
if t.value.startswith('0x'):
t.value = int(t.value[2:], 16)
else:
t.value = int(t.value)
return t
def t_OP(t):
r'(&&|\|\||==|!=|<=|>=|<<|>>)'
t.type = ops.get(t.value)
return t
def t_IDENTIFIER(t):
r'[a-zA-Z_][a-zA-Z_0-9]*'
t.type = keywords.get(t.value, 'IDENTIFIER')
return t
def t_STRING_LITERAL(t):
r'"(\\.|[^"\\])*"'
t.value = t.value[1:-1]
return t
literals = "+*/(){};:,=&|!~^.%?-<>[]"
t_ignore = ' \t'
def t_newline(t):
r'\n+'
t.lexer.lineno += len(t.value)
def t_error(t):
print("WUT: {}".format(t.value))
t.lexer.skip(1)
LEXER = lex.lex()
# PARSER
precedence = (
('right', '?', ':'),
('left', 'OP_LOGICAL_OR', 'OP_LOGICAL_AND'),
('left', '|'),
('left', '^'),
('left', '&'),
('left', 'OP_EQUALEQUAL', 'OP_NOTEQUAL'),
('left', '<', '>', 'OP_LESSEQUAL', 'OP_GREATEREQUAL'),
('left', 'OP_LSHIFT', 'OP_RSHIFT'),
('left', '+', '-'),
('left', '*', '/', '%'),
('right', '!', '~'),
('left', '[', ']', '.')
)
def p_module(p):
'module : element_list'
p[0] = p[1]
def p_element_list(p):
'''element_list : element_list element
| element'''
if len(p) == 2:
p[0] = [p[1]]
else:
p[0] = p[1] + [p[2]]
def p_element(p):
'''element : kernel_definition
| kernel_module_definition
| library_definition
| metakernel_definition
| module_name
| struct_definition
| const_definition
| import_definition'''
p[0] = p[1]
def p_module_name(p):
'module_name : KW_MODULE IDENTIFIER ";"'
p[0] = ('module-name', p[2])
def p_kernel_module_definition(p):
'kernel_module_definition : KW_KERNEL_MODULE IDENTIFIER "(" STRING_LITERAL ")" "{" kernel_definition_list "}"'
p[0] = ('kernel-module', p[2], p[4], p[7])
def p_kernel_definition(p):
'kernel_definition : KW_KERNEL IDENTIFIER optional_annotation_list'
p[0] = ('kernel', p[2], p[3])
def p_library_definition(p):
'library_definition : KW_LIBRARY IDENTIFIER "{" library_definition_list "}"'
p[0] = ('library', p[2], p[4])
def p_library_definition_list(p):
'''library_definition_list :
| library_definition_list IDENTIFIER STRING_LITERAL ";"'''
if len(p) < 3:
p[0] = []
else:
p[0] = p[1]
p[0].append((p[2], p[3]))
def p_import_definition(p):
'import_definition : KW_IMPORT KW_STRUCT IDENTIFIER STRING_LITERAL ";"'
p[0] = ('import', p[4], 'struct', p[3])
def p_links_definition(p):
'links_definition : KW_LINKS IDENTIFIER'
# Process a library include like a preprocessor
global libraries
if not p[2] in libraries:
raise "Not able to find library {0}".format(p[2])
p[0] = libraries[p[2]]
def p_metakernel_definition(p):
'metakernel_definition : KW_METAKERNEL IDENTIFIER "(" optional_parameter_list ")" optional_annotation_list scope'
p[0] = ('meta-kernel', p[2], p[4], p[6], p[7])
def p_kernel_definition_list(p):
'''kernel_definition_list :
| kernel_definition_list kernel_definition ";"
| kernel_definition_list links_definition ";"'''
if len(p) < 3:
p[0] = []
else:
p[0] = p[1]
p[0].append(p[2])
def p_optional_annotation_list(p):
'''optional_annotation_list :
| "<" ">"
| "<" annotation_list ">"'''
if len(p) < 4:
p[0] = {}
else:
p[0] = p[2]
def p_optional_parameter_list(p):
'''optional_parameter_list :
| parameter_list'''
p[0] = p[1]
def p_annotation_list(p):
'''annotation_list : annotation'''
p[0] = p[1]
def p_annotation_list_append(p):
'''annotation_list : annotation_list "," annotation'''
p[0] = {**p[1], **p[3]}
def p_annotation(p):
'''annotation : IDENTIFIER "=" INT_LITERAL
| IDENTIFIER "=" IDENTIFIER
| IDENTIFIER "=" STRING_LITERAL'''
p[0] = {p[1]: p[3]}
def p_parameter_list(p):
'''parameter_list : parameter_definition'''
p[0] = [p[1]]
def p_parameter_list_append(p):
'''parameter_list : parameter_list "," parameter_definition'''
p[0] = p[1]
p[0].append(p[3])
def p_parameter_definition(p):
'parameter_definition : IDENTIFIER IDENTIFIER'
p[0] = (p[1], p[2])
def p_scope(p):
'''scope : "{" optional_statement_list "}"'''
p[0] = p[2]
def p_optional_statement_list(p):
'''optional_statement_list :
| statement_list'''
p[0] = p[1]
def p_statement_list(p):
'''statement_list : statement'''
p[0] = [p[1]]
def p_statement_list_append(p):
'''statement_list : statement_list statement'''
p[0] = p[1]
p[0].append(p[2])
def p_statement(p):
'''statement : definition_statement ";"
| assignment_statement ";"
| load_store_statement ";"
| dispatch_statement ";"
| semaphore_statement ";"
| label
| goto_statement ";"
| scope_statement
| atomic_op_statement ";"
| control_statement ";"
| print_statement ";"
| debug_break_statement ";"'''
p[0] = p[1]
def p_definition_statement(p):
'definition_statement : KW_DEFINE IDENTIFIER value'
p[0] = ('define', p[2], p[3])
def p_assignemt_statement(p):
'assignment_statement : value "=" value'
p[0] = ('assign', p[1], p[3])
def p_load_store_statement_load_dword(p):
'''load_store_statement : value "=" KW_LOAD_DWORD "(" value ")"'''
p[0] = ('load-dword', p[1], p[5])
def p_load_store_statement_load_qword(p):
'''load_store_statement : value "=" KW_LOAD_QWORD "(" value ")"'''
p[0] = ('load-qword', p[1], p[5])
def p_load_store_statement_store_dword(p):
'''load_store_statement : KW_STORE_DWORD "(" value "," value ")"'''
p[0] = ('store-dword', p[3], p[5])
def p_load_store_statement_store_qword(p):
'''load_store_statement : KW_STORE_QWORD "(" value "," value ")"'''
p[0] = ('store-qword', p[3], p[5])
def p_dispatch_statement(p):
'''dispatch_statement : direct_dispatch_statement
| indirect_dispatch_statement'''
p[0] = p[1]
def p_direct_dispatch_statement(p):
'''direct_dispatch_statement : KW_DISPATCH IDENTIFIER "(" value "," value "," value ")" optional_kernel_arg_list optional_postsync'''
p[0] = ('dispatch', p[2], (p[4], p[6], p[8]), p[10], p[11])
def p_indirect_dispatch_statement(p):
'''indirect_dispatch_statement : KW_DISPATCH_INDIRECT IDENTIFIER optional_kernel_arg_list optional_postsync'''
p[0] = ('dispatch', p[2], None, p[3], p[4])
def p_optional_kernel_arg_list(p):
'''optional_kernel_arg_list :
| KW_ARGS "(" value_list ")"'''
p[0] = p[3]
def p_value_list(p):
'''value_list : value'''
p[0] = [p[1]]
def p_value_list_append(p):
'''value_list : value_list "," value'''
p[0] = p[1]
p[0].append(p[3])
def p_optional_postsync(p):
'''optional_postsync :
| postsync_operation'''
if len(p) > 1:
p[0] = p[1]
def p_postsync_operation(p):
'''postsync_operation : postsync_write_dword
| postsync_write_timestamp'''
p[0] = p[1]
def p_postsync_write_dword(p):
'''postsync_write_dword : KW_POSTSYNC KW_STORE_DWORD "(" value "," value ")"'''
p[0] = ('postsync', 'store-dword', p[4], p[6])
def p_postsync_write_timestamp(p):
'''postsync_write_timestamp : KW_POSTSYNC KW_STORE_TIMESTAMP "(" value ")"'''
p[0] = ('postsync', 'timestamp', p[4])
def p_semaphore_statement(p):
'''semaphore_statement : KW_SEMAPHORE_WAIT KW_WHILE "(" "*" value "<" value ")"
| KW_SEMAPHORE_WAIT KW_WHILE "(" "*" value ">" value ")"
| KW_SEMAPHORE_WAIT KW_WHILE "(" "*" value OP_LESSEQUAL value ")"
| KW_SEMAPHORE_WAIT KW_WHILE "(" "*" value OP_GREATEREQUAL value ")"
| KW_SEMAPHORE_WAIT KW_WHILE "(" "*" value OP_EQUALEQUAL value ")"
| KW_SEMAPHORE_WAIT KW_WHILE "(" "*" value OP_NOTEQUAL value ")"'''
p[0] = ('sem-wait-while', p[5], p[6], p[7])
def p_atomic_op_statement(p):
'''atomic_op_statement : KW_ATOMIC IDENTIFIER IDENTIFIER "(" value_list ")"'''
p[0] = ('atomic', p[2], p[3], p[5])
def p_atomic_op_statement_return(p):
'''atomic_op_statement : KW_ATOMIC_RETURN IDENTIFIER IDENTIFIER "(" value_list ")"'''
p[0] = ('atomic-return', p[2], p[3], p[5])
def p_label(p):
'''label : IDENTIFIER ":"'''
p[0] = ('label', p[1])
def p_goto_statement(p):
'''goto_statement : KW_GOTO IDENTIFIER'''
p[0] = ('goto', p[2])
def p_goto_statement_if(p):
'''goto_statement : KW_GOTO IDENTIFIER KW_IF "(" value ")"'''
p[0] = ('goto-if', p[2], p[5])
def p_goto_statement_if_not(p):
'''goto_statement : KW_GOTO IDENTIFIER KW_IF KW_NOT "(" value ")"'''
p[0] = ('goto-if-not', p[2], p[6])
def p_scope_statement(p):
'''scope_statement : scope'''
p[0] = (p[1])
def p_control_statement(p):
'''control_statement : KW_CONTROL "(" id_list ")"'''
p[0] = ('control', p[3])
def p_print_statement(p):
'''print_statement : KW_PRINT "(" printable_list ")"'''
p[0] = ('print', p[3])
def p_printable_list(p):
'''printable_list : printable'''
p[0] = [p[1]]
def p_printable_list_append(p):
'''printable_list : printable_list "," printable'''
p[0] = p[1]
p[0].append(p[3])
def p_printable_str_lit(p):
'''printable : STRING_LITERAL'''
p[0] = '"{}"'.format(p[1])
def p_printable_value(p):
'''printable : value'''
p[0] = p[1]
def p_printable_str_lit_value(p):
'''printable : STRING_LITERAL value'''
p[0] = ('"{}"'.format(p[1]), p[2])
def p_debug_break_statement(p):
'''debug_break_statement : KW_DEBUGBREAK'''
p[0] = ('debug-break')
def p_id_list(p):
'''id_list : IDENTIFIER'''
p[0] = p[1]
def p_id_list_append(p):
'''id_list : id_list "," IDENTIFIER'''
p[0] = p[1]
p[0].append(p[3])
def p_value(p):
'''value : IDENTIFIER
| INT_LITERAL'''
p[0] = p[1]
def p_value_braces(p):
'''value : "(" value ")"'''
p[0] = (p[2])
def p_value_member(p):
'''value : value "." IDENTIFIER'''
p[0] = ('member', p[1], p[3])
def p_value_idx(p):
'''value : value "[" value "]"'''
p[0] = ('index', p[1], p[3])
def p_value_binop(p):
'''value : value "+" value
| value "-" value
| value "*" value
| value "/" value
| value "%" value
| value "&" value
| value "|" value
| value "<" value
| value ">" value
| value "^" value
| value OP_LESSEQUAL value
| value OP_GREATEREQUAL value
| value OP_EQUALEQUAL value
| value OP_NOTEQUAL value
| value OP_LOGICAL_AND value
| value OP_LOGICAL_OR value
| value OP_LSHIFT value
| value OP_RSHIFT value'''
p[0] = (p[2], p[1], p[3])
def p_value_uniop(p):
'''value : "!" value
| "~" value'''
p[0] = (p[1], p[2])
def p_value_cond(p):
'''value : value "?" value ":" value'''
p[0] = ('?', p[1], p[3], p[5])
def p_value_funcop(p):
'''value : KW_OFFSETOF "(" offset_expression ")"
| KW_SHIFTOF "(" IDENTIFIER ")"
| KW_SIZEOF "(" IDENTIFIER ")"'''
p[0] = (p[1], p[3])
def p_offset_expression(p):
'''offset_expression : IDENTIFIER'''
p[0] = p[1]
def p_offset_expression_member(p):
'''offset_expression : offset_expression "." IDENTIFIER'''
p[0] = ('member', p[1], p[3])
def p_offset_expression_idx(p):
'''offset_expression : offset_expression "[" INT_LITERAL "]"'''
p[0] = ('index', p[1], p[3])
def p_struct_definition(p):
'''struct_definition : KW_STRUCT optional_alignment_specifier IDENTIFIER "{" optional_struct_member_list "}" ";"'''
p[0] = ('struct', p[3], p[5], p[2])
def p_optional_alignment_specifier(p):
'''optional_alignment_specifier :
| KW_ALIGNAS "(" INT_LITERAL ")"'''
if len(p) == 1:
p[0] = 0
else:
p[0] = p[3]
def p_optional_struct_member_list(p):
'''optional_struct_member_list :
| struct_member_list'''
if len(p) == 1:
p[0] = {}
else:
p[0] = p[1]
def p_struct_member_list(p):
'''struct_member_list : struct_member'''
p[0] = [p[1]]
def p_struct_member_list_append(p):
'''struct_member_list : struct_member_list struct_member'''
p[0] = p[1] + [p[2]]
def p_struct_member(p):
'''struct_member : struct_member_typename IDENTIFIER ";"'''
p[0] = (p[1], p[2])
def p_struct_member_array(p):
'''struct_member : struct_member_typename IDENTIFIER "[" INT_LITERAL "]" ";"'''
'''struct_member : struct_member_typename IDENTIFIER "[" IDENTIFIER "]" ";"'''
p[0] = {p[1]: p[2], 'count': p[4]}
def p_struct_member_typename(p):
'''struct_member_typename : IDENTIFIER'''
p[0] = p[1]
def p_struct_member_typename_unsigned(p):
'''struct_member_typename : KW_UNSIGNED IDENTIFIER'''
p[0] = ('unsigned', p[2])
def p_struct_member_typename_struct(p):
'''struct_member_typename : KW_STRUCT IDENTIFIER'''
p[0] = ('struct', p[2])
def p_const_definition(p):
'''const_definition : KW_CONST IDENTIFIER "=" INT_LITERAL ";"'''
p[0] = ('named-constant', p[2], p[4])
PARSER = yacc.yacc()
# Shamelessly stolen from some StackOverflow answer
def _remove_comments(text):
def replacer(match):
s = match.group(0)
if s.startswith('/'):
return " " # note: a space and not an empty string
else:
return s
pattern = re.compile(
r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
re.DOTALL | re.MULTILINE
)
return re.sub(pattern, replacer, text)
def parse_grl_file(grl_fname, libs):
global libraries
libraries = libs
with open(grl_fname, 'r') as f:
return PARSER.parse(_remove_comments(f.read()))