diff --git a/Qrakhen.Qamp.Core/Compilation/Builders/FunctionBuilder.cs b/Qrakhen.Qamp.Core/Compilation/Builders/FunctionBuilder.cs index 0502598..e5cbfa5 100644 --- a/Qrakhen.Qamp.Core/Compilation/Builders/FunctionBuilder.cs +++ b/Qrakhen.Qamp.Core/Compilation/Builders/FunctionBuilder.cs @@ -9,6 +9,8 @@ public class FunctionBuilder public int OuterCount; public int ArgumentCount; + public long CurrentInstruction => Segment.Instructions.Count; + public Function Build() { return new Function(Name, Segment.Build(), OuterCount, ArgumentCount); diff --git a/Qrakhen.Qamp.Core/Compilation/Digester.cs b/Qrakhen.Qamp.Core/Compilation/Digester.cs index cd0c76c..2ec5c98 100644 --- a/Qrakhen.Qamp.Core/Compilation/Digester.cs +++ b/Qrakhen.Qamp.Core/Compilation/Digester.cs @@ -183,12 +183,53 @@ public class Digester : ISteppable } if (canAssign && Match(T.Equal)) - ErrorAtCurrent("Invalid assignment target"); + ErrorAtCurrent("Invalid assignment target."); } internal void DeclareClass() { _logger.Method(); + Consume(T.Identifier, "Expected a class name."); + // we need both a global name constant and a local declaration for this class + string name = Previous.Value!; + long identifier = IdentifierConstant(name); + DeclareLocal(name); + EmitDynamic(Op.Class, identifier); + DefineVariable(identifier); + ClassBuilder = new ClassBuilder { + Outer = ClassBuilder, + Name = name, + IsDerived = false + }; + + if (Match(T.Colon)) + { + Consume(T.Identifier, "Expected base class name.", false); + ExpressionParser.Variable(this, false); + if (name == Previous.Value!) + ErrorAtPrevious($"Class {name} can not inherit from itself!"); + + // Add base reference by injecting a synthetic base variable into a virtual scope + BeginScope(); + AddLocal("base"); + DefineVariable(0); + + ExpressionParser.Variable(this, name, false); + ClassBuilder.IsDerived = true; + } + + ExpressionParser.Variable(this, name, false); + Consume(T.ContextOpen, "Expected '{' for class body declaration.", false); + while (!Check(T.ContextClose) && !Check(T.Eof)) + DeclareMethod(); + + Consume(T.ContextClose, "Expected '}' to end class body declaration.", false); + Emit(Op.Pop); + + if (ClassBuilder.IsDerived) + EndScope(); + + ClassBuilder = ClassBuilder.Outer; } internal void DeclareFunction() @@ -203,13 +244,13 @@ public class Digester : ISteppable internal void DeclareMethod() { _logger.Method(); - Consume(T.Identifier, "Expected method name", false); - long name = IdentifierConstant(Previous.Value!); + Consume(T.Identifier, "Expected method name.", false); + long identifier = IdentifierConstant(Previous.Value!); FunctionType type = FunctionType.Method; if (Previous.Value == ClassBuilder?.Name) type = FunctionType.Constructor; CreateFunction(type); - EmitDynamic(Op.Method, name); + EmitDynamic(Op.Method, identifier); } internal void DeclareVariable() @@ -259,17 +300,29 @@ public class Digester : ISteppable internal void Else() { _logger.Method(); - ErrorAtPrevious("Unexpected solitary 'else'"); + ErrorAtPrevious("This 'else' is very alone :( Have you meant to place it after an 'if'?"); } internal void While() { _logger.Method(); + long start = Function.CurrentInstruction; + Consume(T.GroupOpen, "Expected while condition to be placed inside parentheses. Missing '('.", false); + Expression(); + Consume(T.GroupClose, "Expected while condition to be placed inside parentheses. Missing ')'.", false); + long exit = EmitJump(Op.JumpIfFalse); + Emit(Op.Pop); + Statement(); + EmitLoop(start); + PatchJump(exit); + Emit(Op.Pop); } internal void Do() { _logger.Method(); + // todo: just do a while loop here with synthetic counter variables checked at end + throw new NotImplementedException(); } internal void For() @@ -285,7 +338,7 @@ public class Digester : ISteppable StatementExpression(); } - long start = Function.Segment.Instructions.Count; + long start = Function.CurrentInstruction; long exit = -1; if (!Match(T.Semicolon)) { Expression(); diff --git a/Qrakhen.Qamp.Core/Compilation/ExpressionParser.cs b/Qrakhen.Qamp.Core/Compilation/ExpressionParser.cs index 98227ec..cbadfa9 100644 --- a/Qrakhen.Qamp.Core/Compilation/ExpressionParser.cs +++ b/Qrakhen.Qamp.Core/Compilation/ExpressionParser.cs @@ -166,7 +166,7 @@ public static class ExpressionParser digester.EmitConstant(Values.Objects.String.Make(digester.Previous.Value)); } - static void Variable(Digester digester, string name, bool canAssign) + public static void Variable(Digester digester, string name, bool canAssign) { OpCode Get, Set; long variable = digester.ResolveLocal(digester.Builder, name); @@ -192,7 +192,7 @@ public static class ExpressionParser } } - static void Variable(Digester digester, bool canAssign) + public static void Variable(Digester digester, bool canAssign) { Variable(digester, digester.Previous.Value!, canAssign); } diff --git a/Qrakhen.Qamp.Core/Execution/Runner.cs b/Qrakhen.Qamp.Core/Execution/Runner.cs index e99db78..8e67db6 100644 --- a/Qrakhen.Qamp.Core/Execution/Runner.cs +++ b/Qrakhen.Qamp.Core/Execution/Runner.cs @@ -61,10 +61,8 @@ public class InstructionPtr : Pointer int length = Segment.Read(Ptr) ^ 0x80; Ptr++; byte[] data = new byte[8]; - for (int i = 0; i < length; i++) - { + for (int i = 0; i < length; i++) data[i] = Segment.Read(Ptr++); - } return data.ToInt64(); } @@ -211,7 +209,7 @@ public class Runner : IDisposable // todo: add closure case Op.GetGlobal: { - string? name = call.Ptr.GetString(call.Ptr.NextLong())?.Value; + string? name = call.Ptr.GetString(call.Ptr.NextDynamic())?.Value; if (string.IsNullOrEmpty(name)) throw new ExecutionException($"tried to set global variable with empty name"); if (!Globals.Has(name))