| /*********************************************************************** |
| * $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; |
| } |
| |