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