class C99::Grammar::Actions;
our $decls := c99AST::Decls.new(); our $C99DEBUG :=0;
method TOP($/) { if $C99DEBUG { _dumper($decls); } make $decls; }
sub parse_decl_specs($/, $ast) { if $/ { for $/ { $ast.attr( strip_spaces(~$_), 1, 1); } } }
sub typedef($/) { if $/ { for $/ { if ('typedef' eq strip_spaces(~$_) ) { return 1; } } } return 0; }
method declaration($/) { my $ast := c99AST::VarDecl.new( :node($/) ); my $type := "";
#say("=================================================================================================");
#_dumper($/);
# make $decls;
# return 1;
my $decl_specs := $<declaration_specifiers><repeatable_declaration_specifiers>;
if typedef($decl_specs) {
$ast := c99AST::TypeDef.new( :node($/) );
$type := "TypeDef";
}
elsif $/<init_declarator><init_declarator><declarator><direct_declarator><declarator_suffix><declarator_suffix><parameter_type_list> {
$ast := c99AST::FuncDecl.new( :node($/) );
$type := "FuncDecl";
}
# elsif $<declaration_specifiers><type_specifier><type> {
# $ast := c99AST::Struct.new( :node($/) );
# $type := "Struct";
# }
else {
$ast := c99AST::VarDecl.new( :node($/) );
$type := "VarDecl";
}
parse_decl_specs( $<declaration_specifiers><repeatable_declaration_specifiers>, $ast );
# TYPE
settype($<declaration_specifiers><type_specifier>, $ast);
if ($type eq "FuncDecl") {
if (+$<init_declarator> != 1) {
say("ERROR FuncDecl has more than one <init_declarator>");
}
#assert(+$<init_declarator><declarator><direct_declarator><declarator_suffix> == 1);
my $params := $<init_declarator><declarator><declarator><direct_declarator><declarator_suffix><parameter_type_list><parameter_type_list><parameter_list><parameter_declaration>;
for $params {
my $param := c99AST::Param.new( :node( $_ ) );
settype($_<declaration_specifiers><type_specifier>, $param);
my $param_ident := $_<declarator>;
if $param_ident { setname( $_, $param ); }
ispointer($_, $param);
$ast.push( $param );
}
my $declarator := $<init_declarator><declarator>;
my $name := setname($declarator, $ast);
$decls{ $name } := $ast;
ispointer($declarator, $ast);
#if $C99DEBUG { _dumper($ast); }
#say($name);
}
#elsif ($type eq "VarDecl") {
#elsif $<init_declarator> {
else {
for $<init_declarator> {
my $l_ast := $ast.clone();
my $name := setname($_, $l_ast);
$decls{ $name } := $l_ast;
ispointer($_, $l_ast);
#if $C99DEBUG { _dumper($l_ast); }
#say($name);
}
}
#else {
# say("OPAQUE STRUCT OR UNTION");
# _dumper($/);
#}
make $decls;
}
sub countpointer($/) { if $<pointer> { return countpointer($<pointer>) + 1; } else { return 0; } }
sub ispointer($/, $ast) { if $/ { if $<declarator><pointer> { $ast.pointer(1); $ast.pointer_cnt(countpointer($<declarator><pointer>)); } }
my $lookup_ast := $ast;
repeat {
=begin comment
if $lookup_ast.pointer() {
$ast.pointer(1);
my $pc := +$ast.pointer_cnt();
$pc++;
$ast.pointer_cnt($pc);
}
=end comment
if $lookup_ast.builtin_type() {
$ast.primitive_type(~($lookup_ast.type()));
return 1;
}
my $type_name := $lookup_ast.type();
my $lookup_ast_name := $lookup_ast.name();
#FIXME struct or union typedef
if ($lookup_ast.name() eq $type_name) {
return 1;
}
$lookup_ast := $decls{$type_name};
unless $lookup_ast {
#say("Parent " ~~ $lookup_ast_name ~~ " " ~~ $type_name ~~ " not defined");
return 1;
}
if $lookup_ast.pointer() {
$ast.pointer(1);
my $pc := +$ast.pointer_cnt();
$pc := $pc + $lookup_ast.pointer_cnt();
$ast.pointer_cnt($pc);
}
} while (1);
#_dumper($ast);
}
sub settype($/, $ast) { if $/ { #is it a struct or union my $struct_or_union := strip_spaces(~$<type>); if $struct_or_union { my $ident := $<struct_or_union_specifier><identifier><identifier>; if $ident { $ident := strip_spaces($ident.text()); } else { $ident := $<struct_or_union_specifier><identifier>; if $ident { $ident := strip_spaces($ident.text()); } else { $ident := "anonymous_" ~~ $struct_or_union~~ "1"; } } $ast.type($ident); my $s_or_u := $<struct_or_union_specifier><struct_declaration>; if $s_or_u { my $su; if ($struct_or_union eq "struct" ) { $su := c99AST::Struct.new( :node($/) ); } else { $su := c99AST::Union.new( :node($/) ); } $su.name($ident); build_struct_or_union($s_or_u, $su); $ast.complex($su); } } else { $ast.type(strip_spaces(~$/)); # BUILTIN_TYPE if $<builtin_type> { $ast.builtin_type(1); } } } else { say("ERROR no type specifier"); } }
sub strip_spaces($_) { $_.replace(' ', ''); return $_; }
sub build_struct_or_union($/, $ast) { for $/ { my $smt := c99AST::VarDecl.new( :node($_) ); settype( $_<specifier_qualifier_list><type_specifier>, $smt ); for $_<struct_declarator_list> { my $sm := $smt.clone(); my $declarator := $_<struct_declarator><declarator>; $sm.name(strip_spaces($declarator.text())); ispointer($declarator, $sm); $ast.push($sm); } } }
sub setname($/, $ast) { my $name_node := $<declarator><direct_declarator><declarator_prefix>; if $name_node { $ast.name( ~$name_node ); return ~$name_node; } else { say("ERROR node doesn't have <direct_declarator><declarator_prefix>"); _dumper($/); } }
# Local Variables: # mode: cperl # cperl-indent-level: 4 # fill-column: 100 # End: # vim: expandtab shiftwidth=4 syntax=perl6: