/* pfParse build up parse tree from token stream. */ /* Copyright 2005-6 Jim Kent. All rights reserved. */ /* This is a pretty standard recursive descent parser. The expression * parsing is handled with a function at each operator precedence level * that handles all the operators at that level, and which calls the * function that * handles operators at the next higher expression level * for input. The highest precedence level also looks for parenthesis, * and if it finds them, then calls back to the lowest level to handle * what's inside the parenthesis. * * At the statement level, all statements begin with a key word. * If no key word is found then the statement is passed off to the * expression level. The "key word" '{' indicates the start of a * block of code, and works similarly to the parenthesis, but at * the statement rather than the expression level. * * The trickiest thing here is the handling of variable declarations. * Comma separated lists of variable declarations can occur in a * number of places - in the input and output declarations of a * function, in declaring member variables of a class, and in * handling normal variable declarations. The grammar for a variable * declaration list should be: * varDecList: typedList * varDecList ',' typedList * typedList: qualifiedType nameList * qualifiedType: qualifier typeExpression * qualifier: 'static' | 'local' | 'readable' | 'global' * typeExpression: typeName * array '[' expression ']' 'of' typeExpression * array 'of' typeExpression * dir 'of' typeExpression * nameList: name * nameList ',' name * typeName: name * module '.' name * Currently it's not exactly this, but it should be soon. * * A problem the compiler faces is how does it tell a type name * from any other name, particularly since variables and types * can both user the same name. In particular if 'obj' is a type * name then you can declare a variable named obj of type obj as * so: * obj obj; * The only hope of resolving this lies in the fact that a declaration * is the only place in the language where two names are allowed * together without an intervening operator. Unfortunately * the more general form: * name, ..., name * the typeExpression could have generated quite an elaborate parse * tree before it figures out that it really wasn't a type expression * after all, but perhaps something like: * obj = 10; * that was being parsed. The current parse is allowed as much * lookahead into the token stream as it wants, but I'm reluctant * to let it back-track as well. * * The current solution to this is to put variable declarations * at the level of precedence right under the = operations, which * in turn are right under tuple. The tuple is then responsible * for seeing if the first item it sees is a variable declaration, * and if so, making sure all of the others are compatible with * being a var-decs too. */ #include "common.h" #include "hash.h" #include "dystring.h" #include "localmem.h" #include "pfType.h" #include "pfScope.h" #include "pfToken.h" #include "pfCompile.h" #include "pfParse.h" struct pfParse *pfParseStatement(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope); struct pfParse *pfParseExpression(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope); static struct pfParse *parseOf(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope); /* Parse out name of name . */ static void syntaxError(struct pfToken *tok, int code) /* Complain about syntax error and quit. */ { errAt(tok, "Syntax error #%d", code); } static void eatSemi(struct pfToken **pTokList) /* Make sure there's a semicolon, and eat it. */ { struct pfToken *tok = *pTokList; if (tok->type != '}') { if (tok->type != ';') expectingGot(";", tok); tok = tok->next; *pTokList = tok; } } struct pfParse *pfParseNew(enum pfParseType type, struct pfToken *tok, struct pfParse *parent, struct pfScope *scope) /* Return new parse node. It's up to caller to fill in * children later. */ { struct pfParse *pp; AllocVar(pp); pp->type = type; pp->tok = tok; pp->parent = parent; pp->scope = scope; return pp; } boolean pfParseIsInside(struct pfParse *outside, struct pfParse *inside) /* Return TRUE if inside is a child of outside. */ { while (inside != NULL) { if (inside == outside) return TRUE; inside = inside->parent; } return FALSE; } boolean pfParseIsConst(struct pfParse *pp) /* Return TRUE if pp is a simple constant. */ { switch (pp->type) { case pptConstBit: case pptConstByte: case pptConstChar: case pptConstShort: case pptConstInt: case pptConstLong: case pptConstFloat: case pptConstDouble: case pptConstString: return TRUE; default: return FALSE; } } void pfParsePutChildrenInPlaceOfSelf(struct pfParse **pPp) /* Replace self with children. */ { struct pfParse *pp = *pPp; if (pp->children == NULL) *pPp = pp->next; else { struct pfParse *lastChild = slLastEl(pp->children); lastChild->next = pp->next; *pPp = pp->children; } } struct pfParse *pfParseEnclosingFunction(struct pfParse *pp) /* Find enclosing function if any. */ { for (pp = pp->parent; pp != NULL; pp = pp->parent) { if (pp->type == pptToDec || pp->type == pptFlowDec) { return pp; } } return NULL; } struct pfParse *pfParseEnclosingClass(struct pfParse *pp) /* Find enclosing class if any. It treats self as enclosing self. */ { for (pp = pp->parent; pp != NULL; pp = pp->parent) { if (pp->type == pptClass) { return pp; } } return NULL; } struct pfParse *emptyTuple(struct pfParse *parent, struct pfToken *tok, struct pfScope *scope) /* Return empty tuple. */ { return pfParseNew(pptTuple, tok, parent, scope); } struct pfParse *emptyStatement(struct pfParse *parent, struct pfToken *tok, struct pfScope *scope) /* Return no-op statement. */ { return pfParseNew(pptNop, tok, parent, scope); } struct pfParse *pfSingleTuple(struct pfParse *parent, struct pfToken *tok, struct pfParse *single) /* Wrap tuple around single and return it. . */ { struct pfParse *tuple = pfParseNew(pptTuple, tok, parent, parent->scope); tuple->children = single; tuple->next = single->next; single->parent = tuple; single->next = NULL; return tuple; } static void skipRequiredWord(char *name, struct pfToken **pTokList) /* Make sure next token matches name, and then skip it. */ { struct pfToken *tok = *pTokList; if (tok->type < pftReservedWordStart || !sameString(tok->val.s, name)) expectingGot(name, tok); *pTokList = tok->next; } static void skipRequiredCharType(enum pfTokType type, struct pfToken **pTokList) /* Make sure next token is of given type simple char type, and then skip it. */ { struct pfToken *tok = *pTokList; if (tok->type != type) { char sym[2]; sym[0] = type; sym[1] = 0; expectingGot(sym, tok); } *pTokList = tok->next; } static struct pfParse *parseNameUse(struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Make sure have a name, and create a varUse type node * based on it. */ { struct pfToken *tok = *pTokList; struct pfParse *pp = pfParseNew(pptNameUse, tok, parent, scope); if (tok->type != pftName) { errAt(tok, "Expecting variable name."); } pp->name = tok->val.s; *pTokList = tok->next; return pp; } static struct pfParse *parseInputTuple(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope, struct pfToken **pCloseParenTok) /* Parse input/output tuple. Similar to a regular tuple, but * () can be an empty tuple, and (x) is interpreted as a tuple * of one rather than just x. */ { struct pfToken *tok = *pTokList; struct pfParse *tuple; skipRequiredCharType('(', &tok); if (tok->type == ')') { *pCloseParenTok = tok; tuple = emptyTuple(parent, tok, scope); tok = tok->next; } else { tuple = pfParseExpression(pfc, parent, &tok, scope); if (tuple->type != pptTuple) tuple = pfSingleTuple(parent, tok, tuple); *pCloseParenTok = tok; skipRequiredCharType(')', &tok); } tuple->type = pptTypeTuple; *pTokList = tok; return tuple; } static void parseFunctionIo(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope, struct pfParse **retInput, struct pfParse **retOutput) /* Parse out something like (int x, int y) into (int z) * into input and output, both of which will be (possibly empty) * pptTuples. */ { struct pfToken *tok = *pTokList; struct pfParse *input, *output = NULL; struct pfToken *outToken; input = parseInputTuple(pfc, parent, &tok, scope, &outToken); if (tok->type == pftInto) { boolean gotParen = FALSE; tok = tok->next; outToken = tok; /* To handle function pointer declarations need to parse * out parens here rather than leaving it to parse expression */ if (tok->type == '(') { gotParen = TRUE; tok = tok->next; } output = pfParseExpression(pfc, parent, &tok, scope); if (gotParen) skipRequiredCharType(')', &tok); if (output->type != pptTuple) output = pfSingleTuple(parent, outToken, output); } else { output = emptyTuple(parent, outToken, scope); } output->type = pptTypeTuple; *pTokList = tok; *retInput = input; *retOutput = output; } static void decsToFormal(struct pfParse *pp) /* Convert pptVarInits to pptFormalParameters on children */ { for (pp = pp->children; pp != NULL; pp = pp->next) { if (pp->type != pptVarInit) errAt(pp->tok, "expecting formal parameter"); pp->type = pptFormalParameter; } } static struct pfParse *parseShortFuncType(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse out something like: * to (int x,y=2) into (int f) * Into a parse tree that looks like * pptTypeToPt (or pptTypeFlowPt) * pptTypeTuple * pptFormalParameter x * pptTypeName int * pptSymName x * pptFormalParameter y * pptTypeName int * pptSymName y * pptConstUse 2 * pptTypeTuple * pptFormalParameter f * pptTypeName int * pptSymName f */ { struct pfToken *tok = *pTokList; enum pfParseType ppt = (tok->type == pftTo ? pptTypeToPt : pptTypeFlowPt); struct pfScope *funcScope = pfScopeNew(pfc, scope, 0, NULL); struct pfParse *pp = pfParseNew(ppt, tok, parent, funcScope); struct pfParse *input, *output; tok = tok->next; /* Skip over 'to' or 'flow' */ parseFunctionIo(pfc, pp, &tok, funcScope, &input, &output); decsToFormal(input); decsToFormal(output); pp->children = input; input->next = output; *pTokList = tok; return pp; } static struct pfParse *constUse(struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Create constant use */ { struct pfToken *tok = *pTokList; struct pfParse *pp = pfParseNew(pptConstUse, tok, parent, scope); *pTokList = tok->next; return pp; } static struct pfParse *parseAtom(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse atomic expression - const, variable, or parenthesized expression. */ { struct pfToken *tok = *pTokList; struct pfParse *pp = NULL; switch (tok->type) { case pftName: pp = parseNameUse(parent, &tok, scope); break; case pftString: case pftSubstitute: case pftInt: case pftLong: case pftFloat: case pftNil: pp = constUse(parent, &tok, scope); break; case '(': { struct pfToken *startTok = tok; tok = tok->next; if (tok->type == ')') { pp = emptyTuple(parent, tok, scope); } else { pp = pfParseExpression(pfc, parent, &tok, scope); if (tok->type != ')') errAt(tok, "Unclosed parenthesis."); } tok = tok->next; break; } default: syntaxError(tok, 1); break; } *pTokList = tok; return pp; } static struct pfParse *parseArrayCallDot(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse out this.that()[].other[]().more and the like. */ { struct pfParse *pp = parseAtom(pfc, parent, pTokList, scope); struct pfToken *tok = *pTokList; while (tok->type == '(' || tok->type == '[' || tok->type == '.') { enum pfTokType tokType = tok->type; struct pfParse *left = pp, *right; tok = tok->next; switch (tokType) { case '(': pp = pfParseNew(pptCall, tok, parent, scope); if (tok->type == ')') right = emptyTuple(pp, tok, scope); else { right = pfParseExpression(pfc, pp, &tok, scope); if (right->type != pptTuple) { struct pfParse *tuple = pfSingleTuple(pp, tok, right); right->parent = tuple; right = tuple; } } skipRequiredCharType(')', &tok); break; case '[': pp = pfParseNew(pptIndex, tok, parent, scope); right = pfParseExpression(pfc, pp, &tok, scope); skipRequiredCharType(']', &tok); break; case '.': pp = pfParseNew(pptDot, tok, parent, scope); right = parseAtom(pfc, pp, &tok, scope); break; default: internalErr(); right = NULL; break; } left->parent = pp; pp->children = left; left->next = right; } *pTokList = tok; return pp; } static struct pfParse *parseParaInvoke(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope, boolean isStatement) /* Parse para invokation. This is of general form: * 'para' type var 'in' collection action expression * where action can be do, add, multiply or, and, min, max, top intExpression, * sample intExpression, filter, or get. In the case where the action is 'do' * then the action is followed by a statement rather than an expression. */ { struct pfToken *tok = *pTokList; struct pfParse *pp; struct pfParse *element; struct pfParse *collection; struct pfParse *body; boolean actionNeedsStatement = FALSE; char *action = NULL; enum pfParseType paraType = pptNone; /* Preliminaries, scope and as of yet untyped parse node for para. */ scope = pfScopeNew(pfc, scope, 1, NULL); tok = tok->next; /* Skip over para. */ skipRequiredCharType('(', &tok); pp = pfParseNew(pptNone, tok, parent, scope); /* Parse out the element in collection */ element = pfParseExpression(pfc, pp, &tok, scope); skipRequiredWord("in", &tok); collection = pfParseExpression(pfc, pp, &tok, scope); skipRequiredCharType(')', &tok); /* Parse out the action keyword, and figure out what to do depending on * action. Determine parse node type from action. */ action = pfTokenAsString(tok); if (sameString("do", action)) { paraType = pptParaDo; actionNeedsStatement = TRUE; } else if (sameString("+", action)) paraType = pptParaAdd; else if (sameString("*", action)) paraType = pptParaMultiply; else if (sameString("&&", action)) paraType = pptParaAnd; else if (sameString("||", action)) paraType = pptParaOr; else if (sameString("min", action)) paraType = pptParaMin; else if (sameString("max", action)) paraType = pptParaMax; else if (sameString("get", action)) paraType = pptParaGet; else if (sameString("argmax", action)) paraType = pptParaArgMax; else if (sameString("argmin", action)) paraType = pptParaArgMin; else if (sameString("filter", action)) paraType = pptParaFilter; else errAt(tok, "unrecognized para action"); if (isStatement != actionNeedsStatement) { if (isStatement) errAt(tok, "para action \"%s\" has a value that needs to be used", action); else errAt(tok, "para action \"%s\" has no value", action); } freez(&action); tok = tok->next; pp->type = paraType; /* Get expression or statement that gets executed in parallel. */ if (actionNeedsStatement) body = pfParseStatement(pfc, pp, &tok, scope); else { body = pfParseExpression(pfc, pp, &tok, scope); if (isStatement) eatSemi(&tok); } /* Hang various things off of para parse node. */ slAddHead(&pp->children, body); slAddHead(&pp->children, element); slAddHead(&pp->children, collection); *pTokList = tok; return pp; } static void markModuleDotType(struct pfCompile *pfc, struct pfParse *pp) /* This type dot is in a context where it better be module.type. * We mark it as such. We leave it to a later pass to make sure * that the module really is a module and the type really is a type * in that module. */ { struct pfParse *left = pp->children; struct pfParse *right = left->next; if (left->type == pptNameUse && right->type == pptNameUse) { pp->type = pptModuleDotType; } else { errAt(pp->tok, "misplaced dot in type expression"); } } static void checkIsTypeExp(struct pfCompile *pfc, struct pfParse *pp, struct pfScope *scope) /* Make sure that subtree is all type expression. */ { switch (pp->type) { case pptNameUse: if (!pfScopeFindType(scope, pp->name)) errAt(pp->tok, "%s is not a type name.", pp->name); pp->type = pptTypeName; break; case pptIndex: checkIsTypeExp(pfc, pp->children, scope); break; case pptOf: for (pp = pp->children; pp != NULL; pp = pp->next) checkIsTypeExp(pfc, pp, scope); break; case pptTypeFlowPt: case pptTypeToPt: break; case pptDot: markModuleDotType(pfc, pp); break; default: { expectingGot("type expression", pp->tok); break; } } } static void submergeTypeIndex(struct pfParse **pPp) /* Convert a subtree that looks like so: * pptIndex * pptName * expressionTree * to something that looks like so: * pptName * expressionTree * Why to do this? Well it's what the rest of the typing * system expects from an earlier design of the type expression * parse. */ { struct pfParse *pp = *pPp; if (pp->type == pptIndex) { struct pfParse *name = pp->children; struct pfParse *exp = name->next; name->next = pp->next; name->children = exp; *pPp = name; } else { for (pPp = &pp->children; *pPp != NULL; pPp = &(*pPp)->next) submergeTypeIndex(pPp); } } static void massageIntoType(struct pfCompile *pfc, struct pfParse **pPp, struct pfScope *scope) /* Given a parse tree that is just known to be some sort of expression, * make sure it's a type expression, and do any necessary reworking * of the subtree that that entails. */ { checkIsTypeExp(pfc, *pPp, scope); submergeTypeIndex(pPp); } static struct pfParse *parseNewObject(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse things of form new type(init parameters) */ { struct pfToken *tok = *pTokList, *newTok; struct pfParse *newPp; newTok = tok; tok = tok->next; /* We covered 'new' */ newPp = parseOf(pfc, parent, &tok, scope); if (newPp->type != pptCall) errAt(newTok, "missing input tuple to new operator"); massageIntoType(pfc, &newPp->children, scope); newPp->type = pptNew; *pTokList = tok; return newPp; } static struct pfParse *parseParaOrNew(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse expression that might include 'para' */ { struct pfToken *tok = *pTokList; switch (tok->type) { case pftPara: return parseParaInvoke(pfc, parent, pTokList, scope, FALSE); case pftNew: return parseNewObject(pfc, parent, pTokList, scope); default: return parseArrayCallDot(pfc, parent, pTokList, scope); } } static void makeIncrementNode(struct pfParse *pp, struct pfParse *exp, struct pfToken *incTok, struct pfScope *scope) /* Add children to pp, which is a += node, so as to * make it into an increment/decrement operation. */ { struct pfToken *constantTok; struct pfParse *constant; /* We have to fake a token because of the way the constant * handling code works. */ constantTok = CloneVar(incTok); constantTok->type = pftInt; constantTok->val.i = (incTok->type == pftPlusPlus ? 1 : -1); constant = pfParseNew(pptConstUse, constantTok, pp, scope); /* Hook everything onto pp. */ pp->children = exp; exp->next = constant; } static struct pfParse *parseIncrement(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse ++exp or --exp */ { struct pfToken *tok = *pTokList; struct pfParse *exp, *pp; struct pfToken *incTok; switch (tok->type) { case pftPlusPlus: case pftMinusMinus: { incTok = tok; tok = tok->next; pp = pfParseNew(pptPlusEquals, tok, parent, scope); exp = parseParaOrNew(pfc, pp, &tok, scope); makeIncrementNode(pp, exp, incTok, scope); *pTokList = tok; return pp; } } exp = parseParaOrNew(pfc, parent, pTokList, scope); tok = *pTokList; switch (tok->type) { case pftPlusPlus: case pftMinusMinus: { incTok = tok; tok = tok->next; pp = pfParseNew(pptPlusEquals, tok, parent, scope); makeIncrementNode(pp, exp, incTok, scope); *pTokList = tok; return pp; } } return exp; } static struct pfParse *parseNegation(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse unary minus, and other things at that level. */ { struct pfToken *tok = *pTokList; struct pfParse *pp; if (tok->type == '-') { pp = pfParseNew(pptNegate, tok, parent, scope); tok = tok->next; pp->children = parseNegation(pfc, pp, &tok, scope); } else if (tok->type == '!') { pp = pfParseNew(pptNot, tok, parent, scope); tok = tok->next; pp->children = parseNegation(pfc, pp, &tok, scope); } else if (tok->type == '~') { pp = pfParseNew(pptFlipBits, tok, parent, scope); tok = tok->next; pp->children = parseNegation(pfc, pp, &tok, scope); } else { pp = parseIncrement(pfc, parent, &tok, scope); } *pTokList = tok; return pp; } struct pfParse *parseProduct(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse things separated by '*' or '/' or '%'. */ { struct pfToken *tok = *pTokList; struct pfParse *pp = parseNegation(pfc, parent, &tok, scope); while (tok->type == '*' || tok->type == '/' || tok->type == '%' || tok->type == pftShiftLeft || tok->type == pftShiftRight || tok->type == '&' && tok->next->type != ')') { struct pfParse *left = pp, *right; enum pfTokType tt = pptNone; switch (tok->type) { case '*': tt = pptMul; break; case '/': tt = pptDiv; break; case '%': tt = pptMod; break; case pftShiftLeft: tt = pptShiftLeft; break; case pftShiftRight: tt = pptShiftRight; break; case '&': tt = pptBitAnd; break; } pp = pfParseNew(tt, tok, parent, scope); left->parent = pp; tok = tok->next; right = parseNegation(pfc, pp, &tok, scope); pp->children = left; left->next = right; } *pTokList = tok; return pp; } struct pfParse *parseSum(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse + and - part of expression. */ { struct pfToken *tok = *pTokList; struct pfParse *pp = parseProduct(pfc, parent, &tok, scope); while (tok->type == '+' || tok->type == '-' || tok->type == '|' || tok->type == '^' && tok->next->type != ')') { struct pfParse *left = pp, *right; enum pfTokType tt = pptNone; switch (tok->type) { case '+': tt = pptPlus; break; case '-': tt = pptMinus; break; case '|': tt = pptBitOr; break; case '^': tt = pptBitXor; break; } pp = pfParseNew(tt, tok, parent, scope); left->parent = pp; tok = tok->next; right = parseProduct(pfc, pp, &tok, scope); pp->children = left; left->next = right; } *pTokList = tok; return pp; } struct pfParse *parseCmp(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse == >=, <= > < and != part of expression. */ { struct pfToken *tok = *pTokList; struct pfParse *pp = parseSum(pfc, parent, &tok, scope); if (tok->type == pftEqualsEquals || tok->type == pftNotEquals || tok->type == pftGreaterOrEquals || tok->type == pftLessOrEquals || tok->type == '<' || tok->type == '>') { struct pfParse *left = pp, *right; enum pfTokType tt = 0; switch (tok->type) { case pftEqualsEquals: tt = pptSame; break; case pftNotEquals: tt = pptNotSame; break; case pftGreaterOrEquals: tt = pptGreaterOrEquals; break; case pftLessOrEquals: tt = pptLessOrEquals; break; case '>': tt = pptGreater; break; case '<': tt = pptLess; break; } pp = pfParseNew(tt, tok, parent, scope); left->parent = pp; tok = tok->next; right = parseSum(pfc, pp, &tok, scope); pp->children = left; left->next = right; } *pTokList = tok; return pp; } struct pfParse *parseLogAnd(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse logical and. */ { struct pfToken *tok = *pTokList; struct pfParse *pp = parseCmp(pfc, parent, &tok, scope); while (tok->type == pftLogAnd && tok->next->type != ')') { struct pfParse *left = pp, *right; pp = pfParseNew(pptLogAnd, tok, parent, scope); left->parent = pp; tok = tok->next; right = parseCmp(pfc, pp, &tok, scope); pp->children = left; left->next = right; } *pTokList = tok; return pp; } struct pfParse *parseLogOr(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse logical and. */ { struct pfToken *tok = *pTokList; struct pfParse *pp = parseLogAnd(pfc, parent, &tok, scope); while (tok->type == pftLogOr && tok->next->type != ')') { struct pfParse *left = pp, *right; pp = pfParseNew(pptLogOr, tok, parent, scope); left->parent = pp; tok = tok->next; right = parseLogAnd(pfc, pp, &tok, scope); pp->children = left; left->next = right; } *pTokList = tok; return pp; } struct pfParse *parseIndexRange(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse range (X to Y) */ { struct pfToken *tok = *pTokList; struct pfParse *pp = parseLogOr(pfc, parent, &tok, scope); if (tok->type == pftTil) { struct pfParse *left = pp, *right; pp = pfParseNew(pptIndexRange, tok, parent, scope); left->parent = pp; tok = tok->next; right = parseLogOr(pfc, parent, &tok, scope); pp->children = left; left->next = right; } *pTokList = tok; return pp; } struct pfParse *parseVarOfFunc(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* If it starts with 'var of to/flow' then parse out a function pointer * declaration. Else use parseIndexRange. */ { struct pfToken *tok = *pTokList; if (tok->type == pftName && sameString(tok->val.s, "var")) { struct pfToken *ofTok = tok->next; if (ofTok->type == pftOf) { struct pfToken *funcTok = ofTok->next; if (funcTok->type == pftFlow || funcTok->type == pftTo) { *pTokList = funcTok; return parseShortFuncType(pfc, parent, pTokList, scope); } } } return parseIndexRange(pfc, parent, pTokList, scope); } static struct pfParse *parseOf(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse out name of name . */ { struct pfParse *pp = parseVarOfFunc(pfc, parent, pTokList, scope); struct pfToken *tok = *pTokList; if (tok->type == pftOf) { struct pfParse *of = pfParseNew(pptOf, tok, parent, scope); pp->parent = of; of->children = pp; while (tok->type == pftOf) { tok = tok->next; pp = parseVarOfFunc(pfc, of, &tok, scope); slAddHead(&of->children, pp); } slReverse(&of->children); *pTokList= tok; return of; } else return pp; } static boolean inFunction(struct pfParse *pp) /* Return TRUE if pp or one of it's parents is a function. */ { while (pp != NULL) { if (pp->type == pptToDec || pp->type == pptFlowDec) return TRUE; pp = pp->parent; } return FALSE; } static void rSetAccess(struct pfParse *pp, enum pfAccessType access, bool isConst) /* Set access type for self and descendents. */ { pp->access = access; pp->isConst = isConst; for (pp = pp->children; pp != NULL; pp = pp->next) rSetAccess(pp, access, isConst); } static struct pfParse *parseVarDec(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse something of the form [static] typeExp varName */ { struct pfToken *tok = *pTokList, *constTok = NULL; struct pfParse *pp; bool inAccessSection = TRUE, isConst = FALSE; enum pfAccessType access = paUsual; while (inAccessSection) { enum pfTokType tokType = tok->type; switch (tokType) { case pftGlobal: case pftLocal: case pftReadable: case pftStatic: case pftWritable: { boolean inFunc = inFunction(parent); if (access != paUsual) errAt(tok, "Only one of global/readable/const/writable/local/static allowed."); switch (tokType) { case pftGlobal: access = paGlobal; if (inFunc) errAt(tok, "'global' inside function"); break; case pftLocal: if (scope->module) errAt(tok, "'local' unneeded here"); access = paLocal; break; case pftReadable: if (scope->module) errAt(tok, "use global rather than readable here"); access = paReadable; break; case pftStatic: access = paStatic; if (!inFunc) errAt(tok, "'static' outside of function"); break; case pftWritable: access = paWritable; break; } tok = tok->next; break; } default: inAccessSection = FALSE; break; } } if (tok->type == pftConst) { isConst = TRUE; constTok = tok; tok = tok->next; } pp = parseOf(pfc, parent, &tok, scope); if (tok->type == pftName) { struct pfParse *dec = pfParseNew(pptVarDec, tok, parent, scope); struct pfParse *type = pp; struct pfParse *name = parseNameUse(dec, &tok, scope); massageIntoType(pfc, &type, scope); pp->parent = dec; dec->children = type; type->next = name; dec->access = access; dec->isConst = isConst; rSetAccess(type, access, isConst); dec->name = name->name; pp = dec; } else if (isConst) errAt(constTok, "Misplaced const"); *pTokList = tok; return pp; } static void checkNoNestedAssigns(struct pfParse *pp) /* Make sure that there are no assignments inside of subtree. */ { switch (pp->type) { case pptAssign: case pptPlusEquals: case pptMinusEquals: case pptMulEquals: case pptDivEquals: errAt(pp->tok, "nested assignments not allowed in paraFlow"); break; } for (pp = pp->children; pp != NULL; pp = pp->next) checkNoNestedAssigns(pp); } struct pfParse *parseAssign(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse '=' separated expression */ { struct pfToken *tok = *pTokList; struct pfParse *assign = NULL; enum pfParseType type = pptNone; struct pfParse *pp = parseVarDec(pfc, parent, &tok, scope); switch (tok->type) { case '=': type = pptAssign; break; case pftPlusEquals: type = pptPlusEquals; break; case pftMinusEquals: type = pptMinusEquals; break; case pftMulEquals: type = pptMulEquals; break; case pftDivEquals: type = pptDivEquals; break; case '@': type = pptKeyVal; break; } if (type != pptNone) { if (pp->type == pptVarDec && type != pptAssign) errAt(tok, "You can only initialize a variable with a simple '='"); assign = pfParseNew(type, tok, parent, scope); assign->children = pp; assign->access = pp->access; assign->isConst = pp->isConst; assign->name = pp->name; pp->parent = assign; for (;;) { tok = tok->next; pp = parseIndexRange(pfc, assign, &tok, scope); checkNoNestedAssigns(pp); slAddHead(&assign->children, pp); if (tok->type != '=' || type != pptAssign) break; } } *pTokList = tok; if (assign != NULL) { slReverse(&assign->children); return assign; } else return pp; } static void flipNameUseToVarDec(struct pfParse *pp, struct pfParse *type, struct pfParse *parent) { struct pfParse *dupeType, *dupeName; AllocVar(dupeName); *dupeName = *pp; AllocVar(dupeType); *dupeType = *type; pp->children = dupeType; dupeType->next = dupeName; dupeName->next = NULL; pp->type = pptVarDec; pp->access = type->access; pp->isConst = type->isConst; } static boolean anyVarDecs(struct pfParse *pp) /* Return TRUE if there are any varDecs in subtree */ { if (pp->type == pptVarDec) return TRUE; for (pp = pp->children; pp != NULL; pp = pp->next) { if (anyVarDecs(pp)) return TRUE; } return FALSE; } static boolean isVarDecOrVarDecInit(struct pfParse *pp) /* Return true if pp is either varDec or assignment to varDec. */ { return (pp->type == pptVarDec || (pp->type == pptAssign && pp->children->type == pptVarDec)); } static void updateAccessAndConst(struct pfParse *pp, struct pfParse *type, enum pfAccessType *pAccess, bool *pIsConst) /* If pp has access or const modifiers then update pAccess and pConst * with them. Otherwise use pAccess and pConst to set them. */ { if (pp->access == paUsual) type->access = pp->access = *pAccess; else *pAccess = pp->access; if (pp->isConst) *pIsConst = TRUE; else type->isConst = pp->isConst = *pIsConst; } static void addMissingTypesInDeclareTuple(struct pfParse *tuple) /* If first element of tuple is typed, then make sure rest * is typed too. */ { struct pfParse *vars = tuple->children, *next; struct pfParse *type = NULL; struct pfParse *pp; enum pfAccessType access = vars->access; bool isConst = vars->isConst; /* Make sure first item in tuple is a var dec. */ if (!isVarDecOrVarDecInit(vars)) errAt(vars->tok, "Missing type information."); /* Make sure everything is either a varDec or an assignment */ for (pp = vars; pp != NULL; pp = pp->next) { if (pp->type != pptVarDec && pp->type != pptAssign && pp->type != pptNameUse) { errAt(pp->tok, "Expecting variable declaration"); } } /* Fill in any missing types from type of previous one in list. * We've guaranteed now that first one will have a type. */ for (pp = vars; pp != NULL; pp = pp->next) { if (pp->type == pptVarDec) { type = pp->children; pp->name = type->next->name; updateAccessAndConst(pp, type, &access, &isConst); } else if (pp->type == pptNameUse) { flipNameUseToVarDec(pp, type, tuple); } else if (pp->type == pptAssign) { struct pfParse *sub = pp->children; if (sub->type == pptVarDec) { type = sub->children; updateAccessAndConst(sub, type, &access, &isConst); } else if (sub->type == pptNameUse) { flipNameUseToVarDec(sub, type, pp); pp->access = sub->access; pp->isConst = sub->isConst; pp->ty = sub->ty; } } } } static void varDecAndAssignToVarInit(struct pfParse *pp) /* Convert pptVarDec and when appropriate pptAssign to pptVarInit. */ { if (pp->type == pptVarDec) { pp->type = pptVarInit; if (pp->isConst) { boolean ok = FALSE; if (pp->parent->type == pptTuple || pp->parent->type == pptTypeTuple) { enum pfParseType grandparentType = pp->parent->parent->type; if (grandparentType == pptToDec || grandparentType == pptFlowDec) ok = TRUE; } if (!ok) errAt(pp->tok, "Uninitialized const."); } } else if (pp->type == pptAssign) { struct pfParse *left = pp->children; if (left->type == pptVarDec) { struct pfParse *right = left->next; pp->type = pptVarInit; pp->access = left->access; pp->isConst = left->isConst; pp->children = left->children; slAddTail(&pp->children, right); } } if (pp->type == pptVarInit || pp->type == pptFormalParameter) { struct pfParse *type = pp->children; struct pfParse *name = type->next; name->type = pptSymName; pp->name = name->name; } for (pp = pp->children; pp != NULL; pp = pp->next) varDecAndAssignToVarInit(pp); } static struct pfParse *parseTuple(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse , separated expression. */ { struct pfToken *tok = *pTokList; struct pfParse *pp = parseAssign(pfc, parent, &tok, scope); struct pfParse *tuple = NULL; if (tok->type == ',') { tuple = pfParseNew(pptTuple, tok, parent, scope); tuple->children = pp; pp->parent = tuple; while (tok->type == ',') { tok = tok->next; if (tok->type == ')') break; pp = parseAssign(pfc, tuple, &tok, scope); slAddHead(&tuple->children, pp); } } *pTokList = tok; if (tuple != NULL) { slReverse(&tuple->children); if (anyVarDecs(tuple)) { addMissingTypesInDeclareTuple(tuple); varDecAndAssignToVarInit(tuple); } return tuple; } else { if (isVarDecOrVarDecInit(pp)) varDecAndAssignToVarInit(pp); return pp; } } struct pfParse *pfParseExpression(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse expression. */ { return parseTuple(pfc, parent, pTokList, scope); } static struct pfParse *parseCompound(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse a compound statement (statement list surrounded * by brackets */ { struct pfParse *pp; struct pfToken *tok = *pTokList; struct pfParse *statement; if (tok->type != '{') expectingGot("{", tok); tok->val.scope->parent = scope; scope = tok->val.scope; pp = pfParseNew(pptCompound, *pTokList, parent, scope); tok = tok->next; for (;;) { if (tok == NULL) { errAt(*pTokList, "End of file in compound statement"); } if (tok->type == '}') { tok = tok->next; break; } statement = pfParseStatement(pfc, pp, &tok, scope); slAddHead(&pp->children, statement); } *pTokList = tok; slReverse(&pp->children); return pp; } static struct pfParse *parseFunction(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope, enum pfParseType type) /* Parse something (...) [into (...)] */ { struct pfToken *tok = *pTokList; struct pfParse *pp; struct pfParse *name, *input, *output = NULL, *body = NULL; scope = pfScopeNew(pfc, scope, 0, NULL); pfScopeMarkLocal(scope); pp = pfParseNew(type, tok, parent, scope); tok = tok->next; /* Skip to/flow (implicit in type) */ name = parseNameUse(parent, &tok, scope); pp->name = name->name; parseFunctionIo(pfc, pp, &tok, scope, &input, &output); if (pfc->isPfh || (parent->type == pptCompound && parent->parent->type == pptInterface)) eatSemi(&tok); else { body = parseCompound(pfc, pp, &tok, scope); pfScopeMarkLocal(body->scope); slAddHead(&pp->children, body); } slAddHead(&pp->children, output); slAddHead(&pp->children, input); slAddHead(&pp->children, name); *pTokList = tok; return pp; } static struct pfParse *parseTo(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse to (...) [into (...)] */ { return parseFunction(pfc, parent, pTokList, scope, pptToDec); } static struct pfParse *parseFlow(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse flow (...) [into (...)] */ { return parseFunction(pfc, parent, pTokList, scope, pptFlowDec); } static struct pfParse *parseOperator(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse to (...) [into (...)] */ { if (!pfc->isSys) { struct pfToken *tok = *pTokList; errAt(tok, "the word '%s' is reserved for system use", pfTokenAsString(tok)); } return parseFunction(pfc, parent, pTokList, scope, pptOperatorDec); } static struct pfParse *parsePolymorphic(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse flow (...) [into (...)] */ { struct pfToken *tok = *pTokList; struct pfParse *pp = pfParseNew(pptPolymorphic, tok, parent, scope); if (pfParseEnclosingClass(parent) == NULL) errAt(tok, "polymorphic keyword outside of class"); tok = tok->next; switch (tok->type) { case pftTo: case pftPara: case pftFlow: pp->children = pfParseStatement(pfc, pp, &tok, scope); break; default: expectingGot("Expecting function declaration after polymorphic", tok); break; } *pTokList = tok; return pp; } static void checkForCircularity(struct pfBaseType *parent, struct pfBaseType *newBase, struct pfToken *tok) /* Make sure that newBase is not parent, grandparent, etc of itself. */ { while (parent != NULL) { if (parent == newBase) errAt(tok, "loop in inheritance tree"); parent = parent->parent; } } static struct pfParse *parseClassOrInterface(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope, enum pfParseType pptType) /* Parse class declaration statement. */ { struct pfToken *tok = *pTokList; struct pfParse *pp; struct pfParse *name, *body, *extends = NULL; struct pfBaseType *myBase; pp = pfParseNew(pptType, tok, parent, scope); tok = tok->next; /* Skip 'class' or 'interface' token */ name = parseNameUse(pp, &tok, scope); name->type = pptTypeName; pp->name = name->name; myBase = pfScopeFindType(scope, name->name); if (myBase == NULL) internalErr(); myBase->def = pp; if (tok->type == pftExtends) { struct pfBaseType *parentBase; tok = tok->next; if (tok->type != pftName) expectingGot("name", tok); parentBase = pfScopeFindType(scope, tok->val.s); if (parentBase == NULL) errAt(tok, "undefined parent %s", tok->val.s); if (parentBase->isClass != myBase->isClass) errAt(tok, "can't mix classes and interfaces in extensions"); extends = pfParseNew(pptTypeName, tok, pp, scope); extends->ty = pfTypeNew(parentBase); extends->name = tok->val.s; checkForCircularity(parentBase, myBase, tok); myBase->parent = parentBase; slAddHead(&pp->children, extends); tok = tok->next; } body = parseCompound(pfc, pp, &tok, scope); body->scope->class = myBase; slAddHead(&pp->children, body); slAddHead(&pp->children, name); *pTokList = tok; return pp; } static struct pfParse *parseClass(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse class declaration statement. */ { return parseClassOrInterface(pfc, parent, pTokList, scope, pptClass); } static struct pfParse *parseInterface(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse interface declaration statement. */ { return parseClassOrInterface(pfc, parent, pTokList, scope, pptInterface); } static struct pfParse *parseIf(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse if statement (which may include else) */ { struct pfToken *tok = *pTokList; struct pfParse *pp = pfParseNew(pptIf, tok, parent, scope); struct pfParse *conditional, *trueBody, *falseBody = NULL; tok = tok->next; /* Skip 'if' */ /* Get conditional. */ if (tok->type != '(') expectingGot("(", tok); tok = tok->next; conditional = pfParseExpression(pfc, pp, &tok, scope); skipRequiredCharType(')', &tok); /* Body */ trueBody = pfParseStatement(pfc, pp, &tok, scope); /* Else clause. */ if (tok->type == pftElse) { tok = tok->next; falseBody = pfParseStatement(pfc, pp, &tok, scope); } *pTokList = tok; pp->children = conditional; conditional->next = trueBody; trueBody->next = falseBody; return pp; } static struct pfParse *parseCase(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Process case statement, which is of form: * case (exp) * { * tuple : statement * tuple : statement * else : statement * } * The output parse tree is * pptCase * * pptCaseList * pptCaseItem * * * pptCaseItem * * * pptCaseElse * */ { struct pfToken *tok = *pTokList; struct pfParse *casePp = pfParseNew(pptCase, tok, parent, scope); struct pfParse *expression; struct pfParse *list; boolean gotElse = FALSE; tok = tok->next; skipRequiredCharType('(', &tok); expression = pfParseExpression(pfc, casePp, &tok, scope); if (expression->type == pptTuple) errAt(expression->tok, "Only single values allowed inside case expression."); skipRequiredCharType(')', &tok); list = pfParseNew(pptCaseList, tok, casePp, scope); skipRequiredCharType('{', &tok); for (;;) { if (tok->type == '}') break; else if (tok->type == pftElse) { struct pfParse *elsePp = pfParseNew(pptCaseElse, tok, list, scope); struct pfParse *statement; if (gotElse) errAt(tok, "Multiple elses inside of case."); gotElse = TRUE; tok = tok->next; skipRequiredCharType(':', &tok); statement = pfParseStatement(pfc, elsePp, &tok, scope); elsePp->children = statement; slAddHead(&list->children, elsePp); } else { struct pfParse *item; struct pfParse *tuple; struct pfParse *statement; if (gotElse) errAt(tok, "Additional cases following else not allowed."); item = pfParseNew(pptCaseItem, tok, list, scope); tuple = pfParseExpression(pfc, item, &tok, scope); if (anyVarDecs(tuple)) errAt(tuple->tok, "You can't declare variables to the left of : in a case."); if (tuple->type != pptTuple) tuple = pfSingleTuple(tuple->parent, tuple->tok, tuple); skipRequiredCharType(':', &tok); statement = pfParseStatement(pfc, item, &tok, scope); item->children = tuple; tuple->next = statement; slAddHead(&list->children, item); } } skipRequiredCharType('}', &tok); slReverse(&list->children); casePp->children = expression; expression->next = list; *pTokList = tok; return casePp; } static struct pfParse *parseWhile(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse if statement (which may include else) */ { struct pfToken *tok = *pTokList; struct pfParse *pp = pfParseNew(pptWhile, tok, parent, scope); struct pfParse *conditional, *body; tok = tok->next; /* Skip 'while' */ /* Get conditional. */ if (tok->type != '(') expectingGot("(", tok); tok = tok->next; conditional = pfParseExpression(pfc, pp, &tok, scope); if (tok->type != ')') expectingGot(")", tok); tok = tok->next; /* Body */ body = pfParseStatement(pfc, pp, &tok, scope); *pTokList = tok; pp->children = conditional; conditional->next = body; return pp; } static struct pfParse *parseForIn(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse for (el in collection) statement */ { struct pfToken *tok = *pTokList; struct pfParse *pp; struct pfParse *element; struct pfParse *collection; struct pfParse *statement; scope = pfScopeNew(pfc, scope, 1, NULL); pp = pfParseNew(pptForeach, tok, parent, scope); tok = tok->next; /* Skip over 'foreach' */ skipRequiredCharType('(', &tok); element = pfParseExpression(pfc, pp, &tok, scope); skipRequiredWord("in", &tok); collection = pfParseExpression(pfc, pp, &tok, scope); skipRequiredCharType(')', &tok); statement = pfParseStatement(pfc, pp, &tok, scope); slAddHead(&pp->children, statement); slAddHead(&pp->children, element); slAddHead(&pp->children, collection); *pTokList = tok; return pp; } static struct pfParse *parseExpressionAndSemi(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse expression and following semicolon if any. */ { struct pfToken *tok = *pTokList; struct pfParse *pp; if (tok->type == ';') pp = emptyStatement(parent, tok, scope); else pp = pfParseExpression(pfc, parent, &tok, scope); eatSemi(&tok); *pTokList = tok; return pp; } static struct pfParse *parseParaStatement(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse when it starts with 'para' - will either be a para function declaration * or a para invokation. */ { struct pfToken *tok = *pTokList; /* "para" */ tok = tok->next; if (tok->type != '(') expectingGot("(", tok); return parseParaInvoke(pfc, parent, pTokList, scope, TRUE); } static struct pfParse *parseForLikeC(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse for (;;) statement */ { struct pfToken *tok = *pTokList; struct pfParse *pp; struct pfParse *init; struct pfParse *check; struct pfParse *advance; struct pfParse *statement; scope = pfScopeNew(pfc, scope, 1, NULL); pp = pfParseNew(pptFor, tok, parent, scope); tok = tok->next; /* Skip over 'for' */ if (tok->type != '(') expectingGot("(", tok); tok = tok->next; init = parseExpressionAndSemi(pfc, pp, &tok, scope); check = parseExpressionAndSemi(pfc, pp, &tok, scope); /* Parse advance statment and closing ')' */ if (tok->type == ')') advance = emptyStatement(pp, tok, scope); else advance = pfParseExpression(pfc, pp, &tok, scope); if (tok->type != ')') expectingGot(")", tok); tok = tok->next; /* Get body of loop. */ statement = pfParseStatement(pfc, pp, &tok, scope); /* Hang various subparts of for statement onto children list. */ slAddHead(&pp->children, statement); slAddHead(&pp->children, advance); slAddHead(&pp->children, check); slAddHead(&pp->children, init); *pTokList = tok; return pp; } static struct pfParse *parseFor(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse for statement. Figure out whether it's for (;;) or * for (el in array) type and go to it. */ { struct pfToken *forTok = *pTokList; struct pfToken *openTok = forTok->next, *closeTok; if (openTok->type != '(') expectingGot("(", openTok); closeTok = pfTokenGetClosingTok(openTok, '(', ')'); if (pfTokenFirstBetween(openTok->next, closeTok, ';')) return parseForLikeC(pfc, parent, pTokList, scope); else return parseForIn(pfc, parent, pTokList, scope); } static struct pfParse *fakeVarInit(char *string, struct pfBaseType *base, struct pfToken *tok, struct pfParse *parent, struct pfScope *scope) /* Create a little parse tree like so: * pptVarInit * pptTypeName * pptSymName name */ { struct pfParse *varInit = pfParseNew(pptVarInit, tok, parent, scope); struct pfParse *type = pfParseNew(pptTypeName, tok, varInit, scope); struct pfParse *name = pfParseNew(pptSymName, tok, varInit, scope); varInit->name = name->name = string; type->name = base->name; varInit->children = type; type->next = name; return varInit; } static struct pfParse *parseTry(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse try {statements} catch (exception e) statement * into: * pptTry * * pptCatch * pptVarDec * pptTypeName string * pptNameUse message * */ { struct pfToken *tok = *pTokList; struct pfParse *tryPp = pfParseNew(pptTry, tok, parent, scope); struct pfParse *tryBody, *catchPp, *exceptionPp, *catchBody; struct pfToken *catchTok; int level = 0; struct pfScope *catchScope; /* Scope for catch var. */ tok = tok->next; /* Skip "try" */ /* We require the statement after try to be compound. * This is just in case some day in the future we want * to add something (like a time-out maybe) as a parameter * to try. */ if (tok->type != '{') expectingGot("{", tok); tryBody = pfParseStatement(pfc, tryPp, &tok, scope); /* Parse out the catch. */ if (tok->type != pftCatch) expectingGot("catch", tok); catchTok = tok; catchScope = pfScopeNew(pfc, scope, 2, NULL); catchPp = pfParseNew(pptCatch, tok, tryPp, catchScope); tok = tok->next; /* Get exception declaration. */ skipRequiredCharType('(', &tok); exceptionPp = parseVarDec(pfc, catchPp, &tok, catchScope); if (exceptionPp->type != pptVarDec) expectingGot("exception e", exceptionPp->tok); exceptionPp->type = pptVarInit; skipRequiredCharType(')', &tok); /* And get body of catch. */ catchBody = pfParseStatement(pfc, tryPp, &tok, catchScope); /* Arrange parse tree under try. */ tryPp->children = tryBody; tryBody->next = catchPp; catchPp->children = exceptionPp; catchPp->next = catchBody; *pTokList = tok; return tryPp; } static struct pfParse *parseWordStatement(struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope, enum pfParseType type) /* Parse one word statement. */ { struct pfToken *tok = *pTokList; struct pfParse *pp = pfParseNew(type, tok, parent, scope); tok = tok->next; eatSemi(&tok); *pTokList = tok; return pp; } static struct pfParse *parseBreak(struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse break statement */ { return parseWordStatement(parent, pTokList, scope, pptBreak); } static struct pfParse *parseContinue(struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse continue statement */ { return parseWordStatement(parent, pTokList, scope, pptContinue); } static struct pfParse *parseReturn(struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse return statement */ { return parseWordStatement(parent, pTokList, scope, pptReturn); } static boolean isGoodSymName(char *s) /* Return TRUE if it looks like a good symbol name - starting * with '_' or alphabet, and continuing with '_' or alphnum. */ { char c = *s++; if (c != '_' && !isalpha(c)) return FALSE; while ((c = *s++) != 0) { if (c != '_' && !isalnum(c)) return FALSE; } return TRUE; } static void checkModuleNameSimple(struct pfToken *tok, char *modName) /* Make sure nothing but alphanumerics and _ in module name. */ { if (!isGoodSymName(modName)) errAt(tok, "Module names (after the path component) must follow rules for variable names"); } static struct pfParse *parseIncludeOrImport(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope, enum pfParseType ppt) /* Parse include or import statement (depending on ppt parameter) */ { struct pfToken *tok = *pTokList; struct pfParse *pp = pfParseNew(ppt, tok, parent, scope); struct pfParse *modPp; char *pathName, *moduleName; switch (parent->type) { case pptMainModule: case pptModuleRef: case pptModule: break; default: errAt(tok, "includes and imports only allowed at highest level of a module, not inside of any functions, classes, loops, etc."); break; } tok = tok->next; /* Have covered 'include' */ pp->children = modPp = pfParseNew(pptSymName, tok, pp, scope); pathName = tok->val.s; pp->name = pp->children->name = pathName; tok = tok->next; if (ppt == pptImport) { struct pfParse *localName; struct pfType *type; if (tok->type == pftName && sameString(tok->val.s, "as")) { tok = tok->next; if (tok->type != pftName) errAt(tok, "Expecting symbol name after \"import as\""); localName = pfParseNew(pptSymName, tok, pp, scope); tok = tok->next; localName->name = localName->tok->val.s; } else { struct pfToken *pathTok = pp->children->tok; moduleName = strrchr(pathName, '/'); if (moduleName == NULL) moduleName = pathName; else moduleName += 1; if (!isGoodSymName(moduleName)) errAt(tok, "Module names (after the path component) must follow rules " "for variable names unless you include an 'as'."); checkModuleNameSimple(pathTok, moduleName); localName = pfParseNew(pptSymName, pathTok, pp, scope); localName->name = moduleName; } slAddTail(&pp->children, localName); type = pfTypeNew(pfc->moduleType); type->tyty = tytyModule; pp->var = pfScopeAddVar(pp->scope, localName->name, type, pp); } *pTokList = tok; return pp; } static void preventNakedUse(struct pfParse *pp) /* Prevent statement that is nothing but a varUse. */ /* Make sure that expression used as statement has a side effect. */ { switch(pp->type) { case pptNameUse: errAt(pp->tok, "Statement with no effect."); break; } } struct pfParse *pfParseStatement(struct pfCompile *pfc, struct pfParse *parent, struct pfToken **pTokList, struct pfScope *scope) /* Parse a single statement up to but not including semicolon, * and add results to (head) of * parent->childList. */ { struct pfToken *tok = *pTokList; struct pfParse *statement; switch (tok->type) { case pftInclude: statement = parseIncludeOrImport(pfc, parent, &tok, scope, pptInclude); break; case pftImport: statement = parseIncludeOrImport(pfc, parent, &tok, scope, pptImport); break; case ';': statement = emptyStatement(parent, tok, scope); tok = tok->next; break; case '{': statement = parseCompound(pfc, parent, &tok, scope); break; case pftIf: statement = parseIf(pfc, parent, &tok, scope); break; case pftCase: statement = parseCase(pfc, parent, &tok, scope); break; case pftWhile: statement = parseWhile(pfc, parent, &tok, scope); break; case pftFor: statement = parseFor(pfc, parent, &tok, scope); break; case pftBreak: statement = parseBreak(parent, &tok, scope); break; case pftContinue: statement = parseContinue(parent, &tok, scope); break; case pftReturn: statement = parseReturn(parent, &tok, scope); break; case pftClass: statement = parseClass(pfc, parent, &tok, scope); break; case pftInterface: statement = parseInterface(pfc, parent, &tok, scope); break; case pftTo: statement = parseTo(pfc, parent, &tok, scope); break; case pftOperator: statement = parseOperator(pfc, parent, &tok, scope); break; case pftPara: statement = parseParaStatement(pfc, parent, &tok, scope); break; case pftFlow: statement = parseFlow(pfc, parent, &tok, scope); break; case pftPolymorphic: statement = parsePolymorphic(pfc, parent, &tok, scope); break; case pftTry: statement = parseTry(pfc, parent, &tok, scope); break; case pftLocal: case pftGlobal: { enum pfTokType thisType = tok->type; enum pfTokType nextType = tok->next->type; enum pfAccessType access = (thisType == pftGlobal ? paGlobal : paLocal); if (nextType == pftTo || nextType == pftFlow || nextType == pftOperator) { tok = tok->next; statement = pfParseStatement(pfc, parent, &tok, scope); statement->access = access; } else if (nextType == pftClass || nextType == pftInterface) { struct pfParse *classPp; struct pfBaseType *base; tok = tok->next; statement = classPp = pfParseStatement(pfc, parent, &tok, scope); base = pfScopeFindType(scope, classPp->name); base->access = access; } else { statement = pfParseExpression(pfc, parent, &tok, scope); eatSemi(&tok); } break; } case pftConst: case pftStatic: case pftReadable: case pftWritable: case pftPlusPlus: case pftMinusMinus: case '(': statement = pfParseExpression(pfc, parent, &tok, scope); eatSemi(&tok); break; case pftName: statement = pfParseExpression(pfc, parent, &tok, scope); eatSemi(&tok); preventNakedUse(statement); break; case pftPrivate: case pftProtected: errAt(tok, "This word is reserved for future expansion."); statement = NULL; break; default: expectingGot("statement", tok); statement = NULL; break; } *pTokList = tok; return statement; } static struct pfParse *pfParseTokens(struct pfCompile *pfc, struct pfToken *tokList, struct pfScope *scope, struct pfParse *program) /* Convert token list to parsed program. */ { struct pfToken *tok = tokList; while (tok->type != pftEnd) { struct pfParse *statement = pfParseStatement(pfc, program, &tok, scope); slAddHead(&program->children, statement); } slReverse(&program->children); return program; } struct pfParse *pfParseModule(struct pfCompile *pfc, struct pfModule *module, struct pfParse *parent, struct pfScope *scope, enum pfParseType modType) /* Parse a module and return parse tree associated with it. */ { struct pfParse *modParse = pfParseNew(modType, NULL, parent, scope); pfParseTokens(pfc, module->tokList, scope, modParse); if (modType == pptMainModule) {FILE *f = mustOpen("out.preParse", "w"); pfParseDump(modParse, 0, f); carefulClose(&f);} module->pp = modParse; return modParse; }