/// <summary> /// Adds a dependency to a .NET/CLR library using clr.load, /// then it sets type to the type in the assembly. /// </summary> /// <param name="c"></param> /// <param name="assembly"></param> /// <param name="type"></param> public static void AddClrDependency(Chunk c, string assembly, string type) { AddClrDependency(c, assembly); // Assignment with FunctionCall AssignmentStatement a = new AssignmentStatement(); CallExpr call = new CallExpr(); call.Scope = c.Scope; Variable require = c.Scope.GetVariable("clr"); VariableExpression v = new VariableExpression(); if (require == null) { require = c.Scope.CreateGlobal("clr"); require.IsGlobal = true; } string name = "", varName = ""; if (type.Contains('.')) { name = type.Substring(0, type.LastIndexOf('.')); varName = type.Substring(type.LastIndexOf('.') + 1); } else { name = assembly; varName = type; } v.Var = require; MemberExpr me = new MemberExpr(); me.Base = v; me.Indexer = "."; me.Ident = "getns"; call.Base = me; call.Arguments.Add(new StringExpr(name) { StringType = TokenType.DoubleQuoteString }); a.IsLocal = true; // local import MemberExpr me2 = new MemberExpr(); me2.Base = call; me2.Indexer = "."; me2.Ident = varName; a.Rhs.Add(me2); Variable var = c.Scope.GetVariable(varName); VariableExpression v2 = new VariableExpression(); if (var == null) { var = c.Scope.CreateLocal(varName); } v2.Var = var; a.Lhs.Add(v2); // Insert after the load c.Body.Insert(1, a); }
void DoExpr(Expression e, bool setVar = false, int setVarLhsCount = -1, bool onlyCheckConsts = false) { if (e is AnonymousFunctionExpr) // function() ... end { } else if (e is BinOpExpr) { BinOpExpr boe = e as BinOpExpr; switch (boe.GetOperator()) { case BinaryOperator.Add: binOp("ADD", boe.Lhs, boe.Rhs); return; case BinaryOperator.Subtract: binOp("SUB", boe.Lhs, boe.Rhs); return; case BinaryOperator.Multiply: binOp("MUL", boe.Lhs, boe.Rhs); return; case BinaryOperator.Divide: binOp("DIV", boe.Lhs, boe.Rhs); return; case BinaryOperator.Power: binOp("POW", boe.Lhs, boe.Rhs); return; case BinaryOperator.Modulus: binOp("MOD", boe.Lhs, boe.Rhs); return; case BinaryOperator.Concat: binOp("CONCAT", boe.Lhs, boe.Rhs); return; case BinaryOperator.And: break; case BinaryOperator.Or: break; case BinaryOperator.LessThan: break; case BinaryOperator.LessThanOrEqualTo: break; case BinaryOperator.GreaterThan: break; case BinaryOperator.GreaterThanOrEqualTo: break; case BinaryOperator.NotEqual: break; case BinaryOperator.ShiftRight: CallExpr ce = new CallExpr(); ce.Arguments.Add(boe.Lhs); ce.Arguments.Add(boe.Rhs); ce.Base = new MemberExpr() { Base = new VariableExpression() { Var = new Variable() { Name = "bit", IsGlobal = true } }, Ident = "rshift", Indexer = ".", }; DoExpr(ce); return; case BinaryOperator.ShiftLeft: ce = new CallExpr(); ce.Arguments.Add(boe.Lhs); ce.Arguments.Add(boe.Rhs); ce.Base = new MemberExpr() { Base = new VariableExpression() { Var = new Variable() { Name = "bit", IsGlobal = true } }, Ident = "lshift", Indexer = ".", }; DoExpr(ce); return; case BinaryOperator.Xor: ce = new CallExpr(); ce.Arguments.Add(boe.Lhs); ce.Arguments.Add(boe.Rhs); ce.Base = new MemberExpr() { Base = new VariableExpression() { Var = new Variable() { Name = "bit", IsGlobal = true } }, Ident = "bxor", Indexer = ".", }; DoExpr(ce); return; case BinaryOperator.BitAnd: ce = new CallExpr(); ce.Arguments.Add(boe.Lhs); ce.Arguments.Add(boe.Rhs); ce.Base = new MemberExpr() { Base = new VariableExpression() { Var = new Variable() { Name = "bit", IsGlobal = true } }, Ident = "band", Indexer = ".", }; DoExpr(ce); return; case BinaryOperator.BitOr: ce = new CallExpr(); ce.Arguments.Add(boe.Lhs); ce.Arguments.Add(boe.Rhs); ce.Base = new MemberExpr() { Base = new VariableExpression() { Var = new Variable() { Name = "bit", IsGlobal = true } }, Ident = "bor", Indexer = ".", }; DoExpr(ce); return; case BinaryOperator.BitNot: ce = new CallExpr(); ce.Arguments.Add(boe.Lhs); ce.Arguments.Add(boe.Rhs); ce.Base = new MemberExpr() { Base = new VariableExpression() { Var = new Variable() { Name = "bit", IsGlobal = true } }, Ident = "bnot", Indexer = ".", }; DoExpr(ce); return; case BinaryOperator.NONE: default: throw new Exception("Unknown binary operator '" + boe.Op + "'"); } } else if (e is BoolExpr) { bool v = ((BoolExpr)e).Value; Instruction i = new Instruction("LOADBOOL"); i.A = block.getreg(); i.B = v ? 1 : 0; i.C = 0; emit(i); return; } else if (e is CallExpr)//&& (!(e is StringCallExpr) && !(e is TableCallExpr))) { CallExpr ce = e as CallExpr; int breg = ++block.regnum; DoExpr(ce.Base); bool isZero = false; bool isMethod = false; Expression ex = ce.Base; while (ex != null) { if (ex is IndexExpr) ex = ((IndexExpr)ex).Index; else if (ex is MemberExpr) { MemberExpr me = ex as MemberExpr; if (me.Indexer == ":") { isMethod = true; break; } else break; //ex = me.Ident; } else break; } foreach (Expression e2 in ce.Arguments) { DoExpr(e2); if (e2 is CallExpr || block.Chunk.Instructions[block.Chunk.Instructions.Count - 1].Opcode == Instruction.LuaOpcode.CALL) { isZero = true; Instruction i_ = block.Chunk.Instructions[block.Chunk.Instructions.Count - 1]; Debug.Assert(i_.Opcode == Instruction.LuaOpcode.CALL); i_.C = 0; } } Instruction i = new Instruction("CALL"); i.A = breg; if (isMethod) //i.B++; i.B = isZero ? 2 : (ce.Arguments.Count > 0 ? 2 + ce.Arguments.Count : 2); else i.B = isZero ? 0 : (ce.Arguments.Count > 0 ? 1 + ce.Arguments.Count : 1); i.C = setVarLhsCount == 0 || setVarLhsCount == -1 ? 1 : //(isZero ? 0 : 1) : 1 + setVarLhsCount; // (isZero ? 0 : 1 + setVarLhsCount); //i.C = setVarLhsCount == 0 || setVarLhsCount == -1 ? 1 : 1 + setVarLhsCount; emit(i); return; } else if (e is StringCallExpr) { throw new Exception(); } else if (e is TableCallExpr) { throw new Exception(); } else if (e is IndexExpr) { IndexExpr ie = e as IndexExpr; int regnum = block.regnum; DoExpr(ie.Base); DoExpr(ie.Index); Instruction i = new Instruction("GETTABLE"); i.A = regnum; i.B = regnum; i.C = block.regnum - 1;// block.getreg(); emit(i); block.regnum = regnum + 1; return; } else if (e is InlineFunctionExpression) // |<args>| -> <exprs> { InlineFunctionExpression i = e as InlineFunctionExpression; AnonymousFunctionExpr af = new AnonymousFunctionExpr(); af.Arguments = i.Arguments; af.IsVararg = i.IsVararg; af.Body = new List<Statement>() { new ReturnStatement { Arguments = i.Expressions } }; DoExpr(af); } else if (e is MemberExpr) { MemberExpr me = e as MemberExpr; if (me.Indexer == ".") { int regnum = block.regnum; DoExpr(me.Base); DoExpr(new StringExpr(me.Ident), false, -1, true); Instruction i = new Instruction("GETTABLE"); i.A = regnum; i.B = regnum; i.C = 256 + block.K[me.Ident]; //i.C = block.regnum - 1;// block.getreg(); emit(i); block.regnum = regnum + 1; return; } else if (me.Indexer == ":") { int regnum = block.regnum; DoExpr(me.Base); DoExpr(new StringExpr(me.Ident), false, -1, true); Instruction i = new Instruction("SELF"); i.A = regnum; i.B = regnum; i.C = 256 + block.K[me.Ident]; //i.C = block.regnum - 1;// block.getreg(); emit(i); block.regnum = regnum + 1; return; } else throw new Exception("Unknown member indexer '" + me.Indexer + "'"); } else if (e is NilExpr) { Instruction i = new Instruction("LOADNIL"); i.A = block.getreg(); i.B = setVarLhsCount == -1 ? i.A : setVarLhsCount - 1; i.C = 0; emit(i); return; } else if (e is NumberExpr) { NumberExpr ne = e as NumberExpr; // TODO: this can optimized into a Dictionary to avoid re-parsing numbers each time double r; int x = Lua.luaO_str2d(ne.Value.Replace("_", ""), out r); if (x == 0) throw new LuaSourceException(line, 0, "Invalid number"); if (onlyCheckConsts == false) { Instruction i = new Instruction("loadk"); i.A = block.getreg(); i.Bx = block.K[r]; emit(i); } else block.K.Check(r); return; } else if (e is StringExpr) { StringExpr se = e as StringExpr; string s = se.Value; if (se.StringType != TokenType.LongString) s = Unescaper.Unescape(s); else { int i = 1; while (s[i] != '[') i++; i++; s = s.Substring(i, s.Length - i - 2); } if (onlyCheckConsts == false) { Instruction i2 = new Instruction("loadk"); i2.A = block.getreg(); i2.Bx = block.K[s]; emit(i2); } else block.K.Check(s); return; } else if (e is TableConstructorExpr) { Instruction i = new Instruction("NEWTABLE"); int tblA = block.regnum; i.A = block.getreg(); i.B = 0; i.C = 0; emit(i); TableConstructorExpr tce = e as TableConstructorExpr; if (tce.EntryList.Count == 0) return; int b = 0; bool wasLastCall = false; foreach (Expression e2 in tce.EntryList) { if (e2 is TableConstructorKeyExpr) { TableConstructorKeyExpr tcke = e2 as TableConstructorKeyExpr; DoExpr(tcke.Key); DoExpr(tcke.Value); } else if (e2 is TableConstructorNamedFunctionExpr) { TableConstructorNamedFunctionExpr tcnfe = e2 as TableConstructorNamedFunctionExpr; } else if (e2 is TableConstructorStringKeyExpr) { TableConstructorStringKeyExpr tcske = e2 as TableConstructorStringKeyExpr; DoExpr(new StringExpr(tcske.Key)); DoExpr(tcske.Value); } else if (e2 is TableConstructorValueExpr) { TableConstructorValueExpr tcve = e2 as TableConstructorValueExpr; DoExpr(tcve.Value); if (tcve.Value is VarargExpr || tcve.Value is CallExpr) wasLastCall = true; else wasLastCall = false; } b++; } i.B = b; i = new Instruction("SETLIST"); i.A = tblA; if (wasLastCall) i.B = 0; else i.B = block.regnum - 1; i.C = tblA + 1; emit(i); block.regnum = tblA; return; } else if (e is UnOpExpr) { UnOpExpr uoe = e as UnOpExpr; switch (uoe.GetOperator()) { case UnaryOperator.Not: unOp("NOT", uoe.Rhs); return; case UnaryOperator.Length: unOp("LEN", uoe.Rhs); return; case UnaryOperator.BitNot: CallExpr ce = new CallExpr(); ce.Arguments.Add(uoe.Rhs); ce.Base = new MemberExpr() { Base = new VariableExpression() { Var = new Variable() { Name = "bit", IsGlobal = true } }, Ident = "bnot", Indexer = ".", }; DoExpr(ce); return; case UnaryOperator.Negate: unOp("UNM", uoe.Rhs); return; case UnaryOperator.UnNegate: ce = new CallExpr(); ce.Arguments.Add(uoe.Rhs); ce.Base = new MemberExpr() { Base = new VariableExpression() { Var = new Variable() { Name = "math", IsGlobal = true } }, Ident = "abs", Indexer = ".", }; DoExpr(ce); return; case UnaryOperator.NONE: default: throw new Exception("Unknown unary operator '" + uoe.Op + "'"); } } else if (e is VarargExpr) { if (block.Chunk.Vararg == 0) throw new LuaSourceException(0, 0, "Cannot use varargs (...) outside of a vararg function"); Instruction i = new Instruction("VARARG"); i.A = block.getreg(); if (setVar) { i.B = setVarLhsCount == -1 ? 0 : 1 + setVarLhsCount; } else { i.B = 0; } emit(i); return; } else if (e is VariableExpression) { VariableExpression ve = e as VariableExpression; if (ve.Var.IsGlobal == false) { // local if (setVar) { //Instruction i = new Instruction("move"); //i.B = block.V[ve.Var.Name]; // moved into here //i.A = block.getreg(); // from here //emit(i); int _TMP_001_ = block.V[ve.Var.Name]; // Should probably just add a Check method in Var2Reg block.CheckLocalName(ve.Var.Name); } else { Instruction i = new Instruction("move"); i.B = block.V[ve.Var.Name]; // moved into here i.A = block.getreg(); // from here emit(i); } } else { // global Instruction i = null; if (setVar) { i = new Instruction("setglobal"); i.A = block.regnum - 1; // ret } else { i = new Instruction("getglobal"); i.A = block.getreg(); // ret } i.Bx = block.K[ve.Var.Name]; // const emit(i); } return; } throw new NotImplementedException(e.GetType().Name + " is not implemented"); }
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; }