nexmon – Rev 1

Subversion Repositories:
Rev:
########################
# 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);
}