ExprPtr Parser::patternMatch(bool se) { expect(TCase); auto const& value = primaryExpression(false); expect(TLBrace); IntrusiveRefCntPtr<MatchExpr> matchExpr(new MatchExpr(value)); do { if(token == TTypeIdent || token == TIdent) // TODO: firstPattern set { enterScope(new Scope); auto pt = pattern(false); // TODO: Allow pattern = expr auto body = exitScope<Expr>(block(true)); matchExpr->legs.push_back(MatchLeg(pt, body)); } } while(test(TSemicolon)); expect(TRBrace, se); return ExprPtr(matchExpr.getPtr()); // TEMP }
/* parses a class or an interface: * class Foo {} * class Foo extends Bar {} * class Foo extends Bar implements iFoo, iBar {} * interface iFoo {} * interface iBar extends iFoo {} * * if @name is not NULL, parses an anonymous class with name @name * new class {} * new class(1, 2) {} * new class(1, 2) extends Foo implements iFoo, iBar {} */ static boolean parseClassOrIface (tokenInfo *const token, const phpKind kind, const tokenInfo *name) { boolean readNext = TRUE; implType impl = CurrentStatement.impl; tokenInfo *nameFree = NULL; vString *inheritance = NULL; readToken (token); if (name) /* anonymous class */ { /* skip possible construction arguments */ skipOverParens (token); } else /* normal, named class */ { if (token->type != TOKEN_IDENTIFIER) return FALSE; name = nameFree = newToken (); copyToken (nameFree, token, TRUE); readToken (token); } inheritance = vStringNew (); /* read every identifiers, keywords and commas, and assume each * identifier (not keyword) is an inheritance * (like in "class Foo extends Bar implements iA, iB") */ while (token->type == TOKEN_IDENTIFIER || token->type == TOKEN_KEYWORD || token->type == TOKEN_COMMA) { if (token->type == TOKEN_IDENTIFIER) { if (vStringLength (inheritance) > 0) vStringPut (inheritance, ','); vStringCat (inheritance, token->string); } readToken (token); } makeClassOrIfaceTag (kind, name, inheritance, impl); if (token->type == TOKEN_OPEN_CURLY) enterScope (token, name->string, kind); else readNext = FALSE; if (nameFree) deleteToken (nameFree); vStringDelete (inheritance); return readNext; }
static void findPowerShellTags (void) { tokenInfo *const token = newToken (); do { enterScope (token, NULL, -1); } while (token->type != TOKEN_EOF); /* keep going even with unmatched braces */ deleteToken (token); }
ExprPtr Parser::controlSeqExpression(bool se) { if(test(TIf)) { auto ifExpr = enterScope<IfExpr>(new IfExpr); ifExpr->cond = expression(false); enterScope(new Scope); ifExpr->trueCase = exitScope<Expr>(controlSeqExpression(se)); exitScope<IfExpr>(ExprPtr()); ExprPtr ret = ifExpr; if(test(TElse)) { enterScope(new Scope); auto falseCase = exitScope<Expr>(controlSeqExpression(se)); ret = inScope(new IfElseExpr(ifExpr, falseCase)); } return ret; } /* TODO else if (Test(Token.While)) { var whileExpr = EnterScopeT<WhileExpr>(); whileExpr.Cond = Expression(false); whileExpr.Body = ControlSeqExpression(se); ExitScopeT<WhileExpr>(null); return whileExpr; }*/ else if(token == TLBrace) { return block(se); } assert(false); return ExprPtr(); }
static void enterScope (tokenInfo *const parentToken, const vString *const extraScope, const int parentKind) { tokenInfo *token = newToken (); int origParentKind = parentToken->parentKind; copyToken (token, parentToken, true); if (extraScope) { addToScope (token, extraScope); token->parentKind = parentKind; } readToken (token); while (token->type != TOKEN_EOF && token->type != TOKEN_CLOSE_CURLY) { bool readNext = true; switch (token->type) { case TOKEN_OPEN_CURLY: enterScope (token, NULL, -1); break; case TOKEN_KEYWORD: readNext = parseFunction (token); break; case TOKEN_VARIABLE: readNext = parseVariable (token); break; default: break; } if (readNext) readToken (token); } copyToken (parentToken, token, false); parentToken->parentKind = origParentKind; deleteToken (token); }
static void findTags (boolean startsInPhpMode) { tokenInfo *const token = newToken (); InPhp = startsInPhpMode; CurrentStatement.access = ACCESS_UNDEFINED; CurrentStatement.impl = IMPL_UNDEFINED; CurrentNamesapce = vStringNew (); FullScope = vStringNew (); AnonymousID = 0; do { enterScope (token, NULL, -1); } while (token->type != TOKEN_EOF); /* keep going even with unmatched braces */ vStringDelete (FullScope); vStringDelete (CurrentNamesapce); deleteToken (token); }
/* parses namespace declarations * namespace Foo {} * namespace Foo\Bar {} * namespace Foo; * namespace Foo\Bar; * namespace; * napespace {} */ static boolean parseNamespace (tokenInfo *const token) { tokenInfo *nsToken = newToken (); vStringClear (CurrentNamesapce); copyToken (nsToken, token, FALSE); do { readToken (token); if (token->type == TOKEN_IDENTIFIER) { if (vStringLength (CurrentNamesapce) > 0) { const char *sep; sep = phpScopeSeparatorFor(K_NAMESPACE, K_NAMESPACE); vStringCatS (CurrentNamesapce, sep); } vStringCat (CurrentNamesapce, token->string); } } while (token->type != TOKEN_EOF && token->type != TOKEN_SEMICOLON && token->type != TOKEN_OPEN_CURLY); vStringTerminate (CurrentNamesapce); if (vStringLength (CurrentNamesapce) > 0) makeNamespacePhpTag (nsToken, CurrentNamesapce); if (token->type == TOKEN_OPEN_CURLY) enterScope (token, NULL, -1); deleteToken (nsToken); return TRUE; }
// handlerDecl ::= 'handler' IDENT (';' | [do] stmt) std::unique_ptr<Handler> FlowParser::handlerDecl() { FNTRACE(); FlowLocation loc(location()); nextToken(); // 'handler' consume(FlowToken::Ident); std::string name = stringValue(); if (consumeIf(FlowToken::Semicolon)) { // forward-declaration loc.update(end()); return std::make_unique<Handler>(name, loc); } std::unique_ptr<SymbolTable> st = enterScope("handler-" + name); std::unique_ptr<Stmt> body = stmt(); leaveScope(); if (!body) return nullptr; loc.update(body->location().end); // forward-declared / previousely -declared? if (Handler* handler = scope()->lookup<Handler>(name, Lookup::Self)) { if (handler->body() != nullptr) { // TODO say where we found the other hand, compared to this one. reportError("Redeclaring handler '%s'", handler->name().c_str()); return nullptr; } handler->implement(std::move(st), std::move(body)); handler->owner()->removeSymbol(handler); return std::unique_ptr<Handler>(handler); } return std::make_unique<Handler>(name, std::move(st), std::move(body), loc); }
/* parses a trait: * trait Foo {} */ static boolean parseTrait (tokenInfo *const token) { boolean readNext = TRUE; tokenInfo *name; readToken (token); if (token->type != TOKEN_IDENTIFIER) return FALSE; name = newToken (); copyToken (name, token, TRUE); makeSimplePhpTag (name, K_TRAIT, ACCESS_UNDEFINED); readToken (token); if (token->type == TOKEN_OPEN_CURLY) enterScope (token, name->string, K_TRAIT); else readNext = FALSE; deleteToken (name); return readNext; }
static void enterScope (tokenInfo *const parentToken, const vString *const extraScope, const int parentKind) { tokenInfo *token = newToken (); int origParentKind = parentToken->parentKind; copyToken (token, parentToken, TRUE); if (extraScope) { token->parentKind = parentKind; addToScope (token, extraScope, origParentKind); } readToken (token); while (token->type != TOKEN_EOF && token->type != TOKEN_CLOSE_CURLY) { boolean readNext = TRUE; switch (token->type) { case TOKEN_OPEN_CURLY: enterScope (token, NULL, -1); break; case TOKEN_KEYWORD: switch (token->keyword) { /* handle anonymous classes */ case KEYWORD_new: readToken (token); if (token->keyword != KEYWORD_class) readNext = FALSE; else { char buf[32]; tokenInfo *name = newToken (); copyToken (name, token, TRUE); snprintf (buf, sizeof buf, "AnonymousClass%u", ++AnonymousID); vStringCopyS (name->string, buf); readNext = parseClassOrIface (token, K_CLASS, name); deleteToken (name); } break; case KEYWORD_class: readNext = parseClassOrIface (token, K_CLASS, NULL); break; case KEYWORD_interface: readNext = parseClassOrIface (token, K_INTERFACE, NULL); break; case KEYWORD_trait: readNext = parseTrait (token); break; case KEYWORD_function: readNext = parseFunction (token, NULL); break; case KEYWORD_const: readNext = parseConstant (token); break; case KEYWORD_define: readNext = parseDefine (token); break; case KEYWORD_namespace: readNext = parseNamespace (token); break; case KEYWORD_private: CurrentStatement.access = ACCESS_PRIVATE; break; case KEYWORD_protected: CurrentStatement.access = ACCESS_PROTECTED; break; case KEYWORD_public: CurrentStatement.access = ACCESS_PUBLIC; break; case KEYWORD_var: CurrentStatement.access = ACCESS_PUBLIC; break; case KEYWORD_abstract: CurrentStatement.impl = IMPL_ABSTRACT; break; default: break; } break; case TOKEN_VARIABLE: readNext = parseVariable (token); break; default: break; } if (readNext) readToken (token); } copyToken (parentToken, token, FALSE); parentToken->parentKind = origParentKind; deleteToken (token); }
/* parse a function * * if @name is NULL, parses a normal function * function myfunc($foo, $bar) {} * function &myfunc($foo, $bar) {} * function myfunc($foo, $bar) : type {} * * if @name is not NULL, parses an anonymous function with name @name * $foo = function($foo, $bar) {} * $foo = function&($foo, $bar) {} * $foo = function($foo, $bar) use ($x, &$y) {} * $foo = function($foo, $bar) use ($x, &$y) : type {} */ static boolean parseFunction (tokenInfo *const token, const tokenInfo *name) { boolean readNext = TRUE; accessType access = CurrentStatement.access; implType impl = CurrentStatement.impl; tokenInfo *nameFree = NULL; readToken (token); /* skip a possible leading ampersand (return by reference) */ if (token->type == TOKEN_AMPERSAND) readToken (token); if (! name) { if (token->type != TOKEN_IDENTIFIER && token->type != TOKEN_KEYWORD) return FALSE; name = nameFree = newToken (); copyToken (nameFree, token, TRUE); readToken (token); } if (token->type == TOKEN_OPEN_PAREN) { vString *arglist = vStringNew (); int depth = 1; vStringPut (arglist, '('); do { readToken (token); switch (token->type) { case TOKEN_OPEN_PAREN: depth++; break; case TOKEN_CLOSE_PAREN: depth--; break; default: break; } /* display part */ switch (token->type) { case TOKEN_AMPERSAND: vStringPut (arglist, '&'); break; case TOKEN_CLOSE_CURLY: vStringPut (arglist, '}'); break; case TOKEN_CLOSE_PAREN: vStringPut (arglist, ')'); break; case TOKEN_CLOSE_SQUARE: vStringPut (arglist, ']'); break; case TOKEN_COLON: vStringPut (arglist, ':'); break; case TOKEN_COMMA: vStringCatS (arglist, ", "); break; case TOKEN_EQUAL_SIGN: vStringCatS (arglist, " = "); break; case TOKEN_OPEN_CURLY: vStringPut (arglist, '{'); break; case TOKEN_OPEN_PAREN: vStringPut (arglist, '('); break; case TOKEN_OPEN_SQUARE: vStringPut (arglist, '['); break; case TOKEN_PERIOD: vStringPut (arglist, '.'); break; case TOKEN_SEMICOLON: vStringPut (arglist, ';'); break; case TOKEN_BACKSLASH: vStringPut (arglist, '\\'); break; case TOKEN_STRING: { vStringCatS (arglist, "'"); vStringCat (arglist, token->string); vStringCatS (arglist, "'"); break; } case TOKEN_IDENTIFIER: case TOKEN_KEYWORD: case TOKEN_VARIABLE: { switch (vStringLast (arglist)) { case 0: case ' ': case '{': case '(': case '[': case '.': case '\\': /* no need for a space between those and the identifier */ break; default: vStringPut (arglist, ' '); break; } if (token->type == TOKEN_VARIABLE) vStringPut (arglist, '$'); vStringCat (arglist, token->string); break; } default: break; } } while (token->type != TOKEN_EOF && depth > 0); vStringTerminate (arglist); makeFunctionTag (name, arglist, access, impl); vStringDelete (arglist); readToken (token); /* normally it's an open brace or "use" keyword */ } /* skip use(...) */ if (token->type == TOKEN_KEYWORD && token->keyword == KEYWORD_use) { readToken (token); skipOverParens (token); } /* PHP7 return type declaration or if parsing Zephir, skip function return * type hint */ if ((getInputLanguage () == Lang_php && token->type == TOKEN_COLON) || (getInputLanguage () == Lang_zephir && token->type == TOKEN_OPERATOR)) { do readToken (token); while (token->type == TOKEN_IDENTIFIER || token->type == TOKEN_BACKSLASH); } if (token->type == TOKEN_OPEN_CURLY) enterScope (token, name->string, K_FUNCTION); else readNext = FALSE; if (nameFree) deleteToken (nameFree); return readNext; }
void check_stmts (tree t) { int typeL, typeR, t_start, t_end; for (; t != NULL; t = t->next) { switch (t->kind) { case Procedure: enterScope(); fprintf (stderr, "Procedure: scope++ : %d\n", scope); handle_decls (t->second); check_stmts (t->third); printST(); exitScope(); fprintf (stderr, "Procedure: scope-- : %d\n", scope); break; case Assign: if ( t->first->kind != LBrac) { // non-array assignment // ST lookup if ( ST[t->first->value]->valid && ST[t->first->value]->scope <= scope) { // entry in ST typeL = ST[t->first->value]->type; typeR = check_expr(t->second); } else { // no visible entry in ST fprintf(stderr, "Entry %d-%s invalid in ST:%d\n", t->first->value, id_name (t->first->value), scope); error_num++; return; } } else if (t->first->kind == LBrac) { // array assignment // ST array lookup if ( ST[t->first->first->value]->valid && ST[t->first->first->value]->type == Array && ST[t->first->first->value]->scope <= scope) { // entry in ST typeL = check_expr (t->first); typeR = check_expr (t->second); } else { // no visible entry in ST fprintf(stderr, "Entry %d-%s invalid in ST:%d\n", t->first->first->value, id_name (t->first->first->value), scope); error_num++; return; } } if (typeL != typeR) { // LHS and RHS of assignment must match type_error(t->kind); return; } break; case If: case Elsif: // expr in If must be Boolean if (check_expr(t->first) != Boolean) { type_error(t->kind); return; } check_stmts(t->second); check_stmts(t->third); break; case Else: check_stmts(t->first); break; case For: // starts a new scope enterScope(); fprintf (stderr, "For: scope++ : %d\n", scope); // 2 range values must be same type t_start = check_expr(t->second->first); t_end = check_expr(t->second->second); if ( t_start != t_end || t_start == NoType) { type_error(t->kind); return; } else { // add Ident to ST w/ type of t_start in a new scope int pos = t->first->value; // Ident in For loop if ( ST[pos]->valid == true) { push (ST[pos]); // push prev scope entry to stack } ST[pos]->index = pos; ST[pos]->type = t_start; ST[pos]->scope = scope; // will be scope not 2 char *tmp = id_name(pos); ST[pos]->name = tmp; ST[pos]->valid = true; ST[pos]->typeSize = -1; ST[pos]->addr = -1; } check_stmts(t->third); // body of For loop printST(); exitScope(); fprintf (stderr, "For: scope-- : %d\n", scope); break; // Exit without bool_expr handled in for loop b/c tree will be NULL case Exit: typeL = check_expr(t->first); if ( typeL != Boolean || typeL == NoType) { type_error( t->kind); } break; case Declare: // starts a new scope enterScope(); fprintf (stderr, "Declare: scope++ : %d\n", scope); handle_decls (t->first); check_stmts (t->second); printST(); exitScope(); fprintf (stderr, "Declare: scope-- : %d\n", scope); break; default: fprintf(stderr, "No stmt match for token %d\n", t->kind); // endScope() } // end switch statement } // end for loop } // end check_stmts()
void Parser::constructor(string const& name, IntrusiveRefCntPtr<TypeDef> type, TypePtr const& typeRef) { IntrusiveRefCntPtr<TypeConstructor> ctor(new TypeConstructor(name)); type->constructors.push_back(ctor); ctor->type = typeRef; expect(TLParen); if(token != TRParen) { do { bool isLet = test(TLet); string name; // TODO: Common pattern? TypePtr type = maybeType(false); if(! type) { name = expectIdent(); type = typeName(false); } ctor->members.push_back(TypeMember(name, type)); ++ctor->ctorParams; } while(test(TComma)); } { string const& ctorName = name; // Constructor function IntrusiveRefCntPtr<FuncDef> ctorFunc(new FuncDef(ctorName)); enterFunction(ctorFunc); enterScope(new Scope); // Constructor function has the same type parameters for(auto& p : type->typeParams) ctorFunc->typeParams.push_back(p); int dummy = 0; IntrusiveRefCntPtr<CreateExpr> body(new CreateExpr()); body->ctor = ctor; for(int i = 0; i < ctor->ctorParams; ++i) { auto& member = ctor->members[i]; auto p = declParam(ctorFunc, member.name, member.type->fresh(), &dummy); body->parameters.push_back(ExprPtr(new VarRef(p))); } ctorFunc->body = exitScope<Expr>(body); ctorFunc->returnType = typeRef; // TODO: Check for name collision mod->functions[ctorName] = ctorFunc; mod->constructorDefs[ctorName] = ctor; exitFunction(); } expect(TRParen, true); }
IntrusiveRefCntPtr<FuncDef> Parser::funcDef(bool allowImplicitTypeVars) { string const& name = nextIdent(false); // TODO: Always surround this in a type scope? IntrusiveRefCntPtr<FuncDef> def(new FuncDef(name)); int paramCount = 0; enterFunction(def); enterScope(new Scope); if(test(TLParen)) { if(token != TRParen) { do { TypePtr type; string paramName; if(allowImplicitTypeVars) { paramName = expectIdent(); type = typeNameOrNewVar(false); } else { type = maybeType(false); if(! type) { paramName = expectIdent(); type = typeName(false); } } declParam(def, paramName, type, ¶mCount); } while(test(TComma)); } expect(TRParen, true); } TypePtr returnType; if(firstType[token] || token == TIdent) // TODO: Types may be TIdent too, in certain circumstances { returnType = typeNameOrNewVar(true); } if(test(TLBrace)) { auto const& body = statements(); expect(TRBrace, true); def->body = exitScope<Expr>(body); } def->returnType = returnType; assert(paramCount == def->parameters.size()); // TODO: Check for name collision mod->functions[def->name] = def; exitFunction(); return def; }
/* parse a function * * function myfunc($foo, $bar) {} */ static bool parseFunction (tokenInfo *const token) { bool readNext = true; tokenInfo *nameFree = NULL; const char *access; readToken (token); if (token->type != TOKEN_IDENTIFIER) return false; access = parsePowerShellScope (token); nameFree = newToken (); copyToken (nameFree, token, true); readToken (token); if (token->type == TOKEN_OPEN_PAREN) { vString *arglist = vStringNew (); int depth = 1; vStringPut (arglist, '('); do { readToken (token); switch (token->type) { case TOKEN_OPEN_PAREN: depth++; break; case TOKEN_CLOSE_PAREN: depth--; break; default: break; } /* display part */ switch (token->type) { case TOKEN_CLOSE_CURLY: vStringPut (arglist, '}'); break; case TOKEN_CLOSE_PAREN: vStringPut (arglist, ')'); break; case TOKEN_CLOSE_SQUARE: vStringPut (arglist, ']'); break; case TOKEN_COLON: vStringPut (arglist, ':'); break; case TOKEN_COMMA: vStringCatS (arglist, ", "); break; case TOKEN_EQUAL_SIGN: vStringCatS (arglist, " = "); break; case TOKEN_OPEN_CURLY: vStringPut (arglist, '{'); break; case TOKEN_OPEN_PAREN: vStringPut (arglist, '('); break; case TOKEN_OPEN_SQUARE: vStringPut (arglist, '['); break; case TOKEN_PERIOD: vStringPut (arglist, '.'); break; case TOKEN_SEMICOLON: vStringPut (arglist, ';'); break; case TOKEN_STRING: vStringCatS (arglist, "'...'"); break; case TOKEN_IDENTIFIER: case TOKEN_KEYWORD: case TOKEN_VARIABLE: { switch (vStringLast (arglist)) { case 0: case ' ': case '{': case '(': case '[': case '.': /* no need for a space between those and the identifier */ break; default: vStringPut (arglist, ' '); break; } if (token->type == TOKEN_VARIABLE) vStringPut (arglist, '$'); vStringCat (arglist, token->string); break; } default: break; } } while (token->type != TOKEN_EOF && depth > 0); makeFunctionTag (nameFree, arglist, access); vStringDelete (arglist); readToken (token); } else if (token->type == TOKEN_OPEN_CURLY) { /* filters doesn't need to have an arglist */ makeFunctionTag (nameFree, NULL, access); } if (token->type == TOKEN_OPEN_CURLY) enterScope (token, nameFree->string, K_FUNCTION); else readNext = false; if (nameFree) deleteToken (nameFree); return readNext; }