Browse Source

Add a barebones BASIC interpreter as a separate project

Bepis 6 years ago
parent
commit
f8aa42f42e

+ 39 - 0
NTERA.Interpreter/Engine.cs

@@ -0,0 +1,39 @@
+using System;
+using NTERA.Core;
+using NTERA.EmuEra.Game.EraEmu.Content;
+
+namespace NTERA.Interpreter
+{
+	public class Engine : IScriptEngine
+	{
+		public bool Initialize(IConsole console)
+		{
+			throw new NotImplementedException();
+		}
+
+		public void Start()
+		{
+			throw new NotImplementedException();
+		}
+
+		public void InputString(string input)
+		{
+			throw new NotImplementedException();
+		}
+
+		public void InputInteger(long input)
+		{
+			throw new NotImplementedException();
+		}
+
+		public void InputSystemInteger(long input)
+		{
+			throw new NotImplementedException();
+		}
+
+		public CroppedImage GetImage(string name)
+		{
+			throw new NotImplementedException();
+		}
+	}
+}

+ 66 - 0
NTERA.Interpreter/Functions/BuiltInFunctions.cs

@@ -0,0 +1,66 @@
+using System;
+using System.Collections.Generic;
+
+namespace NTERA.Interpreter.Functions
+{
+    class BuiltInFunctions
+    {
+        public static void InstallAll(Interpreter interpreter)
+        {
+            interpreter.AddFunction("str", Str);
+            interpreter.AddFunction("num", Num);
+            interpreter.AddFunction("abs", Abs);
+            interpreter.AddFunction("min", Min);
+            interpreter.AddFunction("max", Max);
+            interpreter.AddFunction("not", Not);
+        }
+
+        public static Value Str(Interpreter interpreter, List<Value> args)
+        {
+            if (args.Count < 1)
+                throw new ArgumentException();
+
+            return args[0].Convert(ValueType.String);
+        }
+
+        public static Value Num(Interpreter interpreter, List<Value> args)
+        {
+            if (args.Count < 1)
+                throw new ArgumentException();
+
+            return args[0].Convert(ValueType.Real);
+        }
+
+        public static Value Abs(Interpreter interpreter, List<Value> args)
+        {
+            if (args.Count < 1)
+                throw new ArgumentException();
+
+            return new Value(Math.Abs(args[0].Real));
+        }
+
+        public static Value Min(Interpreter interpreter, List<Value> args)
+        {
+            if (args.Count < 2)
+                throw new ArgumentException();
+
+            return new Value(Math.Min(args[0].Real, args[1].Real));
+        }
+
+        public static Value Max(Interpreter interpreter, List<Value> args)
+        {
+            if (args.Count < 1)
+                throw new ArgumentException();
+
+            return new Value(Math.Max(args[0].Real, args[1].Real));
+        }
+
+        public static Value Not(Interpreter interpreter, List<Value> args)
+        {
+            if (args.Count < 1)
+                throw new ArgumentException();
+
+            return new Value(args[0].Real == 0 ? 1 : 0);
+        }
+    }
+}

+ 263 - 0
NTERA.Interpreter/Interpreter.cs

@@ -0,0 +1,263 @@
+using System;
+using System.Collections.Generic;
+using NTERA.Interpreter.Functions;
+
+namespace NTERA.Interpreter
+{
+    public partial class Interpreter
+    {
+        public bool HasPrint { get; set; } = true;
+        public bool HasInput { get; set; } = true;
+
+        private Lexer lex;
+        private Token prevToken;
+        private Token lastToken;
+
+        private Dictionary<string, Value> vars;
+        private Dictionary<string, Marker> loops;
+
+        public delegate Value BasicFunction(Interpreter interpreter, List<Value> args);
+        private Dictionary<string, BasicFunction> funcs;
+
+        private int ifcounter;
+
+        private Marker lineMarker;
+
+        private bool exit;
+
+        public Interpreter(string input)
+        {
+            lex = new Lexer(input);
+            vars = new Dictionary<string, Value>();
+            loops = new Dictionary<string, Marker>();
+            funcs = new Dictionary<string, BasicFunction>();
+            ifcounter = 0;
+            BuiltInFunctions.InstallAll(this);
+        }
+
+        public Value GetVar(string name)
+        {
+            if (!vars.ContainsKey(name))
+                throw new Exception("Variable with name " + name + " does not exist.");
+            return vars[name];
+        }
+
+        public void SetVar(string name, Value val)
+        {
+            if (!vars.ContainsKey(name)) vars.Add(name, val);
+            else vars[name] = val;
+        }
+
+        public void AddFunction(string name, BasicFunction function)
+        {
+            if (!funcs.ContainsKey(name)) funcs.Add(name, function);
+            else funcs[name] = function;
+        }
+
+        void Error(string text)
+        {
+            throw new Exception(text + " at line: " + lineMarker.Line);
+        }
+
+        void Match(Token tok)
+        {
+            if (lastToken != tok)
+                Error("Expect " + tok + " got " + lastToken);
+        }
+
+        public void Exec()
+        {
+            exit = false;
+            GetNextToken();
+            while (!exit) Line();
+        }
+
+        Token GetNextToken()
+        {
+            prevToken = lastToken;
+            lastToken = lex.GetToken();
+
+            if (lastToken == Token.EOF && prevToken == Token.EOF)
+                Error("Unexpected end of file");
+            
+            return lastToken;
+        }
+
+        void Line()
+        {
+            while (lastToken == Token.NewLine) GetNextToken();
+
+            if (lastToken == Token.EOF)
+            {
+                exit = true;
+                return;
+            }
+
+            lineMarker = lex.TokenMarker;
+            Statment();
+
+            if (lastToken != Token.NewLine && lastToken != Token.EOF)
+                Error("Expect new line got " + lastToken);
+        }
+
+        void Statment()
+        {
+            Token keyword = lastToken;
+            GetNextToken();
+            switch (keyword)
+            {
+                case Token.Print: Print(); break;
+                case Token.Input: Input(); break;
+                case Token.If: If(); break;
+                case Token.Else: Else(); break;
+                case Token.EndIf: break;
+                case Token.For: For(); break;
+                case Token.Next: Next(); break;
+                case Token.Let: Let(); break;
+                case Token.End: End(); 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;
+            }
+            if (lastToken == Token.Colon)
+            {
+                GetNextToken();
+                Statment();
+            }
+        }
+
+        void Print()
+        {
+            if (!HasPrint)
+                Error("Print command not allowed");
+
+            Console.WriteLine(Expr().ToString());
+        }
+
+        void Input()
+        {
+            if (!HasInput)
+                Error("Input command not allowed");
+
+            while (true)
+            {
+                Match(Token.Identifer);
+               
+                if (!vars.ContainsKey(lex.Identifer)) vars.Add(lex.Identifer, new Value());
+                
+                string input = Console.ReadLine();
+                double d;
+                if (double.TryParse(input, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out d))
+                    vars[lex.Identifer] = new Value(d);
+                else
+                    vars[lex.Identifer] = new Value(input);
+                
+                GetNextToken();
+                if (lastToken != Token.Comma) break;
+                GetNextToken();
+            }
+        }
+
+        Value Expr()
+        {
+            Dictionary<Token, int> prec = new Dictionary<Token, int>() 
+            {
+                { Token.Or, 0 }, { Token.And, 0 },
+                { Token.Equal, 1 }, { Token.NotEqual, 1 },       
+                { Token.Less, 1 }, { Token.More, 1 }, { Token.LessEqual, 1 },  { Token.MoreEqual, 1 },
+                { Token.Plus, 2 }, { Token.Minus, 2 },
+                { Token.Asterisk, 3 }, {Token.Slash, 3 },
+                { Token.Caret, 4 }
+            };
+
+            Stack<Value> stack = new Stack<Value>();
+            Stack<Token> operators = new Stack<Token>();
+
+            int i = 0;
+            while (true)
+            {
+                if (lastToken == Token.Value)
+                {
+                    stack.Push(lex.Value);
+                }
+                else if (lastToken == Token.Identifer)
+                {
+                    if (vars.ContainsKey(lex.Identifer))
+                    {
+                        stack.Push(vars[lex.Identifer]);
+                    }
+                    else if (funcs.ContainsKey(lex.Identifer))
+                    {
+                        string name = lex.Identifer;
+                        List<Value> args = new List<Value>();
+                        GetNextToken();
+                        Match(Token.LParen);
+
+                        start:
+                        if (GetNextToken() != Token.RParen)
+                        {
+                            args.Add(Expr());
+                            if (lastToken == Token.Comma)
+                                goto start;
+                        }
+
+                        stack.Push(funcs[name](null, args));
+                    }
+                    else
+                    {
+                        Error("Undeclared variable " + lex.Identifer);
+                    }
+                }
+                else if (lastToken == Token.LParen)
+                {
+                    GetNextToken();
+                    stack.Push(Expr());
+                    Match(Token.RParen);
+                }
+                else if (lastToken >= Token.Plus && lastToken <= Token.Not)
+                {
+                    if ((lastToken == Token.Minus || lastToken == Token.Minus) && (i == 0 || prevToken == Token.LParen))
+                    {
+                        stack.Push(new Value(0));
+                        operators.Push(lastToken);
+                    }
+                    else
+                    {
+                        while (operators.Count > 0 && prec[lastToken] <= prec[operators.Peek()])
+                            Operation(ref stack, operators.Pop());
+                        operators.Push(lastToken);
+                    }
+                }
+                else
+                {
+                    if (i == 0)
+                        Error("Empty expression");
+                    break;
+                }
+
+                i++;
+                GetNextToken();
+            }
+
+            while (operators.Count > 0)
+                Operation(ref stack, operators.Pop());
+
+            return stack.Pop();
+        }
+
+        void Operation(ref Stack<Value> stack, Token token)
+        {
+            Value b = stack.Pop();
+            Value a = stack.Pop();
+            Value result = a.BinOp(b, token);
+            stack.Push(result);
+        }
+    }
+}

+ 137 - 0
NTERA.Interpreter/Keywords/Control.cs

@@ -0,0 +1,137 @@
+namespace NTERA.Interpreter
+{
+	public partial class Interpreter
+	{
+		private void If() 
+        {
+            bool result = Expr().BinOp(new Value(0), Token.Equal).Real == 1;
+
+            Match(Token.Then);
+            GetNextToken();
+
+            if (result)
+            {
+                int i = ifcounter;
+                while (true)
+                {
+                    if (lastToken == Token.If)
+                    {
+                        i++;
+                    }
+                    else if (lastToken == Token.Else)
+                    {
+                        if (i == ifcounter)
+                        {
+                            GetNextToken();
+                            return;
+                        }
+                    }
+                    else if (lastToken == Token.EndIf)
+                    {
+                        if(i == ifcounter)
+                        {
+                            GetNextToken();
+                            return;
+                        }
+                        i--;
+                    }
+                    GetNextToken();
+                }
+            }
+        }
+
+		private void Else()
+		{
+			int i = ifcounter;
+			while (true)
+            {
+                if (lastToken == Token.If)
+                {
+                    i++;
+                }
+                else if (lastToken == Token.EndIf)
+                {
+                    if(i == ifcounter)
+                    {
+                        GetNextToken();
+                        return;
+                    }
+                    i--;
+                }
+				GetNextToken ();
+			}
+		}
+
+		private void End()
+        {
+            exit = true;
+        }
+
+		private void Let()
+        {
+            if (lastToken != Token.Equal)
+            {
+                Match(Token.Identifer);
+                GetNextToken();
+                Match(Token.Equal);
+            }
+
+            string id = lex.Identifer;
+
+            GetNextToken();
+            
+            SetVar(id, Expr());
+        }
+
+		private void For()
+        {
+            Match(Token.Identifer);
+            string var = lex.Identifer;
+
+            GetNextToken();
+            Match(Token.Equal);
+
+            GetNextToken();
+            Value v  = Expr();
+
+            if (loops.ContainsKey(var))
+            {
+                loops[var] = lineMarker;
+            }
+            else
+            {
+                SetVar(var, v);
+                loops.Add(var, lineMarker);
+            }
+
+            Match(Token.To);
+
+            GetNextToken();
+            v = Expr();
+            
+            if (vars[var].BinOp(v, Token.More).Real == 1)
+            {
+                while (true)
+                {
+                    while (!(GetNextToken() == Token.Identifer && prevToken == Token.Next)) ;
+                    if (lex.Identifer == var)
+                    {
+                        loops.Remove(var);
+                        GetNextToken();
+                        Match(Token.NewLine);
+                        break;
+                    }
+                }
+            }
+        }
+
+		private void Next()
+        {
+            Match(Token.Identifer);
+            string var = lex.Identifer;
+            vars[var] = vars[var].BinOp(new Value(1), Token.Plus);
+            lex.GoTo(new Marker(loops[var].Pointer - 1, loops[var].Line, loops[var].Column - 1));
+            lastToken = Token.NewLine;
+        }
+	}
+}

+ 154 - 0
NTERA.Interpreter/Lexer.cs

@@ -0,0 +1,154 @@
+using System;
+
+namespace NTERA.Interpreter
+{
+    public class Lexer
+    {
+        private readonly string source;
+        private Marker sourceMarker;
+        private char lastChar;
+
+        public Marker TokenMarker { get; set; }
+
+        public string Identifer { get; set; }
+        public Value Value { get; set; }
+
+        public Lexer(string input)
+        {
+            source = input;
+            sourceMarker = new Marker(0, 1, 1);
+            lastChar = source[0];
+        }
+
+        public void GoTo(Marker marker)
+        {
+            sourceMarker = marker;
+        }
+
+        char GetChar()
+        {
+            sourceMarker.Column++;
+            sourceMarker.Pointer++;
+
+            if (sourceMarker.Pointer >= source.Length)
+                return lastChar = (char)0;
+
+            if ((lastChar = source[sourceMarker.Pointer]) == '\n')
+            {
+                sourceMarker.Column = 1;
+                sourceMarker.Line++;
+            }
+            return lastChar;
+        }
+
+        public Token GetToken()
+        {
+            while (lastChar == ' ' || lastChar == '\t' || lastChar == '\r')
+                GetChar();
+
+            TokenMarker = sourceMarker;
+
+            if (char.IsLetter(lastChar))
+            {
+                Identifer = lastChar.ToString();
+                while (char.IsLetterOrDigit(GetChar()))
+                    Identifer += lastChar;
+                switch (Identifer.ToUpper())
+                {
+                    case "PRINT": return Token.Print;
+                    case "IF": return Token.If;
+                    case "ENDIF": return Token.EndIf;
+                    case "THEN": return Token.Then;
+                    case "ELSE": return Token.Else;
+                    case "FOR": return Token.For;
+                    case "TO": return Token.To;
+                    case "NEXT": return Token.Next;
+                    case "INPUT": return Token.Input;
+                    case "LET": return Token.Let;
+                    case "GOSUB": return Token.Gosub;
+                    case "RETURN": return Token.Return;
+                    case "END": return Token.End;
+                    case "OR": return Token.Or;
+                    case "AND": return Token.And;
+                    case "REM":
+                        while (lastChar != '\n') GetChar();
+                        GetChar();
+                        return GetToken();
+                    default:
+                        return Token.Identifer;
+                }
+            }
+
+            if (char.IsDigit(lastChar))
+            {
+                string num = "";
+                do { num += lastChar; } while (char.IsDigit(GetChar()) || lastChar == '.');
+
+                double real;
+                if (!double.TryParse(num, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out real))
+                    throw new Exception("ERROR while parsing number");
+                Value = new Value(real);
+                return Token.Value;
+            }
+
+            Token tok = Token.Unkown;
+            switch (lastChar)
+            {
+                case '\n': tok = Token.NewLine; break;
+                case ':': tok = Token.Colon; break;
+                case ';': tok = Token.Semicolon; break;
+                case ',': tok = Token.Comma; break;
+                case '=': tok = Token.Equal; break;
+                case '+': tok = Token.Plus; break;
+                case '-': tok = Token.Minus; break;
+                case '/': tok = Token.Slash; break;
+                case '*': tok = Token.Asterisk; break;
+                case '^': tok = Token.Caret; break;
+                case '(': tok = Token.LParen; break;
+                case ')': tok = Token.RParen; break;
+                case '\'':
+                    while (lastChar != '\n') GetChar();
+                    GetChar();
+                    return GetToken();
+                case '<':
+                    GetChar();
+                    if (lastChar == '>') tok = Token.NotEqual;
+                    else if (lastChar == '=') tok = Token.LessEqual;
+                    else return Token.Less;
+                    break;
+                case '>':
+                    GetChar();
+                    if (lastChar == '=') tok = Token.MoreEqual;
+                    else return Token.More;
+                    break;
+                case '"':
+                    string str = "";
+                    while (GetChar() != '"')
+                    {
+                        if (lastChar == '\\')
+                        {
+                            switch (char.ToLower(GetChar()))
+                            {
+                                case 'n': str += '\n'; break;
+                                case 't': str += '\t'; break;
+                                case '\\': str += '\\'; break;
+                                case '"': str += '"'; break;
+                            }
+                        }
+                        else
+                        {
+                            str += lastChar;
+                        }
+                    }
+                    Value = new Value(str);
+                    tok = Token.Value;
+                    break;
+                case (char)0:
+                    return Token.EOF;
+            }
+
+            GetChar();
+            return tok;
+        }
+    }
+}

+ 17 - 0
NTERA.Interpreter/Marker.cs

@@ -0,0 +1,17 @@
+
+namespace NTERA.Interpreter
+{
+    public struct Marker
+    {
+        public int Pointer { get; set; }
+        public int Line { get; set; }
+        public int Column { get; set; }
+
+        public Marker(int pointer, int line, int column) : this()
+        {
+            Pointer = pointer;
+            Line = line;
+            Column = column;
+        }
+    }
+}

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

@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{F3B58EF3-E3FF-4B76-92B8-2AB87663FC4D}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>NTERA.Interpreter</RootNamespace>
+    <AssemblyName>NTERA.Interpreter</AssemblyName>
+    <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Engine.cs" />
+    <Compile Include="Functions\BuiltInFunctions.cs" />
+    <Compile Include="Interpreter.cs" />
+    <Compile Include="Keywords\Control.cs" />
+    <Compile Include="Lexer.cs" />
+    <Compile Include="Marker.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Token.cs" />
+    <Compile Include="Value.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\NTERA.Core\NTERA.Core.csproj">
+      <Project>{62A2B84E-9207-46B0-8A8D-28B5931FCB6E}</Project>
+      <Name>NTERA.Core</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+</Project>

+ 35 - 0
NTERA.Interpreter/Properties/AssemblyInfo.cs

@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("NTERA.Interpereter")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("NTERA.Interpereter")]
+[assembly: AssemblyCopyright("Copyright ©  2018")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components.  If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("f3b58ef3-e3ff-4b76-92b8-2ab87663fc4d")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 52 - 0
NTERA.Interpreter/Token.cs

@@ -0,0 +1,52 @@
+
+namespace NTERA.Interpreter
+{
+    public enum Token
+    {
+        Unkown,
+
+        Identifer,
+        Value,
+
+        //Keywords
+        Print,
+        If,
+		EndIf,
+        Then,
+        Else,
+        For,
+        To,
+        Next,
+        Input,
+        Let,
+        Gosub,
+        Return,
+        Rem,
+        End,
+
+        NewLine,
+        Colon,
+        Semicolon,
+        Comma,
+
+        Plus,
+        Minus,
+        Slash,
+        Asterisk,
+        Caret,
+        Equal,
+        Less,
+        More,
+        NotEqual,
+        LessEqual,
+        MoreEqual,
+        Or,
+        And,
+        Not,
+
+        LParen,
+        RParen,
+
+        EOF = -1   //End Of File
+    }
+}

+ 110 - 0
NTERA.Interpreter/Value.cs

@@ -0,0 +1,110 @@
+using System;
+
+namespace NTERA.Interpreter
+{
+    public enum ValueType
+    {
+        Real,
+        String
+    }
+
+    public struct Value
+    {
+        public static readonly Value Zero = new Value(0);
+        public ValueType Type { get; set; }
+
+        public double Real { get; set; }
+        public string String { get; set; }
+
+        public Value(double real) : this()
+        {
+            Type = ValueType.Real;
+            Real = real;
+        }
+
+        public Value(string str)
+            : this()
+        {
+            Type = ValueType.String;
+            String = str;
+        }
+
+        public Value Convert(ValueType type)
+        {
+            if (Type != type)
+            {
+                switch (type)
+                {
+                    case ValueType.Real:
+                        Real = double.Parse(String);
+                        Type = ValueType.Real;
+                        break;
+                    case ValueType.String:
+                        String = Real.ToString();
+                        Type = ValueType.String;
+                        break;
+                }
+            }
+            return this;
+        }
+
+        public Value BinOp(Value b, Token tok)
+        {
+            Value a = this;
+            if (a.Type != b.Type)
+            {
+                if (a.Type > b.Type)
+                    b = b.Convert(a.Type);
+                else
+                    a = a.Convert(b.Type);
+            }
+
+            if (tok == Token.Plus)
+            {
+                if (a.Type == ValueType.Real)
+                    return new Value(a.Real + b.Real);
+                else
+                    return new Value(a.String + b.String);
+            }
+            else if(tok == Token.Equal)
+            {
+                 if (a.Type == ValueType.Real)
+                     return new Value(a.Real == b.Real ? 1 : 0);
+                else
+                     return new Value(a.String == b.String ? 1 : 0);
+            }
+            else if(tok == Token.NotEqual)
+            {
+                 if (a.Type == ValueType.Real)
+                     return new Value(a.Real == b.Real ? 0 : 1);
+                else
+                     return new Value(a.String == b.String ? 0 : 1);
+            }
+            else
+            {
+                if (a.Type == ValueType.String)
+                    throw new Exception("Cannot do binop on strings(except +).");
+
+                switch (tok)
+                {
+                    case Token.Minus: return new Value(a.Real - b.Real);
+                    case Token.Asterisk: return new Value(a.Real * b.Real);
+                    case Token.Slash: return new Value(a.Real / b.Real);
+                    case Token.Caret: return new Value(Math.Pow(a.Real, b.Real));
+                    case Token.Less: return new Value(a.Real < b.Real ? 1 : 0);
+                    case Token.More: return new Value(a.Real > b.Real ? 1 : 0);
+                    case Token.LessEqual: return new Value(a.Real <= b.Real ? 1 : 0);
+                    case Token.MoreEqual: return new Value(a.Real >= b.Real ? 1 : 0);
+                }
+            }
+            throw new Exception("Unkown binop");
+        }
+
+        public override string ToString()
+        {
+            if (Type == ValueType.Real)
+                return Real.ToString();
+            return String;
+        }
+    }
+}

+ 6 - 0
NTERA.sln

@@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NTERA.EmuEra", "NTERA.EmuEr
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NTERA.Core", "NTERA.Core\NTERA.Core.csproj", "{62A2B84E-9207-46B0-8A8D-28B5931FCB6E}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NTERA.Interpreter", "NTERA.Interpreter\NTERA.Interpreter.csproj", "{F3B58EF3-E3FF-4B76-92B8-2AB87663FC4D}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -27,6 +29,10 @@ Global
 		{62A2B84E-9207-46B0-8A8D-28B5931FCB6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{62A2B84E-9207-46B0-8A8D-28B5931FCB6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{62A2B84E-9207-46B0-8A8D-28B5931FCB6E}.Release|Any CPU.Build.0 = Release|Any CPU
+		{F3B58EF3-E3FF-4B76-92B8-2AB87663FC4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{F3B58EF3-E3FF-4B76-92B8-2AB87663FC4D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{F3B58EF3-E3FF-4B76-92B8-2AB87663FC4D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{F3B58EF3-E3FF-4B76-92B8-2AB87663FC4D}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE