/*********************************************************************** * $Id$ * Copyright 2009 Aplix Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Hand-crafted recursive descent parser for Web IDL grammar. ***********************************************************************/ #include <assert.h> #include <stdarg.h> #include <stdio.h> #include <string.h> #include "comment.h" #include "lex.h" #include "misc.h" #include "node.h" #include "parse.h" /*********************************************************************** * tokerrorexit : error and exit with line number from token */ static void tokerrorexit(struct tok *tok, const char *format, ...) { va_list ap; char *m; va_start(ap, format); m = vmemprintf(format, ap); if (tok->type == TOK_EOF) locerrorexit(tok->filename, tok->linenum, "at end of input: %s", m); else locerrorexit(tok->filename, tok->linenum, "at '%.*s': %s", tok->len, tok->start, m); va_end(ap); } /*********************************************************************** * lexnocomment : lex next token, discarding or storing comments * * A block comment starting with |** or |*! is a doxygen comment. * If it starts with |**< or |*!< then it refers to the previous * identifier, not the next one. (I am using | to represent / otherwise * this comment would be illegal.) * * An inline comment starting with /// or //! is a doxygen comment. * If it starts with ///< or //!< then it refers to the previous * identifier, not the next one. */ static struct tok * lexnocomment(void) { struct tok *tok; for (;;) { tok = lex(); if (tok->type != TOK_BLOCKCOMMENT && tok->type != TOK_INLINECOMMENT) break; addcomment(tok); } return tok; } /*********************************************************************** * eat : check token then read the following one * * Enter: tok struct * type = type of token expected, error given if no match * * On return, tok is updated to the following token. */ static void eat(struct tok *tok, int type) { if (tok->type != type) { const char *p; if (type < TOK_DOMString) tokerrorexit(tok, "expected '%c'", type); p = keywords; while (type != TOK_DOMString) { p += strlen(p) + 1; type--; } tokerrorexit(tok, "expected '%s'", p); } lexnocomment(); } /*********************************************************************** * setid : flag that an id attribute is required on node * * Enter: node * * node->id is set to the value of the name attribute. This makes * outputnode give it an id attribute whose value is the id attribute * of the parent if any, then "::", then node->id. */ static void setid(struct node *node) { node->id = getattr(node, "name"); } /*********************************************************************** * setidentifier : allocate 0-terminated string for identifier token * * Enter: tok = token, error given if not identifier * * Return: allocated 0-terminated string */ static char * setidentifier(struct tok *tok) { char *s; if (tok->type != TOK_IDENTIFIER) tokerrorexit(tok, "expected identifier"); s = memprintf("%.*s", tok->len, tok->start); return s; } /*********************************************************************** * setargumentname : allocate 0-terminated string for identifier token * * Enter: tok = token, error given if not identifier * * Return: allocated 0-terminated string */ static char * setargumentname(struct tok *tok) { char *s; if (tok->type != TOK_IDENTIFIER && tok->type < TOK_attribute) tokerrorexit(tok, "expected argument name"); s = memprintf("%.*s", tok->len, tok->start); return s; } /* Prototypes for funcs that have a forward reference. */ static struct node *parsetype(struct tok *tok); static struct node *parsedefaultvalue(struct tok *tok, struct node *parent); static struct node *parseuniontype(struct tok *tok); static struct node *parseargumentlist(struct tok *tok); static void parsedefinitions(struct tok *tok, struct node *parent); static struct node *parsetypesuffixstartingwitharray(struct tok *tok, struct node *node); /*********************************************************************** * parsescopedname : parse [53] ScopedName * * Enter: tok = next token * name = name of attribute to put scoped name in * ref = whether to enable enclosing of the name in <ref> in * outputwidl * * Return: node struct for new attribute * tok updated */ static struct node * parsescopedname(struct tok *tok, const char *name, int ref) { const char *start = tok->start, *end; struct node *node; unsigned int len = 0; char *s = memalloc(3); if (tok->type != TOK_IDENTIFIER) tokerrorexit(tok, "expected identifier"); s = memrealloc(s, len + tok->len + 1); memcpy(s + len, tok->start, tok->len); len += tok->len; end = tok->start + tok->len; lexnocomment(); s[len] = 0; node = newattr(name, s); if (ref) { node->start = start; node->end = end; } return node; } /*********************************************************************** * parsescopednamelist : parse [51] ScopedNameList * * Enter: tok = next token * name = name of element for scoped name list * name2 = name of element for entry in list * comment = whether to attach documentation to each name * * Return: node for list of scoped names * tok updated */ static struct node * parsescopednamelist(struct tok *tok, const char *name, const char *name2, int comment) { struct node *node = newelement(name); for (;;) { struct node *attr = parsescopedname(tok, "name", 1); struct node *n = newelement(name2); if (comment) setcommentnode(n); addnode(n, attr); addnode(node, n); if (tok->type != ',') break; lexnocomment(); } return node; } /*********************************************************************** * parsereturntype : parse [50] ReturnType * * Enter: tok = next token * * Return: node for type * tok updated */ static struct node * parsereturntype(struct tok *tok) { if (tok->type == TOK_void) { struct node *node = newelement("Type"); addnode(node, newattr("type", "void")); lexnocomment(); return node; } return parsetype(tok); } /*********************************************************************** * parseunsignedintegertype : parse [46] UnsignedIntegerType * * Enter: tok = next token (one of "unsigned", "short" or "long") * * Return: 0-terminated canonical string for the type * tok updated to just past UnsignedIntegerType */ static const char * parseunsignedintegertype(struct tok *tok) { static const char *names[] = { "short", "long", "long long", 0, "unsigned short", "unsigned long", "unsigned long long" }; enum { TYPE_SHORT, TYPE_LONG, TYPE_LONGLONG, TYPE_UNSIGNED = 4 }; int type = 0; if (tok->type == TOK_unsigned) { type = TYPE_UNSIGNED; lexnocomment(); } if (tok->type == TOK_short) { type |= TYPE_SHORT; lexnocomment(); } else if (tok->type != TOK_long) tokerrorexit(tok, "expected 'short' or 'long' after 'unsigned'"); else { type |= TYPE_LONG; lexnocomment(); if (tok->type == TOK_long) { type += TYPE_LONGLONG - TYPE_LONG; lexnocomment(); } } return names[type]; } /*********************************************************************** * parsetypesuffix : parse [44] TypeSuffix * * Enter: tok = next token * * Return: node for type * tok updated */ static struct node * parsetypesuffix(struct tok *tok, struct node *node) { if (tok->type == TOK_DOUBLEBRACKET) { struct node *typenode = node; node = newelement("Type"); addnode(node, newattr("type", "array")); addnode(node, typenode); lexnocomment(); node = parsetypesuffix(tok, node); } else if (tok->type == '?') { addnode(node, newattr("nullable", "nullable")); lexnocomment(); node = parsetypesuffixstartingwitharray(tok, node); } return node; } /*********************************************************************** * parsetypesuffixstartingwitharray : parse [44] TypeSuffixStartingWithArray * * Enter: tok = next token * * Return: node for type * tok updated */ static struct node * parsetypesuffixstartingwitharray(struct tok *tok, struct node *node) { if (tok->type == TOK_DOUBLEBRACKET) { struct node *typenode = node; node = newelement("Type"); addnode(node, newattr("type", "array")); addnode(node, typenode); lexnocomment(); node = parsetypesuffix(tok, node); } return node; } /*********************************************************************** * parseprimitiveorstringtype : parse [45] PrimitiveOrString * * Enter: tok = next token * * Return: node for type * tok updated */ static struct node * parseprimitiveorstringtype(struct tok *tok) { struct node *node; switch (tok->type) { case TOK_unsigned: case TOK_short: case TOK_long: node = newelement("Type"); addnode(node, newattr("type", parseunsignedintegertype(tok))); break; default: node = newelement("Type"); switch (tok->type) { default: tokerrorexit(tok, "expected type"); break; case TOK_unrestricted: lexnocomment(); if (tok->type == TOK_float) { addnode(node, newattr("type", "unrestricted float")); } else if (tok->type == TOK_double) { addnode(node, newattr("type", "unrestricted double")); } else { tokerrorexit(tok, "expected float or double after unrestricted"); } break; case TOK_boolean: addnode(node, newattr("type", "boolean")); break; case TOK_byte: addnode(node, newattr("type", "byte")); break; case TOK_octet: addnode(node, newattr("type", "octet")); break; case TOK_float: addnode(node, newattr("type", "float")); break; case TOK_double: addnode(node, newattr("type", "double")); break; case TOK_DOMString: addnode(node, newattr("type", "DOMString")); break; case TOK_ByteString: addnode(node, newattr("type", "ByteString")); break; case TOK_Date: addnode(node, newattr("type", "Date")); break; case TOK_RegExp: addnode(node, newattr("type", "RegExp")); break; } lexnocomment(); } return node; } /*********************************************************************** * parsenonanytype : parse NonAnyType * * Enter: tok = next token * * Return: node for type * tok updated */ static struct node * parsenonanytype(struct tok *tok) { struct node *node; switch (tok->type) { case TOK_IDENTIFIER: node = newelement("Type"); addnode(node, parsescopedname(tok, "name", 1)); node = parsetypesuffix(tok, node); break; case TOK_sequence: node = newelement("Type"); addnode(node, newattr("type", "sequence")); lexnocomment(); eat(tok, '<'); addnode(node, parsetype(tok)); eat(tok, '>'); if (tok->type == '?') { addnode(node, newattr("nullable", "nullable")); lexnocomment(); } break; case TOK_object: node = newelement("Type"); addnode(node, newattr("type", "object")); lexnocomment(); node = parsetypesuffix(tok, node); break; default: node = parseprimitiveorstringtype(tok); node = parsetypesuffix(tok, node); break; } return node; } /*********************************************************************** * parseunionmembertype: parse UnionMemberType * * Enter: tok = next token * * Return: node for type * tok updated */ static struct node * parseunionmembertype(struct tok *tok) { struct node *node; if (tok->type == TOK_any) { struct node *typenode = newelement("Type"); addnode(typenode, newattr("type", "any")); lexnocomment(); eat(tok, TOK_DOUBLEBRACKET); node = newelement("Type"); addnode(node, newattr("type", "array")); addnode(node, typenode); lexnocomment(); node = parsetypesuffix(tok, node); } else if (tok->type == '(') { node = parseuniontype(tok); } else { node = parsenonanytype(tok); } return node; } /*********************************************************************** * parseuniontype : parse UnionType * * Enter: tok = next token * * Return: node for type * tok updated */ static struct node * parseuniontype(struct tok *tok) { struct node *node; eat(tok, '('); node = newelement("Type"); addnode(node, newattr("type", "union")); if (tok->type != ')') { for (;;) { addnode(node, parseunionmembertype(tok)); if (tok->type != TOK_or) break; lexnocomment(); } } eat(tok, ')'); node = parsetypesuffix(tok, node); return node; } /*********************************************************************** * parsetype : parse [44] Type * * Enter: tok = next token * * Return: node for type * tok updated */ static struct node * parsetype(struct tok *tok) { struct node *node; if (tok->type == '(') { node = parseuniontype(tok); } else if (tok->type == TOK_any) { node = newelement("Type"); addnode(node, newattr("type", "any")); lexnocomment(); node = parsetypesuffixstartingwitharray(tok, node); } else { node = parsenonanytype(tok); } return node; } /*********************************************************************** * parseextendedattribute : parse [39] ExtendedAttribute * * Enter: tok = next token * * Return: node for extended attribute * * This parses the various forms of extended attribute, as in * rules [57] [58] [59] [60] [61]. * * This does not spot the error that you cannot have a ScopedName * and an ArgumentList. */ static struct node * parseextendedattribute(struct tok *tok) { const char *start ; struct node *node = newelement("ExtendedAttribute"); char *attrname = setidentifier(tok); addnode(node, newattr("name", attrname)); start = tok->prestart; node->wsstart = start; node->end = tok->start + tok->len; if(!strcmp(attrname, "Constructor") || !strcmp(attrname, "NamedConstructor")) { setcommentnode(node); } lexnocomment(); if (tok->type == '=') { lexnocomment(); addnode(node, parsescopedname(tok, "value", 0)); } if (tok->type == '(') { lexnocomment(); addnode(node, parseargumentlist(tok)); node->end = tok->start + tok->len; eat(tok, ')'); } return node; } /*********************************************************************** * parseextendedattributelist : parse [37] ExtendedAttributeList * * Enter: tok = next token * * Return: 0 else node for extended attribute list * tok updated if anything parsed */ static struct node * parseextendedattributelist(struct tok *tok) { struct node *node; if (tok->type != '[') return 0; node = newelement("ExtendedAttributeList"); for (;;) { lexnocomment(); addnode(node, parseextendedattribute(tok)); if (tok->type != ',') break; } if (tok->type != ']') tokerrorexit(tok, "expected ',' or ']'"); lexnocomment(); return node; } /*********************************************************************** * parseexceptionfield : parse [36] ExceptionField * * Enter: tok = next token * eal = 0 else extended attribute list node * * Return: new node for the exceptionfield * tok updated */ static struct node * parseexceptionfield(struct tok *tok, struct node *eal) { struct node *node = newelement("ExceptionField"); if (eal) addnode(node, eal); setcommentnode(node); addnode(node, parsetype(tok)); addnode(node, newattr("name", setidentifier(tok))); tok = lexnocomment(); return node; } /*********************************************************************** * parseargument : parse [31] Argument * * Enter: tok = next token * * Return: new node * * tok updated on return */ static struct node * parseargument(struct tok *tok) { struct node *node = newelement("Argument"); struct node *eal = parseextendedattributelist(tok); setcommentnode(node); if (eal) addnode(node, eal); if (tok->type == TOK_optional) { addnode(node, newattr("optional", "optional")); lexnocomment(); } addnode(node, parsetype(tok)); if (tok->type == TOK_ELLIPSIS) { addnode(node, newattr("ellipsis", "ellipsis")); lexnocomment(); } addnode(node, newattr("name", setargumentname(tok))); lexnocomment(); // Optional default value if (tok->type == '=') { tok = lexnocomment(); node = parsedefaultvalue(tok, node); } return node; } /*********************************************************************** * parseargumentlist : parse [29] ArgumentList * * Enter: tok = next token * * Return: new node for the arglist * tok updated */ static struct node * parseargumentlist(struct tok *tok) { struct node *node = newelement("ArgumentList"); /* We rely on the fact that ArgumentList is always followed by ')'. */ if (tok->type != ')') { for (;;) { addnode(node, parseargument(tok)); if (tok->type != ',') break; lexnocomment(); } } return node; } /*********************************************************************** * parseoperationrest : parse [25] OperationRest * * Enter: tok = next token * node * * Return: node * tok on terminating ';' */ static struct node * parseoperationrest(struct tok *tok, struct node *node) { if (tok->type == TOK_IDENTIFIER) { addnode(node, newattr("name", setidentifier(tok))); lexnocomment(); } eat(tok, '('); addnode(node, parseargumentlist(tok)); eat(tok, ')'); return node; } /*********************************************************************** * parsereturntypeandoperationrest: parse ReturnType OperationRest * Enter: tok = next token * eal * attrs list of attributes * Return: node * tok on terminating ';' */ static struct node * parsereturntypeandoperationrest(struct tok *tok, struct node *eal, struct node *attrs) { struct node *node = newelement("Operation"); struct node *nodeType = parsereturntype(tok); if (eal) addnode(node, eal); setcommentnode(node); addnode(node, attrs); addnode(node, nodeType); return parseoperationrest(tok, node); } /*********************************************************************** * parseiteratorrest : parse OptionalIteratorInterface * * Enter: tok = next token * node * * Return: node * tok on terminating ';' */ static struct node * parseoptionaliteratorinterface(struct tok *tok, struct node *node) { if (tok->type == '=') { lexnocomment(); addnode(node, newattr("interface", setidentifier(tok))); lexnocomment(); } return node; } /*********************************************************************** * parseoperationoriteratorrest : parse [25] OperationOrIteratorRest * * Enter: tok = next token * eal = 0 else extended attribute list node * attrs = list-of-attrs node containing attrs to add to new node * * Return: new node * tok on terminating ';' */ static struct node * parseoperationoriteratorrest(struct tok *tok, struct node *eal, struct node *attrs) { struct node *node; struct node *nodeType = parsereturntype(tok); unsigned int isIterator = 0; if (tok->type == TOK_iterator) { lexnocomment(); if (tok->type == TOK_object) { lexnocomment(); node = newelement("IteratorObject"); addnode(node, nodeType); return node; } else { node = newelement("Iterator"); isIterator = 1; } } else { node = newelement("Operation"); } if (eal) addnode(node, eal); setcommentnode(node); addnode(node, attrs); addnode(node, nodeType); if (isIterator) return parseoptionaliteratorinterface(tok, node); else return parseoperationrest(tok, node); } /*********************************************************************** * parseattribute : parse [17] Attribute * * Enter: tok = next token ("readonly" or "attribute") * eal = 0 else extended attribute list node * attrs = list-of-attrs node containing attrs to add to new node * * Return: node * tok on terminating ';' */ static struct node * parseattribute(struct tok *tok, struct node *eal, struct node *attrs) { struct node *node = newelement("Attribute"); if (eal) addnode(node, eal); setcommentnode(node); addnode(node, attrs); if (tok->type == TOK_inherit) { lexnocomment(); addnode(node, newattr("inherit", "inherit")); } if (tok->type == TOK_readonly) { lexnocomment(); addnode(node, newattr("readonly", "readonly")); } eat(tok, TOK_attribute); addnode(node, parsetype(tok)); addnode(node, newattr("name", setidentifier(tok))); lexnocomment(); return node; } /*********************************************************************** * parseserializer : parse Serializer * * Enter: tok = next token * eal * * Return: node updated with value * tok updated */ static struct node * parseserializer (struct tok *tok, struct node *eal) { struct node *nodeAttribute; struct node *node = newelement("Serializer"); if (tok->type == '=') { if (eal) addnode(node, eal); lexnocomment(); if (tok->type == TOK_IDENTIFIER) { addnode(node, newattr("attribute", setidentifier(tok))); lexnocomment(); } else if (tok->type == '{') { unsigned int done = 0; struct node *nodeMap = newelement("Map"); lexnocomment(); if (tok->type == TOK_getter) { addnode(nodeMap, newattr("pattern", "getter")); done = 1; } else if (tok->type == TOK_attribute) { addnode(nodeMap, newattr("pattern", "all")); done = 1; } else if (tok->type == TOK_inherit) { addnode(nodeMap, newattr("inherit", "inherit")); lexnocomment(); eat(tok, ','); if (tok->type == TOK_attribute) { addnode(nodeMap, newattr("pattern", "all")); done = 1; } } else if (tok->type != TOK_IDENTIFIER) { tokerrorexit(tok, "expected 'attribute', 'getter', 'inherit' or attribute identifiers in serializer map"); } if (done) { lexnocomment(); eat(tok, '}'); } else { addnode(nodeMap, newattr("pattern", "selection")); do { if (tok->type != TOK_IDENTIFIER) tokerrorexit(tok, "expected attribute identifiers in serializer map %s", tok->prestart); nodeAttribute = newelement("PatternAttribute"); addnode(nodeAttribute, newattr("name", setidentifier(tok))); addnode(nodeMap, nodeAttribute); lexnocomment(); if (tok->type == ',') lexnocomment(); } while (tok->type != '}'); eat(tok, '}'); } addnode(node, nodeMap); } else if (tok->type == '[') { struct node *nodeList = newelement("List"); lexnocomment(); if (tok->type == TOK_getter) { addnode(nodeList, newattr("pattern", "getter")); lexnocomment(); eat(tok, ']'); } else { addnode(nodeList, newattr("pattern", "selection")); do { if (tok->type != TOK_IDENTIFIER) tokerrorexit(tok, "expected attribute identifiers in serializer list"); nodeAttribute = newelement("PatternAttribute"); addnode(nodeAttribute, newattr("name", setidentifier(tok))); addnode(nodeList, nodeAttribute); lexnocomment(); if (tok->type == ',') lexnocomment(); } while (tok->type != ']'); eat(tok, ']'); } addnode(node, nodeList); } else { tokerrorexit(tok, "Expected '{', '[' or an attribute identifier in the serializer declaration"); } return node; } else { if (eal) addnode(node, eal); return node; } } /*********************************************************************** * parseattributeoroperationoriterator : parse [15] AttributeOrOperationOrIterator * * Enter: tok = next token * eal = 0 else extended attribute list node * * Return: new node * tok on terminating ';' */ static struct node * parseattributeoroperationoriterator(struct tok *tok, struct node *eal) { int alreadyseen ; struct node *attrs = newattrlist(); if (tok->type == TOK_serializer) { lexnocomment(); if (tok->type == '=' || tok->type ==';') { return parseserializer(tok, eal); } else { addnode(attrs, newattr("serializer", "serializer")); return parsereturntypeandoperationrest(tok, eal, attrs); } } if (tok->type == TOK_stringifier) { addnode(attrs, newattr("stringifier", "stringifier")); lexnocomment(); if (tok->type == ';') { struct node *node = newelement("Stringifier"); if (eal) addnode(node, eal); return node; } } if (tok->type == TOK_static) { lexnocomment(); addnode(attrs, newattr("static", "static")); } if (tok->type == TOK_inherit || tok->type == TOK_readonly || tok->type == TOK_attribute) return parseattribute(tok, eal, attrs); if (!nodeisempty(attrs)) return parsereturntypeandoperationrest(tok, eal, attrs); alreadyseen = 0; for (;;) { static const int t[] = { TOK_getter, TOK_setter, TOK_creator, TOK_deleter, TOK_legacycaller, 0 }; const int *tt = t; char *s; while (*tt && *tt != tok->type) tt++; if (!*tt) break; s = memprintf("%.*s", tok->len, tok->start); if (alreadyseen & (1 << (tt - t))) tokerrorexit(tok, "'%s' qualifier cannot be repeated", s); alreadyseen |= 1 << (tt - t); addnode(attrs, newattr(s, s)); lexnocomment(); } if (!nodeisempty(attrs)) return parsereturntypeandoperationrest(tok, eal, attrs); else return parseoperationoriteratorrest(tok, eal, attrs); } /*********************************************************************** * parseconstexpr : parse ConstValue * * Enter: tok = next token * node * * Return: node updated with value * tok updated */ static struct node * parseconstexpr (struct tok *tok, struct node *node) { char *s; switch(tok->type) { case TOK_true: case TOK_false: case TOK_minusinfinity: case TOK_INTEGER: case TOK_FLOAT: case TOK_null: case TOK_infinity: case TOK_NaN: break; default: tokerrorexit(tok, "expected constant value"); break; } s = memalloc(tok->len + 1); memcpy(s, tok->start, tok->len); s[tok->len] = 0; if (tok->type != TOK_STRING) { addnode(node, newattr("value", s)); } else { addnode(node, newattr("stringvalue", s)); } lexnocomment(); return node; } /*********************************************************************** * parsedefaultvalue : parse DefaultValue * * Enter: tok = next token * node * * Return: node updated with value * tok updated */ static struct node * parsedefaultvalue (struct tok *tok, struct node *node) { char *s; if (tok->type == TOK_STRING) { s = memalloc(tok->len + 1); memcpy(s, tok->start, tok->len); s[tok->len] = 0; addnode(node, newattr("stringvalue", s)); lexnocomment(); return node; } else { return parseconstexpr(tok, node); } } /*********************************************************************** * parsedictionarymember : parse DictionaryMember * * Enter: tok = next token * eal = 0 else extended attribute list node * * Return: new node * tok on terminating ';' */ static struct node * parsedictionarymember(struct tok *tok, struct node *eal) { struct node *node = newelement("DictionaryMember"); if (eal) addnode(node, eal); setcommentnode(node); addnode(node, parsetype(tok)); addnode(node, newattr("name", setidentifier(tok))); tok = lexnocomment(); // Optional value if (tok->type == '=') { tok = lexnocomment(); node = parsedefaultvalue(tok, node); } return node; } /*********************************************************************** * parseconst : parse [12] Const * * Enter: tok = next token, known to be TOK_const * eal = 0 else extended attribute list node * * Return: new node for the const * tok on terminating ';' */ static struct node * parseconst(struct tok *tok, struct node *eal) { struct node *node = newelement("Const"); setcommentnode(node); if (eal) addnode(node, eal); tok = lexnocomment(); switch(tok->type) { case TOK_boolean: case TOK_byte: case TOK_octet: case TOK_float: case TOK_double: case TOK_unsigned: case TOK_unrestricted: case TOK_short: case TOK_long: addnode(node, parsetype(tok)); break; default: tokerrorexit(tok, "expected acceptable constant type"); break; } addnode(node, newattr("name", setidentifier(tok))); tok = lexnocomment(); eat(tok, '='); node = parseconstexpr(tok, node); return node; } /*********************************************************************** * parseimplementsstatement : parse [11] ImplementsStatement * * Enter: tok = next token, known to be :: or TOK_IDENTIFIER * eal = 0 else extended attribute list node * * Return: new node for the typedef * tok updated to the terminating ';' */ static struct node * parseimplementsstatement(struct tok *tok, struct node *eal) { struct node *node = newelement("Implements"); setcommentnode(node); if (eal) addnode(node, eal); addnode(node, parsescopedname(tok, "name1", 1)); eat(tok, TOK_implements); addnode(node, parsescopedname(tok, "name2", 1)); return node; } /*********************************************************************** * parsetypedef : parse [10] Typedef * * Enter: tok = next token, known to be TOK_typedef * eal = 0 else extended attribute list node * * Return: new node for the typedef * tok updated to the terminating ';' */ static struct node * parsetypedef(struct tok *tok, struct node *eal) { struct node *ealtype; struct node *typenode; struct node *node = newelement("Typedef"); setcommentnode(node); if (eal) addnode(node, eal); tok = lexnocomment(); ealtype = parseextendedattributelist(tok); typenode = parsetype(tok); if (ealtype) addnode(typenode, ealtype); addnode(node, typenode); addnode(node, newattr("name", setidentifier(tok))); tok = lexnocomment(); return node; } /*********************************************************************** * parseexception : parse [8] Exception * * Enter: tok = next token, known to be TOK_exception * eal = 0 else extended attribute list node * * Return: new node for the exception * tok updated to the terminating ';' */ static struct node * parseexception(struct tok *tok, struct node *eal) { struct node *node = newelement("Exception"); setcommentnode(node); if (eal) addnode(node, eal); tok = lexnocomment(); addnode(node, newattr("name", setidentifier(tok))); lexnocomment(); if (tok->type == ':') { lexnocomment(); addnode(node, parsescopednamelist(tok, "ExceptionInheritance", "Name", 1)); } eat(tok, '{'); while (tok->type != '}') { const char *start = tok->prestart; struct node *node2; struct node *eal = parseextendedattributelist(tok); if (tok->type == TOK_const) node2 = parseconst(tok, eal); else node2 = parseexceptionfield(tok, eal); addnode(node, node2); node2->wsstart = start; node2->end = tok->start + tok->len; setid(node2); eat(tok, ';'); } lexnocomment(); return node; } /*********************************************************************** * parseinterface : parse [4] Interface * * Enter: tok = next token, known to be TOK_interface * eal = 0 else extended attribute list node * * Return: new node for the interface * tok updated to the terminating ';' */ static struct node * parseinterface(struct tok *tok, struct node *eal) { struct node *node = newelement("Interface"); if (eal) addnode(node, eal); setcommentnode(node); tok = lexnocomment(); addnode(node, newattr("name", setidentifier(tok))); tok = lexnocomment(); if (tok->type == ':') { lexnocomment(); addnode(node, parsescopednamelist(tok, "InterfaceInheritance", "Name", 1)); } eat(tok, '{'); while (tok->type != '}') { const char *start = tok->prestart; struct node *eal = parseextendedattributelist(tok); struct node *node2; if (tok->type == TOK_const) addnode(node, node2 = parseconst(tok, eal)); else addnode(node, node2 = parseattributeoroperationoriterator(tok, eal)); node2->wsstart = start; node2->end = tok->start + tok->len; setid(node2); eat(tok, ';'); } lexnocomment(); return node; } /*********************************************************************** * parsecallback : parse Callback * * Enter: tok = next token, known to be TOK_dictionary * eal = 0 else extended attribute list node * * Return: new node for the enum * tok updated to the terminating ';' */ static struct node * parsecallback(struct tok *tok, struct node *eal) { struct node *node; if (tok->type == TOK_interface) { node = parseinterface(tok, eal); addnode(node, newattr("callback", "callback")); } else { node = newelement("Callback"); if (eal) addnode(node, eal); setcommentnode(node); addnode(node, newattr("name", setidentifier(tok))); tok = lexnocomment(); eat(tok, '='); addnode(node, parsereturntype(tok)); eat(tok, '('); addnode(node, parseargumentlist(tok)); eat(tok, ')'); } return node; } /*********************************************************************** * parsedictionary : parse Dictionary * * Enter: tok = next token, known to be TOK_dictionary * eal = 0 else extended attribute list node * * Return: new node for the dictionary * tok updated to the terminating ';' */ static struct node * parsedictionary(struct tok *tok, struct node *eal) { struct node *node = newelement("Dictionary"); if (eal) addnode(node, eal); setcommentnode(node); tok = lexnocomment(); addnode(node, newattr("name", setidentifier(tok))); tok = lexnocomment(); if (tok->type == ':') { lexnocomment(); addnode(node, parsescopednamelist(tok, "DictionaryInheritance", "Name", 1)); } eat(tok, '{'); while (tok->type != '}') { const char *start = tok->prestart; struct node *eal = parseextendedattributelist(tok); struct node *node2; if (tok->type == TOK_const) addnode(node, node2 = parseconst(tok, eal)); else addnode(node, node2 = parsedictionarymember(tok, eal)); node2->wsstart = start; node2->end = tok->start + tok->len; setid(node2); eat(tok, ';'); } lexnocomment(); return node; } /*********************************************************************** * parseenum : parse Enum * * Enter: tok = next token, known to be TOK_dictionary * eal = 0 else extended attribute list node * * Return: new node for the enum * tok updated to the terminating ';' */ static struct node * parseenum(struct tok *tok, struct node *eal) { char *s; struct node *node = newelement("Enum"); if (eal) addnode(node, eal); setcommentnode(node); tok = lexnocomment(); addnode(node, newattr("name", setidentifier(tok))); tok = lexnocomment(); eat(tok, '{'); while (tok->type != '}') { if (tok->type == TOK_STRING) { const char *start = tok->prestart; struct node *node2 = newelement("EnumValue"); setcommentnode(node2); s = memalloc(tok->len + 1); memcpy(s, tok->start, tok->len); s[tok->len] = 0; addnode(node2, newattr("stringvalue", s)); node2->wsstart = start; node2->end = tok->start + tok->len; setid(node2); addnode(node, node2); } else { tokerrorexit(tok, "expected string in enum"); } lexnocomment(); if (tok->type == ',') { lexnocomment(); } } eat(tok, '}'); return node; } /*********************************************************************** * parsedefinitions : parse [1] Definitions * * Enter: tok = next token * parent = parent node to add definitions to * * On return, tok has been updated. */ static void parsedefinitions(struct tok *tok, struct node *parent) { parent->wsstart = tok->prestart; for (;;) { const char *wsstart = tok->prestart; struct node *eal = parseextendedattributelist(tok); struct node *node; switch (tok->type) { case TOK_partial: eat(tok, TOK_partial); if (tok->type == TOK_dictionary) { node = parsedictionary(tok, eal); } else { node = parseinterface(tok, eal); } addnode(node, newattr("partial", "partial")); break; case TOK_interface: node = parseinterface(tok, eal); break; case TOK_callback: eat(tok, TOK_callback); node = parsecallback(tok, eal); break; case TOK_dictionary: node = parsedictionary(tok, eal); break; case TOK_enum: node = parseenum(tok, eal); break; case TOK_exception: node = parseexception(tok, eal); break; case TOK_typedef: node = parsetypedef(tok, eal); break; case TOK_IDENTIFIER: node = parseimplementsstatement(tok, eal); break; default: if (eal) tokerrorexit(tok, "expected definition after extended attribute list"); node = 0; break; } if (!node) break; node->wsstart = wsstart; node->end = tok->start + tok->len; eat(tok, ';'); addnode(parent, node); setid(node); parent->end = node->end; } } /*********************************************************************** * parse * * Return: root element containing (possibly empty) list of definitions */ struct node * parse(void) { struct tok *tok; struct node *root = newelement("Definitions"); setcommentnode(root); tok = lexnocomment(); parsedefinitions(tok, root); if (tok->type != TOK_EOF) tokerrorexit(tok, "expected end of input"); reversechildren(root); return root; }