diff options
Diffstat (limited to 'pidl/idl.yp')
-rw-r--r-- | pidl/idl.yp | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/pidl/idl.yp b/pidl/idl.yp new file mode 100644 index 0000000000..d557590494 --- /dev/null +++ b/pidl/idl.yp @@ -0,0 +1,497 @@ +######################## +# IDL Parse::Yapp parser +# Copyright (C) Andrew Tridgell <tridge@samba.org> +# released under the GNU GPL version 3 or later + + + +# the precedence actually doesn't matter at all for this grammar, but +# by providing a precedence we reduce the number of conflicts +# enormously +%left '-' '+' '&' '|' '*' '>' '.' '/' '(' ')' '[' ',' ';' + + +################ +# grammar +%% +idl: + #empty { {} } + | idl interface { push(@{$_[1]}, $_[2]); $_[1] } + | idl coclass { push(@{$_[1]}, $_[2]); $_[1] } + | idl import { push(@{$_[1]}, $_[2]); $_[1] } + | idl include { push(@{$_[1]}, $_[2]); $_[1] } + | idl importlib { push(@{$_[1]}, $_[2]); $_[1] } + | idl cpp_quote { push(@{$_[1]}, $_[2]); $_[1] } +; + +import: 'import' commalist ';' {{ + "TYPE" => "IMPORT", + "PATHS" => $_[2], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE} + }} +; +include: 'include' commalist ';' {{ + "TYPE" => "INCLUDE", + "PATHS" => $_[2], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE} + }} +; +importlib: 'importlib' commalist ';' {{ + "TYPE" => "IMPORTLIB", + "PATHS" => $_[2], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE} + }} +; + +commalist: + text { [ $_[1] ] } + | commalist ',' text { push(@{$_[1]}, $_[3]); $_[1] } +; + +coclass: property_list 'coclass' identifier '{' interface_names '}' optional_semicolon + {{ + "TYPE" => "COCLASS", + "PROPERTIES" => $_[1], + "NAME" => $_[3], + "DATA" => $_[5], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} +; + +interface_names: + #empty { {} } + | interface_names 'interface' identifier ';' { push(@{$_[1]}, $_[2]); $_[1] } +; + +interface: property_list 'interface' identifier base_interface '{' definitions '}' optional_semicolon + {{ + "TYPE" => "INTERFACE", + "PROPERTIES" => $_[1], + "NAME" => $_[3], + "BASE" => $_[4], + "DATA" => $_[6], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} +; + +base_interface: + #empty + | ':' identifier { $_[2] } +; + + +cpp_quote: 'cpp_quote' '(' text ')' + {{ + "TYPE" => "CPP_QUOTE", + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + "DATA" => $_[3] + }} +; + +definitions: + definition { [ $_[1] ] } + | definitions definition { push(@{$_[1]}, $_[2]); $_[1] } +; + + +definition: function | const | typedef | typedecl +; + +const: 'const' identifier pointers identifier '=' anytext ';' + {{ + "TYPE" => "CONST", + "DTYPE" => $_[2], + "POINTERS" => $_[3], + "NAME" => $_[4], + "VALUE" => $_[6], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} + | 'const' identifier pointers identifier array_len '=' anytext ';' + {{ + "TYPE" => "CONST", + "DTYPE" => $_[2], + "POINTERS" => $_[3], + "NAME" => $_[4], + "ARRAY_LEN" => $_[5], + "VALUE" => $_[7], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} +; + + +function: property_list type identifier '(' element_list2 ')' ';' + {{ + "TYPE" => "FUNCTION", + "NAME" => $_[3], + "RETURN_TYPE" => $_[2], + "PROPERTIES" => $_[1], + "ELEMENTS" => $_[5], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} +; + +typedef: property_list 'typedef' type identifier array_len ';' + {{ + "TYPE" => "TYPEDEF", + "PROPERTIES" => $_[1], + "NAME" => $_[4], + "DATA" => $_[3], + "ARRAY_LEN" => $_[5], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} +; + +usertype: struct | union | enum | bitmap; + +typedecl: usertype ';' { $_[1] }; + +sign: 'signed' | 'unsigned'; + +existingtype: + sign identifier { ($_[1]?$_[1]:"signed") ." $_[2]" } + | identifier +; + +type: usertype | existingtype | void { "void" } ; + +enum_body: '{' enum_elements '}' { $_[2] }; +opt_enum_body: | enum_body; +enum: property_list 'enum' optional_identifier opt_enum_body + {{ + "TYPE" => "ENUM", + "PROPERTIES" => $_[1], + "NAME" => $_[3], + "ELEMENTS" => $_[4] + }} +; + +enum_elements: + enum_element { [ $_[1] ] } + | enum_elements ',' enum_element { push(@{$_[1]}, $_[3]); $_[1] } +; + +enum_element: identifier + | identifier '=' anytext { "$_[1]$_[2]$_[3]" } +; + +bitmap_body: '{' opt_bitmap_elements '}' { $_[2] }; +opt_bitmap_body: | bitmap_body; +bitmap: property_list 'bitmap' optional_identifier opt_bitmap_body + {{ + "TYPE" => "BITMAP", + "PROPERTIES" => $_[1], + "NAME" => $_[3], + "ELEMENTS" => $_[4] + }} +; + +bitmap_elements: + bitmap_element { [ $_[1] ] } + | bitmap_elements ',' bitmap_element { push(@{$_[1]}, $_[3]); $_[1] } +; + +opt_bitmap_elements: | bitmap_elements; + +bitmap_element: identifier '=' anytext { "$_[1] ( $_[3] )" } +; + +struct_body: '{' element_list1 '}' { $_[2] }; +opt_struct_body: | struct_body; + +struct: property_list 'struct' optional_identifier opt_struct_body + {{ + "TYPE" => "STRUCT", + "PROPERTIES" => $_[1], + "NAME" => $_[3], + "ELEMENTS" => $_[4] + }} +; + +empty_element: property_list ';' + {{ + "NAME" => "", + "TYPE" => "EMPTY", + "PROPERTIES" => $_[1], + "POINTERS" => 0, + "ARRAY_LEN" => [], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} +; + +base_or_empty: base_element ';' | empty_element; + +optional_base_element: + property_list base_or_empty { $_[2]->{PROPERTIES} = FlattenHash([$_[1],$_[2]->{PROPERTIES}]); $_[2] } +; + +union_elements: + #empty + | union_elements optional_base_element { push(@{$_[1]}, $_[2]); $_[1] } +; + +union_body: '{' union_elements '}' { $_[2] }; +opt_union_body: | union_body; + +union: property_list 'union' optional_identifier opt_union_body + {{ + "TYPE" => "UNION", + "PROPERTIES" => $_[1], + "NAME" => $_[3], + "ELEMENTS" => $_[4] + }} +; + +base_element: property_list type pointers identifier array_len + {{ + "NAME" => $_[4], + "TYPE" => $_[2], + "PROPERTIES" => $_[1], + "POINTERS" => $_[3], + "ARRAY_LEN" => $_[5], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} +; + + +pointers: + #empty + { 0 } + | pointers '*' { $_[1]+1 } +; + +element_list1: + { [] } + | element_list1 base_element ';' { push(@{$_[1]}, $_[2]); $_[1] } +; + +optional_const: + #empty + | 'const' +; + +element_list2: + #empty + | 'void' + | optional_const base_element { [ $_[2] ] } + | element_list2 ',' optional_const base_element { push(@{$_[1]}, $_[4]); $_[1] } +; + +array_len: + #empty { [] } + | '[' ']' array_len { push(@{$_[3]}, "*"); $_[3] } + | '[' anytext ']' array_len { push(@{$_[4]}, "$_[2]"); $_[4] } +; + + +property_list: + #empty + | property_list '[' properties ']' { FlattenHash([$_[1],$_[3]]); } +; + +properties: property { $_[1] } + | properties ',' property { FlattenHash([$_[1], $_[3]]); } +; + +property: identifier {{ "$_[1]" => "1" }} + | identifier '(' commalisttext ')' {{ "$_[1]" => "$_[3]" }} +; + +commalisttext: + anytext + | commalisttext ',' anytext { "$_[1],$_[3]" } +; + +anytext: #empty + { "" } + | identifier | constant | text + | anytext '-' anytext { "$_[1]$_[2]$_[3]" } + | anytext '.' anytext { "$_[1]$_[2]$_[3]" } + | anytext '*' anytext { "$_[1]$_[2]$_[3]" } + | anytext '>' anytext { "$_[1]$_[2]$_[3]" } + | anytext '<' anytext { "$_[1]$_[2]$_[3]" } + | anytext '|' anytext { "$_[1]$_[2]$_[3]" } + | anytext '&' anytext { "$_[1]$_[2]$_[3]" } + | anytext '/' anytext { "$_[1]$_[2]$_[3]" } + | anytext '?' anytext { "$_[1]$_[2]$_[3]" } + | anytext ':' anytext { "$_[1]$_[2]$_[3]" } + | anytext '=' anytext { "$_[1]$_[2]$_[3]" } + | anytext '+' anytext { "$_[1]$_[2]$_[3]" } + | anytext '~' anytext { "$_[1]$_[2]$_[3]" } + | anytext '(' commalisttext ')' anytext { "$_[1]$_[2]$_[3]$_[4]$_[5]" } + | anytext '{' commalisttext '}' anytext { "$_[1]$_[2]$_[3]$_[4]$_[5]" } +; + +identifier: IDENTIFIER +; + +optional_identifier: + IDENTIFIER + | #empty { undef } +; + +constant: CONSTANT +; + +text: TEXT { "\"$_[1]\"" } +; + +optional_semicolon: + #empty + | ';' +; + + +##################################### +# start code +%% + +use Parse::Pidl qw(error); + +##################################################################### +# flatten an array of hashes into a single hash +sub FlattenHash($) +{ + my $a = shift; + my %b; + for my $d (@{$a}) { + for my $k (keys %{$d}) { + $b{$k} = $d->{$k}; + } + } + return \%b; +} + + + +##################################################################### +# traverse a perl data structure removing any empty arrays or +# hashes and any hash elements that map to undef +sub CleanData($) +{ + sub CleanData($); + my($v) = shift; + return undef if (not defined($v)); + if (ref($v) eq "ARRAY") { + foreach my $i (0 .. $#{$v}) { + CleanData($v->[$i]); + } + # this removes any undefined elements from the array + @{$v} = grep { defined $_ } @{$v}; + } elsif (ref($v) eq "HASH") { + foreach my $x (keys %{$v}) { + CleanData($v->{$x}); + if (!defined $v->{$x}) { delete($v->{$x}); next; } + } + } + return $v; +} + +sub _Error { + if (exists $_[0]->YYData->{ERRMSG}) { + error($_[0]->YYData, $_[0]->YYData->{ERRMSG}); + delete $_[0]->YYData->{ERRMSG}; + return; + } + my $last_token = $_[0]->YYData->{LAST_TOKEN}; + + error($_[0]->YYData, "Syntax error near '$last_token'"); +} + +sub _Lexer($) +{ + my($parser)=shift; + + $parser->YYData->{INPUT} or return('',undef); + +again: + $parser->YYData->{INPUT} =~ s/^[ \t]*//; + + for ($parser->YYData->{INPUT}) { + if (/^\#/) { + if (s/^\# (\d+) \"(.*?)\"( \d+|)//) { + $parser->YYData->{LINE} = $1-1; + $parser->YYData->{FILE} = $2; + goto again; + } + if (s/^\#line (\d+) \"(.*?)\"( \d+|)//) { + $parser->YYData->{LINE} = $1-1; + $parser->YYData->{FILE} = $2; + goto again; + } + if (s/^(\#.*)$//m) { + goto again; + } + } + if (s/^(\n)//) { + $parser->YYData->{LINE}++; + goto again; + } + if (s/^\"(.*?)\"//) { + $parser->YYData->{LAST_TOKEN} = $1; + return('TEXT',$1); + } + if (s/^(\d+)(\W|$)/$2/) { + $parser->YYData->{LAST_TOKEN} = $1; + return('CONSTANT',$1); + } + if (s/^([\w_]+)//) { + $parser->YYData->{LAST_TOKEN} = $1; + if ($1 =~ + /^(coclass|interface|const|typedef|union|cpp_quote + |struct|enum|bitmap|void|unsigned|signed|import|include + |importlib)$/x) { + return $1; + } + return('IDENTIFIER',$1); + } + if (s/^(.)//s) { + $parser->YYData->{LAST_TOKEN} = $1; + return($1,$1); + } + } +} + +sub parse_string +{ + my ($data,$filename) = @_; + + my $self = new Parse::Pidl::IDL; + + $self->YYData->{FILE} = $filename; + $self->YYData->{INPUT} = $data; + $self->YYData->{LINE} = 0; + $self->YYData->{LAST_TOKEN} = "NONE"; + + my $idl = $self->YYParse( yylex => \&_Lexer, yyerror => \&_Error ); + + return CleanData($idl); +} + +sub parse_file($$) +{ + my ($filename,$incdirs) = @_; + + my $saved_delim = $/; + undef $/; + my $cpp = $ENV{CPP}; + if (! defined $cpp) { + $cpp = "cpp"; + } + my $includes = join('',map { " -I$_" } @$incdirs); + my $data = `$cpp -D__PIDL__$includes -xc $filename`; + $/ = $saved_delim; + + return parse_string($data, $filename); +} |