Skip to content

Commit

Permalink
Implement handling of goto. Allow functions to be part of an expressi…
Browse files Browse the repository at this point in the history
…on (e.g. function call in if() statement)
  • Loading branch information
ethanmoffat committed Jan 30, 2022
1 parent 81a0279 commit 4421603
Show file tree
Hide file tree
Showing 10 changed files with 91 additions and 55 deletions.
2 changes: 1 addition & 1 deletion EOBot/EOBot.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
<Compile Include="Interpreter\States\ExpressionEvaluator.cs" />
<Compile Include="Interpreter\States\ExpressionTailEvaluator.cs" />
<Compile Include="Interpreter\States\FunctionEvaluator.cs" />
<Compile Include="Interpreter\States\GotoEvaluator.cs" />
<Compile Include="Interpreter\States\IfEvaluator.cs" />
<Compile Include="Interpreter\States\IScriptEvaluator.cs" />
<Compile Include="Interpreter\States\KeywordEvaluator.cs" />
Expand All @@ -126,7 +127,6 @@
<Compile Include="Interpreter\Variables\IIdentifiable.cs" />
<Compile Include="Interpreter\Variables\IntVariable.cs" />
<Compile Include="Interpreter\Variables\IVariable.cs" />
<Compile Include="Interpreter\Variables\LabelIdentifier.cs" />
<Compile Include="Interpreter\Variables\StringVariable.cs" />
<Compile Include="NamesList.cs" />
<Compile Include="Program.cs" />
Expand Down
1 change: 1 addition & 0 deletions EOBot/Interpreter/BotInterpreter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public void Run(ArgumentsParser parsedArgs, IReadOnlyList<BotToken> tokens)
evaluators.Add(new ExpressionTailEvaluator(evaluators));
evaluators.Add(new OperandEvaluator(evaluators));
evaluators.Add(new IfEvaluator(evaluators));
evaluators.Add(new GotoEvaluator());

var scriptEvaluator = new ScriptEvaluator(evaluators);

Expand Down
2 changes: 1 addition & 1 deletion EOBot/Interpreter/BotTokenParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public BotToken GetNextToken()
if (char.IsLetter(inputChar))
{
var identifier = inputChar.ToString();
while (char.IsLetterOrDigit((char)_inputStream.Peek()))
while (char.IsLetterOrDigit(Peek()) || Peek() == '_')
identifier += Read();

var type = Keywords.Contains(identifier)
Expand Down
31 changes: 19 additions & 12 deletions EOBot/Interpreter/States/ExpressionEvaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,22 +44,29 @@ public bool Evaluate(ProgramState input)
{
// an expression can be a function call
if (_evaluators.OfType<FunctionEvaluator>().Single().Evaluate(input))
return true;

if (!_evaluators.OfType<OperandEvaluator>().Single().Evaluate(input))
return false;

// expression_tail is optional
if (!_evaluators.OfType<ExpressionTailEvaluator>().Single().Evaluate(input))
{
if (input.OperationStack.Count == 0)
// there may or may not be an expression tail after a function call
if (!_evaluators.OfType<ExpressionTailEvaluator>().Single().Evaluate(input))
return true;
}
else
{

if (!_evaluators.OfType<OperandEvaluator>().Single().Evaluate(input))
return false;

// convert to variable token (resolve identifier) so consumer of expression result can use it
var singleOperand = GetOperand(input);
input.OperationStack.Push(singleOperand);
// expression_tail is optional
if (!_evaluators.OfType<ExpressionTailEvaluator>().Single().Evaluate(input))
{
if (input.OperationStack.Count == 0)
return false;

return true;
// convert to variable token (resolve identifier) so consumer of expression result can use it
var singleOperand = GetOperand(input);
input.OperationStack.Push(singleOperand);

return true;
}
}
}

Expand Down
23 changes: 23 additions & 0 deletions EOBot/Interpreter/States/GotoEvaluator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using EOBot.Interpreter.Extensions;

namespace EOBot.Interpreter.States
{
public class GotoEvaluator : IScriptEvaluator
{
public bool Evaluate(ProgramState input)
{
// ensure we have the right keyword before advancing the program
var current = input.Current();
if (current.TokenType != BotTokenType.Keyword || current.TokenValue != "goto")
return false;

input.Expect(BotTokenType.Keyword);

if (!input.Match(BotTokenType.Identifier))
return false;

var label = input.OperationStack.Pop().TokenValue;
return input.Goto(input.Labels[label]);
}
}
}
4 changes: 4 additions & 0 deletions EOBot/Interpreter/States/IfEvaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ public bool Evaluate(ProgramState input)
if ((input.Expect(BotTokenType.LBrace) && !_evaluators.OfType<StatementListEvaluator>().Single().Evaluate(input)) ||
!_evaluators.OfType<StatementEvaluator>().Single().Evaluate(input))
return false;

// hack: put the \n token back since StatementList/Statement will have consumed it
if (input.Program[input.ExecutionIndex - 1].TokenType == BotTokenType.NewLine)
input.Goto(input.ExecutionIndex - 1);
}
else if (input.Expect(BotTokenType.LBrace))
{
Expand Down
4 changes: 2 additions & 2 deletions EOBot/Interpreter/States/KeywordEvaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ public KeywordEvaluator(IEnumerable<IScriptEvaluator> evaluators)

public bool Evaluate(ProgramState input)
{
return Evaluate<IfEvaluator>(input);
return Evaluate<IfEvaluator>(input)
//|| Evaluate<WhileEvaluator>(input)
//|| Evaluate<GotoEvaluator>(input);
|| Evaluate<GotoEvaluator>(input);
}

private bool Evaluate<T>(ProgramState input)
Expand Down
23 changes: 3 additions & 20 deletions EOBot/Interpreter/States/LabelEvaluator.cs
Original file line number Diff line number Diff line change
@@ -1,27 +1,10 @@
using EOBot.Interpreter.Variables;

namespace EOBot.Interpreter.States
namespace EOBot.Interpreter.States
{
public class LabelEvaluator : IScriptEvaluator
{
public bool Evaluate(ProgramState input)
{
if (!input.MatchPair(BotTokenType.Identifier, BotTokenType.Colon))
return false;

if (input.OperationStack.Count < 2)
return false;

// pop the colon
input.OperationStack.Pop();

var labelToken = input.OperationStack.Pop();
if (labelToken.TokenType != BotTokenType.Identifier)
return false;

input.Labels.Add(new LabelIdentifier(labelToken.TokenValue), input.ExecutionIndex);

return true;
return input.ExpectPair(BotTokenType.Identifier, BotTokenType.Colon);
}
}
}
}
45 changes: 37 additions & 8 deletions EOBot/Interpreter/States/ProgramState.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using EOBot.Interpreter.Variables;
using System;
using System.Collections.Generic;
using System.Linq;

namespace EOBot.Interpreter.States
{
Expand All @@ -11,7 +13,7 @@ public class ProgramState

public Dictionary<string, (bool ReadOnly, IIdentifiable Identifiable)> SymbolTable { get; }

public Dictionary<LabelIdentifier, int> Labels { get; }
public Dictionary<string, int> Labels { get; }

public int ExecutionIndex { get; private set; }

Expand All @@ -20,10 +22,27 @@ public ProgramState(IReadOnlyList<BotToken> program)
OperationStack = new Stack<BotToken>();
Program = program;
SymbolTable = new Dictionary<string, (bool ReadOnly, IIdentifiable Identifiable)>();
Labels = new Dictionary<LabelIdentifier, int>();
Labels = Program
.Select((token, ndx) => (token, ndx))
.Where(x => x.token.TokenType == BotTokenType.Identifier && Program[x.ndx + 1].TokenType == BotTokenType.Colon)
.ToDictionary(x => x.token.TokenValue, y => y.ndx + 2);
ExecutionIndex = 0;
}

public void SkipToken()
{
ExecutionIndex++;
}

public bool Goto(int executionIndex)
{
if (executionIndex >= Program.Count)
return false;

ExecutionIndex = executionIndex;
return true;
}

/// <summary>
/// Check for a token at the program's execution index. If it is the expected type, increment execution index.
/// </summary>
Expand All @@ -41,11 +60,6 @@ public bool Expect(BotTokenType tokenType)
return false;
}

internal void SkipToken()
{
ExecutionIndex++;
}

/// <summary>
/// Check for a token at the program's execution index. If it is the expected type, push it onto the operation stack and increment execution index.
/// </summary>
Expand All @@ -64,10 +78,25 @@ public bool Match(BotTokenType tokenType)
return false;
}

public bool ExpectPair(BotTokenType first, BotTokenType second)
{
if (ExecutionIndex >= Program.Count - 1)
return false;

if (Program[ExecutionIndex].TokenType == first &&
Program[ExecutionIndex + 1].TokenType == second)
{
ExecutionIndex += 2;
return true;
}

return false;
}

/// <summary>
/// Matches a pair of tokens in order at the program's execution index.
/// </summary>
internal bool MatchPair(BotTokenType first, BotTokenType second)
public bool MatchPair(BotTokenType first, BotTokenType second)
{
if (ExecutionIndex >= Program.Count - 1)
return false;
Expand Down
11 changes: 0 additions & 11 deletions EOBot/Interpreter/Variables/LabelIdentifier.cs

This file was deleted.

0 comments on commit 4421603

Please sign in to comment.