Source code for cnorm.parsing.declaration

from pyrser import meta, directives
from pyrser.passes import to_yml
from pyrser.hooks import echo
from pyrser.parsing.node import Node
from cnorm import nodes
from pyrser.grammar import Grammar
from cnorm.parsing.statement import Statement
from cnorm.parsing.expression import Idset
from weakref import ref


# TODO: see if a better solution
import sys
sys.setrecursionlimit(10000)


[docs]class Declaration(Grammar, Statement): """ interaction with other CNORM PART: """ #: entry point for C programming language entry = "translation_unit" #: complete C declaration grammar grammar = """ translation_unit = [ @ignore("C/C++") [ __scope__:current_block #new_root(_, current_block) [ declaration ]* ] Base.eof ] declaration = [ ';' // garbage single comma | c_decl | preproc_decl | asm_decl ] preproc_decl = [ ['#' preproc_directive ]:decl #raw_decl(decl) #end_decl(current_block, decl) ] asm_decl = [ [ Base.id:i #check_asm(i) [ Base.id:i #check_quali(i) ]? attr_asm_decl_follow ';'? ]:decl #raw_decl(decl) #end_decl(current_block, decl) ] attr_asm_decl = [ [ Base.id:i #check_asmattr(i) attr_asm_decl_follow ] ] attr_asm_decl_follow = [ '(' dummy_with_paren* ')' | '{' dummy_with_brace* '}' | '__extension__' ] c_decl = [ __scope__:local_specifier #create_ctype(local_specifier) declaration_specifier*:dsp init_declarator:decl #not_empty(current_block, dsp, decl) #end_decl(current_block, decl) [ ',' #copy_ctype(local_specifier, decl) init_declarator:decl #end_decl(current_block, decl) ]* [ ';' | Statement.compound_statement:b #add_body(decl, b) ] ] declaration_specifier = [ Base.id:i #new_decl_spec(local_specifier, i, current_block) [ #is_composed(local_specifier) composed_type_specifier | #is_enum(local_specifier) enum_specifier | #is_typeof(i) typeof_expr ]? | attr_asm_decl:attr #add_attr_specifier(local_specifier, attr) ] type_qualifier = [ Base.id:i #add_qual(local_specifier, i) | attr_asm_decl:attr #add_attr_specifier(local_specifier, attr) ] name_of_composed_type = [ Base.id ] composed_type_specifier = [ [ attr_asm_decl:attr #add_attr_composed(local_specifier, attr) ]? name_of_composed_type?:n composed_fields?:body #add_composed(local_specifier, n, body) ] composed_fields = [ '{' __scope__:current_block #new_composed(_, current_block) declaration* '}' ] enum_name = [ Base.id ] enum_specifier = [ enum_name?:n enumerator_list?:body #add_enum(local_specifier, n, body) ] enumerator_list = [ '{' enumerator:e #add_enumerator(_, e) [ ',' enumerator:e #add_enumerator(_, e) ]* ','? // trailing comma '}' ] enumerator = [ identifier:i __scope__:c ['=' constant_expression:>c]? #new_enumerator(_, i, c) ] typeof_expr = [ // TODO: split inside typeof ['(' [ type_name !!')' | expression ] ')']:tof #add_typeof(local_specifier, tof) ] init_declarator = [ declarator:>_ [ ':' constant_expression:cexpr #colon_expr(_, cexpr) ]? [ attr_asm_decl:attr #add_attr_decl(_, attr) ]* [ '=' initializer:aexpr #assign_expr(_, aexpr) ]? !![','|';'|'{'] ] declarator = [ [ "*" #first_pointer(local_specifier) declarator_recurs:>_ | absolute_declarator:>_ ] #commit_declarator(_, local_specifier) ] declarator_recurs = [ pointer absolute_declarator:>_ ] pointer = [ [ "*" #add_pointer(local_specifier) | type_qualifier ]* ] f_or_v_id = [ identifier ] absolute_declarator = [ [ '(' #add_paren(local_specifier) type_qualifier? declarator_recurs:>_ #close_paren(local_specifier) ')' | f_or_v_id?:name #name_absdecl(local_specifier, name) ] direct_absolute_declarator? ] direct_absolute_declarator = [ [ '[' // TODO: handle c99 qual for trees "static"? ["const"|"volatile"]? "static"? __scope__:expr [ assignement_expression:>expr | '*':star #new_raw(expr, star) ]? #add_ary(local_specifier, expr) ']' ]+ | '(' #open_params(local_specifier) [ //kr_parameter_type_list //| parameter_type_list ]? ')' /* [ // K&R STYLE !![';'|','|'{'|'('|')'] | declaration* ] */ ] kr_parameter_type_list = [ identifier [',' identifier]* !!')' ] parameter_type_list = [ [type_name ';']* [ parameter_list ]? ','? ["..." #add_ellipsis(local_specifier)]? ] parameter_list = [ parameter_declaration:p #add_param(local_specifier, p) [',' parameter_declaration:p #add_param(local_specifier, p) ]* ] parameter_declaration = [ type_name:>_ ] initializer = [ [ initializer_block | assignement_expression ]:>_ ] initializer_block = [ '{' __scope__:init_list #new_blockinit(init_list) [initializer_list]? ','? // trailing comma #bind('_', init_list) '}' ] initializer_list = [ designation?:dsign initializer:init #add_init(init_list, init, dsign) [ ',' designation?:dsign initializer:init #add_init(init_list, init, dsign) ]* ] designation = [ designation_list+ '='? | identifier ':' ] designation_list = [ '[' range_expression ']' | dot identifier ] type_name = [ __scope__:local_specifier #create_ctype(local_specifier) declaration_specifier+ declarator:>_ ] ///////// OVERLOAD OF STATEMENT // add declaration in block line_of_code = [ declaration | single_statement:line #end_loc(current_block, line) ] for_statement = [ '(' __scope__:init [ expression_statement:>init | __scope__:current_block #for_decl_begin(current_block) declaration #for_decl_end(init, current_block) ] expression_statement:cond expression?:inc ')' single_statement:body #new_for(_, init, cond, inc, body) ] ///////// OVERLOAD OF EXPRESSION // add cast / sizeof unary_expression = [ // CAST '(' type_name:t ')' [ // simple cast unary_expression | // compound literal initializer_block ]:>_ #to_cast(_, t) | // SIZEOF Base.id:i #sizeof(i) __scope__:n [ '(' type_name:>n ')' | Expression.unary_expression:>n ] #new_sizeof(_, i, n) | Expression.unary_expression:>_ ] // ({}) and __builtin_offsetof primary_expression = [ "({" __scope__:current_block #new_blockexpr(_, current_block) [ line_of_code ]* "})" | // TODO: create special node for that "__builtin_offsetof" '(' [type_name ',' postfix_expression]:bof ')' #new_builtoffset(_, bof) | Expression.primary_expression:>_ ] """
@meta.hook(Declaration)
[docs]def check_asm(self, ident): ident_value = self.value(ident) if ident_value in Idset and Idset[ident_value] == "asm": return True return False
@meta.hook(Declaration)
[docs]def check_quali(self, ident): ident_value = self.value(ident) if ident_value in Idset and Idset[ident_value] == "qualifier": return True return False
@meta.hook(Declaration)
[docs]def check_asmattr(self, ident): ident_value = self.value(ident) if ((ident_value in Idset and (Idset[ident_value] == "asm" or Idset[ident_value] == "attribute"))): return True return False
@meta.hook(Declaration)
[docs]def new_root(self, ast, current_block): ast.set(nodes.RootBlockStmt([])) current_block.ref = ast return True
@meta.rule(Declaration, "preproc_directive")
[docs]def preproc_directive(self) -> bool: """Consume a preproc directive.""" self._stream.save_context() if self.read_until("\n", '\\'): return self._stream.validate_context() return self._stream.restore_context()
@meta.hook(Declaration)
[docs]def raw_decl(self, decl): decl.set(nodes.Raw(self.value(decl))) return True
@meta.hook(Declaration)
[docs]def create_ctype(self, lspec): lspec.ctype = None return True
@meta.hook(Declaration)
[docs]def copy_ctype(self, lspec, previous): lspec.ctype = previous.ctype.copy() return True
@meta.hook(Declaration)
[docs]def new_decl_spec(self, lspec, i, current_block): idsetval = "" i_value = self.value(i) if i_value in Idset: idsetval = Idset[i_value] # don't fuck with reserved keywords if idsetval == "reserved": return False # not for asm or attribute if idsetval != "" and idsetval[0] != 'a': lspec.ctype = nodes.makeCType(i_value, lspec.ctype) return True if ((hasattr(current_block.ref, 'types') and i_value in current_block.ref.types)): if lspec.ctype is None: lspec.ctype = nodes.PrimaryType(i_value) else: lspec.ctype._identifier = i_value lspec.ctype._identifier = i_value return True return False
@meta.hook(Declaration)
[docs]def add_body(self, ast, body): ast.body = body return True
@meta.hook(Declaration)
[docs]def end_decl(self, current_block, ast): current_block.ref.body.append(ast) if ((hasattr(ast, 'ctype') and ast._name != "" and ast.ctype._storage == nodes.Storages.TYPEDEF)): current_block.ref.types[ast._name] = ref(ast) return True
@meta.hook(Declaration)
[docs]def not_empty(self, current_block, dsp, decl): # empty declspec only in global scope if type(current_block.ref) is nodes.BlockStmt and self.value(dsp) == "": return False return True
@meta.hook(Declaration)
[docs]def colon_expr(self, ast, expr): ast.colon_expr(expr) return True
@meta.hook(Declaration)
[docs]def assign_expr(self, ast, expr): ast.assign_expr(expr) return True
@meta.hook(Declaration)
[docs]def is_composed(self, lspec): if ((lspec.ctype._specifier == nodes.Specifiers.STRUCT or lspec.ctype._specifier == nodes.Specifiers.UNION)): return True return False
@meta.hook(Declaration)
[docs]def is_enum(self, lspec): if lspec.ctype._specifier == nodes.Specifiers.ENUM: return True return False
@meta.hook(Declaration)
[docs]def is_typeof(self, i): i_value = self.value(i) if i_value in Idset and Idset[i_value] == "typeof": return True return False
@meta.hook(Declaration)
[docs]def add_typeof(self, lspec, tof): lspec.ctype = nodes.PrimaryType("typeof" + self.value(tof)) return True
@meta.hook(Declaration)
[docs]def add_qual(self, lspec, qualspec): dspec = self.value(qualspec) if dspec in Idset and Idset[dspec] == "qualifier": cleantxt = dspec.strip("_") lspec.ctype.push( nodes.QualType(nodes.Qualifiers.map[cleantxt.upper()]) ) return True return False
@meta.hook(Declaration)
[docs]def add_attr_specifier(self, lspec, attrspec): if lspec.ctype is None: lspec.ctype = nodes.makeCType('int', lspec.ctype) lspec.ctype.push(nodes.AttrType(self.value(attrspec))) return True
@meta.hook(Declaration)
[docs]def add_attr_composed(self, lspec, attrspec): if not hasattr(lspec.ctype, '_attr_composed'): lspec.ctype._attr_composed = [] lspec.ctype._attr_composed.append(self.value(attrspec)) return True
@meta.hook(Declaration)
[docs]def add_attr_decl(self, lspec, attrspec): if not hasattr(lspec, '_attr_decl'): lspec._attr_decl = [] lspec._attr_decl.append(self.value(attrspec)) return True
@meta.hook(Declaration)
[docs]def add_composed(self, lspec, n, block): ctype = nodes.ComposedType(self.value(n)) if lspec.ctype is not None: ctype._storage = lspec.ctype._storage ctype._specifier = lspec.ctype._specifier if hasattr(lspec.ctype, '_attr_composed'): ctype._attr_composed = lspec.ctype._attr_composed lspec.ctype = ctype if hasattr(block, 'body'): lspec.ctype.fields = block.body return True
@meta.hook(Declaration)
[docs]def add_enum(self, lspec, n, block): ctype = nodes.ComposedType(self.value(n)) if lspec.ctype is not None: ctype._storage = lspec.ctype._storage ctype._specifier = lspec.ctype._specifier lspec.ctype = ctype if hasattr(block, 'list'): lspec.ctype.enums = block.list return True
@meta.hook(Declaration)
[docs]def add_enumerator(self, ast, enum): if not hasattr(ast, 'list'): ast.list = [] ast.list.append(enum) return True
@meta.hook(Declaration)
[docs]def new_enumerator(self, ast, ident, constexpr): ast.set(nodes.Enumerator(self.value(ident), constexpr)) return True
@meta.hook(Declaration)
[docs]def new_composed(self, ast, current_block): ast.set(nodes.BlockStmt([])) current_block.ref = ast parent = self.rule_nodes.parents if 'current_block' in parent: current_block.ref.types = parent['current_block'].ref.types.new_child() return True
@meta.hook(Declaration)
[docs]def first_pointer(self, lspec): if not hasattr(lspec, 'ctype') or lspec.ctype is None: lspec.ctype = nodes.makeCType('int', lspec.ctype) lspec.ctype.push(nodes.PointerType()) return True
@meta.hook(Declaration)
[docs]def commit_declarator(self, ast, lspec): if hasattr(lspec.ctype, '_params'): lspec.ctype.__class__ = nodes.FuncType name = "" if hasattr(lspec, '_name'): name = lspec._name ast.set(nodes.Decl(name, lspec.ctype)) return True
@meta.hook(Declaration)
[docs]def add_pointer(self, lspec): if not hasattr(lspec, 'ctype'): lspec.ctype = nodes.makeCType('int', lspec.ctype) if not hasattr(lspec.ctype, 'push'): return False lspec.ctype.push(nodes.PointerType()) return True
@meta.hook(Declaration)
[docs]def add_paren(self, lspec): if not hasattr(lspec, 'ctype') or lspec.ctype is None: lspec.ctype = nodes.makeCType('int') paren = nodes.ParenType() if not hasattr(lspec, 'cur_paren'): lspec.cur_paren = [] lspec.cur_paren.append(ref(lspec.ctype)) last = lspec.cur_paren.pop() lspec.cur_paren.append(ref(paren)) lspec.cur_paren.append(last) lspec.ctype.push(paren) return True
@meta.hook(Declaration)
[docs]def add_ary(self, lspec, expr): if not hasattr(lspec, 'ctype') or lspec.ctype is None: lspec.ctype = nodes.makeCType('int') if not hasattr(lspec, 'cur_paren'): lspec.cur_paren = [] lspec.cur_paren.append(ref(lspec.ctype)) lspec.cur_paren[-1]().push(nodes.ArrayType(expr)) return True
@meta.hook(Declaration)
[docs]def name_absdecl(self, ast, ident): ident_value = self.value(ident) if ident_value != "": ast._name = ident_value ast._could_be_fpointer = True return True
@meta.hook(Declaration)
[docs]def close_paren(self, lspec): lspec.cur_paren.pop() return True
@meta.hook(Declaration)
[docs]def open_params(self, lspec): if lspec.ctype is None: lspec.ctype = nodes.makeCType('int') if not hasattr(lspec, 'cur_paren'): lspec.cur_paren = [] lspec.cur_paren.append(ref(lspec.ctype)) if not hasattr(lspec.cur_paren[-1](), '_params'): lspec.cur_paren[-1]()._params = [] return True
@meta.hook(Declaration)
[docs]def add_param(self, lspec, param): lspec.cur_paren[-1]()._params.append(param) return True
@meta.hook(Declaration)
[docs]def add_ellipsis(self, lspec): lspec.cur_paren[-1]()._ellipsis = True return True
@meta.hook(Declaration)
[docs]def new_blockinit(self, init_list): init_list.set(nodes.BlockInit([])) return True
@meta.hook(Declaration)
[docs]def new_blockexpr(self, ast, current_block): ast.set(nodes.BlockExpr([])) current_block.ref = ast parent = self.rule_nodes.parents if 'current_block' in parent: current_block.ref.types = parent['current_block'].ref.types.new_child() return True
@meta.hook(Declaration)
[docs]def add_init(self, ast, expr, designation): ast.body.append(expr) dvalue = self.value(designation) if dvalue != "": ast.body[-1].designation = dvalue return True
@meta.hook(Declaration)
[docs]def for_decl_begin(self, current_block): current_block.ref = Node() current_block.ref.body = [] # new to link this fake body to other block parent = self.rule_nodes.parents if 'current_block' in parent: current_block.ref.types = parent['current_block'].ref.types.new_child() return True
@meta.hook(Declaration)
[docs]def for_decl_end(self, init, current_block): if len(current_block.ref.body) > 0: init.set(current_block.ref.body[0]) return True
@meta.hook(Declaration)
[docs]def to_cast(self, ast, typename): expr = Node() expr.set(ast) ast.set(nodes.Cast(nodes.Raw('()'), [typename.ctype, expr])) return True
@meta.hook(Declaration)
[docs]def sizeof(self, ident): ident_value = self.value(ident) if ident_value in Idset and Idset[ident_value] == "sizeof": return True return False
@meta.hook(Declaration)
[docs]def new_sizeof(self, ast, i, n): thing = n if isinstance(thing, nodes.Decl): thing = n.ctype ast.set(nodes.Sizeof(nodes.Raw(self.value(i)), [thing])) return True
@meta.hook(Declaration)
[docs]def new_builtoffset(self, ast, bof): ast.set(nodes.Raw("__builtin_offsetof(" + self.value(bof) + ")")) return True