From ec5a59201ba33958804e53331135a1ce0214d27b Mon Sep 17 00:00:00 2001 From: Sean Date: Wed, 18 Mar 2026 21:58:03 -0400 Subject: [PATCH] Add capability for For Loop processing --- Axiom/Axiom.Core/Interpreter/Assembler.cs | 7 +- Axiom/Axiom.Core/Interpreter/CapturedCode.cs | 17 ++ Axiom/Axiom.Core/Interpreter/CodeRunner.cs | 35 +-- Axiom/Axiom.Core/Interpreter/Emitter.cs | 229 ++++++++------ Axiom/Axiom.Core/Interpreter/Parser.cs | 304 ++++++++++++++++++- Axiom/Axiom.Core/Interpreter/Scanner.cs | 10 +- Axiom/Axiom.Core/Interpreter/SymbolTable.cs | 1 + 7 files changed, 477 insertions(+), 126 deletions(-) create mode 100644 Axiom/Axiom.Core/Interpreter/CapturedCode.cs diff --git a/Axiom/Axiom.Core/Interpreter/Assembler.cs b/Axiom/Axiom.Core/Interpreter/Assembler.cs index 48feec6..42d955e 100755 --- a/Axiom/Axiom.Core/Interpreter/Assembler.cs +++ b/Axiom/Axiom.Core/Interpreter/Assembler.cs @@ -126,7 +126,7 @@ namespace Axiom.Interpreter case Parser.ParserSymbols.codeend2 : break; default : - MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No action for symbol {0}",Parser.SymbolToString(symbol))); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No action for symbol {0}",Parser.SymbolToString(symbol))); break; } } @@ -134,7 +134,7 @@ namespace Axiom.Interpreter } catch(Exception exception) { - MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception during Assembly:{0}",exception)); + MDTrace.WriteLine(LogLevel.DEBUG,$"Exception during Assembly:{exception.ToString()}"); return false; } } @@ -610,6 +610,7 @@ namespace Axiom.Interpreter lastMessage=exception.ToString(); } } + private void Negate() { try @@ -757,7 +758,7 @@ namespace Axiom.Interpreter { isInError=true; lastMessage=exception.ToString(); - MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception:{0}",exception)); + MDTrace.WriteLine(LogLevel.DEBUG,$"Exception:{exception.ToString()}"); } } public void VariableAccess() diff --git a/Axiom/Axiom.Core/Interpreter/CapturedCode.cs b/Axiom/Axiom.Core/Interpreter/CapturedCode.cs new file mode 100644 index 0000000..5a96210 --- /dev/null +++ b/Axiom/Axiom.Core/Interpreter/CapturedCode.cs @@ -0,0 +1,17 @@ +using System; + +namespace Axiom.Interpreter +{ + public class CapturedCode + { + private readonly byte[] codeBytes; + + public CapturedCode(byte[] codeBytes) + { + this.codeBytes = codeBytes ?? throw new ArgumentNullException(nameof(codeBytes)); + } + public int Length => codeBytes.Length; + + public byte[] Bytes => (byte[])codeBytes; + } +} diff --git a/Axiom/Axiom.Core/Interpreter/CodeRunner.cs b/Axiom/Axiom.Core/Interpreter/CodeRunner.cs index 02166a1..7debdde 100755 --- a/Axiom/Axiom.Core/Interpreter/CodeRunner.cs +++ b/Axiom/Axiom.Core/Interpreter/CodeRunner.cs @@ -142,7 +142,7 @@ namespace Axiom.Interpreter { LastMessage="Failed to scan the input document, possible invalid character sequence."; IsInError=true; - MDTrace.WriteLine(LogLevel.DEBUG,LastMessage); + MDTrace.WriteLine(LogLevel.DEBUG, LastMessage); return false; } binaryWriter.BaseStream.Seek(0, SeekOrigin.Begin); @@ -156,10 +156,10 @@ namespace Axiom.Interpreter { LastMessage=String.Format("Failed to parse the input, {0} at {1}", parser.LastMessage, parser.LastLineNumber); IsInError=true; - MDTrace.WriteLine(LogLevel.DEBUG,LastMessage); + MDTrace.WriteLine(LogLevel.DEBUG, LastMessage); return false; } - parserWriter.BaseStream.Seek(0, SeekOrigin.Begin); + parserWriter.BaseStream.Seek(0, SeekOrigin.Begin); // SEEK TO THE BEGINNING OF THE CODE assemblerReader = new BinaryReader(parserWriter.BaseStream); assembler = new Assembler(assemblerReader, symbolTable); assembler.Debug = Trace; @@ -168,7 +168,7 @@ namespace Axiom.Interpreter { LastMessage=String.Format("Error: Failed to run the assembler, {0}", assembler.LastMessage); IsInError=true; - MDTrace.WriteLine(LogLevel.DEBUG,LastMessage); + MDTrace.WriteLine(LogLevel.DEBUG, LastMessage); return false; } if (Trace) DumpSymbolTable(); @@ -180,7 +180,7 @@ namespace Axiom.Interpreter { LastMessage=String.Format("Exception:{0}",exception.ToString()); IsInError=true; - MDTrace.WriteLine(LogLevel.DEBUG,LastMessage); + MDTrace.WriteLine(LogLevel.DEBUG, LastMessage); return false; } finally @@ -208,6 +208,7 @@ namespace Axiom.Interpreter { IsInError=false; LastMessage=""; + symbolTable = new SymbolTable(); binaryReader = new BinaryReader(Utility.StreamFromString(expression)); binaryWriter = new BinaryWriter(new MemoryStream()); Scanner scanner = new Scanner(binaryReader, binaryWriter, symbolTable); @@ -216,7 +217,7 @@ namespace Axiom.Interpreter { LastMessage="Failed to scan the input document, possible invalid character sequence."; IsInError=true; - MDTrace.WriteLine(LogLevel.DEBUG,LastMessage); + MDTrace.WriteLine(LogLevel.DEBUG, LastMessage); return disassembly; } binaryWriter.BaseStream.Seek(0, SeekOrigin.Begin); @@ -230,7 +231,7 @@ namespace Axiom.Interpreter { LastMessage=String.Format("Failed to parse the input, {0} at {1}", parser.LastMessage, parser.LastLineNumber); IsInError=true; - MDTrace.WriteLine(LogLevel.DEBUG,LastMessage); + MDTrace.WriteLine(LogLevel.DEBUG, LastMessage); return disassembly; } parserWriter.BaseStream.Seek(0, SeekOrigin.Begin); @@ -243,7 +244,7 @@ namespace Axiom.Interpreter { LastMessage=String.Format("Exception:{0}",exception.ToString()); IsInError=true; - MDTrace.WriteLine(LogLevel.DEBUG,LastMessage); + MDTrace.WriteLine(LogLevel.DEBUG, LastMessage); return disassembly; } finally @@ -267,7 +268,7 @@ namespace Axiom.Interpreter try { int hashcode = expression.GetHashCode(); - if (Trace) MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Trace:{0}", expression)); + if (Trace) MDTrace.WriteLine(LogLevel.DEBUG,$"Trace:{expression}"); if (precompiles.ContainsKey(hashcode)) { precompiledStream = precompiles[hashcode]; // if the precompiled cache contains the expression then fetch it @@ -285,7 +286,7 @@ namespace Axiom.Interpreter if(assembler.IsInError) { LastMessage=String.Format("Error: Failed to run the assembler, {0}", assembler.LastMessage); - MDTrace.WriteLine(LogLevel.DEBUG,LastMessage); + MDTrace.WriteLine(LogLevel.DEBUG, LastMessage); return false; } assembler.Dispose(); @@ -295,7 +296,7 @@ namespace Axiom.Interpreter catch (Exception exception) { LastMessage=String.Format("Exception:{0}", exception.ToString()); - MDTrace.WriteLine(LogLevel.DEBUG,LastMessage); + MDTrace.WriteLine(LogLevel.DEBUG, LastMessage); return false; } finally @@ -341,7 +342,7 @@ namespace Axiom.Interpreter catch (Exception exception) { axiomResult.LastMessage = String.Format("Exception:{0}", exception.ToString()); - MDTrace.WriteLine(LogLevel.DEBUG,axiomResult.LastMessage); + MDTrace.WriteLine(LogLevel.DEBUG, axiomResult.LastMessage); return axiomResult; } finally @@ -368,7 +369,7 @@ namespace Axiom.Interpreter if(!scanner.Analyze()) { LastMessage = "Failed to scan the input document, possible invalid character sequence"; - MDTrace.WriteLine(LogLevel.DEBUG,LastMessage); + MDTrace.WriteLine(LogLevel.DEBUG, LastMessage); return null; } binaryReader.Close(); @@ -382,7 +383,7 @@ namespace Axiom.Interpreter if (parser.IsInError) { LastMessage = String.Format("Error:{0} at {1}", parser.LastMessage, parser.LastLineNumber); - MDTrace.WriteLine(LogLevel.DEBUG,LastMessage); + MDTrace.WriteLine(LogLevel.DEBUG, LastMessage); return null; } parserReader.Close(); @@ -396,7 +397,7 @@ namespace Axiom.Interpreter } catch (Exception exception) { - MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception:{0}", exception.ToString())); + MDTrace.WriteLine(LogLevel.DEBUG, $"Exception:{exception.ToString()}"); return null; } finally @@ -410,10 +411,10 @@ namespace Axiom.Interpreter { MDTrace.WriteLine(LogLevel.DEBUG,"********************************************************* O U T P U T ************************************************"); List list = new List(symbolTable.Values); - list = (from Symbol symbol in list where symbol.TypeOfSymbol.Equals(Symbol.SymbolType.UserSymbol) select symbol).ToList(); +// list = (from Symbol symbol in list where symbol.TypeOfSymbol.Equals(Symbol.SymbolType.UserSymbol) select symbol).ToList(); foreach (Symbol symbol in list) { - MDTrace.WriteLine(LogLevel.DEBUG,(String.Format("SYMBOL NAME:'{0}',VALUE:'{1}'", symbol.SymbolName, null == symbol.GenericData ? "" : symbol.GenericData.ToString()))); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("SYMBOL NAME:'{0}',VALUE:'{1}'", symbol.SymbolName, null == symbol.GenericData ? "" : symbol.GenericData.ToString())); } } } diff --git a/Axiom/Axiom.Core/Interpreter/Emitter.cs b/Axiom/Axiom.Core/Interpreter/Emitter.cs index e8a9c5e..c5281d5 100755 --- a/Axiom/Axiom.Core/Interpreter/Emitter.cs +++ b/Axiom/Axiom.Core/Interpreter/Emitter.cs @@ -1,11 +1,9 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using Axiom.Utils; -// FileName : Emitter.cs -// Author : Sean Kessler - namespace Axiom.Interpreter { public class Emitter @@ -13,191 +11,232 @@ namespace Axiom.Interpreter private bool emitting = true; private int lastSymbol; private BinaryReader inputStream; - private BinaryWriter outputStream; + private BinaryWriter outputStream; // the assigned output stream + private BinaryWriter activeStream; // the active stream + private Stack writeStack = new Stack(); + private bool isCapturing = false; private bool debug = true; public Emitter(BinaryReader inputStream, BinaryWriter outputStream) { this.inputStream = inputStream; this.outputStream = outputStream; + this.activeStream = outputStream; } + public bool Debug { get { return debug; } set { debug = value; } } + + public void BeginCapture() + { + writeStack.Push(activeStream); + activeStream = new BinaryWriter(new MemoryStream()); + isCapturing = true; + } + + public CapturedCode EndCapture() + { + if (!isCapturing)throw new InvalidOperationException("EndCapture called without a matching BeginCapture."); + activeStream.Flush(); + MemoryStream memoryStream = (MemoryStream)activeStream.BaseStream; + byte[] capturedBytes = memoryStream.ToArray(); + activeStream.Close(); + activeStream.Dispose(); + activeStream = writeStack.Pop(); + isCapturing = writeStack.Count > 0; + return new CapturedCode(capturedBytes); + } + + public void Emit(CapturedCode code) + { + activeStream.Write(code.Bytes,0,code.Length); + } + public void Emit(String literalValue) { if (!emitting) return; - outputStream.Write(literalValue.Length); - outputStream.Write(literalValue); + activeStream.Write(literalValue.Length); + activeStream.Write(literalValue); } -// ************************************************************************ + public void Emit(Scanner.ScanSymbols code) { if (!emitting) return; - outputStream.Write((int)code); - if(Debug)MDTrace.WriteLine(LogLevel.DEBUG,Scanner.SymbolToString(code)); + activeStream.Write((int)code); + if (Debug) MDTrace.WriteLine(LogLevel.DEBUG, Scanner.SymbolToString(code)); } + public void Emit(Scanner.ScanSymbols code,int value) { if (!emitting) return; - outputStream.Write((int)code); - outputStream.Write(value); - if(Debug)MDTrace.WriteLine(LogLevel.DEBUG,Scanner.SymbolToString(code)+","+value.ToString()); + activeStream.Write((int)code); + activeStream.Write(value); + if (Debug) MDTrace.WriteLine(LogLevel.DEBUG, Scanner.SymbolToString(code) + "," + value.ToString()); } + public void Emit(Scanner.ScanSymbols code,double value) { if (!emitting) return; - outputStream.Write((int)code); - outputStream.Write(value); - if(Debug)MDTrace.WriteLine(LogLevel.DEBUG,Scanner.SymbolToString(code)+","+value.ToString()); + activeStream.Write((int)code); + activeStream.Write(value); + if (Debug) MDTrace.WriteLine(LogLevel.DEBUG, Scanner.SymbolToString(code) + "," + value.ToString()); } + public void Emit(Scanner.ScanSymbols code,String value) { if (!emitting) return; - outputStream.Write((int)code); - outputStream.Write(value); - if(Debug)MDTrace.WriteLine(LogLevel.DEBUG,Scanner.SymbolToString(code)+","+value.ToString()); + activeStream.Write((int)code); + activeStream.Write(value); + if (Debug) MDTrace.WriteLine(LogLevel.DEBUG, Scanner.SymbolToString(code) + "," + value.ToString()); } -// ********************************************************************************************************************************************** - public long CodePointer() - { - return outputStream.BaseStream.Position; + + public long CodePointer() + { + return activeStream.BaseStream.Position; } - public void Seek(long position) - { - outputStream.BaseStream.Seek(position, SeekOrigin.Begin); + + public void Seek(long position) + { + activeStream.BaseStream.Seek(position, SeekOrigin.Begin); } + public void Emit(Parser.ParserSymbols code) { if (!emitting) return; - long positionBefore=outputStream.BaseStream.Position; - outputStream.Write((int)code); - long positionAfter=outputStream.BaseStream.Position; - if(Debug)MDTrace.WriteLine(LogLevel.DEBUG,Parser.SymbolToString(code)+"["+positionBefore+","+positionAfter+"]"); - + long positionBefore = activeStream.BaseStream.Position; + activeStream.Write((int)code); + long positionAfter = activeStream.BaseStream.Position; + if (Debug) MDTrace.WriteLine(LogLevel.DEBUG, Parser.SymbolToString(code) + "[" + positionBefore + "," + positionAfter + "]"); } + public void Emit(Parser.ParserSymbols code,Object value) { if (!emitting) return; - long positionBefore=outputStream.BaseStream.Position; - outputStream.Write((int)code); + long positionBefore = activeStream.BaseStream.Position; + activeStream.Write((int)code); Type type = value.GetType(); - outputStream.Write(type.ToString()); - outputStream.Write(value.ToString()); - long positionAfter=outputStream.BaseStream.Position; - if(Debug)MDTrace.WriteLine(LogLevel.DEBUG,Parser.SymbolToString(code)+","+type.ToString()+","+value.ToString()+"["+positionBefore+","+positionAfter+"]"); + activeStream.Write(type.ToString()); + activeStream.Write(value.ToString()); + long positionAfter = activeStream.BaseStream.Position; + if (Debug) MDTrace.WriteLine(LogLevel.DEBUG, Parser.SymbolToString(code) + "," + type.ToString() + "," + value.ToString() + "[" + positionBefore + "," + positionAfter + "]"); } + public void Emit(Parser.ParserSymbols code,Object value,int intValue) { if (!emitting) return; - long positionBefore=outputStream.BaseStream.Position; - outputStream.Write((int)code); + long positionBefore = activeStream.BaseStream.Position; + activeStream.Write((int)code); Type type = value.GetType(); - outputStream.Write(type.ToString()); - outputStream.Write(value.ToString()); - outputStream.Write(intValue); - long positionAfter=outputStream.BaseStream.Position; - if(Debug)MDTrace.WriteLine(LogLevel.DEBUG,Parser.SymbolToString(code)+","+type.ToString()+","+value.ToString()+","+intValue+"["+positionBefore+","+positionAfter+"]"); + activeStream.Write(type.ToString()); + activeStream.Write(value.ToString()); + activeStream.Write(intValue); + long positionAfter = activeStream.BaseStream.Position; + if (Debug) MDTrace.WriteLine(LogLevel.DEBUG, Parser.SymbolToString(code) + "," + type.ToString() + "," + value.ToString() + "," + intValue + "[" + positionBefore + "," + positionAfter + "]"); } + public void Emit(Parser.ParserSymbols code,long value) { if (!emitting) return; - long positionBefore=outputStream.BaseStream.Position; - outputStream.Write((int)code); - outputStream.Write(value); - long positionAfter=outputStream.BaseStream.Position; - if(Debug)MDTrace.WriteLine(LogLevel.DEBUG,Parser.SymbolToString(code)+","+value.ToString()+","+value.ToString()+"["+positionBefore+","+positionAfter+"]"); + long positionBefore = activeStream.BaseStream.Position; + activeStream.Write((int)code); + activeStream.Write(value); + long positionAfter = activeStream.BaseStream.Position; + if (Debug) MDTrace.WriteLine(LogLevel.DEBUG, Parser.SymbolToString(code) + "," + value.ToString() + "," + value.ToString() + "[" + positionBefore + "," + positionAfter + "]"); } + public void EmitAsNull(Parser.ParserSymbols code) { if (!emitting) return; - long positionBefore=outputStream.BaseStream.Position; - outputStream.Write((int)code); - Type type = typeof(System.Nullable); //value.GetType(); - outputStream.Write(type.ToString()); - outputStream.Write("null".ToString()); - long positionAfter=outputStream.BaseStream.Position; - if(Debug)MDTrace.WriteLine(LogLevel.DEBUG,Parser.SymbolToString(code)+","+type.ToString()+","+"null".ToString()+"["+positionBefore+","+positionAfter+"]"); + long positionBefore = activeStream.BaseStream.Position; + activeStream.Write((int)code); + Type type = typeof(System.Nullable); + activeStream.Write(type.ToString()); + activeStream.Write("null".ToString()); + long positionAfter = activeStream.BaseStream.Position; + if (Debug) MDTrace.WriteLine(LogLevel.DEBUG, Parser.SymbolToString(code) + "," + type.ToString() + "," + "null" + "[" + positionBefore + "," + positionAfter + "]"); } -// ************************************************************************ - public void Emit(int code, int op) + + public void Emit(int code,int op) { if (!emitting) return; - outputStream.Write(code); - outputStream.Write(op); + activeStream.Write(code); + activeStream.Write(op); } + public void Emit(int identifier) { if (!emitting) return; - outputStream.Write(identifier); + activeStream.Write(identifier); } + public void Emit(byte value) { if (!emitting) return; - outputStream.Write(value); + activeStream.Write(value); + } + + public int Peek(ref int value) + { + value = inputStream.PeekChar(); + return value; + } + + public int Peek() + { + int value = inputStream.PeekChar(); + return value; + } + + public int PeekIgnore(ref int value,int[] ignoreChars) + { + long streamPosition = inputStream.BaseStream.Position; + while (true) + { + int readValue = (int)inputStream.ReadChar(); + value = readValue; + if (!ignoreChars.Any(x => x.Equals(readValue))) break; + } + inputStream.BaseStream.Seek(streamPosition, SeekOrigin.Begin); + return value; } - public int Peek(ref int value) - { - value = inputStream.PeekChar(); - return value; - } - public int Peek() - { - int value = inputStream.PeekChar(); - return value; - } - public int PeekIgnore(ref int value,int[] ignoreChars) - { - long streamPosition=inputStream.BaseStream.Position; - while(true) - { - int readValue; - readValue=(int)inputStream.ReadChar(); - value=readValue; - if(!ignoreChars.Any(x=>x.Equals(readValue)))break; - } - inputStream.BaseStream.Seek(streamPosition,SeekOrigin.Begin); - return value; - } public int Read() { lastSymbol = inputStream.Read(); return lastSymbol; } + public int Read(ref String literal) { - literal=inputStream.ReadString(); + literal = inputStream.ReadString(); return 0; } + public int Read(ref byte value) { try { value = inputStream.ReadByte(); return 0; } catch (EndOfStreamException) { return 0xFFFF; } } + public int Read(ref int value) { try { value = inputStream.ReadInt32(); return 0; } catch (EndOfStreamException) { return 0xFFFF; } - } + public int Read(ref double value) { try { value = inputStream.ReadDouble(); return 0; } catch (EndOfStreamException) { return 0xFFFF; } } + public bool Emitting { - get - { - return emitting; - } - set - { - emitting = value; - } + get { return emitting; } + set { emitting = value; } } } -} +} \ No newline at end of file diff --git a/Axiom/Axiom.Core/Interpreter/Parser.cs b/Axiom/Axiom.Core/Interpreter/Parser.cs index 916dbb0..2dbae74 100755 --- a/Axiom/Axiom.Core/Interpreter/Parser.cs +++ b/Axiom/Axiom.Core/Interpreter/Parser.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.IO; + // FileName : Parser.cs // Author : Sean Kessler @@ -34,7 +35,7 @@ namespace Axiom.Interpreter private String stringValue; private bool strictStatementMode=false; // is StrictStatementMode is true then the parser will return an error if there is not at least one statement processed. private Stack breakStack = new Stack(); // when we encounter a break statement we need to record the address of the code pointer - private Stack whileStack = new Stack(); // when we encounter a while statement we need to record the event so that we can validate any break statements + private Stack loopStack = new Stack(); // when we encounter a loop (while/for) statement we need to record the event so that we can validate any break statements public Parser(BinaryReader binaryReader, BinaryWriter binaryWriter, SymbolTable symbolTable) : base(binaryReader, binaryWriter) @@ -135,11 +136,16 @@ namespace Axiom.Interpreter BreakStatement(); StatementNumber++; } - else if (Scanner.ScanSymbols.while1.Equals(currentSymbolType)) + else if (Scanner.ScanSymbols.while1.Equals(currentSymbolType)) { WhileStatement(); StatementNumber++; } + else if (Scanner.ScanSymbols.for1.Equals(currentSymbolType)) + { + ForStatement(); + StatementNumber++; + } else if (SymbolIn(termSymbols)) { Term(); @@ -157,6 +163,7 @@ namespace Axiom.Interpreter RemoveSymbols(new ParseSymbol(Scanner.ScanSymbols.endtext1)); return; } + public void DeclarationStatement() { bool moreDeclarations = true; @@ -178,21 +185,24 @@ namespace Axiom.Interpreter } RemoveSymbols(new ParseSymbol(Scanner.ScanSymbols.variable1)); } + public void DirectiveClearModified() { Emit(Parser.ParserSymbols.directive_clear_modified2); Expect(Scanner.ScanSymbols.directive_clear_modified1); } + public void ParseLiteral() { Emit(Parser.ParserSymbols.push2, stringValue); Expect(Scanner.ScanSymbols.literal1); } + public void BreakStatement() { - if(0.Equals(whileStack.Count)) + if(0.Equals(loopStack.Count)) { - SyntaxError("Encountered 'break' without 'while'"); + SyntaxError("Encountered 'break' without 'while'/'for'"); } if(breakStack.Count>0) { @@ -205,6 +215,267 @@ namespace Axiom.Interpreter breakStack.Push(CodePointer()); Expect(Scanner.ScanSymbols.break1); } + // ******************************************************************************************************************************** + // *************************************************** F O R S T A T E M E N T ************************************************* + // ******************************************************************************************************************************** + //INIT + //L1: + // CONDITION + // IF FALSE -> L2 + //BODY + //INCREMENT + //GOTO L1 + //L2: NOOP + public void ForStatement() + { + long codePointerL1; + long codePointerFalseBranch; + long codePointerL2; + long codePointerTerminalAddress; + + loopStack.Push(true); // register this is a for loop + InsertSymbols(expressionSymbols); + InsertSymbols(directiveSymbols); + Expect(Scanner.ScanSymbols.for1); + Expect(Scanner.ScanSymbols.leftparen1); + RemoveSymbols(expressionSymbols); + RemoveSymbols(directiveSymbols); + if (IsInError) { loopStack.Pop(); return; } + DoInitialization(); // Do initialization. I=1 + codePointerL1 = CodePointer(); // execution target codePointerL1 + DoForCondition(); // e.g., I < 10 // emit the for condition + codePointerFalseBranch = CodePointer(); // get the location in the stream for the defaddr2 that follows + Emit(Parser.ParserSymbols.defaddr2, 0L); // emit defaddr2 (jump if false) with dummy address of 0L + BeginCapture(); // start a new code emit frame + DoForIncrement(); // emit the increment code e.g., I = I + 1 + CapturedCode code = EndCapture(); // end the capture and retrieve the emitted code + DoForBlock(); // emit the BODY + Emit(code); // emit the increment + if (IsInError) { loopStack.Pop(); return; } // check error condition + Emit(Parser.ParserSymbols.goto2, codePointerL1); // jump back to condition after the body + codePointerL2 = CodePointer(); // get the location in the stream for the L2 exit location + Emit(Parser.ParserSymbols.noop2); // emit a noop at the current location + codePointerTerminalAddress = CodePointer(); // Record code pointer before seeking + Seek(codePointerFalseBranch); // seek to the defaddr2 statement that we emitted above + Emit(Parser.ParserSymbols.defaddr2, codePointerL2); // rewrite that statement with the correct branch address of L2 exit marker + HandleBreak(codePointerL2); // Handle any breaks + Seek(codePointerTerminalAddress); // seek to Terminal address in order to leave the stream at the correct location at the end of this all. + loopStack.Pop(); + } + + private void DoForIncrement() + { + InsertSymbols(new ParseSymbol(Scanner.ScanSymbols.rightparen1)); + InsertSymbols(statementSymbols); + Expect(Scanner.ScanSymbols.semicolon1); + Statement(); + RemoveSymbols(statementSymbols); + RemoveSymbols(new ParseSymbol(Scanner.ScanSymbols.rightparen1)); + InsertSymbols(new ParseSymbol(Scanner.ScanSymbols.leftcurly1)); + Expect(Scanner.ScanSymbols.rightparen1); + RemoveSymbols(new ParseSymbol(Scanner.ScanSymbols.leftcurly1)); + } + + private void DoForBlock() + { + InsertSymbols(new ParseSymbol(Scanner.ScanSymbols.rightcurly1)); + InsertSymbols(statementSymbols); + Expect(Scanner.ScanSymbols.leftcurly1); // at this point we have { + if(IsInError)return; + while(!SymbolIn(new ParseSymbol(Scanner.ScanSymbols.rightcurly1)) && !IsInError) + { + Statement(); + } + RemoveSymbols(new ParseSymbol(Scanner.ScanSymbols.rightcurly1)); + Expect(Scanner.ScanSymbols.rightcurly1); + if(IsInError)return; + RemoveSymbols(statementSymbols); + } + + + private void DoForCondition() + { + long enterSymbolCount = SymbolCount(); + Stack logicalOperatorStack = new Stack(); + + while (!IsInError) + { + // Stop at the increment separator + if (SymbolIn(new ParseSymbol(Scanner.ScanSymbols.semicolon1))) + break; + + bool inShortFunction = false; + InsertSymbols(expressionSymbols); + if (PeekSymbolIn(directiveSymbols)) inShortFunction = true; + + InsertSymbols(new ParseSymbol(Scanner.ScanSymbols.semicolon1)); + InsertSymbols(logicalSymbols); + + // Parse left-hand side expression and relation + Expression(); + Relation(); + + InsertSymbols(relationSymbols); + SyntaxCheck(); + if (IsInError) return; + + RemoveSymbols(relationSymbols); + RemoveSymbols(logicalSymbols); + RemoveSymbols(new ParseSymbol(Scanner.ScanSymbols.semicolon1)); + + // Handle short functions or logical chaining + if (inShortFunction || SymbolIn(logicalSymbols)) + { + if (SymbolIn(new ParseSymbol(Scanner.ScanSymbols.oror1))) + { + logicalOperatorStack.Push(Parser.ParserSymbols.oror2); + InsertSymbols(expressionSymbols); + InsertSymbols(directiveSymbols); + Expect(currentSymbolType); + RemoveSymbols(expressionSymbols); + RemoveSymbols(expressionSymbols); // yes, twice + RemoveSymbols(directiveSymbols); + continue; + } + else if (SymbolIn(new ParseSymbol(Scanner.ScanSymbols.andand1))) + { + logicalOperatorStack.Push(Parser.ParserSymbols.andand2); + InsertSymbols(expressionSymbols); + InsertSymbols(directiveSymbols); + Expect(currentSymbolType); + RemoveSymbols(expressionSymbols); + RemoveSymbols(expressionSymbols); // yes, twice + RemoveSymbols(directiveSymbols); + continue; + } + } + + // Stop if next symbol is semicolon (increment) + if (SymbolIn(new ParseSymbol(Scanner.ScanSymbols.semicolon1))) + { + RemoveSymbols(expressionSymbols); + break; + } + + // Parse relational operator + ParseSymbol relationSymbol = new ParseSymbol(currentSymbolType); + InsertSymbols(new ParseSymbol(Scanner.ScanSymbols.leftcurly1)); + InsertSymbols(logicalSymbols); + InsertSymbols(directiveSymbols); + Expect(currentSymbolType); + if (IsInError) return; + + RemoveSymbols(expressionSymbols); + RemoveSymbols(directiveSymbols); + + if (!inShortFunction) + { + InsertSymbols(new ParseSymbol(Scanner.ScanSymbols.rightparen1)); + + if (SymbolIn(directiveSymbols)) + { + InsertSymbols(relationSymbols); + Directive(); + Relation(); + RemoveSymbols(relationSymbols); + } + + RemoveSymbols(new ParseSymbol(Scanner.ScanSymbols.rightparen1)); + if (SymbolIn(new ParseSymbol(Scanner.ScanSymbols.rightparen1))) + Expect(Scanner.ScanSymbols.rightparen1); + + EmitRelation(relationSymbol); + } + + // Emit any logical operators in stack + if (logicalOperatorStack.Count != 0) + Emit(logicalOperatorStack.Pop()); + + // Check for trailing logical operators + if (SymbolIn(new ParseSymbol(Scanner.ScanSymbols.oror1))) + { + logicalOperatorStack.Push(Parser.ParserSymbols.oror2); + InsertSymbols(expressionSymbols); + InsertSymbols(directiveSymbols); + Expect(currentSymbolType); + RemoveSymbols(expressionSymbols); + RemoveSymbols(directiveSymbols); + } + else if (SymbolIn(new ParseSymbol(Scanner.ScanSymbols.andand1))) + { + logicalOperatorStack.Push(Parser.ParserSymbols.andand2); + InsertSymbols(expressionSymbols); + InsertSymbols(directiveSymbols); + Expect(currentSymbolType); + RemoveSymbols(expressionSymbols); + RemoveSymbols(directiveSymbols); + } + + RemoveSymbols(logicalSymbols); + RemoveSymbols(new ParseSymbol(Scanner.ScanSymbols.leftcurly1)); + RemoveSymbols(expressionSymbols); + + if (!SymbolIn(expressionSymbols) && !SymbolIn(directiveSymbols)) + break; + } + + // Emit remaining logical operators + while (logicalOperatorStack.Count > 0) + Emit(logicalOperatorStack.Pop()); + + // Check symbol stack consistency + long exitSymbolCount = SymbolCount(); + if (enterSymbolCount != exitSymbolCount) + throw new InvalidOperationException("Symbol counts do not match"); + } + + private void EmitRelation(ParseSymbol relationSymbol) + { + if (relationSymbol.Equals(new ParseSymbol(Scanner.ScanSymbols.notequal1))) + { + Emit(Parser.ParserSymbols.notequal2); + } + else if (relationSymbol.Equals(new ParseSymbol(Scanner.ScanSymbols.equalequal1))) + { + Emit(Parser.ParserSymbols.equalequal2); + } + else if (relationSymbol.Equals(new ParseSymbol(Scanner.ScanSymbols.less1))) + { + Emit(Parser.ParserSymbols.less2); + } + else if (relationSymbol.Equals(new ParseSymbol(Scanner.ScanSymbols.lessequal1))) + { + Emit(Parser.ParserSymbols.lessequal2); + } + else if (relationSymbol.Equals(new ParseSymbol(Scanner.ScanSymbols.greater1))) + { + Emit(Parser.ParserSymbols.greater2); + } + else if (relationSymbol.Equals(new ParseSymbol(Scanner.ScanSymbols.greaterequal1))) + { + Emit(Parser.ParserSymbols.greaterequal2); + } + } + + private void DoInitialization() + { + // for (; ... ) → empty initialization + if(SymbolIn(new ParseSymbol(Scanner.ScanSymbols.semicolon1)))return; + // Reuse your normal assignment parser + Statement(); + } + + private void DoIncrement() + { + if(SymbolIn(new ParseSymbol(Scanner.ScanSymbols.rightparen1))) + { + return; + } + Expression(); + } +// ******************************************************************************************************************************** +// ************************************************** W H I L E S T A T E M E N T ********************************************** +// ******************************************************************************************************************************** /// /// WhileStatement Parses WhileStatement. ///WHILE @@ -221,36 +492,42 @@ namespace Axiom.Interpreter long codePointerL2; long codePointerTerminalAddress; - whileStack.Push(true); // Register that we are processing a while loop + loopStack.Push(true); // Register that we are processing a while loop InsertSymbols(expressionSymbols); InsertSymbols(directiveSymbols); Expect(Scanner.ScanSymbols.while1); Expect(Scanner.ScanSymbols.leftparen1); RemoveSymbols(expressionSymbols); RemoveSymbols(directiveSymbols); - if (IsInError){whileStack.Pop(); return;} + if (IsInError){loopStack.Pop(); return;} codePointerL1 = CodePointer(); // L1: DoBForWhile(); // B - if(IsInError){whileStack.Pop(); return;} + if(IsInError){loopStack.Pop(); return;} codePointerFalseInstructionAddress = CodePointer(); // Record the location of the DEFADDR2 instruction. Emit(Parser.ParserSymbols.defaddr2,0L); // Write DEFADDR2,0L. This will be jump to condition:false DoSForWhile(); // S - if(IsInError){whileStack.Pop(); return;} + if(IsInError){loopStack.Pop(); return;} Emit(Parser.ParserSymbols.goto2,codePointerL1); // Goto(L1) codePointerL2=CodePointer(); // L2: Emit(Parser.ParserSymbols.noop2); // Don't write any instructions past this point codePointerTerminalAddress=CodePointer(); // Record code pointer before seeking Seek(codePointerFalseInstructionAddress); // Seek to DEFADDR2 instruction Emit(Parser.ParserSymbols.defaddr2,codePointerL2); // Write DEFADDR2, L2 - HandleBreak(codePointerL2); // If there was a break statement then handle to jump condition + HandleBreak(codePointerL2); // If there was a break statement then handle the jump to condition Seek(codePointerTerminalAddress); + loopStack.Pop(); } + + /// + /// This is the handling for break statements in while loops + /// For the while statement there will be a DEFADDR, L2 just prior to a NOOP + /// + /// public void HandleBreak(long codePointerL2) { if(0==breakStack.Count)return; Seek(breakStack.Pop()); Emit(Parser.ParserSymbols.goto2,codePointerL2); - } /// /// B Section for While. @@ -1499,6 +1776,12 @@ namespace Axiom.Interpreter else SyntaxError(symbol); SyntaxCheck(); } + + public long SymbolCount() + { + return parseSymbols.Count; + } + public void InsertSymbols(ParseSymbols groupSymbols) { parseSymbols.InsertSymbols(groupSymbols); @@ -1600,6 +1883,7 @@ namespace Axiom.Interpreter statementSymbols.Add(new ParseSymbol(Scanner.ScanSymbols.literal1)); statementSymbols.Add(new ParseSymbol(Scanner.ScanSymbols.if1)); statementSymbols.Add(new ParseSymbol(Scanner.ScanSymbols.while1)); + statementSymbols.Add(new ParseSymbol(Scanner.ScanSymbols.for1)); statementSymbols.Add(new ParseSymbol(Scanner.ScanSymbols.declare1)); statementSymbols.Add(new ParseSymbol(Scanner.ScanSymbols.break1)); } diff --git a/Axiom/Axiom.Core/Interpreter/Scanner.cs b/Axiom/Axiom.Core/Interpreter/Scanner.cs index 8c0fa14..51e1ce6 100755 --- a/Axiom/Axiom.Core/Interpreter/Scanner.cs +++ b/Axiom/Axiom.Core/Interpreter/Scanner.cs @@ -9,7 +9,11 @@ namespace Axiom.Interpreter { public class Scanner : Emitter { - public enum ScanSymbols { unknown1, directive_clear_modified1,declare1, null1, isnull1, convert1, getprice1, substring1, in1, like1, trim1, upper1, lower1, assign1, if1, while1, then1,else1,goto1,equal1,equalequal1,less1,lessequal1,greater1,greaterequal1, notequal1,variable1, asterisk1, apostrophe1, comma1, label1, literal1, leftcurly1, rightcurly1, leftbracket1, rightbracket1, numeral1, char1, divide1, plus1, minus1, leftparen1, rightparen1, newline1, semicolon1,endtext1, andand1, oror1, abs1, not1, pow1, sqrt1, break1, end1 }; + public enum ScanSymbols { unknown1, directive_clear_modified1,declare1, null1, isnull1, convert1, + getprice1, substring1, in1, like1, trim1, upper1, lower1, assign1, if1, while1, for1, then1, else1, goto1, + equal1, equalequal1, less1, lessequal1, greater1, greaterequal1, notequal1, variable1, asterisk1, apostrophe1, + comma1, label1, literal1, leftcurly1, rightcurly1, leftbracket1, rightbracket1, numeral1, char1, divide1, plus1, + minus1, leftparen1, rightparen1, newline1, semicolon1,endtext1, andand1, oror1, abs1, not1, pow1, sqrt1, break1, end1 }; private enum WhiteSpace{spacechar=32,tabchar=9}; private int character; private StringBuilder word; @@ -409,6 +413,8 @@ namespace Axiom.Interpreter return "if"; case Scanner.ScanSymbols.while1 : return "while"; + case Scanner.ScanSymbols.for1 : + return "for"; case Scanner.ScanSymbols.then1 : return "then"; case Scanner.ScanSymbols.else1 : @@ -523,6 +529,8 @@ namespace Axiom.Interpreter return "if1"; case Scanner.ScanSymbols.while1 : return "while1"; + case Scanner.ScanSymbols.for1 : + return "for1"; case Scanner.ScanSymbols.then1 : return "then1"; case Scanner.ScanSymbols.else1 : diff --git a/Axiom/Axiom.Core/Interpreter/SymbolTable.cs b/Axiom/Axiom.Core/Interpreter/SymbolTable.cs index 6f839dd..8148ffd 100755 --- a/Axiom/Axiom.Core/Interpreter/SymbolTable.cs +++ b/Axiom/Axiom.Core/Interpreter/SymbolTable.cs @@ -138,6 +138,7 @@ namespace Axiom.Interpreter Add("if",new Symbol("if",Scanner.ScanSymbols.if1,Symbol.SymbolType.KeywordSymbol)); Add("break",new Symbol("break",Scanner.ScanSymbols.break1,Symbol.SymbolType.KeywordSymbol)); Add("while",new Symbol("while",Scanner.ScanSymbols.while1,Symbol.SymbolType.KeywordSymbol)); + Add("for",new Symbol("for",Scanner.ScanSymbols.for1,Symbol.SymbolType.KeywordSymbol)); Add("then", new Symbol("then", Scanner.ScanSymbols.then1, Symbol.SymbolType.KeywordSymbol)); Add("else", new Symbol("else", Scanner.ScanSymbols.else1, Symbol.SymbolType.KeywordSymbol)); Add("and", new Symbol("and", Scanner.ScanSymbols.andand1, Symbol.SymbolType.KeywordSymbol));