internal override void Accept(NodeFnDef fn) { // Leaves the function on the stack Accept(fn as NodeFnExpr); // Assign it to a thing if (fn.isGlobal) { // I hate nesting ifs without brackets... // The only valid node here will be an identifier if (fn.target is NodeIdentifier) { builder.currentLineNumber = fn.target.EndLine; builder.OpGStore((fn.target as NodeIdentifier).image); } else log.Error(fn.target.location, "Function target must be an identifier when assigning to the global namespace."); } else { // I really hate nesting ifs without brackets... if (fn.target is NodeIdentifier) { builder.currentLineNumber = fn.target.EndLine; builder.OpKStore((fn.target as NodeIdentifier).image); } // TODO check all the things plz else log.Error(fn.target.location, "Invalid function target."); } // We'll always have a value on the stack here, pop the value if we don't need it if (!fn.isResultRequired) builder.OpPop(); }
void ASTVisitor.Accept(NodeFnDef value) { Accept(value); }
private Node ParseFn(bool isGenerator = false) { // Use the location of 'fn' as the location of this function. // The end of the function can be determined by the end of // it's body, so it's only expected to rely on the // line/col fields for functions. var loc = Location; // The first token we'll have is 'fn', so skip it. Advance(); // We'll use this later, after checking for a name NodeFnExpr fn; // Next, check if there's an open brace. // If an open brace follows 'fn', it must be a lambda, // otherwise it's a definition, we need a location. if (Check(ASSIGN) || Check(OPEN_CURLY_BRACE)) { fn = new NodeFnExpr(loc); fn.fullAnon = true; } else if (!Check(OPEN_BRACE)) { fn = new NodeFnDef(loc); (fn as NodeFnDef).target = PrimaryExpression(false); } else fn = new NodeFnExpr(loc); fn.isGenerator = isGenerator; if (!fn.fullAnon) { // Hope that this is the open brace we seek. // TODO If not, maybe I should try to recover better. Expect(OPEN_BRACE, "Expected an open brace ('(') to start the function parameter list."); // Now, we need to get the argument list. while (true) { if (!TokensRemain) { log.Error(tokens[-1].location, "End of file reached, expected a parameter declaration."); break; } // Break out early if we see a close brace. // Even if it's not at the right time, it's // a safe place to break. if (Check(CLOSE_BRACE)) break; // Next check for a parameter. string paramName; // If there's an identifier, it's a param if (ExpectIdentifier(out paramName, "Expected identifier to name function parameter.")) fn.paramNames.Add(paramName); // Otherwise, let's try to get the next param immediately // rather than skipping or fumbling with more tokens. else continue; if (Check(ASSIGN)) { Advance(); if (!TokensRemain) { log.Error(tokens[-1].location, "Expected expression for default parameter value, but the end of the file was reached."); return fn; } fn.defaultParams.Add(Expression()); } else if (fn.defaultParams.Count > 0) fn.defaultParams.Add(new NodeNull(null)); // TODO check for vargs. // If there's a comma, we expect there to be more parameters. // Simply skip the comma and prepare for more. if (Check(COMMA)) Advance(); // No more parameters, break out. else break; } // Param lists end with ')', so look for it. Expect(CLOSE_BRACE, "Expected a close brace (')') to end the function parameter list."); } // If the function is declared with an assign operator, it shouldn't be void. if (TokensRemain && Current.type == ASSIGN) { if (fn.isGenerator) log.Error(Location, "Generators must be declared void."); Advance(); fn.isVoid = false; } else fn.isVoid = true; // Finally, we get the body of the function. // Whether or not a function is void, its body is always an expression. if (!TokensRemain) { log.Error(fn.location, "Expected expression for function body, but the end of the file was reached."); return fn; } SetCurrentFunction(fn); fn.body = Expression(); ReleaseCurrentFunction(); return fn; }