From 45dc6f63ea4a5aa6530e99b3da5139445e300d6d Mon Sep 17 00:00:00 2001 From: Michael Kiselev Date: Thu, 11 Jul 2024 19:17:00 -0500 Subject: [PATCH] Substantial progress on renderer among other things --- Game.cs | 35 ++-- Program.cs | 8 +- common/FastLinkedList.cs | 166 +++++++++++++++ gearbox/GameState.cs | 9 + gearbox/GameStateMain.cs | 118 +++++++++++ gearbox/GameStateQuitting.cs | 23 ++ gearbox/StateMessage.cs | 7 + interpreter/Environment.cs | 40 +--- interpreter/LispPrimitives.cs | 134 +++++------- interpreter/LispProcedure.cs | 9 +- interpreter/MathPrimitives.cs | 198 +++++------------- interpreter/PrimitiveProcedure.cs | 4 +- interpreter/Procedure.cs | 2 +- interpreter/ReadEvalPrintLoop.cs | 6 +- interpreter/Symbol.cs | 2 +- interpreter/expression/Expression.cs | 66 +++++- interpreter/expression/FloatExpression.cs | 41 ---- interpreter/expression/IntegerExpression.cs | 42 ---- interpreter/expression/ListExpression.cs | 15 -- interpreter/expression/ListFactory.cs | 2 +- interpreter/expression/NilExpression.cs | 14 +- interpreter/expression/NumberExpression.cs | 31 ++- interpreter/expression/NumberFactory.cs | 15 -- interpreter/expression/ProcedureExpression.cs | 15 +- .../expression/ProperListExpression.cs | 31 +-- interpreter/expression/StringExpression.cs | 16 +- interpreter/expression/SymbolExpression.cs | 24 ++- interpreter/expression/TrueExpression.cs | 10 - {parser => reader}/AtomToken.cs | 4 +- {parser => reader}/EndOfFileToken.cs | 0 reader/FakeExpression.cs | 22 ++ {parser => reader}/Lexer.cs | 0 {parser => reader}/ParenthesisToken.cs | 0 {parser => reader}/Parser.cs | 0 {parser => reader}/ParserExceptions.cs | 0 {parser => reader}/ParserListVisitor.cs | 6 +- {parser => reader}/ParserQuoteVisitor.cs | 8 +- {parser => reader}/ParserTopLevelVisitor.cs | 2 +- {parser => reader}/SpecialToken.cs | 0 {parser => reader}/Stack.cs | 0 {parser => reader}/StringToken.cs | 0 reader/SymbolTable.cs | 36 ++++ {parser => reader}/Token.cs | 0 {parser => reader}/TokenVisitor.cs | 0 renderer/Background.cs | 49 +++-- renderer/DummyText.cs | 29 +++ renderer/Glyph.cs | 101 +++++++++ renderer/GraphicalUserInterface.cs | 36 ++++ renderer/IDrawable.cs | 2 - renderer/Image.cs | 21 -- renderer/InterfaceElement.cs | 13 ++ renderer/ManagedFont.cs | 73 +++++++ renderer/Scene.cs | 4 +- renderer/SceneElement.cs | 20 +- renderer/Sprite.cs | 61 +++--- renderer/Text.cs | 149 +++++++++++++ renderer/TextBox.cs | 176 ++++++++++++++++ renderer/sdl/SDLFont.cs | 26 +++ renderer/sdl/SDLRenderer.cs | 9 +- renderer/sdl/SDLTexture.cs | 34 ++- renderer/sdl/SDLWindow.cs | 12 +- 61 files changed, 1370 insertions(+), 606 deletions(-) create mode 100644 common/FastLinkedList.cs create mode 100644 gearbox/GameState.cs create mode 100644 gearbox/GameStateMain.cs create mode 100644 gearbox/GameStateQuitting.cs create mode 100644 gearbox/StateMessage.cs delete mode 100644 interpreter/expression/FloatExpression.cs delete mode 100644 interpreter/expression/IntegerExpression.cs delete mode 100644 interpreter/expression/ListExpression.cs delete mode 100644 interpreter/expression/NumberFactory.cs rename {parser => reader}/AtomToken.cs (85%) rename {parser => reader}/EndOfFileToken.cs (100%) create mode 100644 reader/FakeExpression.cs rename {parser => reader}/Lexer.cs (100%) rename {parser => reader}/ParenthesisToken.cs (100%) rename {parser => reader}/Parser.cs (100%) rename {parser => reader}/ParserExceptions.cs (100%) rename {parser => reader}/ParserListVisitor.cs (88%) rename {parser => reader}/ParserQuoteVisitor.cs (85%) rename {parser => reader}/ParserTopLevelVisitor.cs (95%) rename {parser => reader}/SpecialToken.cs (100%) rename {parser => reader}/Stack.cs (100%) rename {parser => reader}/StringToken.cs (100%) create mode 100644 reader/SymbolTable.cs rename {parser => reader}/Token.cs (100%) rename {parser => reader}/TokenVisitor.cs (100%) create mode 100644 renderer/DummyText.cs create mode 100644 renderer/Glyph.cs create mode 100644 renderer/GraphicalUserInterface.cs delete mode 100644 renderer/Image.cs create mode 100644 renderer/InterfaceElement.cs create mode 100644 renderer/ManagedFont.cs create mode 100644 renderer/Text.cs create mode 100644 renderer/TextBox.cs create mode 100644 renderer/sdl/SDLFont.cs diff --git a/Game.cs b/Game.cs index b1f63b8..3022c6e 100644 --- a/Game.cs +++ b/Game.cs @@ -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(); } } \ No newline at end of file diff --git a/Program.cs b/Program.cs index 96ab12c..dbc709d 100644 --- a/Program.cs +++ b/Program.cs @@ -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(); } } } \ No newline at end of file diff --git a/common/FastLinkedList.cs b/common/FastLinkedList.cs new file mode 100644 index 0000000..9357292 --- /dev/null +++ b/common/FastLinkedList.cs @@ -0,0 +1,166 @@ +namespace KumiScript.Common +{ + public class FastLinkedList + { + ListNode? _first; + ListNode? _last; + uint _length; + public FastLinkedList() + { + _length = 0; + } + + public void InsertLast(T addend) + { + ListNode n = new ListNode(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 n = new ListNode(addend); + if (_length == 0) + { + _first = n; + _last = n; + _length = 1; + return; + } + _first!.SetPrev(n); + n.SetNext(_first); + _first = n; + _length++; + return; + } + + private ListNode? FindNth(uint index) + { + if (index > _length - 1) + return null; + + if (index - 1 == _length) + return _last; + + ListNode? 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 nn = new ListNode(value); + ListNode? ni = FindNth(index); + + } + + public T? Find(IEqualityComparer eq, T other) + { + ListNode? 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 + { + ListNode? _next; + ListNode? _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 next) + { + _next = next; + } + + internal void SetPrev(ListNode prev) + { + _prev = prev; + } + + internal bool HasNext() + { + return _next is not null; + } + + internal bool HasPrev() + { + return _prev is not null; + } + + internal ListNode? GetNext() + { + return _next; + } + + internal ListNode? GetPrev() + { + return _prev; + } + } +} + diff --git a/gearbox/GameState.cs b/gearbox/GameState.cs new file mode 100644 index 0000000..6970f28 --- /dev/null +++ b/gearbox/GameState.cs @@ -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(); + } +} \ No newline at end of file diff --git a/gearbox/GameStateMain.cs b/gearbox/GameStateMain.cs new file mode 100644 index 0000000..655656d --- /dev/null +++ b/gearbox/GameStateMain.cs @@ -0,0 +1,118 @@ +using SDL2; + +using KumiScript.Renderer; +using TextBox = KumiScript.Renderer.TextBox; + +namespace KumiScript.Gearbox +{ + public class GameStateMain : GameState + { + List _scenes; + Scene _curScene; + List _sprites; + List _backgrounds; + List _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 + { + _curScene + }; + Sprite seiba = new Sprite("Resources/Sprites/test.png", renderer); + _sprites = new List { + seiba + }; + Background background = new Background("Resources/Backgrounds/test.png", renderer); + _backgrounds = new List + { + 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 { + 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(); + } + } +} \ No newline at end of file diff --git a/gearbox/GameStateQuitting.cs b/gearbox/GameStateQuitting.cs new file mode 100644 index 0000000..08f26f2 --- /dev/null +++ b/gearbox/GameStateQuitting.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/gearbox/StateMessage.cs b/gearbox/StateMessage.cs new file mode 100644 index 0000000..05bc59d --- /dev/null +++ b/gearbox/StateMessage.cs @@ -0,0 +1,7 @@ +namespace KumiScript.Gearbox +{ + public abstract class StateMessage + { + + } +} \ No newline at end of file diff --git a/interpreter/Environment.cs b/interpreter/Environment.cs index 82622e0..9ad279f 100644 --- a/interpreter/Environment.cs +++ b/interpreter/Environment.cs @@ -1,5 +1,3 @@ -using System.Diagnostics.CodeAnalysis; - namespace KumiScript.Interpreter { public class Environment @@ -8,11 +6,11 @@ namespace KumiScript.Interpreter readonly Dictionary _bindings; public Environment() { - _bindings = new Dictionary(SymbolComparer.GetInstance()); + _bindings = new Dictionary(ReferenceEqualityComparer.Instance); } public Environment(Environment outer) { - _bindings = new Dictionary(SymbolComparer.GetInstance()); + _bindings = new Dictionary(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 - { - 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(); - } - } -} \ No newline at end of file +} diff --git a/interpreter/LispPrimitives.cs b/interpreter/LispPrimitives.cs index c5dcc88..25b9229 100644 --- a/interpreter/LispPrimitives.cs +++ b/interpreter/LispPrimitives.cs @@ -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 headl = head.GetMembers(); + List headl = car.GetMembers(); List arg_names = new List(); 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 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 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 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 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 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 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 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 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 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 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 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 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 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 arg1l = arg1.GetMembers(); + int n = (int) argl[0].GetValueAsFloat(); + List 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 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; } } diff --git a/interpreter/LispProcedure.cs b/interpreter/LispProcedure.cs index 96066e2..b9c6f10 100644 --- a/interpreter/LispProcedure.cs +++ b/interpreter/LispProcedure.cs @@ -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 evaluatedArgs = args.EvalMembers(env); if (_parameters.Count != evaluatedArgs.Count) @@ -40,5 +40,10 @@ namespace KumiScript.Interpreter { return string.Concat("λ(", string.Join(' ', _parameters), ")"); } + + public List GetParameters() + { + return _parameters; + } } -} +} \ No newline at end of file diff --git a/interpreter/MathPrimitives.cs b/interpreter/MathPrimitives.cs index dd047ff..c4abd3c 100644 --- a/interpreter/MathPrimitives.cs +++ b/interpreter/MathPrimitives.cs @@ -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 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 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 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) - { - List 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 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 GreaterThan(ListExpression args, Environment env) + public static NumberExpression DividedBy(Expression args, Environment env) { List 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 new NumberExpression(argl[0].GetValueAsFloat() / argl[1].GetValueAsFloat()); } - public static Expression LessThan(ListExpression args, Environment env) + + public static NumberExpression Modulus(Expression args, Environment env) { List 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 new NumberExpression(argl[0].GetValueAsFloat() % argl[1].GetValueAsFloat()); } - public static Expression LessThanEquals(ListExpression args, Environment env) + public static Expression GreaterThan(Expression args, Environment env) { List 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 LessThan(Expression args, Environment env) { List 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 LessThanEquals(Expression args, Environment env) + { + List argl = args.EvalMembers(env); + if (argl.Count != 2) + throw new InterpreterInvalidApplicationException(); + + return argl[0].GetValueAsFloat() <= argl[1].GetValueAsFloat() ? TrueExpression.GetInstance() : NilExpression.GetInstance(); + } + + public static Expression GreaterThanEquals(Expression args, Environment env) + { + List argl = args.EvalMembers(env); + if (argl.Count != 2) + throw new InterpreterInvalidApplicationException(); + + return argl[0].GetValueAsFloat() >= argl[1].GetValueAsFloat() ? TrueExpression.GetInstance() : NilExpression.GetInstance(); + } + + public static Expression PlusPlus(Expression args, Environment env) { List 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 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; } } diff --git a/interpreter/PrimitiveProcedure.cs b/interpreter/PrimitiveProcedure.cs index 925c49b..9a250b6 100644 --- a/interpreter/PrimitiveProcedure.cs +++ b/interpreter/PrimitiveProcedure.cs @@ -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); } diff --git a/interpreter/Procedure.cs b/interpreter/Procedure.cs index 5febe60..adc9dfb 100644 --- a/interpreter/Procedure.cs +++ b/interpreter/Procedure.cs @@ -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(); } } \ No newline at end of file diff --git a/interpreter/ReadEvalPrintLoop.cs b/interpreter/ReadEvalPrintLoop.cs index 8433d53..0648464 100644 --- a/interpreter/ReadEvalPrintLoop.cs +++ b/interpreter/ReadEvalPrintLoop.cs @@ -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 diff --git a/interpreter/Symbol.cs b/interpreter/Symbol.cs index e8b137d..c0a4a54 100644 --- a/interpreter/Symbol.cs +++ b/interpreter/Symbol.cs @@ -10,7 +10,7 @@ namespace KumiScript.Interpreter public bool Equals(Symbol s) { - return s.ToString() == _name; + return ReferenceEquals(this, s); } public override string ToString() diff --git a/interpreter/expression/Expression.cs b/interpreter/expression/Expression.cs index 209fa91..0f06b64 100644 --- a/interpreter/expression/Expression.cs +++ b/interpreter/expression/Expression.cs @@ -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 el = new List(2) + { + this, + expr + }; + return new ProperListExpression(el); + } + + public virtual List EvalMembers(Environment env) + { + throw new InterpreterTypingException(); + } + + public virtual List 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); + } } } \ No newline at end of file diff --git a/interpreter/expression/FloatExpression.cs b/interpreter/expression/FloatExpression.cs deleted file mode 100644 index 8c37a24..0000000 --- a/interpreter/expression/FloatExpression.cs +++ /dev/null @@ -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(); - } - } -} \ No newline at end of file diff --git a/interpreter/expression/IntegerExpression.cs b/interpreter/expression/IntegerExpression.cs deleted file mode 100644 index 39ffc3e..0000000 --- a/interpreter/expression/IntegerExpression.cs +++ /dev/null @@ -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(); - } - } -} \ No newline at end of file diff --git a/interpreter/expression/ListExpression.cs b/interpreter/expression/ListExpression.cs deleted file mode 100644 index 6ce8db6..0000000 --- a/interpreter/expression/ListExpression.cs +++ /dev/null @@ -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 EvalMembers(Environment env); - public abstract List GetMembers(); - } -} \ No newline at end of file diff --git a/interpreter/expression/ListFactory.cs b/interpreter/expression/ListFactory.cs index eec0ac1..2d206bf 100644 --- a/interpreter/expression/ListFactory.cs +++ b/interpreter/expression/ListFactory.cs @@ -2,7 +2,7 @@ namespace KumiScript.Interpreter { public class ListFactory { - public static ListExpression MakeList(List expressions) + public static Expression MakeList(List expressions) { if (expressions.Any()) return new ProperListExpression(expressions); diff --git a/interpreter/expression/NilExpression.cs b/interpreter/expression/NilExpression.cs index 1c784f1..efb2f7e 100644 --- a/interpreter/expression/NilExpression.cs +++ b/interpreter/expression/NilExpression.cs @@ -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 {expr}); } - public override bool Equals(Expression expr) - { - return expr is NilExpression; - } - - public override Expression Eval(Environment env) - { - return this; - } - public override List EvalMembers(Environment env) { return new List(); diff --git a/interpreter/expression/NumberExpression.cs b/interpreter/expression/NumberExpression.cs index 60ead07..2f00659 100644 --- a/interpreter/expression/NumberExpression.cs +++ b/interpreter/expression/NumberExpression.cs @@ -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; + } + } } } \ No newline at end of file diff --git a/interpreter/expression/NumberFactory.cs b/interpreter/expression/NumberFactory.cs deleted file mode 100644 index 0cd5576..0000000 --- a/interpreter/expression/NumberFactory.cs +++ /dev/null @@ -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); - } - } -} \ No newline at end of file diff --git a/interpreter/expression/ProcedureExpression.cs b/interpreter/expression/ProcedureExpression.cs index 9caf636..01d889b 100644 --- a/interpreter/expression/ProcedureExpression.cs +++ b/interpreter/expression/ProcedureExpression.cs @@ -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(); } } } \ No newline at end of file diff --git a/interpreter/expression/ProperListExpression.cs b/interpreter/expression/ProperListExpression.cs index 3236e95..880118b 100644 --- a/interpreter/expression/ProperListExpression.cs +++ b/interpreter/expression/ProperListExpression.cs @@ -1,6 +1,6 @@ namespace KumiScript.Interpreter { - public class ProperListExpression : ListExpression + public class ProperListExpression : Expression { readonly List _values; public ProperListExpression(List 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 rest = new List(_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 appended = new List(_values); + appended.Append(expr); + return new ProperListExpression(appended); } } } \ No newline at end of file diff --git a/interpreter/expression/StringExpression.cs b/interpreter/expression/StringExpression.cs index 5fec1e2..2cf24a0 100644 --- a/interpreter/expression/StringExpression.cs +++ b/interpreter/expression/StringExpression.cs @@ -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 + "\""; } } } \ No newline at end of file diff --git a/interpreter/expression/SymbolExpression.cs b/interpreter/expression/SymbolExpression.cs index feee1b6..093a085 100644 --- a/interpreter/expression/SymbolExpression.cs +++ b/interpreter/expression/SymbolExpression.cs @@ -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; + } + } } } \ No newline at end of file diff --git a/interpreter/expression/TrueExpression.cs b/interpreter/expression/TrueExpression.cs index 7f9f027..f34cdd5 100644 --- a/interpreter/expression/TrueExpression.cs +++ b/interpreter/expression/TrueExpression.cs @@ -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"; diff --git a/parser/AtomToken.cs b/reader/AtomToken.cs similarity index 85% rename from parser/AtomToken.cs rename to reader/AtomToken.cs index a09639b..3b2fba5 100644 --- a/parser/AtomToken.cs +++ b/reader/AtomToken.cs @@ -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() diff --git a/parser/EndOfFileToken.cs b/reader/EndOfFileToken.cs similarity index 100% rename from parser/EndOfFileToken.cs rename to reader/EndOfFileToken.cs diff --git a/reader/FakeExpression.cs b/reader/FakeExpression.cs new file mode 100644 index 0000000..a7f252d --- /dev/null +++ b/reader/FakeExpression.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/parser/Lexer.cs b/reader/Lexer.cs similarity index 100% rename from parser/Lexer.cs rename to reader/Lexer.cs diff --git a/parser/ParenthesisToken.cs b/reader/ParenthesisToken.cs similarity index 100% rename from parser/ParenthesisToken.cs rename to reader/ParenthesisToken.cs diff --git a/parser/Parser.cs b/reader/Parser.cs similarity index 100% rename from parser/Parser.cs rename to reader/Parser.cs diff --git a/parser/ParserExceptions.cs b/reader/ParserExceptions.cs similarity index 100% rename from parser/ParserExceptions.cs rename to reader/ParserExceptions.cs diff --git a/parser/ParserListVisitor.cs b/reader/ParserListVisitor.cs similarity index 88% rename from parser/ParserListVisitor.cs rename to reader/ParserListVisitor.cs index e79a606..6d092d8 100644 --- a/parser/ParserListVisitor.cs +++ b/reader/ParserListVisitor.cs @@ -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 list = new List(); 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); } diff --git a/parser/ParserQuoteVisitor.cs b/reader/ParserQuoteVisitor.cs similarity index 85% rename from parser/ParserQuoteVisitor.cs rename to reader/ParserQuoteVisitor.cs index 1a35b99..ba78365 100644 --- a/parser/ParserQuoteVisitor.cs +++ b/reader/ParserQuoteVisitor.cs @@ -14,7 +14,7 @@ namespace KumiScript.Reader { List quotedAtom = new List { - 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 list = new List(); 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 quotedList = new List { - new SymbolExpression(new Symbol("quote")), + new SymbolExpression(SymbolTable.GetInstance().FromString("quote")), listExpression }; return new ProperListExpression(quotedList); @@ -56,7 +56,7 @@ namespace KumiScript.Reader { List quotedString = new List { - new SymbolExpression(new Symbol("quote")), + new SymbolExpression(SymbolTable.GetInstance().FromString("quote")), str.ToExpression() }; return new ProperListExpression(quotedString); diff --git a/parser/ParserTopLevelVisitor.cs b/reader/ParserTopLevelVisitor.cs similarity index 95% rename from parser/ParserTopLevelVisitor.cs rename to reader/ParserTopLevelVisitor.cs index eb1734e..5099249 100644 --- a/parser/ParserTopLevelVisitor.cs +++ b/reader/ParserTopLevelVisitor.cs @@ -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); diff --git a/parser/SpecialToken.cs b/reader/SpecialToken.cs similarity index 100% rename from parser/SpecialToken.cs rename to reader/SpecialToken.cs diff --git a/parser/Stack.cs b/reader/Stack.cs similarity index 100% rename from parser/Stack.cs rename to reader/Stack.cs diff --git a/parser/StringToken.cs b/reader/StringToken.cs similarity index 100% rename from parser/StringToken.cs rename to reader/StringToken.cs diff --git a/reader/SymbolTable.cs b/reader/SymbolTable.cs new file mode 100644 index 0000000..d68e7f6 --- /dev/null +++ b/reader/SymbolTable.cs @@ -0,0 +1,36 @@ +using KumiScript.Interpreter; + +namespace KumiScript.Reader +{ + public class SymbolTable + { + static SymbolTable? _instance; + readonly Dictionary _table; + + private SymbolTable() + { + _instance = this; + _table = new Dictionary(); + } + + 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; + } + } +} \ No newline at end of file diff --git a/parser/Token.cs b/reader/Token.cs similarity index 100% rename from parser/Token.cs rename to reader/Token.cs diff --git a/parser/TokenVisitor.cs b/reader/TokenVisitor.cs similarity index 100% rename from parser/TokenVisitor.cs rename to reader/TokenVisitor.cs diff --git a/renderer/Background.cs b/renderer/Background.cs index 32557ab..3174b4c 100644 --- a/renderer/Background.cs +++ b/renderer/Background.cs @@ -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; - } - } } \ No newline at end of file diff --git a/renderer/DummyText.cs b/renderer/DummyText.cs new file mode 100644 index 0000000..7046c20 --- /dev/null +++ b/renderer/DummyText.cs @@ -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; + } + + } +} \ No newline at end of file diff --git a/renderer/Glyph.cs b/renderer/Glyph.cs new file mode 100644 index 0000000..0f202ee --- /dev/null +++ b/renderer/Glyph.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/renderer/GraphicalUserInterface.cs b/renderer/GraphicalUserInterface.cs new file mode 100644 index 0000000..236e440 --- /dev/null +++ b/renderer/GraphicalUserInterface.cs @@ -0,0 +1,36 @@ +namespace KumiScript.Renderer +{ + public class GraphicalUserInterface : IDisposable + { + List _elements; + public GraphicalUserInterface() + { + _elements = new List(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(); + } + } +} \ No newline at end of file diff --git a/renderer/IDrawable.cs b/renderer/IDrawable.cs index 637760e..3e99f41 100644 --- a/renderer/IDrawable.cs +++ b/renderer/IDrawable.cs @@ -5,7 +5,5 @@ namespace KumiScript.Renderer void Draw(int x, int y); int GetBitmapWidth(); int GetBitmapHeight(); - int GetXOffset(); - int GetYOffset(); } } \ No newline at end of file diff --git a/renderer/Image.cs b/renderer/Image.cs deleted file mode 100644 index aa15c79..0000000 --- a/renderer/Image.cs +++ /dev/null @@ -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; - } - } -} \ No newline at end of file diff --git a/renderer/InterfaceElement.cs b/renderer/InterfaceElement.cs new file mode 100644 index 0000000..00eb6bc --- /dev/null +++ b/renderer/InterfaceElement.cs @@ -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(); + } +} \ No newline at end of file diff --git a/renderer/ManagedFont.cs b/renderer/ManagedFont.cs new file mode 100644 index 0000000..f8f917b --- /dev/null +++ b/renderer/ManagedFont.cs @@ -0,0 +1,73 @@ +using SDL2; + +namespace KumiScript.Renderer +{ + public class ManagedFont : IDisposable + { + SDLFont _font; + int _cachesize; + SDL.SDL_Color _color; + SDLRenderer _renderer; + Dictionary _glyphs; + LinkedList _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(); + _incidence = new LinkedList(); + _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 glyphs = _glyphs.Values.ToList(); + foreach (Glyph g in glyphs) + g.Dispose(); + + _font.Dispose(); + } + } +} \ No newline at end of file diff --git a/renderer/Scene.cs b/renderer/Scene.cs index d854bdf..b91dcf4 100644 --- a/renderer/Scene.cs +++ b/renderer/Scene.cs @@ -2,11 +2,9 @@ namespace KumiScript.Renderer { public class Scene { - SDLRenderer _renderer; List _elements; - public Scene(SDLRenderer renderer) + public Scene() { - _renderer = renderer; _elements = new List(16); } diff --git a/renderer/SceneElement.cs b/renderer/SceneElement.cs index 2ec08a1..929bd7b 100644 --- a/renderer/SceneElement.cs +++ b/renderer/SceneElement.cs @@ -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(); } } \ No newline at end of file diff --git a/renderer/Sprite.cs b/renderer/Sprite.cs index ca50819..cca5cff 100644 --- a/renderer/Sprite.cs +++ b/renderer/Sprite.cs @@ -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; - } } } \ No newline at end of file diff --git a/renderer/Text.cs b/renderer/Text.cs new file mode 100644 index 0000000..9f4bc9e --- /dev/null +++ b/renderer/Text.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/renderer/TextBox.cs b/renderer/TextBox.cs new file mode 100644 index 0000000..f9c2db5 --- /dev/null +++ b/renderer/TextBox.cs @@ -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 _texts; + List _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(8); + _textsQueue = new List(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(8); + _textsQueue = new List(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; + } + } +} \ No newline at end of file diff --git a/renderer/sdl/SDLFont.cs b/renderer/sdl/SDLFont.cs new file mode 100644 index 0000000..2776750 --- /dev/null +++ b/renderer/sdl/SDLFont.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/renderer/sdl/SDLRenderer.cs b/renderer/sdl/SDLRenderer.cs index 6fc15b2..dc3331f 100644 --- a/renderer/sdl/SDLRenderer.cs +++ b/renderer/sdl/SDLRenderer.cs @@ -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); diff --git a/renderer/sdl/SDLTexture.cs b/renderer/sdl/SDLTexture.cs index c086c90..8d2c9f1 100644 --- a/renderer/sdl/SDLTexture.cs +++ b/renderer/sdl/SDLTexture.cs @@ -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); } } } \ No newline at end of file diff --git a/renderer/sdl/SDLWindow.cs b/renderer/sdl/SDLWindow.cs index a1f6bd2..76c7c67 100644 --- a/renderer/sdl/SDLWindow.cs +++ b/renderer/sdl/SDLWindow.cs @@ -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); + } } } \ No newline at end of file