Эх сурвалжийг харах

Move lexer token logic to token enum attributes

Bepis 6 жил өмнө
parent
commit
20ff8520e6

+ 26 - 2
NTERA.Interpreter/Attributes.cs

@@ -2,12 +2,36 @@
 
 namespace NTERA.Interpreter
 {
+    [AttributeUsage(AttributeTargets.Field)]
+    public class LexerCharacterAttribute : Attribute
+    {
+        public char Character { get; }
+
+        public LexerCharacterAttribute(char character)
+        {
+            Character = character;
+        }
+    }
+
+    [AttributeUsage(AttributeTargets.Field)]
+    public class LexerKeywordAttribute : Attribute
+    {
+        public string Keyword { get; }
+        public bool IsLineKeyword { get; }
+
+        public LexerKeywordAttribute(string keyword, bool isLineKeyword = false)
+        {
+            Keyword = keyword;
+            IsLineKeyword = isLineKeyword;
+        }
+    }
+
     [AttributeUsage(AttributeTargets.Method)]
-    public class TargetKeywordAttribute : Attribute
+    public class KeywordMethodAttribute : Attribute
     {
         public Token Token { get; }
 
-        public TargetKeywordAttribute(Token token)
+        public KeywordMethodAttribute(Token token)
         {
             Token = token;
         }

+ 54 - 74
NTERA.Interpreter/Interpreter.cs

@@ -13,7 +13,7 @@ namespace NTERA.Interpreter
         private Token prevToken;
         private Token lastToken;
 
-        private Dictionary<string, Value> vars;
+        public readonly Dictionary<string, Value> Variables;
         private Dictionary<string, Marker> loops;
 
         public delegate Value BasicFunction(List<Value> args);
@@ -30,7 +30,7 @@ namespace NTERA.Interpreter
         {
             this.console = console;
             lex = new Lexer(input);
-            vars = new Dictionary<string, Value>();
+            Variables = new Dictionary<string, Value>();
             loops = new Dictionary<string, Marker>();
             ifcounter = 0;
 
@@ -40,15 +40,15 @@ namespace NTERA.Interpreter
 
         public Value GetVar(string name)
         {
-            if (!vars.ContainsKey(name))
+            if (!Variables.ContainsKey(name))
                 throw new Exception("Variable with name " + name + " does not exist.");
-            return vars[name];
+            return Variables[name];
         }
 
         public void SetVar(string name, Value val)
         {
-            if (!vars.ContainsKey(name)) vars.Add(name, val);
-            else vars[name] = val;
+            if (!Variables.ContainsKey(name)) Variables.Add(name, val);
+            else Variables[name] = val;
         }
 
         void Error(string text)
@@ -163,25 +163,6 @@ namespace NTERA.Interpreter
             }
         }
 
-
-        [BuiltInFunction("str")]
-        private Value Str(List<Value> args)
-        {
-            if (args.Count < 1)
-                throw new ArgumentException();
-
-            return args[0].Convert(ValueType.String);
-        }
-
-        [BuiltInFunction("num")]
-        private Value Num(List<Value> args)
-        {
-            if (args.Count < 1)
-                throw new ArgumentException();
-
-            return args[0].Convert(ValueType.Real);
-        }
-
         [BuiltInFunction("abs")]
         private Value Abs(List<Value> args)
         {
@@ -241,7 +222,7 @@ namespace NTERA.Interpreter
 
             foreach (var method in typeof(Interpreter).GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
             {
-                var attribute = method.GetCustomAttributes(typeof(TargetKeywordAttribute), true).FirstOrDefault() as TargetKeywordAttribute;
+                var attribute = method.GetCustomAttributes(typeof(KeywordMethodAttribute), true).FirstOrDefault() as KeywordMethodAttribute;
 
                 if (attribute == null)
                     continue;
@@ -251,27 +232,27 @@ namespace NTERA.Interpreter
         }
 
 
-        [TargetKeyword(Token.Print)]
+        [KeywordMethod(Token.Print)]
         void Print()
         {
             console.Write(Expr().ToString());
         }
 
-        [TargetKeyword(Token.PrintL)]
+        [KeywordMethod(Token.PrintL)]
         void PrintL()
         {
             console.PrintSingleLine(Expr().ToString());
         }
 
 
-        [TargetKeyword(Token.PrintImg)]
+        [KeywordMethod(Token.PrintImg)]
         void PrintImg()
         {
             console.PrintImg(Expr().ToString().Trim().Trim('"'));
         }
 
 
-        [TargetKeyword(Token.PrintButton)]
+        [KeywordMethod(Token.PrintButton)]
         void PrintButton()
         {
             console.PrintButton(Expr().ToString(), 0);
@@ -279,32 +260,32 @@ namespace NTERA.Interpreter
 
         private static readonly Regex FormRegex = new Regex("{(.*?)}");
 
-        [TargetKeyword(Token.PrintFormL)]
+        [KeywordMethod(Token.PrintFormL)]
         void PrintFormL()
         {
             string rawString = Expr().ToString();
 
-            var evaluator = new MatchEvaluator(match => vars[match.Groups[1].Value].ToString());
+            var evaluator = new MatchEvaluator(match => Variables[match.Groups[1].Value].ToString());
 
             console.PrintSingleLine(FormRegex.Replace(rawString, evaluator));
         }
 
-        [TargetKeyword(Token.DrawLine)]
+        [KeywordMethod(Token.DrawLine)]
         void DrawLine()
         {
             console.PrintBar();
         }
 
-        [TargetKeyword(Token.DrawLineForm)]
+        [KeywordMethod(Token.DrawLineForm)]
         void DrawLineForm()
         {
             console.printCustomBar(Expr().ToString().Trim());
         }
 
-        [TargetKeyword(Token.If)]
+        [KeywordMethod(Token.If)]
         private void If()
         {
-            bool result = Expr().BinOp(new Value(0), Token.Equal).Real == 1;
+            bool result = Expr().Real == 1;
 
             Match(Token.Then);
             GetNextToken();
@@ -340,7 +321,7 @@ namespace NTERA.Interpreter
             }
         }
 
-        [TargetKeyword(Token.Else)]
+        [KeywordMethod(Token.Else)]
         private void Else()
         {
             int i = ifcounter;
@@ -363,19 +344,19 @@ namespace NTERA.Interpreter
             }
         }
 
-        [TargetKeyword(Token.EndIf)]
+        [KeywordMethod(Token.EndIf)]
         private void EndIf()
         {
 
         }
 
-        [TargetKeyword(Token.End)]
+        [KeywordMethod(Token.End)]
         private void End()
         {
             exit = true;
         }
 
-        [TargetKeyword(Token.Let)]
+        [KeywordMethod(Token.Let)]
         private void Let()
         {
             if (lastToken != Token.Equal)
@@ -392,7 +373,7 @@ namespace NTERA.Interpreter
             SetVar(id, Expr());
         }
 
-        [TargetKeyword(Token.For)]
+        [KeywordMethod(Token.For)]
         private void For()
         {
             Match(Token.Identifer);
@@ -419,7 +400,7 @@ namespace NTERA.Interpreter
             GetNextToken();
             v = Expr();
 
-            if (vars[var].BinOp(v, Token.More).Real == 1)
+            if (Variables[var].Operate(v, Token.More).Real == 1)
             {
                 while (true)
                 {
@@ -435,17 +416,17 @@ namespace NTERA.Interpreter
             }
         }
 
-        [TargetKeyword(Token.Next)]
+        [KeywordMethod(Token.Next)]
         private void Next()
         {
             Match(Token.Identifer);
             string var = lex.Identifer;
-            vars[var] = vars[var].BinOp(new Value(1), Token.Plus);
+            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));
             lastToken = Token.NewLine;
         }
 
-        [TargetKeyword(Token.Times)]
+        [KeywordMethod(Token.Times)]
         private void Times()
         {
             Match(Token.Identifer);
@@ -458,7 +439,7 @@ namespace NTERA.Interpreter
             GetNextToken();
             var arg2 = Expr();
 
-            vars[var] = vars[var].BinOp(arg2, Token.Asterisk);
+            Variables[var] = Variables[var].Operate(arg2, Token.Asterisk);
             
             Match(Token.NewLine);
         }
@@ -471,15 +452,15 @@ namespace NTERA.Interpreter
             {
                 Match(Token.Identifer);
                
-                if (!vars.ContainsKey(lex.Identifer)) vars.Add(lex.Identifer, new Value());
+                if (!Variables.ContainsKey(lex.Identifer)) Variables.Add(lex.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))
-                    vars[lex.Identifer] = new Value(d);
+                    Variables[lex.Identifer] = new Value(d);
                 else
-                    vars[lex.Identifer] = new Value(input);
+                    Variables[lex.Identifer] = new Value(input);
                 
                 GetNextToken();
                 if (lastToken != Token.Comma) break;
@@ -487,18 +468,18 @@ namespace NTERA.Interpreter
             }
         }
 
-        Value Expr()
+        private static readonly Dictionary<Token, int> OrderOfOps = new Dictionary<Token, int>()
         {
-            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 }
-            };
+            { 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 }
+        };
 
+        Value Expr()
+        {
             Stack<Value> stack = new Stack<Value>();
             Stack<Token> operators = new Stack<Token>();
 
@@ -511,9 +492,9 @@ namespace NTERA.Interpreter
                 }
                 else if (lastToken == Token.Identifer)
                 {
-                    if (vars.ContainsKey(lex.Identifer))
+                    if (Variables.ContainsKey(lex.Identifer))
                     {
-                        stack.Push(vars[lex.Identifer]);
+                        stack.Push(Variables[lex.Identifer]);
                     }
                     else if (FunctionDictionary.ContainsKey(lex.Identifer))
                     {
@@ -521,13 +502,12 @@ namespace NTERA.Interpreter
                         List<Value> args = new List<Value>();
                         GetNextToken();
                         Match(Token.LParen);
-
-                        start:
-                        if (GetNextToken() != Token.RParen)
+                        
+                        while (GetNextToken() != Token.RParen)
                         {
                             args.Add(Expr());
-                            if (lastToken == Token.Comma)
-                                goto start;
+                            if (lastToken != Token.Comma)
+                                break;
                         }
 
                         stack.Push(FunctionDictionary[name](args));
@@ -543,17 +523,17 @@ namespace NTERA.Interpreter
                     stack.Push(Expr());
                     Match(Token.RParen);
                 }
-                else if (lastToken >= Token.Plus && lastToken <= Token.Not)
+                else if (lastToken.IsArithmetic())
                 {
-                    if ((lastToken == Token.Minus || lastToken == Token.Minus) && (i == 0 || prevToken == Token.LParen))
+                    if (lastToken.IsUnary() && (i == 0 || prevToken == Token.LParen))
                     {
-                        stack.Push(new Value(0));
+                        stack.Push(0);
                         operators.Push(lastToken);
                     }
                     else
                     {
-                        while (operators.Count > 0 && prec[lastToken] <= prec[operators.Peek()])
-                            Operation(ref stack, operators.Pop());
+                        while (operators.Count > 0 && OrderOfOps[lastToken] <= OrderOfOps[operators.Peek()])
+                            Operation(stack, operators.Pop());
                         operators.Push(lastToken);
                     }
                 }
@@ -569,16 +549,16 @@ namespace NTERA.Interpreter
             }
 
             while (operators.Count > 0)
-                Operation(ref stack, operators.Pop());
+                Operation(stack, operators.Pop());
 
             return stack.Pop();
         }
 
-        void Operation(ref Stack<Value> stack, Token token)
+        void Operation(Stack<Value> stack, Token token)
         {
             Value b = stack.Pop();
             Value a = stack.Pop();
-            Value result = a.BinOp(b, token);
+            Value result = a.Operate(b, token);
             stack.Push(result);
         }
     }

+ 31 - 48
NTERA.Interpreter/Lexer.cs

@@ -19,6 +19,8 @@ namespace NTERA.Interpreter
         {
             source = input;
             sourceMarker = new Marker(-1, 1, 0);
+
+            InitTokenDictionaries();
         }
 
         public void GoTo(Marker marker)
@@ -42,41 +44,30 @@ namespace NTERA.Interpreter
             return lastChar;
         }
 
-        private readonly Dictionary<string, Token> TokenDictionary = new Dictionary<string, Token>(StringComparer.InvariantCultureIgnoreCase)
-        {
-            //["PRINT"] = Token.Print,
-            //["PRINTL"] = Token.PrintL,
-            ["DRAWLINE"] = Token.DrawLine,
-            //["DRAWLINEFORM"] = Token.DrawLineForm,
-            ["DIM"] = Token.Dim,
-            ["CONST"] = Token.Const,
-            ["IF"] = Token.If,
-            ["ENDIF"] = Token.EndIf,
-            ["THEN"] = Token.Then,
-            ["ELSE"] = Token.Else,
-            ["FOR"] = Token.For,
-            ["TO"] = Token.To,
-            ["NEXT"] = Token.Next,
-            ["INPUT"] = Token.Input,
-            ["LET"] = Token.Let,
-            ["GOSUB"] = Token.Gosub,
-            ["RETURN"] = Token.Return,
-            ["END"] = Token.End,
-            ["OR"] = Token.Or,
-            ["AND"] = Token.And,
-
-            ["TIMES"] = Token.Times,
-        };
-
-        private readonly Dictionary<string, Token> TokenLineDictionary = new Dictionary<string, Token>(StringComparer.InvariantCultureIgnoreCase)
+        private readonly Dictionary<string, Token> TokenDictionary = new Dictionary<string, Token>(StringComparer.InvariantCultureIgnoreCase);
+
+        private readonly Dictionary<string, Token> TokenLineDictionary = new Dictionary<string, Token>(StringComparer.InvariantCultureIgnoreCase);
+
+        private readonly Dictionary<char, Token> TokenCharDictionary = new Dictionary<char, Token>();
+
+        private void InitTokenDictionaries()
         {
-            ["PRINT"] = Token.Print,
-            ["PRINTL"] = Token.PrintL,
-            ["DRAWLINEFORM"] = Token.DrawLineForm,
-            ["PRINTFORML"] = Token.PrintFormL,
-            ["PRINT_IMG"] = Token.PrintImg,
-            ["PRINTBUTTON"] = Token.PrintButton,
-        };
+            foreach (Token token in Enum.GetValues(typeof(Token)))
+            {
+                foreach (var attribute in Utility.GetEnumAttributes<Token, LexerKeywordAttribute>(token))
+                {
+                    if (attribute.IsLineKeyword)
+                        TokenLineDictionary[attribute.Keyword] = token;
+                    else
+                        TokenDictionary[attribute.Keyword] = token;
+                }
+
+                foreach (var attribute in Utility.GetEnumAttributes<Token, LexerCharacterAttribute>(token))
+                {
+                    TokenCharDictionary[attribute.Character] = token;
+                }
+            }
+        }
 
         public IEnumerable<Token> GetTokens()
         {
@@ -146,23 +137,15 @@ namespace NTERA.Interpreter
 
                     continue;
                 }
+
+                if (TokenCharDictionary.TryGetValue(lastChar, out Token charToken))
+                {
+                    yield return charToken;
+                    continue;
+                }
                 
                 switch (lastChar)
                 {
-                    case '\n': yield return Token.NewLine; continue;
-                    case '@': yield return Token.Function; continue;
-                    case '#': yield return Token.Global; continue;
-                    case ':': yield return Token.Colon; continue;
-                    case ';': yield return Token.Semicolon; continue;
-                    case ',': yield return Token.Comma; continue;
-                    case '=': yield return Token.Equal; continue;
-                    case '+': yield return Token.Plus; continue;
-                    case '-': yield return Token.Minus; continue;
-                    case '/': yield return Token.Slash; continue;
-                    case '*': yield return Token.Asterisk; continue;
-                    case '^': yield return Token.Caret; continue;
-                    case '(': yield return Token.LParen; continue;
-                    case ')': yield return Token.RParen; continue;
                     case '\'':
                         while (lastChar != '\n')
                             GetChar();

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

@@ -45,6 +45,7 @@
     <Compile Include="Marker.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Token.cs" />
+    <Compile Include="Utility.cs" />
     <Compile Include="Value.cs" />
   </ItemGroup>
   <ItemGroup>

+ 80 - 7
NTERA.Interpreter/Token.cs

@@ -5,63 +5,136 @@ namespace NTERA.Interpreter
     {
         Unkown,
 
-        Global,
-        Function,
-
         Identifer,
         Value,
 
+        [LexerCharacter('#')]
+        Global,
+        [LexerCharacter('@')]
+        Function,
+
+        [LexerKeyword("DIM")]
         Dim,
+        [LexerKeyword("CONST")]
         Const,
 
+        //Eralang print keywords 
+        [LexerKeyword("DRAWLINE", false)]
         DrawLine,
+        [LexerKeyword("DRAWLINEFORM", true)]
         DrawLineForm,
+        [LexerKeyword("PRINT", true)]
         Print,
+        [LexerKeyword("PRINTL", true)]
         PrintL,
+        [LexerKeyword("PRINTFORM", true)]
         PrintForm,
+        [LexerKeyword("PRINTFORML", true)]
         PrintFormL,
+        [LexerKeyword("PRINT_IMG", true)]
         PrintImg,
+        [LexerKeyword("PRINTBUTTON", true)]
         PrintButton,
 
-        //Keywords
+        //Eralang arithmetic keywords
+        [LexerKeyword("TIMES")]
+        Times,
+
+        //Standard BASIC keywords
+        [LexerKeyword("IF")]
         If,
-		EndIf,
+        [LexerKeyword("ENDIF")]
+        EndIf,
+        [LexerKeyword("THEN")]
         Then,
+        [LexerKeyword("ELSE")]
         Else,
+        [LexerKeyword("FOR")]
         For,
+        [LexerKeyword("TO")]
         To,
+        [LexerKeyword("NEXT")]
         Next,
+        [LexerKeyword("INPUT")]
         Input,
+        [LexerKeyword("LET")]
         Let,
+        [LexerKeyword("GOSUB")]
         Gosub,
+        [LexerKeyword("RETURN")]
         Return,
+        [LexerKeyword("REM")]
         Rem,
+        [LexerKeyword("END")]
         End,
 
+        [LexerCharacter('\n')]
         NewLine,
+        [LexerCharacter(':')]
         Colon,
+        [LexerCharacter(';')]
         Semicolon,
+        [LexerCharacter(',')]
         Comma,
 
+        [LexerCharacter('+')]
         Plus,
+        [LexerCharacter('-')]
         Minus,
+        [LexerCharacter('/')]
         Slash,
+        [LexerCharacter('*')]
         Asterisk,
+        [LexerCharacter('^')]
         Caret,
+        [LexerCharacter('=')]
         Equal,
         Less,
         More,
         NotEqual,
         LessEqual,
         MoreEqual,
+        [LexerKeyword("OR")]
+        [LexerCharacter('|')]
         Or,
+        [LexerKeyword("AND")]
+        [LexerCharacter('&')]
         And,
+        [LexerCharacter('!')]
         Not,
-        Times,
 
+        [LexerCharacter('(')]
         LParen,
+        [LexerCharacter(')')]
         RParen,
-
+        
         EOF = -1   //End Of File
     }
+
+    public static class TokenEnumExtensions
+    {
+        public static bool IsUnary(this Token token)
+        {
+            return token == Token.Plus
+                || token == Token.Minus;
+        }
+
+        public static bool IsArithmetic(this Token token)
+        {
+            return token == Token.Plus
+                || token == Token.Minus
+                || token == Token.Slash
+                || token == Token.Asterisk
+                || token == Token.Caret
+                || token == Token.Equal
+                || token == Token.NotEqual
+                || token == Token.Less
+                || token == Token.LessEqual
+                || token == Token.More
+                || token == Token.MoreEqual
+                || token == Token.Or
+                || token == Token.And
+                || token == Token.Not;
+        }
+    }
 }

+ 13 - 0
NTERA.Interpreter/Utility.cs

@@ -0,0 +1,13 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace NTERA.Interpreter
+{
+    public static class Utility
+    {
+        public static IEnumerable<TAttribute> GetEnumAttributes<TEnum, TAttribute>(TEnum enumValue)
+        {
+            return typeof(TEnum).GetField(enumValue.ToString()).GetCustomAttributes(typeof(TAttribute), false).Cast<TAttribute>();
+        }
+    }
+}

+ 54 - 55
NTERA.Interpreter/Value.cs

@@ -10,16 +10,21 @@ namespace NTERA.Interpreter
 
     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 double Real { get; }
+        public string String { get; }
+
+        public Value(bool boolean) : this(boolean ? 1d : 0d)
+        {
+
+        }
 
         public Value(double real) : this()
         {
             Type = ValueType.Real;
             Real = real;
+            String = real.ToString("N");
         }
 
         public Value(string str)
@@ -27,66 +32,31 @@ namespace NTERA.Interpreter
         {
             Type = ValueType.String;
             String = str;
+            Real = Double.NaN;
         }
 
-        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)
+        public Value Operate(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)
+            bool isStringOperation = a.Type == ValueType.String || b.Type == ValueType.String;
+
+            if (isStringOperation)
             {
-                 if (a.Type == ValueType.Real)
-                     return new Value(a.Real == b.Real ? 0 : 1);
-                else
-                     return new Value(a.String == b.String ? 0 : 1);
+                switch (tok)
+                {
+                    case Token.Plus: return new Value(a.String + b.String);
+                    case Token.Equal: return new Value(a.String == b.String);
+                    case Token.NotEqual: return new Value(a.String != b.String);
+                }
             }
             else
             {
-                if (a.Type == ValueType.String)
-                    throw new Exception("Cannot do binop on strings(except +).");
-
                 switch (tok)
                 {
+                    case Token.Plus: return new Value(a.Real + b.Real);
+                    case Token.Equal: return new Value(a.Real == b.Real);
+                    case Token.NotEqual: return new Value(a.Real != b.Real);
                     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);
@@ -97,14 +67,43 @@ namespace NTERA.Interpreter
                     case Token.MoreEqual: return new Value(a.Real >= b.Real ? 1 : 0);
                 }
             }
-            throw new Exception("Unkown binop");
+
+            throw new Exception($"Invalid operation on value ({tok}) on {(isStringOperation ? "string" : "double")}");
         }
 
         public override string ToString()
         {
-            if (Type == ValueType.Real)
-                return Real.ToString();
             return String;
         }
+
+        public static implicit operator double(Value value)
+        {
+            return value.Real;
+        }
+
+        public static implicit operator string(Value value)
+        {
+            return value.String;
+        }
+
+        public static implicit operator bool(Value value)
+        {
+            return value.Real != 0;
+        }
+
+        public static implicit operator Value(double value)
+        {
+            return new Value(value);
+        }
+
+        public static implicit operator Value(string value)
+        {
+            return new Value(value);
+        }
+
+        public static implicit operator Value(bool value)
+        {
+            return new Value(value);
+        }
     }
 }