Skip to content

Instantly share code, notes, and snippets.

@jessefreeman
Created July 25, 2023 15:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jessefreeman/ac8e6154f12769623450bc9f21b66836 to your computer and use it in GitHub Desktop.
Save jessefreeman/ac8e6154f12769623450bc9f21b66836 to your computer and use it in GitHub Desktop.
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