Substantial progress on renderer among other things

This commit is contained in:
Michael Kiselev 2024-07-11 19:17:00 -05:00
parent 5c14525067
commit 45dc6f63ea
Signed by: Sangui
GPG key ID: DAB055E38B47526A
61 changed files with 1370 additions and 606 deletions

35
Game.cs
View file

@ -1,11 +1,14 @@
using KumiScript.Renderer;
using SDL2;
using KumiScript.Renderer;
using KumiScript.Gearbox;
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;
@ -17,35 +20,21 @@ public class EngineWindow
if (SDL_image.IMG_Init(SDL_image.IMG_InitFlags.IMG_INIT_PNG) < 0)
throw new Exception("SDL2 Image init failed!");
if (SDL_ttf.TTF_Init() < 0)
throw new Exception("SDL2 ttf 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)
GameState state = new GameStateMain(renderer);
while (!state.IsQuitting())
{
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();
}
state = state.UpdateState();
}
renderer.Dispose();
window.Dispose();
}
}

View file

@ -4,11 +4,11 @@ namespace KumiScript {
class KumiScript {
static void Main (string[] args) {
Console.WriteLine(System.IO.Directory.GetCurrentDirectory());
/*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();*/
engineWindow.show();*/
ReadEvalPrintLoop repl = new ReadEvalPrintLoop(Console.OpenStandardInput(), Console.OpenStandardOutput());
repl.Loop();
}
}
}

166
common/FastLinkedList.cs Normal file
View file

@ -0,0 +1,166 @@
namespace KumiScript.Common
{
public class FastLinkedList<T>
{
ListNode<T>? _first;
ListNode<T>? _last;
uint _length;
public FastLinkedList()
{
_length = 0;
}
public void InsertLast(T addend)
{
ListNode<T> n = new ListNode<T>(addend);
if (_length == 0)
{
_first = n;
_last = n;
_length = 1;
return;
}
_last!.SetNext(n);
n.SetPrev(_last);
_last = n;
_length++;
return;
}
public void InsertFirst(T addend)
{
ListNode<T> n = new ListNode<T>(addend);
if (_length == 0)
{
_first = n;
_last = n;
_length = 1;
return;
}
_first!.SetPrev(n);
n.SetNext(_first);
_first = n;
_length++;
return;
}
private ListNode<T>? FindNth(uint index)
{
if (index > _length - 1)
return null;
if (index - 1 == _length)
return _last;
ListNode<T>? ni = _first;
if (ni is null)
return null;
for (uint i = 0; i < index; i++)
{
ni = ni!.GetNext();
}
return ni;
}
public void InsertAfter(uint index, T value)
{
ListNode<T> nn = new ListNode<T>(value);
ListNode<T>? ni = FindNth(index);
}
public T? Find(IEqualityComparer<T> eq, T other)
{
ListNode<T>? n = _first;
if (n is null)
return default;
do
{
T nv = n.GetValue();
if (eq.Equals(nv, other))
{
return nv;
}
n = n.GetNext();
} while (n is not null);
return default;
}
public T? GetFirst()
{
if (_first is null)
return default;
return _first.GetValue();
}
public T? GetLast()
{
if (_last is null)
return default;
return _last.GetValue();
}
public uint GetLength()
{
return _length;
}
}
internal class ListNode<T>
{
ListNode<T>? _next;
ListNode<T>? _prev;
T _value;
internal ListNode(T value)
{
_value = value;
}
internal T GetValue()
{
return _value;
}
internal void SetValue(T value)
{
_value = value;
return;
}
internal void SetNext(ListNode<T> next)
{
_next = next;
}
internal void SetPrev(ListNode<T> prev)
{
_prev = prev;
}
internal bool HasNext()
{
return _next is not null;
}
internal bool HasPrev()
{
return _prev is not null;
}
internal ListNode<T>? GetNext()
{
return _next;
}
internal ListNode<T>? GetPrev()
{
return _prev;
}
}
}

9
gearbox/GameState.cs Normal file
View file

@ -0,0 +1,9 @@
namespace KumiScript.Gearbox
{
public abstract class GameState
{
public abstract GameState UpdateState();
public abstract void AcceptMessage(StateMessage message);
public abstract bool IsQuitting();
}
}

118
gearbox/GameStateMain.cs Normal file
View file

@ -0,0 +1,118 @@
using SDL2;
using KumiScript.Renderer;
using TextBox = KumiScript.Renderer.TextBox;
namespace KumiScript.Gearbox
{
public class GameStateMain : GameState
{
List<Scene> _scenes;
Scene _curScene;
List<Sprite> _sprites;
List<Background> _backgrounds;
List<ManagedFont> _fonts;
GraphicalUserInterface _graphicalUI;
TextBox _bigBox;
SDLRenderer _renderer;
SDL.SDL_Event e;
ulong _tA;
ulong _tB;
float _delta;
public GameStateMain(SDLRenderer renderer)
{
_renderer = renderer;
_curScene = new Scene();
_scenes = new List<Scene>
{
_curScene
};
Sprite seiba = new Sprite("Resources/Sprites/test.png", renderer);
_sprites = new List<Sprite> {
seiba
};
Background background = new Background("Resources/Backgrounds/test.png", renderer);
_backgrounds = new List<Background>
{
background
};
_curScene.AddElement(background);
_curScene.AddElement(seiba);
_graphicalUI = new GraphicalUserInterface();
SDL.SDL_Color color;
color.r = 255;
color.g = 255;
color.b = 255;
color.a = 0;
ManagedFont font = new ManagedFont("Resources/Fonts/DejaVuSerif.ttf", 36, color, renderer, 256); //ascii
_fonts = new List<ManagedFont> {
font
};
Text hello = new Text("Hello, world!", font);
Text lorem = new Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", font);
_bigBox = new TextBox("Resources/Interface/bigbox.png", renderer, 36, true);
_bigBox.AddText(hello);
_bigBox.BreakLine();
_bigBox.AddText(lorem);
_graphicalUI.AddElement(_bigBox);
_tA = SDL.SDL_GetPerformanceCounter();
_tB = SDL.SDL_GetPerformanceCounter();
_delta = 0;
}
public override void AcceptMessage(StateMessage message)
{
throw new NotImplementedException();
}
public override bool IsQuitting()
{
return false;
}
public override GameState UpdateState()
{
_tA = SDL.SDL_GetPerformanceCounter();
while (SDL.SDL_PollEvent(out e) > 0)
{
switch (e.type)
{
case SDL.SDL_EventType.SDL_QUIT:
CleanUp();
return new GameStateQuitting();
case SDL.SDL_EventType.SDL_KEYUP:
switch (e.key.keysym.sym)
{
case SDL.SDL_Keycode.SDLK_RETURN:
_bigBox.UpdateState();
break;
}
break;
}
}
_tB = _tA;
_renderer.Clear();
_curScene.DrawScene();
_graphicalUI.DrawInterface();
_renderer.SwapBuffers();
_tB = SDL.SDL_GetPerformanceCounter();
_delta = (_tB - _tA) / SDL.SDL_GetPerformanceFrequency() * 1000.0f;
SDL.SDL_Delay((uint) (1000/60 - _delta));
return this;
}
private void CleanUp()
{
foreach (Sprite s in _sprites)
s.Dispose();
foreach(Background b in _backgrounds)
b.Dispose();
foreach(ManagedFont f in _fonts)
f.Dispose();
_graphicalUI.Dispose();
}
}
}

View file

@ -0,0 +1,23 @@
namespace KumiScript.Gearbox
{
public class GameStateQuitting : GameState
{
public GameStateQuitting()
{
}
public override void AcceptMessage(StateMessage message)
{
return;
}
public override bool IsQuitting()
{
return true;
}
public override GameState UpdateState()
{
return this;
}
}
}

7
gearbox/StateMessage.cs Normal file
View file

@ -0,0 +1,7 @@
namespace KumiScript.Gearbox
{
public abstract class StateMessage
{
}
}

View file

@ -1,5 +1,3 @@
using System.Diagnostics.CodeAnalysis;
namespace KumiScript.Interpreter
{
public class Environment
@ -8,11 +6,11 @@ namespace KumiScript.Interpreter
readonly Dictionary<Symbol, Expression> _bindings;
public Environment()
{
_bindings = new Dictionary<Symbol, Expression>(SymbolComparer.GetInstance());
_bindings = new Dictionary<Symbol, Expression>(ReferenceEqualityComparer.Instance);
}
public Environment(Environment outer)
{
_bindings = new Dictionary<Symbol, Expression>(SymbolComparer.GetInstance());
_bindings = new Dictionary<Symbol, Expression>(ReferenceEqualityComparer.Instance);
_outer = outer;
}
@ -40,8 +38,7 @@ namespace KumiScript.Interpreter
{
if (_bindings.ContainsKey(symbol))
{
_bindings.Remove(symbol);
_bindings.Add(symbol, value);
_bindings[symbol] = value;
return;
}
@ -51,33 +48,4 @@ namespace KumiScript.Interpreter
_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

@ -1,32 +1,26 @@
using KumiScript.Reader;
namespace KumiScript.Interpreter
{
public class LispPrimitives
{
public static Expression Lambda(ListExpression args, Environment env)
public static Expression Lambda(Expression args, Environment env)
{
Expression car = args.Car();
ListExpression rest = args.Cdr();
Expression rest = args.Cdr();
ListExpression? head = car as ListExpression;
if (head is null)
throw new InterpreterInvalidDefinitionException();
List<Expression> headl = head.GetMembers();
List<Expression> headl = car.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());
arg_names.Add(headl[i].GetSymbol());
}
Expression body = rest.Car();
return new ProcedureExpression(new LispProcedure(arg_names, body, env));
}
public static Expression Quote(ListExpression args, Environment env)
public static Expression Quote(Expression args, Environment env)
{
List<Expression> argl = args.GetMembers();
if (argl.Count != 1)
@ -35,78 +29,60 @@ namespace KumiScript.Interpreter
return argl[0];
}
public static Expression Define(ListExpression args, Environment env)
public static Expression Define(Expression 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();
Expression? binding = argl[0];
Symbol name = binding.GetSymbol();
Expression definition = argl[1].Eval(env);
env.AddSymbol(name, definition);
return definition;
}
public static Expression Car(ListExpression args, Environment env)
public static Expression Car(Expression 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();
return argl[0].Car();
}
public static ListExpression Cdr(ListExpression args, Environment env)
public static Expression Cdr(Expression 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();
return argl[0].Cdr();
}
public static ProperListExpression Cons(ListExpression args, Environment env)
public static ProperListExpression Cons(Expression 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();
Expression cdr = argl[1];
return cdr.Cons(car);
}
public static Expression Quit(ListExpression args, Environment env)
public static Expression Quit(Expression args, Environment env)
{
System.Environment.Exit(0);
return new SymbolExpression(new Symbol("bye"));
}
public static Expression Cond(ListExpression args, Environment env)
public static Expression Cond(Expression 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 conditional = argl[i];
Expression antecedent = conditional.Car();
Expression consequent = conditional.Cdr().Car();
if (antecedent.Eval(env) != NilExpression.GetInstance())
@ -115,7 +91,7 @@ namespace KumiScript.Interpreter
return NilExpression.GetInstance();
}
public static Expression Eq(ListExpression args, Environment env)
public static Expression Eq(Expression args, Environment env)
{
List<Expression> argl = args.EvalMembers(env);
if (argl.Count != 2)
@ -127,24 +103,19 @@ namespace KumiScript.Interpreter
return NilExpression.GetInstance();
}
public static Expression Set(ListExpression args, Environment env)
public static Expression Set(Expression 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();
Symbol name = argl[0].GetSymbol();
Expression definition = argl[1].Eval(env);
env.RedefineSymbol(name, definition);
return definition;
}
public static Expression For(ListExpression args, Environment env)
public static Expression For(Expression args, Environment env)
{
List<Expression> argl = args.GetMembers();
if (argl.Count != 4)
@ -163,19 +134,18 @@ namespace KumiScript.Interpreter
return result;
}
public static Expression Progn(ListExpression args, Environment env)
public static Expression Progn(Expression args, Environment env)
{
List<Expression> argl = args.GetMembers();
Expression result = NilExpression.GetInstance();
foreach (Expression expr in argl)
{
result = expr.Eval(env);
}
return result;
}
public static Expression Prog1(ListExpression args, Environment env)
public static Expression Prog1(Expression args, Environment env)
{
List<Expression> argl = args.GetMembers();
Expression result = argl.Count > 0 ? argl[0] : NilExpression.GetInstance();
@ -186,7 +156,7 @@ namespace KumiScript.Interpreter
return result;
}
public static Expression Not(ListExpression args, Environment env)
public static Expression Not(Expression args, Environment env)
{
List<Expression> argl = args.EvalMembers(env);
if (argl.Count != 1)
@ -195,26 +165,18 @@ namespace KumiScript.Interpreter
return argl[0] != NilExpression.GetInstance() ? NilExpression.GetInstance() : TrueExpression.GetInstance();
}
public static Expression Nth(ListExpression args, Environment env)
public static Expression Nth(Expression args, Environment env)
{
List<Expression> argl = args.EvalMembers(env);
if (argl.Count != 2)
throw new InterpreterInvalidApplicationException();
IntegerExpression? arg0 = argl[0] as IntegerExpression;
if (arg0 is null)
throw new InterpreterTypingException();
ListExpression? arg1 = argl[1] as ListExpression;
if (arg1 is null)
throw new InterpreterTypingException();
int n = arg0.GetValueAsInt();
List<Expression> arg1l = arg1.GetMembers();
int n = (int) argl[0].GetValueAsFloat();
List<Expression> arg1l = argl[1].GetMembers();
return n >= 0 && n < arg1l.Count ? arg1l[n] : NilExpression.GetInstance();
}
public static Expression While(ListExpression args, Environment env)
public static Expression While(Expression args, Environment env)
{
List<Expression> argl = args.GetMembers();
if (argl.Count != 2)
@ -234,22 +196,22 @@ namespace KumiScript.Interpreter
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("eq"), new ProcedureExpression(new PrimitiveProcedure(Eq)));
result.AddSymbol(new Symbol("set!"), new ProcedureExpression(new PrimitiveProcedure(Set)));
result.AddSymbol(new Symbol("for"), new ProcedureExpression(new PrimitiveProcedure(For)));
result.AddSymbol(new Symbol("progn"), new ProcedureExpression(new PrimitiveProcedure(Progn)));
result.AddSymbol(new Symbol("not"), new ProcedureExpression(new PrimitiveProcedure(Not)));
result.AddSymbol(new Symbol("prog1"), new ProcedureExpression(new PrimitiveProcedure(Prog1)));
result.AddSymbol(new Symbol("nth"), new ProcedureExpression(new PrimitiveProcedure(Nth)));
result.AddSymbol(new Symbol("while"), new ProcedureExpression(new PrimitiveProcedure(While)));
result.AddSymbol(SymbolTable.GetInstance().FromString("lambda"), new ProcedureExpression(new PrimitiveProcedure(Lambda)));
result.AddSymbol(SymbolTable.GetInstance().FromString("quote"), new ProcedureExpression(new PrimitiveProcedure(Quote)));
result.AddSymbol(SymbolTable.GetInstance().FromString("define"), new ProcedureExpression(new PrimitiveProcedure(Define)));
result.AddSymbol(SymbolTable.GetInstance().FromString("car"), new ProcedureExpression(new PrimitiveProcedure(Car)));
result.AddSymbol(SymbolTable.GetInstance().FromString("cdr"), new ProcedureExpression(new PrimitiveProcedure(Cdr)));
result.AddSymbol(SymbolTable.GetInstance().FromString("cons"), new ProcedureExpression(new PrimitiveProcedure(Cons)));
result.AddSymbol(SymbolTable.GetInstance().FromString("quit"), new ProcedureExpression(new PrimitiveProcedure(Quit)));
result.AddSymbol(SymbolTable.GetInstance().FromString("cond"), new ProcedureExpression(new PrimitiveProcedure(Cond)));
result.AddSymbol(SymbolTable.GetInstance().FromString("eq"), new ProcedureExpression(new PrimitiveProcedure(Eq)));
result.AddSymbol(SymbolTable.GetInstance().FromString("set!"), new ProcedureExpression(new PrimitiveProcedure(Set)));
result.AddSymbol(SymbolTable.GetInstance().FromString("for"), new ProcedureExpression(new PrimitiveProcedure(For)));
result.AddSymbol(SymbolTable.GetInstance().FromString("progn"), new ProcedureExpression(new PrimitiveProcedure(Progn)));
result.AddSymbol(SymbolTable.GetInstance().FromString("not"), new ProcedureExpression(new PrimitiveProcedure(Not)));
result.AddSymbol(SymbolTable.GetInstance().FromString("prog1"), new ProcedureExpression(new PrimitiveProcedure(Prog1)));
result.AddSymbol(SymbolTable.GetInstance().FromString("nth"), new ProcedureExpression(new PrimitiveProcedure(Nth)));
result.AddSymbol(SymbolTable.GetInstance().FromString("while"), new ProcedureExpression(new PrimitiveProcedure(While)));
return result;
}
}

View file

@ -12,7 +12,7 @@ namespace KumiScript.Interpreter
_closureEnv = closureEnv;
}
public override Expression ApplyWithArgs(ListExpression args, Environment env)
public override Expression ApplyWithArgs(Expression args, Environment env)
{
List<Expression> evaluatedArgs = args.EvalMembers(env);
if (_parameters.Count != evaluatedArgs.Count)
@ -40,5 +40,10 @@ namespace KumiScript.Interpreter
{
return string.Concat("λ(", string.Join(' ', _parameters), ")");
}
public List<Symbol> GetParameters()
{
return _parameters;
}
}
}

View file

@ -1,211 +1,127 @@
using System.Reflection.Metadata.Ecma335;
using KumiScript.Reader;
namespace KumiScript.Interpreter
{
public class MathPrimitives
{
public static NumberExpression Plus(ListExpression args, Environment env)
public static NumberExpression Plus(Expression 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();
acc += argl[i].GetValueAsFloat();
}
return NumberFactory.NormalizeFloat(acc);
return new NumberExpression(acc);
}
public static NumberExpression Minus(ListExpression args, Environment env)
public static NumberExpression Minus(Expression 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();
decimal acc = argl[0].GetValueAsFloat();
if (argl.Count == 1)
return NumberFactory.NormalizeFloat(1 - acc);
return new NumberExpression(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();
acc -= argl[i].GetValueAsFloat();
}
return NumberFactory.NormalizeFloat(acc);
return new NumberExpression(acc);
}
public static NumberExpression Times(ListExpression args, Environment env)
public static NumberExpression Times(Expression 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();
decimal f = argl[0].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();
f *= argl[i].GetValueAsFloat();
}
return NumberFactory.NormalizeFloat(f);
return new NumberExpression(f);
}
public static NumberExpression DividedBy(ListExpression args, Environment env)
public static NumberExpression DividedBy(Expression 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());
return new NumberExpression(argl[0].GetValueAsFloat() / argl[1].GetValueAsFloat());
}
public static NumberExpression Modulus(ListExpression args, Environment env)
public static NumberExpression Modulus(Expression 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());
return new NumberExpression(argl[0].GetValueAsFloat() % argl[1].GetValueAsFloat());
}
public static Expression GreaterThan(ListExpression args, Environment env)
public static Expression GreaterThan(Expression 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 arg0.GetValueAsFloat() > arg1.GetValueAsFloat() ? TrueExpression.GetInstance() : NilExpression.GetInstance();
return argl[0].GetValueAsFloat() > argl[1].GetValueAsFloat() ? TrueExpression.GetInstance() : NilExpression.GetInstance();
}
public static Expression LessThan(ListExpression args, Environment env)
public static Expression LessThan(Expression 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 arg0.GetValueAsFloat() < arg1.GetValueAsFloat() ? TrueExpression.GetInstance() : NilExpression.GetInstance();
return argl[0].GetValueAsFloat() < argl[1].GetValueAsFloat() ? TrueExpression.GetInstance() : NilExpression.GetInstance();
}
public static Expression LessThanEquals(ListExpression args, Environment env)
public static Expression LessThanEquals(Expression 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 arg0.GetValueAsFloat() <= arg1.GetValueAsFloat() ? TrueExpression.GetInstance() : NilExpression.GetInstance();
return argl[0].GetValueAsFloat() <= argl[1].GetValueAsFloat() ? TrueExpression.GetInstance() : NilExpression.GetInstance();
}
public static Expression GreaterThanEquals(ListExpression args, Environment env)
public static Expression GreaterThanEquals(Expression 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 arg0.GetValueAsFloat() >= arg1.GetValueAsFloat() ? TrueExpression.GetInstance() : NilExpression.GetInstance();
return argl[0].GetValueAsFloat() >= argl[1].GetValueAsFloat() ? TrueExpression.GetInstance() : NilExpression.GetInstance();
}
public static Expression PlusPlus(ListExpression args, Environment env)
public static Expression PlusPlus(Expression args, Environment env)
{
List<Expression> argl = args.GetMembers();
if (argl.Count != 1)
throw new InterpreterInvalidApplicationException();
SymbolExpression? binding = argl[0] as SymbolExpression;
if (binding is null)
throw new InterpreterTypingException();
Symbol name = binding.GetSymbol();
Symbol name = argl[0].GetSymbol();
Expression val = env.Lookup(name);
NumberExpression? num = val as NumberExpression;
if (num is null)
throw new InterpreterTypingException();
NumberExpression nump = NumberFactory.NormalizeFloat(num.GetValueAsFloat() + 1);
NumberExpression nump = new NumberExpression(val.GetValueAsFloat() + 1);
env.RedefineSymbol(name, nump);
return nump;
}
public static Expression MinusMinus(ListExpression args, Environment env)
public static Expression MinusMinus(Expression args, Environment env)
{
List<Expression> argl = args.GetMembers();
if (argl.Count != 1)
throw new InterpreterInvalidApplicationException();
SymbolExpression? binding = argl[0] as SymbolExpression;
if (binding is null)
throw new InterpreterTypingException();
Symbol name = binding.GetSymbol();
Symbol name = argl[0].GetSymbol();
Expression val = env.Lookup(name);
NumberExpression? num = val as NumberExpression;
if (num is null)
throw new InterpreterTypingException();
NumberExpression nump = NumberFactory.NormalizeFloat(num.GetValueAsFloat() - 1);
NumberExpression nump = new NumberExpression(val.GetValueAsFloat() - 1);
env.RedefineSymbol(name, nump);
return nump;
}
@ -213,17 +129,17 @@ namespace KumiScript.Interpreter
public static Environment RegisterPrimitives(Environment outer)
{
Environment result = new Environment(outer);
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(">"), new ProcedureExpression(new PrimitiveProcedure(GreaterThan)));
result.AddSymbol(new Symbol("<"), new ProcedureExpression(new PrimitiveProcedure(LessThan)));
result.AddSymbol(new Symbol("<="), new ProcedureExpression(new PrimitiveProcedure(LessThanEquals)));
result.AddSymbol(new Symbol(">="), new ProcedureExpression(new PrimitiveProcedure(GreaterThanEquals)));
result.AddSymbol(new Symbol("++!"), new ProcedureExpression(new PrimitiveProcedure(PlusPlus)));
result.AddSymbol(new Symbol("--!"), new ProcedureExpression(new PrimitiveProcedure(MinusMinus)));
result.AddSymbol(SymbolTable.GetInstance().FromString("+"), new ProcedureExpression(new PrimitiveProcedure(Plus)));
result.AddSymbol(SymbolTable.GetInstance().FromString("-"), new ProcedureExpression(new PrimitiveProcedure(Minus)));
result.AddSymbol(SymbolTable.GetInstance().FromString("*"), new ProcedureExpression(new PrimitiveProcedure(Times)));
result.AddSymbol(SymbolTable.GetInstance().FromString("/"), new ProcedureExpression(new PrimitiveProcedure(DividedBy)));
result.AddSymbol(SymbolTable.GetInstance().FromString("%"), new ProcedureExpression(new PrimitiveProcedure(Modulus)));
result.AddSymbol(SymbolTable.GetInstance().FromString(">"), new ProcedureExpression(new PrimitiveProcedure(GreaterThan)));
result.AddSymbol(SymbolTable.GetInstance().FromString("<"), new ProcedureExpression(new PrimitiveProcedure(LessThan)));
result.AddSymbol(SymbolTable.GetInstance().FromString("<="), new ProcedureExpression(new PrimitiveProcedure(LessThanEquals)));
result.AddSymbol(SymbolTable.GetInstance().FromString(">="), new ProcedureExpression(new PrimitiveProcedure(GreaterThanEquals)));
result.AddSymbol(SymbolTable.GetInstance().FromString("++!"), new ProcedureExpression(new PrimitiveProcedure(PlusPlus)));
result.AddSymbol(SymbolTable.GetInstance().FromString("--!"), new ProcedureExpression(new PrimitiveProcedure(MinusMinus)));
return result;
}
}

View file

@ -2,14 +2,14 @@ namespace KumiScript.Interpreter
{
public class PrimitiveProcedure : Procedure
{
public delegate Expression PrimitiveDelegate(ListExpression args, Environment env);
public delegate Expression PrimitiveDelegate(Expression args, Environment env);
readonly PrimitiveDelegate _function;
public PrimitiveProcedure(PrimitiveDelegate function)
{
_function = function;
}
public override Expression ApplyWithArgs(ListExpression args, Environment env)
public override Expression ApplyWithArgs(Expression args, Environment env)
{
return _function(args, env);
}

View file

@ -6,7 +6,7 @@ namespace KumiScript.Interpreter
{
}
public abstract Expression ApplyWithArgs(ListExpression args, Environment env);
public abstract Expression ApplyWithArgs(Expression args, Environment env);
public abstract bool IsPrimitive();
}
}

View file

@ -24,9 +24,9 @@ namespace KumiScript.Interpreter
Expression expr = parser.NextTopLevelExpression();
try
{
Expression result = expr.Eval(top);
streamWriter.Write(string.Format("{0}\n", result.ToString()));
streamWriter.Flush();
Expression result = expr.Eval(top);
streamWriter.Write(string.Format("{0}\n", result.ToString()));
streamWriter.Flush();
} catch (Exception ex) {
if (ex is InterpreterInvalidApplicationException
or InterpreterInvalidDefinitionException

View file

@ -10,7 +10,7 @@ namespace KumiScript.Interpreter
public bool Equals(Symbol s)
{
return s.ToString() == _name;
return ReferenceEquals(this, s);
}
public override string ToString()

View file

@ -1,12 +1,70 @@
namespace KumiScript.Interpreter
{
public abstract class Expression
public class Expression
{
public Expression()
public virtual Expression Eval(Environment env)
{
return this;
}
public abstract Expression Eval(Environment env);
public abstract bool Equals(Expression expr);
public virtual decimal GetValueAsFloat()
{
throw new InterpreterTypingException();
}
public virtual Expression Car()
{
throw new InterpreterTypingException();
}
public virtual Expression Cdr()
{
throw new InterpreterTypingException();
}
public virtual ProperListExpression Cons(Expression expr)
{
throw new InterpreterTypingException();
}
public virtual ProperListExpression AppendToList(Expression expr)
{
List<Expression> el = new List<Expression>(2)
{
this,
expr
};
return new ProperListExpression(el);
}
public virtual List<Expression> EvalMembers(Environment env)
{
throw new InterpreterTypingException();
}
public virtual List<Expression> GetMembers()
{
throw new InterpreterTypingException();
}
public virtual Symbol GetSymbol()
{
throw new InterpreterTypingException();
}
public virtual Expression Apply(Expression operand, Environment env)
{
throw new InterpreterTypingException();
}
public virtual bool IsPrimitive()
{
throw new InterpreterTypingException();
}
public virtual bool Equals (Expression expr)
{
return ReferenceEquals(this, expr);
}
}
}

View file

@ -1,41 +0,0 @@
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

@ -1,42 +0,0 @@
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

@ -1,15 +0,0 @@
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

@ -2,7 +2,7 @@ namespace KumiScript.Interpreter
{
public class ListFactory
{
public static ListExpression MakeList(List<Expression> expressions)
public static Expression MakeList(List<Expression> expressions)
{
if (expressions.Any())
return new ProperListExpression(expressions);

View file

@ -1,6 +1,6 @@
namespace KumiScript.Interpreter
{
public class NilExpression : ListExpression
public class NilExpression : Expression
{
private static NilExpression? _instance;
private NilExpression()
@ -20,7 +20,7 @@ namespace KumiScript.Interpreter
return this;
}
public override ListExpression Cdr()
public override Expression Cdr()
{
return this;
}
@ -30,16 +30,6 @@ namespace KumiScript.Interpreter
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>();

View file

@ -1,12 +1,35 @@
namespace KumiScript.Interpreter
{
public abstract class NumberExpression : Expression
public class NumberExpression : Expression
{
public NumberExpression()
decimal _value;
public NumberExpression(decimal value)
{
_value = value;
}
public abstract decimal GetValueAsFloat();
public abstract int GetValueAsInt();
public override decimal GetValueAsFloat()
{
return _value;
}
public override string ToString()
{
return _value.ToString();
}
public override bool Equals(Expression expr)
{
try
{
decimal d = expr.GetValueAsFloat();
return d == _value;
}
catch (InterpreterTypingException)
{
return false;
}
}
}
}

View file

@ -1,15 +0,0 @@
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

@ -21,7 +21,7 @@ namespace KumiScript.Interpreter
return this;
}
public Expression Apply(ListExpression args, Environment env)
public override Expression Apply(Expression args, Environment env)
{
return _proc.ApplyWithArgs(args, env);
}
@ -34,18 +34,9 @@ namespace KumiScript.Interpreter
return string.Format("#Procedure<{0}>", _proc.ToString());
}
public override bool Equals(Expression expr)
public override bool IsPrimitive()
{
ProcedureExpression? pe = expr as ProcedureExpression;
if (pe is null)
return false;
return _proc.Equals(pe._proc);
}
public Procedure GetProcedure()
{
return _proc;
return _proc.IsPrimitive();
}
}
}

View file

@ -1,6 +1,6 @@
namespace KumiScript.Interpreter
{
public class ProperListExpression : ListExpression
public class ProperListExpression : Expression
{
readonly List<Expression> _values;
public ProperListExpression(List<Expression> expressions)
@ -10,11 +10,8 @@ namespace KumiScript.Interpreter
public override Expression Eval(Environment env)
{
ProcedureExpression? rator = Car().Eval(env) as ProcedureExpression;
if (rator is null)
throw new InterpreterInvalidInvocationException();
ListExpression rand = Cdr();
Expression rator = Car().Eval(env);
Expression rand = Cdr();
return rator.Apply(rand, env);
}
@ -34,24 +31,18 @@ namespace KumiScript.Interpreter
public override string ToString()
{
return String.Concat("(",
String.Join(' ', (object[]) _values.ToArray()),
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()
public override Expression Cdr()
{
if (_values.Count == 0)
return this;
List<Expression> rest = new List<Expression>(_values);
rest.RemoveAt(0);
return ListFactory.MakeList(rest);
@ -64,13 +55,11 @@ namespace KumiScript.Interpreter
return new ProperListExpression(consd);
}
public override bool Equals(Expression expr)
public override ProperListExpression AppendToList(Expression expr)
{
ProperListExpression? lexpr = expr as ProperListExpression;
if (lexpr is null)
return false;
return lexpr.GetMembers().SequenceEqual(_values);
List<Expression> appended = new List<Expression>(_values);
appended.Append(expr);
return new ProperListExpression(appended);
}
}
}

View file

@ -8,23 +8,9 @@ namespace KumiScript.Interpreter
_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;
return "\"" + _value + "\"";
}
}
}

View file

@ -8,21 +8,12 @@ namespace KumiScript.Interpreter
_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()
public override Symbol GetSymbol()
{
return _value;
}
@ -31,5 +22,18 @@ namespace KumiScript.Interpreter
{
return _value.ToString();
}
public override bool Equals(Expression expr)
{
try
{
Symbol s = expr.GetSymbol();
return s != _value;
}
catch (InterpreterTypingException)
{
return false;
}
}
}
}

View file

@ -15,16 +15,6 @@ namespace KumiScript.Interpreter
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";

View file

@ -19,7 +19,7 @@ namespace KumiScript.Reader
{
decimal f;
if (decimal.TryParse(_value, out f))
return NumberFactory.NormalizeFloat(f);
return new NumberExpression(f);
if (_value == "null")
return NilExpression.GetInstance();
@ -27,7 +27,7 @@ namespace KumiScript.Reader
if (_value == "t")
return TrueExpression.GetInstance();
return new SymbolExpression(new Symbol(_value));
return new SymbolExpression(SymbolTable.GetInstance().FromString(_value));
}
public override string GetValue()

22
reader/FakeExpression.cs Normal file
View file

@ -0,0 +1,22 @@
using KumiScript.Interpreter;
namespace KumiScript.Reader
{
public class FakeExpression : Expression
{
static FakeExpression? _instance;
private FakeExpression()
{
_instance = this;
}
public static FakeExpression GetInstance()
{
if (_instance is null)
return new FakeExpression();
return _instance;
}
}
}

View file

@ -28,17 +28,15 @@ namespace KumiScript.Reader
public Expression VisitParen(ParenthesisToken paren)
{
if (!paren._leftParen)
return null; //TODO: some other way of throwing it back
return FakeExpression.GetInstance(); //TODO: some other way of throwing it back
List<Expression> list = new List<Expression>();
Expression item = _parser.NextExpressionCC(this);
while (item is not null)
while (!ReferenceEquals(item, FakeExpression.GetInstance()))
{
list.Add(item);
item = _parser.NextExpressionCC(this);
}
return ListFactory.MakeList(list);
}

View file

@ -14,7 +14,7 @@ namespace KumiScript.Reader
{
List<Expression> quotedAtom = new List<Expression>
{
new SymbolExpression(new Symbol("quote")),
new SymbolExpression(SymbolTable.GetInstance().FromString("quote")),
atom.ToExpression()
};
return new ProperListExpression(quotedAtom);
@ -33,7 +33,7 @@ namespace KumiScript.Reader
List<Expression> list = new List<Expression>();
ParserListVisitor listVisitor = new ParserListVisitor(_parser);
Expression item = _parser.NextExpressionCC(listVisitor);
while (item is not null)
while (!ReferenceEquals(item, FakeExpression.GetInstance()))
{
list.Add(item);
item = _parser.NextExpressionCC(listVisitor);
@ -41,7 +41,7 @@ namespace KumiScript.Reader
Expression listExpression = ListFactory.MakeList(list);
List<Expression> quotedList = new List<Expression>
{
new SymbolExpression(new Symbol("quote")),
new SymbolExpression(SymbolTable.GetInstance().FromString("quote")),
listExpression
};
return new ProperListExpression(quotedList);
@ -56,7 +56,7 @@ namespace KumiScript.Reader
{
List<Expression> quotedString = new List<Expression>
{
new SymbolExpression(new Symbol("quote")),
new SymbolExpression(SymbolTable.GetInstance().FromString("quote")),
str.ToExpression()
};
return new ProperListExpression(quotedString);

View file

@ -34,7 +34,7 @@ namespace KumiScript.Reader
ParserListVisitor listVisitor = new ParserListVisitor(_parser);
Expression item = _parser.NextExpressionCC(listVisitor);
while (item is not null)
while (!ReferenceEquals(item, FakeExpression.GetInstance()))
{
list.Add(item);
item = _parser.NextExpressionCC(listVisitor);

36
reader/SymbolTable.cs Normal file
View file

@ -0,0 +1,36 @@
using KumiScript.Interpreter;
namespace KumiScript.Reader
{
public class SymbolTable
{
static SymbolTable? _instance;
readonly Dictionary<string, Symbol> _table;
private SymbolTable()
{
_instance = this;
_table = new Dictionary<string, Symbol>();
}
public static SymbolTable GetInstance()
{
if (_instance is null)
return new SymbolTable();
return _instance;
}
public Symbol FromString(string s)
{
Symbol? sym;
_table.TryGetValue(s, out sym);
if (sym is null)
{
sym = new Symbol(s);
_table.Add(s, sym);
}
return sym;
}
}
}

View file

@ -1,44 +1,59 @@
using KumiScript.Loader;
using SDL2;
namespace KumiScript.Renderer
{
public class Background : IDrawable
public class Background : SceneElement, IDisposable
{
SDLTexture _texture;
SDLRenderer _renderer;
Image _image;
int _height;
int _width;
int _anim;
int _frame;
public Background(string path, SDLRenderer renderer)
{
_texture = new SDLTexture(path, renderer);
_renderer = renderer;
_image = new Image(path, renderer);
KSMetaParser parser = new KSMetaParser(string.Concat(path, ".ksmeta"));
_width = parser.GetAttribute("width");
_height = parser.GetAttribute("height");
_anim = 0;
_frame = 0;
}
public void Draw(int x, int y)
public void Dispose()
{
SDL.SDL_RenderCopy(_renderer.id, _image.GetTexture().id, 0, 0);
_texture.Dispose();
return;
}
public int GetBitmapHeight()
public override void Draw(int x, int y)
{
return _height;
SDL.SDL_Rect destRect = new SDL.SDL_Rect();
destRect.x = x;
destRect.y = y;
destRect.w = 800;
destRect.h = 600;
SDL.SDL_Rect sourceRect = new SDL.SDL_Rect();
sourceRect.x = _anim * _width;
sourceRect.y = _frame * _height;
sourceRect.w = _width;
sourceRect.h = _height;
_texture.Draw(_renderer, sourceRect, destRect);
}
public int GetBitmapWidth()
public override int GetBitmapHeight()
{
return _width;
return 600;
}
public int GetXOffset()
public override int GetBitmapWidth()
{
return 0;
return 800;
}
public int GetYOffset()
{
return 0;
}
}
}

29
renderer/DummyText.cs Normal file
View file

@ -0,0 +1,29 @@
namespace KumiScript.Renderer
{
public class DummyText : Text
{
public DummyText(string text, ManagedFont font) : base(text, font)
{
}
public override void Draw(int x, int y)
{
return;
}
public override void DrawWrapping(int x, int y, int maxw, int spacing, int retw, char delim, out int penx, out int peny)
{
penx = retw;
peny = y + spacing;
return;
}
public override bool DrawWrappingAnimate(int x, int y, int maxw, int spacing, int retw, char delim, out int penx, out int peny)
{
penx = retw;
peny = y + spacing;
return true;
}
}
}

101
renderer/Glyph.cs Normal file
View file

@ -0,0 +1,101 @@
using SDL2;
namespace KumiScript.Renderer
{
public class Glyph : IDisposable
{
readonly char _char;
readonly int _width;
readonly int _height;
SDLTexture _tex;
SDLRenderer _renderer;
public Glyph(char c, SDLFont font, SDL.SDL_Color clr, SDLRenderer renderer)
{
_char = c;
nint surface = SDL_ttf.TTF_RenderGlyph32_Blended(font.GetHandle(), c, clr);
_tex = new SDLTexture(surface, renderer);
SDL.SDL_FreeSurface(surface);
_tex.QueryTexture(out _, out _, out _width, out _height);
_renderer = renderer;
}
public void Dispose()
{
_tex.Dispose();
return;
}
public char GetCharacter()
{
return _char;
}
public SDLTexture GetTexture()
{
return _tex;
}
public int GetWidth()
{
return _width;
}
public void DrawAt(int x, int y)
{
SDL.SDL_Rect destRect;
destRect.x = x;
destRect.y = y;
destRect.w = _width;
destRect.h = _height;
SDL.SDL_Rect sourceRect;
sourceRect.x = 0;
sourceRect.y = 0;
sourceRect.w = _width;
sourceRect.h = _height;
_tex.Draw(_renderer, sourceRect, destRect);
}
public void DrawAdvancePen(int x, int y, out int pen)
{
SDL.SDL_Rect destRect;
destRect.x = x;
destRect.y = y;
destRect.w = _width;
destRect.h = _height;
SDL.SDL_Rect sourceRect;
sourceRect.x = 0;
sourceRect.y = 0;
sourceRect.w = _width;
sourceRect.h = _height;
_tex.Draw(_renderer, sourceRect, destRect);
pen = x + _width;
}
internal void DrawAdvancePenModAlpha(int x, int y, out int pen, byte v)
{
_tex.SetAlpha(v);
SDL.SDL_Rect destRect;
destRect.x = x;
destRect.y = y;
destRect.w = _width;
destRect.h = _height;
SDL.SDL_Rect sourceRect;
sourceRect.x = 0;
sourceRect.y = 0;
sourceRect.w = _width;
sourceRect.h = _height;
_tex.Draw(_renderer, sourceRect, destRect);
_tex.SetAlpha(255);
pen = x + _width;
return;
}
}
}

View file

@ -0,0 +1,36 @@
namespace KumiScript.Renderer
{
public class GraphicalUserInterface : IDisposable
{
List<InterfaceElement> _elements;
public GraphicalUserInterface()
{
_elements = new List<InterfaceElement>(16);
}
public void DrawInterface()
{
foreach (InterfaceElement e in _elements)
{
e.Draw(0, 0);
}
}
public void AddElement(InterfaceElement e)
{
_elements.Add(e);
return;
}
public bool RemoveElement(InterfaceElement e)
{
return _elements.Remove(e);
}
public void Dispose()
{
foreach (InterfaceElement e in _elements)
e.Dispose();
}
}
}

View file

@ -5,7 +5,5 @@ namespace KumiScript.Renderer
void Draw(int x, int y);
int GetBitmapWidth();
int GetBitmapHeight();
int GetXOffset();
int GetYOffset();
}
}

View file

@ -1,21 +0,0 @@
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;
}
}
}

View file

@ -0,0 +1,13 @@
namespace KumiScript.Renderer
{
public abstract class InterfaceElement : IDrawable, IDisposable
{
public abstract void Click();
public abstract void OnMouseover();
public abstract void OnMouseout();
public abstract void Draw(int x, int y);
public abstract int GetBitmapHeight();
public abstract int GetBitmapWidth();
public abstract void Dispose();
}
}

73
renderer/ManagedFont.cs Normal file
View file

@ -0,0 +1,73 @@
using SDL2;
namespace KumiScript.Renderer
{
public class ManagedFont : IDisposable
{
SDLFont _font;
int _cachesize;
SDL.SDL_Color _color;
SDLRenderer _renderer;
Dictionary<char, Glyph> _glyphs;
LinkedList<char> _incidence;
public ManagedFont(string path, int points, SDL.SDL_Color color, SDLRenderer renderer, int num_cache)
{
_font = new SDLFont(path, points);
_renderer = renderer;
_color = color;
_glyphs = new Dictionary<char, Glyph>();
_incidence = new LinkedList<char>();
_cachesize = num_cache;
}
public Glyph GetGlyph(char c)
{
UpdateFreq(c);
Glyph? g;
if (!_glyphs.TryGetValue(c, out g))
return CacheGlyph(c);
return g;
}
private Glyph CacheGlyph(char c)
{
if (_glyphs.Count >= _cachesize)
PurgeCache();
Glyph g = new Glyph(c, _font, _color, _renderer);
_glyphs.Add(c, g);
return g;
}
private void UpdateFreq(char c)
{
_incidence.Remove(c);
_incidence.AddFirst(c);
return;
}
private void PurgeCache()
{
for (int i = _cachesize - 1; i > _cachesize * .75; i--)
{
char c = _incidence.Last();
Glyph g = _glyphs[c];
_glyphs.Remove(c);
g.Dispose();
_incidence.RemoveLast();
}
return;
}
public void Dispose()
{
List<Glyph> glyphs = _glyphs.Values.ToList();
foreach (Glyph g in glyphs)
g.Dispose();
_font.Dispose();
}
}
}

View file

@ -2,11 +2,9 @@ namespace KumiScript.Renderer
{
public class Scene
{
SDLRenderer _renderer;
List<SceneElement> _elements;
public Scene(SDLRenderer renderer)
public Scene()
{
_renderer = renderer;
_elements = new List<SceneElement>(16);
}

View file

@ -1,21 +1,9 @@
namespace KumiScript.Renderer
{
public class SceneElement
public abstract class SceneElement : IDrawable
{
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;
}
public abstract void Draw(int x, int y);
public abstract int GetBitmapHeight();
public abstract int GetBitmapWidth();
}
}

View file

@ -1,56 +1,59 @@
using System.Text.Json.Serialization;
using KumiScript.Loader;
using SDL2;
namespace KumiScript.Renderer
{
public class Sprite : IDrawable
public class Sprite : SceneElement, IDisposable
{
readonly SDLRenderer _renderer;
readonly Image _image;
SDL.SDL_Rect _drawCoords;
SDLTexture _texture;
SDLRenderer _renderer;
readonly int _height;
readonly int _width;
readonly int _xOffset;
readonly int _yOffset;
int _anim;
int _frame;
public Sprite(string path, SDLRenderer renderer)
{
_texture = new SDLTexture(path, 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;
_height = parser.GetAttribute("height");
_anim = 0;
_frame = 0;
}
public void Draw(int x, int y)
public void Dispose()
{
_drawCoords.x = x + _xOffset;
_drawCoords.y = y + _yOffset;
SDL.SDL_RenderCopy(_renderer.id, _image.GetTexture().id, 0, ref _drawCoords);
_texture.Dispose();
return;
}
public int GetBitmapHeight()
public override void Draw(int x, int y)
{
SDL.SDL_Rect destRect = new SDL.SDL_Rect();
destRect.x = x;
destRect.y = y;
destRect.w = _width;
destRect.h = _height;
SDL.SDL_Rect sourceRect = new SDL.SDL_Rect();
sourceRect.x = _anim * _width;
sourceRect.y = _frame * _height;
sourceRect.w = _width;
sourceRect.h = _height;
_texture.Draw(_renderer, sourceRect, destRect);
}
public override int GetBitmapHeight()
{
return _height;
}
public int GetBitmapWidth()
public override int GetBitmapWidth()
{
return _width;
}
public int GetXOffset()
{
return _xOffset;
}
public int GetYOffset()
{
return _yOffset;
}
}
}

149
renderer/Text.cs Normal file
View file

@ -0,0 +1,149 @@
namespace KumiScript.Renderer
{
public class Text : InterfaceElement
{
readonly string _text;
readonly ManagedFont _font;
string[]? _words;
int _progress;
int _animProg;
readonly int _charcount;
public Text(string text, ManagedFont font)
{
_text = text;
_font = font;
_progress = 0;
_animProg = -24;
_charcount = text.Length;
}
public override void Click()
{
return;
}
public override void Draw(int x, int y)
{
foreach (char c in _text)
{
Glyph g = _font.GetGlyph(c);
g.DrawAdvancePen(x, y, out x);
}
}
public virtual void DrawWrapping(int x, int y, int maxw, int spacing, int retw, char delim, out int penx, out int peny)
{
if (_words is null)
_words = _text.Split(delim);
int spacew = _font.GetGlyph(delim).GetWidth();
penx = x;
peny = y;
foreach (string word in _words)
{
int w = 0;
Glyph[] gs = new Glyph[word.Length];
for (int i = 0; i < word.Length; i++)
{
gs[i] = _font.GetGlyph(word[i]);
w += gs[i].GetWidth();
}
if (penx + w >= maxw)
{
penx = retw;
peny += spacing;
}
foreach (Glyph g in gs)
g.DrawAdvancePen(penx, peny, out penx);
penx += spacew;
}
return;
}
public virtual bool DrawWrappingAnimate(int x, int y, int maxw, int spacing, int retw, char delim, out int penx, out int peny)
{
int c = 2;
int dist;
_animProg += 24;
if (_words is null)
_words = _text.Split(delim);
int spacew = _font.GetGlyph(delim).GetWidth();
penx = x;
peny = y;
foreach (string word in _words)
{
int w = 0;
Glyph[] gs = new Glyph[word.Length];
for (int i = 0; i < word.Length; i++)
{
gs[i] = _font.GetGlyph(word[i]);
w += gs[i].GetWidth();
}
if (penx + w >= maxw)
{
penx = retw;
peny += spacing;
}
foreach (Glyph g in gs)
{
if (_animProg >= 60)
{
_animProg = 0;
_progress++;
}
dist = _progress - c;
if (dist > 0)
{
g.DrawAdvancePen(penx, peny, out penx);
c++;
}
else if (dist < -4)
{
return false;
}
else
{
g.DrawAdvancePenModAlpha(penx, peny, out penx, (byte) (255 + dist * 42));
c++;
}
}
penx += spacew;
}
return true;
}
public override int GetBitmapHeight()
{
throw new NotImplementedException();
}
public override int GetBitmapWidth()
{
throw new NotImplementedException();
}
public override void OnMouseout()
{
return;
}
public override void OnMouseover()
{
return;
}
public void ForceProgress()
{
_progress = _charcount;
return;
}
public override void Dispose()
{
return;
}
}
}

176
renderer/TextBox.cs Normal file
View file

@ -0,0 +1,176 @@
using KumiScript.Loader;
using SDL2;
namespace KumiScript.Renderer
{
public class TextBox : InterfaceElement
{
SDLTexture _texture;
readonly SDLRenderer _renderer;
int _width;
int _height;
int _hpadding;
int _vpadding;
int _linespacing;
readonly bool _consecutive;
List<Text> _texts;
List<Text> _textsQueue;
TextBoxState _state;
private enum TextBoxState
{
StateRenderLine,
StateWaitEnter,
StateDone
}
public TextBox(string path, SDLRenderer renderer, bool consecutive)
{
_texture = new SDLTexture(path, renderer);
_renderer = renderer;
KSMetaParser parser = new KSMetaParser(string.Concat(path, ".ksmeta"));
_width = parser.GetAttribute("width");
_height = parser.GetAttribute("height");
_hpadding = parser.GetAttribute("hpadding");
_vpadding = parser.GetAttribute("vpadding");
_linespacing = parser.GetAttribute("linespacing");
_texts = new List<Text>(8);
_textsQueue = new List<Text>(8);
_consecutive = consecutive;
if (consecutive)
_state = TextBoxState.StateRenderLine;
else
_state = TextBoxState.StateDone;
}
public TextBox(string path, SDLRenderer renderer, int linespacing, bool consecutive)
{
_texture = new SDLTexture(path, renderer);
_renderer = renderer;
KSMetaParser parser = new KSMetaParser(string.Concat(path, ".ksmeta"));
_width = parser.GetAttribute("width");
_height = parser.GetAttribute("height");
_hpadding = parser.GetAttribute("hpadding");
_vpadding = parser.GetAttribute("vpadding");
_linespacing = linespacing;
_texts = new List<Text>(8);
_textsQueue = new List<Text>(8);
_consecutive = consecutive;
if (consecutive)
_state = TextBoxState.StateRenderLine;
else
_state = TextBoxState.StateDone;
}
public override void Click()
{
return;
}
public override void Dispose()
{
_texture.Dispose();
}
public override void Draw(int x, int y)
{
SDL.SDL_Rect destRect = new SDL.SDL_Rect();
destRect.x = x;
destRect.y = y;
destRect.w = _width;
destRect.h = _height;
SDL.SDL_Rect sourceRect = new SDL.SDL_Rect();
sourceRect.x = 0;
sourceRect.y = 0;
sourceRect.w = _width;
sourceRect.h = _height;
_texture.Draw(_renderer, sourceRect, destRect);
int retw = x + _hpadding;
int penx = retw;
int peny = y + _vpadding;
foreach (Text t in _texts)
{
t.DrawWrapping(penx, peny, _width - _hpadding, _linespacing, retw, ' ', out penx, out peny);
}
if (_state == TextBoxState.StateRenderLine)
{
Text t2 = _textsQueue.First();
bool done = t2.DrawWrappingAnimate(penx, peny, _width - _hpadding, _linespacing, retw, ' ', out penx, out peny);
if (done)
{
_texts.Add(t2);
_textsQueue.RemoveAt(0);
_state = _textsQueue.Any() ? TextBoxState.StateWaitEnter : TextBoxState.StateDone;
}
}
}
public void BreakLine()
{
AddText(new DummyText("", null));
}
public void AddText(Text t)
{
if (_consecutive)
_textsQueue.Add(t);
else
_texts.Add(t);
return;
}
public void UpdateState()
{
switch(_state)
{
case TextBoxState.StateRenderLine:
SkipLine();
return;
case TextBoxState.StateWaitEnter:
_state = TextBoxState.StateRenderLine;
return;
}
return;
}
private void SkipLine()
{
Text t = _textsQueue.First();
t.ForceProgress();
return;
}
public void ClearText()
{
_texts.Clear();
}
public bool IsDone()
{
return _state == TextBoxState.StateDone;
}
public override int GetBitmapHeight()
{
return _height;
}
public override int GetBitmapWidth()
{
return _width;
}
public override void OnMouseout()
{
return;
}
public override void OnMouseover()
{
return;
}
}
}

26
renderer/sdl/SDLFont.cs Normal file
View file

@ -0,0 +1,26 @@
using SDL2;
namespace KumiScript.Renderer
{
public class SDLFont : IDisposable
{
nint _handle;
public SDLFont(string path, int points)
{
_handle = SDL_ttf.TTF_OpenFont(path, points);
if (_handle == 0)
throw new Exception(SDL_ttf.TTF_GetError());
}
public void Dispose()
{
SDL_ttf.TTF_CloseFont(_handle);
return;
}
public nint GetHandle()
{
return _handle;
}
}
}

View file

@ -2,12 +2,12 @@ using SDL2;
namespace KumiScript.Renderer
{
public class SDLRenderer
public class SDLRenderer : IDisposable
{
internal readonly nint id;
internal SDLRenderer(SDLWindow window)
{
id = SDL.SDL_CreateRenderer(window.Id, -1, SDL.SDL_RendererFlags.SDL_RENDERER_ACCELERATED);
id = SDL.SDL_CreateRenderer(window.Id, -1, SDL.SDL_RendererFlags.SDL_RENDERER_ACCELERATED | SDL.SDL_RendererFlags.SDL_RENDERER_PRESENTVSYNC);
if (id == 0)
throw new Exception("Failed to create renderer!");
}
@ -17,6 +17,11 @@ namespace KumiScript.Renderer
SDL.SDL_RenderClear(id);
}
public void Dispose()
{
SDL.SDL_DestroyRenderer(id);
}
public void SwapBuffers()
{
SDL.SDL_RenderPresent(id);

View file

@ -4,7 +4,7 @@ namespace KumiScript.Renderer
{
public class SDLTexture : IDisposable
{
internal readonly nint id;
readonly nint _id;
public SDLTexture(string path, SDLRenderer renderer)
{
nint surface = SDL_image.IMG_Load(path);
@ -16,11 +16,39 @@ namespace KumiScript.Renderer
throw new Exception(SDL.SDL_GetError());
SDL.SDL_FreeSurface(surface);
id = texture;
_id = texture;
}
public SDLTexture(nint surface, SDLRenderer renderer)
{
nint texture = SDL.SDL_CreateTextureFromSurface(renderer.id, surface);
if (texture == 0)
throw new Exception(SDL.SDL_GetError());
_id = texture;
}
public void Draw(SDLRenderer renderer, SDL.SDL_Rect sourceRect, SDL.SDL_Rect destRect)
{
SDL.SDL_RenderCopy(renderer.id, _id, ref sourceRect, ref destRect);
return;
}
public void Dispose()
{
SDL.SDL_DestroyTexture(id);
SDL.SDL_DestroyTexture(_id);
return;
}
public void QueryTexture(out uint format, out int access, out int width, out int height)
{
SDL.SDL_QueryTexture(_id, out format, out access, out width, out height);
return;
}
public void SetAlpha(byte alpha)
{
SDL.SDL_SetTextureAlphaMod(_id, alpha);
}
}
}

View file

@ -2,7 +2,7 @@ using SDL2;
namespace KumiScript.Renderer
{
public class SDLWindow
public class SDLWindow : IDisposable
{
internal readonly nint Id;
ushort _width;
@ -35,5 +35,15 @@ namespace KumiScript.Renderer
return _renderer;
}
public void SetResizable(bool r)
{
SDL.SDL_SetWindowResizable(Id, r ? SDL.SDL_bool.SDL_TRUE : SDL.SDL_bool.SDL_FALSE);
}
public void Dispose()
{
SDL.SDL_DestroyWindow(Id);
}
}
}