Browse Source

Added some preliminary tests

Bepsi 6 years ago
parent
commit
798f6d7294

+ 3 - 8
NTERA.Interpreter.Tests/Common.cs

@@ -1,10 +1,5 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using NTERA.Core;
+using NTERA.Core;
+using NUnit.Framework;
 
 namespace NTERA.Interpreter.Tests
 {
@@ -15,7 +10,7 @@ namespace NTERA.Interpreter.Tests
             Assert.AreEqual(value, interpreter.Variables["LOCAL"].Real);
         }
 
-        public static Interpreter Create(string code, IConsole console = null)
+        public static Interpreter Run(string code, IConsole console = null)
         {
             Interpreter interpreter = new Interpreter(console, code);
 

+ 116 - 4
NTERA.Interpreter.Tests/KeywordTests.cs

@@ -1,17 +1,129 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+using NTERA.Core;
+using NUnit.Framework;
 
 namespace NTERA.Interpreter.Tests
 {
-    [TestClass]
+    [TestFixture]
     public class KeywordTests
     {
         #region Console
 
+        [TestCase("PRINTV")]
+        public void WriteV(string keyword)
+        {
+            var console = new Mock<IConsole>();
+
+            string code = $@"{keyword} 69";
+
+            Common.Run(code, console.Object);
+
+            console.Verify(x => x.Write("69"), Moq.Times.Once);
+        }
+        
+        [TestCase("PRINTFORM", "%LOCALS%")]
+        public void WriteS(string keyword, string operand)
+        {
+            var console = new Mock<IConsole>();
+
+            string code =
+                $@"LOCALS = big guy 4 u
+                  {keyword} {operand}";
+
+            Common.Run(code, console.Object);
+
+            console.Verify(x => x.Write("big guy 4 u"), Moq.Times.Once);
+        }
+
+        [TestCase("PRINT", "{LOCALS}", "{LOCALS}")]
+        [TestCase("PRINTFORMS", "\"TEXT\"", "TEXT")]
+        public void WriteLiteral(string keyword, string input, string output)
+        {
+            var console = new Mock<IConsole>();
+
+            string code = $"{keyword} {input}";
+
+            Common.Run(code, console.Object);
+            
+            console.Verify(x => x.Write(output), Moq.Times.Once);
+        }
+        
+        [TestCase("PRINTFORML", "{LOCALS}")]
+        public void Print(string keyword, string operand)
+        {
+            var console = new Mock<IConsole>();
+
+            string code =
+                $@"LOCALS = big guy 4 u
+                  {keyword} {operand}";
+
+            Common.Run(code, console.Object);
+
+            console.Verify(x => x.Write("big guy 4 u"), Moq.Times.Once);
+            console.Verify(x => x.NewLine(), Moq.Times.Once);
+        }
+
+        [Test]
+        public void PrintImg()
+        {
+            var console = new Mock<IConsole>();
+
+            Common.Run("PRINT_IMG image", console.Object);
+
+            console.Verify(x => x.PrintImg("image"), Moq.Times.Once);
+        }
+
+        [Test]
+        public void PrintButton()
+        {
+            var console = new Mock<IConsole>();
+
+            Common.Run("PRINTBUTTON \"button to press\", 50", console.Object);
+
+            console.Verify(x => x.PrintButton("button to press", 50), Moq.Times.Once);
+        }
+        
+        public void ClearLine(string keyword, string operand)
+        {
+            var console = new Mock<IConsole>();
+
+            string code =
+                  @"PRINT something
+                    CLEARLINE 1";
+
+            Common.Run(code, console.Object);
+
+            console.Verify(x => x.Write("something"), Moq.Times.Once);
+            console.Verify(x => x.deleteLine(1), Moq.Times.Once);
+        }
+
+        [Test]
+        public void DrawLine()
+        {
+            var console = new Mock<IConsole>();
+
+            Common.Run("DRAWLINE", console.Object);
+
+            console.Verify(x => x.PrintBar(), Moq.Times.Once);
+        }
+
+        [TestCase("-")]
+        [TestCase("=")]
+        [TestCase("━")]
+        public void DrawLineCustom(string character)
+        {
+            var console = new Mock<IConsole>();
+
+            Common.Run($"CUSTOMDRAWLINE {character}", console.Object);
+
+            console.Verify(x => x.printCustomBar(character), Moq.Times.Once);
+        }
+
         #endregion
 
         #region Arithmetic
 
-        [TestMethod]
+        [Test]
 
         public void Times()
         {
@@ -19,7 +131,7 @@ namespace NTERA.Interpreter.Tests
                 @"LOCAL = 5
                   TIMES LOCAL, 1.5";
 
-            var interpreter = Common.Create(code);
+            var interpreter = Common.Run(code);
 
             Common.AssertLocal(interpreter, 7.5);
         }

+ 24 - 8
NTERA.Interpreter.Tests/NTERA.Interpreter.Tests.csproj

@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <Import Project="..\packages\MSTest.TestAdapter.1.2.1\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.1.2.1\build\net45\MSTest.TestAdapter.props')" />
+  <Import Project="..\packages\NUnit.3.10.1\build\NUnit.props" Condition="Exists('..\packages\NUnit.3.10.1\build\NUnit.props')" />
+  <Import Project="..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props')" />
   <PropertyGroup>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
@@ -39,13 +40,26 @@
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
-      <HintPath>..\packages\MSTest.TestFramework.1.2.1\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
+    <Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
+      <HintPath>..\packages\Castle.Core.4.3.1\lib\net45\Castle.Core.dll</HintPath>
     </Reference>
-    <Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
-      <HintPath>..\packages\MSTest.TestFramework.1.2.1\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
+    <Reference Include="Moq, Version=4.9.0.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
+      <HintPath>..\packages\Moq.4.9.0\lib\net45\Moq.dll</HintPath>
+    </Reference>
+    <Reference Include="nunit.framework, Version=3.10.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
+      <HintPath>..\packages\NUnit.3.10.1\lib\net45\nunit.framework.dll</HintPath>
     </Reference>
     <Reference Include="System" />
+    <Reference Include="System.Configuration" />
+    <Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.1\lib\netstandard1.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.1\lib\portable-net45+win8+wp8+wpa81\System.Threading.Tasks.Extensions.dll</HintPath>
+    </Reference>
+    <Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.ValueTuple.4.5.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
+    </Reference>
   </ItemGroup>
   <Choose>
     <When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
@@ -61,6 +75,7 @@
     <Compile Include="Properties\AssemblyInfo.cs" />
   </ItemGroup>
   <ItemGroup>
+    <None Include="app.config" />
     <None Include="packages.config" />
   </ItemGroup>
   <ItemGroup>
@@ -97,10 +112,11 @@
     <PropertyGroup>
       <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
     </PropertyGroup>
-    <Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.2.1\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.2.1\build\net45\MSTest.TestAdapter.props'))" />
-    <Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.2.1\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.2.1\build\net45\MSTest.TestAdapter.targets'))" />
+    <Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props'))" />
+    <Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets'))" />
+    <Error Condition="!Exists('..\packages\NUnit.3.10.1\build\NUnit.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NUnit.3.10.1\build\NUnit.props'))" />
   </Target>
-  <Import Project="..\packages\MSTest.TestAdapter.1.2.1\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.1.2.1\build\net45\MSTest.TestAdapter.targets')" />
+  <Import Project="..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets')" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.
   <Target Name="BeforeBuild">

+ 8 - 2
NTERA.Interpreter.Tests/packages.config

@@ -1,5 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="MSTest.TestAdapter" version="1.2.1" targetFramework="net452" />
-  <package id="MSTest.TestFramework" version="1.2.1" targetFramework="net452" />
+  <package id="Castle.Core" version="4.3.1" targetFramework="net452" />
+  <package id="Moq" version="4.9.0" targetFramework="net452" />
+  <package id="MSTest.TestAdapter" version="1.3.2" targetFramework="net452" />
+  <package id="MSTest.TestFramework" version="1.3.2" targetFramework="net452" />
+  <package id="NUnit" version="3.10.1" targetFramework="net452" />
+  <package id="System.Runtime.CompilerServices.Unsafe" version="4.5.1" targetFramework="net452" />
+  <package id="System.Threading.Tasks.Extensions" version="4.5.1" targetFramework="net452" />
+  <package id="System.ValueTuple" version="4.5.0" targetFramework="net452" />
 </packages>

+ 80 - 64
NTERA.Interpreter/Interpreter.cs

@@ -6,12 +6,12 @@ namespace NTERA.Interpreter
 {
     public partial class Interpreter
     {
-        private Lexer lex;
+        protected Lexer Lexer { get; set; }
         private Token prevToken;
         private Token lastToken;
 
-        public Dictionary<string, Value> Variables { get; } = new Dictionary<string, Value>();
-        private Dictionary<string, Marker> loops;
+        public VariableDictionary Variables { get; } = new VariableDictionary();
+        protected Dictionary<string, Marker> Loops { get; } = new Dictionary<string, Marker>();
 
         public delegate Value BasicFunction(List<Value> args);
 
@@ -26,9 +26,7 @@ namespace NTERA.Interpreter
         public Interpreter(IConsole console, string input)
         {
             this.console = console;
-            lex = new Lexer(input);
-            Variables = new Dictionary<string, Value>();
-            loops = new Dictionary<string, Marker>();
+            Lexer = new Lexer(input);
             ifcounter = 0;
 
             GenerateKeywordDictionary();
@@ -40,7 +38,7 @@ namespace NTERA.Interpreter
             throw new Exception(text + " at line: " + lineMarker.Line);
         }
 
-        protected bool TryAssertNextToken(Token tok, bool pullNext = true)
+        protected bool TryAssertToken(Token tok, bool pullNext = true)
         {
             if (pullNext)
                 GetNextToken();
@@ -50,7 +48,7 @@ namespace NTERA.Interpreter
 
         protected void AssertToken(Token tok, bool pullNext = true)
         {
-            if (!TryAssertNextToken(tok, pullNext))
+            if (!TryAssertToken(tok, pullNext))
                 Error("Expect " + tok + " got " + lastToken);
         }
 
@@ -60,7 +58,7 @@ namespace NTERA.Interpreter
         {
             exit = false;
 
-            tokens = lex.GetTokens().GetEnumerator();
+            tokens = Lexer.GetTokens().GetEnumerator();
 
             GetNextToken();
 
@@ -83,7 +81,8 @@ namespace NTERA.Interpreter
 
         private void Line()
         {
-            while (lastToken == Token.NewLine) GetNextToken();
+            while (lastToken == Token.NewLine)
+                GetNextToken();
 
             if (lastToken == Token.EOF)
             {
@@ -91,7 +90,7 @@ namespace NTERA.Interpreter
                 return;
             }
 
-            lineMarker = lex.TokenMarker;
+            lineMarker = Lexer.TokenMarker;
             Statement();
 
             if (lastToken != Token.NewLine && lastToken != Token.EOF)
@@ -100,44 +99,53 @@ namespace NTERA.Interpreter
 
         private void Statement()
         {
-            Token keyword = lastToken;
-            GetNextToken();
-
-            if (KeywordMethods.ContainsKey(keyword))
-                KeywordMethods[keyword]();
-            else
+            while (true)
             {
-                switch (keyword)
+                Token keyword = lastToken;
+                GetNextToken();
+
+                if (KeywordMethods.ContainsKey(keyword))
+                    KeywordMethods[keyword]();
+                else
                 {
-                    case Token.Input: Input(); break;
-
-                    case Token.Function:
-                    case Token.Global:
-                    case Token.Dim:
-                    case Token.Const:
-                        while (GetNextToken() != Token.NewLine) { }
-                        break;
-
-                    case Token.Identifer:
-                        if (lastToken == Token.Equal) Let();
-                        else goto default;
-                        break;
-                    case Token.EOF:
-                        exit = true;
-                        break;
-                    default:
-                        Error("Expect keyword got " + keyword);
-                        break;
+                    switch (keyword)
+                    {
+                        case Token.Input:
+                            Input();
+                            break;
+
+                        case Token.Function:
+                        case Token.Global:
+                        case Token.Dim:
+                        case Token.Const:
+                            while (GetNextToken() != Token.NewLine) { }
+                            break;
+
+                        case Token.Identifer:
+                            if (lastToken == Token.Equal)
+                                Let();
+                            else
+                                Error("Expected assignment got " + lastToken);
+                            break;
+                        case Token.EOF:
+                            exit = true;
+                            break;
+                        default:
+                            Error("Expect keyword got " + keyword);
+                            break;
+                    }
                 }
-            }
 
-            if (lastToken == Token.Colon)
-            {
-                GetNextToken();
-                Statement();
+                if (lastToken == Token.Colon)
+                {
+                    GetNextToken();
+                    continue;
+                }
+
+                break;
             }
         }
-        
+
         public Dictionary<string, BasicFunction> FunctionDictionary { get; protected set; }
 
         public Dictionary<Token, Action> KeywordMethods { get; set; }
@@ -149,17 +157,17 @@ namespace NTERA.Interpreter
             {
                 AssertToken(Token.Identifer);
                
-                if (!Variables.ContainsKey(lex.Identifer)) Variables.Add(lex.Identifer, new Value());
+                if (!Variables.ContainsKey(Lexer.Identifer)) Variables.Add(Lexer.Identifer, new Value());
 
                 console.WaitInput(new Core.Interop.InputRequest() { InputType = Core.Interop.InputType.StrValue });
                 string input = console.LastInput;
                 double d;
                 if (double.TryParse(input, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out d))
-                    Variables[lex.Identifer] = new Value(d);
+                    Variables[Lexer.Identifer] = new Value(d);
                 else
-                    Variables[lex.Identifer] = new Value(input);
+                    Variables[Lexer.Identifer] = new Value(input);
                 
-                if (!TryAssertNextToken(Token.Comma))
+                if (!TryAssertToken(Token.Comma))
                     break;
 
                 GetNextToken();
@@ -176,34 +184,42 @@ namespace NTERA.Interpreter
             { Token.Caret, 4 }
         };
 
-        private Value Expr()
+        private Value RealExpression()
         {
             Stack<Value> stack = new Stack<Value>();
             Stack<Token> operators = new Stack<Token>();
 
+            void Operation(Token token)
+            {
+                Value b = stack.Pop();
+                Value a = stack.Pop();
+                Value result = a.Operate(b, token);
+                stack.Push(result);
+            }
+
             int i = 0;
             while (true)
             {
                 if (lastToken == Token.Value)
                 {
-                    stack.Push(lex.Value);
+                    stack.Push(Lexer.Value);
                 }
                 else if (lastToken == Token.Identifer)
                 {
-                    if (Variables.ContainsKey(lex.Identifer))
+                    if (Variables.ContainsKey(Lexer.Identifer))
                     {
-                        stack.Push(Variables[lex.Identifer]);
+                        stack.Push(Variables[Lexer.Identifer]);
                     }
-                    else if (FunctionDictionary.ContainsKey(lex.Identifer))
+                    else if (FunctionDictionary.ContainsKey(Lexer.Identifer))
                     {
-                        string name = lex.Identifer;
+                        string name = Lexer.Identifer;
                         List<Value> args = new List<Value>();
                         GetNextToken();
                         AssertToken(Token.LParen, false);
                         
-                        while (!TryAssertNextToken(Token.RParen))
+                        while (!TryAssertToken(Token.RParen))
                         {
-                            args.Add(Expr());
+                            args.Add(RealExpression());
                             if (lastToken != Token.Comma)
                                 break;
                         }
@@ -212,13 +228,13 @@ namespace NTERA.Interpreter
                     }
                     else
                     {
-                        Error("Undeclared variable " + lex.Identifer);
+                        Error("Undeclared variable " + Lexer.Identifer);
                     }
                 }
                 else if (lastToken == Token.LParen)
                 {
                     GetNextToken();
-                    stack.Push(Expr());
+                    stack.Push(RealExpression());
                     AssertToken(Token.RParen, false);
                 }
                 else if (lastToken.IsArithmetic())
@@ -231,7 +247,7 @@ namespace NTERA.Interpreter
                     else
                     {
                         while (operators.Count > 0 && OrderOfOps[lastToken] <= OrderOfOps[operators.Peek()])
-                            Operation(stack, operators.Pop());
+                            Operation(operators.Pop());
                         operators.Push(lastToken);
                     }
                 }
@@ -247,17 +263,17 @@ namespace NTERA.Interpreter
             }
 
             while (operators.Count > 0)
-                Operation(stack, operators.Pop());
+                Operation(operators.Pop());
 
             return stack.Pop();
         }
 
-        private void Operation(Stack<Value> stack, Token token)
+        private Value StringExpression()
         {
-            Value b = stack.Pop();
-            Value a = stack.Pop();
-            Value result = a.Operate(b, token);
-            stack.Push(result);
+            foreach (var t in Lexer.ReturnAsLine(Token.Unknown)) { }
+
+            lastToken = Token.NewLine;
+            return Lexer.Value;
         }
     }
 }

+ 96 - 22
NTERA.Interpreter/Interpreter/Keywords.cs

@@ -1,8 +1,11 @@
 using System;
 using System.Collections.Generic;
+using System.Drawing;
+using System.Globalization;
 using System.Linq;
 using System.Reflection;
 using System.Text.RegularExpressions;
+using NTERA.Core.Interop;
 
 namespace NTERA.Interpreter
 {
@@ -23,31 +26,39 @@ namespace NTERA.Interpreter
             }
         }
 
+        #region Printing
 
         [KeywordMethod(Token.Print)]
         private void Print()
         {
-            console.Write(Expr().ToString());
+            console.Write(RealExpression().ToString());
         }
 
         [KeywordMethod(Token.PrintL)]
         private void PrintL()
         {
-            console.PrintSingleLine(Expr().ToString());
+            console.PrintSingleLine(RealExpression().ToString());
+        }
+
+
+        [KeywordMethod(Token.PrintHtml)]
+        private void PrintHtml()
+        {
+            console.PrintHtml(RealExpression().ToString().Trim().Trim('"'), true);
         }
 
 
         [KeywordMethod(Token.PrintImg)]
         private void PrintImg()
         {
-            console.PrintImg(Expr().ToString().Trim().Trim('"'));
+            console.PrintImg(RealExpression().ToString().Trim().Trim('"'));
         }
 
 
         [KeywordMethod(Token.PrintButton)]
         private void PrintButton()
         {
-            console.PrintButton(Expr().ToString(), 0);
+            console.PrintButton(RealExpression().ToString(), 0);
         }
 
         private static readonly Regex FormRegex = new Regex("{(.*?)}");
@@ -55,7 +66,7 @@ namespace NTERA.Interpreter
         [KeywordMethod(Token.PrintFormL)]
         private void PrintFormL()
         {
-            string rawString = Expr().ToString();
+            string rawString = RealExpression().ToString();
 
             var evaluator = new MatchEvaluator(match => Variables[match.Groups[1].Value].ToString());
 
@@ -71,13 +82,67 @@ namespace NTERA.Interpreter
         [KeywordMethod(Token.DrawLineForm)]
         private void DrawLineForm()
         {
-            console.printCustomBar(Expr().ToString().Trim());
+            console.printCustomBar(RealExpression().ToString().Trim());
+        }
+
+        [KeywordMethod(Token.Alignment)]
+        private void Alignment()
+        {
+            AssertToken(Token.Value, false);
+
+            console.Alignment = (DisplayLineAlignment)Enum.Parse(typeof(DisplayLineAlignment), Lexer.Value);
+
+            GetNextToken();
+        }
+
+        [KeywordMethod(Token.SetColor)]
+        private void SetColor()
+        {
+            if (TryAssertToken(Token.Identifer, false))
+            {
+                console.SetStringStyle(Color.FromName(Lexer.Value));
+            }
+            else
+            {
+                AssertToken(Token.Value, false);
+
+                if (TryAssertToken(Token.Comma))
+                {
+                    int r = (int)Lexer.Value.Real;
+
+                    AssertToken(Token.Value);
+
+                    int g = (int)Lexer.Value;
+
+                    AssertToken(Token.Comma);
+                    AssertToken(Token.Value);
+
+                    int b = (int)Lexer.Value;
+
+                    console.SetStringStyle(Color.FromArgb(r, g, b));
+                }
+                else
+                {
+                    int hexResult = int.Parse(Lexer.Identifer.Substring(1), NumberStyles.HexNumber);
+
+                    int argb = (int)(hexResult | 0xFF000000);
+                    Color c = Color.FromArgb(argb);
+
+                    console.SetStringStyle(c);
+                }
+            }
+
+            GetNextToken();
         }
 
+        #endregion
+
+        #region Control
+
         [KeywordMethod(Token.If)]
         private void If()
         {
-            bool result = Expr().Real == 1;
+            bool result = RealExpression().Real == 1;
 
             AssertToken(Token.Then, false);
             GetNextToken();
@@ -157,38 +222,45 @@ namespace NTERA.Interpreter
                 AssertToken(Token.Equal);
             }
 
-            string id = lex.Identifer;
+            string id = Lexer.Identifer;
 
-            GetNextToken();
 
-            Variables[id] = Expr();
+            var typeHint = Variables[id].Type;
+
+            if (typeHint == ValueType.Real)
+            {
+                GetNextToken();
+                Variables[id] = RealExpression();
+            }
+            else
+                Variables[id] = StringExpression();
         }
 
         [KeywordMethod(Token.For)]
         private void For()
         {
             AssertToken(Token.Identifer, false);
-            string var = lex.Identifer;
+            string var = Lexer.Identifer;
 
             AssertToken(Token.Equal);
 
             GetNextToken();
-            Value v = Expr();
+            Value v = RealExpression();
 
-            if (loops.ContainsKey(var))
+            if (Loops.ContainsKey(var))
             {
-                loops[var] = lineMarker;
+                Loops[var] = lineMarker;
             }
             else
             {
                 Variables[var] = v;
-                loops.Add(var, lineMarker);
+                Loops.Add(var, lineMarker);
             }
 
             AssertToken(Token.To, false);
 
             GetNextToken();
-            v = Expr();
+            v = RealExpression();
 
             if (Variables[var].Operate(v, Token.More).Real == 1)
             {
@@ -196,9 +268,9 @@ namespace NTERA.Interpreter
                 {
                     while (!(GetNextToken() == Token.Identifer && prevToken == Token.Next)) { }
 
-                    if (lex.Identifer == var)
+                    if (Lexer.Identifer == var)
                     {
-                        loops.Remove(var);
+                        Loops.Remove(var);
                         AssertToken(Token.NewLine);
                         break;
                     }
@@ -210,9 +282,9 @@ namespace NTERA.Interpreter
         private void Next()
         {
             AssertToken(Token.Identifer, false);
-            string var = lex.Identifer;
+            string var = Lexer.Identifer;
             Variables[var] = Variables[var].Operate(new Value(1), Token.Plus);
-            lex.GoTo(new Marker(loops[var].Pointer - 1, loops[var].Line, loops[var].Column - 1));
+            Lexer.GoTo(new Marker(Loops[var].Pointer - 1, Loops[var].Line, Loops[var].Column - 1));
             lastToken = Token.NewLine;
         }
 
@@ -221,14 +293,16 @@ namespace NTERA.Interpreter
         {
             AssertToken(Token.Identifer, false);
 
-            string var = lex.Identifer;
+            string var = Lexer.Identifer;
 
             AssertToken(Token.Comma);
 
             GetNextToken();
-            var arg2 = Expr();
+            var arg2 = RealExpression();
 
             Variables[var] = Variables[var].Operate(arg2, Token.Asterisk);
         }
+        
+        #endregion
     }
 }

+ 21 - 0
NTERA.Interpreter/Interpreter/VariableDictionary.cs

@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+
+namespace NTERA.Interpreter
+{
+    public class VariableDictionary : Dictionary<string, Value>
+    {
+        public VariableDictionary() : base(StringComparer.InvariantCultureIgnoreCase)
+        {
+            InitDefaults();
+        }
+
+        private void InitDefaults()
+        {
+            Add("LOCAL", 0);
+            Add("LOCALS", "");
+            Add("RESULT", 0);
+            Add("RESULTS", "");
+        }
+    }
+}

+ 6 - 4
NTERA.Interpreter/Lexer.cs

@@ -192,7 +192,7 @@ namespace NTERA.Interpreter
                         yield return Token.EOF;
                         break;
                     default:
-                        yield return Token.Unkown;
+                        yield return Token.Unknown;
                         continue;
                 }
 
@@ -204,15 +204,17 @@ namespace NTERA.Interpreter
         {
             StringBuilder bodyBuilder = new StringBuilder();
 
-            while (lastChar != '\n')
+            while (lastChar != '\n' && lastChar != '\0')
                 bodyBuilder.Append(GetChar());
+            
+            bodyBuilder.Capacity -= 1;
 
             yield return token;
 
-            Value = new Value(bodyBuilder.ToString().TrimEnd());
+            Value = new Value(bodyBuilder.ToString().TrimEnd('\0', '\r', '\n'));
             yield return Token.Value;
 
-            yield return Token.NewLine;
+            yield return lastChar == '\0' ? Token.EOF : Token.NewLine;
         }
     }
 }

+ 1 - 0
NTERA.Interpreter/NTERA.Interpreter.csproj

@@ -49,6 +49,7 @@
     <Compile Include="Token.cs" />
     <Compile Include="Utility.cs" />
     <Compile Include="Value.cs" />
+    <Compile Include="Interpreter\VariableDictionary.cs" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\NTERA.Core\NTERA.Core.csproj">

+ 7 - 1
NTERA.Interpreter/Token.cs

@@ -3,7 +3,7 @@ namespace NTERA.Interpreter
 {
     public enum Token
     {
-        Unkown,
+        Unknown,
 
         Identifer,
         Value,
@@ -31,10 +31,16 @@ namespace NTERA.Interpreter
         PrintForm,
         [LexerKeyword("PRINTFORML", true)]
         PrintFormL,
+        [LexerKeyword("HTML_PRINT", false)]
+        PrintHtml,
         [LexerKeyword("PRINT_IMG", true)]
         PrintImg,
         [LexerKeyword("PRINTBUTTON", true)]
         PrintButton,
+        [LexerKeyword("ALIGNMENT", true)]
+        Alignment,
+        [LexerKeyword("SETCOLOR", false)]
+        SetColor,
 
         //Eralang arithmetic keywords
         [LexerKeyword("TIMES")]

+ 1 - 1
NTERA/formMain.cs

@@ -9,7 +9,7 @@ namespace NTERA
 {
 	public partial class formMain : Form
 	{
-		private GameInstance instance = new GameInstance();
+		private readonly GameInstance instance = new GameInstance();
 
 		public formMain()
 		{