@ -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 $ |