diff --git a/BasicNumber.cs b/BasicNumber.cs new file mode 100644 index 0000000..0ab63c4 --- /dev/null +++ b/BasicNumber.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SuperBASIC +{ + enum NumberType{ + Ans, + Number, + Operand + }; + + struct BasicNumber + { + internal NumberType type; + + readonly Runtime runtime; + readonly private float number; + readonly private int operand; + + internal BasicNumber(Runtime rt, float v) + { + type = NumberType.Number; + number = v; + operand = 0; + runtime = rt; + } + internal BasicNumber(Runtime rt) + { + type = NumberType.Ans; + number = 0; + operand = 0; + runtime = rt; + } + internal BasicNumber(Runtime rt, int v) + { + type = NumberType.Operand; + number = 0; + operand = v; + runtime = rt; + } + + internal int GetOperand() + { + return operand; + } + + public float GetValue() + { + if (type == NumberType.Number) + { + return number; + } + else + { + return runtime.GetRegister(); + } + } + } +} diff --git a/Bytecode.cs b/Bytecode.cs new file mode 100644 index 0000000..f69a8ba --- /dev/null +++ b/Bytecode.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SuperBASIC +{ + class Bytecode + { + internal List<BasicNumber> bytecode; + + internal Bytecode() + { + bytecode = new List<BasicNumber>(); + } + } +} diff --git a/Functions/Multiply.cs b/Functions/Multiply.cs new file mode 100644 index 0000000..cbfb8e8 --- /dev/null +++ b/Functions/Multiply.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SuperBASIC.Functions +{ + class Multiply : IFunction + { + float IFunction.Apply(List<BasicNumber> arguments) + { + return arguments[0].GetValue() * arguments[1].GetValue(); + } + } +} diff --git a/Functions/Print.cs b/Functions/Print.cs new file mode 100644 index 0000000..eb359d7 --- /dev/null +++ b/Functions/Print.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SuperBASIC.Functions +{ + class Print : IFunction + { + float IFunction.Apply(List<BasicNumber> arguments) + { + Console.WriteLine(arguments[0].GetValue()); + return 0f; + } + } +} diff --git a/IFunction.cs b/IFunction.cs new file mode 100644 index 0000000..006d617 --- /dev/null +++ b/IFunction.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SuperBASIC +{ + interface IFunction + { + public float Apply(List<BasicNumber> arguments); + } +} diff --git a/Library.cs b/Library.cs new file mode 100644 index 0000000..dee7876 --- /dev/null +++ b/Library.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SuperBASIC +{ + class Library + { + internal List<IFunction> functions; + internal List<int> arities; + internal Dictionary<string, int> nameResolution; + + public Library() + { + functions = new List<IFunction>(); + arities = new List<int>(); + nameResolution = new Dictionary<string, int>(); + } + + public int AddFunction(IFunction fn, int arity, string name) + { + int idx = functions.Count; + functions.Add(fn); + arities.Add(arity); + nameResolution[name] = idx; + return idx; + } + } +} diff --git a/Parser.cs b/Parser.cs new file mode 100644 index 0000000..f0716e3 --- /dev/null +++ b/Parser.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; +using System.Text.RegularExpressions; + +namespace SuperBASIC +{ + class Parser + { + + [Serializable] + public class ParseException : Exception + { + public ParseException() { } + public ParseException(string message) : base(message) { } + public ParseException(string message, Exception inner) : base(message, inner) { } + protected ParseException( + System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + } + + readonly Library library; + readonly Runtime runtime; + public Parser(Runtime rt) + { + runtime = rt; + library = rt.lib; + } + + public Bytecode ParseFile(string filepath) + { + Bytecode c = new Bytecode(); + string[] sourceLines = File.ReadAllLines(filepath); + Regex lws = new Regex(@"\s+"); + Regex leadings = new Regex(@"^\s+"); + Regex trailings = new Regex(@"\s+$"); + List<string> codeLines = new List<string>(); + + foreach (string line in sourceLines) + { + string l = lws.Replace(line, " "); + l = leadings.Replace(l, ""); + l = trailings.Replace(l, ""); + + if(l != String.Empty) + { + codeLines.Add(l); + } + } + + foreach(string line in codeLines) + { + var components = line.Split(' '); + + if(!library.nameResolution.ContainsKey(components[0])) + { + throw new ParseException("Unknown operation \"" + components[0] + "\""); + } + + int opcode = library.nameResolution[components[0]]; + int arity = library.arities[opcode]; + + if(arity != components.Length-1) + { + throw new ParseException("Operation " + components[0] + " was provided with the wrong number of arguments: Expected "+arity.ToString()+" found "+(components.Length-1).ToString()); + } + + c.bytecode.Add(new BasicNumber(runtime, opcode)); + foreach (string elem in components.AsSpan(1)) + { + if (elem != "$") + { + c.bytecode.Add(new BasicNumber(runtime, float.Parse(elem))); + } + else + { + c.bytecode.Add(new BasicNumber(runtime)); + } + } + } + + return c; + } + } +} diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..feff13b --- /dev/null +++ b/Program.cs @@ -0,0 +1,18 @@ +using System; +using System.IO; + +namespace SuperBASIC +{ + class Program + { + static void Main(string[] args) + { + Library lib = new Library(); + lib.AddFunction(new Functions.Print(), 1, "PRINT"); + lib.AddFunction(new Functions.Multiply(), 2, "MULTIPLY"); + Runtime r = new Runtime(lib); + r.OpenFile(Directory.GetCurrentDirectory() + "\\Test.basic"); + r.Run(); + } + } +} diff --git a/Runtime.cs b/Runtime.cs new file mode 100644 index 0000000..65f5f63 --- /dev/null +++ b/Runtime.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SuperBASIC +{ + class Runtime + { + float register; + internal Library lib; + Bytecode code; + + public Runtime(Library library) + { + lib = library; + } + + public void OpenFile(string path) + { + code = new Parser(this).ParseFile(path); + } + + public void Run() + { + for(int idx = 0; idx < code.bytecode.Count;) + { + int opcode = code.bytecode[idx].GetOperand(); + int arity = lib.arities[opcode]; + IFunction op = lib.functions[opcode]; + var args = code.bytecode.GetRange(idx + 1, arity); + SetRegister(op.Apply(args)); + idx += arity + 1; + } + } + + internal void SetRegister(float value) + { + register = value; + } + internal float GetRegister() + { + return register; + } + } +} diff --git a/SuperBASIC.csproj b/SuperBASIC.csproj new file mode 100644 index 0000000..fdb69aa --- /dev/null +++ b/SuperBASIC.csproj @@ -0,0 +1,14 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <OutputType>Exe</OutputType> + <TargetFramework>netcoreapp3.1</TargetFramework> + </PropertyGroup> + + <ItemGroup> + <None Update="Test.basic"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </None> + </ItemGroup> + +</Project> diff --git a/SuperBASIC.sln b/SuperBASIC.sln new file mode 100644 index 0000000..d445c26 --- /dev/null +++ b/SuperBASIC.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30907.101 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperBASIC", "SuperBASIC.csproj", "{51942D92-4C96-4FD9-AA5B-C35661E3B063}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {51942D92-4C96-4FD9-AA5B-C35661E3B063}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {51942D92-4C96-4FD9-AA5B-C35661E3B063}.Debug|Any CPU.Build.0 = Debug|Any CPU + {51942D92-4C96-4FD9-AA5B-C35661E3B063}.Release|Any CPU.ActiveCfg = Release|Any CPU + {51942D92-4C96-4FD9-AA5B-C35661E3B063}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B03097C8-5C88-49D7-BD8F-EF905E9827EA} + EndGlobalSection +EndGlobal diff --git a/Test.basic b/Test.basic new file mode 100644 index 0000000..15dbbd5 --- /dev/null +++ b/Test.basic @@ -0,0 +1,2 @@ +MULTIPLY 2 3 +PRINT $ \ No newline at end of file