real initial commit

This commit is contained in:
Mikhail 2024-01-25 23:37:51 -06:00
parent c1e4dbcf02
commit b3363d3f5d
52 changed files with 2073 additions and 0 deletions

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
*.dll
command_examples.txt
screen.txt
.vscode/
bin/
obj/
Resources/

16
Engine.csproj Normal file
View file

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net7.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Sayers.SDL2.Core" Version="1.0.11" />
</ItemGroup>
</Project>

4
Engine.csproj.user Normal file
View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup />
</Project>

25
Engine.sln Normal file
View file

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.002.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Engine", "Engine.csproj", "{C33BE1C6-5FB0-44EC-935F-9E307963A8DF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C33BE1C6-5FB0-44EC-935F-9E307963A8DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C33BE1C6-5FB0-44EC-935F-9E307963A8DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C33BE1C6-5FB0-44EC-935F-9E307963A8DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C33BE1C6-5FB0-44EC-935F-9E307963A8DF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FA1AF6AE-3E75-40AC-8795-FD3959110D5B}
EndGlobalSection
EndGlobal

51
Game.cs Normal file
View file

@ -0,0 +1,51 @@
using KumiScript.Renderer;
using SDL2;
public class EngineWindow
{
internal ushort width {get; private set;}
internal ushort height {get; private set;}
internal string title {get; private set;}
public EngineWindow(ushort horizontalResolution, ushort verticalResolution, string windowTitle)
{
width = horizontalResolution;
height = verticalResolution;
title = windowTitle;
if (SDL.SDL_Init(SDL.SDL_INIT_VIDEO) < 0)
throw new Exception("SDL2 Video init failed!");
if (SDL_image.IMG_Init(SDL_image.IMG_InitFlags.IMG_INIT_PNG) < 0)
throw new Exception("SDL2 Image init failed!");
}
public void show()
{
SDLWindow window = new SDLWindow(width, height, title);
SDLRenderer renderer = new SDLRenderer(window);
bool mainLoop = true;
SDL.SDL_Event e;
Scene s = new Scene(renderer);
SceneElement background = new SceneElement(new Background("Resources/Backgrounds/test.png", renderer), 0, 0);
SceneElement seiba = new SceneElement(new Sprite("Resources/Sprites/test.png", renderer), 0, 0);
s.AddElement(background);
s.AddElement(seiba);
while (mainLoop)
{
while (SDL.SDL_PollEvent(out e) > 0)
{
switch (e.type)
{
case SDL.SDL_EventType.SDL_QUIT:
mainLoop = false;
break;
}
renderer.Clear();
s.DrawScene();
renderer.SwapBuffers();
}
}
}
}

0
LICENSE.txt Normal file
View file

14
Program.cs Normal file
View file

@ -0,0 +1,14 @@
using KumiScript.Interpreter;
namespace KumiScript {
class KumiScript {
static void Main (string[] args) {
Console.WriteLine(System.IO.Directory.GetCurrentDirectory());
EngineWindow engineWindow = new EngineWindow(800, 600, "KumiScript Unicode");
engineWindow.show();
/*ReadEvalPrintLoop repl = new ReadEvalPrintLoop(Console.OpenStandardInput(), Console.OpenStandardOutput());
repl.Loop();*/
}
}
}

View file

@ -0,0 +1,83 @@
using System.Diagnostics.CodeAnalysis;
namespace KumiScript.Interpreter
{
public class Environment
{
readonly Environment? _outer;
readonly Dictionary<Symbol, Expression> _bindings;
public Environment()
{
_bindings = new Dictionary<Symbol, Expression>(SymbolComparer.GetInstance());
}
public Environment(Environment outer)
{
_bindings = new Dictionary<Symbol, Expression>(SymbolComparer.GetInstance());
_outer = outer;
}
public Expression Lookup(Symbol symbol)
{
Expression? result;
_bindings.TryGetValue(symbol, out result);
if (result is not null)
return result;
if (_outer is null)
throw new InterpreterUnboundSymbolException();
return _outer.Lookup(symbol);
}
public void AddSymbol(Symbol symbol, Expression value)
{
_bindings.Add(symbol, value);
return;
}
public void RedefineSymbol(Symbol symbol, Expression value)
{
if (_bindings.ContainsKey(symbol))
{
_bindings.Remove(symbol);
_bindings.Add(symbol, value);
return;
}
if (_outer is null)
throw new InterpreterUnboundSymbolException(symbol.ToString());
_outer.RedefineSymbol(symbol, value);
}
}
internal class SymbolComparer : IEqualityComparer<Symbol>
{
private static SymbolComparer? _instance;
private SymbolComparer()
{
}
public static SymbolComparer GetInstance()
{
if (_instance is null)
_instance = new SymbolComparer();
return _instance;
}
public bool Equals(Symbol? x, Symbol? y)
{
if (x is not Symbol || y is not Symbol)
return false;
return x.Equals(y);
}
public int GetHashCode([DisallowNull] Symbol obj)
{
return obj.ToString().GetHashCode();
}
}
}

View file

@ -0,0 +1,101 @@
using System.Runtime.Serialization;
namespace KumiScript.Interpreter
{
[Serializable]
internal class InterpreterInvalidApplicationException : Exception //trying to invoke procedure with wrong args
{
public InterpreterInvalidApplicationException()
{
}
public InterpreterInvalidApplicationException(string? message) : base(message)
{
}
public InterpreterInvalidApplicationException(string? message, Exception? innerException) : base(message, innerException)
{
}
protected InterpreterInvalidApplicationException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
internal class InterpreterInvalidInvocationException : Exception //trying to invoke non-procedure as procedure
{
public InterpreterInvalidInvocationException()
{
}
public InterpreterInvalidInvocationException(string? message) : base(message)
{
}
public InterpreterInvalidInvocationException(string? message, Exception? innerException) : base(message, innerException)
{
}
protected InterpreterInvalidInvocationException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
internal class InterpreterUnboundSymbolException : Exception //try to lookup unbound symbol
{
public InterpreterUnboundSymbolException()
{
}
public InterpreterUnboundSymbolException(string? message) : base(message)
{
}
public InterpreterUnboundSymbolException(string? message, Exception? innerException) : base(message, innerException)
{
}
protected InterpreterUnboundSymbolException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
internal class InterpreterInvalidDefinitionException : Exception //broken definition of procedure
{
public InterpreterInvalidDefinitionException()
{
}
public InterpreterInvalidDefinitionException(string? message) : base(message)
{
}
public InterpreterInvalidDefinitionException(string? message, Exception? innerException) : base(message, innerException)
{
}
protected InterpreterInvalidDefinitionException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
internal class InterpreterTypingException : Exception //wrong type of arg
{
public InterpreterTypingException()
{
}
public InterpreterTypingException(string? message) : base(message)
{
}
public InterpreterTypingException(string? message, Exception? innerException) : base(message, innerException)
{
}
protected InterpreterTypingException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}

View file

@ -0,0 +1,267 @@
namespace KumiScript.Interpreter
{
public class LispPrimitives
{
public static Expression Lambda(ListExpression args, Environment env)
{
Expression car = args.Car();
ListExpression rest = args.Cdr();
ListExpression? head = car as ListExpression;
if (head is null)
throw new InterpreterInvalidDefinitionException();
List<Expression> headl = head.GetMembers();
List<Symbol> arg_names = new List<Symbol>();
for (int i = 0; i < headl.Count; i++)
{
SymbolExpression? argi = headl[i] as SymbolExpression;
if (argi is null)
throw new InterpreterInvalidDefinitionException();
arg_names.Add(argi.GetSymbol());
}
Expression body = rest.Car();
return new ProcedureExpression(new LispProcedure(arg_names, body, env));
}
public static NumberExpression Plus(ListExpression args, Environment env)
{
List<Expression> argl = args.EvalMembers(env);
decimal acc = 0;
for (int i = 0; i < argl.Count; i++)
{
NumberExpression? argin = argl[i] as NumberExpression;
if (argin is null)
throw new InterpreterTypingException();
acc += argin.GetValueAsFloat();
}
return NumberFactory.NormalizeFloat(acc);
}
public static NumberExpression Minus(ListExpression args, Environment env)
{
List<Expression> argl = args.EvalMembers(env);
if (argl.Count == 0)
throw new InterpreterInvalidApplicationException();
NumberExpression? argn0 = argl[0] as NumberExpression;
if (argn0 is null)
throw new InterpreterTypingException();
decimal acc = argn0.GetValueAsFloat();
if (argl.Count == 1)
return NumberFactory.NormalizeFloat(1 - acc);
for (int i = 1; i < argl.Count; i++)
{
NumberExpression? argin = argl[i] as NumberExpression;
if (argin is null)
throw new InterpreterTypingException();
acc -= argin.GetValueAsFloat();
}
return NumberFactory.NormalizeFloat(acc);
}
public static NumberExpression Times(ListExpression args, Environment env)
{
List<Expression> argl = args.EvalMembers(env);
if (argl.Count < 2)
throw new InterpreterInvalidInvocationException();
NumberExpression? arg0 = argl[0] as NumberExpression;
if (arg0 is null)
throw new InterpreterTypingException();
decimal f = arg0.GetValueAsFloat();
for (int i = 1; i < argl.Count; i++)
{
NumberExpression? argn = argl[i] as NumberExpression;
if (argn is null)
throw new InterpreterTypingException();
f *= argn.GetValueAsFloat();
}
return NumberFactory.NormalizeFloat(f);
}
public static NumberExpression DividedBy(ListExpression args, Environment env)
{
List<Expression> argl = args.EvalMembers(env);
if (argl.Count != 2)
throw new InterpreterInvalidApplicationException();
NumberExpression? arg0 = argl[0] as NumberExpression;
if (arg0 is null)
throw new InterpreterTypingException();
NumberExpression? arg1 = argl[1] as NumberExpression;
if (arg1 is null)
throw new InterpreterTypingException();
return NumberFactory.NormalizeFloat(arg0.GetValueAsFloat() / arg1.GetValueAsFloat());
}
public static NumberExpression Modulus(ListExpression args, Environment env)
{
List<Expression> argl = args.EvalMembers(env);
if (argl.Count != 2)
throw new InterpreterInvalidApplicationException();
NumberExpression? arg0 = argl[0] as NumberExpression;
if (arg0 is null)
throw new InterpreterTypingException();
NumberExpression? arg1 = argl[1] as NumberExpression;
if (arg1 is null)
throw new InterpreterTypingException();
return NumberFactory.NormalizeFloat(arg0.GetValueAsFloat() % arg1.GetValueAsFloat());
}
public static Expression Quote(ListExpression args, Environment env)
{
List<Expression> argl = args.GetMembers();
if (argl.Count != 1)
throw new InterpreterInvalidApplicationException();
return argl[0];
}
public static Expression Define(ListExpression args, Environment env)
{
List<Expression> argl = args.GetMembers();
if (argl.Count != 2)
throw new InterpreterInvalidApplicationException();
SymbolExpression? binding = argl[0] as SymbolExpression;
if (binding is null)
throw new InterpreterTypingException();
Symbol name = binding.GetSymbol();
Expression definition = argl[1].Eval(env);
env.AddSymbol(name, definition);
return definition;
}
public static Expression Car(ListExpression args, Environment env)
{
List<Expression> argl = args.EvalMembers(env);
if (argl.Count != 1)
throw new InterpreterInvalidApplicationException();
ListExpression? list = argl[0] as ListExpression;
if (list is null)
throw new InterpreterTypingException();
return list.Car();
}
public static ListExpression Cdr(ListExpression args, Environment env)
{
List<Expression> argl = args.EvalMembers(env);
if (argl.Count != 1)
throw new InterpreterInvalidApplicationException();
ListExpression? list = argl[0] as ListExpression;
if (list is null)
throw new InterpreterTypingException();
return list.Cdr();
}
public static ProperListExpression Cons(ListExpression args, Environment env)
{
List<Expression> argl = args.EvalMembers(env);
if (argl.Count != 2)
throw new InterpreterInvalidApplicationException();
Expression car = argl[0];
ListExpression? cdr = argl[1] as ListExpression;
if (cdr is null)
throw new InterpreterTypingException();
return cdr.Cons(car);
}
public static Expression Quit(ListExpression args, Environment env)
{
System.Environment.Exit(0);
return new SymbolExpression(new Symbol("bye"));
}
public static Expression Cond(ListExpression args, Environment env)
{
List<Expression> argl = args.GetMembers();
for (int i = 0; i < argl.Count; i ++)
{
ProperListExpression? conditional = argl[i] as ProperListExpression;
if (conditional is null)
throw new InterpreterTypingException();
Expression antecedent = conditional.Car();
Expression consequent = conditional.Cdr().Car();
if (!(antecedent.Eval(env) is NilExpression))
return consequent.Eval(env);
}
return NilExpression.GetInstance();
}
public static Expression Eq(ListExpression args, Environment env)
{
List<Expression> argl = args.EvalMembers(env);
if (argl.Count != 2)
throw new InterpreterInvalidApplicationException();
if (argl[0].Equals(argl[1]))
return TrueExpression.GetInstance();
return NilExpression.GetInstance();
}
public static Expression Set(ListExpression args, Environment env)
{
List<Expression> argl = args.GetMembers();
if (argl.Count != 2)
throw new InterpreterInvalidApplicationException();
SymbolExpression? binding = argl[0] as SymbolExpression;
if (binding is null)
throw new InterpreterTypingException();
Symbol name = binding.GetSymbol();
Expression definition = argl[1].Eval(env);
env.RedefineSymbol(name, definition);
return definition;
}
public static Environment RegisterPrimitives()
{
Environment result = new Environment();
result.AddSymbol(new Symbol("lambda"), new ProcedureExpression(new PrimitiveProcedure(Lambda)));
result.AddSymbol(new Symbol("quote"), new ProcedureExpression(new PrimitiveProcedure(Quote)));
result.AddSymbol(new Symbol("define"), new ProcedureExpression(new PrimitiveProcedure(Define)));
result.AddSymbol(new Symbol("car"), new ProcedureExpression(new PrimitiveProcedure(Car)));
result.AddSymbol(new Symbol("cdr"), new ProcedureExpression(new PrimitiveProcedure(Cdr)));
result.AddSymbol(new Symbol("cons"), new ProcedureExpression(new PrimitiveProcedure(Cons)));
result.AddSymbol(new Symbol("quit"), new ProcedureExpression(new PrimitiveProcedure(Quit)));
result.AddSymbol(new Symbol("cond"), new ProcedureExpression(new PrimitiveProcedure(Cond)));
result.AddSymbol(new Symbol("+"), new ProcedureExpression(new PrimitiveProcedure(Plus)));
result.AddSymbol(new Symbol("-"), new ProcedureExpression(new PrimitiveProcedure(Minus)));
result.AddSymbol(new Symbol("*"), new ProcedureExpression(new PrimitiveProcedure(Times)));
result.AddSymbol(new Symbol("/"), new ProcedureExpression(new PrimitiveProcedure(DividedBy)));
result.AddSymbol(new Symbol("%"), new ProcedureExpression(new PrimitiveProcedure(Modulus)));
result.AddSymbol(new Symbol("eq"), new ProcedureExpression(new PrimitiveProcedure(Eq)));
result.AddSymbol(new Symbol("set!"), new ProcedureExpression(new PrimitiveProcedure(Set)));
return result;
}
}
}

View file

@ -0,0 +1,46 @@
using System.DirectoryServices;
namespace KumiScript.Interpreter
{
public class LispProcedure : Procedure
{
readonly List<Symbol> _parameters;
readonly Expression _body;
readonly Environment _closureEnv;
public LispProcedure(List<Symbol> parameters, Expression body, Environment closureEnv)
{
_parameters = parameters;
_body = body;
_closureEnv = closureEnv;
}
public override Expression ApplyWithArgs(ListExpression args, Environment env)
{
List<Expression> evaluatedArgs = args.EvalMembers(env);
if (_parameters.Count != evaluatedArgs.Count)
throw new InterpreterInvalidApplicationException();
Environment inner = MapArgs(evaluatedArgs, _closureEnv);
return _body.Eval(inner);
}
private Environment MapArgs(List<Expression> args, Environment env)
{
Environment inner = new Environment(env);
for (int i = 0; i < _parameters.Count; i++)
inner.AddSymbol(_parameters[i], args[i]);
return inner;
}
public override bool IsPrimitive()
{
return false;
}
public override string ToString()
{
return string.Concat("λ(", string.Join(' ', _parameters), ")");
}
}
}

View file

@ -0,0 +1,27 @@
namespace KumiScript.Interpreter
{
public class PrimitiveProcedure : Procedure
{
public delegate Expression PrimitiveDelegate(ListExpression args, Environment env);
readonly PrimitiveDelegate _function;
public PrimitiveProcedure(PrimitiveDelegate function)
{
_function = function;
}
public override Expression ApplyWithArgs(ListExpression args, Environment env)
{
return _function(args, env);
}
public override bool IsPrimitive()
{
return true;
}
public override string ToString()
{
return _function.ToString();
}
}
}

12
interpreter/Procedure.cs Normal file
View file

@ -0,0 +1,12 @@
namespace KumiScript.Interpreter
{
public abstract class Procedure
{
public Procedure()
{
}
public abstract Expression ApplyWithArgs(ListExpression args, Environment env);
public abstract bool IsPrimitive();
}
}

View file

@ -0,0 +1,46 @@
using KumiScript.Reader;
namespace KumiScript.Interpreter
{
public class ReadEvalPrintLoop
{
Stream _stdin;
Stream _stdout;
public ReadEvalPrintLoop(Stream stdin, Stream stdout)
{
_stdin = stdin;
_stdout = stdout;
}
public void Loop()
{
Lexer lexer = new Lexer(_stdin);
Parser parser = new Parser(lexer);
StreamWriter streamWriter = new StreamWriter(_stdout);
Environment top = LispPrimitives.RegisterPrimitives();
while (true) //this thing is ugly but it's just a little test
{
Expression expr = parser.NextTopLevelExpression();
try
{
Expression result = expr.Eval(top);
streamWriter.Write(string.Format("{0}\n", result.ToString()));
streamWriter.Flush();
} catch (Exception ex) {
if (ex is InterpreterInvalidApplicationException
or InterpreterInvalidDefinitionException
or InterpreterInvalidInvocationException
or InterpreterTypingException
or InterpreterUnboundSymbolException)
{
streamWriter.WriteLine(ex);
streamWriter.Flush();
continue;
}
throw;
}
}
}
}
}

21
interpreter/Symbol.cs Normal file
View file

@ -0,0 +1,21 @@
namespace KumiScript.Interpreter
{
public class Symbol
{
readonly string _name;
public Symbol(string name)
{
_name = name;
}
public bool Equals(Symbol s)
{
return s.ToString() == _name;
}
public override string ToString()
{
return _name;
}
}
}

View file

@ -0,0 +1,12 @@
namespace KumiScript.Interpreter
{
public abstract class Expression
{
public Expression()
{
}
public abstract Expression Eval(Environment env);
public abstract bool Equals(Expression expr);
}
}

View file

@ -0,0 +1,41 @@
using System.Numerics;
namespace KumiScript.Interpreter
{
public class FloatExpression : NumberExpression
{
readonly decimal _value;
public FloatExpression(decimal f)
{
_value = f;
}
public override bool Equals(Expression expr)
{
NumberExpression? nexpr = expr as NumberExpression;
if (nexpr is null)
return false;
return nexpr.GetValueAsFloat() == _value;
}
public override Expression Eval(Environment env)
{
return this;
}
public override decimal GetValueAsFloat()
{
return _value;
}
public override int GetValueAsInt()
{
return (int) _value;
}
public override string ToString()
{
return _value.ToString();
}
}
}

View file

@ -0,0 +1,42 @@
using System.Numerics;
namespace KumiScript.Interpreter
{
public class IntegerExpression : NumberExpression
{
readonly int _value;
public IntegerExpression(int n)
{
_value = n;
}
public override bool Equals(Expression expr)
{
NumberExpression? nexpr = expr as NumberExpression;
if (nexpr is null)
return false;
return nexpr.GetValueAsFloat() == _value;
}
public override Expression Eval(Environment env)
{
return this;
}
public override decimal GetValueAsFloat()
{
return _value;
}
public override int GetValueAsInt()
{
return _value;
}
public override string ToString()
{
return _value.ToString();
}
}
}

View file

@ -0,0 +1,15 @@
namespace KumiScript.Interpreter
{
public abstract class ListExpression : Expression
{
public ListExpression()
{
}
public abstract Expression Car();
public abstract ListExpression Cdr();
public abstract ProperListExpression Cons(Expression expr);
public abstract List<Expression> EvalMembers(Environment env);
public abstract List<Expression> GetMembers();
}
}

View file

@ -0,0 +1,13 @@
namespace KumiScript.Interpreter
{
public class ListFactory
{
public static ListExpression MakeList(List<Expression> expressions)
{
if (expressions.Any())
return new ProperListExpression(expressions);
return NilExpression.GetInstance();
}
}
}

View file

@ -0,0 +1,58 @@
namespace KumiScript.Interpreter
{
public class NilExpression : ListExpression
{
private static NilExpression? _instance;
private NilExpression()
{
}
public static NilExpression GetInstance()
{
if (_instance is null)
_instance = new NilExpression();
return _instance;
}
public override Expression Car()
{
return this;
}
public override ListExpression Cdr()
{
return this;
}
public override ProperListExpression Cons(Expression expr)
{
return new ProperListExpression(new List<Expression> {expr});
}
public override bool Equals(Expression expr)
{
return expr is NilExpression;
}
public override Expression Eval(Environment env)
{
return this;
}
public override List<Expression> EvalMembers(Environment env)
{
return new List<Expression>();
}
public override List<Expression> GetMembers()
{
return new List<Expression>();
}
public override string ToString()
{
return "null";
}
}
}

View file

@ -0,0 +1,12 @@
namespace KumiScript.Interpreter
{
public abstract class NumberExpression : Expression
{
public NumberExpression()
{
}
public abstract decimal GetValueAsFloat();
public abstract int GetValueAsInt();
}
}

View file

@ -0,0 +1,15 @@
namespace KumiScript.Interpreter
{
public class NumberFactory
{
public static NumberExpression NormalizeFloat(decimal f)
{
//TODO: Handle overflows
int i = (int) f;
if (i == f)
return new IntegerExpression(i);
return new FloatExpression(f);
}
}
}

View file

@ -0,0 +1,51 @@
namespace KumiScript.Interpreter
{
public class ProcedureExpression : Expression
{
readonly Procedure _proc;
readonly bool _branching;
public ProcedureExpression(Procedure p)
{
_proc = p;
_branching = false;
}
public ProcedureExpression(Procedure p, bool branching)
{
_proc = p;
_branching = branching;
}
public override Expression Eval(Environment env)
{
return this;
}
public Expression Apply(ListExpression args, Environment env)
{
return _proc.ApplyWithArgs(args, env);
}
public override string ToString()
{
if (_proc.IsPrimitive())
return string.Format("#Primitive<{0}>", _proc.ToString());
return string.Format("#Procedure<{0}>", _proc.ToString());
}
public override bool Equals(Expression expr)
{
ProcedureExpression? pe = expr as ProcedureExpression;
if (pe is null)
return false;
return _proc.Equals(pe._proc);
}
public Procedure GetProcedure()
{
return _proc;
}
}
}

View file

@ -0,0 +1,76 @@
namespace KumiScript.Interpreter
{
public class ProperListExpression : ListExpression
{
readonly List<Expression> _values;
public ProperListExpression(List<Expression> expressions)
{
_values = expressions;
}
public override Expression Eval(Environment env)
{
ProcedureExpression? rator = Car().Eval(env) as ProcedureExpression;
if (rator is null)
throw new InterpreterInvalidInvocationException();
ListExpression rand = Cdr();
return rator.Apply(rand, env);
}
public override List<Expression> EvalMembers(Environment env)
{
List<Expression> result = new List<Expression>();
for (int i = 0; i < _values.Count; i++)
result.Add(_values[i].Eval(env));
return result;
}
public override List<Expression> GetMembers()
{
return _values;
}
public override string ToString()
{
return String.Concat("(",
String.Join(' ', (object[]) _values.ToArray()),
")");
}
public override Expression Car()
{
if (_values.Count == 0)
return this;
return _values.First();
}
public override ListExpression Cdr()
{
if (_values.Count == 0)
return this;
List<Expression> rest = new List<Expression>(_values);
rest.RemoveAt(0);
return ListFactory.MakeList(rest);
}
public override ProperListExpression Cons(Expression expr)
{
List<Expression> consd = new List<Expression>(_values);
consd.Insert(0, expr);
return new ProperListExpression(consd);
}
public override bool Equals(Expression expr)
{
ProperListExpression? lexpr = expr as ProperListExpression;
if (lexpr is null)
return false;
return lexpr.GetMembers().SequenceEqual(_values);
}
}
}

View file

@ -0,0 +1,30 @@
namespace KumiScript.Interpreter
{
public class StringExpression : Expression
{
string _value;
public StringExpression(string str)
{
_value = str;
}
public override bool Equals(Expression expr)
{
StringExpression? se = expr as StringExpression;
if (se is null)
return false;
return _value == se._value;
}
public override Expression Eval(Environment env)
{
return this;
}
public override string ToString()
{
return _value;
}
}
}

View file

@ -0,0 +1,35 @@
namespace KumiScript.Interpreter
{
public class SymbolExpression : Expression
{
readonly Symbol _value;
public SymbolExpression(Symbol s)
{
_value = s;
}
public override bool Equals(Expression expr)
{
SymbolExpression? sy = expr as SymbolExpression;
if (sy is null)
return false;
return _value.Equals(sy._value);
}
public override Expression Eval(Environment env)
{
return env.Lookup(_value);
}
public Symbol GetSymbol()
{
return _value;
}
public override string ToString()
{
return _value.ToString();
}
}
}

View file

@ -0,0 +1,33 @@
namespace KumiScript.Interpreter
{
public class TrueExpression : Expression
{
private static TrueExpression? _instance;
private TrueExpression()
{
}
public static TrueExpression GetInstance()
{
if (_instance is null)
_instance = new TrueExpression();
return _instance;
}
public override bool Equals(Expression expr)
{
return expr is TrueExpression;
}
public override Expression Eval(Environment env)
{
return this;
}
public override string ToString()
{
return "t";
}
}
}

43
loader/KSMetaParser.cs Normal file
View file

@ -0,0 +1,43 @@
using System.Text.RegularExpressions;
namespace KumiScript.Loader
{
public class KSMetaParser
{
readonly string _path;
readonly StreamReader _streamReader;
readonly Dictionary<string, int> _properties;
public KSMetaParser(string path)
{
_path = path;
_streamReader = new StreamReader(File.OpenRead(path));
_properties = new Dictionary<string, int>(8);
ParseFile();
}
private void ParseFile()
{
while (!_streamReader.EndOfStream)
{
string? line = _streamReader.ReadLine();
if (line is null)
continue;
line = Regex.Replace(line, "\\s+", string.Empty);
string[] kvp = line.Split(":", 2);
int v;
if (!int.TryParse(kvp[1], out v))
throw new Exception("Bad file!");
_properties.Add(kvp[0], v);
}
}
public int GetAttribute(string name)
{
int v = 0;
_properties.TryGetValue(name, out v);
return v;
}
}
}

38
parser/AtomToken.cs Normal file
View file

@ -0,0 +1,38 @@
using KumiScript.Interpreter;
namespace KumiScript.Reader
{
public class AtomToken : Token
{
readonly string _value;
public AtomToken(string value)
{
_value = value;
}
public override Expression Accept(ITokenVisitor tokenVisitor)
{
return tokenVisitor.VisitAtom(this);
}
public virtual Expression ToExpression()
{
decimal f;
if (decimal.TryParse(_value, out f))
return NumberFactory.NormalizeFloat(f);
if (_value == "null")
return NilExpression.GetInstance();
if (_value == "t")
return TrueExpression.GetInstance();
return new SymbolExpression(new Symbol(_value));
}
public override string GetValue()
{
return _value;
}
}
}

21
parser/EndOfFileToken.cs Normal file
View file

@ -0,0 +1,21 @@
using KumiScript.Reader;
namespace KumiScript.Interpreter
{
public class EndOfFileToken : Token
{
public EndOfFileToken()
{
}
public override Expression Accept(ITokenVisitor tokenVisitor)
{
return tokenVisitor.VisitEoF(this);
}
public override string GetValue()
{
return "EOF";
}
}
}

151
parser/Lexer.cs Normal file
View file

@ -0,0 +1,151 @@
using KumiScript.Interpreter;
namespace KumiScript.Reader
{
public class Lexer
{
Stack<Token> _tokenStack;
readonly StreamReader _streamReader;
public Lexer(Stream stream)
{
_tokenStack = new Stack<Token>(16);
_streamReader = new StreamReader(stream);
}
public Token NextToken()
{
if (_tokenStack.IsEmpty())
return LexFromStream();
return _tokenStack.Pop();
}
public Token PeekToken()
{
Token t = NextToken();
PushToken(t);
return t;
}
public void PushToken(Token token)
{
_tokenStack.Push(token);
return;
}
private Token LexFromStream()
{
int c = GetChar();
while (char.IsWhiteSpace((char) c))
c = GetChar();
if (c == ';')
{
while ((c = GetChar()) != '\n');
c = GetChar();
}
if (c == -1)
return new EndOfFileToken();
if (c == '(')
return new ParenthesisToken(true);
if (c == ')')
return new ParenthesisToken(false);
if (c == '"')
return LexString();
if (isspec(c))
return new SpecialToken((char) c);
return LexGeneric(c);
}
private Token LexString()
{
List<char> chars = new List<char>();
int c = GetChar();
while (c != '"')
{
if (c == '\\')
c = GetChar();
chars.Add((char) c);
c = GetChar();
if (c == -1)
throw new Exception("Unexpected EOF!");
}
return new StringToken(new string(chars.ToArray()));
}
private Token LexGeneric(int c)
{
List<char> chars = new List<char>();
chars.Add((char) c); //NOTE: The language server is WRONG! List<char>((char) c) BREAKS! **** you m$
int p = PeekChar();
while (!char.IsWhiteSpace((char) p) && !isspecparen(p) && p != -1)
{
c = GetChar();
chars.Add((char) c);
p = PeekChar();
}
return new AtomToken(new string(chars.ToArray()));
}
private static bool isspec(int c)
{
switch (c)
{
case '#':
return true;
case '\'':
return true;
case '\\':
return true;
case '`':
return true;
case ',':
return true;
}
return false;
}
private static bool isspecparen(int c)
{
switch (c)
{
case '(':
return true;
case ')':
return true;
case '#':
return true;
case '\'':
return true;
case '\\':
return true;
case '`':
return true;
case ',':
return true;
}
return false;
}
private int GetChar()
{
return _streamReader.Read();
}
private int PeekChar()
{
return _streamReader.Peek();
}
}
}

View file

@ -0,0 +1,25 @@
using KumiScript.Interpreter;
namespace KumiScript.Reader
{
public class ParenthesisToken : Token
{
public readonly bool _leftParen;
public ParenthesisToken(bool lp)
{
_leftParen = lp;
}
public override Expression Accept(ITokenVisitor tokenVisitor)
{
return tokenVisitor.VisitParen(this);
}
public override string GetValue()
{
if (_leftParen)
return "(";
return ")";
}
}
}

25
parser/Parser.cs Normal file
View file

@ -0,0 +1,25 @@
using KumiScript.Interpreter;
namespace KumiScript.Reader
{
public class Parser
{
Lexer _lexer;
public Parser(Lexer lexer)
{
_lexer = lexer;
}
public Expression NextTopLevelExpression()
{
Token t = _lexer.NextToken();
return t.Accept(new ParserTopLevelVisitor(this));
}
public Expression NextExpressionCC(ITokenVisitor visitor)
{
Token t = _lexer.NextToken();
return t.Accept(visitor);
}
}
}

View file

@ -0,0 +1,62 @@
using System.Runtime.Serialization;
namespace KumiScript.Reader
{
[Serializable]
internal class ParserEndOfFileException : Exception
{
public ParserEndOfFileException()
{
}
public ParserEndOfFileException(string? message) : base(message)
{
}
public ParserEndOfFileException(string? message, Exception? innerException) : base(message, innerException)
{
}
protected ParserEndOfFileException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
internal class ParserUnexpectedTokenException : Exception
{
public ParserUnexpectedTokenException()
{
}
public ParserUnexpectedTokenException(string? message) : base(message)
{
}
public ParserUnexpectedTokenException(string? message, Exception? innerException) : base(message, innerException)
{
}
protected ParserUnexpectedTokenException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
internal class ParserUnexpectedEndOfFileException : Exception
{
public ParserUnexpectedEndOfFileException()
{
}
public ParserUnexpectedEndOfFileException(string? message) : base(message)
{
}
public ParserUnexpectedEndOfFileException(string? message, Exception? innerException) : base(message, innerException)
{
}
protected ParserUnexpectedEndOfFileException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}

View file

@ -0,0 +1,53 @@
using KumiScript.Interpreter;
namespace KumiScript.Reader
{
public class ParserListVisitor : ITokenVisitor
{
Parser _parser;
public ParserListVisitor(Parser parser)
{
_parser = parser;
}
public Expression VisitAtom(AtomToken atom)
{
return atom.ToExpression();
}
public Expression VisitString(StringToken str)
{
return str.ToExpression();
}
public Expression VisitEoF(EndOfFileToken eof)
{
throw new ParserUnexpectedEndOfFileException("Expected ')' before end of file.");
}
public Expression VisitParen(ParenthesisToken paren)
{
if (!paren._leftParen)
return null; //TODO: some other way of throwing it back
List<Expression> list = new List<Expression>();
Expression item = _parser.NextExpressionCC(this);
while (item is not null)
{
list.Add(item);
item = _parser.NextExpressionCC(this);
}
return ListFactory.MakeList(list);
}
public Expression VisitSpecial(SpecialToken spec)
{
if (spec._value == '\'')
return _parser.NextExpressionCC(new ParserQuoteVisitor(_parser));
throw new NotImplementedException();
}
}
}

View file

@ -0,0 +1,65 @@
using KumiScript.Interpreter;
namespace KumiScript.Reader
{
public class ParserQuoteVisitor : ITokenVisitor
{
Parser _parser;
public ParserQuoteVisitor(Parser parser)
{
_parser = parser;
}
public Expression VisitAtom(AtomToken atom)
{
List<Expression> quotedAtom = new List<Expression>
{
new SymbolExpression(new Symbol("quote")),
atom.ToExpression()
};
return new ProperListExpression(quotedAtom);
}
public Expression VisitEoF(EndOfFileToken eof)
{
throw new ParserUnexpectedEndOfFileException("Expected <atom>, <list>, got EOF.");
}
public Expression VisitParen(ParenthesisToken paren)
{
if (!paren._leftParen)
throw new ParserUnexpectedTokenException("Unexpected Token ')'! Wanted <atom>, '('.");
List<Expression> list = new List<Expression>();
ParserListVisitor listVisitor = new ParserListVisitor(_parser);
Expression item = _parser.NextExpressionCC(listVisitor);
while (item is not null)
{
list.Add(item);
item = _parser.NextExpressionCC(listVisitor);
}
Expression listExpression = ListFactory.MakeList(list);
List<Expression> quotedList = new List<Expression>
{
new SymbolExpression(new Symbol("quote")),
listExpression
};
return new ProperListExpression(quotedList);
}
public Expression VisitSpecial(SpecialToken spec)
{
throw new ParserUnexpectedTokenException("Expected <atom>, <list>, got SPECIAL.");
}
public Expression VisitString(StringToken str)
{
List<Expression> quotedString = new List<Expression>
{
new SymbolExpression(new Symbol("quote")),
str.ToExpression()
};
return new ProperListExpression(quotedString);
}
}
}

View file

@ -0,0 +1,54 @@
using KumiScript.Interpreter;
namespace KumiScript.Reader
{
public class ParserTopLevelVisitor : ITokenVisitor
{
Parser _parser;
public ParserTopLevelVisitor(Parser parser)
{
_parser = parser;
}
public Expression VisitAtom(AtomToken atom)
{
return atom.ToExpression();
}
public Expression VisitString(StringToken str)
{
return str.ToExpression();
}
public Expression VisitEoF(EndOfFileToken eof)
{
throw new ParserEndOfFileException();
}
public Expression VisitParen(ParenthesisToken paren)
{
if (!paren._leftParen)
throw new ParserUnexpectedTokenException("Unexpected Token ')'! Wanted ATOM, '(', or EOF.");
List<Expression> list = new List<Expression>();
ParserListVisitor listVisitor = new ParserListVisitor(_parser);
Expression item = _parser.NextExpressionCC(listVisitor);
while (item is not null)
{
list.Add(item);
item = _parser.NextExpressionCC(listVisitor);
}
return ListFactory.MakeList(list);
}
public Expression VisitSpecial(SpecialToken spec)
{
if (spec._value == '\'')
return _parser.NextExpressionCC(new ParserQuoteVisitor(_parser));
throw new NotImplementedException();
}
}
}

23
parser/SpecialToken.cs Normal file
View file

@ -0,0 +1,23 @@
using KumiScript.Interpreter;
namespace KumiScript.Reader
{
public class SpecialToken : Token
{
public readonly char _value;
public SpecialToken(char value)
{
_value = value;
}
public override Expression Accept(ITokenVisitor tokenVisitor)
{
return tokenVisitor.VisitSpecial(this);
}
public override string GetValue()
{
return _value.ToString();
}
}
}

39
parser/Stack.cs Normal file
View file

@ -0,0 +1,39 @@
namespace KumiScript.Reader
{
public class Stack<T>
{
List<T> _values;
public Stack(int size)
{
_values = new List<T>(size);
}
public T Pop()
{
if (!_values.Any())
throw new Exception("Stack empty!");
T last = _values.Last();
_values.Remove(last);
return last;
}
public bool Push(T item)
{
_values.Add(item);
return true;
}
public bool IsEmpty()
{
return !_values.Any();
}
public List<T> ToList()
{
return _values;
}
}
}

23
parser/StringToken.cs Normal file
View file

@ -0,0 +1,23 @@
using KumiScript.Interpreter;
namespace KumiScript.Reader
{
public class StringToken : AtomToken
{
readonly string _value;
public StringToken(string value) : base (value)
{
_value = value;
}
public override Expression ToExpression()
{
return new StringExpression(_value);
}
public override string GetValue()
{
return _value;
}
}
}

10
parser/Token.cs Normal file
View file

@ -0,0 +1,10 @@
using KumiScript.Interpreter;
namespace KumiScript.Reader
{
public abstract class Token
{
public abstract String GetValue();
public abstract Expression Accept(ITokenVisitor tokenVisitor);
}
}

13
parser/TokenVisitor.cs Normal file
View file

@ -0,0 +1,13 @@
using KumiScript.Interpreter;
namespace KumiScript.Reader
{
public interface ITokenVisitor
{
Expression VisitEoF(EndOfFileToken eof);
Expression VisitAtom(AtomToken atom);
Expression VisitString(StringToken str);
Expression VisitParen(ParenthesisToken paren);
Expression VisitSpecial(SpecialToken spec);
}
}

44
renderer/Background.cs Normal file
View file

@ -0,0 +1,44 @@
using SDL2;
namespace KumiScript.Renderer
{
public class Background : IDrawable
{
SDLRenderer _renderer;
Image _image;
int _height;
int _width;
public Background(string path, SDLRenderer renderer)
{
_renderer = renderer;
_image = new Image(path, renderer);
}
public void Draw(int x, int y)
{
SDL.SDL_RenderCopy(_renderer.id, _image.GetTexture().id, 0, 0);
}
public int GetBitmapHeight()
{
return _height;
}
public int GetBitmapWidth()
{
return _width;
}
public int GetXOffset()
{
return 0;
}
public int GetYOffset()
{
return 0;
}
}
}

11
renderer/IDrawable.cs Normal file
View file

@ -0,0 +1,11 @@
namespace KumiScript.Renderer
{
public interface IDrawable
{
void Draw(int x, int y);
int GetBitmapWidth();
int GetBitmapHeight();
int GetXOffset();
int GetYOffset();
}
}

21
renderer/Image.cs Normal file
View file

@ -0,0 +1,21 @@
using SDL2;
namespace KumiScript.Renderer
{
public class Image
{
SDLRenderer _renderer;
SDLTexture _texture;
public Image(string path, SDLRenderer renderer)
{
_renderer = renderer;
_texture = new SDLTexture(path, renderer);
}
public SDLTexture GetTexture()
{
return _texture;
}
}
}

32
renderer/Scene.cs Normal file
View file

@ -0,0 +1,32 @@
namespace KumiScript.Renderer
{
public class Scene
{
SDLRenderer _renderer;
List<SceneElement> _elements;
public Scene(SDLRenderer renderer)
{
_renderer = renderer;
_elements = new List<SceneElement>(16);
}
public void DrawScene()
{
foreach (SceneElement e in _elements)
{
e.Draw(0, 0);
}
}
public void AddElement(SceneElement e)
{
_elements.Add(e);
return;
}
public bool RemoveElement(SceneElement e)
{
return _elements.Remove(e);
}
}
}

21
renderer/SceneElement.cs Normal file
View file

@ -0,0 +1,21 @@
namespace KumiScript.Renderer
{
public class SceneElement
{
IDrawable _elem;
int _x;
int _y;
public SceneElement(IDrawable elem, int xPos, int yPos)
{
_elem = elem;
_x = xPos;
_y = yPos;
}
public void Draw(int sceneX, int sceneY)
{
_elem.Draw(_x + sceneX, _y + sceneY);
return;
}
}
}

56
renderer/Sprite.cs Normal file
View file

@ -0,0 +1,56 @@
using System.Text.Json.Serialization;
using KumiScript.Loader;
using SDL2;
namespace KumiScript.Renderer
{
public class Sprite : IDrawable
{
readonly SDLRenderer _renderer;
readonly Image _image;
SDL.SDL_Rect _drawCoords;
readonly int _height;
readonly int _width;
readonly int _xOffset;
readonly int _yOffset;
public Sprite(string path, SDLRenderer renderer)
{
_renderer = renderer;
_image = new Image(path, renderer);
KSMetaParser parser = new KSMetaParser(string.Concat(path, ".ksmeta"));
_height = parser.GetAttribute("height");
_width = parser.GetAttribute("width");
_xOffset = parser.GetAttribute("xOffset");
_yOffset = parser.GetAttribute("yOffset");
_drawCoords.w = _width;
_drawCoords.h = _height;
}
public void Draw(int x, int y)
{
_drawCoords.x = x + _xOffset;
_drawCoords.y = y + _yOffset;
SDL.SDL_RenderCopy(_renderer.id, _image.GetTexture().id, 0, ref _drawCoords);
}
public int GetBitmapHeight()
{
return _height;
}
public int GetBitmapWidth()
{
return _width;
}
public int GetXOffset()
{
return _xOffset;
}
public int GetYOffset()
{
return _yOffset;
}
}
}

View file

@ -0,0 +1,25 @@
using SDL2;
namespace KumiScript.Renderer
{
public class SDLRenderer
{
internal readonly nint id;
internal SDLRenderer(SDLWindow window)
{
id = SDL.SDL_CreateRenderer(window.Id, -1, SDL.SDL_RendererFlags.SDL_RENDERER_ACCELERATED);
if (id == 0)
throw new Exception("Failed to create renderer!");
}
public void Clear()
{
SDL.SDL_RenderClear(id);
}
public void SwapBuffers()
{
SDL.SDL_RenderPresent(id);
}
}
}

View file

@ -0,0 +1,26 @@
using SDL2;
namespace KumiScript.Renderer
{
public class SDLTexture : IDisposable
{
internal readonly nint id;
public SDLTexture(string path, SDLRenderer renderer)
{
nint surface = SDL_image.IMG_Load(path);
if (surface == 0)
throw new Exception(SDL_image.IMG_GetError());
nint texture = SDL.SDL_CreateTextureFromSurface(renderer.id, surface);
if (texture == 0)
throw new Exception(SDL.SDL_GetError());
SDL.SDL_FreeSurface(surface);
id = texture;
}
public void Dispose()
{
SDL.SDL_DestroyTexture(id);
}
}
}

39
renderer/sdl/SDLWindow.cs Normal file
View file

@ -0,0 +1,39 @@
using SDL2;
namespace KumiScript.Renderer
{
public class SDLWindow
{
internal readonly nint Id;
ushort _width;
ushort _height;
string _title;
SDLRenderer? _renderer;
public SDLWindow(ushort horizontalResolution, ushort verticalResolution, string windowTitle)
{
_width = horizontalResolution;
_height = verticalResolution;
_title = windowTitle;
nint window = SDL.SDL_CreateWindow(windowTitle, SDL.SDL_WINDOWPOS_CENTERED, SDL.SDL_WINDOWPOS_CENTERED,
horizontalResolution, verticalResolution, 0);
if (window == 0)
throw new Exception("Failed to create window!");
Id = window;
}
public int UpdateSurface()
{
return SDL.SDL_UpdateWindowSurface(Id);
}
public SDLRenderer GetRenderer()
{
if (_renderer is null)
_renderer = new SDLRenderer(this);
return _renderer;
}
}
}