diff options
Diffstat (limited to 'pidl/idl.yp')
-rw-r--r-- | pidl/idl.yp | 696 |
1 files changed, 696 insertions, 0 deletions
diff --git a/pidl/idl.yp b/pidl/idl.yp new file mode 100644 index 0000000..c8a65f6 --- /dev/null +++ b/pidl/idl.yp @@ -0,0 +1,696 @@ +######################## +# 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", + "DATA" => $_[3], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} +; + +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 pointers identifier array_len ';' + {{ + "TYPE" => "TYPEDEF", + "PROPERTIES" => $_[1], + "NAME" => $_[5], + "DATA" => $_[3], + "POINTERS" => $_[4], + "ARRAY_LEN" => $_[6], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} +; + +usertype: + struct + | + union + | + enum + | + bitmap + | + pipe +; + +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: + #empty + | + enum_body +; + +enum: + property_list 'enum' optional_identifier opt_enum_body + {{ + "TYPE" => "ENUM", + "PROPERTIES" => $_[1], + "NAME" => $_[3], + "ELEMENTS" => $_[4], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} +; + +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: + #empty + | + bitmap_body +; + +bitmap: + property_list 'bitmap' optional_identifier opt_bitmap_body + {{ + "TYPE" => "BITMAP", + "PROPERTIES" => $_[1], + "NAME" => $_[3], + "ELEMENTS" => $_[4], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} +; + +bitmap_elements: + bitmap_element { [ $_[1] ] } + | + bitmap_elements ',' bitmap_element { push(@{$_[1]}, $_[3]); $_[1] } +; + +opt_bitmap_elements: + #empty + | + bitmap_elements +; + +bitmap_element: + identifier '=' anytext { "$_[1] ( $_[3] )" } +; + +struct_body: + '{' element_list1 '}' { $_[2] } +; + +opt_struct_body: + #empty + | + struct_body +; + +struct: + property_list 'struct' optional_identifier opt_struct_body + {{ + "TYPE" => "STRUCT", + "PROPERTIES" => $_[1], + "NAME" => $_[3], + "ELEMENTS" => $_[4], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} +; + +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: + #empty + | + union_body +; + +union: + property_list 'union' optional_identifier opt_union_body + {{ + "TYPE" => "UNION", + "PROPERTIES" => $_[1], + "NAME" => $_[3], + "ELEMENTS" => $_[4], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} +; + +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 } +; + +pipe: + property_list 'pipe' type + {{ + "TYPE" => "PIPE", + "PROPERTIES" => $_[1], + "NAME" => undef, + "DATA" => { + "TYPE" => "STRUCT", + "PROPERTIES" => $_[1], + "NAME" => undef, + "ELEMENTS" => [{ + "NAME" => "count", + "PROPERTIES" => $_[1], + "POINTERS" => 0, + "ARRAY_LEN" => [], + "TYPE" => "uint3264", + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + },{ + "NAME" => "array", + "PROPERTIES" => $_[1], + "POINTERS" => 0, + "ARRAY_LEN" => [ "count" ], + "TYPE" => $_[3], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }, + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} +; + +element_list1: + #empty + { [] } + | + 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: + #empty { undef } + | + IDENTIFIER +; + +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 (/^\#/) { + # Linemarker format is described at + # http://gcc.gnu.org/onlinedocs/cpp/Preprocessor-Output.html + if (s/^\# (\d+) \"(.*?)\"(( \d+){1,4}|)//) { + $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|import|importlib + |include|cpp_quote|typedef + |union|struct|enum|bitmap|pipe + |void|const|unsigned|signed)$/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}; + my $options = ""; + if (! defined $cpp) { + if (defined $ENV{CC}) { + $cpp = "$ENV{CC}"; + $options = "-E"; + } else { + $cpp = "cpp"; + } + } + my $includes = join('',map { " -I$_" } @$incdirs); + my $data = `$cpp $options -D__PIDL__$includes -xc "$filename"`; + $/ = $saved_delim; + + return parse_string($data, $filename); +} |