Created
July 25, 2023 15:43
-
-
Save jessefreeman/ac8e6154f12769623450bc9f21b66836 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Text; | |
namespace PixelVision8 | |
{ | |
public static class StringExtension | |
{ | |
/// <summary> | |
/// Checks string object's value to array of string values | |
/// </summary> | |
/// <param name="stringValues">Array of string values to compare</param> | |
/// <returns>Return true if any string value matches</returns> | |
public static string RemoveQuotes(this string value) | |
{ | |
if(value.StartsWith('"') && value.EndsWith('"')) | |
{ | |
value = value.Substring(1, value.Length - 2); | |
} | |
return value; | |
} | |
} | |
public partial class Interpreter | |
{ | |
public Random Random = new Random(); | |
// public TextReader InputReader { get; } | |
public GameChipTextWriter OutputWriter { get; } | |
public ErrorTextWriter ErrorWriter { get; } | |
public bool Running => _running; | |
private SortedDictionary<int, IStatement> _statements; | |
private Stack<int> _stack = new Stack<int>(); | |
private int _currentLineNumber; | |
private Stack<ForState> _forStack = new Stack<ForState>(); | |
private Dictionary<string, int> _numberVariables = new Dictionary<string, int>(); | |
private bool _running = false; | |
private int _dataIndex = -1; | |
private List<Value> _data = new List<Value>(); | |
// public Interpreter(GameChipTextWriter outputWriter, ErrorTextWriter errorWriter) | |
// : this(outputWriter, errorWriter) | |
// { | |
// } | |
public Interpreter(GameChipTextWriter outputWriter, | |
ErrorTextWriter errorWriter) | |
{ | |
// TODO need to load this into memory | |
// _statements = statements; | |
// _currentLineNumber = statements.First().Key; | |
// InputReader = inputReader; | |
OutputWriter = outputWriter; | |
ErrorWriter = errorWriter; | |
} | |
public void Load(string program) | |
{ | |
// Clear the existing program | |
_program.Clear(); | |
// Get all of the lines | |
var lines = program.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); | |
// Loop through each line | |
for (int i = 0; i < lines.Length; i++) | |
{ | |
// Use the built in ProcessNewLine() method to parse the line | |
ProcessNewLine(lines[i].Trim()); | |
} | |
// Tell the compiler we are done | |
End(); | |
} | |
private static SortedDictionary<int, IStatement> ParseStatementsFromProgramText(string program, GameChipTextWriter outputwriter, ErrorTextWriter errorWriter) | |
{ | |
var programStream = new MemoryStream(Encoding.UTF8.GetBytes(program)); | |
var statementParser = new StatementParser(); | |
var statements = statementParser.Parse(programStream, outputwriter, errorWriter); | |
return statements; | |
} | |
public void ParseFromString(string program) | |
{ | |
var programStream = new MemoryStream(Encoding.UTF8.GetBytes(program)); | |
Console.WriteLine("Loading program... {0} bytes", programStream.Length); | |
var statementParser = new StatementParser(); | |
_statements = statementParser.Parse(programStream, OutputWriter, ErrorWriter); | |
_currentLineNumber = _statements.First().Key; | |
} | |
public Func<string, string> LoadScript { get; set; } | |
public void LoadFromFile(string scriptName) | |
{ | |
var script = LoadScript(scriptName); | |
if (script != String.Empty) | |
{ | |
Load(script); | |
return; | |
} | |
throw new InterpreterException(Errors.ScriptNotFound, scriptName); | |
} | |
public void Start() | |
{ | |
_running = true; | |
Next(); | |
} | |
public void Next() | |
{ | |
try | |
{ | |
if (_running && !Finished) | |
{ | |
if (_statements.TryGetValue(_currentLineNumber, out var statement)) | |
{ | |
statement.Execute(this); | |
} | |
} | |
} | |
catch (InterpreterException e) | |
{ | |
ErrorWriter.WriteLine(e.Message); | |
Console.WriteLine(e); | |
} | |
} | |
public void Stop() | |
{ | |
// Don't call this again if the interpreter is already stopped | |
if(_running == false) return; | |
// TODO this is a hack to not show the line number when running a single line of code (the line number defaults to 0) but will fail if a program is using 0 as a starting line | |
if (_currentLineNumber > 0) | |
{ | |
// It's important to note that we don't use the error writer here because it would create a never ending loop | |
OutputWriter.WriteLine($"! Stopped at line {_currentLineNumber}"); | |
} | |
// Stop the interpreter | |
_running = false; | |
} | |
public void End() | |
{ | |
_currentLineNumber = Int32.MaxValue; | |
// TODO need to make sure this is displayed when the program ends | |
OutputWriter.WriteLine("OK"); | |
// TODO - this is a hack to get the program to end | |
_running = false; | |
} | |
public bool Finished => _currentLineNumber == int.MaxValue; | |
public void AdvanceLine() | |
{ | |
var currentIndex = _statements.Keys.ToList().BinarySearch(_currentLineNumber); | |
_currentLineNumber = _statements.Keys.ElementAtOrDefault(currentIndex + 1) != 0 | |
? _statements.Keys.ElementAtOrDefault(currentIndex + 1) | |
: int.MaxValue; | |
} | |
public void GotoLine(int targetLine) | |
{ | |
if (_statements.ContainsKey(targetLine)) | |
{ | |
_currentLineNumber = targetLine; | |
} | |
else | |
{ | |
throw new ArgumentException(); | |
} | |
} | |
public void PopLineNumber() | |
{ | |
if (!_stack.TryPop(out _currentLineNumber)) | |
{ | |
throw new InvalidOperationException(); | |
} | |
} | |
public void PushLineNumber() | |
{ | |
_stack.Push(_currentLineNumber); | |
} | |
public ForState CreateForState(string variableName, int limit, int step) | |
{ | |
return new ForState(variableName, limit, _currentLineNumber, step); | |
} | |
public void PushForLoop(ForState forState) | |
{ | |
_forStack.Push(forState); | |
} | |
public ForState PopForLoop() | |
{ | |
return _forStack.Pop(); | |
} | |
// public int ReadNumberVariable(string variableName) | |
// { | |
// return _numberVariables[variableName]; | |
// } | |
// | |
// public void WriteNumberVariable(string variableName, int value) | |
// { | |
// _numberVariables[variableName] = value; | |
// } | |
private Dictionary<string, Value> _variables = new Dictionary<string, Value>(); | |
public Value ReadVariable(string variableName, params byte[]? indexes) | |
{ | |
// Check to see if the variable exits and read from it | |
if (_variables.ContainsKey(variableName)) | |
return _variables[variableName].Read(indexes); | |
// if we are trying to access an array that wasn't defined, throw an error | |
if(indexes != null) | |
throw new InterpreterException(Errors.VarNotFound, variableName); | |
// Reading a variable that doesn't exist should return 0 | |
return new Value(0d); | |
} | |
public bool HasVariable(string variableName) | |
{ | |
return _variables.ContainsKey(variableName); | |
} | |
public void WriteVariable(string variableName, Value value, params byte[] indexes) | |
{ | |
if (_variables.ContainsKey(variableName)) | |
{ | |
_variables[variableName] = _variables[variableName].Write(value, indexes);//.Write(value, indexes); | |
} | |
else | |
{ | |
if (indexes != null && indexes.Length > 0 && !value.IsArray) | |
throw new InterpreterException(Errors.VarNotFound, variableName); | |
_variables.Add(variableName, value); | |
} | |
} | |
public void WriteData(Value value) | |
{ | |
_data.Add(value); | |
} | |
public Value ReadData() | |
{ | |
_dataIndex++; | |
if(_dataIndex >= _data.Count) throw new InterpreterException(Errors.DataOutOfBounds); | |
return _data[_dataIndex]; | |
} | |
public void ResetData() | |
{ | |
_dataIndex = -1; | |
} | |
public bool Paused => _pause; | |
public bool WaitingForInput => _waitingForInput; | |
public bool Waiting => _waiting; | |
private bool _waiting; | |
private bool _pause = false; | |
private bool _waitingForInput = false; | |
private string _inputVariableName; | |
private ValueType _inputVariableType; | |
public void WaitForInput(string variableName) | |
{ | |
_pause = true; | |
_waitingForInput = true; | |
_inputVariableName = variableName; | |
_inputVariableType = variableName.EndsWith("$") ? ValueType.Text : ValueType.Number; | |
} | |
public int CurrentLineNumber => _currentLineNumber; | |
public void AcceptInput(string input) | |
{ | |
_pause = false; | |
_waitingForInput = false; | |
Value? value = null; | |
try | |
{ | |
if (_inputVariableType == ValueType.Number) | |
{ | |
// TODO need to remove white space | |
WriteVariable(_inputVariableName, new Value(Int32.Parse(input))); | |
} | |
else | |
{ | |
// Need to remove any quotes? | |
WriteVariable(_inputVariableName, new Value(input)); | |
} | |
} | |
catch (Exception e) | |
{ | |
// TODO this is a number issue | |
Console.WriteLine(e); | |
throw; | |
} | |
// Go to the next line if there is one | |
AdvanceLine(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment