%{ // IDL parser // // This software is copyright (c) 2006 Scott Wood . // // This software is provided 'as-is', without any express or implied warranty. // In no event will the authors or contributors be held liable for any damages // arising from the use of this software. // // Permission is hereby granted to everyone, free of charge, to use, copy, // modify, prepare derivative works of, publish, distribute, perform, // sublicense, and/or sell copies of the Software, provided that the above // copyright notice and disclaimer of warranty be included in all copies or // substantial portions of this software. #include #include #include #include #include #define YYDEBUG 1 #if 1 #define do_yyerror() do { \ fprintf(stderr, "YYERROR at %d\n", __LINE__); \ throw UserError(); \ } while (0) #else #define do_yyerror() YYERROR #endif static StrListRef nspace_name; static StrListRef cur_strlist; static ConListRef cur_conlist; static IDListRef cur_idlist; %} %union { // The lifetime of any of these pointers is one instance // of the one_input rule. String *string; StrList *strl; IDList *idl; struct Decl { // Used for namespace-qualified type declarations NameSpace *ns; // Namespace portion -- all but last field const String *ident; // New identifier portion -- last field } decl; SymList *syml; Symbol *sym; Enum *en; Struct *str; BitField *bf; Interface *iface; Method *meth; Datum *datum; bool boolean; IFaceList *ifl; Con con; ConList *conl; Array *array; struct { StrList *type; Array *array; } type; int val; struct { Decl decl; StrList *attr; } struct_decl_attr; struct { StrList *ids; StrList *attr; } ids_attr; uint64_t guid[2]; } // The token list must be exactly the same as in idlparse.y, so that // the same lexer can be used. %token TOK_IDENT %token TOK_IFACE %token TOK_STRUCT %token TOK_CHAR %token TOK_OCTET %token TOK_ICON %token TOK_FCON %token TOK_UCON %token TOK_INVALID %token TOK_BOOL %token TOK_SHORT %token TOK_INT %token TOK_LONG %token TOK_USHORT %token TOK_UINT %token TOK_ULONG %token TOK_FSHORT %token TOK_FLONG %token TOK_CONST %token TOK_BITFIELD %token TOK_ENUM %token TOK_NAMESPACE %token TOK_USING %token TOK_ASYNC %token TOK_INOUT %token TOK_OUT %token TOK_3DOT %token TOK_2DOT %token TOK_STR %token TOK_SHARED %token TOK_PUSH %token TOK_TYPEDEF %token TOK_ALIAS %token TOK_VIRTUAL %token TOK_GUID %token TOK_INLINE %token TOK_STATIC %token TOK_IMMUTABLE %token TOK_TRUE %token TOK_FALSE // CDL tokens %token TOK_COPY %token TOK_METHOD %token TOK_CLASS %token TOK_NAME // These are not real tokens, but are used as special values in places that // normally accept tokens. %token TOK_NONE %token TOK_ANON %token TOK_DCON %type basictype %type type %type const %type maybeconst %type arraybounds %type constnominus %type bfelem %type bfelems %type maybe_bfelems %type maybe_ids %type ids %type ideqs %type ident %type strlist %type maybe_strlist %type end_strlist %type size %type bftype %type qualified_ident %type qualified_ident_nodbldot %type qualified_decl %type maybe_qualified_decl %type anon_decl %type qualified_idlist %type maybe_dot_star %type inherit_struct %type struct %type bitfield %type enum %type iface %type inherit_ifaces %type typedef_or_alias_keyword %type guid %type struct_decl_and_attr %type ids_attr %% input: /* empty */ | input one_input ; one_input_real: using ';' | namespace | enum ';' {} | bitfield ';' {} | struct ';' {} | iface ';' {} | typedef_or_alias ';' {} | const_datum ';' {} ; one_input: one_input_real ; namespace_body: ';' { NameSpace *ret = add_nspace(nspace_name, false); nspace_name = NULL; if (!ret) do_yyerror(); } | '{' { NameSpace *ret = add_nspace(nspace_name, true); nspace_name = NULL; if (!ret) do_yyerror(); } input '}' { pop_nspace(); } | { NameSpace *ret = add_nspace(nspace_name, true); nspace_name = NULL; if (!ret) do_yyerror(); } one_input { pop_nspace(); } ; namespace: TOK_NAMESPACE qualified_ident { nspace_name = $2; } namespace_body ; const_datum: TOK_CONST type ideqs { ConList *cl = $3; for (ConList::iterator i = cl->begin(); i != cl->end(); ++i) Datum::declare((*i).str, cur_nspace, $2.type, $2.array, &(*i).con); } ; ids_attr: /* empty */ { $$.ids = NULL; $$.attr = NULL; } | ids maybe_strlist { $$.ids = $1; $$.attr = $2; } ; struct_elem: const_datum ';' | type ids maybe_strlist ';' { declare_data(cur_nspace, $2, $1.type, $1.array, $3); } | enum maybe_ids ';' { if (!$2 && $1->name->token == TOK_ANON) yyerrorfl($1->name->file, $1->name->line, "An anonymous type must declare a datum."); if ($2) { StrList *strl = $1->get_fq_name(); strl->push_front(new String("")); declare_data(cur_nspace, $2, strl, NULL, NULL); } } | bitfield maybe_ids ';' { if (!$2 && $1->name->token == TOK_ANON) yyerrorfl($1->name->file, $1->name->line, "An anonymous type must declare a datum."); if ($2) { StrList *strl = $1->get_fq_name(); strl->push_front(new String("")); declare_data(cur_nspace, $2, strl, NULL, NULL); } } | struct ids_attr ';' { if (!$2.ids && $1->name->token == TOK_ANON) yyerrorfl($1->name->file, $1->name->line, "An anonymous type must declare a datum."); if ($2.ids) { StrList *strl = $1->get_fq_name(); strl->push_front(new String("")); declare_data(cur_nspace, $2.ids, strl, NULL, $2.attr); } } | typedef_or_alias ';' | using ';' | guid ';' { Struct *str = dynamic_cast(*cur_nspace); assert(str); str->set_guid($1); } ; struct_body: /* empty */ | struct_elem struct_body ; inherit_struct: /* empty */ { $$ = NULL; } | ':' qualified_ident { $$ = $2; } ; struct_decl_and_attr: anon_decl { // Anonymous structs have no attributes. $$.decl = $1; $$.attr = new StrList; } | qualified_decl maybe_strlist { $$.decl = $1; $$.attr = $2; } ; struct: TOK_STRUCT struct_decl_and_attr inherit_struct '{' { $$ = Struct::declare($2.decl.ident, $2.decl.ns, $3); for (StrList::iterator i = $2.attr->begin(); i != $2.attr->end(); ++i) { const String *attr = *i; switch (attr->token) { case TOK_VIRTUAL: $$->set_virtual(); break; case TOK_INLINE: $$->set_inline(); break; default: yyerrorfl(attr->file, attr->line, "Invalid attribute \"%s\"", (*i)->c_str()); } } nspace_stack.push_front(cur_nspace); cur_nspace = $$; } struct_body '}' { // One for the struct's namespace, and one for the // namespace it was declared in. pop_nspace(); pop_nspace(); $$ = $5; } ; param: type strlist { const String *name = $2->front(); $2->pop_front(); CompiledParam::Flags flags = {}; int dirs = 0; flags.field.In = 1; for (StrList::iterator i = $2->begin(); i != $2->end(); ++i) { const String *attr = *i; switch (attr->token) { case TOK_OUT: if (dirs++ > 0) { yyerrorf("Only one direction attribute may be given."); } else { flags.field.In = 0; flags.field.Out = 1; } break; case TOK_INOUT: if (dirs++ > 0) { yyerrorf("Only one direction attribute may be given."); } else { flags.field.In = 1; flags.field.Out = 1; } break; case TOK_SHARED: flags.field.Shared = 1; break; case TOK_PUSH: flags.field.Push = 1; break; case TOK_INLINE: flags.field.Inline = 1; break; case TOK_IMMUTABLE: flags.field.Immutable = 1; break; default: yyerrorfl(attr->file, attr->line, "Invalid attribute \"%s\"", (*i)->c_str()); } } Param::declare(name, cur_nspace, $1.type, flags, $1.array); } ; more_params: /* empty */ | ',' param_list ; param_list: /* empty */ | param more_params ; method: ident '(' { $$ = Method::declare($1, cur_nspace); nspace_stack.push_front(cur_nspace); cur_nspace = $$; } param_list ')' maybe_strlist { for (StrList::iterator i = $6->begin(); i != $6->end(); ++i) { const String *attr = *i; switch (attr->token) { case TOK_ASYNC: $3->set_async(); break; default: yyerrorfl(attr->file, attr->line, "Invalid attribute \"%s\".", (*i)->c_str()); } } pop_nspace(); } ; iface_body: /* empty */ | method ';' iface_body | enum ';' iface_body {} | bitfield ';' iface_body {} | struct ';' iface_body {} | iface ';' iface_body {} | typedef_or_alias ';' iface_body | const_datum ';' iface_body | guid ';' iface_body { Interface *iface = dynamic_cast(*cur_nspace); assert(iface); iface->set_guid($1); } | using ';' iface_body ; guid: TOK_GUID ':' TOK_STR { parse_guid($3->c_str(), (char *)$$); } ; inherit_ifaces: /* empty */ { $$ = new IDList; } | ':' qualified_idlist { $$ = $2; } ; iface: TOK_IFACE qualified_decl inherit_ifaces { $$ = Interface::declare($2.ident, $2.ns, $3); nspace_stack.push_front(cur_nspace); cur_nspace = $$; } '{' iface_body '}' { pop_nspace(); pop_nspace(); $$ = $3; } ; maybe_namespace: /* empty */ | TOK_NAMESPACE ; using_list_entry: maybe_namespace qualified_ident { const String *end = $2->back(); bool ns = false; if (end->token == '*') { ns = true; $2->pop_back(); } if (ns) { cur_nspace->add_search($2); } else { Alias::declare(end, cur_nspace, $2, true); } } ; using_list: using_list_entry | using_list_entry ',' using_list ; using: TOK_USING using_list ; ids_body: ident { cur_strlist->push_back($1); } | ids_body ',' ident { cur_strlist->push_back($3); } ; ids: ids_body { $$ = cur_strlist; cur_strlist = new StrList; } ; maybe_ids: /* empty */ { $$ = NULL; } | ids { $$ = $1; } ; coninit: ident '=' const { cur_conlist->push_back(ConInit($1, $3)); } ; /* ideqs is ids with a constant initializer for each element. */ ideqs_body: coninit | ids_body ',' coninit ; ideqs: ideqs_body { $$ = cur_conlist; cur_conlist = new ConList; } ; ident: TOK_IDENT | TOK_ASYNC { $$ = new String("async", cur_input_file, curline, TOK_ASYNC); } | TOK_INOUT { $$ = new String("inout", cur_input_file, curline, TOK_INOUT); } | TOK_OUT { $$ = new String("out", cur_input_file, curline, TOK_OUT); } | TOK_SHARED { $$ = new String("shared", cur_input_file, curline, TOK_SHARED); } | TOK_PUSH { $$ = new String("push", cur_input_file, curline, TOK_PUSH); } | TOK_SHORT { $$ = new String("short", cur_input_file, curline, TOK_SHORT); } | TOK_INT { $$ = new String("int", cur_input_file, curline, TOK_INT); } | TOK_LONG { $$ = new String("long", cur_input_file, curline, TOK_LONG); } | TOK_USHORT { $$ = new String("ushort", cur_input_file, curline, TOK_USHORT); } | TOK_UINT { $$ = new String("uint", cur_input_file, curline, TOK_UINT); } | TOK_ULONG { $$ = new String("ulong", cur_input_file, curline, TOK_ULONG); } | TOK_CHAR { $$ = new String("char", cur_input_file, curline, TOK_CHAR); } | TOK_OCTET { $$ = new String("octet", cur_input_file, curline, TOK_OCTET); } | TOK_FSHORT { $$ = new String("fshort", cur_input_file, curline, TOK_FSHORT); } | TOK_FLONG { $$ = new String("flong", cur_input_file, curline, TOK_FLONG); } | TOK_BOOL { $$ = new String("bool", cur_input_file, curline, TOK_BOOL); } | TOK_METHOD { $$ = new String("method", cur_input_file, curline, TOK_METHOD); } | TOK_NAME { $$ = new String("name", cur_input_file, curline, TOK_NAME); } | TOK_COPY { $$ = new String("copy", cur_input_file, curline, TOK_COPY); } | TOK_CLASS { $$ = new String("class", cur_input_file, curline, TOK_CLASS); } | TOK_GUID { $$ = new String("guid", cur_input_file, curline, TOK_GUID); } | TOK_STATIC { $$ = new String("static", cur_input_file, curline, TOK_STATIC); } | TOK_VIRTUAL { $$ = new String("virtual", cur_input_file, curline, TOK_VIRTUAL); } | TOK_INLINE { $$ = new String("inline", cur_input_file, curline, TOK_INLINE); } | TOK_IMMUTABLE { $$ = new String("immutable", cur_input_file, curline, TOK_IMMUTABLE); } ; strlist_body: ident { cur_strlist->push_back($1); } | strlist_body ident { cur_strlist->push_back($2); } ; end_strlist: { $$ = cur_strlist; cur_strlist = new StrList; } ; strlist: strlist_body end_strlist { $$ = $2; } ; maybe_strlist: end_strlist | strlist ; qualified_ident_raw: ident { cur_strlist->push_back($1); } | qualified_ident_raw '.' ident { cur_strlist->push_back($3); } ; maybe_dot_star: /* empty */ { $$ = false; } | '.' '*' { $$ = true; } ; qualified_ident_nodbldot: qualified_ident_raw maybe_dot_star { if ($2) cur_strlist->push_back(new String("*", cur_input_file, curline, '*')); $$ = cur_strlist; cur_strlist = new StrList; } ; qualified_ident: TOK_2DOT { cur_strlist->push_front(new String("", cur_input_file, curline, TOK_IDENT)); } qualified_ident_nodbldot { $$ = $3; } | qualified_ident_nodbldot ; qualified_decl: qualified_ident_raw { $$.ident = cur_strlist->back(); $$.ident->retain(); cur_strlist->pop_back(); if (!cur_strlist->empty()) $$.ns = add_nspace(cur_strlist, true); else { $$.ns = cur_nspace; nspace_stack.push_front(cur_nspace); } cur_strlist = new StrList; if (!$$.ns) do_yyerror(); } ; anon_decl: { char buf[32]; snprintf(buf, 32, "_anon_%u", cur_nspace->anon++); $$.ns = cur_nspace; $$.ident = new String(buf, cur_input_file, curline, TOK_ANON); nspace_stack.push_front(cur_nspace); } ; maybe_qualified_decl: anon_decl | qualified_decl ; qualified_ids: qualified_ident { cur_idlist->push_back($1); } | qualified_ids ',' qualified_ident { cur_idlist->push_back($3); } ; qualified_idlist: qualified_ids { $$ = cur_idlist; cur_idlist = new IDList; } ; enum: TOK_ENUM maybe_qualified_decl size '{' { int bits = 32; if ($3.type == TOK_UCON) bits = $3.con.ucon; $$ = Enum::declare($2.ident, $2.ns, bits); nspace_stack.push_front(cur_nspace); cur_nspace = $$; } enuments '}' { pop_nspace(); pop_nspace(); $$ = $5; } ; more_enuments: /* empty */ | ',' enuments ; enuments: /* empty */ | ident { Enum *en = dynamic_cast(*cur_nspace); assert(en); CompiledBasicType bt = { bits: en->def.bits }; // GCC can't handle complex labelled initializers as of 3.3; // plus, the syntax gets kind of ugly. bt.flags.field.Unsigned = 1; Con con; con.type = TOK_UCON; con.con.ucon = en->next_val++; Datum::declare($1, en, bt, NULL, &con); } more_enuments ; bitfield: TOK_BITFIELD maybe_qualified_decl size '{' { int bits = 32; if ($3.type == TOK_UCON) bits = $3.con.ucon; $$ = BitField::declare($2.ident, $2.ns, bits); nspace_stack.push_front(cur_nspace); cur_nspace = $$; } maybe_bfelems '}' { pop_nspace(); pop_nspace(); $$ = $5; } ; size: /* empty */ { $$.type = TOK_NONE; } | ':' const { $$ = $2; if ($$.type == TOK_DCON) { // FIXME: support symbolic sizes yyerrorf("Symbolic sizes are currently unsupported.\n"); $$.type = TOK_INVALID; } else if (!(($$.type == TOK_UCON && $$.con.ucon > 0) || ($$.type == TOK_ICON && $$.con.icon > 0))) { yyerrorf("Sizes must be positive integers."); $$.type = TOK_INVALID; $$.con.ucon = 0; } else { // Promote signed to unsigned. $$.type = TOK_UCON; } } ; bfelems: bfelem { $$ = new SymList; $$->push_back($1); } | bfelems ',' bfelem { $1->push_back($3); } ; maybe_comma: /* empty */ | ',' ; maybe_bfelems: /* empty */ { $$ = new SymList; } | bfelems maybe_comma ; bftype: ident { CompiledBasicType bt = {}; bt.flags.field.Unsigned = 1; bt.bits = dynamic_cast(*cur_nspace)->def.bits; $$ = Datum::declare($1, cur_nspace, bt, NULL); } | basictype ident { $$ = Datum::declare($2, cur_nspace, $1, NULL); } | enum ident { StrList *strl = $1->get_fq_name(); strl->push_front(new String("")); $$ = Datum::declare($2, cur_nspace, strl, NULL); } | bitfield ident { StrList *strl = $1->get_fq_name(); strl->push_front(new String("")); $$ = Datum::declare($2, cur_nspace, strl, NULL); } ; bfelem: bftype size { $$ = $1; if ($2.type == TOK_UCON) $$->def.ucon = $2.con.ucon; else $$->def.icon = -1; } ; basictype: qualified_ident { $$ = $1; } ; type: basictype { $$.type = $1; $$.array = NULL; } | basictype '[' arraybounds ']' { $$.type = $1; $$.array = $3; } ; maybeconst: /* empty */ { $$.type = TOK_NONE; } | const { $$ = $1; } ; const: constnominus { $$ = $1; } | '-' constnominus { switch($2.type) { case TOK_UCON: yyerrorf("The constant %" PRIu64 " is too large to be negated.", $2.con.ucon); do_yyerror(); break; case TOK_ICON: $$.con.icon = -$2.con.icon; break; case TOK_FCON: $$.con.fcon = -$2.con.fcon; } $$.type = $2.type; } | TOK_FALSE { $$.type = TOK_BOOL; $$.con.ucon = 0; } | TOK_TRUE { $$.type = TOK_BOOL; $$.con.ucon = 1; } ; constnominus: TOK_ICON { $$ = $1; } | TOK_UCON { $$ = $1; } | TOK_FCON { $$ = $1; } | TOK_INVALID { $$ = $1; } | qualified_ident { assert($1); $$.type = TOK_DCON; $$.con.dcon = $1; } ; arraybounds: /* empty */ { $$ = new Array(cur_nspace); $$->set_unbounded(); } | const { $$ = new Array(cur_nspace); $$->set_bound($1, 0); $$->set_bound($1, 1); } | maybeconst TOK_3DOT maybeconst { $$ = new Array(cur_nspace); $$->set_bound($1, 0); $$->set_bound($3, 1); } ; typedef_or_alias_keyword: TOK_TYPEDEF { $$ = true; } | TOK_ALIAS { $$ = false; } ; typedef_or_alias: typedef_or_alias_keyword basictype ids { Type *t = lookup_type($2, NULL, true); BasicType *bt = dynamic_cast(t); if (bt) { // It's a basic type, so a symbolic typedef won't work. declare_basictypes(cur_nspace, $3, bt, $1); } else { declare_aliases(cur_nspace, $3, $2, $1); } } ; %% void setup_idlparse() { yylval_con = &idl_lval.con; yylval_string = &idl_lval.string; // These are kept in an initialized state so as to avoid the need // for some early actions, thus eliminating some conflicts that // would otherwise have caused things like "guid" to need to be // reserved words. cur_strlist = new StrList; cur_conlist = new ConList; cur_idlist = new IDList; }