Evaluation in REPL

This commit is contained in:
Alexander Luzgarev 2020-07-14 18:48:00 +02:00
parent df0cd4dea3
commit 886b897a4c
11 changed files with 248 additions and 17 deletions

View File

@ -1,7 +1,11 @@
namespace Parser
using Parser.Objects;
using System.Collections.Generic;
namespace Parser
{
public class CompilationContext
{
public Dictionary<string, MObject> Variables { get; } = new Dictionary<string, MObject>();
public static CompilationContext Empty => new CompilationContext();
}
}

View File

@ -1,21 +1,31 @@
using Parser.Internal;
using Parser.MFunctions;
using Parser.Objects;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
namespace Parser
{
internal class EvaluationScope
{
public Dictionary<string, MObject> Variables { get; } = new Dictionary<string, MObject>();
}
internal class Evaluator
{
private SyntaxTree _syntaxTree;
private CompilationContext _context;
private DiagnosticsBag _diagnostics;
private readonly SyntaxTree _syntaxTree;
private readonly CompilationContext _context;
private readonly DiagnosticsBag _diagnostics = new DiagnosticsBag();
private bool _insideFunction = false;
private readonly Stack<EvaluationScope> _scopeStack = new Stack<EvaluationScope>();
public Evaluator(SyntaxTree syntaxTree, CompilationContext context)
{
_syntaxTree = syntaxTree;
_context = context;
_diagnostics = new DiagnosticsBag();
var outerScope = new EvaluationScope();
_scopeStack.Push(outerScope);
}
internal EvaluationResult Evaluate()
@ -178,7 +188,7 @@ namespace Parser
private MObject? EvaluateParenthesizedExpression(ParenthesizedExpressionSyntaxNode expression)
{
throw new NotImplementedException();
return EvaluateExpression(expression.Expression);
}
private MObject? EvaluateClassInvokation(BaseClassInvokationSyntaxNode expression)
@ -243,17 +253,47 @@ namespace Parser
private MObject? EvaluateNumberLiteralExpression(NumberLiteralSyntaxNode expression)
{
throw new NotImplementedException();
return expression.Number.Value is double value
? MObject.CreateDoubleNumber(value)
: null;
}
private MObject? EvaluateIdentifierNameExpression(IdentifierNameExpressionSyntaxNode expression)
{
throw new NotImplementedException();
var variableName = expression.Name.Text;
var maybeValue = GetVariableValue(variableName);
if (maybeValue is null)
{
_diagnostics.ReportVariableNotFound(
new TextSpan(expression.Name.Position, expression.Name.Text.Length),
variableName);
}
return maybeValue;
}
private MObject? EvaluateBinaryOperation(BinaryOperationExpressionSyntaxNode expression)
{
throw new NotImplementedException();
var left = EvaluateExpression(expression.Lhs);
if (left is null)
{
return null;
}
var right = EvaluateExpression(expression.Rhs);
if (right is null)
{
return null;
}
return expression.Operation.Kind switch
{
TokenKind.PlusToken => MOperations.Plus(left, right),
TokenKind.MinusToken => MOperations.Minus(left, right),
TokenKind.StarToken => MOperations.Star(left, right),
TokenKind.SlashToken => MOperations.Slash(left, right),
_ => throw new NotImplementedException($"Binary operation {expression.Operation.Kind} is not implemented."),
};
}
private MObject? EvaluateCompoundName(CompoundNameExpressionSyntaxNode expression)
@ -273,9 +313,69 @@ namespace Parser
private MObject? EvaluateAssignmentExpression(AssignmentExpressionSyntaxNode expression)
{
var rightValue = EvaluateExpression(expression.Rhs);
if (rightValue is null)
{
_diagnostics.ReportCannotEvaluateExpression(
new TextSpan(expression.Rhs.Position, expression.Rhs.Position + expression.Rhs.FullWidth));
return null;
}
var left = expression.Lhs;
if (left.Kind == TokenKind.IdentifierNameExpression)
{
var leftIdentifier = (IdentifierNameExpressionSyntaxNode)left;
var variableName = leftIdentifier.Name.Text;
SetVariableValue(variableName, rightValue);
return rightValue;
}
throw new NotImplementedException();
}
private MObject? GetVariableValue(string name)
{
if (_insideFunction)
{
if (_context.Variables.TryGetValue(name, out var globalValue))
{
return globalValue;
}
var currentScope = _scopeStack.Peek();
return currentScope.Variables.TryGetValue(name, out var localValue) ? globalValue : null;
}
else
{
if (_context.Variables.TryGetValue(name, out var globalValue))
{
return globalValue;
}
return null;
}
}
private void SetVariableValue(string name, MObject value)
{
if (_insideFunction)
{
if (_context.Variables.ContainsKey(name))
{
_context.Variables[name] = value;
}
else
{
var currentScope = _scopeStack.Peek();
currentScope.Variables[name] = value;
}
}
else
{
_context.Variables[name] = value;
}
}
private MObject? EvaluateLambdaExpression(LambdaExpressionSyntaxNode expression)
{
throw new NotImplementedException();

View File

@ -1,4 +1,5 @@
using System.Collections;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
@ -67,6 +68,11 @@ namespace Parser.Internal
Report(span, "Unmatched open parenthesis by the end of file.");
}
internal void ReportCannotEvaluateExpression(TextSpan span)
{
Report(span, $"Cannot evaluate expression.");
}
public IEnumerator<Diagnostic> GetEnumerator()
{
return _diagnostics.GetEnumerator();
@ -77,5 +83,9 @@ namespace Parser.Internal
return GetEnumerator();
}
internal void ReportVariableNotFound(TextSpan span, string variableName)
{
Report(span, $"Variable '{variableName}' not found.");
}
}
}

View File

@ -43,6 +43,11 @@ namespace Parser.Internal
internal abstract Parser.SyntaxNode CreateRed(Parser.SyntaxNode parent, int position);
public virtual object? GetValue()
{
return null;
}
protected int _fullWidth;
public int FullWidth => _fullWidth;

View File

@ -13,6 +13,7 @@ namespace Parser.Internal
public string Text { get; set; } = "";
public string StringValue { get; set; } = "";
public double DoubleValue { get; set; }
public bool ImaginaryFlag { get; set; }
}
private ITextWindow Window { get; }
@ -358,20 +359,48 @@ namespace Parser.Internal
if (success)
{
tokenInfo.Kind = TokenKind.NumberLiteralToken;
Range rangeToParse;
if (Window.PeekChar(n) == 'i' || Window.PeekChar(n) == 'j')
{
tokenInfo.ImaginaryFlag = true;
n++;
rangeToParse = ..^1;
}
else
{
rangeToParse = ..;
}
var s = Window.GetAndConsumeChars(n);
tokenInfo.Kind = TokenKind.NumberLiteralToken;
var s = Window.GetAndConsumeChars(n);
tokenInfo.Text = s;
var maybeValue = ParseDoubleValue(s[rangeToParse]);
if (maybeValue is double value)
{
tokenInfo.DoubleValue = value;
return true;
}
else
{
tokenInfo.DoubleValue = double.NaN;
return true;
}
}
return false;
}
private double? ParseDoubleValue(string s)
{
if (double.TryParse(s, out var doubleValue))
{
return doubleValue;
} else
{
return null;
}
}
private bool ContinueLexingGeneralStringLiteral(ref TokenInfo tokenInfo, char quote)
{
var status = 0; // no errors

View File

@ -116,6 +116,7 @@ namespace Parser.Internal
_fullWidth = text?.Length ?? 0;
}
public override void WriteTokenTo(TextWriter writer, bool leading, bool trailing)
{
writer.Write(_text);
@ -126,7 +127,9 @@ namespace Parser.Internal
return new SyntaxTokenWithValue<T>(Kind, _text, _value, diagnostics);
}
public T Value => _value;
public T TypedValue => _value;
public override object? Value => TypedValue;
}
internal class SyntaxTokenWithValueAndTrivia<T> : SyntaxTokenWithValue<T>
@ -181,7 +184,7 @@ namespace Parser.Internal
public override GreenNode SetDiagnostics(TokenDiagnostic[] diagnostics)
{
return new SyntaxTokenWithValueAndTrivia<T>(Kind, _text, Value, LeadingTrivia, TrailingTrivia, diagnostics);
return new SyntaxTokenWithValueAndTrivia<T>(Kind, _text, TypedValue, LeadingTrivia, TrailingTrivia, diagnostics);
}
}
@ -311,6 +314,10 @@ namespace Parser.Internal
internal static SyntaxToken NoneToken => new MissingTokenWithTrivia(TokenKind.None, s_EmptySyntaxTriviaList, s_EmptySyntaxTriviaList);
public virtual object? Value => null;
public override object? GetValue() => Value;
public virtual int Width => Text.Length;
public override IReadOnlyList<SyntaxTrivia> LeadingTriviaCore => s_EmptySyntaxTriviaList;

View File

@ -0,0 +1,50 @@
using Parser.Objects;
namespace Parser.MFunctions
{
public static class MOperations
{
public static MObject? Plus(MObject left, MObject right)
{
if (left is MDoubleNumber { Value: var lValue }
&& right is MDoubleNumber { Value: var rValue })
{
return MObject.CreateDoubleNumber(lValue + rValue);
}
return null;
}
public static MObject? Minus(MObject left, MObject right)
{
if (left is MDoubleNumber { Value: var lValue }
&& right is MDoubleNumber { Value: var rValue })
{
return MObject.CreateDoubleNumber(lValue - rValue);
}
return null;
}
public static MObject? Star(MObject left, MObject right)
{
if (left is MDoubleNumber { Value: var lValue }
&& right is MDoubleNumber { Value: var rValue })
{
return MObject.CreateDoubleNumber(lValue * rValue);
}
return null;
}
public static MObject? Slash(MObject left, MObject right)
{
if (left is MDoubleNumber { Value: var lValue }
&& right is MDoubleNumber { Value: var rValue })
{
return MObject.CreateDoubleNumber(lValue / rValue);
}
return null;
}
}
}

View File

@ -1,6 +1,24 @@
namespace Parser.Objects
using System.Globalization;
namespace Parser.Objects
{
public class MDoubleNumber : MObject
{
private MDoubleNumber(double value)
{
Value = value;
}
public double Value { get; }
public static MDoubleNumber Create(double value)
{
return new MDoubleNumber(value);
}
public override string ToString()
{
return Value.ToString(CultureInfo.InvariantCulture);
}
}
}

View File

@ -2,5 +2,9 @@
{
public abstract class MObject
{
public static MDoubleNumber CreateDoubleNumber(double value)
{
return MDoubleNumber.Create(value);
}
}
}

View File

@ -29,6 +29,8 @@ namespace Parser
public int Position { get; }
public object? Value => _token.GetValue();
public bool Equals(SyntaxToken other)
{
return Equals(_parent, other._parent) && Equals(_token, other._token);

View File

@ -36,7 +36,9 @@ namespace Repl
private void Print(string result)
{
Console.Write(result);
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine(result);
Console.ResetColor();
}
private string Read()