├── 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 |
--------------------------------------------------------------------------------