real initial commit
This commit is contained in:
parent
c1e4dbcf02
commit
b3363d3f5d
52 changed files with 2073 additions and 0 deletions
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
*.dll
|
||||||
|
command_examples.txt
|
||||||
|
screen.txt
|
||||||
|
.vscode/
|
||||||
|
bin/
|
||||||
|
obj/
|
||||||
|
Resources/
|
16
Engine.csproj
Normal file
16
Engine.csproj
Normal 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
4
Engine.csproj.user
Normal 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
25
Engine.sln
Normal 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
51
Game.cs
Normal 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
0
LICENSE.txt
Normal file
14
Program.cs
Normal file
14
Program.cs
Normal 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();*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
83
interpreter/Environment.cs
Normal file
83
interpreter/Environment.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
101
interpreter/InterpreterException.cs
Normal file
101
interpreter/InterpreterException.cs
Normal 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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
267
interpreter/LispPrimitives.cs
Normal file
267
interpreter/LispPrimitives.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
46
interpreter/LispProcedure.cs
Normal file
46
interpreter/LispProcedure.cs
Normal 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), ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
interpreter/PrimitiveProcedure.cs
Normal file
27
interpreter/PrimitiveProcedure.cs
Normal 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
12
interpreter/Procedure.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
46
interpreter/ReadEvalPrintLoop.cs
Normal file
46
interpreter/ReadEvalPrintLoop.cs
Normal 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
21
interpreter/Symbol.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
interpreter/expression/Expression.cs
Normal file
12
interpreter/expression/Expression.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
41
interpreter/expression/FloatExpression.cs
Normal file
41
interpreter/expression/FloatExpression.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
interpreter/expression/IntegerExpression.cs
Normal file
42
interpreter/expression/IntegerExpression.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
interpreter/expression/ListExpression.cs
Normal file
15
interpreter/expression/ListExpression.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
13
interpreter/expression/ListFactory.cs
Normal file
13
interpreter/expression/ListFactory.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
58
interpreter/expression/NilExpression.cs
Normal file
58
interpreter/expression/NilExpression.cs
Normal 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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
interpreter/expression/NumberExpression.cs
Normal file
12
interpreter/expression/NumberExpression.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
namespace KumiScript.Interpreter
|
||||||
|
{
|
||||||
|
public abstract class NumberExpression : Expression
|
||||||
|
{
|
||||||
|
public NumberExpression()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract decimal GetValueAsFloat();
|
||||||
|
public abstract int GetValueAsInt();
|
||||||
|
}
|
||||||
|
}
|
15
interpreter/expression/NumberFactory.cs
Normal file
15
interpreter/expression/NumberFactory.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
interpreter/expression/ProcedureExpression.cs
Normal file
51
interpreter/expression/ProcedureExpression.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
76
interpreter/expression/ProperListExpression.cs
Normal file
76
interpreter/expression/ProperListExpression.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
interpreter/expression/StringExpression.cs
Normal file
30
interpreter/expression/StringExpression.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
interpreter/expression/SymbolExpression.cs
Normal file
35
interpreter/expression/SymbolExpression.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
interpreter/expression/TrueExpression.cs
Normal file
33
interpreter/expression/TrueExpression.cs
Normal 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
43
loader/KSMetaParser.cs
Normal 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
38
parser/AtomToken.cs
Normal 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
21
parser/EndOfFileToken.cs
Normal 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
151
parser/Lexer.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
parser/ParenthesisToken.cs
Normal file
25
parser/ParenthesisToken.cs
Normal 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
25
parser/Parser.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
62
parser/ParserExceptions.cs
Normal file
62
parser/ParserExceptions.cs
Normal 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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
parser/ParserListVisitor.cs
Normal file
53
parser/ParserListVisitor.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
parser/ParserQuoteVisitor.cs
Normal file
65
parser/ParserQuoteVisitor.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
54
parser/ParserTopLevelVisitor.cs
Normal file
54
parser/ParserTopLevelVisitor.cs
Normal 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
23
parser/SpecialToken.cs
Normal 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
39
parser/Stack.cs
Normal 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
23
parser/StringToken.cs
Normal 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
10
parser/Token.cs
Normal 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
13
parser/TokenVisitor.cs
Normal 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
44
renderer/Background.cs
Normal 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
11
renderer/IDrawable.cs
Normal 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
21
renderer/Image.cs
Normal 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
32
renderer/Scene.cs
Normal 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
21
renderer/SceneElement.cs
Normal 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
56
renderer/Sprite.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
renderer/sdl/SDLRenderer.cs
Normal file
25
renderer/sdl/SDLRenderer.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
renderer/sdl/SDLTexture.cs
Normal file
26
renderer/sdl/SDLTexture.cs
Normal 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
39
renderer/sdl/SDLWindow.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue