Browse Source

binary dump

Xanathar 10 years ago
parent
commit
5cd046c018

+ 277 - 0
src/MoonSharp.Interpreter.Tests/EndToEnd/BinaryDumpTests.cs

@@ -0,0 +1,277 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+
+namespace MoonSharp.Interpreter.Tests.EndToEnd
+{
+	[TestFixture]
+	public class BinaryDumpTests
+	{
+		private DynValue Script_RunString(string script)
+		{
+			Script s1 = new Script();
+			DynValue v1 = s1.LoadString(script);
+
+			using (MemoryStream ms = new MemoryStream())
+			{
+				s1.Dump(v1, ms);
+				ms.Seek(0, SeekOrigin.Begin);
+
+				Script s2 = new Script();
+				DynValue func = s2.LoadStream(ms);
+				return func.Function.Call();
+			}
+		}
+
+		private DynValue Script_LoadFunc(string script, string funcname)
+		{
+			Script s1 = new Script();
+			DynValue v1 = s1.DoString(script);
+			DynValue func = s1.Globals.Get(funcname);
+
+			using (MemoryStream ms = new MemoryStream())
+			{
+				s1.Dump(func, ms);
+				ms.Seek(0, SeekOrigin.Begin);
+
+				Script s2 = new Script();
+				return s2.LoadStream(ms);
+			}
+		}
+
+		[Test]
+		public void BinDump_StringDump()
+		{
+			string script = @"
+				local str = string.dump(function(n) return n * n; end);
+				local fn = load(str);
+				return fn(9);
+			";
+
+			DynValue res = Script.RunString(script);
+
+			Assert.AreEqual(DataType.Number, res.Type);
+			Assert.AreEqual(81, res.Number);
+		}
+
+		[Test]
+		public void BinDump_StandardDumpFunc()
+		{
+			string script = @"
+				function fact(n)
+					return n * 24;
+				end
+
+				local str = string.dump(fact);
+				
+			";
+
+			DynValue fact = Script_LoadFunc(script, "fact");
+			DynValue res = fact.Function.Call(5);
+
+			Assert.AreEqual(DataType.Number, res.Type);
+			Assert.AreEqual(120, res.Number);
+		}
+
+
+
+		[Test]
+		public void BinDump_FactorialDumpFunc()
+		{
+			string script = @"
+				function fact(n)
+					if (n == 0) then return 1; end
+					return fact(n - 1) * n;
+				end
+			";
+
+			DynValue fact = Script_LoadFunc(script, "fact");
+			fact.Function.OwnerScript.Globals.Set("fact", fact);
+			DynValue res = fact.Function.Call(5);
+
+			Assert.AreEqual(DataType.Number, res.Type);
+			Assert.AreEqual(120, res.Number);
+		}
+
+		[Test]
+		public void BinDump_FactorialDumpFuncGlobal()
+		{
+			string script = @"
+				x = 0
+
+				function fact(n)
+					if (n == x) then return 1; end
+					return fact(n - 1) * n;
+				end
+			";
+
+			DynValue fact = Script_LoadFunc(script, "fact");
+			fact.Function.OwnerScript.Globals.Set("fact", fact);
+			fact.Function.OwnerScript.Globals.Set("x", DynValue.NewNumber(0));
+			DynValue res = fact.Function.Call(5);
+
+			Assert.AreEqual(DataType.Number, res.Type);
+			Assert.AreEqual(120, res.Number);
+		}
+
+
+		[Test]
+		[ExpectedException(typeof(ArgumentException))]
+		public void BinDump_FactorialDumpFuncUpvalue()
+		{
+			string script = @"
+				local x = 0
+
+				function fact(n)
+					if (n == x) then return 1; end
+					return fact(n - 1) * n;
+				end
+			";
+
+			DynValue fact = Script_LoadFunc(script, "fact");
+			fact.Function.OwnerScript.Globals.Set("fact", fact);
+			fact.Function.OwnerScript.Globals.Set("x", DynValue.NewNumber(0));
+			DynValue res = fact.Function.Call(5);
+
+			Assert.AreEqual(DataType.Number, res.Type);
+			Assert.AreEqual(120, res.Number);
+		}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+		[Test]
+		public void BinDump_FactorialClosure()
+		{
+			string script = @"
+local x = 5;
+
+function fact(n)
+	if (n == x) then return 1; end
+	return fact(n - 1) * n;
+end
+
+x = 0;
+
+y = fact(5);
+
+x = 3;
+
+y = y + fact(5);
+
+return y;
+";
+
+			DynValue res = Script_RunString(script);
+
+			Assert.AreEqual(DataType.Number, res.Type);
+			Assert.AreEqual(140, res.Number);
+		}
+	
+		[Test]
+		public void BinDump_ClosureOnParam()
+		{
+			string script = @"
+				local function g (z)
+				  local function f(a)
+					return a + z;
+				  end
+				  return f;
+				end
+
+				return (g(3)(2));";
+
+			DynValue res = Script_RunString(script);
+
+			Assert.AreEqual(DataType.Number, res.Type);
+			Assert.AreEqual(5, res.Number);
+		}
+
+		[Test]
+		public void BinDump_NestedUpvalues()
+		{
+			string script = @"
+	local y = y;
+
+	local x = 0;
+	local m = { };
+
+	function m:a()
+		self.t = {
+			dojob = function() 
+				if (x == 0) then return 1; else return 0; end
+			end,
+		};
+	end
+
+	m:a();
+
+	return 10 * m.t.dojob();
+								";
+
+			DynValue res = Script_RunString(script);
+
+			Assert.AreEqual(DataType.Number, res.Type);
+			Assert.AreEqual(10, res.Number);
+		}
+
+
+		[Test]
+		public void BinDump_NestedOutOfScopeUpvalues()
+		{
+			string script = @"
+
+	function X()
+		local y = y;
+
+		local x = 0;
+		local m = { };
+
+		function m:a()
+			self.t = {
+				dojob = function() 
+					if (x == 0) then return 1; else return 0; end
+				end,
+			};
+		end
+
+		return m;
+	end
+
+	Q = X();
+
+	Q:a();
+
+	return 10 * Q.t.dojob();
+								";
+
+			DynValue res = Script_RunString(script);
+
+			Assert.AreEqual(DataType.Number, res.Type);
+			Assert.AreEqual(10, res.Number);
+		}
+
+	}
+}

+ 107 - 0
src/MoonSharp.Interpreter.Tests/Units/BinDumpStreamTests.cs

@@ -0,0 +1,107 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using MoonSharp.Interpreter.IO;
+using NUnit.Framework;
+
+namespace MoonSharp.Interpreter.Tests.Units
+{
+	[TestFixture]
+	public class BinDumpStreamTests
+	{
+		[Test]
+		public void BinDumpBinaryStreams_TestIntWrites()
+		{
+			int[] values = new int[] { 0, 1, -1, 10, -10, 32767, 32768, -32767, -32768, int.MinValue, int.MaxValue };
+
+			using(MemoryStream ms_orig = new MemoryStream())
+			{
+				UndisposableStream ms = new UndisposableStream(ms_orig);
+
+				using(BinDumpBinaryWriter bdbw = new BinDumpBinaryWriter(ms, Encoding.UTF8))
+				{
+					for(int i = 0; i < values.Length; i++)
+					{
+						bdbw.Write(values[i]);
+					}
+				}
+
+				ms.Seek(0, SeekOrigin.Begin);
+
+				using(BinDumpBinaryReader bdbr = new BinDumpBinaryReader(ms, Encoding.UTF8))
+				{
+					for (int i = 0; i < values.Length; i++)
+					{
+						int v = bdbr.ReadInt32();
+						Assert.AreEqual(values[i], v, "i = "+ i.ToString());
+					}
+				}
+			}
+		}
+
+		[Test]
+		public void BinDumpBinaryStreams_TestUIntWrites()
+		{
+			uint[] values = new uint[] { 0, 1, 0x7F, 10, 0x7E, 32767, 32768, uint.MinValue, uint.MaxValue };
+
+			using (MemoryStream ms_orig = new MemoryStream())
+			{
+				UndisposableStream ms = new UndisposableStream(ms_orig);
+
+				using (BinDumpBinaryWriter bdbw = new BinDumpBinaryWriter(ms, Encoding.UTF8))
+				{
+					for (int i = 0; i < values.Length; i++)
+					{
+						bdbw.Write(values[i]);
+					}
+				}
+
+				ms.Seek(0, SeekOrigin.Begin);
+
+				using (BinDumpBinaryReader bdbr = new BinDumpBinaryReader(ms, Encoding.UTF8))
+				{
+					for (int i = 0; i < values.Length; i++)
+					{
+						uint v = bdbr.ReadUInt32();
+						Assert.AreEqual(values[i], v, "i = " + i.ToString());
+					}
+				}
+			}
+		}
+
+
+		[Test]
+		public void BinDumpBinaryStreams_TestStringWrites()
+		{
+			string[] values = new string[] { "hello", "you", "fool", "hello", "I", "love", "you" };
+
+			using (MemoryStream ms_orig = new MemoryStream())
+			{
+				UndisposableStream ms = new UndisposableStream(ms_orig);
+
+				using (BinDumpBinaryWriter bdbw = new BinDumpBinaryWriter(ms, Encoding.UTF8))
+				{
+					for (int i = 0; i < values.Length; i++)
+					{
+						bdbw.Write(values[i]);
+					}
+				}
+
+				ms.Seek(0, SeekOrigin.Begin);
+
+				using (BinDumpBinaryReader bdbr = new BinDumpBinaryReader(ms, Encoding.UTF8))
+				{
+					for (int i = 0; i < values.Length; i++)
+					{
+						string v = bdbr.ReadString();
+						Assert.AreEqual(values[i], v, "i = " + i.ToString());
+					}
+				}
+			}
+		}
+
+
+	}
+}

+ 133 - 0
src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_BinaryDump.cs

@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using MoonSharp.Interpreter.DataStructs;
+using MoonSharp.Interpreter.Debugging;
+using MoonSharp.Interpreter.IO;
+
+namespace MoonSharp.Interpreter.Execution.VM
+{
+	sealed partial class Processor
+	{
+		const ulong DUMP_CHUNK_MAGIC = 0x1A0D234E4F4F4D1D;
+		const int DUMP_CHUNK_VERSION = 0x100;
+
+		internal static bool IsDumpStream(Stream stream)
+		{
+			using (BinaryReader br = new BinaryReader(stream, Encoding.UTF8))
+			{
+				ulong magic = br.ReadUInt64();
+				stream.Seek(-8, SeekOrigin.Current);
+				return magic == DUMP_CHUNK_MAGIC;
+			}
+		}
+
+		internal int Dump(Stream stream, int baseAddress, bool hasUpvalues)
+		{
+			using (BinaryWriter bw = new BinDumpBinaryWriter(stream, Encoding.UTF8))
+			{
+				Dictionary<SymbolRef, int> symbolMap = new Dictionary<SymbolRef, int>();
+
+				Instruction meta = m_RootChunk.Code[baseAddress];
+
+				// skip nops
+				while (meta.OpCode == OpCode.Nop)
+				{
+					baseAddress++;
+					meta = m_RootChunk.Code[baseAddress];
+				}
+
+				if (meta.OpCode != OpCode.FuncMeta)
+					throw new ArgumentException("baseAddress");
+
+				bw.Write(DUMP_CHUNK_MAGIC);
+				bw.Write(DUMP_CHUNK_VERSION);
+				bw.Write(hasUpvalues);
+				bw.Write(meta.NumVal);
+
+				for (int i = 0; i <= meta.NumVal; i++)
+				{
+					SymbolRef[] symbolList;
+					SymbolRef symbol;
+
+					m_RootChunk.Code[baseAddress + i].GetSymbolReferences(out symbolList, out symbol);
+
+					if (symbol != null)
+						AddSymbolToMap(symbolMap, symbol);
+
+					if (symbolList != null)
+						foreach (var s in symbolList)
+							AddSymbolToMap(symbolMap, s);
+				}
+
+				foreach (SymbolRef sr in symbolMap.Keys.ToArray())
+				{
+					if (sr.i_Env != null)
+						AddSymbolToMap(symbolMap, sr.i_Env);
+				}
+
+				bw.Write(symbolMap.Count);
+
+				foreach (KeyValuePair<SymbolRef, int> pair in symbolMap.OrderBy(kvp => kvp.Value))
+					pair.Key.WriteBinary(bw);
+
+				foreach (KeyValuePair<SymbolRef, int> pair in symbolMap.OrderBy(kvp => kvp.Value))
+					pair.Key.WriteBinaryEnv(bw, symbolMap);
+
+				for (int i = 0; i <= meta.NumVal; i++)
+					m_RootChunk.Code[baseAddress + i].WriteBinary(bw, baseAddress, symbolMap);
+
+				return meta.NumVal + baseAddress + 1;
+			}
+		}
+
+		private void AddSymbolToMap(Dictionary<SymbolRef, int> symbolMap, SymbolRef s)
+		{
+			if (!symbolMap.ContainsKey(s))
+				symbolMap.Add(s, symbolMap.Count);
+		}
+
+		internal int Undump(Stream stream, int sourceID, Table envTable, out bool hasUpvalues)
+		{
+			int baseAddress = m_RootChunk.Code.Count;
+			SourceRef sourceRef = new SourceRef(sourceID, 0, 0, 0, 0, false);
+
+			using (BinaryReader br = new BinDumpBinaryReader(stream, Encoding.UTF8))
+			{
+				ulong headerMark = br.ReadUInt64();
+
+				if (headerMark != DUMP_CHUNK_MAGIC)
+					throw new ArgumentException("Not a MoonSharp chunk");
+
+				int version = br.ReadInt32();
+
+				if (version != DUMP_CHUNK_VERSION)
+					throw new ArgumentException("Invalid version");
+
+				hasUpvalues = br.ReadBoolean();
+
+				int len = br.ReadInt32();
+
+				int numSymbs = br.ReadInt32();
+				SymbolRef[] allSymbs = new SymbolRef[numSymbs];
+
+				for (int i = 0; i < numSymbs; i++)
+					allSymbs[i] = SymbolRef.ReadBinary(br);
+
+				for (int i = 0; i < numSymbs; i++)
+					allSymbs[i].ReadBinaryEnv(br, allSymbs);
+
+				for (int i = 0; i <= len; i++)
+				{
+					Instruction I = Instruction.ReadBinary(sourceRef, br, baseAddress, envTable, allSymbs);
+					m_RootChunk.Code.Add(I);
+				}
+
+				return baseAddress;
+			}
+		}
+	}
+}

+ 4 - 2
src/MoonSharp.Interpreter/MoonSharp.Interpreter.csproj

@@ -71,8 +71,9 @@
     <CodeAnalysisRuleSet>ExtendedDesignGuidelineRules.ruleset</CodeAnalysisRuleSet>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="Antlr4.Runtime.net35">
-      <HintPath>..\packages\Antlr4.Runtime.4.3.0\lib\net35-client\Antlr4.Runtime.net35.dll</HintPath>
+    <Reference Include="Antlr4.Runtime, Version=4.4.0.0, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\..\antlr4cs\runtime\CSharp\Antlr4.Runtime\bin\net35-client\Release\Antlr4.Runtime.dll</HintPath>
     </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
@@ -253,6 +254,7 @@
     <Compile Include="Tree\Expressions\DynamicExpression.cs" />
     <Compile Include="Tree\Expressions\PowerOperatorExpression.cs" />
     <Compile Include="Tree\Expressions\UnaryOperatorExpression.cs" />
+    <Compile Include="Tree\Fast_Interface\llex.cs" />
     <Compile Include="Tree\IVariable.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Tree\Expressions\ExprListExpression.cs" />

+ 1 - 4
src/MoonSharp.Interpreter/Tree/Expressions/ExprListExpression.cs

@@ -14,10 +14,7 @@ namespace MoonSharp.Interpreter.Tree.Expressions
 		public ExprListExpression(LuaParser.ExplistContext tree, ScriptLoadingContext lcontext)
 			: base(tree, lcontext)
 		{
-			expressions = tree.children
-				.Select(t => NodeFactory.CreateExpression(t, lcontext))
-				.Where(e => e != null)
-				.ToArray();
+			expressions = NodeFactory.CreateExpessionArray(tree.children, lcontext);
 		}
 
 

+ 523 - 0
src/MoonSharp.Interpreter/Tree/Fast_Interface/llex.cs

@@ -0,0 +1,523 @@
+#if false
+/*
+** $Id: llex.c,v 2.20.1.1 2007/12/27 13:02:25 roberto Exp $
+** Lexical Analyzer
+** See Copyright Notice in lua.h
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+
+namespace KopiLua
+{
+	using TValue = MoonSharp.Interpreter.DataType;
+	using LuaNumberType = System.Double;
+
+	public partial class Lua
+	{
+		public const int FIRSTRESERVED	= 257;
+
+		/* maximum length of a reserved word */
+		public const int TOKENLEN	= 9; // "function"
+
+
+		/*
+		* WARNING: if you change the order of this enumeration,
+		* grep "ORDER RESERVED"
+		*/
+		public enum RESERVED {
+		  /* terminal symbols denoted by reserved words */
+		  TK_AND = FIRSTRESERVED, TK_BREAK,
+		  TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
+		  TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
+		  TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
+		  /* other terminal symbols */
+		  TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER,
+		  TK_NAME, TK_STRING, TK_EOS
+		};
+
+		/* number of reserved words */
+		public const int NUMRESERVED = (int)RESERVED.TK_WHILE - FIRSTRESERVED + 1;
+
+		public class SemInfo {
+			public SemInfo() { }
+			public SemInfo(SemInfo copy)
+			{
+				this.r = copy.r;
+				this.ts = copy.ts;
+			}
+			public LuaNumberType r;
+			public string ts;
+		} ;  /* semantics information */
+
+		public class Token {
+			public Token() { }
+			public Token(Token copy)
+			{
+				this.token = copy.token;
+				this.seminfo = new SemInfo(copy.seminfo);
+			}
+			public int token;
+			public SemInfo seminfo = new SemInfo();
+		};
+
+
+		public class LexState {
+			public int current;  /* current character (charint) */
+			public int linenumber;  /* input line counter */
+			public int lastline;  /* line of last token `consumed' */
+			public Token t = new Token();  /* current token */
+			public Token lookahead = new Token();  /* look ahead token */
+			public FuncState fs;  /* `FuncState' is private to the parser */
+			public LuaState L;
+			public ZIO z;  /* input stream */
+			public Mbuffer buff;  /* buffer for tokens */
+			public string source;  /* current source name */
+			public char decpoint;  /* locale decimal point */
+		};
+
+
+		public static void Next(LexState ls) { ls.current = zgetc(ls.z); }
+
+
+		public static bool CurrIsNewline(LexState ls) { return (ls.current == '\n' || ls.current == '\r'); }
+
+
+		/* ORDER RESERVED */
+		public static readonly string[] LuaXTokens = {
+			"and", "break", "do", "else", "elseif",
+			"end", "false", "for", "function", "if",
+			"in", "local", "nil", "not", "or", "repeat",
+			"return", "then", "true", "until", "while",
+			"..", "...", "==", ">=", "<=", "~=",
+			"<number>", "<name>", "<string>", "<eof>"
+		};
+
+
+		public static void SaveAndNext(LexState ls) {Save(ls, ls.current); Next(ls);}
+
+		private static void Save (LexState ls, int c) {
+		  Mbuffer b = ls.buff;
+		  if (b.n + 1 > b.buffsize) {
+			uint newsize;
+			if (b.buffsize >= MAXSIZET/2)
+			  LuaXLexError(ls, "lexical element too long", 0);
+			newsize = b.buffsize * 2;
+			luaZ_resizebuffer(ls.L, b, (int)newsize);
+		  }
+		  b.buffer[b.n++] = (char)c;
+		}
+
+		
+		public static void LuaXInit (LuaState L) {
+		  int i;
+		  for (i=0; i<NUMRESERVED; i++) {
+			TString ts = luaS_new(L, LuaXTokens[i]);
+			luaS_fix(ts);  /* reserved words are never collected */
+			LuaAssert(LuaXTokens[i].Length+1 <= TOKENLEN);
+			ts.tsv.reserved = CastByte(i+1);  /* reserved word */
+		  }
+		}
+
+
+		public const int MAXSRC          = 80;
+
+
+		public static CharPtr LuaXTokenToString (LexState ls, int token) {
+		  if (token < FIRSTRESERVED) {
+			LuaAssert(token == (byte)token);
+			return (iscntrl(token)) ? LuaOPushFString(ls.L, "char(%d)", token) :
+									  LuaOPushFString(ls.L, "%c", token);
+		  }
+		  else
+			return LuaXTokens[(int)token-FIRSTRESERVED];
+		}
+
+
+		public static CharPtr TextToken (LexState ls, int token) {
+		  switch (token) {
+			case (int)RESERVED.TK_NAME:
+			case (int)RESERVED.TK_STRING:
+			case (int)RESERVED.TK_NUMBER:
+			  Save(ls, '\0');
+			  return luaZ_buffer(ls.buff);
+			default:
+			  return LuaXTokenToString(ls, token);
+		  }
+		}
+
+		public static void LuaXLexError (LexState ls, CharPtr msg, int token) {
+		  CharPtr buff = new char[MAXSRC];
+		  LuaOChunkID(buff, GetStr(ls.source), MAXSRC);
+		  msg = LuaOPushFString(ls.L, "%s:%d: %s", buff, ls.linenumber, msg);
+		  if (token != 0)
+			LuaOPushFString(ls.L, "%s near " + LUA_QS, msg, TextToken(ls, token));
+		  LuaDThrow(ls.L, LUA_ERRSYNTAX);
+		}
+
+		public static void LuaXSyntaxError (LexState ls, CharPtr msg) {
+		  LuaXLexError(ls, msg, ls.t.token);
+		}
+
+		[CLSCompliantAttribute(false)]
+		public static TString LuaXNewString(LexState ls, CharPtr str, uint l)
+		{
+		  LuaState L = ls.L;
+		  TString ts = luaS_newlstr(L, str, l);
+		  TValue o = luaH_setstr(L, ls.fs.h, ts);  /* entry for `str' */
+		  if (TTIsNil (o)) {
+				SetBValue (o, 1);  /* make sure `str' will not be collected */
+				LuaCCheckGC(L);
+		  }
+		  return ts;
+		}
+
+
+		private static void IncLineNumber (LexState ls) {
+		  int old = ls.current;
+		  LuaAssert(CurrIsNewline(ls));
+		  Next(ls);  /* skip `\n' or `\r' */
+		  if (CurrIsNewline(ls) && ls.current != old)
+			Next(ls);  /* skip `\n\r' or `\r\n' */
+		  if (++ls.linenumber >= MAXINT)
+			LuaXSyntaxError(ls, "chunk has too many lines");
+		}
+
+
+		public static void LuaXSetInput (LuaState L, LexState ls, ZIO z, TString source) {
+		  ls.decpoint = '.';
+		  ls.L = L;
+		  ls.lookahead.token = (int)RESERVED.TK_EOS;  /* no look-ahead token */
+		  ls.z = z;
+		  ls.fs = null;
+		  ls.linenumber = 1;
+		  ls.lastline = 1;
+		  ls.source = source;
+		  luaZ_resizebuffer(ls.L, ls.buff, LUAMINBUFFER);  /* initialize buffer */
+		  Next(ls);  /* read first char */
+		}
+
+
+
+		/*
+		** =======================================================
+		** LEXICAL ANALYZER
+		** =======================================================
+		*/
+
+
+		private static int CheckNext (LexState ls, CharPtr set) {
+		  if (strchr(set, (char)ls.current) == null)
+			return 0;
+		  SaveAndNext(ls);
+		  return 1;
+		}
+
+
+		private static void BufferReplace (LexState ls, char from, char to) {
+		  uint n = luaZ_bufflen(ls.buff);
+		  CharPtr p = luaZ_buffer(ls.buff);
+		  while ((n--) != 0)
+			  if (p[n] == from) p[n] = to;
+		}
+
+
+		private static void TryDecPoint (LexState ls, SemInfo seminfo) {
+		  /* format error: try to update decimal point separator */
+			// todo: add proper support for localeconv - mjf
+			//lconv cv = localeconv();
+			char old = ls.decpoint;
+			ls.decpoint = '.'; // (cv ? cv.decimal_point[0] : '.');
+			BufferReplace(ls, old, ls.decpoint);  /* try updated decimal separator */
+			if (LuaOStr2d(luaZ_buffer(ls.buff), out seminfo.r) == 0)
+			{
+				/* format error with correct decimal point: no more options */
+				BufferReplace(ls, ls.decpoint, '.');  /* undo change (for error message) */
+				LuaXLexError(ls, "malformed number", (int)RESERVED.TK_NUMBER);
+			}
+		}
+
+
+		/* LUA_NUMBER */
+		private static void ReadNumeral (LexState ls, SemInfo seminfo) {
+		  LuaAssert(isdigit(ls.current));
+		  do {
+			SaveAndNext(ls);
+		  } while (isdigit(ls.current) || ls.current == '.');
+		  if (CheckNext(ls, "Ee") != 0)  /* `E'? */
+			CheckNext(ls, "+-");  /* optional exponent sign */
+		  while (isalnum(ls.current) || ls.current == '_')
+			SaveAndNext(ls);
+		  Save(ls, '\0');
+		  BufferReplace(ls, '.', ls.decpoint);  /* follow locale for decimal point */
+		  if (LuaOStr2d(luaZ_buffer(ls.buff), out seminfo.r) == 0)  /* format error? */
+			TryDecPoint(ls, seminfo); /* try to update decimal point separator */
+		}
+
+
+		private static int SkipSep (LexState ls) {
+		  int count = 0;
+		  int s = ls.current;
+		  LuaAssert(s == '[' || s == ']');
+		  SaveAndNext(ls);
+		  while (ls.current == '=') {
+			SaveAndNext(ls);
+			count++;
+		  }
+		  return (ls.current == s) ? count : (-count) - 1;
+		}
+
+
+		private static void ReadLongString (LexState ls, SemInfo seminfo, int sep) {
+		  //int cont = 0;
+		  //(void)(cont);  /* avoid warnings when `cont' is not used */
+		  SaveAndNext(ls);  /* skip 2nd `[' */
+		  if (CurrIsNewline(ls))  /* string starts with a newline? */
+			IncLineNumber(ls);  /* skip it */
+		  for (;;) {
+			switch (ls.current) {
+			  case EOZ:
+				LuaXLexError(ls, (seminfo != null) ? "unfinished long string" :
+										   "unfinished long comment", (int)RESERVED.TK_EOS);
+				break;  /* to avoid warnings */
+		#if LUA_COMPAT_LSTR
+			  case '[': {
+				if (skip_sep(ls) == sep) {
+				  save_and_next(ls);  /* skip 2nd `[' */
+				  cont++;
+		#if LUA_COMPAT_LSTR
+				  if (sep == 0)
+					luaX_lexerror(ls, "nesting of [[...]] is deprecated", '[');
+		#endif
+				}
+				break;
+			  }
+		#endif
+			  case ']':
+				if (SkipSep(ls) == sep)
+				{
+				  SaveAndNext(ls);  /* skip 2nd `]' */
+		//#if defined(LUA_COMPAT_LSTR) && LUA_COMPAT_LSTR == 2
+		//          cont--;
+		//          if (sep == 0 && cont >= 0) break;
+		//#endif
+				  goto endloop;
+				}
+			  break;
+			  case '\n':
+			  case '\r':
+				Save(ls, '\n');
+				IncLineNumber(ls);
+				if (seminfo == null) luaZ_resetbuffer(ls.buff);  /* avoid wasting space */
+				break;
+			  default: {
+				if (seminfo != null) SaveAndNext(ls);
+				else Next(ls);
+			  }
+			  break;
+			}
+		  } endloop:
+		  if (seminfo != null)
+		  {
+			  seminfo.ts = LuaXNewString(ls, luaZ_buffer(ls.buff) + (2 + sep), 
+											(uint)(luaZ_bufflen(ls.buff) - 2*(2 + sep)));
+		  }
+		}
+
+
+		static void ReadString (LexState ls, int del, SemInfo seminfo) {
+		  SaveAndNext(ls);
+		  while (ls.current != del) {
+			switch (ls.current) {
+			  case EOZ:
+				LuaXLexError(ls, "unfinished string", (int)RESERVED.TK_EOS);
+				continue;  /* to avoid warnings */
+			  case '\n':
+			  case '\r':
+				LuaXLexError(ls, "unfinished string", (int)RESERVED.TK_STRING);
+				continue;  /* to avoid warnings */
+			  case '\\': {
+				int c;
+				Next(ls);  /* do not save the `\' */
+				switch (ls.current) {
+				  case 'a': c = '\a'; break;
+				  case 'b': c = '\b'; break;
+				  case 'f': c = '\f'; break;
+				  case 'n': c = '\n'; break;
+				  case 'r': c = '\r'; break;
+				  case 't': c = '\t'; break;
+				  case 'v': c = '\v'; break;
+				  case '\n':  /* go through */
+				  case '\r': Save(ls, '\n'); IncLineNumber(ls); continue;
+				  case EOZ: continue;  /* will raise an error next loop */
+				  default: {
+					if (!isdigit(ls.current))
+					  SaveAndNext(ls);  /* handles \\, \", \', and \? */
+					else {  /* \xxx */
+					  int i = 0;
+					  c = 0;
+					  do {
+						c = 10*c + (ls.current-'0');
+						Next(ls);
+					  } while (++i<3 && isdigit(ls.current));
+					  if (c > System.Byte.MaxValue)
+						LuaXLexError(ls, "escape sequence too large", (int)RESERVED.TK_STRING);
+					  Save(ls, c);
+					}
+					continue;
+				  }
+				}
+				Save(ls, c);
+				Next(ls);
+				continue;
+			  }
+			  default:
+				SaveAndNext(ls);
+				break;
+			}
+		  }
+		  SaveAndNext(ls);  /* skip delimiter */
+		  seminfo.ts = LuaXNewString(ls, luaZ_buffer(ls.buff) + 1,
+		                                  luaZ_bufflen(ls.buff) - 2);
+		}
+
+
+		private static int LLex (LexState ls, SemInfo seminfo) {
+		  luaZ_resetbuffer(ls.buff);
+		  for (;;) {
+			switch (ls.current) {
+			  case '\n':
+			  case '\r': {
+				IncLineNumber(ls);
+				continue;
+			  }
+			  case '-': {
+				Next(ls);
+				if (ls.current != '-') return '-';
+				/* else is a comment */
+				Next(ls);
+				if (ls.current == '[') {
+				  int sep = SkipSep(ls);
+				  luaZ_resetbuffer(ls.buff);  /* `skip_sep' may dirty the buffer */
+				  if (sep >= 0) {
+					ReadLongString(ls, null, sep);  /* long comment */
+					luaZ_resetbuffer(ls.buff);
+					continue;
+				  }
+				}
+				/* else short comment */
+				while (!CurrIsNewline(ls) && ls.current != EOZ)
+				  Next(ls);
+				continue;
+			  }
+			  case '[': {
+				int sep = SkipSep(ls);
+				if (sep >= 0) {
+				  ReadLongString(ls, seminfo, sep);
+				  return (int)RESERVED.TK_STRING;
+				}
+				else if (sep == -1) return '[';
+				else LuaXLexError(ls, "invalid long string delimiter", (int)RESERVED.TK_STRING);
+			  }
+			  break;
+			  case '=': {
+				Next(ls);
+				if (ls.current != '=') return '=';
+				else { Next(ls); return (int)RESERVED.TK_EQ; }
+			  }
+			  case '<': {
+				Next(ls);
+				if (ls.current != '=') return '<';
+				else { Next(ls); return (int)RESERVED.TK_LE; }
+			  }
+			  case '>': {
+				Next(ls);
+				if (ls.current != '=') return '>';
+				else { Next(ls); return (int)RESERVED.TK_GE; }
+			  }
+			  case '~': {
+				Next(ls);
+				if (ls.current != '=') return '~';
+				else { Next(ls); return (int)RESERVED.TK_NE; }
+			  }
+			  case '"':
+			  case '\'': {
+				ReadString(ls, ls.current, seminfo);
+				return (int)RESERVED.TK_STRING;
+			  }
+			  case '.': {
+				SaveAndNext(ls);
+				if (CheckNext(ls, ".") != 0) {
+				  if (CheckNext(ls, ".") != 0)
+					  return (int)RESERVED.TK_DOTS;   /* ... */
+				  else return (int)RESERVED.TK_CONCAT;   /* .. */
+				}
+				else if (!isdigit(ls.current)) return '.';
+				else {
+				  ReadNumeral(ls, seminfo);
+				  return (int)RESERVED.TK_NUMBER;
+				}
+			  }
+			  case EOZ: {
+				  return (int)RESERVED.TK_EOS;
+			  }
+			  default: {
+				if (isspace(ls.current)) {
+				  LuaAssert(!CurrIsNewline(ls));
+				  Next(ls);
+				  continue;
+				}
+				else if (isdigit(ls.current)) {
+				  ReadNumeral(ls, seminfo);
+				  return (int)RESERVED.TK_NUMBER;
+				}
+				else if (isalpha(ls.current) || ls.current == '_') {
+				  /* identifier or reserved word */
+				  TString ts;
+				  do {
+					SaveAndNext(ls);
+				  } while (isalnum(ls.current) || ls.current == '_');
+				  ts = LuaXNewString(ls, luaZ_buffer(ls.buff),
+										  luaZ_bufflen(ls.buff));
+				  if (ts.tsv.reserved > 0)  /* reserved word? */
+					return ts.tsv.reserved - 1 + FIRSTRESERVED;
+				  else {
+					seminfo.ts = ts;
+					return (int)RESERVED.TK_NAME;
+				  }
+				}
+				else {
+				  int c = ls.current;
+				  Next(ls);
+				  return c;  /* single-char tokens (+ - / ...) */
+				}
+			  }
+			}
+		  }
+		}
+
+
+		public static void LuaXNext (LexState ls) {
+		  ls.lastline = ls.linenumber;
+		  if (ls.lookahead.token != (int)RESERVED.TK_EOS)
+		  {  /* is there a look-ahead token? */
+			ls.t = new Token(ls.lookahead);  /* use this one */
+			ls.lookahead.token = (int)RESERVED.TK_EOS;  /* and discharge it */
+		  }
+		  else
+			ls.t.token = LLex(ls, ls.t.seminfo);  /* read next token */
+		}
+
+
+		public static void LuaXLookAhead (LexState ls) {
+			LuaAssert(ls.lookahead.token == (int)RESERVED.TK_EOS);
+		  ls.lookahead.token = LLex(ls, ls.lookahead.seminfo);
+		}
+
+	}
+}
+
+#endif

+ 48 - 0
src/MoonSharp.Interpreter/Tree/NodeFactory.cs

@@ -231,5 +231,53 @@ namespace MoonSharp.Interpreter.Tree
 
 			return new Expression[] { CreateExpression(tree, lcontext) };
 		}
+
+		public static Expression[] CreateExpessionArray(IList<IParseTree> expressionNodes, ScriptLoadingContext lcontext)
+		{
+			List<Expression> exps = new List<Expression>();
+
+			foreach (var c in expressionNodes)
+			{
+				var e = NodeFactory.CreateExpression(c, lcontext);
+
+				if (e != null)
+					exps.Add(e);
+			}
+
+			return exps.ToArray();
+		}
+
+		public static IVariable[] CreateVariablesArray(IList<LuaParser.VarContext> expressionNodes, ScriptLoadingContext lcontext)
+		{
+			List<IVariable> exps = new List<IVariable>();
+
+			foreach (var c in expressionNodes)
+			{
+				var e = NodeFactory.CreateVariableExpression(c, lcontext) as IVariable;
+
+				if (e != null)
+					exps.Add(e);
+			}
+
+			return exps.ToArray();
+		}
+
+
+		internal static IVariable[] CreateVariablesArray(IParseTree context, ITerminalNode[] terminalNodes, ScriptLoadingContext lcontext)
+		{
+			List<IVariable> exps = new List<IVariable>();
+
+			foreach (var n in terminalNodes)
+			{
+				string name = n.GetText();
+				var localVar = lcontext.Scope.TryDefineLocal(name);
+				var symbol = new SymbolRefExpression(context, lcontext, localVar) as IVariable;
+
+				if (symbol != null)
+					exps.Add(symbol);
+			}
+
+			return exps.ToArray();
+		}
 	}
 }

+ 6 - 19
src/MoonSharp.Interpreter/Tree/Statements/AssignmentStatement.cs

@@ -18,15 +18,8 @@ namespace MoonSharp.Interpreter.Tree.Statements
 		public AssignmentStatement(LuaParser.Stat_assignmentContext context, ScriptLoadingContext lcontext)
 			: base(context, lcontext)
 		{
-			m_LValues = context.varlist().var()
-				.Select(v => NodeFactory.CreateVariableExpression(v, lcontext))
-				.Cast<IVariable>()
-				.ToArray();
-
-			m_RValues = context.explist()
-				.exp()
-				.Select(e => NodeFactory.CreateExpression(e, lcontext))
-				.ToArray();
+			m_LValues = NodeFactory.CreateVariablesArray(context.varlist().var(), lcontext);
+			m_RValues = NodeFactory.CreateExpessionArray(context.explist().exp(), lcontext);
 
 			m_Ref = BuildSourceRef(context.Start, context.Stop);
 		}
@@ -38,20 +31,14 @@ namespace MoonSharp.Interpreter.Tree.Statements
 
 			if (explist != null)
 			{
-				m_RValues = explist
-				.exp()
-				.Select(e => NodeFactory.CreateExpression(e, lcontext))
-				.ToArray();
+				m_RValues = NodeFactory.CreateExpessionArray(explist.exp(), lcontext);
 			}
 			else
+			{
 				m_RValues = new Expression[0];
+			}
 
-			m_LValues = context.namelist().NAME()
-				.Select(n => n.GetText())
-				.Select(n => lcontext.Scope.TryDefineLocal(n))
-				.Select(s => new SymbolRefExpression(context, lcontext, s))
-				.Cast<IVariable>()
-				.ToArray();
+			m_LValues = NodeFactory.CreateVariablesArray(context, context.namelist().NAME(), lcontext);
 
 			m_Ref = BuildSourceRef(context.Start, context.Stop);
 		}

+ 1 - 0
src/MoonSharp/Program.cs

@@ -42,6 +42,7 @@ namespace MoonSharp
 			else
 			{
 				Console.WriteLine("Type <enter> twice to execute code.\n");
+				Console.WriteLine("Type !help to see help.");
 
 				Script script = new Script(CoreModules.Preset_Complete);
 

+ 32 - 11
src/MoonSharpTests/Program.cs

@@ -10,23 +10,34 @@ using System.Text;
 using MoonSharp.Interpreter.Tests;
 using NUnit.Framework;
 using System.Diagnostics;
+using System.IO;
 
 namespace MoonSharpTests
 {
 	class Program
 	{
-		public const string RESTRICT_TEST = null;
+		public const string RESTRICT_TEST = "ClosureNoTable";
+
 
 		static void Main(string[] args)
 		{
-			TestRunner T = new TestRunner(Log);
+			try
+			{
+				TestRunner T = new TestRunner(Log);
+
+				//File.WriteAllText("moonsharp_tests.log", "");
 
-			T.Test(RESTRICT_TEST);
+				T.Test(RESTRICT_TEST);
 
-			if (Debugger.IsAttached)
+				if (Debugger.IsAttached)
+				{
+					Console.WriteLine("Press any key...");
+					Console.ReadKey();
+				}
+			}
+			catch (Exception ex)
 			{
-				Console.WriteLine("Press any key...");
-				Console.ReadKey();
+				Console.WriteLine(ex.ToString());
 			}
 		}
 
@@ -37,27 +48,37 @@ namespace MoonSharpTests
 				Console.ForegroundColor = ConsoleColor.Red;
 
 				if (r.Exception!= null)
-					Console.WriteLine("{0} - {1}", r.TestName, r.Exception);
+					Console_WriteLine("{0} - {1}", r.TestName, r.Exception);
 				else
-					Console.WriteLine("{0} - {1}", r.TestName, r.Message);
+					Console_WriteLine("{0} - {1}", r.TestName, r.Message);
 			}
 			else if (r.Type == TestResultType.Ok)
 			{
 				Console.ForegroundColor = ConsoleColor.DarkGreen;
-				Console.WriteLine("{0} - {1}", r.TestName, r.Message);
+				Console_WriteLine("{0} - {1}", r.TestName, r.Message);
 			}
 			else if (r.Type == TestResultType.Skipped)
 			{
 				Console.ForegroundColor = ConsoleColor.Yellow;
-				Console.WriteLine("{0} - {1}", r.TestName, r.Message);
+				Console_WriteLine("{0} - {1}", r.TestName, r.Message);
 			}
 			else
 			{
 				Console.ForegroundColor = ConsoleColor.Gray;
-				Console.WriteLine("{0}", r.Message);
+				Console_WriteLine("{0}", r.Message);
 			}
 		}
 
+		private static void Console_WriteLine(string format, params object[] args)
+		{
+			string txt = string.Format(format, args);
+
+			Console.WriteLine(txt);
+
+			//File.AppendAllText("moonsharp_tests.log", txt);
+			//File.AppendAllText("moonsharp_tests.log", "\n\n");
+		}
+
 
 	}
 }

+ 1 - 0
src/MoonSharpTests/packages.config

@@ -1,4 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
+  <package id="Antlr4.Runtime" version="4.3.0" targetFramework="net35" />
   <package id="NUnit" version="2.6.3" targetFramework="net35" />
 </packages>

BIN
src/Unity/UnityTestBed/Assets/UnityVS/Editor/UnityVS.VersionSpecific.dll


BIN
src/Unity/UnityTestBed/ProjectSettings/GraphicsSettings.asset


BIN
src/Unity/UnityTestBed/ProjectSettings/NavMeshAreas.asset


BIN
src/Unity/UnityTestBed/ProjectSettings/ProjectSettings.asset


+ 7 - 4
src/Unity/UnityTestBed/UnityVS.UnityTestBed.CSharp.csproj

@@ -16,8 +16,8 @@
     <TargetFrameworkProfile>Unity Subset v3.5</TargetFrameworkProfile>
     <CompilerResponseFile></CompilerResponseFile>
     <UnityProjectType>Game:1</UnityProjectType>
-    <UnityBuildTarget>StandaloneWindows:5</UnityBuildTarget>
-    <UnityVersion>4.5.5f1</UnityVersion>
+    <UnityBuildTarget>Android:13</UnityBuildTarget>
+    <UnityVersion>5.0.0f4</UnityVersion>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
     <DebugType>pdbonly</DebugType>
@@ -25,7 +25,7 @@
     <OutputPath>Temp\UnityVS_bin\Debug\</OutputPath>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
-    <DefineConstants>DEBUG;TRACE;UNITY_4_5_5;UNITY_4_5;UNITY_STANDALONE_WIN;ENABLE_MICROPHONE;ENABLE_TEXTUREID_MAP;ENABLE_UNITYEVENTS;ENABLE_NEW_HIERARCHY ;ENABLE_AUDIO_FMOD;UNITY_STANDALONE;ENABLE_MONO;ENABLE_TERRAIN;ENABLE_SUBSTANCE;ENABLE_GENERICS;INCLUDE_WP8SUPPORT;ENABLE_MOVIES;ENABLE_WWW;ENABLE_IMAGEEFFECTS;INCLUDE_WP_BLUE_SUPPORT;ENABLE_WEBCAM;INCLUDE_METROSUPPORT;RENDER_SOFTWARE_CURSOR;ENABLE_NETWORK;ENABLE_PHYSICS;ENABLE_CACHING;ENABLE_CLOTH;ENABLE_2D_PHYSICS;ENABLE_SHADOWS;ENABLE_AUDIO;ENABLE_NAVMESH_CARVING;ENABLE_DUCK_TYPING;ENABLE_SINGLE_INSTANCE_BUILD_SETTING;ENABLE_PROFILER;UNITY_EDITOR;UNITY_EDITOR_WIN</DefineConstants>
+    <DefineConstants>DEBUG;TRACE;UNITY_5_0_0;UNITY_5_0;UNITY_5;ENABLE_LICENSE_RENAME;ENABLE_NEW_BUGREPORTER;ENABLE_2D_PHYSICS;ENABLE_4_6_FEATURES;ENABLE_AUDIO;ENABLE_CACHING;ENABLE_CLOTH;ENABLE_FRAME_DEBUGGER;ENABLE_GENERICS;ENABLE_HOME_SCREEN;ENABLE_IMAGEEFFECTS;ENABLE_LIGHT_PROBES_LEGACY;ENABLE_MICROPHONE;ENABLE_MULTIPLE_DISPLAYS;ENABLE_NEW_HIERARCHY;ENABLE_PHYSICS;ENABLE_PHYSICS_PHYSX3;ENABLE_PLUGIN_INSPECTOR;ENABLE_SHADOWS;ENABLE_SINGLE_INSTANCE_BUILD_SETTING;ENABLE_SPRITES;ENABLE_TERRAIN;ENABLE_UNITYEVENTS;ENABLE_WEBCAM;ENABLE_WWW;ENABLE_AUDIOMIXER_SUSPEND;ENABLE_NONPRO;INCLUDE_DYNAMIC_GI;INCLUDE_GI;INCLUDE_IL2CPP;PLATFORM_SUPPORTS_MONO;RENDER_SOFTWARE_CURSOR;UNITY_ANDROID;UNITY_ANDROID_API;ENABLE_SUBSTANCE;UNITY_ANDROID_API;ENABLE_TEXTUREID_MAP;ENABLE_EGL;ENABLE_NETWORK;ENABLE_RUNTIME_GI;ENABLE_MONO;ENABLE_PROFILER;UNITY_EDITOR;UNITY_EDITOR_64;UNITY_EDITOR_WIN</DefineConstants>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
     <DebugType>pdbonly</DebugType>
@@ -33,7 +33,7 @@
     <OutputPath>Temp\UnityVS_bin\Release\</OutputPath>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
-    <DefineConstants>TRACE;UNITY_4_5_5;UNITY_4_5;UNITY_STANDALONE_WIN;ENABLE_MICROPHONE;ENABLE_TEXTUREID_MAP;ENABLE_UNITYEVENTS;ENABLE_NEW_HIERARCHY ;ENABLE_AUDIO_FMOD;UNITY_STANDALONE;ENABLE_MONO;ENABLE_TERRAIN;ENABLE_SUBSTANCE;ENABLE_GENERICS;INCLUDE_WP8SUPPORT;ENABLE_MOVIES;ENABLE_WWW;ENABLE_IMAGEEFFECTS;INCLUDE_WP_BLUE_SUPPORT;ENABLE_WEBCAM;INCLUDE_METROSUPPORT;RENDER_SOFTWARE_CURSOR;ENABLE_NETWORK;ENABLE_PHYSICS;ENABLE_CACHING;ENABLE_CLOTH;ENABLE_2D_PHYSICS;ENABLE_SHADOWS;ENABLE_AUDIO;ENABLE_NAVMESH_CARVING;ENABLE_DUCK_TYPING;ENABLE_SINGLE_INSTANCE_BUILD_SETTING;ENABLE_PROFILER;UNITY_EDITOR;UNITY_EDITOR_WIN</DefineConstants>
+    <DefineConstants>TRACE;UNITY_5_0_0;UNITY_5_0;UNITY_5;ENABLE_LICENSE_RENAME;ENABLE_NEW_BUGREPORTER;ENABLE_2D_PHYSICS;ENABLE_4_6_FEATURES;ENABLE_AUDIO;ENABLE_CACHING;ENABLE_CLOTH;ENABLE_FRAME_DEBUGGER;ENABLE_GENERICS;ENABLE_HOME_SCREEN;ENABLE_IMAGEEFFECTS;ENABLE_LIGHT_PROBES_LEGACY;ENABLE_MICROPHONE;ENABLE_MULTIPLE_DISPLAYS;ENABLE_NEW_HIERARCHY;ENABLE_PHYSICS;ENABLE_PHYSICS_PHYSX3;ENABLE_PLUGIN_INSPECTOR;ENABLE_SHADOWS;ENABLE_SINGLE_INSTANCE_BUILD_SETTING;ENABLE_SPRITES;ENABLE_TERRAIN;ENABLE_UNITYEVENTS;ENABLE_WEBCAM;ENABLE_WWW;ENABLE_AUDIOMIXER_SUSPEND;ENABLE_NONPRO;INCLUDE_DYNAMIC_GI;INCLUDE_GI;INCLUDE_IL2CPP;PLATFORM_SUPPORTS_MONO;RENDER_SOFTWARE_CURSOR;UNITY_ANDROID;UNITY_ANDROID_API;ENABLE_SUBSTANCE;UNITY_ANDROID_API;ENABLE_TEXTUREID_MAP;ENABLE_EGL;ENABLE_NETWORK;ENABLE_RUNTIME_GI;ENABLE_MONO;ENABLE_PROFILER;UNITY_EDITOR;UNITY_EDITOR_64;UNITY_EDITOR_WIN</DefineConstants>
   </PropertyGroup>
   <ItemGroup>
     <Reference Include="mscorlib" />
@@ -49,6 +49,9 @@
       <HintPath>Library\UnityAssemblies\UnityEditor.dll</HintPath>
     </Reference>
     <Reference Include="System.Xml.Linq" />
+    <Reference Include="UnityEngine.UI">
+      <HintPath>Library\UnityAssemblies\UnityEngine.UI.dll</HintPath>
+    </Reference>
     <Reference Include="Antlr4.Runtime.net35">
       <HintPath>Assets\Antlr4.Runtime.net35.dll</HintPath>
     </Reference>