1 /* 2 * ActorParser.cs 3 * 4 * This source file is part of the FoundationDB open source project 5 * 6 * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 21 using System; 22 using System.Collections.Generic; 23 using System.Linq; 24 using System.Text; 25 using System.Text.RegularExpressions; 26 27 namespace actorcompiler 28 { 29 class Error : Exception 30 { 31 public int SourceLine { get; private set; } Error(int SourceLine, string format, params object[] args)32 public Error(int SourceLine, string format, params object[] args) 33 : base(string.Format(format,args)) 34 { 35 this.SourceLine = SourceLine; 36 } 37 }; 38 39 class ErrorMessagePolicy 40 { 41 public bool DisableActorWithoutWaitWarning = false; HandleActorWithoutWait(String sourceFile, Actor actor)42 public void HandleActorWithoutWait(String sourceFile, Actor actor) 43 { 44 if (!DisableActorWithoutWaitWarning && !actor.isTestCase) 45 { 46 // TODO(atn34): Once cmake is the only build system we can make this an error instead of a warning. 47 Console.Error.WriteLine("{0}:{1}: warning: ACTOR {2} does not contain a wait() statement", sourceFile, actor.SourceLine, actor.name); 48 } 49 } 50 } 51 52 class Token 53 { 54 public string Value; 55 public int Position; 56 public int SourceLine; 57 public int BraceDepth; 58 public int ParenDepth; 59 public bool IsWhitespace { get { return Value == " " || Value == "\n" || Value == "\r" || Value == "\r\n" || Value == "\t" || Value.StartsWith("//") || Value.StartsWith("/*"); } } ToString()60 public override string ToString() { return Value; } Assert(string error, Func<Token, bool> pred)61 public Token Assert(string error, Func<Token, bool> pred) 62 { 63 if (!pred(this)) throw new Error(SourceLine, error); 64 return this; 65 } GetMatchingRangeIn(TokenRange range)66 public TokenRange GetMatchingRangeIn(TokenRange range) 67 { 68 Func<Token,bool> pred; 69 int dir; 70 switch (Value) { 71 case "(": pred = t=> t.Value != ")" || t.ParenDepth != ParenDepth; dir = +1; break; 72 case ")": pred = t=> t.Value != "(" || t.ParenDepth != ParenDepth; dir = -1; break; 73 case "{": pred = t=> t.Value != "}" || t.BraceDepth != BraceDepth; dir = +1; break; 74 case "}": pred = t=> t.Value != "{" || t.BraceDepth != BraceDepth; dir = -1; break; 75 case "<": return 76 new TokenRange(range.GetAllTokens(), 77 Position+1, 78 AngleBracketParser.NotInsideAngleBrackets( 79 new TokenRange(range.GetAllTokens(), Position, range.End)) 80 .Skip(1) // skip the "<", which is considered "outside" 81 .First() // get the ">", which is likewise "outside" 82 .Position); 83 default: throw new NotSupportedException("Can't match this token!"); 84 } 85 TokenRange r; 86 if (dir == -1) 87 { 88 r = new TokenRange(range.GetAllTokens(), range.Begin, Position) 89 .RevTakeWhile(pred); 90 if (r.Begin == range.Begin) 91 throw new Error(SourceLine, "Syntax error: Unmatched " + Value); 92 } 93 else 94 { 95 r = new TokenRange(range.GetAllTokens(), Position+1, range.End) 96 .TakeWhile(pred); 97 if (r.End == range.End) 98 throw new Error(SourceLine, "Syntax error: Unmatched " + Value); 99 } 100 return r; 101 } 102 }; 103 104 class TokenRange : IEnumerable<Token> 105 { TokenRange(Token[] tokens, int beginPos, int endPos)106 public TokenRange(Token[] tokens, int beginPos, int endPos) 107 { 108 if (beginPos > endPos) throw new InvalidOperationException("Invalid TokenRange"); 109 this.tokens = tokens; 110 this.beginPos = beginPos; 111 this.endPos = endPos; 112 } 113 114 public bool IsEmpty { get { return beginPos==endPos; } } 115 public int Begin { get { return beginPos; } } 116 public int End { get { return endPos; } } First()117 public Token First() { 118 if (beginPos == endPos) throw new InvalidOperationException("Empty TokenRange"); 119 return tokens[beginPos]; 120 } Last()121 public Token Last() { 122 if (beginPos == endPos) throw new InvalidOperationException("Empty TokenRange"); 123 return tokens[endPos - 1]; 124 } Last(Func<Token, bool> pred)125 public Token Last(Func<Token, bool> pred) 126 { 127 for (int i = endPos - 1; i >= beginPos; i--) 128 if (pred(tokens[i])) 129 return tokens[i]; 130 throw new Exception("Matching token not found"); 131 } Skip(int count)132 public TokenRange Skip(int count) 133 { 134 return new TokenRange(tokens, beginPos + count, endPos); 135 } Consume(string value)136 public TokenRange Consume(string value) 137 { 138 First().Assert("Expected " + value, t => t.Value == value); 139 return Skip(1); 140 } Consume(string error, Func<Token, bool> pred)141 public TokenRange Consume(string error, Func<Token, bool> pred) 142 { 143 First().Assert(error, pred); 144 return Skip(1); 145 } System.Collections.IEnumerable.GetEnumerator()146 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } GetEnumerator()147 public IEnumerator<Token> GetEnumerator() 148 { 149 for (int i = beginPos; i < endPos; i++) 150 yield return tokens[i]; 151 } SkipWhile(Func<Token, bool> pred)152 public TokenRange SkipWhile(Func<Token, bool> pred) 153 { 154 for (int e = beginPos; e < endPos; e++) 155 if (!pred(tokens[e])) 156 return new TokenRange(tokens, e, endPos); 157 return new TokenRange(tokens, endPos, endPos); 158 } TakeWhile(Func<Token, bool> pred)159 public TokenRange TakeWhile(Func<Token, bool> pred) 160 { 161 for (int e = beginPos; e < endPos; e++) 162 if (!pred(tokens[e])) 163 return new TokenRange(tokens, beginPos, e); 164 return new TokenRange(tokens, beginPos, endPos); 165 } RevTakeWhile(Func<Token, bool> pred)166 public TokenRange RevTakeWhile(Func<Token, bool> pred) 167 { 168 for (int e = endPos-1; e >= beginPos; e--) 169 if (!pred(tokens[e])) 170 return new TokenRange(tokens, e+1, endPos); 171 return new TokenRange(tokens, beginPos, endPos); 172 } RevSkipWhile(Func<Token, bool> pred)173 public TokenRange RevSkipWhile(Func<Token, bool> pred) 174 { 175 for (int e = endPos - 1; e >= beginPos; e--) 176 if (!pred(tokens[e])) 177 return new TokenRange(tokens, beginPos, e + 1); 178 return new TokenRange(tokens, beginPos, beginPos); 179 } GetAllTokens()180 public Token[] GetAllTokens() { return tokens; } 181 182 public int Length { 183 get { 184 return endPos - beginPos; 185 } 186 } 187 188 Token[] tokens; 189 int beginPos; 190 int endPos; 191 }; 192 193 static class AngleBracketParser 194 { NotInsideAngleBrackets(IEnumerable<Token> tokens)195 public static IEnumerable<Token> NotInsideAngleBrackets(IEnumerable<Token> tokens) 196 { 197 int AngleDepth = 0; 198 int? BasePD = null; 199 foreach (var tok in tokens) 200 { 201 if (BasePD == null) BasePD = tok.ParenDepth; 202 if (tok.ParenDepth == BasePD && tok.Value == ">") AngleDepth--; 203 if (AngleDepth == 0) 204 yield return tok; 205 if (tok.ParenDepth == BasePD && tok.Value == "<") AngleDepth++; 206 } 207 } 208 }; 209 210 class ActorParser 211 { 212 public bool LineNumbersEnabled = true; 213 214 Token[] tokens; 215 string sourceFile; 216 ErrorMessagePolicy errorMessagePolicy; 217 ActorParser(string text, string sourceFile, ErrorMessagePolicy errorMessagePolicy)218 public ActorParser(string text, string sourceFile, ErrorMessagePolicy errorMessagePolicy) 219 { 220 this.sourceFile = sourceFile; 221 this.errorMessagePolicy = errorMessagePolicy; 222 tokens = Tokenize(text).Select(t=>new Token{ Value=t }).ToArray(); 223 CountParens(); 224 //if (sourceFile.EndsWith(".h")) LineNumbersEnabled = false; 225 //Console.WriteLine("{0} chars -> {1} tokens", text.Length, tokens.Length); 226 //showTokens(); 227 } 228 Write(System.IO.TextWriter writer, string destFileName)229 public void Write(System.IO.TextWriter writer, string destFileName) 230 { 231 writer.NewLine = "\n"; 232 writer.WriteLine("#define POST_ACTOR_COMPILER 1"); 233 int outLine = 1; 234 if (LineNumbersEnabled) 235 { 236 writer.WriteLine("#line {0} \"{1}\"", tokens[0].SourceLine, sourceFile); 237 outLine++; 238 } 239 int inBlocks = 0; 240 for(int i=0; i<tokens.Length; i++) 241 { 242 if(tokens[0].SourceLine == 0) 243 { 244 throw new Exception("Internal error: Invalid source line (0)"); 245 } 246 if (tokens[i].Value == "ACTOR" || tokens[i].Value == "TEST_CASE") 247 { 248 int end; 249 var actor = ParseActor(i, out end); 250 var actorWriter = new System.IO.StringWriter(); 251 actorWriter.NewLine = "\n"; 252 new ActorCompiler(actor, sourceFile, inBlocks==0, LineNumbersEnabled).Write(actorWriter); 253 string[] actorLines = actorWriter.ToString().Split('\n'); 254 255 bool hasLineNumber = false; 256 bool hadLineNumber = true; 257 foreach (var line in actorLines) 258 { 259 if (LineNumbersEnabled) 260 { 261 bool isLineNumber = line.Contains("#line"); 262 if (isLineNumber) hadLineNumber = true; 263 if (!isLineNumber && !hasLineNumber && hadLineNumber) 264 { 265 writer.WriteLine("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t#line {0} \"{1}\"", outLine + 1, destFileName); 266 outLine++; 267 hadLineNumber = false; 268 } 269 hasLineNumber = isLineNumber; 270 } 271 writer.WriteLine(line.TrimEnd('\n','\r')); 272 outLine++; 273 } 274 275 i = end; 276 if (i != tokens.Length && LineNumbersEnabled) 277 { 278 writer.WriteLine("#line {0} \"{1}\"", tokens[i].SourceLine, sourceFile); 279 outLine++; 280 } 281 } 282 else if (tokens[i].Value == "DESCR") 283 { 284 int end; 285 var descr = ParseDescr(i, out end); 286 int lines; 287 new DescrCompiler(descr, tokens[i].BraceDepth).Write(writer, out lines); 288 i = end; 289 outLine += lines; 290 if (i != tokens.Length && LineNumbersEnabled) 291 { 292 writer.WriteLine("#line {0} \"{1}\"", tokens[i].SourceLine, sourceFile); 293 outLine++; 294 } 295 } 296 else 297 { 298 if (tokens[i].Value == "{") inBlocks++; 299 else if (tokens[i].Value == "}") inBlocks--; 300 writer.Write(tokens[i].Value); 301 outLine += tokens[i].Value.Count(c => c == '\n'); 302 } 303 } 304 } 305 SplitParameterList( TokenRange toks, string delimiter )306 IEnumerable<TokenRange> SplitParameterList( TokenRange toks, string delimiter ) { 307 if (toks.Begin==toks.End) yield break; 308 while (true) { 309 Token comma = AngleBracketParser.NotInsideAngleBrackets( toks ) 310 .FirstOrDefault( t=> t.Value==delimiter && t.ParenDepth == toks.First().ParenDepth ); 311 if (comma == null) break; 312 yield return range(toks.Begin,comma.Position); 313 toks = range(comma.Position + 1, toks.End); 314 } 315 yield return toks; 316 } 317 NormalizeWhitespace(IEnumerable<Token> tokens)318 IEnumerable<Token> NormalizeWhitespace(IEnumerable<Token> tokens) 319 { 320 bool inWhitespace = false; 321 bool leading = true; 322 foreach (var tok in tokens) 323 { 324 if (!tok.IsWhitespace) 325 { 326 if (inWhitespace && !leading) yield return new Token { Value = " " }; 327 inWhitespace = false; 328 yield return tok; 329 leading = false; 330 } 331 else 332 { 333 inWhitespace = true; 334 } 335 } 336 } 337 ParseDeclaration(TokenRange tokens, out Token name, out TokenRange type, out TokenRange initializer, out bool constructorSyntax)338 void ParseDeclaration(TokenRange tokens, 339 out Token name, 340 out TokenRange type, 341 out TokenRange initializer, 342 out bool constructorSyntax) 343 { 344 initializer = null; 345 TokenRange beforeInitializer = tokens; 346 constructorSyntax = false; 347 348 Token equals = AngleBracketParser.NotInsideAngleBrackets(tokens) 349 .FirstOrDefault(t => t.Value == "=" && t.ParenDepth == tokens.First().ParenDepth); 350 if (equals != null) 351 { 352 // type name = initializer; 353 beforeInitializer = range(tokens.Begin,equals.Position); 354 initializer = range(equals.Position + 1, tokens.End); 355 } 356 else 357 { 358 Token paren = AngleBracketParser.NotInsideAngleBrackets(tokens) 359 .FirstOrDefault(t => t.Value == "("); 360 if (paren != null) 361 { 362 // type name(initializer); 363 constructorSyntax = true; 364 beforeInitializer = range(tokens.Begin, paren.Position); 365 initializer = 366 range(paren.Position + 1, tokens.End) 367 .TakeWhile(t => t.ParenDepth > paren.ParenDepth); 368 } 369 } 370 name = beforeInitializer.Last(NonWhitespace); 371 if (beforeInitializer.Begin == name.Position) 372 throw new Error(beforeInitializer.First().SourceLine, "Declaration has no type."); 373 type = range(beforeInitializer.Begin, name.Position); 374 } 375 ParseVarDeclaration(TokenRange tokens)376 VarDeclaration ParseVarDeclaration(TokenRange tokens) 377 { 378 Token name; 379 TokenRange type, initializer; 380 bool constructorSyntax; 381 ParseDeclaration( tokens, out name, out type, out initializer, out constructorSyntax ); 382 return new VarDeclaration 383 { 384 name = name.Value, 385 type = str(NormalizeWhitespace(type)), 386 initializer = initializer == null ? "" : str(NormalizeWhitespace(initializer)), 387 initializerConstructorSyntax = constructorSyntax 388 }; 389 } 390 391 readonly Func<Token, bool> Whitespace = (Token t) => t.IsWhitespace; 392 readonly Func<Token, bool> NonWhitespace = (Token t) => !t.IsWhitespace; 393 ParseDescrHeading(Descr descr, TokenRange toks)394 void ParseDescrHeading(Descr descr, TokenRange toks) 395 { 396 toks.First(NonWhitespace).Assert("non-struct DESCR!", t => t.Value == "struct"); 397 toks = toks.SkipWhile(Whitespace).Skip(1).SkipWhile(Whitespace); 398 399 var colon = toks.FirstOrDefault(t => t.Value == ":"); 400 if (colon != null) 401 { 402 descr.superClassList = str(range(colon.Position + 1, toks.End)).Trim(); 403 toks = range(toks.Begin, colon.Position); 404 } 405 descr.name = str(toks).Trim(); 406 } 407 ParseTestCaseHeading(Actor actor, TokenRange toks)408 void ParseTestCaseHeading(Actor actor, TokenRange toks) 409 { 410 actor.isStatic = true; 411 412 // The parameter(s) to the TEST_CASE macro are opaque to the actor compiler 413 TokenRange paramRange = toks.Last(NonWhitespace) 414 .Assert("Unexpected tokens after test case parameter list.", 415 t => t.Value == ")" && t.ParenDepth == toks.First().ParenDepth) 416 .GetMatchingRangeIn(toks); 417 actor.testCaseParameters = str(paramRange); 418 419 actor.name = "flowTestCase" + toks.First().SourceLine; 420 actor.parameters = new VarDeclaration[] { }; 421 actor.returnType = "Void"; 422 } 423 ParseActorHeading(Actor actor, TokenRange toks)424 void ParseActorHeading(Actor actor, TokenRange toks) 425 { 426 var template = toks.First(NonWhitespace); 427 if (template.Value == "template") 428 { 429 var templateParams = range(template.Position+1, toks.End) 430 .First(NonWhitespace) 431 .Assert("Invalid template declaration", t=>t.Value=="<") 432 .GetMatchingRangeIn(toks); 433 434 actor.templateFormals = SplitParameterList(templateParams, ",") 435 .Select(p => ParseVarDeclaration(p)) //< SOMEDAY: ? 436 .ToArray(); 437 438 toks = range(templateParams.End + 1, toks.End); 439 } 440 441 var staticKeyword = toks.First(NonWhitespace); 442 if (staticKeyword.Value == "static") 443 { 444 actor.isStatic = true; 445 toks = range(staticKeyword.Position + 1, toks.End); 446 } 447 448 var uncancellableKeyword = toks.First(NonWhitespace); 449 if (uncancellableKeyword.Value == "UNCANCELLABLE") 450 { 451 actor.isUncancellable = true; 452 toks = range(uncancellableKeyword.Position + 1, toks.End); 453 } 454 455 // Find the parameter list 456 TokenRange paramRange = toks.Last(NonWhitespace) 457 .Assert("Unexpected tokens after actor parameter list.", 458 t => t.Value == ")" && t.ParenDepth == toks.First().ParenDepth) 459 .GetMatchingRangeIn(toks); 460 actor.parameters = SplitParameterList(paramRange, ",") 461 .Select(p => ParseVarDeclaration(p)) 462 .ToArray(); 463 464 var name = range(toks.Begin,paramRange.Begin-1).Last(NonWhitespace); 465 actor.name = name.Value; 466 467 // SOMEDAY: refactor? 468 var returnType = range(toks.First().Position + 1, name.Position).SkipWhile(Whitespace); 469 var retToken = returnType.First(); 470 if (retToken.Value == "Future") 471 { 472 var ofType = returnType.Skip(1).First(NonWhitespace).Assert("Expected <", tok => tok.Value == "<").GetMatchingRangeIn(returnType); 473 actor.returnType = str(NormalizeWhitespace(ofType)); 474 toks = range(ofType.End + 1, returnType.End); 475 } 476 else if (retToken.Value == "void"/* && !returnType.Skip(1).Any(NonWhitespace)*/) 477 { 478 actor.returnType = null; 479 toks = returnType.Skip(1); 480 } 481 else 482 throw new Error(actor.SourceLine, "Actor apparently does not return Future<T>"); 483 484 toks = toks.SkipWhile(Whitespace); 485 if (!toks.IsEmpty) 486 { 487 if (toks.Last().Value == "::") 488 { 489 actor.nameSpace = str(range(toks.Begin, toks.End - 1)); 490 } 491 else 492 { 493 Console.WriteLine("Tokens: '{0}' {1} '{2}'", str(toks), toks.Count(), toks.Last().Value); 494 throw new Error(actor.SourceLine, "Unrecognized tokens preceding parameter list in actor declaration"); 495 } 496 } 497 } 498 ParseLoopStatement(TokenRange toks)499 LoopStatement ParseLoopStatement(TokenRange toks) 500 { 501 return new LoopStatement { 502 body = ParseCompoundStatement( toks.Consume("loop") ) 503 }; 504 } 505 ParseChooseStatement(TokenRange toks)506 ChooseStatement ParseChooseStatement(TokenRange toks) 507 { 508 return new ChooseStatement 509 { 510 body = ParseCompoundStatement(toks.Consume("choose")) 511 }; 512 } 513 ParseWhenStatement(TokenRange toks)514 WhenStatement ParseWhenStatement(TokenRange toks) 515 { 516 var expr = toks.Consume("when") 517 .SkipWhile(Whitespace) 518 .First() 519 .Assert("Expected (", t => t.Value == "(") 520 .GetMatchingRangeIn(toks) 521 .SkipWhile(Whitespace); 522 523 return new WhenStatement { 524 wait = ParseWaitStatement(expr), 525 body = ParseCompoundStatement(range(expr.End+1, toks.End)) 526 }; 527 } 528 ParseStateDeclaration(TokenRange toks)529 StateDeclarationStatement ParseStateDeclaration(TokenRange toks) 530 { 531 toks = toks.Consume("state").RevSkipWhile(t => t.Value == ";"); 532 return new StateDeclarationStatement { 533 decl = ParseVarDeclaration(toks) 534 }; 535 } 536 ParseReturnStatement(TokenRange toks)537 ReturnStatement ParseReturnStatement(TokenRange toks) 538 { 539 toks = toks.Consume("return").RevSkipWhile(t => t.Value == ";"); 540 return new ReturnStatement 541 { 542 expression = str(NormalizeWhitespace(toks)) 543 }; 544 } 545 ParseThrowStatement(TokenRange toks)546 ThrowStatement ParseThrowStatement(TokenRange toks) 547 { 548 toks = toks.Consume("throw").RevSkipWhile(t => t.Value == ";"); 549 return new ThrowStatement 550 { 551 expression = str(NormalizeWhitespace(toks)) 552 }; 553 } 554 ParseWaitStatement(TokenRange toks)555 WaitStatement ParseWaitStatement(TokenRange toks) 556 { 557 WaitStatement ws = new WaitStatement(); 558 ws.FirstSourceLine = toks.First().SourceLine; 559 if (toks.First().Value == "state") 560 { 561 ws.resultIsState = true; 562 toks = toks.Consume("state"); 563 } 564 TokenRange initializer; 565 if (toks.First().Value == "wait" || toks.First().Value == "waitNext") 566 { 567 initializer = toks.RevSkipWhile(t=>t.Value==";"); 568 ws.result = new VarDeclaration { 569 name = "_", 570 type = "Void", 571 initializer = "", 572 initializerConstructorSyntax = false 573 }; 574 } else { 575 Token name; 576 TokenRange type; 577 bool constructorSyntax; 578 ParseDeclaration( toks.RevSkipWhile(t=>t.Value==";"), out name, out type, out initializer, out constructorSyntax ); 579 580 string typestring = str(NormalizeWhitespace(type)); 581 if (typestring == "Void") { 582 throw new Error(ws.FirstSourceLine, "Assigning the result of a Void wait is not allowed. Just use a standalone wait statement."); 583 } 584 585 ws.result = new VarDeclaration 586 { 587 name = name.Value, 588 type = str(NormalizeWhitespace(type)), 589 initializer = "", 590 initializerConstructorSyntax = false 591 }; 592 } 593 594 if (initializer == null) throw new Error(ws.FirstSourceLine, "Wait statement must be a declaration or standalone statement"); 595 596 var waitParams = initializer 597 .SkipWhile(Whitespace).Consume("Statement contains a wait, but is not a valid wait statement or a supported compound statement.1", 598 t=> { 599 if (t.Value=="wait") return true; 600 if (t.Value=="waitNext") { ws.isWaitNext = true; return true; } 601 return false; 602 }) 603 .SkipWhile(Whitespace).First().Assert("Expected (", t => t.Value == "(") 604 .GetMatchingRangeIn(initializer); 605 if (!range(waitParams.End, initializer.End).Consume(")").All(Whitespace)) { 606 throw new Error(toks.First().SourceLine, "Statement contains a wait, but is not a valid wait statement or a supported compound statement.2"); 607 } 608 609 ws.futureExpression = str(NormalizeWhitespace(waitParams)); 610 return ws; 611 } 612 ParseWhileStatement(TokenRange toks)613 WhileStatement ParseWhileStatement(TokenRange toks) 614 { 615 var expr = toks.Consume("while") 616 .First(NonWhitespace) 617 .Assert("Expected (", t => t.Value == "(") 618 .GetMatchingRangeIn(toks); 619 return new WhileStatement 620 { 621 expression = str(NormalizeWhitespace(expr)), 622 body = ParseCompoundStatement(range(expr.End + 1, toks.End)) 623 }; 624 } 625 ParseForStatement(TokenRange toks)626 Statement ParseForStatement(TokenRange toks) 627 { 628 var head = 629 toks.Consume("for") 630 .First(NonWhitespace) 631 .Assert("Expected (", t => t.Value == "(") 632 .GetMatchingRangeIn(toks); 633 634 Token[] delim = 635 head.Where( 636 t => t.ParenDepth == head.First().ParenDepth && 637 t.BraceDepth == head.First().BraceDepth && 638 t.Value==";" 639 ).ToArray(); 640 if (delim.Length == 2) 641 { 642 var init = range(head.Begin, delim[0].Position); 643 var cond = range(delim[0].Position + 1, delim[1].Position); 644 var next = range(delim[1].Position + 1, head.End); 645 var body = range(head.End + 1, toks.End); 646 647 return new ForStatement 648 { 649 initExpression = str(NormalizeWhitespace(init)), 650 condExpression = str(NormalizeWhitespace(cond)), 651 nextExpression = str(NormalizeWhitespace(next)), 652 body = ParseCompoundStatement(body) 653 }; 654 } 655 656 delim = 657 head.Where( 658 t => t.ParenDepth == head.First().ParenDepth && 659 t.BraceDepth == head.First().BraceDepth && 660 t.Value == ":" 661 ).ToArray(); 662 if (delim.Length != 1) 663 { 664 throw new Error(head.First().SourceLine, "for statement must be 3-arg style or c++11 2-arg style"); 665 } 666 667 return new RangeForStatement 668 { 669 // The container over which to iterate 670 rangeExpression = str(NormalizeWhitespace(range(delim[0].Position + 1, head.End).SkipWhile(Whitespace))), 671 // Type and name of the variable assigned in each iteration 672 rangeDecl = str(NormalizeWhitespace(range(head.Begin, delim[0].Position - 1).SkipWhile(Whitespace))), 673 // The body of the for loop 674 body = ParseCompoundStatement(range(head.End + 1, toks.End)) 675 }; 676 } 677 ParseIfStatement(TokenRange toks)678 Statement ParseIfStatement(TokenRange toks) 679 { 680 var expr = toks.Consume("if") 681 .First(NonWhitespace) 682 .Assert("Expected (", t => t.Value == "(") 683 .GetMatchingRangeIn(toks); 684 return new IfStatement { 685 expression = str(NormalizeWhitespace(expr)), 686 ifBody = ParseCompoundStatement(range(expr.End+1, toks.End)) 687 // elseBody will be filled in later if necessary by ParseElseStatement 688 }; 689 } ParseElseStatement(TokenRange toks, Statement prevStatement)690 void ParseElseStatement(TokenRange toks, Statement prevStatement) 691 { 692 var ifStatement = prevStatement as IfStatement; 693 while (ifStatement != null && ifStatement.elseBody != null) 694 ifStatement = ifStatement.elseBody as IfStatement; 695 if (ifStatement == null) 696 throw new Error(toks.First().SourceLine, "else without matching if"); 697 ifStatement.elseBody = ParseCompoundStatement(toks.Consume("else")); 698 } 699 ParseTryStatement(TokenRange toks)700 Statement ParseTryStatement(TokenRange toks) 701 { 702 return new TryStatement 703 { 704 tryBody = ParseCompoundStatement(toks.Consume("try")), 705 catches = new List<TryStatement.Catch>() // will be filled in later by ParseCatchStatement 706 }; 707 } ParseCatchStatement(TokenRange toks, Statement prevStatement)708 void ParseCatchStatement(TokenRange toks, Statement prevStatement) 709 { 710 var tryStatement = prevStatement as TryStatement; 711 if (tryStatement == null) 712 throw new Error(toks.First().SourceLine, "catch without matching try"); 713 var expr = toks.Consume("catch") 714 .First(NonWhitespace) 715 .Assert("Expected (", t => t.Value == "(") 716 .GetMatchingRangeIn(toks); 717 tryStatement.catches.Add( 718 new TryStatement.Catch 719 { 720 expression = str(NormalizeWhitespace(expr)), 721 body = ParseCompoundStatement(range(expr.End + 1, toks.End)), 722 FirstSourceLine = expr.First().SourceLine 723 }); 724 } 725 726 static readonly HashSet<string> IllegalKeywords = new HashSet<string> { "goto", "do", "finally", "__if_exists", "__if_not_exists" }; 727 ParseDeclaration(TokenRange toks, List<Declaration> declarations)728 void ParseDeclaration(TokenRange toks, List<Declaration> declarations) 729 { 730 Declaration dec = new Declaration(); 731 732 Token delim = toks.First(t => t.Value == ";"); 733 var nameRange = range(toks.Begin, delim.Position).RevSkipWhile(Whitespace).RevTakeWhile(NonWhitespace); 734 var typeRange = range(toks.Begin, nameRange.Begin); 735 var commentRange = range(delim.Position + 1, toks.End); 736 737 dec.name = str(nameRange).Trim(); 738 dec.type = str(typeRange).Trim(); 739 dec.comment = str(commentRange).Trim().TrimStart('/'); 740 741 declarations.Add(dec); 742 } 743 ParseStatement(TokenRange toks, List<Statement> statements)744 void ParseStatement(TokenRange toks, List<Statement> statements) 745 { 746 toks = toks.SkipWhile(Whitespace); 747 748 Action<Statement> Add = stmt => 749 { 750 stmt.FirstSourceLine = toks.First().SourceLine; 751 statements.Add(stmt); 752 }; 753 754 switch (toks.First().Value) 755 { 756 case "loop": Add(ParseLoopStatement(toks)); break; 757 case "while": Add(ParseWhileStatement(toks)); break; 758 case "for": Add(ParseForStatement(toks)); break; 759 case "break": Add(new BreakStatement()); break; 760 case "continue": Add(new ContinueStatement()); break; 761 case "return": Add(ParseReturnStatement(toks)); break; 762 case "{": Add(ParseCompoundStatement(toks)); break; 763 case "if": Add(ParseIfStatement(toks)); break; 764 case "else": ParseElseStatement(toks, statements[statements.Count - 1]); break; 765 case "choose": Add(ParseChooseStatement(toks)); break; 766 case "when": Add(ParseWhenStatement(toks)); break; 767 case "try": Add(ParseTryStatement(toks)); break; 768 case "catch": ParseCatchStatement(toks, statements[statements.Count - 1]); break; 769 case "throw": Add(ParseThrowStatement(toks)); break; 770 default: 771 if (IllegalKeywords.Contains(toks.First().Value)) 772 throw new Error(toks.First().SourceLine, "Statement '{0}' not supported in actors.", toks.First().Value); 773 if (toks.Any(t => t.Value == "wait" || t.Value == "waitNext")) 774 Add(ParseWaitStatement(toks)); 775 else if (toks.First().Value == "state") 776 Add(ParseStateDeclaration(toks)); 777 else if (toks.First().Value == "switch" && toks.Any(t => t.Value == "return")) 778 throw new Error(toks.First().SourceLine, "Unsupported compound statement containing return."); 779 else if (toks.RevSkipWhile(t => t.Value == ";").Any(NonWhitespace)) 780 Add(new PlainOldCodeStatement 781 { 782 code = str(NormalizeWhitespace(toks.RevSkipWhile(t => t.Value == ";"))) + ";" 783 }); 784 break; 785 }; 786 } 787 ParseCompoundStatement(TokenRange toks)788 Statement ParseCompoundStatement(TokenRange toks) 789 { 790 var first = toks.First(NonWhitespace); 791 if (first.Value == "{") { 792 var inBraces = first.GetMatchingRangeIn(toks); 793 if (!range(inBraces.End, toks.End).Consume("}").All(Whitespace)) 794 throw new Error(inBraces.Last().SourceLine, "Unexpected tokens after compound statement"); 795 return ParseCodeBlock(inBraces); 796 } else { 797 List<Statement> statements = new List<Statement>(); 798 ParseStatement( toks.Skip(1), statements ); 799 return statements[0]; 800 } 801 } 802 ParseDescrCodeBlock(TokenRange toks)803 List<Declaration> ParseDescrCodeBlock(TokenRange toks) 804 { 805 List<Declaration> declarations = new List<Declaration>(); 806 while (true) 807 { 808 Token delim = toks.FirstOrDefault(t => t.Value == ";"); 809 if (delim == null) 810 break; 811 812 int pos = delim.Position + 1; 813 var potentialComment = range(pos, toks.End).SkipWhile(t => t.Value == "\t" || t.Value == " "); 814 if (!potentialComment.IsEmpty && potentialComment.First().Value.StartsWith("//")) 815 { 816 pos = potentialComment.First().Position + 1; 817 } 818 819 ParseDeclaration(range(toks.Begin, pos), declarations); 820 821 toks = range(pos, toks.End); 822 } 823 if (!toks.All(Whitespace)) 824 throw new Error(toks.First(NonWhitespace).SourceLine, "Trailing unterminated statement in code block"); 825 return declarations; 826 } 827 ParseCodeBlock(TokenRange toks)828 CodeBlock ParseCodeBlock(TokenRange toks) 829 { 830 List<Statement> statements = new List<Statement>(); 831 while (true) 832 { 833 Token delim = toks 834 .FirstOrDefault( 835 t=> t.ParenDepth == toks.First().ParenDepth && 836 t.BraceDepth == toks.First().BraceDepth && 837 (t.Value==";" || t.Value == "}") 838 ); 839 if (delim == null) 840 break; 841 ParseStatement(range(toks.Begin, delim.Position + 1), statements); 842 toks = range(delim.Position + 1, toks.End); 843 } 844 if (!toks.All(Whitespace)) 845 throw new Error(toks.First(NonWhitespace).SourceLine, "Trailing unterminated statement in code block"); 846 return new CodeBlock { statements = statements.ToArray() }; 847 } 848 range(int beginPos, int endPos)849 TokenRange range(int beginPos, int endPos) 850 { 851 return new TokenRange(tokens, beginPos, endPos); 852 } 853 ParseDescr(int pos, out int end)854 Descr ParseDescr(int pos, out int end) 855 { 856 var descr = new Descr(); 857 var toks = range(pos + 1, tokens.Length); 858 var heading = toks.TakeWhile(t => t.Value != "{"); 859 var body = range(heading.End + 1, tokens.Length) 860 .TakeWhile(t => t.BraceDepth > toks.First().BraceDepth || t.Value == ";" ); //assumes no whitespace between the last "}" and the ";" 861 862 ParseDescrHeading(descr, heading); 863 descr.body = ParseDescrCodeBlock(body); 864 865 end = body.End + 1; 866 return descr; 867 } 868 ParseActor( int pos, out int end )869 Actor ParseActor( int pos, out int end ) { 870 var actor = new Actor(); 871 var head_token = tokens[pos]; 872 actor.SourceLine = head_token.SourceLine; 873 874 var toks = range(pos+1, tokens.Length); 875 var heading = toks.TakeWhile(t => t.Value != "{"); 876 var toSemicolon = toks.TakeWhile(t => t.Value != ";"); 877 actor.isForwardDeclaration = toSemicolon.Length < heading.Length; 878 if (actor.isForwardDeclaration) { 879 heading = toSemicolon; 880 if (head_token.Value == "ACTOR") { 881 ParseActorHeading(actor, heading); 882 } else { 883 head_token.Assert("ACTOR expected!", t => false); 884 } 885 end = heading.End + 1; 886 } else { 887 var body = range(heading.End+1, tokens.Length) 888 .TakeWhile(t => t.BraceDepth > toks.First().BraceDepth); 889 890 if (head_token.Value == "ACTOR") 891 { 892 ParseActorHeading(actor, heading); 893 } 894 else if (head_token.Value == "TEST_CASE") { 895 ParseTestCaseHeading(actor, heading); 896 actor.isTestCase = true; 897 } 898 else 899 head_token.Assert("ACTOR or TEST_CASE expected!", t => false); 900 901 actor.body = ParseCodeBlock(body); 902 903 if (!actor.body.containsWait()) 904 this.errorMessagePolicy.HandleActorWithoutWait(sourceFile, actor); 905 906 end = body.End + 1; 907 } 908 return actor; 909 } 910 str(IEnumerable<Token> tokens)911 string str(IEnumerable<Token> tokens) 912 { 913 return string.Join("", tokens.Select(x => x.Value).ToArray()); 914 } str(int begin, int end)915 string str(int begin, int end) 916 { 917 return str(range(begin,end)); 918 } 919 CountParens()920 void CountParens() 921 { 922 int BraceDepth = 0, ParenDepth = 0, LineCount = 1; 923 Token lastParen = null, lastBrace = null; 924 for (int i = 0; i < tokens.Length; i++) 925 { 926 switch (tokens[i].Value) 927 { 928 case "}": BraceDepth--; break; 929 case ")": ParenDepth--; break; 930 case "\r\n": LineCount++; break; 931 case "\n": LineCount++; break; 932 } 933 if (BraceDepth < 0) throw new Error(LineCount, "Mismatched braces"); 934 if (ParenDepth < 0) throw new Error(LineCount, "Mismatched parenthesis"); 935 tokens[i].Position = i; 936 tokens[i].SourceLine = LineCount; 937 tokens[i].BraceDepth = BraceDepth; 938 tokens[i].ParenDepth = ParenDepth; 939 if (tokens[i].Value.StartsWith("/*")) LineCount += tokens[i].Value.Count(c=>c=='\n'); 940 switch (tokens[i].Value) 941 { 942 case "{": BraceDepth++; if (BraceDepth==1) lastBrace = tokens[i]; break; 943 case "(": ParenDepth++; if (ParenDepth==1) lastParen = tokens[i]; break; 944 } 945 } 946 if (BraceDepth != 0) throw new Error(lastBrace.SourceLine, "Unmatched brace"); 947 if (ParenDepth != 0) throw new Error(lastParen.SourceLine, "Unmatched parenthesis"); 948 } 949 showTokens()950 void showTokens() 951 { 952 foreach (var t in tokens) 953 { 954 if (t.Value == "\r\n") 955 Console.WriteLine(); 956 else if (t.Value.Length == 1) 957 Console.Write(t.Value); 958 else 959 Console.Write("|{0}|", t.Value); 960 } 961 } 962 963 readonly Regex[] tokenExpressions = (new string[] { 964 @"\{", 965 @"\}", 966 @"\(", 967 @"\)", 968 @"//[^\n]*", 969 @"/[*]([*][^/]|[^*])*[*]/", 970 @"'(\\.|[^\'\n])*'", //< SOMEDAY: Not fully restrictive 971 @"""(\\.|[^\""\n])*""", 972 @"[a-zA-Z_][a-zA-Z_0-9]*", 973 @"\r\n", 974 @"\n", 975 @"::", 976 @"." 977 }).Select( x=>new Regex(@"\G"+x, RegexOptions.Singleline) ).ToArray(); 978 979 IEnumerable<string> Tokenize(string text) 980 { 981 int pos = 0; 982 while (pos < text.Length) 983 { 984 bool ok = false; 985 foreach (var re in tokenExpressions) 986 { 987 var m = re.Match(text, pos); 988 if (m.Success) 989 { 990 yield return m.Value; 991 pos += m.Value.Length; 992 ok = true; 993 break; 994 } 995 } 996 if (!ok) 997 throw new Exception( String.Format("Can't tokenize! {0}", pos)); 998 } 999 } 1000 } 1001 } 1002