StateDeclarationStatement ParseStateDeclaration(TokenRange toks) { toks = toks.Consume("state").RevSkipWhile(t => t.Value == ";"); return(new StateDeclarationStatement { decl = ParseVarDeclaration(toks) }); }
CodeBlock ParseCodeBlock(TokenRange toks) { List <Statement> statements = new List <Statement>(); while (true) { Token delim = toks .FirstOrDefault( t => t.ParenDepth == toks.First().ParenDepth&& t.BraceDepth == toks.First().BraceDepth&& (t.Value == ";" || t.Value == "}") ); if (delim == null) { break; } ParseStatement(range(toks.Begin, delim.Position + 1), statements); toks = range(delim.Position + 1, toks.End); } if (!toks.All(Whitespace)) { throw new Error(toks.First(NonWhitespace).SourceLine, "Trailing unterminated statement in code block"); } return(new CodeBlock { statements = statements.ToArray() }); }
List <Declaration> ParseDescrCodeBlock(TokenRange toks) { List <Declaration> declarations = new List <Declaration>(); while (true) { Token delim = toks.FirstOrDefault(t => t.Value == ";"); if (delim == null) { break; } int pos = delim.Position + 1; var potentialComment = range(pos, toks.End).SkipWhile(t => t.Value == "\t" || t.Value == " "); if (!potentialComment.IsEmpty && potentialComment.First().Value.StartsWith("//")) { pos = potentialComment.First().Position + 1; } ParseDeclaration(range(toks.Begin, pos), declarations); toks = range(pos, toks.End); } if (!toks.All(Whitespace)) { throw new Error(toks.First(NonWhitespace).SourceLine, "Trailing unterminated statement in code block"); } return(declarations); }
ChooseStatement ParseChooseStatement(TokenRange toks) { return(new ChooseStatement { body = ParseCompoundStatement(toks.Consume("choose")) }); }
Statement ParseTryStatement(TokenRange toks) { return(new TryStatement { tryBody = ParseCompoundStatement(toks.Consume("try")), catches = new List <TryStatement.Catch>() // will be filled in later by ParseCatchStatement }); }
ThrowStatement ParseThrowStatement(TokenRange toks) { toks = toks.Consume("throw").RevSkipWhile(t => t.Value == ";"); return(new ThrowStatement { expression = str(NormalizeWhitespace(toks)) }); }
public TokenRange GetMatchingRangeIn(TokenRange range) { Func <Token, bool> pred; int dir; switch (Value) { case "(": pred = t => t.Value != ")" || t.ParenDepth != ParenDepth; dir = +1; break; case ")": pred = t => t.Value != "(" || t.ParenDepth != ParenDepth; dir = -1; break; case "{": pred = t => t.Value != "}" || t.BraceDepth != BraceDepth; dir = +1; break; case "}": pred = t => t.Value != "{" || t.BraceDepth != BraceDepth; dir = -1; break; case "<": return (new TokenRange(range.GetAllTokens(), Position + 1, AngleBracketParser.NotInsideAngleBrackets( new TokenRange(range.GetAllTokens(), Position, range.End)) .Skip(1) // skip the "<", which is considered "outside" .First() // get the ">", which is likewise "outside" .Position)); case "[": return (new TokenRange(range.GetAllTokens(), Position + 1, BracketParser.NotInsideBrackets( new TokenRange(range.GetAllTokens(), Position, range.End)) .Skip(1) // skip the "[", which is considered "outside" .First() // get the "]", which is likewise "outside" .Position)); default: throw new NotSupportedException("Can't match this token!"); } TokenRange r; if (dir == -1) { r = new TokenRange(range.GetAllTokens(), range.Begin, Position) .RevTakeWhile(pred); if (r.Begin == range.Begin) { throw new Error(SourceLine, "Syntax error: Unmatched " + Value); } } else { r = new TokenRange(range.GetAllTokens(), Position + 1, range.End) .TakeWhile(pred); if (r.End == range.End) { throw new Error(SourceLine, "Syntax error: Unmatched " + Value); } } return(r); }
WaitStatement ParseWaitStatement(TokenRange toks) { WaitStatement ws = new WaitStatement(); ws.FirstSourceLine = toks.First().SourceLine; if (toks.First().Value == "state") { ws.resultIsState = true; toks = toks.Consume("state"); } Token name; TokenRange type, initializer; bool constructorSyntax; ParseDeclaration(toks.RevSkipWhile(t => t.Value == ";"), out name, out type, out initializer, out constructorSyntax); ws.result = new VarDeclaration { name = name.Value, type = str(NormalizeWhitespace(type)), initializer = "", initializerConstructorSyntax = false }; if (initializer == null) { throw new Error(ws.FirstSourceLine, "Wait statement must be a declaration"); } var waitParams = initializer .SkipWhile(Whitespace).Consume("Statement contains a wait, but is not a valid wait statement or a supported compound statement.", t => { if (t.Value == "wait") { return(true); } if (t.Value == "waitNext") { ws.isWaitNext = true; return(true); } return(false); }) .SkipWhile(Whitespace).First().Assert("Expected (", t => t.Value == "(") .GetMatchingRangeIn(initializer); if (!range(waitParams.End, initializer.End).Consume(")").All(Whitespace)) { throw new Error(toks.First().SourceLine, "Statement contains a wait, but is not a valid wait statement or a supported compound statement."); } ws.futureExpression = str(NormalizeWhitespace(waitParams)); return(ws); }
WhenStatement ParseWhenStatement(TokenRange toks) { var expr = toks.Consume("when") .First(NonWhitespace) .Assert("Expected (", t => t.Value == "(") .GetMatchingRangeIn(toks); return(new WhenStatement { wait = ParseWaitStatement(expr), body = ParseCompoundStatement(range(expr.End + 1, toks.End)) }); }
Statement ParseIfStatement(TokenRange toks) { var expr = toks.Consume("if") .First(NonWhitespace) .Assert("Expected (", t => t.Value == "(") .GetMatchingRangeIn(toks); return(new IfStatement { expression = str(NormalizeWhitespace(expr)), ifBody = ParseCompoundStatement(range(expr.End + 1, toks.End)) // elseBody will be filled in later if necessary by ParseElseStatement }); }
WhileStatement ParseWhileStatement(TokenRange toks) { var expr = toks.Consume("while") .First(NonWhitespace) .Assert("Expected (", t => t.Value == "(") .GetMatchingRangeIn(toks); return(new WhileStatement { expression = str(NormalizeWhitespace(expr)), body = ParseCompoundStatement(range(expr.End + 1, toks.End)) }); }
Statement ParseForStatement(TokenRange toks) { var head = toks.Consume("for") .First(NonWhitespace) .Assert("Expected (", t => t.Value == "(") .GetMatchingRangeIn(toks); Token[] delim = head.Where( t => t.ParenDepth == head.First().ParenDepth&& t.BraceDepth == head.First().BraceDepth&& t.Value == ";" ).ToArray(); if (delim.Length == 2) { var init = range(head.Begin, delim[0].Position); var cond = range(delim[0].Position + 1, delim[1].Position); var next = range(delim[1].Position + 1, head.End); var body = range(head.End + 1, toks.End); return(new ForStatement { initExpression = str(NormalizeWhitespace(init)), condExpression = str(NormalizeWhitespace(cond)), nextExpression = str(NormalizeWhitespace(next)), body = ParseCompoundStatement(body) }); } delim = head.Where( t => t.ParenDepth == head.First().ParenDepth&& t.BraceDepth == head.First().BraceDepth&& t.Value == ":" ).ToArray(); if (delim.Length != 1) { throw new Error(head.First().SourceLine, "for statement must be 3-arg style or c++11 2-arg style"); } return(new RangeForStatement { // The container over which to iterate rangeExpression = str(NormalizeWhitespace(range(delim[0].Position + 1, head.End).SkipWhile(Whitespace))), // Type and name of the variable assigned in each iteration rangeDecl = str(NormalizeWhitespace(range(head.Begin, delim[0].Position - 1).SkipWhile(Whitespace))), // The body of the for loop body = ParseCompoundStatement(range(head.End + 1, toks.End)) }); }
void ParseDescrHeading(Descr descr, TokenRange toks) { toks.First(NonWhitespace).Assert("non-struct DESCR!", t => t.Value == "struct"); toks = toks.SkipWhile(Whitespace).Skip(1).SkipWhile(Whitespace); var colon = toks.FirstOrDefault(t => t.Value == ":"); if (colon != null) { descr.superClassList = str(range(colon.Position + 1, toks.End)).Trim(); toks = range(toks.Begin, colon.Position); } descr.name = str(toks).Trim(); }
void ParseElseStatement(TokenRange toks, Statement prevStatement) { var ifStatement = prevStatement as IfStatement; while (ifStatement != null && ifStatement.elseBody != null) { ifStatement = ifStatement.elseBody as IfStatement; } if (ifStatement == null) { throw new Error(toks.First().SourceLine, "else without matching if"); } ifStatement.elseBody = ParseCompoundStatement(toks.Consume("else")); }
void ParseDeclaration(TokenRange toks, List <Declaration> declarations) { Declaration dec = new Declaration(); Token delim = toks.First(t => t.Value == ";"); var nameRange = range(toks.Begin, delim.Position).RevSkipWhile(Whitespace).RevTakeWhile(NonWhitespace); var typeRange = range(toks.Begin, nameRange.Begin); var commentRange = range(delim.Position + 1, toks.End); dec.name = str(nameRange).Trim(); dec.type = str(typeRange).Trim(); dec.comment = str(commentRange).Trim().TrimStart('/'); declarations.Add(dec); }
void ParseTestCaseHeading(Actor actor, TokenRange toks) { actor.isStatic = true; // The parameter(s) to the TEST_CASE macro are opaque to the actor compiler TokenRange paramRange = toks.Last(NonWhitespace) .Assert("Unexpected tokens after test case parameter list.", t => t.Value == ")" && t.ParenDepth == toks.First().ParenDepth) .GetMatchingRangeIn(toks); actor.testCaseParameters = str(paramRange); actor.name = "flowTestCase" + toks.First().SourceLine; actor.parameters = new VarDeclaration[] { }; actor.returnType = "Void"; }
IEnumerable <TokenRange> SplitParameterList(TokenRange toks, string delimiter) { if (toks.Begin == toks.End) { yield break; } while (true) { Token comma = AngleBracketParser.NotInsideAngleBrackets(toks) .FirstOrDefault(t => t.Value == delimiter && t.ParenDepth == toks.First().ParenDepth); if (comma == null) { break; } yield return(range(toks.Begin, comma.Position)); toks = range(comma.Position + 1, toks.End); } yield return(toks); }
Statement ParseCompoundStatement(TokenRange toks) { var first = toks.First(NonWhitespace); if (first.Value == "{") { var inBraces = first.GetMatchingRangeIn(toks); if (!range(inBraces.End, toks.End).Consume("}").All(Whitespace)) { throw new Error(inBraces.Last().SourceLine, "Unexpected tokens after compound statement"); } return(ParseCodeBlock(inBraces)); } else { List <Statement> statements = new List <Statement>(); ParseStatement(toks.Skip(1), statements); return(statements[0]); } }
void ParseCatchStatement(TokenRange toks, Statement prevStatement) { var tryStatement = prevStatement as TryStatement; if (tryStatement == null) { throw new Error(toks.First().SourceLine, "catch without matching try"); } var expr = toks.Consume("catch") .First(NonWhitespace) .Assert("Expected (", t => t.Value == "(") .GetMatchingRangeIn(toks); tryStatement.catches.Add( new TryStatement.Catch { expression = str(NormalizeWhitespace(expr)), body = ParseCompoundStatement(range(expr.End + 1, toks.End)), FirstSourceLine = expr.First().SourceLine }); }
void ParseActorHeading(Actor actor, TokenRange toks) { var template = toks.First(NonWhitespace); if (template.Value == "template") { var templateParams = range(template.Position + 1, toks.End) .First(NonWhitespace) .Assert("Invalid template declaration", t => t.Value == "<") .GetMatchingRangeIn(toks); actor.templateFormals = SplitParameterList(templateParams, ",") .Select(p => ParseVarDeclaration(p)) //< SOMEDAY: ? .ToArray(); toks = range(templateParams.End + 1, toks.End); } var attribute = toks.First(NonWhitespace); while (attribute.Value == "[") { var attributeContents = attribute.GetMatchingRangeIn(toks); var asArray = attributeContents.ToArray(); if (asArray.Length < 2 || asArray[0].Value != "[" || asArray[asArray.Length - 1].Value != "]") { throw new Error(actor.SourceLine, "Invalid attribute: Expected [[...]]"); } actor.attributes.Add("[" + str(NormalizeWhitespace(attributeContents)) + "]"); toks = range(attributeContents.End + 1, toks.End); attribute = toks.First(NonWhitespace); } var staticKeyword = toks.First(NonWhitespace); if (staticKeyword.Value == "static") { actor.isStatic = true; toks = range(staticKeyword.Position + 1, toks.End); } var uncancellableKeyword = toks.First(NonWhitespace); if (uncancellableKeyword.Value == "UNCANCELLABLE") { actor.SetUncancellable(); toks = range(uncancellableKeyword.Position + 1, toks.End); } // Find the parameter list TokenRange paramRange = toks.Last(NonWhitespace) .Assert("Unexpected tokens after actor parameter list.", t => t.Value == ")" && t.ParenDepth == toks.First().ParenDepth) .GetMatchingRangeIn(toks); actor.parameters = SplitParameterList(paramRange, ",") .Select(p => ParseVarDeclaration(p)) .ToArray(); var name = range(toks.Begin, paramRange.Begin - 1).Last(NonWhitespace); actor.name = name.Value; // SOMEDAY: refactor? var returnType = range(toks.First().Position + 1, name.Position).SkipWhile(Whitespace); var retToken = returnType.First(); if (retToken.Value == "Future") { var ofType = returnType.Skip(1).First(NonWhitespace).Assert("Expected <", tok => tok.Value == "<").GetMatchingRangeIn(returnType); actor.returnType = str(NormalizeWhitespace(ofType)); toks = range(ofType.End + 1, returnType.End); } else if (retToken.Value == "void" /* && !returnType.Skip(1).Any(NonWhitespace)*/) { actor.returnType = null; toks = returnType.Skip(1); } else { throw new Error(actor.SourceLine, "Actor apparently does not return Future<T>"); } toks = toks.SkipWhile(Whitespace); if (!toks.IsEmpty) { if (toks.Last().Value == "::") { actor.nameSpace = str(range(toks.Begin, toks.End - 1)); } else { Console.WriteLine("Tokens: '{0}' {1} '{2}'", str(toks), toks.Count(), toks.Last().Value); throw new Error(actor.SourceLine, "Unrecognized tokens preceding parameter list in actor declaration"); } } if (errorMessagePolicy.ActorsNoDiscardByDefault() && !actor.attributes.Contains("[[flow_allow_discard]]")) { if (actor.IsCancellable()) { actor.attributes.Add("[[nodiscard]]"); } } HashSet <string> knownFlowAttributes = new HashSet <string>(); knownFlowAttributes.Add("[[flow_allow_discard]]"); foreach (var flowAttribute in actor.attributes.Where(a => a.StartsWith("[[flow_"))) { if (!knownFlowAttributes.Contains(flowAttribute)) { throw new Error(actor.SourceLine, "Unknown flow attribute {0}", flowAttribute); } } actor.attributes = actor.attributes.Where(a => !a.StartsWith("[[flow_")).ToList(); }
void ParseStatement(TokenRange toks, List <Statement> statements) { toks = toks.SkipWhile(Whitespace); Action <Statement> Add = stmt => { stmt.FirstSourceLine = toks.First().SourceLine; statements.Add(stmt); }; switch (toks.First().Value) { case "loop": Add(ParseLoopStatement(toks)); break; case "while": Add(ParseWhileStatement(toks)); break; case "for": Add(ParseForStatement(toks)); break; case "break": Add(new BreakStatement()); break; case "continue": Add(new ContinueStatement()); break; case "return": Add(ParseReturnStatement(toks)); break; case "{": Add(ParseCompoundStatement(toks)); break; case "if": Add(ParseIfStatement(toks)); break; case "else": ParseElseStatement(toks, statements[statements.Count - 1]); break; case "choose": Add(ParseChooseStatement(toks)); break; case "when": Add(ParseWhenStatement(toks)); break; case "try": Add(ParseTryStatement(toks)); break; case "catch": ParseCatchStatement(toks, statements[statements.Count - 1]); break; case "throw": Add(ParseThrowStatement(toks)); break; default: if (IllegalKeywords.Contains(toks.First().Value)) { throw new Error(toks.First().SourceLine, "Statement '{0}' not supported in actors.", toks.First().Value); } if (toks.Any(t => t.Value == "wait" || t.Value == "waitNext")) { Add(ParseWaitStatement(toks)); } else if (toks.First().Value == "state") { Add(ParseStateDeclaration(toks)); } else if (toks.First().Value == "switch" && toks.Any(t => t.Value == "return")) { throw new Error(toks.First().SourceLine, "Unsupported compound statement containing return."); } else if (toks.RevSkipWhile(t => t.Value == ";").Any(NonWhitespace)) { Add(new PlainOldCodeStatement { code = str(NormalizeWhitespace(toks.RevSkipWhile(t => t.Value == ";"))) + ";" }); } break; } ; }
IEnumerable <Token> NormalizeWhitespace(IEnumerable <Token> tokens) { bool inWhitespace = false; bool leading = true; foreach (var tok in tokens) { if (!tok.IsWhitespace) { if (inWhitespace && !leading) { yield return new Token { Value = " " } } ; inWhitespace = false; yield return(tok); leading = false; } else { inWhitespace = true; } } } void ParseDeclaration(TokenRange tokens, out Token name, out TokenRange type, out TokenRange initializer, out bool constructorSyntax) { initializer = null; TokenRange beforeInitializer = tokens; constructorSyntax = false; Token equals = AngleBracketParser.NotInsideAngleBrackets(tokens) .FirstOrDefault(t => t.Value == "=" && t.ParenDepth == tokens.First().ParenDepth); if (equals != null) { // type name = initializer; beforeInitializer = range(tokens.Begin, equals.Position); initializer = range(equals.Position + 1, tokens.End); } else { Token paren = AngleBracketParser.NotInsideAngleBrackets(tokens) .FirstOrDefault(t => t.Value == "("); if (paren != null) { // type name(initializer); constructorSyntax = true; beforeInitializer = range(tokens.Begin, paren.Position); initializer = range(paren.Position + 1, tokens.End) .TakeWhile(t => t.ParenDepth > paren.ParenDepth); } } name = beforeInitializer.Last(NonWhitespace); if (beforeInitializer.Begin == name.Position) { throw new Error(beforeInitializer.First().SourceLine, "Declaration has no type."); } type = range(beforeInitializer.Begin, name.Position); } VarDeclaration ParseVarDeclaration(TokenRange tokens) { Token name; TokenRange type, initializer; bool constructorSyntax; ParseDeclaration(tokens, out name, out type, out initializer, out constructorSyntax); return(new VarDeclaration { name = name.Value, type = str(NormalizeWhitespace(type)), initializer = initializer == null ? "" : str(NormalizeWhitespace(initializer)), initializerConstructorSyntax = constructorSyntax }); }
void ParseActorHeading(Actor actor, TokenRange toks) { var template = toks.First(NonWhitespace); if (template.Value == "template") { var templateParams = range(template.Position + 1, toks.End) .First(NonWhitespace) .Assert("Invalid template declaration", t => t.Value == "<") .GetMatchingRangeIn(toks); actor.templateFormals = SplitParameterList(templateParams, ",") .Select(p => ParseVarDeclaration(p)) //< SOMEDAY: ? .ToArray(); toks = range(templateParams.End + 1, toks.End); } var staticKeyword = toks.First(NonWhitespace); if (staticKeyword.Value == "static") { actor.isStatic = true; toks = range(staticKeyword.Position + 1, toks.End); } var uncancellableKeyword = toks.First(NonWhitespace); if (uncancellableKeyword.Value == "UNCANCELLABLE") { actor.isUncancellable = true; toks = range(uncancellableKeyword.Position + 1, toks.End); } // Find the parameter list TokenRange paramRange = toks.Last(NonWhitespace) .Assert("Unexpected tokens after actor parameter list.", t => t.Value == ")" && t.ParenDepth == toks.First().ParenDepth) .GetMatchingRangeIn(toks); actor.parameters = SplitParameterList(paramRange, ",") .Select(p => ParseVarDeclaration(p)) .ToArray(); var name = range(toks.Begin, paramRange.Begin - 1).Last(NonWhitespace); actor.name = name.Value; // SOMEDAY: refactor? var returnType = range(toks.First().Position + 1, name.Position).SkipWhile(Whitespace); var retToken = returnType.First(); if (retToken.Value == "Future") { var ofType = returnType.Skip(1).First(NonWhitespace).Assert("Expected <", tok => tok.Value == "<").GetMatchingRangeIn(returnType); actor.returnType = str(NormalizeWhitespace(ofType)); toks = range(ofType.End + 1, returnType.End); } else if (retToken.Value == "void" /* && !returnType.Skip(1).Any(NonWhitespace)*/) { actor.returnType = null; toks = returnType.Skip(1); } else { throw new Error(actor.SourceLine, "Actor apparently does not return Future<T>"); } toks = toks.SkipWhile(Whitespace); if (!toks.IsEmpty) { if (toks.Last().Value == "::") { actor.nameSpace = str(range(toks.Begin, toks.End - 1)); } else { Console.WriteLine("Tokens: '{0}' {1} '{2}'", str(toks), toks.Count(), toks.Last().Value); throw new Error(actor.SourceLine, "Unrecognized tokens preceding parameter list in actor declaration"); } } }
LoopStatement ParseLoopStatement(TokenRange toks) { return(new LoopStatement { body = ParseCompoundStatement(toks.Consume("loop")) }); }