void ParseParamList() { if(Token!=TOK_CLOSE) { ParseTypeAndName(); while(Token==TOK_COMMA) { Match(TOK_COMMA); if(Token==TOK_ELIPSIS) { Match(); break; } else { ParseTypeAndName(); } } } }
/* * Parse an operation definition. * * Operation ::= %operation { OperFlag } Type [ClassName ] IDENTIFIER * '(' [ Params ] ')' [ '=' LITERAL_CODE ] [ ';' ] * OperFlag ::= %virtual | %inline * ClassName ::= IDENTIFIER "::" * Params ::= Param { ',' Param } * Param ::= TypeAndName | '[' TypeAndName ']' */ static void ParseOperation(TreeCCContext *context) { TreeCCInput *input = context->input; int flags; char *returnType; char *name; char *className; char *defValue; TreeCCParam *params = 0; TreeCCParam *lastParam = 0; TreeCCParam *newParam; int numTriggers = 0; char *paramType; char *paramName; int paramFlags; TreeCCOperation *oper; char *filename; long linenum; TreeCCNode *typeNode; /* Skip the "%operation" keyword */ TreeCCNextToken(input); /* Parse the operation flags */ flags = 0; for(;;) { if(input->token == TREECC_TOKEN_VIRTUAL) { flags |= TREECC_OPER_VIRTUAL; TreeCCNextToken(input); } else if(input->token == TREECC_TOKEN_INLINE) { flags |= TREECC_OPER_INLINE; TreeCCNextToken(input); } else if(input->token == TREECC_TOKEN_SPLIT) { flags |= TREECC_OPER_SPLIT; TreeCCNextToken(input); } else { break; } } if((flags & TREECC_OPER_VIRTUAL) != 0 && (flags & TREECC_OPER_INLINE) != 0) { TreeCCError(input, "`virtual' and `inline' cannot be used together"); flags &= ~TREECC_OPER_INLINE; } /* Parse the return type and name of the operation */ ParseTypeAndName(input, &returnType, &name); filename = input->filename; linenum = input->linenum; if(!returnType || !name) { if(returnType) { TreeCCError(input, "operation name expected"); } else { TreeCCError(input, "operation return type expected"); } if(returnType) { free(returnType); } if(name) { free(name); } return; } /* If the next token is "::", then "name" is actually the class name and we need to get the real operation name */ if(input->token == TREECC_TOKEN_COLON_COLON) { className = name; TreeCCNextToken(input); if(input->token != TREECC_TOKEN_IDENTIFIER) { TreeCCError(input, "operation name expected"); free(returnType); free(className); return; } name = TreeCCValue(input); TreeCCNextToken(input); } else { className = 0; } /* If the operation is non-virtual, and the language is C#, then we must have a class name which is different from the operation name. This is necessary because of a "feature" in the design of C# and Microsoft's compilers */ if((flags & TREECC_OPER_VIRTUAL) == 0 && context->language == TREECC_LANG_CSHARP) { if(!className) { TreeCCErrorOnLine(input, filename, linenum, "C# requires that a class name be specified"); } else if(!strcmp(className, name)) { TreeCCErrorOnLine(input, filename, linenum, "C# requires different class and operation names"); } } /* Parse the parameter list for the operation */ if(input->token == TREECC_TOKEN_LPAREN) { TreeCCNextToken(input); while(input->token == TREECC_TOKEN_IDENTIFIER || input->token == TREECC_TOKEN_LSQUARE) { /* Parse the parameter information */ if(input->token == TREECC_TOKEN_IDENTIFIER) { paramFlags = 0; ParseTypeAndName(input, ¶mType, ¶mName); } else { paramFlags = TREECC_PARAM_TRIGGER; TreeCCNextToken(input); ParseTypeAndName(input, ¶mType, ¶mName); if(input->token == TREECC_TOKEN_RSQUARE) { TreeCCNextToken(input); } else { TreeCCError(input, "`]' expected"); } ++numTriggers; } /* Create the parameter block */ if(!paramType) { TreeCCError(input, "parameter declaration expected"); } else if(!strcmp(paramType, "void")) { /* Ignore "void" parameters */ free(paramType); if(paramName) { free(paramName); } } else { newParam = (TreeCCParam *)malloc(sizeof(TreeCCParam)); if(!newParam) { TreeCCOutOfMemory(input); } newParam->name = paramName; newParam->type = paramType; newParam->flags = paramFlags; newParam->size = 0; newParam->next = 0; if(lastParam) { lastParam->next = newParam; } else { params = newParam; } lastParam = newParam; if((paramFlags & TREECC_PARAM_TRIGGER) != 0) { if((typeNode = TreeCCNodeFindByType(context, paramType)) == 0) { TreeCCError(input, "`%s' is not a valid trigger type", paramType); } else { ValidateSuffixes(context, paramType, typeNode, input->filename, input->linenum); } } } /* Skip the comma and go on to the next parameter */ if(input->token == TREECC_TOKEN_COMMA) { TreeCCNextToken(input); if(input->token != TREECC_TOKEN_IDENTIFIER && input->token != TREECC_TOKEN_LSQUARE) { TreeCCError(input, "parameter declaration expected"); } } else { break; } } if(input->token == TREECC_TOKEN_RPAREN) { TreeCCNextToken(input); } else { TreeCCError(input, "`)' expected"); } } else { TreeCCError(input, "`(' expected"); } /* Recognise the default value, if present */ if(input->token == TREECC_TOKEN_EQUALS) { TreeCCNextToken(input); if(input->token == TREECC_TOKEN_LITERAL_CODE) { defValue = TreeCCValue(input); TreeCCNextToken(input); } else { TreeCCError(input, "default value expected"); defValue = 0; } } else { defValue = 0; if((flags & TREECC_OPER_VIRTUAL) == 0 && strcmp(returnType, "void") != 0) { TreeCCError(input, "default value required"); } } /* Recognise an optional semi-colon */ if(input->token == TREECC_TOKEN_SEMI) { TreeCCNextToken(input); } /* Set a default trigger parameter if necessary */ if(numTriggers == 0 && params != 0) { numTriggers = 1; params->flags |= TREECC_PARAM_TRIGGER; if((typeNode = TreeCCNodeFindByType(context, params->type)) == 0) { TreeCCErrorOnLine(input, filename, linenum, "`%s' is not a valid trigger type", params->type); } else { ValidateSuffixes(context, params->type, typeNode, filename, linenum); } } /* If the operation is virtual, then check that the first parameter is the sole trigger for the operation */ if((flags & TREECC_OPER_VIRTUAL) != 0) { if(!params || numTriggers != 1 || (params->flags & TREECC_PARAM_TRIGGER) == 0) { if(!params) { TreeCCError(input, "virtual operations must have at least one parameter"); } else { TreeCCError(input, "the first parameter of a virtual must be the trigger"); } flags &= ~TREECC_OPER_VIRTUAL; } else if((typeNode = TreeCCNodeFindByType(context, params->type)) == 0) { flags &= ~TREECC_OPER_VIRTUAL; } else if((typeNode->flags & TREECC_NODE_ENUM) != 0) { TreeCCError(input, "cannot use enumerated types as triggers for virtual operations"); flags &= ~TREECC_OPER_VIRTUAL; } } /* See if we already have an operation with this name */ oper = TreeCCOperationFind(context, name); if(oper != 0) { TreeCCErrorOnLine(input, filename, linenum, "operation `%s' is already declared", name); TreeCCErrorOnLine(input, oper->filename, oper->linenum, "previous declaration here"); free(returnType); free(name); if(className) { free(className); } if(defValue) { free(defValue); } while(params != 0) { newParam = params->next; if(params->name) { free(params->name); } if(params->type) { free(params->type); } free(params); params = newParam; } return; } /* Create the operation */ oper = TreeCCOperationCreate(context, returnType, name, className, defValue, params, flags, numTriggers, filename, linenum); /* If the operation is virtual, then attach it to the node type */ if((flags & TREECC_OPER_VIRTUAL) != 0) { TreeCCNodeAddVirtual (context, TreeCCNodeFindByType(context, params->type), oper); } }
/* * Parse a node definition. * * Node ::= %node IDENTIFIER [ IDENTIFIER ] { NodeFlag } [ "=" Fields ] * NodeFlag ::= %abstract | %typedef * Fields ::= "{" { Field } "}" * Field ::= [ %nocreate ] TypeAndName [ "=" LITERAL_CODE ] ";" */ static void ParseNode(TreeCCContext *context) { TreeCCInput *input = context->input; char *name; char *parent; int flags; long linenum; TreeCCNode *node; char *fieldName; char *fieldType; char *fieldValue; /* Skip the "%node" keyword */ TreeCCNextToken(input); linenum = input->linenum; /* We need an identifier for the node that is being declared */ if(input->token != TREECC_TOKEN_IDENTIFIER) { /* Report an error for the identifier */ TreeCCError(input, "identifier expected"); return; } name = TreeCCValue(input); TreeCCNextToken(input); /* Get the name of the parent */ if(input->token == TREECC_TOKEN_IDENTIFIER) { parent = TreeCCValue(input); TreeCCNextToken(input); } else { parent = 0; } /* Parse the node definition flags */ flags = 0; for(;;) { if(input->token == TREECC_TOKEN_ABSTRACT) { flags |= TREECC_NODE_ABSTRACT; TreeCCNextToken(input); } else if(input->token == TREECC_TOKEN_TYPEDEF) { flags |= TREECC_NODE_TYPEDEF; TreeCCNextToken(input); } else { break; } } /* Create the node */ node = TreeCCNodeCreate(context, linenum, name, parent, flags); /* If we have a field definition block, then read it */ if(input->token == TREECC_TOKEN_EQUALS) { input->parseLiteral = 0; TreeCCNextToken(input); input->parseLiteral = 1; if(input->token == TREECC_TOKEN_LBRACE) { /* Skip the "{" token */ TreeCCNextToken(input); /* Process the field definitions */ while(input->token != TREECC_TOKEN_RBRACE && input->token != TREECC_TOKEN_EOF) { /* Skip spurious semi-colons in the field definition */ if(input->token == TREECC_TOKEN_SEMI) { TreeCCNextToken(input); continue; } /* Process field definition flags */ if(input->token == TREECC_TOKEN_NOCREATE) { flags = TREECC_FIELD_NOCREATE; TreeCCNextToken(input); } else { flags = 0; } /* Parse the field type and name */ ParseTypeAndName(input, &fieldType, &fieldName); if(fieldType && fieldName) { if(input->token == TREECC_TOKEN_EQUALS) { TreeCCNextToken(input); if(input->token == TREECC_TOKEN_LITERAL_CODE) { fieldValue = TreeCCValue(input); TreeCCNextToken(input); } else { TreeCCError (input, "literal code constant expected"); fieldValue = 0; } } else { fieldValue = 0; } if(fieldValue && (flags & TREECC_FIELD_NOCREATE) == 0) { TreeCCError(input, "default values can only be specified for " "`%%nocreate' fields"); } TreeCCFieldCreate(context, node, fieldName, fieldType, fieldValue, flags); } else { if(fieldType) { TreeCCError(input, "field name expected"); } else { TreeCCError(input, "field declaration expected"); } if(fieldType) { free(fieldType); } if(fieldName) { free(fieldName); } /* Attempt to recover from the error */ while(input->token != TREECC_TOKEN_SEMI && input->token != TREECC_TOKEN_RBRACE && input->token != TREECC_TOKEN_IDENTIFIER && input->token != TREECC_TOKEN_EOF) { TreeCCNextToken(input); } } /* Recognize the ";" at the end of the field */ if(input->token == TREECC_TOKEN_SEMI) { TreeCCNextToken(input); } else { /* Attempt to recover from the lack of a semi-colon */ TreeCCError(input, "`;' expected"); if(input->token != TREECC_TOKEN_IDENTIFIER && input->token != TREECC_TOKEN_RBRACE) { TreeCCNextToken(input); } } } /* Skip the "}" token */ TreeCCNextToken(input); } else { TreeCCError(input, "field definition block expected"); } } }