├── LICENSE ├── BasicSharp ├── Properties │ └── AssemblyInfo.cs ├── Marker.cs ├── BasicException.cs ├── Token.cs ├── BuiltIns.cs ├── BasicSharp.csproj ├── Value.cs ├── Lexer.cs └── Interpreter.cs ├── BasicSharp.Test ├── Tests │ ├── goto.bas │ ├── for.bas │ ├── ifs.bas │ └── operators.bas ├── App.config ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── BasicSharp.Test.csproj ├── README.md ├── BasicSharp.sln └── .gitignore /LICENSE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Timu5/BasicSharp/HEAD/LICENSE -------------------------------------------------------------------------------- /BasicSharp/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Timu5/BasicSharp/HEAD/BasicSharp/Properties/AssemblyInfo.cs -------------------------------------------------------------------------------- /BasicSharp.Test/Tests/goto.bas: -------------------------------------------------------------------------------- 1 | GoTo abc 2 | assert 0 3 | 4 | def: 5 | assert 1 6 | End 7 | 8 | abc: 9 | assert 1 10 | 11 | GoTo def 12 | assert 0 -------------------------------------------------------------------------------- /BasicSharp.Test/Tests/for.bas: -------------------------------------------------------------------------------- 1 | 2 | let a = 0 3 | 4 | For i = 1 To 10 5 | let a = a + 1 6 | Next i 7 | 8 | assert a = 10 9 | 10 | For i = 10 To 9 11 | assert 0 12 | Next i 13 | -------------------------------------------------------------------------------- /BasicSharp.Test/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /BasicSharp.Test/Tests/ifs.bas: -------------------------------------------------------------------------------- 1 | If 1 = 1 Then 2 | assert 1 3 | Else 4 | assert 0 5 | EndIf 6 | 7 | If 1 = 1 Then 8 | If 2 = 3 Then 9 | assert 0 10 | EndIf 11 | Else 12 | assert 0 13 | EndIf 14 | 15 | If 1 = 1 Then 16 | If 2 = 3 Then 17 | assert 0 18 | Else 19 | assert 1 20 | EndIf 21 | Else 22 | assert 0 23 | EndIf 24 | -------------------------------------------------------------------------------- /BasicSharp.Test/Tests/operators.bas: -------------------------------------------------------------------------------- 1 | assert 1 = 1 2 | assert 3 = 2 + 1 3 | assert 2 = 5 - 3 4 | assert 6 = 3 * 2 5 | assert 2 = 6 / 3 6 | assert -1 = 1 - 2 7 | assert 1 <> 2 8 | assert Not 1 <> 1 9 | assert 3 > 2 10 | assert 2 < 3 11 | assert 2 >= 1 12 | assert 1 >= 1 13 | assert 1 < 2 14 | assert 1 <= 1 15 | assert 1 And 1 16 | assert 1 Or 0 17 | assert "abc" = "abc" 18 | assert "abc" <> "abc1" 19 | assert 1 = Not 0 20 | assert 0 = Not 1 21 | -------------------------------------------------------------------------------- /BasicSharp/Marker.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace BasicSharp 3 | { 4 | public struct Marker 5 | { 6 | public int Pointer { get; set; } 7 | public int Line { get; set; } 8 | public int Column { get; set; } 9 | 10 | public Marker(int pointer, int line, int column) 11 | : this() 12 | { 13 | Pointer = pointer; 14 | Line = line; 15 | Column = Column; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /BasicSharp/BasicException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BasicSharp 4 | { 5 | class BasicException : Exception 6 | { 7 | public int line; 8 | public BasicException() 9 | { 10 | } 11 | 12 | public BasicException(string message, int line) 13 | : base(message) 14 | { 15 | this.line = line; 16 | } 17 | 18 | public BasicException(string message, int line, Exception inner) 19 | : base(message, inner) 20 | { 21 | this.line = line; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /BasicSharp/Token.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace BasicSharp 3 | { 4 | public enum Token 5 | { 6 | Unknown, 7 | 8 | Identifier, 9 | Value, 10 | 11 | //Keywords 12 | Print, 13 | If, 14 | EndIf, 15 | Then, 16 | Else, 17 | For, 18 | To, 19 | Next, 20 | Goto, 21 | Input, 22 | Let, 23 | Gosub, 24 | Return, 25 | Rem, 26 | End, 27 | Assert, 28 | 29 | NewLine, 30 | Colon, 31 | Semicolon, 32 | Comma, 33 | 34 | Plus, 35 | Minus, 36 | Slash, 37 | Asterisk, 38 | Caret, 39 | Equal, 40 | Less, 41 | More, 42 | NotEqual, 43 | LessEqual, 44 | MoreEqual, 45 | Or, 46 | And, 47 | Not, 48 | 49 | LParen, 50 | RParen, 51 | 52 | EOF = -1 //End Of File 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /BasicSharp.Test/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Collections.Generic; 4 | 5 | namespace BasicSharp.Test 6 | { 7 | class Program 8 | { 9 | static void Main(string[] args) 10 | { 11 | foreach (string file in Directory.GetFiles(Path.Combine(Environment.CurrentDirectory, "Tests"), "*.bas")) 12 | { 13 | Interpreter basic = new Interpreter(File.ReadAllText(file)); 14 | basic.printHandler += Console.WriteLine; 15 | basic.inputHandler += Console.ReadLine; 16 | try 17 | { 18 | basic.Exec(); 19 | } 20 | catch (Exception e) 21 | { 22 | Console.WriteLine("BAD"); 23 | Console.WriteLine(e.Message); 24 | continue; 25 | } 26 | Console.WriteLine("OK"); 27 | } 28 | Console.Read(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | BasicSharp 2 | ==== 3 | Simple BASIC interpreter written in C#. Language syntax is modernize version of BASIC, see example below. 4 | 5 | Example 6 | ------- 7 | ```BlitzBasic 8 | print "Hello World" 9 | 10 | let a = 10 11 | print "Variable a: " + a 12 | 13 | let b = 20 14 | print "a+b=" + (a+b) 15 | 16 | if a = 10 then 17 | print "True" 18 | else 19 | print "False" 20 | endif 21 | 22 | for i = 1 to 10 23 | print i 24 | next i 25 | 26 | goto mylabel 27 | print "False" 28 | 29 | mylabel: 30 | Print "True" 31 | 32 | ``` 33 | 34 | How to use 35 | ---------- 36 | 37 | ```cs 38 | using System; 39 | using BasicSharp; 40 | 41 | namespace MyApp 42 | { 43 | class Program 44 | { 45 | static void Main(string[] args) 46 | { 47 | string code = "print \"Hello World\""; 48 | Interpreter basic = new Interpreter(code); 49 | basic.printHandler += Console.WriteLine; 50 | basic.inputHandler += Console.ReadLine; 51 | try 52 | { 53 | basic.Exec(); 54 | } 55 | catch (BasicException e) 56 | { 57 | Console.WriteLine(e.Message); 58 | Console.WriteLine(e.Line); 59 | } 60 | } 61 | } 62 | } 63 | ``` 64 | -------------------------------------------------------------------------------- /BasicSharp.Test/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Test")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("")] 13 | [assembly: AssemblyCopyright("Copyright © Matusz Muszyński 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("8c5042d9-3e82-4df6-9c39-bf33234e56af")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /BasicSharp/BuiltIns.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace BasicSharp 8 | { 9 | class BuiltIns 10 | { 11 | public static void InstallAll(Interpreter interpreter) 12 | { 13 | interpreter.AddFunction("str", Str); 14 | interpreter.AddFunction("num", Num); 15 | interpreter.AddFunction("abs", Abs); 16 | interpreter.AddFunction("min", Min); 17 | interpreter.AddFunction("max", Max); 18 | interpreter.AddFunction("not", Not); 19 | } 20 | 21 | public static Value Str(Interpreter interpreter, List args) 22 | { 23 | if (args.Count < 1) 24 | throw new ArgumentException(); 25 | 26 | return args[0].Convert(ValueType.String); 27 | } 28 | 29 | public static Value Num(Interpreter interpreter, List args) 30 | { 31 | if (args.Count < 1) 32 | throw new ArgumentException(); 33 | 34 | return args[0].Convert(ValueType.Real); 35 | } 36 | 37 | public static Value Abs(Interpreter interpreter, List args) 38 | { 39 | if (args.Count < 1) 40 | throw new ArgumentException(); 41 | 42 | return new Value(Math.Abs(args[0].Real)); 43 | } 44 | 45 | public static Value Min(Interpreter interpreter, List args) 46 | { 47 | if (args.Count < 2) 48 | throw new ArgumentException(); 49 | 50 | return new Value(Math.Min(args[0].Real, args[1].Real)); 51 | } 52 | 53 | public static Value Max(Interpreter interpreter, List args) 54 | { 55 | if (args.Count < 1) 56 | throw new ArgumentException(); 57 | 58 | return new Value(Math.Max(args[0].Real, args[1].Real)); 59 | } 60 | 61 | public static Value Not(Interpreter interpreter, List args) 62 | { 63 | if (args.Count < 1) 64 | throw new ArgumentException(); 65 | 66 | return new Value(args[0].Real == 0 ? 1 : 0); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /BasicSharp/BasicSharp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | 10.0.0 7 | 2.0 8 | {EE73A059-52E2-4E50-B9EB-115C9A0A4AF6} 9 | Library 10 | BasicSharp 11 | BasicSharp 12 | 13 | 14 | v4.5 15 | 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug 22 | DEBUG; 23 | prompt 24 | 4 25 | true 26 | x86 27 | ../../test.bas 28 | false 29 | 30 | 31 | full 32 | true 33 | bin\Release 34 | prompt 35 | 4 36 | true 37 | x86 38 | false 39 | 40 | 41 | true 42 | bin\Debug\ 43 | DEBUG; 44 | full 45 | AnyCPU 46 | prompt 47 | MinimumRecommendedRules.ruleset 48 | 49 | 50 | bin\Release\ 51 | true 52 | full 53 | AnyCPU 54 | prompt 55 | MinimumRecommendedRules.ruleset 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /BasicSharp.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.421 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BasicSharp", "BasicSharp\BasicSharp.csproj", "{EE73A059-52E2-4E50-B9EB-115C9A0A4AF6}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BasicSharp.Test", "BasicSharp.Test\BasicSharp.Test.csproj", "{413B4EE1-BE77-4F68-8190-FD750590123A}" 9 | ProjectSection(ProjectDependencies) = postProject 10 | {EE73A059-52E2-4E50-B9EB-115C9A0A4AF6} = {EE73A059-52E2-4E50-B9EB-115C9A0A4AF6} 11 | EndProjectSection 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|Any CPU = Debug|Any CPU 16 | Debug|Mixed Platforms = Debug|Mixed Platforms 17 | Debug|x86 = Debug|x86 18 | Release|Any CPU = Release|Any CPU 19 | Release|Mixed Platforms = Release|Mixed Platforms 20 | Release|x86 = Release|x86 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {EE73A059-52E2-4E50-B9EB-115C9A0A4AF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {EE73A059-52E2-4E50-B9EB-115C9A0A4AF6}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {EE73A059-52E2-4E50-B9EB-115C9A0A4AF6}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 26 | {EE73A059-52E2-4E50-B9EB-115C9A0A4AF6}.Debug|Mixed Platforms.Build.0 = Debug|x86 27 | {EE73A059-52E2-4E50-B9EB-115C9A0A4AF6}.Debug|x86.ActiveCfg = Debug|x86 28 | {EE73A059-52E2-4E50-B9EB-115C9A0A4AF6}.Debug|x86.Build.0 = Debug|x86 29 | {EE73A059-52E2-4E50-B9EB-115C9A0A4AF6}.Release|Any CPU.ActiveCfg = Release|x86 30 | {EE73A059-52E2-4E50-B9EB-115C9A0A4AF6}.Release|Mixed Platforms.ActiveCfg = Release|x86 31 | {EE73A059-52E2-4E50-B9EB-115C9A0A4AF6}.Release|Mixed Platforms.Build.0 = Release|x86 32 | {EE73A059-52E2-4E50-B9EB-115C9A0A4AF6}.Release|x86.ActiveCfg = Release|x86 33 | {EE73A059-52E2-4E50-B9EB-115C9A0A4AF6}.Release|x86.Build.0 = Release|x86 34 | {413B4EE1-BE77-4F68-8190-FD750590123A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {413B4EE1-BE77-4F68-8190-FD750590123A}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {413B4EE1-BE77-4F68-8190-FD750590123A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 37 | {413B4EE1-BE77-4F68-8190-FD750590123A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 38 | {413B4EE1-BE77-4F68-8190-FD750590123A}.Debug|x86.ActiveCfg = Debug|Any CPU 39 | {413B4EE1-BE77-4F68-8190-FD750590123A}.Release|Any CPU.ActiveCfg = Release|Any CPU 40 | {413B4EE1-BE77-4F68-8190-FD750590123A}.Release|Any CPU.Build.0 = Release|Any CPU 41 | {413B4EE1-BE77-4F68-8190-FD750590123A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 42 | {413B4EE1-BE77-4F68-8190-FD750590123A}.Release|Mixed Platforms.Build.0 = Release|Any CPU 43 | {413B4EE1-BE77-4F68-8190-FD750590123A}.Release|x86.ActiveCfg = Release|Any CPU 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | GlobalSection(ExtensibilityGlobals) = postSolution 49 | SolutionGuid = {27D9D8AC-E8A6-4CCE-819D-E44E0235BB64} 50 | EndGlobalSection 51 | GlobalSection(MonoDevelopProperties) = preSolution 52 | StartupItem = BasicSharp.Test\BasicSharp.Test.csproj 53 | EndGlobalSection 54 | EndGlobal 55 | -------------------------------------------------------------------------------- /BasicSharp.Test/BasicSharp.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {413B4EE1-BE77-4F68-8190-FD750590123A} 8 | Exe 9 | Properties 10 | BasicSharp.Test 11 | BasicSharp.Test 12 | v4.5 13 | 512 14 | publish\ 15 | true 16 | Disk 17 | false 18 | Foreground 19 | 7 20 | Days 21 | false 22 | false 23 | true 24 | 0 25 | 1.0.0.%2a 26 | false 27 | false 28 | true 29 | 30 | 31 | AnyCPU 32 | true 33 | full 34 | false 35 | bin\Debug\ 36 | DEBUG;TRACE 37 | prompt 38 | 4 39 | 40 | 41 | AnyCPU 42 | pdbonly 43 | true 44 | bin\Release\ 45 | TRACE 46 | prompt 47 | 4 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | False 68 | Microsoft .NET Framework 4.5 %28x86 and x64%29 69 | true 70 | 71 | 72 | False 73 | .NET Framework 3.5 SP1 Client Profile 74 | false 75 | 76 | 77 | False 78 | .NET Framework 3.5 SP1 79 | false 80 | 81 | 82 | 83 | 84 | {ee73a059-52e2-4e50-b9eb-115c9a0a4af6} 85 | BasicSharp 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 102 | -------------------------------------------------------------------------------- /BasicSharp/Value.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BasicSharp 4 | { 5 | public enum ValueType 6 | { 7 | Real, // it's double 8 | String 9 | } 10 | 11 | public struct Value 12 | { 13 | public static readonly Value Zero = new Value(0); 14 | public ValueType Type { get; set; } 15 | 16 | public double Real { get; set; } 17 | public string String { get; set; } 18 | 19 | public Value(double real) : this() 20 | { 21 | this.Type = ValueType.Real; 22 | this.Real = real; 23 | } 24 | 25 | public Value(string str) 26 | : this() 27 | { 28 | this.Type = ValueType.String; 29 | this.String = str; 30 | } 31 | 32 | public Value Convert(ValueType type) 33 | { 34 | if (this.Type != type) 35 | { 36 | switch (type) 37 | { 38 | case ValueType.Real: 39 | this.Real = double.Parse(this.String); 40 | this.Type = ValueType.Real; 41 | break; 42 | case ValueType.String: 43 | this.String = this.Real.ToString(); 44 | this.Type = ValueType.String; 45 | break; 46 | } 47 | } 48 | return this; 49 | } 50 | 51 | public Value UnaryOp(Token tok) 52 | { 53 | if (Type != ValueType.Real) 54 | { 55 | throw new Exception("Can only do unary operations on numbers."); 56 | } 57 | 58 | switch (tok) 59 | { 60 | case Token.Plus: return this; 61 | case Token.Minus: return new Value(-Real); 62 | case Token.Not: return new Value(Real == 0 ? 1 : 0); 63 | } 64 | 65 | throw new Exception("Unknown unary operator."); 66 | } 67 | 68 | public Value BinOp(Value b, Token tok) 69 | { 70 | Value a = this; 71 | if (a.Type != b.Type) 72 | { 73 | // promote one value to higher type 74 | if (a.Type > b.Type) 75 | b = b.Convert(a.Type); 76 | else 77 | a = a.Convert(b.Type); 78 | } 79 | 80 | if (tok == Token.Plus) 81 | { 82 | if (a.Type == ValueType.Real) 83 | return new Value(a.Real + b.Real); 84 | else 85 | return new Value(a.String + b.String); 86 | } 87 | else if(tok == Token.Equal) 88 | { 89 | if (a.Type == ValueType.Real) 90 | return new Value(a.Real == b.Real ? 1 : 0); 91 | else 92 | return new Value(a.String == b.String ? 1 : 0); 93 | } 94 | else if(tok == Token.NotEqual) 95 | { 96 | if (a.Type == ValueType.Real) 97 | return new Value(a.Real == b.Real ? 0 : 1); 98 | else 99 | return new Value(a.String == b.String ? 0 : 1); 100 | } 101 | else 102 | { 103 | if (a.Type == ValueType.String) 104 | throw new Exception("Cannot do binop on strings(except +)."); 105 | 106 | switch (tok) 107 | { 108 | case Token.Minus: return new Value(a.Real - b.Real); 109 | case Token.Asterisk: return new Value(a.Real * b.Real); 110 | case Token.Slash: return new Value(a.Real / b.Real); 111 | case Token.Caret: return new Value(Math.Pow(a.Real, b.Real)); 112 | case Token.Less: return new Value(a.Real < b.Real ? 1 : 0); 113 | case Token.More: return new Value(a.Real > b.Real ? 1 : 0); 114 | case Token.LessEqual: return new Value(a.Real <= b.Real ? 1 : 0); 115 | case Token.MoreEqual: return new Value(a.Real >= b.Real ? 1 : 0); 116 | case Token.And: return new Value((a.Real != 0) && (b.Real != 0) ? 1 : 0); 117 | case Token.Or: return new Value((a.Real != 0) || (b.Real != 0) ? 1 : 0); 118 | } 119 | } 120 | 121 | throw new Exception("Unknown binary operator."); 122 | } 123 | 124 | public override string ToString() 125 | { 126 | if (this.Type == ValueType.Real) 127 | return this.Real.ToString(); 128 | return this.String; 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | *.dll 10 | #.exe 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # DNX 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | 50 | *_i.c 51 | *_p.c 52 | *_i.h 53 | *.ilk 54 | *.meta 55 | *.obj 56 | *.pch 57 | *.pdb 58 | *.pgc 59 | *.pgd 60 | *.rsp 61 | *.sbr 62 | *.tlb 63 | *.tli 64 | *.tlh 65 | *.tmp 66 | *.tmp_proj 67 | *.log 68 | *.vspscc 69 | *.vssscc 70 | .builds 71 | *.pidb 72 | *.svclog 73 | *.scc 74 | 75 | # Chutzpah Test files 76 | _Chutzpah* 77 | 78 | # Visual C++ cache files 79 | ipch/ 80 | *.aps 81 | *.ncb 82 | *.opendb 83 | *.opensdf 84 | *.sdf 85 | *.cachefile 86 | *.VC.db 87 | *.VC.VC.opendb 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | *.sap 94 | 95 | # TFS 2012 Local Workspace 96 | $tf/ 97 | 98 | # Guidance Automation Toolkit 99 | *.gpState 100 | 101 | # ReSharper is a .NET coding add-in 102 | _ReSharper*/ 103 | *.[Rr]e[Ss]harper 104 | *.DotSettings.user 105 | 106 | # JustCode is a .NET coding add-in 107 | .JustCode 108 | 109 | # TeamCity is a build add-in 110 | _TeamCity* 111 | 112 | # DotCover is a Code Coverage Tool 113 | *.dotCover 114 | 115 | # NCrunch 116 | _NCrunch_* 117 | .*crunch*.local.xml 118 | nCrunchTemp_* 119 | 120 | # MightyMoose 121 | *.mm.* 122 | AutoTest.Net/ 123 | 124 | # Web workbench (sass) 125 | .sass-cache/ 126 | 127 | # Installshield output folder 128 | [Ee]xpress/ 129 | 130 | # DocProject is a documentation generator add-in 131 | DocProject/buildhelp/ 132 | DocProject/Help/*.HxT 133 | DocProject/Help/*.HxC 134 | DocProject/Help/*.hhc 135 | DocProject/Help/*.hhk 136 | DocProject/Help/*.hhp 137 | DocProject/Help/Html2 138 | DocProject/Help/html 139 | 140 | # Click-Once directory 141 | publish/ 142 | 143 | # Publish Web Output 144 | *.[Pp]ublish.xml 145 | *.azurePubxml 146 | # TODO: Comment the next line if you want to checkin your web deploy settings 147 | # but database connection strings (with potential passwords) will be unencrypted 148 | *.pubxml 149 | *.publishproj 150 | 151 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 152 | # checkin your Azure Web App publish settings, but sensitive information contained 153 | # in these scripts will be unencrypted 154 | PublishScripts/ 155 | 156 | # NuGet Packages 157 | *.nupkg 158 | # The packages folder can be ignored because of Package Restore 159 | **/packages/* 160 | # except build/, which is used as an MSBuild target. 161 | !**/packages/build/ 162 | # Uncomment if necessary however generally it will be regenerated when needed 163 | #!**/packages/repositories.config 164 | # NuGet v3's project.json files produces more ignoreable files 165 | *.nuget.props 166 | *.nuget.targets 167 | 168 | # Microsoft Azure Build Output 169 | csx/ 170 | *.build.csdef 171 | 172 | # Microsoft Azure Emulator 173 | ecf/ 174 | rcf/ 175 | 176 | # Windows Store app package directories and files 177 | AppPackages/ 178 | BundleArtifacts/ 179 | Package.StoreAssociation.xml 180 | _pkginfo.txt 181 | 182 | # Visual Studio cache files 183 | # files ending in .cache can be ignored 184 | *.[Cc]ache 185 | # but keep track of directories ending in .cache 186 | !*.[Cc]ache/ 187 | 188 | # Others 189 | ClientBin/ 190 | ~$* 191 | *~ 192 | *.dbmdl 193 | *.dbproj.schemaview 194 | *.pfx 195 | *.publishsettings 196 | node_modules/ 197 | orleans.codegen.cs 198 | 199 | # Since there are multiple workflows, uncomment next line to ignore bower_components 200 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 201 | #bower_components/ 202 | 203 | # RIA/Silverlight projects 204 | Generated_Code/ 205 | 206 | # Backup & report files from converting an old project file 207 | # to a newer Visual Studio version. Backup files are not needed, 208 | # because we have git ;-) 209 | _UpgradeReport_Files/ 210 | Backup*/ 211 | UpgradeLog*.XML 212 | UpgradeLog*.htm 213 | 214 | # SQL Server files 215 | *.mdf 216 | *.ldf 217 | 218 | # Business Intelligence projects 219 | *.rdl.data 220 | *.bim.layout 221 | *.bim_*.settings 222 | 223 | # Microsoft Fakes 224 | FakesAssemblies/ 225 | 226 | # GhostDoc plugin setting file 227 | *.GhostDoc.xml 228 | 229 | # Node.js Tools for Visual Studio 230 | .ntvs_analysis.dat 231 | 232 | # Visual Studio 6 build log 233 | *.plg 234 | 235 | # Visual Studio 6 workspace options file 236 | *.opt 237 | 238 | # Visual Studio LightSwitch build output 239 | **/*.HTMLClient/GeneratedArtifacts 240 | **/*.DesktopClient/GeneratedArtifacts 241 | **/*.DesktopClient/ModelManifest.xml 242 | **/*.Server/GeneratedArtifacts 243 | **/*.Server/ModelManifest.xml 244 | _Pvt_Extensions 245 | 246 | # Paket dependency manager 247 | .paket/paket.exe 248 | paket-files/ 249 | 250 | # FAKE - F# Make 251 | .fake/ 252 | 253 | # JetBrains Rider 254 | .idea/ 255 | *.sln.iml 256 | -------------------------------------------------------------------------------- /BasicSharp/Lexer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BasicSharp 4 | { 5 | public class Lexer 6 | { 7 | private readonly string source; 8 | private Marker sourceMarker; // current position in source string 9 | private char lastChar; 10 | 11 | public Marker TokenMarker { get; set; } 12 | 13 | public string Identifier { get; set; } // Last encountered identifier 14 | public Value Value { get; set; } // Last number or string 15 | 16 | public Lexer(string input) 17 | { 18 | source = input; 19 | sourceMarker = new Marker(0, 1, 1); 20 | lastChar = source[0]; 21 | } 22 | 23 | public void GoTo(Marker marker) 24 | { 25 | sourceMarker = marker; 26 | } 27 | 28 | public string GetLine(Marker marker) 29 | { 30 | Marker oldMarker = sourceMarker; 31 | marker.Pointer--; 32 | GoTo(marker); 33 | 34 | string line = ""; 35 | do 36 | { 37 | line += GetChar(); 38 | } while (lastChar != '\n' && lastChar != (char)0); 39 | 40 | line.Remove(line.Length - 1); 41 | 42 | GoTo(oldMarker); 43 | 44 | return line; 45 | } 46 | 47 | char GetChar() 48 | { 49 | sourceMarker.Column++; 50 | sourceMarker.Pointer++; 51 | 52 | if (sourceMarker.Pointer >= source.Length) 53 | return lastChar = (char)0; 54 | 55 | if ((lastChar = source[sourceMarker.Pointer]) == '\n') 56 | { 57 | sourceMarker.Column = 1; 58 | sourceMarker.Line++; 59 | } 60 | return lastChar; 61 | } 62 | 63 | public Token GetToken() 64 | { 65 | // skip white chars 66 | while (lastChar == ' ' || lastChar == '\t' || lastChar == '\r') 67 | GetChar(); 68 | 69 | TokenMarker = sourceMarker; 70 | 71 | if (char.IsLetter(lastChar)) 72 | { 73 | Identifier = lastChar.ToString(); 74 | while (char.IsLetterOrDigit(GetChar())) 75 | Identifier += lastChar; 76 | 77 | switch (Identifier.ToUpper()) 78 | { 79 | case "PRINT": return Token.Print; 80 | case "IF": return Token.If; 81 | case "ENDIF": return Token.EndIf; 82 | case "THEN": return Token.Then; 83 | case "ELSE": return Token.Else; 84 | case "FOR": return Token.For; 85 | case "TO": return Token.To; 86 | case "NEXT": return Token.Next; 87 | case "GOTO": return Token.Goto; 88 | case "INPUT": return Token.Input; 89 | case "LET": return Token.Let; 90 | case "GOSUB": return Token.Gosub; 91 | case "RETURN": return Token.Return; 92 | case "END": return Token.End; 93 | case "OR": return Token.Or; 94 | case "AND": return Token.And; 95 | case "NOT": return Token.Not; 96 | case "ASSERT": return Token.Assert; 97 | case "REM": 98 | while (lastChar != '\n') GetChar(); 99 | GetChar(); 100 | return GetToken(); 101 | default: 102 | return Token.Identifier; 103 | } 104 | } 105 | 106 | if (char.IsDigit(lastChar)) 107 | { 108 | string num = ""; 109 | do { num += lastChar; } while (char.IsDigit(GetChar()) || lastChar == '.'); 110 | 111 | double real; 112 | if (!double.TryParse(num, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out real)) 113 | throw new Exception("ERROR while parsing number"); 114 | Value = new Value(real); 115 | return Token.Value; 116 | } 117 | 118 | Token tok = Token.Unknown; 119 | switch (lastChar) 120 | { 121 | case '\n': tok = Token.NewLine; break; 122 | case ':': tok = Token.Colon; break; 123 | case ';': tok = Token.Semicolon; break; 124 | case ',': tok = Token.Comma; break; 125 | case '=': tok = Token.Equal; break; 126 | case '+': tok = Token.Plus; break; 127 | case '-': tok = Token.Minus; break; 128 | case '/': tok = Token.Slash; break; 129 | case '*': tok = Token.Asterisk; break; 130 | case '^': tok = Token.Caret; break; 131 | case '(': tok = Token.LParen; break; 132 | case ')': tok = Token.RParen; break; 133 | case '\'': 134 | // skip comment until new line 135 | while (lastChar != '\n') GetChar(); 136 | GetChar(); 137 | return GetToken(); 138 | case '<': 139 | GetChar(); 140 | if (lastChar == '>') tok = Token.NotEqual; 141 | else if (lastChar == '=') tok = Token.LessEqual; 142 | else return Token.Less; 143 | break; 144 | case '>': 145 | GetChar(); 146 | if (lastChar == '=') tok = Token.MoreEqual; 147 | else return Token.More; 148 | break; 149 | case '"': 150 | string str = ""; 151 | while (GetChar() != '"') 152 | { 153 | if (lastChar == '\\') 154 | { 155 | // parse \n, \t, \\, \" 156 | switch (char.ToLower(GetChar())) 157 | { 158 | case 'n': str += '\n'; break; 159 | case 't': str += '\t'; break; 160 | case '\\': str += '\\'; break; 161 | case '"': str += '"'; break; 162 | } 163 | } 164 | else 165 | { 166 | str += lastChar; 167 | } 168 | } 169 | Value = new Value(str); 170 | tok = Token.Value; 171 | break; 172 | case (char)0: 173 | return Token.EOF; 174 | } 175 | 176 | GetChar(); 177 | return tok; 178 | } 179 | } 180 | } 181 | 182 | -------------------------------------------------------------------------------- /BasicSharp/Interpreter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace BasicSharp 5 | { 6 | public class Interpreter 7 | { 8 | public delegate void PrintFunction(string text); 9 | public delegate string InputFunction(); 10 | 11 | public PrintFunction printHandler; 12 | public InputFunction inputHandler; 13 | 14 | private Lexer lex; 15 | private Token prevToken; // token before last one 16 | private Token lastToken; // last seen token 17 | 18 | private Dictionary vars; // all variables are stored here 19 | private Dictionary labels; // already seen labels 20 | private Dictionary loops; // for loops 21 | 22 | public delegate Value BasicFunction(Interpreter interpreter, List args); 23 | private Dictionary funcs; // all maped functions 24 | 25 | private int ifcounter; // counter used for matching "if" with "else" 26 | 27 | private Marker lineMarker; // current line marker 28 | 29 | private bool exit; // do we need to exit? 30 | 31 | public Interpreter(string input) 32 | { 33 | this.lex = new Lexer(input); 34 | this.vars = new Dictionary(); 35 | this.labels = new Dictionary(); 36 | this.loops = new Dictionary(); 37 | this.funcs = new Dictionary(); 38 | this.ifcounter = 0; 39 | BuiltIns.InstallAll(this); // map all builtins functions 40 | } 41 | 42 | public Value GetVar(string name) 43 | { 44 | if (!vars.ContainsKey(name)) 45 | throw new BasicException("Variable with name " + name + " does not exist.", lineMarker.Line); 46 | return vars[name]; 47 | } 48 | 49 | public void SetVar(string name, Value val) 50 | { 51 | if (!vars.ContainsKey(name)) vars.Add(name, val); 52 | else vars[name] = val; 53 | } 54 | 55 | public string GetLine() 56 | { 57 | return lex.GetLine(lineMarker); 58 | } 59 | 60 | public void AddFunction(string name, BasicFunction function) 61 | { 62 | if (!funcs.ContainsKey(name)) funcs.Add(name, function); 63 | else funcs[name] = function; 64 | } 65 | 66 | void Error(string text) 67 | { 68 | throw new BasicException(text, lineMarker.Line); 69 | } 70 | 71 | void Match(Token tok) 72 | { 73 | // check if current token is what we expect it to be 74 | if (lastToken != tok) 75 | Error("Expect " + tok.ToString() + " got " + lastToken.ToString()); 76 | } 77 | 78 | public void Exec() 79 | { 80 | exit = false; 81 | GetNextToken(); 82 | while (!exit) Line(); // do all lines 83 | } 84 | 85 | Token GetNextToken() 86 | { 87 | prevToken = lastToken; 88 | lastToken = lex.GetToken(); 89 | 90 | if (lastToken == Token.EOF && prevToken == Token.EOF) 91 | Error("Unexpected end of file"); 92 | 93 | return lastToken; 94 | } 95 | 96 | void Line() 97 | { 98 | // skip empty new lines 99 | while (lastToken == Token.NewLine) GetNextToken(); 100 | 101 | if (lastToken == Token.EOF) 102 | { 103 | exit = true; 104 | return; 105 | } 106 | 107 | lineMarker = lex.TokenMarker; // save current line marker 108 | Statment(); // evaluate statment 109 | 110 | if (lastToken != Token.NewLine && lastToken != Token.EOF) 111 | Error("Expect new line got " + lastToken.ToString()); 112 | } 113 | 114 | void Statment() 115 | { 116 | Token keyword = lastToken; 117 | GetNextToken(); 118 | switch (keyword) 119 | { 120 | case Token.Print: Print(); break; 121 | case Token.Input: Input(); break; 122 | case Token.Goto: Goto(); break; 123 | case Token.If: If(); break; 124 | case Token.Else: Else(); break; 125 | case Token.EndIf: break; 126 | case Token.For: For(); break; 127 | case Token.Next: Next(); break; 128 | case Token.Let: Let(); break; 129 | case Token.End: End(); break; 130 | case Token.Assert: Assert(); break; 131 | case Token.Identifier: 132 | if (lastToken == Token.Equal) Let(); 133 | else if (lastToken == Token.Colon) Label(); 134 | else goto default; 135 | break; 136 | case Token.EOF: 137 | exit = true; 138 | break; 139 | default: 140 | Error("Expect keyword got " + keyword.ToString()); 141 | break; 142 | } 143 | if (lastToken == Token.Colon) 144 | { 145 | // we can execute more statments in single line if we use ";" 146 | GetNextToken(); 147 | Statment(); 148 | } 149 | } 150 | 151 | void Print() 152 | { 153 | printHandler?.Invoke(Expr().ToString()); 154 | } 155 | 156 | void Input() 157 | { 158 | while (true) 159 | { 160 | Match(Token.Identifier); 161 | 162 | if (!vars.ContainsKey(lex.Identifier)) vars.Add(lex.Identifier, new Value()); 163 | 164 | string input = inputHandler?.Invoke(); 165 | double d; 166 | // try to parse as double, if failed read value as string 167 | if (double.TryParse(input, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out d)) 168 | vars[lex.Identifier] = new Value(d); 169 | else 170 | vars[lex.Identifier] = new Value(input); 171 | 172 | GetNextToken(); 173 | if (lastToken != Token.Comma) break; 174 | GetNextToken(); 175 | } 176 | } 177 | 178 | void Goto() 179 | { 180 | Match(Token.Identifier); 181 | string name = lex.Identifier; 182 | 183 | if (!labels.ContainsKey(name)) 184 | { 185 | // if we didn't encaunter required label yet, start to search for it 186 | while (true) 187 | { 188 | if (GetNextToken() == Token.Colon && prevToken == Token.Identifier) 189 | { 190 | if (!labels.ContainsKey(lex.Identifier)) 191 | labels.Add(lex.Identifier, lex.TokenMarker); 192 | if (lex.Identifier == name) 193 | break; 194 | } 195 | if (lastToken == Token.EOF) 196 | { 197 | Error("Cannot find label named " + name); 198 | } 199 | } 200 | } 201 | lex.GoTo(labels[name]); 202 | lastToken = Token.NewLine; 203 | } 204 | 205 | void If() 206 | { 207 | // check if argument is equal to 0 208 | bool result = (Expr().BinOp(new Value(0), Token.Equal).Real == 1); 209 | 210 | Match(Token.Then); 211 | GetNextToken(); 212 | 213 | if (result) 214 | { 215 | // in case "if" evaulate to zero skip to matching else or endif 216 | int i = ifcounter; 217 | while (true) 218 | { 219 | if (lastToken == Token.If) 220 | { 221 | i++; 222 | } 223 | else if (lastToken == Token.Else) 224 | { 225 | if (i == ifcounter) 226 | { 227 | GetNextToken(); 228 | return; 229 | } 230 | } 231 | else if (lastToken == Token.EndIf) 232 | { 233 | if (i == ifcounter) 234 | { 235 | GetNextToken(); 236 | return; 237 | } 238 | i--; 239 | } 240 | GetNextToken(); 241 | } 242 | } 243 | } 244 | 245 | void Else() 246 | { 247 | // skip to matching endif 248 | int i = ifcounter; 249 | while (true) 250 | { 251 | if (lastToken == Token.If) 252 | { 253 | i++; 254 | } 255 | else if (lastToken == Token.EndIf) 256 | { 257 | if (i == ifcounter) 258 | { 259 | GetNextToken(); 260 | return; 261 | } 262 | i--; 263 | } 264 | GetNextToken(); 265 | } 266 | } 267 | 268 | void Label() 269 | { 270 | string name = lex.Identifier; 271 | if (!labels.ContainsKey(name)) labels.Add(name, lex.TokenMarker); 272 | 273 | GetNextToken(); 274 | Match(Token.NewLine); 275 | } 276 | 277 | void End() 278 | { 279 | exit = true; 280 | } 281 | 282 | void Let() 283 | { 284 | if (lastToken != Token.Equal) 285 | { 286 | Match(Token.Identifier); 287 | GetNextToken(); 288 | Match(Token.Equal); 289 | } 290 | 291 | string id = lex.Identifier; 292 | 293 | GetNextToken(); 294 | 295 | SetVar(id, Expr()); 296 | } 297 | 298 | void For() 299 | { 300 | Match(Token.Identifier); 301 | string var = lex.Identifier; 302 | 303 | GetNextToken(); 304 | Match(Token.Equal); 305 | 306 | GetNextToken(); 307 | Value v = Expr(); 308 | 309 | // save for loop marker 310 | if (loops.ContainsKey(var)) 311 | { 312 | loops[var] = lineMarker; 313 | } 314 | else 315 | { 316 | SetVar(var, v); 317 | loops.Add(var, lineMarker); 318 | } 319 | 320 | Match(Token.To); 321 | 322 | GetNextToken(); 323 | v = Expr(); 324 | 325 | if (vars[var].BinOp(v, Token.More).Real == 1) 326 | { 327 | while (true) 328 | { 329 | while (!(GetNextToken() == Token.Identifier && prevToken == Token.Next)) ; 330 | if (lex.Identifier == var) 331 | { 332 | loops.Remove(var); 333 | GetNextToken(); 334 | Match(Token.NewLine); 335 | break; 336 | } 337 | } 338 | } 339 | } 340 | 341 | void Next() 342 | { 343 | // jump to begining of the "for" loop 344 | Match(Token.Identifier); 345 | string var = lex.Identifier; 346 | vars[var] = vars[var].BinOp(new Value(1), Token.Plus); 347 | lex.GoTo(new Marker(loops[var].Pointer - 1, loops[var].Line, loops[var].Column - 1)); 348 | lastToken = Token.NewLine; 349 | } 350 | 351 | void Assert() 352 | { 353 | bool result = (Expr().BinOp(new Value(0), Token.Equal).Real == 1); 354 | 355 | if (result) 356 | { 357 | Error("Assertion fault"); // if out assert evaluate to false, throw error with souce code line 358 | } 359 | } 360 | 361 | Value Expr(int min = 0) 362 | { 363 | // originally we were using shunting-yard algorithm, but now we parse it recursively 364 | Dictionary precedens = new Dictionary() 365 | { 366 | { Token.Or, 0 }, { Token.And, 0 }, 367 | { Token.Equal, 1 }, { Token.NotEqual, 1 }, 368 | { Token.Less, 1 }, { Token.More, 1 }, 369 | { Token.LessEqual, 1 }, { Token.MoreEqual, 1 }, 370 | { Token.Plus, 2 }, { Token.Minus, 2 }, 371 | { Token.Asterisk, 3 }, {Token.Slash, 3 }, 372 | { Token.Caret, 4 } 373 | }; 374 | 375 | Value lhs = Primary(); 376 | 377 | while (true) 378 | { 379 | if (lastToken < Token.Plus || lastToken > Token.And || precedens[lastToken] < min) 380 | break; 381 | 382 | Token op = lastToken; 383 | int prec = precedens[lastToken]; // Operator Precedence 384 | int assoc = 0; // 0 left, 1 right; Operator associativity 385 | int nextmin = assoc == 0 ? prec : prec + 1; 386 | GetNextToken(); 387 | Value rhs = Expr(nextmin); 388 | lhs = lhs.BinOp(rhs, op); 389 | } 390 | 391 | return lhs; 392 | } 393 | 394 | Value Primary() 395 | { 396 | Value prim = Value.Zero; 397 | 398 | if (lastToken == Token.Value) 399 | { 400 | // number | string 401 | prim = lex.Value; 402 | GetNextToken(); 403 | } 404 | else if (lastToken == Token.Identifier) 405 | { 406 | // ident | ident '(' args ')' 407 | if (vars.ContainsKey(lex.Identifier)) 408 | { 409 | prim = vars[lex.Identifier]; 410 | } 411 | else if (funcs.ContainsKey(lex.Identifier)) 412 | { 413 | string name = lex.Identifier; 414 | List args = new List(); 415 | GetNextToken(); 416 | Match(Token.LParen); 417 | 418 | start: 419 | if (GetNextToken() != Token.RParen) 420 | { 421 | args.Add(Expr()); 422 | if (lastToken == Token.Comma) 423 | goto start; 424 | } 425 | 426 | prim = funcs[name](null, args); 427 | } 428 | else 429 | { 430 | Error("Undeclared variable " + lex.Identifier); 431 | } 432 | GetNextToken(); 433 | } 434 | else if (lastToken == Token.LParen) 435 | { 436 | // '(' expr ')' 437 | GetNextToken(); 438 | prim = Expr(); 439 | Match(Token.RParen); 440 | GetNextToken(); 441 | } 442 | else if (lastToken == Token.Plus || lastToken == Token.Minus || lastToken == Token.Not) 443 | { 444 | // unary operator 445 | // '-' | '+' primary 446 | Token op = lastToken; 447 | GetNextToken(); 448 | prim = Primary().UnaryOp(op); 449 | } 450 | else 451 | { 452 | Error("Unexpexted token in primary!"); 453 | } 454 | 455 | return prim; 456 | } 457 | } 458 | } 459 | --------------------------------------------------------------------------------