diff --git a/Axiom.Core/Axiom.Core.csproj b/Axiom.Core/Axiom.Core.csproj index f75d3d1..3b7d731 100644 --- a/Axiom.Core/Axiom.Core.csproj +++ b/Axiom.Core/Axiom.Core.csproj @@ -64,6 +64,7 @@ + diff --git a/Axiom.Core/Interpreter/Assembler.cs b/Axiom.Core/Interpreter/Assembler.cs index 989d9de..4030c40 100644 --- a/Axiom.Core/Interpreter/Assembler.cs +++ b/Axiom.Core/Interpreter/Assembler.cs @@ -612,6 +612,7 @@ namespace Axiom.Interpreter lastMessage=exception.ToString(); } } + private void Negate() { try diff --git a/Axiom.Core/Interpreter/CapturedCode.cs b/Axiom.Core/Interpreter/CapturedCode.cs new file mode 100644 index 0000000..eb26fab --- /dev/null +++ b/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.Core/Interpreter/CodeRunner.cs b/Axiom.Core/Interpreter/CodeRunner.cs index 00f1d05..a043e0c 100644 --- a/Axiom.Core/Interpreter/CodeRunner.cs +++ b/Axiom.Core/Interpreter/CodeRunner.cs @@ -29,10 +29,12 @@ namespace Axiom.Interpreter symbolTable=new SymbolTable(); IsInError=false; } + ~CodeRunner() { Dispose(false); } + public void Dispose() { if (!disposed) @@ -41,6 +43,7 @@ namespace Axiom.Interpreter GC.SuppressFinalize(this); } } + protected virtual void Dispose(bool disposing) { if(disposing) @@ -53,32 +56,40 @@ namespace Axiom.Interpreter } disposed = true; } + public int ParseSymbolsCount { get{return parseSymbolCount;} } + public String LastMessage{ get; private set;} + public bool IsInError { get; private set;} + public bool ScanStrict { get{return scanStrict;} set{scanStrict=value;} } + public bool ParseStrict { get{return parseStrict;} set{parseStrict=value;} } + public bool Trace { get {return trace;} set {trace=value;} } + public bool UseCache { get { return useCache; } set { useCache = value; } } + public String GetValue(String name) { if (!symbolTable.ContainsKey(name)) return "null"; @@ -87,6 +98,7 @@ namespace Axiom.Interpreter if (null == genericData || genericData.IsNull()) return "null"; return genericData.Get(); } + public T GetValue(String name) { if(!symbolTable.ContainsKey(name)) return default(T); @@ -95,20 +107,24 @@ namespace Axiom.Interpreter if(null==genericData||genericData.IsNull()) return default(T); return genericData.Get(); } + public SymbolTable SymbolTable { get {return symbolTable;} } + public void Reset() { if (null == symbolTable) return; symbolTable.Reset(); } + public bool Execute(DataTable dataTable, int row, String expression) { Reset(); return ExecuteExpressionOnRow(dataTable, row, expression); } + private bool ExecuteExpressionOnRow(DataTable dataTable, int row, String expression) { symbolTable.AddUserSymbols(dataTable); // add symbol names from the data table columns @@ -123,6 +139,13 @@ namespace Axiom.Interpreter } return true; } + + /// + /// Execute - Executes the given expression. You can then use the helper methods. + /// GetValue(String), GetValue(String) to retrieve resulting variables from the symbolTable + /// + /// + /// public bool Execute(String expression) { BinaryReader binaryReader = null; @@ -161,7 +184,7 @@ namespace Axiom.Interpreter logger.ErrorFormat(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; @@ -196,6 +219,11 @@ namespace Axiom.Interpreter } } + /// + /// Disassemble - This method does not execute code. It scans, parses, and disassembles the geenrated bytecode + /// + /// + /// public List Disassemble(String expression) { BinaryReader binaryReader = null; @@ -210,9 +238,10 @@ namespace Axiom.Interpreter { IsInError=false; LastMessage=""; + SymbolTable localSymbolTable = new SymbolTable(); binaryReader = new BinaryReader(Utility.StreamFromString(expression)); binaryWriter = new BinaryWriter(new MemoryStream()); - Scanner scanner = new Scanner(binaryReader, binaryWriter, symbolTable); + Scanner scanner = new Scanner(binaryReader, binaryWriter, localSymbolTable); scanner.Debug = Trace; if (!scanner.Analyze()) { @@ -224,7 +253,7 @@ namespace Axiom.Interpreter binaryWriter.BaseStream.Seek(0, SeekOrigin.Begin); parserReader = new BinaryReader(binaryWriter.BaseStream); parserWriter = new BinaryWriter(new MemoryStream()); - Parser parser = new Parser(parserReader, parserWriter, symbolTable); + Parser parser = new Parser(parserReader, parserWriter, localSymbolTable); parser.Debug = Trace; parser.Parse(); parseSymbolCount=parser.ParseSymbolsCount; @@ -237,7 +266,7 @@ namespace Axiom.Interpreter } parserWriter.BaseStream.Seek(0, SeekOrigin.Begin); assemblerReader = new BinaryReader(parserWriter.BaseStream); - assembler = new Assembler(assemblerReader, symbolTable); + assembler = new Assembler(assemblerReader, localSymbolTable); assembler.Debug = Trace; return assembler.Disassemble(); } @@ -412,7 +441,7 @@ namespace Axiom.Interpreter { logger.Info("********************************************************* 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) { logger.Info(String.Format("SYMBOL NAME:'{0}',VALUE:'{1}'", symbol.SymbolName, null == symbol.GenericData ? "" : symbol.GenericData.ToString())); diff --git a/Axiom.Core/Interpreter/Emitter.cs b/Axiom.Core/Interpreter/Emitter.cs index ddb7513..f119770 100644 --- a/Axiom.Core/Interpreter/Emitter.cs +++ b/Axiom.Core/Interpreter/Emitter.cs @@ -1,204 +1,248 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; -using log4net; - -// FileName : Emitter.cs -// Author : Sean Kessler +using MarketData; namespace Axiom.Interpreter { public class Emitter { - private static ILog logger = LogManager.GetLogger(typeof(Emitter)); 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 Stack captureStack = 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 void 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; + captureStack.Push(new CapturedCode(capturedBytes)); + } + + /// + /// EmitCapture - Later, for CONTINUE semantics, we can Peek() at the top of the captureStack to get the increment code without popping it. + /// + public void EmitCapture() + { + if(0==captureStack.Count)throw new InvalidOperationException("The CaptureList is empty."); + CapturedCode code = captureStack.Pop(); + 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)logger.Info(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)logger.Info(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)logger.Info(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)logger.Info(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)logger.Info(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)logger.Info(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)logger.Info(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)logger.Info(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)logger.Info(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.Core/Interpreter/Parser.cs b/Axiom.Core/Interpreter/Parser.cs index 4e82b2b..2af96ad 100644 --- a/Axiom.Core/Interpreter/Parser.cs +++ b/Axiom.Core/Interpreter/Parser.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.IO; +using System.Runtime.Remoting.Messaging; // 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 + EndCapture(); // end the capture and retrieve the emitted code + DoForBlock(); // emit the BODY + EmitCapture(); // emit the code block we captured above between BeginCapture and EndCapture + 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.Core/Interpreter/Scanner.cs b/Axiom.Core/Interpreter/Scanner.cs index d3bc546..5c7cd2a 100644 --- a/Axiom.Core/Interpreter/Scanner.cs +++ b/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.Core/Interpreter/SymbolTable.cs b/Axiom.Core/Interpreter/SymbolTable.cs index 0855668..6cf0320 100644 --- a/Axiom.Core/Interpreter/SymbolTable.cs +++ b/Axiom.Core/Interpreter/SymbolTable.cs @@ -140,6 +140,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)); diff --git a/AxiomConsole/App.config b/AxiomConsole/App.config index 8d23437..ecdcf8a 100644 --- a/AxiomConsole/App.config +++ b/AxiomConsole/App.config @@ -1,6 +1,6 @@ - + diff --git a/AxiomConsole/AxiomConsole.csproj b/AxiomConsole/AxiomConsole.csproj index b557efe..3ac3c07 100644 --- a/AxiomConsole/AxiomConsole.csproj +++ b/AxiomConsole/AxiomConsole.csproj @@ -9,7 +9,7 @@ Properties AxiomConsole AxiomConsole - v4.6.2 + v4.7.2 512 diff --git a/AxiomConsole/Program.cs b/AxiomConsole/Program.cs index 805b663..f1b227b 100644 --- a/AxiomConsole/Program.cs +++ b/AxiomConsole/Program.cs @@ -22,52 +22,61 @@ namespace AxiomConsole // IF(A==1)THEN BREAK; // A=A+1; // }"; - String expression=@" - A=1; - B=1; - C=1; - D=1; - WHILE(A<10) - { - WHILE(B<10) + //String expression = @" + // J=1; + // FOR(I=1;I<10;I=I+1) + // { + // J=J+10; + // break; + // } + // "; + + //String expression = @" + // J=1; + // FOR(I=1;I<10;I=I+1) + // { + // J=J+10; + // } + // "; + + String expression = @" + DECLARE A,J,TOTAL; + A=1; + J=1; + TOTAL=0; + FOR(A=1;A<=10;A=A+1) { - WHILE(C<10) + FOR(J=1;J<=10;J=J+1) { - WHILE(D<10) - { - A=A+1; - B=B+1; - C=C+1; - D=D+1; - IF(D==10)THEN BREAK; - BREAK; - } BREAK; + TOTAL=TOTAL+1 } - BREAK; } - BREAK; - }"; + "; + + CodeRunner codeRunner = new CodeRunner(); + codeRunner.Trace=true; //if (!codeRunner.Execute(expression)) //{ // Console.WriteLine("CodeRunner Failed with {0}", codeRunner.LastMessage); // Console.Read(); //} - List disassembly=codeRunner.Disassemble(expression); - if(codeRunner.IsInError) - { - Console.WriteLine("CodeRunner Failed with {0}",codeRunner.LastMessage); - } - else - { - Console.WriteLine(expression); - foreach(String line in disassembly) - { - Console.WriteLine(line); - } - } + + List disassembly = codeRunner.Disassemble(expression); + if (codeRunner.IsInError) + { + Console.WriteLine("CodeRunner Failed with {0}", codeRunner.LastMessage); + } else + { + Console.WriteLine(expression); + foreach (String line in disassembly) + { + Console.WriteLine(line); + } + } + Console.Read(); } } diff --git a/AxiomUnitTestProject/AssemblerTests.cs b/AxiomUnitTestProject/AssemblerTests.cs index 84422b9..1b3749c 100644 --- a/AxiomUnitTestProject/AssemblerTests.cs +++ b/AxiomUnitTestProject/AssemblerTests.cs @@ -176,6 +176,71 @@ namespace AxiomUnitTestProject Assert.IsTrue(codeRunner.GetValue("B").Equals(5)); } + [TestMethod] + public void ForStatementVariant3() + { + String expression=@" + DECLARE A,J,TOTAL; + A=1; + J=1; + TOTAL=0; + FOR(A=1;A<=10;A=A+1) + { + FOR(J=1;J<=10;J=J+1) + { + BREAK; + TOTAL=TOTAL+1 + } + } + "; + CodeRunner codeRunner = new CodeRunner(); + Assert.IsTrue(codeRunner.Execute(expression),codeRunner.LastMessage); + Assert.IsTrue(codeRunner.GetValue("J").Equals(1)); + Assert.IsTrue(codeRunner.GetValue("A").Equals(11)); + Assert.IsTrue(codeRunner.GetValue("TOTAL").Equals(0)); + } + + [TestMethod] + public void ForStatementVariant2() + { + String expression=@" + DECLARE A,J,TOTAL; + A=1; + J=1; + TOTAL=0; + FOR(A=1;A<=10;A=A+1) + { + FOR(J=1;J<=10;J=J+1) + { + TOTAL=TOTAL+1; + } + } + "; + CodeRunner codeRunner = new CodeRunner(); + Assert.IsTrue(codeRunner.Execute(expression),codeRunner.LastMessage); + Assert.IsTrue(codeRunner.GetValue("J").Equals(11)); + Assert.IsTrue(codeRunner.GetValue("A").Equals(11)); + Assert.IsTrue(codeRunner.GetValue("TOTAL").Equals(100)); + } + + [TestMethod] + public void ForStatementVariant1() + { + String expression=@" + DECLARE A,J; + A=1; + J=1; + FOR(A=1;A<10;A=A+1) + { + J=J+1; + } + "; + CodeRunner codeRunner = new CodeRunner(); + Assert.IsTrue(codeRunner.Execute(expression),codeRunner.LastMessage); + Assert.IsTrue(codeRunner.GetValue("J").Equals(10)); + Assert.IsTrue(codeRunner.GetValue("A").Equals(10)); + } + [TestMethod] public void WhileStatementVariant4() {