string fromTokens(List<Token> tokens, Scope s) { StringBuilder sb = new StringBuilder(); foreach (Token t in tokens) sb.Append(fromToken(t, s)); return sb.ToString(); }
string fromToken(Token t, Scope s) { StringBuilder sb = new StringBuilder(); int cnt = 0; bool shortComment = false; int i = 0; foreach (Token t2 in t.Leading) { if (t2.Type == TokenType.LongComment || t2.Type == TokenType.ShortComment || t2.Type == TokenType.DocumentationComment) { sb.Append(t2.Data); cnt++; if (t2.Type == TokenType.ShortComment || t2.Type == TokenType.DocumentationComment || (t.Leading.Count >= i + 1 && (t.Leading[i + 1].Type == TokenType.WhitespaceR || t.Leading[i + 1].Type == TokenType.WhitespaceN))) { shortComment = true; sb.Append(options.EOL); } } i++; } if (cnt > 0) { if (shortComment && (sb[sb.Length - 1] != '\r' || sb[sb.Length - 1] != '\n')) { sb.Append(options.EOL); sb.Append(writeIndent()); } else { sb.Insert(0, " "); sb.Append(" "); } } if (t.Type == TokenType.DoubleQuoteString) sb.Append("\"" + t.Data + "\""); else if (t.Type == TokenType.SingleQuoteString) sb.Append("'" + t.Data + "'"); else if (t.Type == TokenType.Ident) { Variable v = s.GetOldVariable(t.Data); if (v != null) sb.Append(v.Name); else sb.Append(t.Data); } else sb.Append(t.Data); if (t.FollowingEoSToken != null && t.FollowingEoSToken.Type == TokenType.EndOfStream) sb.Append(fromToken(t.FollowingEoSToken, s)); return sb.ToString(); }
AnonymousFunctionExpr ParseExprFunctionArgsAndBody(Scope scope) { AnonymousFunctionExpr func = new AnonymousFunctionExpr(); func.Scope = new Scope(scope); if (reader.ConsumeSymbol('(') == false) error("'(' expected"); // arg list List<Variable> arglist = new List<Variable>(); bool isVarArg = false; while (reader.ConsumeSymbol(')') == false) { if (reader.Is(TokenType.Ident)) { Variable arg = new Variable(); arg.Name = reader.Get().Data; func.Scope.AddLocal(arg); arglist.Add(arg); if (!reader.ConsumeSymbol(',')) { if (reader.ConsumeSymbol(')')) break; else error("')' expected"); break; } } else if (reader.ConsumeSymbol("...")) { isVarArg = true; if (!reader.ConsumeSymbol(')')) error("'...' must be the last argument of a function"); break; } else { error("Argument name or '...' expected"); break; } } // body List<Statement> body = ParseStatementList(func.Scope); // end if (!reader.ConsumeKeyword("end")) error("'end' expected after function body"); //nodeFunc.AstType = AstType.Function; func.Arguments = arglist; func.Body = body; func.IsVararg = isVarArg; return func; }
string fromToken(Token t, Scope s) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < t.Leading.Count; i++) { Token t2 = t.Leading[i]; if (t2.Type == TokenType.WhitespaceTab && options.TabsToSpaces) sb.Append(options.Tab); else if (t2.Type == TokenType.WhitespaceR || t2.Type == TokenType.WhitespaceN) { if (options.ConvertNewLines) { string nLine = options.EOL; if (t2.Type == TokenType.WhitespaceR && t.Leading.Count > i + 1 && t.Leading[i + 1].Type == TokenType.WhitespaceN) { // \r\n new line i++; sb.Append(options.EOL); } else // \n or \r new line sb.Append(nLine); } else sb.Append(t2.Data); } else sb.Append(t2.Data); } if (t.Type == TokenType.DoubleQuoteString) sb.Append("\"" + t.Data + "\""); else if (t.Type == TokenType.SingleQuoteString) sb.Append("'" + t.Data + "'"); else if (t.Type == TokenType.Ident) { Variable v = s.GetOldVariable(t.Data); if (v != null) sb.Append(v.Name); else sb.Append(t.Data); } else sb.Append(t.Data); if (t.FollowingEoSToken != null && t.FollowingEoSToken.Type == TokenType.EndOfStream) sb.Append(fromToken(t.FollowingEoSToken, s)); return sb.ToString(); }
internal string DoExpr(Expression e, Scope s) { int startP = index; index += e.ParenCount; string ret = null; if (e is AnonymousFunctionExpr) // function() ... end { AnonymousFunctionExpr f = e as AnonymousFunctionExpr; StringBuilder sb = new StringBuilder(); sb.Append(fromToken(tok[index++], s)); // 'function' sb.Append(fromToken(tok[index++], s)); // '(' for (int i2 = 0; i2 < f.Arguments.Count; i2++) { sb.Append(fromToken(tok[index++], s)); if (i2 != f.Arguments.Count - 1 || f.IsVararg) sb.Append(fromToken(tok[index++], s) + " "); } if (f.IsVararg) sb.Append(fromToken(tok[index++], s)); sb.Append(fromToken(tok[index++], s)); // ')' if (f.Body.Count > 1) { sb.Append(options.EOL); indent++; sb.Append(DoChunk(f.Body)); sb.Append(nldedent()); } else if (f.Body.Count == 0) { sb.Append(" "); } else { sb.Append(" " + DoStatement(f.Body[0])); sb.Append(" "); } sb.Append(fromToken(tok[index++], s)); // <end> ret = sb.ToString(); } else if (e is BinOpExpr) { //int i = 0; string left = DoExpr((e as BinOpExpr).Lhs, s); string op = fromToken(tok[index++], s); string right = DoExpr((e as BinOpExpr).Rhs, s); ret = string.Format("{0} {1} {2}", left, op, right); } else if (e is BoolExpr) { ret = fromToken(tok[index++], s); } else if (e is CallExpr && (!(e is StringCallExpr) && !(e is TableCallExpr))) { CallExpr c = e as CallExpr; StringBuilder sb = new StringBuilder(); sb.Append(DoExpr(c.Base, s) // <base> + fromToken(tok[index++], s)); // '(' for (int i = 0; i < c.Arguments.Count; i++) { sb.Append(DoExpr(c.Arguments[i], s)); if (i != c.Arguments.Count - 1) { sb.Append(fromToken(tok[index++], s)); // ', ' sb.Append(" "); } } sb.Append(fromToken(tok[index++], s)); // ')' ret = sb.ToString(); } else if (e is StringCallExpr) { StringCallExpr sc = e as StringCallExpr; ret = string.Format("{0} {1}", DoExpr(sc.Base, s), DoExpr(sc.Arguments[0], s)); } else if (e is TableCallExpr) { TableCallExpr sc = e as TableCallExpr; ret = string.Format("{0} {1}", DoExpr(sc.Base, s), DoExpr(sc.Arguments[0], s)); } else if (e is IndexExpr) { IndexExpr i = e as IndexExpr; ret = string.Format("{0}{1}{2}{3}", DoExpr(i.Base, s), fromToken(tok[index++], s), DoExpr(i.Index, s), fromToken(tok[index++], s)); } else if (e is InlineFunctionExpression) // |<args>| -> <exprs> { InlineFunctionExpression ife = e as InlineFunctionExpression; StringBuilder sb = new StringBuilder(); sb.Append(fromToken(tok[index++], s)); // '|; for (int i = 0; i < ife.Arguments.Count; i++) { sb.Append(fromToken(tok[index++], s)); // <arg name> if (i != ife.Arguments.Count - 1 || ife.IsVararg) { sb.Append(fromToken(tok[index++], s)); // ',' sb.Append(" "); } } if (ife.IsVararg) { sb.Append(fromToken(tok[index++], s)); // '...' sb.Append(" "); } sb.Append(fromToken(tok[index++], s)); // '|' sb.Append(" "); sb.Append(fromToken(tok[index++], s)); // '->' sb.Append(" "); for (int i2 = 0; i2 < ife.Expressions.Count; i2++) { sb.Append(DoExpr(ife.Expressions[i2], s)); if (i2 != ife.Expressions.Count - 1) { sb.Append(fromToken(tok[index++], s)); // ',' sb.Append(" "); } } ret = sb.ToString(); } else if (e is TableConstructorKeyExpr) { TableConstructorKeyExpr t = e as TableConstructorKeyExpr; ret = fromToken(tok[index++], s) + DoExpr(t.Key, s) + fromToken(tok[index++], s) + " " + fromToken(tok[index++], s) + " " + DoExpr(t.Value, s); } else if (e is MemberExpr) { MemberExpr m = e as MemberExpr; ret = DoExpr(m.Base, s) + fromToken(tok[index++], s) + fromToken(tok[index++], s); } else if (e is NilExpr) ret = fromToken(tok[index++], s); else if (e is NumberExpr) ret = fromToken(tok[index++], s); else if (e is StringExpr) ret = fromToken(tok[index++], s); else if (e is TableConstructorStringKeyExpr) { TableConstructorStringKeyExpr tcske = e as TableConstructorStringKeyExpr; ret = fromToken(tok[index++], s); // key ret += " "; ret += fromToken(tok[index++], s); // '=' ret += " "; ret += DoExpr(tcske.Value, s); // value } else if (e is TableConstructorExpr) { TableConstructorExpr t = e as TableConstructorExpr; StringBuilder sb = new StringBuilder(); sb.Append(fromToken(tok[index++], s)); // '{' sb.Append(" "); bool needNewLines = false; if (t.EntryList.Count > 4) needNewLines = true; for (int i = 0; i < t.EntryList.Count; i++) { string tmp = DoExpr(t.EntryList[i], s); if (tmp.Length > 10) needNewLines = true; sb.Append(tmp); if (i != t.EntryList.Count - 1) { sb.Append(fromToken(tok[index++], s)); // ',' sb.Append(" "); } } if (t.EntryList.Count > 0) // empty table constructor is just { } sb.Append(" "); sb.Append(fromToken(tok[index++], s)); // '}' ret = sb.ToString(); } else if (e is UnOpExpr) { UnOpExpr u = e as UnOpExpr; string sc = fromToken(tok[index++], s); if (u.Op.Length != 1) sc += " "; ret = sc + DoExpr(u.Rhs, s); } else if (e is TableConstructorValueExpr) ret = DoExpr(((TableConstructorValueExpr)e).Value, s); else if (e is VarargExpr) ret = fromToken(tok[index++], s); else if (e is VariableExpression) ret = fromToken(tok[index++], s); else if (e is TableConstructorNamedFunctionExpr) ret = DoStatement(((TableConstructorNamedFunctionExpr)e).Value); if (ret != null) { if (e.ParenCount > 0) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < e.ParenCount; i++) sb.Append(fromToken(tok[startP++], s)); sb.Append(ret); for (int i = 0; i < e.ParenCount; i++) sb.Append(fromToken(tok[index++], s)); return sb.ToString(); } else return ret; } //return string.Format("{0}{1}{2}", "(".Repeat(e.ParenCount), ret, ")".Repeat(e.ParenCount)); throw new NotImplementedException(e.GetType().Name + " is not implemented"); }
public Chunk(Scope s) { Scope = s; }
internal string DoExpr(Expression e, List<Token> tok, ref int index, Scope s) { int startP = index; for (int i = 0; i < e.ParenCount; i++) index++; string ret = null; if (e is AnonymousFunctionExpr) // function() ... end { AnonymousFunctionExpr f = e as AnonymousFunctionExpr; StringBuilder sb = new StringBuilder(); sb.Append(fromToken(tok[index++], s)); // 'function' sb.Append(fromToken(tok[index++], s)); // '(' for (int i2 = 0; i2 < f.Arguments.Count; i2++) { sb.Append(fromToken(tok[index++], s)); if (i2 != f.Arguments.Count - 1 || f.IsVararg) sb.Append(fromToken(tok[index++], s)); // ',' } if (f.IsVararg) sb.Append(fromToken(tok[index++], s)); sb.Append(fromToken(tok[index++], s)); // ')' sb.Append(DoChunk(f.Body)); //sb.Append(fromToken(tok[tok.Count - 1], s)); // <end> string str = fromToken(tok[tok.Count - 1], s); sb.Append(str); ret = sb.ToString(); } else if (e is BinOpExpr) { //int i = 0; string left = DoExpr((e as BinOpExpr).Lhs, tok, ref index, s); string op = fromToken(tok[index++], s); string right = DoExpr((e as BinOpExpr).Rhs, tok, ref index, s); ret = string.Format("{0}{1}{2}", left, op, right); } else if (e is BoolExpr) { ret = fromToken(tok[index++], s); } else if (e is CallExpr && (!(e is StringCallExpr) && !(e is TableCallExpr))) { CallExpr c = e as CallExpr; StringBuilder sb = new StringBuilder(); sb.Append(DoExpr(c.Base, tok, ref index, s) // <base> + fromToken(tok[index++], s)); // '(' for (int i = 0; i < c.Arguments.Count; i++) { sb.Append(DoExpr(c.Arguments[i], tok, ref index, s)); if (i != c.Arguments.Count - 1) sb.Append(fromToken(tok[index++], s)); // ', ' } sb.Append(fromToken(tok[index++], s)); // ')' ret = sb.ToString(); } else if (e is StringCallExpr) { StringCallExpr sc = e as StringCallExpr; ret = string.Format("{0}{1}", DoExpr(sc.Base, tok, ref index, s), DoExpr(sc.Arguments[0], tok, ref index, s)); } else if (e is TableCallExpr) { TableCallExpr sc = e as TableCallExpr; ret = string.Format("{0}{1}", DoExpr(sc.Base, tok, ref index, s), DoExpr(sc.Arguments[0], tok, ref index, s)); } else if (e is IndexExpr) { IndexExpr i = e as IndexExpr; ret = string.Format("{0}{1}{2}{3}", DoExpr(i.Base, tok, ref index, s), fromToken(tok[index++], s), DoExpr(i.Index, tok, ref index, s), fromToken(tok[index++], s)); } else if (e is InlineFunctionExpression) // |<args>| -> <exprs> { InlineFunctionExpression ife = e as InlineFunctionExpression; StringBuilder sb = new StringBuilder(); sb.Append(fromToken(tok[index++], s)); // '|; for (int i = 0; i < ife.Arguments.Count; i++) { sb.Append(fromToken(tok[index++], s)); // <arg name> if (i != ife.Arguments.Count - 1 || ife.IsVararg) sb.Append(fromToken(tok[index++], s)); // ',' } if (ife.IsVararg) sb.Append(fromToken(tok[index++], s)); // '...' sb.Append(fromToken(tok[index++], s)); // '|' sb.Append(fromToken(tok[index++], s)); // '->' for (int i2 = 0; i2 < ife.Expressions.Count; i2++) { sb.Append(DoExpr(ife.Expressions[i2], tok, ref index, s)); if (i2 != ife.Expressions.Count - 1) sb.Append(fromToken(tok[index++], s)); // ',' } ret = sb.ToString(); } else if (e is TableConstructorKeyExpr) { TableConstructorKeyExpr t = e as TableConstructorKeyExpr; ret = fromToken(tok[index++], s) + DoExpr(t.Key, tok, ref index, s) + fromToken(tok[index++], s) + fromToken(tok[index++], s) + DoExpr(t.Value, tok, ref index, s); } else if (e is MemberExpr) { MemberExpr m = e as MemberExpr; ret = DoExpr(m.Base, tok, ref index, s) + fromToken(tok[index++], s) + fromToken(tok[index++], s); } else if (e is NilExpr) ret = fromToken(tok[index++], s); else if (e is NumberExpr) ret = fromToken(tok[index++], s); else if (e is StringExpr) ret = fromToken(tok[index++], s); else if (e is TableConstructorStringKeyExpr) { TableConstructorStringKeyExpr tcske = e as TableConstructorStringKeyExpr; ret = fromToken(tok[index++], s); // key ret += fromToken(tok[index++], s); // '=' ret += DoExpr(tcske.Value, tok, ref index, s); // value } else if (e is TableConstructorExpr) { TableConstructorExpr t = e as TableConstructorExpr; StringBuilder sb = new StringBuilder(); sb.Append(fromToken(tok[index++], s)); // '{' for (int i = 0; i < t.EntryList.Count; i++) { sb.Append(DoExpr(t.EntryList[i], tok, ref index, s)); if (i != t.EntryList.Count - 1) sb.Append(fromToken(tok[index++], s)); // ',' } sb.Append(fromToken(tok[index++], s)); // '}' ret = sb.ToString(); } else if (e is UnOpExpr) { string sc = fromToken(tok[index++], s); ret = sc + DoExpr((e as UnOpExpr).Rhs, tok, ref index, s); } else if (e is TableConstructorValueExpr) ret = DoExpr(((TableConstructorValueExpr)e).Value, tok, ref index, s); else if (e is VarargExpr) ret = fromToken(tok[index++], s); else if (e is VariableExpression) ret = fromToken(tok[index++], s); else if (e is TableConstructorNamedFunctionExpr) ret = DoStatement(((TableConstructorNamedFunctionExpr)e).Value); if (ret != null) { if (e.ParenCount > 0) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < e.ParenCount; i++) sb.Append(fromToken(tok[startP++], s)); sb.Append(ret); for (int i = 0; i < e.ParenCount; i++) sb.Append(fromToken(tok[index++], s)); return sb.ToString(); } else return ret; } //return string.Format("{0}{1}{2}", "(".Repeat(e.ParenCount), ret, ")".Repeat(e.ParenCount)); throw new NotImplementedException(e.GetType().Name + " is not implemented"); }
public Scope(Scope parent) { Parent = parent; Parent.Children.Add(this); }
Statement ParseStatement(Scope scope) { int startP = reader.p; int startLine = reader.Peek().Line; Statement stat = null; // print(tok.Peek().Print()) if (reader.ConsumeKeyword("if")) { //setup IfStmt _if = new IfStmt(); //clauses do { int sP = reader.p; Expression nodeCond = ParseExpr(scope); if (!reader.ConsumeKeyword("then")) error("'then' expected"); List<Statement> nodeBody = ParseStatementList(scope); List<Token> range = new List<Token>(); range.Add(reader.tokens[sP - 1]); range.AddRange(reader.Range(sP, reader.p)); _if.Clauses.Add(new ElseIfStmt(scope) { Condition = nodeCond, Body = nodeBody, ScannedTokens = range }); } while (reader.ConsumeKeyword("elseif")); // else clause if (reader.ConsumeKeyword("else")) { int sP = reader.p; List<Statement> nodeBody = ParseStatementList(scope); List<Token> range = new List<Token>(); range.Add(reader.tokens[sP - 1]); range.AddRange(reader.Range(sP, reader.p)); _if.Clauses.Add(new ElseStmt(scope) { Body = nodeBody, ScannedTokens = range }); } // end if (!reader.ConsumeKeyword("end")) error("'end' expected"); stat = _if; } else if (reader.ConsumeKeyword("while")) { WhileStatement w = new WhileStatement(scope); // condition Expression nodeCond = ParseExpr(scope); // do if (!reader.ConsumeKeyword("do")) error("'do' expected"); // body List<Statement> body = ParseStatementList(scope); //end if (!reader.ConsumeKeyword("end")) error("'end' expected"); // return w.Condition = nodeCond; w.Body = body; stat = w; } else if (reader.ConsumeKeyword("do")) { // do block List<Statement> b = ParseStatementList(scope); if (!reader.ConsumeKeyword("end")) error("'end' expected"); stat = new DoStatement(scope) { Body = b }; } else if (reader.ConsumeKeyword("for")) { //for block if (!reader.Is(TokenType.Ident)) error("<ident> expected"); Token baseVarName = reader.Get(); if (reader.ConsumeSymbol('=')) { //numeric for NumericForStatement forL = new NumericForStatement(scope); Variable forVar = new Variable() { Name = baseVarName.Data }; forL.Scope.AddLocal(forVar); Expression startEx = ParseExpr(scope); if (!reader.ConsumeSymbol(',')) error("',' expected"); Expression endEx = ParseExpr(scope); Expression stepEx = null; if (reader.ConsumeSymbol(',')) { stepEx = ParseExpr(scope); } if (!reader.ConsumeKeyword("do")) error("'do' expected"); List<Statement> body = ParseStatementList(forL.Scope); if (!reader.ConsumeKeyword("end")) error("'end' expected"); forL.Variable = forVar; forL.Start = startEx; forL.End = endEx; forL.Step = stepEx; forL.Body = body; stat = forL; } else { // generic for GenericForStatement forL = new GenericForStatement(scope); List<Variable> varList = new List<Variable> { forL.Scope.CreateLocal(baseVarName.Data) }; while (reader.ConsumeSymbol(',')) { if (!reader.Is(TokenType.Ident)) error("for variable expected"); varList.Add(forL.Scope.CreateLocal(reader.Get().Data)); } if (!reader.ConsumeKeyword("in")) error("'in' expected"); List<Expression> generators = new List<Expression>(); Expression first = ParseExpr(scope); generators.Add(first); while (reader.ConsumeSymbol(',')) { Expression gen = ParseExpr(scope); generators.Add(gen); } if (!reader.ConsumeKeyword("do")) error("'do' expected"); List<Statement> body = ParseStatementList(forL.Scope); if (!reader.ConsumeKeyword("end")) error("'end' expected"); forL.VariableList = varList; forL.Generators = generators; forL.Body = body; stat = forL; } } else if (reader.ConsumeKeyword("repeat")) { List<Statement> body = ParseStatementList(scope); if (!reader.ConsumeKeyword("until")) error("'until' expected"); Expression cond = ParseExpr(scope); RepeatStatement r = new RepeatStatement(scope); r.Condition = cond; r.Body = body; stat = r; } else if (reader.ConsumeKeyword("function")) { if (!reader.Is(TokenType.Ident)) error("function name expected"); Expression name = ParseSuffixedExpr(scope, true); // true: only dots and colons FunctionStatement func = ParseFunctionArgsAndBody(scope); func.IsLocal = false; func.Name = name; stat = func; } else if (reader.ConsumeKeyword("local")) { if (reader.Is(TokenType.Ident)) { List<string> varList = new List<string> { reader.Get().Data }; while (reader.ConsumeSymbol(',')) { if (!reader.Is(TokenType.Ident)) error("local variable name expected"); varList.Add(reader.Get().Data); } List<Expression> initList = new List<Expression>(); if (reader.ConsumeSymbol('=')) { do { Expression ex = ParseExpr(scope); initList.Add(ex); } while (reader.ConsumeSymbol(',')); } //now patch var list //we can't do this before getting the init list, because the init list does not //have the locals themselves in scope. List<Expression> newVarList = new List<Expression>(); for (int i = 0; i < varList.Count; i++) { Variable x = scope.CreateLocal(varList[i]); newVarList.Add(new VariableExpression { Var = x }); } AssignmentStatement l = new AssignmentStatement(); l.Lhs = newVarList; l.Rhs = initList; l.IsLocal = true; stat = l; } else if (reader.ConsumeKeyword("function")) { if (!reader.Is(TokenType.Ident)) error("Function name expected"); string name = reader.Get().Data; Variable localVar = scope.CreateLocal(name); FunctionStatement func = ParseFunctionArgsAndBody(scope); func.Name = new VariableExpression { Var = localVar }; func.IsLocal = true; stat = func; } else error("local variable or function definition expected"); } #if !VANILLA_LUA else if (reader.ConsumeSymbol("::")) { if (!reader.Is(TokenType.Ident)) error("label name expected"); string label = reader.Get().Data; if (!reader.ConsumeSymbol("::")) error("'::' expected"); LabelStatement l = new LabelStatement(); l.Label = label; stat = l; } #endif else if (reader.ConsumeKeyword("return")) { List<Expression> exprList = new List<Expression>(); if (!reader.IsKeyword("end") && !reader.IsEof()) { Expression firstEx = ParseExpr(scope); exprList.Add(firstEx); while (reader.ConsumeSymbol(',')) { Expression ex = ParseExpr(scope); exprList.Add(ex); } } ReturnStatement r = new ReturnStatement(); r.Arguments = exprList; stat = r; } else if (reader.ConsumeKeyword("break")) { stat = new BreakStatement(); } else if (reader.ConsumeKeyword("continue")) { stat = new ContinueStatement(); } #if !VANILLA_LUA else if (reader.ConsumeKeyword("goto")) { if (!reader.Is(TokenType.Ident)) error("label expected"); string label = reader.Get().Data; GotoStatement g = new GotoStatement(); g.Label = label; stat = g; } else if (reader.ConsumeKeyword("using")) { // using <a, b = 1, x()> do <statements> end UsingStatement us = new UsingStatement(scope); us.Scope = new Scope(scope); List<Expression> lhs = new List<Expression> { ParseExpr(us.Scope) }; while (reader.ConsumeSymbol(',')) { lhs.Add(ParseSuffixedExpr(us.Scope, true)); } // equals if (!reader.ConsumeSymbol('=')) error("'=' expected"); //rhs List<Expression> rhs = new List<Expression>(); rhs.Add(ParseExpr(us.Scope)); while (reader.ConsumeSymbol(',')) { rhs.Add(ParseExpr(scope)); } AssignmentStatement a = new AssignmentStatement(); a.Lhs = lhs; a.Rhs = rhs; a.IsLocal = true; if (!reader.ConsumeKeyword("do")) error("'do' expected"); List<Statement> block = ParseStatementList(us.Scope); if (!reader.ConsumeKeyword("end")) error("'end' expected"); us.Vars = a; us.Body = block; stat = us; } #endif else { // statementParseExpr Expression suffixed = ParseSuffixedExpr(scope); // assignment or call? if (reader.IsSymbol(',') || reader.IsSymbol('=')) { // check that it was not parenthesized, making it not an lvalue if (suffixed.ParenCount > 0) error("Can not assign to parenthesized expression, it is not an lvalue"); // more processing needed List<Expression> lhs = new List<Expression> { suffixed }; while (reader.ConsumeSymbol(',')) { lhs.Add(ParseSuffixedExpr(scope)); } // equals if (!reader.ConsumeSymbol('=')) error("'=' expected"); //rhs List<Expression> rhs = new List<Expression>(); rhs.Add(ParseExpr(scope)); while (reader.ConsumeSymbol(',')) { rhs.Add(ParseExpr(scope)); } AssignmentStatement a = new AssignmentStatement(); a.Lhs = lhs; a.Rhs = rhs; stat = a; } #if !VANILLA_LUA else if (isAugmentedAssignment(reader.Peek())) { AugmentedAssignmentStatement aas = new AugmentedAssignmentStatement(); Expression left = suffixed; Expression right = null; string augmentedOp = reader.Get().Data; right = ParseExpr(scope); BinOpExpr nRight = new BinOpExpr(); nRight.Lhs = left; nRight.Op = augmentedOp.Substring(0, augmentedOp.Length - 1); // strip the '=' nRight.Rhs = right; aas.Lhs = new List<Expression> { left }; aas.Rhs = new List<Expression> { nRight }; stat = aas; } #endif else if (suffixed is CallExpr || suffixed is TableCallExpr || suffixed is StringCallExpr) { //it's a call statement CallStatement c = new CallStatement(); c.Expression = suffixed; stat = c; } else error("assignment statement expected"); } stat.ScannedTokens = reader.Range(startP, reader.p); if (reader.Peek().Data == ";" && reader.Peek().Type == TokenType.Symbol) { stat.HasSemicolon = true; stat.SemicolonToken = reader.Get(); } if (stat.Scope == null) stat.Scope = scope; stat.LineNumber = startLine; return stat; }
Expression ParseExpr(Scope scope) { return ParseSubExpr(scope, 0); }
Expression ParseSubExpr(Scope scope, int level) { // base item, possibly with unop prefix Expression exp = null; if (isUnOp(reader.Peek().Data) && (reader.Peek().Type == TokenType.Symbol || reader.Peek().Type == TokenType.Keyword)) { string op = reader.Get().Data; exp = ParseSubExpr(scope, unopprio); exp = new UnOpExpr { Rhs = exp, Op = op }; } else exp = ParseSimpleExpr(scope); if (exp is InlineFunctionExpression) return exp; // inline functions cannot have any extra parts // next items in chain while (true) { priority_ prio = getpriority(reader.Peek().Data); if (prio != null && prio.l > level) { string op = reader.Get().Data; Expression rhs = ParseSubExpr(scope, prio.r); BinOpExpr binOpExpr = new BinOpExpr(); binOpExpr.Lhs = exp; binOpExpr.Op = op; binOpExpr.Rhs = rhs; exp = binOpExpr; } else break; } return exp; }
Expression ParseSimpleExpr(Scope scope) { if (reader.Is(TokenType.Number)) return new NumberExpr { Value = reader.Get().Data }; else if (reader.Is(TokenType.DoubleQuoteString) || reader.Is(TokenType.SingleQuoteString) || reader.Is(TokenType.LongString)) { StringExpr s = new StringExpr { Value = reader.Peek().Data, StringType = reader.Peek().Type }; reader.Get(); return s; } else if (reader.ConsumeKeyword("nil")) return new NilExpr(); else if (reader.IsKeyword("false") || reader.IsKeyword("true")) return new BoolExpr { Value = reader.Get().Data == "true" }; else if (reader.ConsumeSymbol("...")) return new VarargExpr(); else if (reader.ConsumeSymbol('{')) { TableConstructorExpr v = new TableConstructorExpr(); while (true) { if (reader.IsSymbol('[')) { // key reader.Get(); // eat '[' Expression key = ParseExpr(scope); if (!reader.ConsumeSymbol(']')) { error("']' expected"); break; } if (!reader.ConsumeSymbol('=')) { error("'=' Expected"); break; } Expression value = ParseExpr(scope); v.EntryList.Add(new TableConstructorKeyExpr { Key = key, Value = value, }); } else if (reader.Is(TokenType.Ident)) { // value or key Token lookahead = reader.Peek(1); if (lookahead.Type == TokenType.Symbol && lookahead.Data == "=") { // we are a key Token key = reader.Get(); if (!reader.ConsumeSymbol('=')) error("'=' Expected"); Expression value = ParseExpr(scope); v.EntryList.Add(new TableConstructorStringKeyExpr { Key = key.Data, Value = value, }); } else { // we are a value Expression val = ParseExpr(scope); v.EntryList.Add(new TableConstructorValueExpr { Value = val }); } } #if !VANILLA_LUA else if (reader.ConsumeKeyword("function")) { if (reader.Peek().Type != TokenType.Ident) error("function name expected"); string name = reader.Get().Data; FunctionStatement fs = ParseFunctionArgsAndBody(scope); fs.IsLocal = false; fs.Name = new StringExpr(name); v.EntryList.Add(new TableConstructorNamedFunctionExpr { Value = fs }); } #endif else if (reader.ConsumeSymbol('}')) break; else { //value Expression value = ParseExpr(scope); v.EntryList.Add(new TableConstructorValueExpr { Value = value }); } if (reader.ConsumeSymbol(';') || reader.ConsumeSymbol(',')) { // I could have used just an empty statement (';') here, instead of { } // but that leaves a warning, which clutters up the output // other than that, all is good } else if (reader.ConsumeSymbol('}')) break; else { error("'}' or table entry Expected"); break; } } return v; } else if (reader.ConsumeKeyword("function")) { AnonymousFunctionExpr func = ParseExprFunctionArgsAndBody(scope); //func.IsLocal = true; return func; } #if !VANILLA_LUA else if (reader.ConsumeSymbol('|')) { // inline function... |<arg list>| -> <expr>, <expr> InlineFunctionExpression func = new InlineFunctionExpression(); func.Scope = new Scope(scope); // arg list List<Variable> arglist = new List<Variable>(); bool isVarArg = false; while (reader.ConsumeSymbol('|') == false) { if (reader.Is(TokenType.Ident)) { Variable arg = new Variable(); arg.Name = reader.Get().Data; func.Scope.AddLocal(arg); arglist.Add(arg); if (!reader.ConsumeSymbol(',')) { if (reader.ConsumeSymbol('|')) { break; } else { error("'|' expected"); break; } } } else if (reader.ConsumeSymbol("...")) { isVarArg = true; if (!reader.ConsumeSymbol('|')) error("'...' must be the last argument of a function"); break; } else { error("Argument name or '...' expected"); break; } } if (!reader.ConsumeSymbol("->")) error("'->' expected"); // body List<Expression> body = new List<Expression> { ParseExpr(func.Scope) }; while (reader.ConsumeSymbol(',')) body.Add(ParseExpr(func.Scope)); // end //nodeFunc.AstType = AstType.Function; func.Arguments = arglist; func.Expressions = body; func.IsVararg = isVarArg; return func; } #endif else return ParseSuffixedExpr(scope); }
Expression ParseSuffixedExpr(Scope scope, bool onlyDotColon = false) { // base primary expression Expression prim = ParsePrimaryExpr(scope); while (true) { if (reader.IsSymbol('.') || reader.IsSymbol(':')) { string symb = reader.Get().Data; // '.' or ':' // TODO: should we allow keywords? I vote no. if (!reader.Is(TokenType.Ident)) error("<Ident> expected"); Token id = reader.Get(); MemberExpr m = new MemberExpr(); m.Base = prim; m.Indexer = symb; m.Ident = id.Data; prim = m; } else if (!onlyDotColon && reader.ConsumeSymbol('[')) { int pass = 0; const int maxamount = 100; bool wasLastNumeric = false; bool first = true; bool hadComma = false; do { Token tok = reader.Peek(); int col = tok.Column; int line = tok.Line; Expression ex = ParseExpr(scope); //if (!reader.ConsumeSymbol(']')) //error("']' expected"); IndexExpr i = new IndexExpr(); i.Base = prim; i.Index = ex; prim = i; if ((first || wasLastNumeric) && ex is NumberExpr && hadComma == false) { tok = reader.Peek(); bool cma = reader.ConsumeSymbol(','); if (cma && hadComma == false && first == false) error("Unexpected ',' in matrice indexing", tok.Line, tok.Column, tok); //else if (cma == false && hadComma) // ; hadComma = cma; } else { tok = reader.Peek(); bool cma = reader.ConsumeSymbol(','); //if (cma == false) // break; if (cma && hadComma == false) error("Unexpected ',' in matrice indexing", -1, -1, tok); else if (cma == false && ex is NumberExpr == false && wasLastNumeric && hadComma == false) { error("Expected numeric constant in matrice indexing", line, col, tok); } else if (cma == false && hadComma) if (tok.Type == TokenType.Symbol && tok.Data == "]") ; else error("Expected ','", -1, -1, tok); else if (cma == false) { break; } else { hadComma = true; } hadComma = cma; } if (pass++ >= maxamount) error("Maximum index depth reached"); wasLastNumeric = ex is NumberExpr; first = false; } while (!(reader.Peek().Data == "]")); if (!reader.ConsumeSymbol(']')) error("']' expected"); } else if (!onlyDotColon && reader.ConsumeSymbol('(')) { List<Expression> args = new List<Expression>(); while (!reader.ConsumeSymbol(')')) { Expression ex = ParseExpr(scope); args.Add(ex); if (!reader.ConsumeSymbol(',')) { if (reader.ConsumeSymbol(')')) break; else error("')' expected"); break; } } CallExpr c = new CallExpr(); c.Base = prim; c.Arguments = args; prim = c; } else if (!onlyDotColon && (reader.Is(TokenType.SingleQuoteString) || reader.Is(TokenType.DoubleQuoteString) || reader.Is(TokenType.LongString))) { //string call StringCallExpr e = new StringCallExpr(); e.Base = prim; e.Arguments = new List<Expression> { new StringExpr(reader.Peek().Data) { StringType = reader.Peek().Type } }; reader.Get(); prim = e; } else if (!onlyDotColon && reader.IsSymbol('{')) { // table call // Fix for the issue with whole expr being parsed, not just table. // See LuaMinify issue #2 (https://github.com/stravant/LuaMinify/issues/2) //Expression ex = ParseExpr(scope); Expression ex = ParseSimpleExpr(scope); TableCallExpr t = new TableCallExpr(); t.Base = prim; t.Arguments = new List<Expression> { ex }; prim = t; } else break; } return prim; }
Expression ParsePrimaryExpr(Scope c) { //Console.WriteLine(tok.Peek().Type + " " + tok.Peek().Data); if (reader.ConsumeSymbol('(')) { Expression ex = ParseExpr(c); if (!reader.ConsumeSymbol(')')) error("')' expected"); // save the information about parenthesized expressions somewhere ex.ParenCount = ex.ParenCount + 1; return ex; } else if (reader.Is(TokenType.Ident)) { Token id = reader.Get(); VariableExpression v = new VariableExpression(); Variable var = c.GetLocal(id.Data); if (var == null) { var = c.GetGlobal(id.Data); if (var == null) { v.Var = c.CreateGlobal(id.Data); } else { v.Var = var; v.Var.References++; } } else { v.Var = var; v.Var.References++; } return v; } else error("primary expression expected"); return null; // satisfy the C# compiler, but this will never happen }
public Chunk Parse() { Scope s = new Scope(); return new Chunk { Body = ParseStatementList(s), Scope = s, ScannedTokens = reader.tokens }; }
List<Statement> ParseStatementList(Scope scope) { List<Statement> c = new List<Statement>(); while (!isClosing(reader.Peek().Data) && !reader.IsEof()) { Statement nodeStatement = ParseStatement(scope); //stats[#stats+1] = nodeStatement c.Add(nodeStatement); } return c; }