| @ -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(); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @ -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>(); | |||
| } | |||
| } | |||
| } | |||
| @ -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(); | |||
| } | |||
| } | |||
| } | |||
| @ -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; | |||
| } | |||
| } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Text; | |||
| namespace SuperBASIC | |||
| { | |||
| interface IFunction | |||
| { | |||
| public float Apply(List<BasicNumber> arguments); | |||
| } | |||
| } | |||
| @ -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; | |||
| } | |||
| } | |||
| } | |||
| @ -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; | |||
| } | |||
| } | |||
| } | |||
| @ -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(); | |||
| } | |||
| } | |||
| } | |||
| @ -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; | |||
| } | |||
| } | |||
| } | |||
| @ -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> | |||
| @ -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 | |||
| @ -0,0 +1,2 @@ | |||
| MULTIPLY 2 3 | |||
| PRINT $ | |||