| #!/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())) |