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 class CompilationContext
{ {
public Dictionary<string, MObject> Variables { get; } = new Dictionary<string, MObject>();
public static CompilationContext Empty => new CompilationContext(); public static CompilationContext Empty => new CompilationContext();
} }
} }

View File

@ -1,21 +1,31 @@
using Parser.Internal; using Parser.Internal;
using Parser.MFunctions;
using Parser.Objects; using Parser.Objects;
using System; using System;
using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
namespace Parser namespace Parser
{ {
internal class EvaluationScope
{
public Dictionary<string, MObject> Variables { get; } = new Dictionary<string, MObject>();
}
internal class Evaluator internal class Evaluator
{ {
private SyntaxTree _syntaxTree; private readonly SyntaxTree _syntaxTree;
private CompilationContext _context; private readonly CompilationContext _context;
private DiagnosticsBag _diagnostics; private readonly DiagnosticsBag _diagnostics = new DiagnosticsBag();
private bool _insideFunction = false;
private readonly Stack<EvaluationScope> _scopeStack = new Stack<EvaluationScope>();
public Evaluator(SyntaxTree syntaxTree, CompilationContext context) public Evaluator(SyntaxTree syntaxTree, CompilationContext context)
{ {
_syntaxTree = syntaxTree; _syntaxTree = syntaxTree;
_context = context; _context = context;
_diagnostics = new DiagnosticsBag(); var outerScope = new EvaluationScope();
_scopeStack.Push(outerScope);
} }
internal EvaluationResult Evaluate() internal EvaluationResult Evaluate()
@ -178,7 +188,7 @@ namespace Parser
private MObject? EvaluateParenthesizedExpression(ParenthesizedExpressionSyntaxNode expression) private MObject? EvaluateParenthesizedExpression(ParenthesizedExpressionSyntaxNode expression)
{ {
throw new NotImplementedException(); return EvaluateExpression(expression.Expression);
} }
private MObject? EvaluateClassInvokation(BaseClassInvokationSyntaxNode expression) private MObject? EvaluateClassInvokation(BaseClassInvokationSyntaxNode expression)
@ -243,17 +253,47 @@ namespace Parser
private MObject? EvaluateNumberLiteralExpression(NumberLiteralSyntaxNode expression) private MObject? EvaluateNumberLiteralExpression(NumberLiteralSyntaxNode expression)
{ {
throw new NotImplementedException(); return expression.Number.Value is double value
? MObject.CreateDoubleNumber(value)
: null;
} }
private MObject? EvaluateIdentifierNameExpression(IdentifierNameExpressionSyntaxNode expression) 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) 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) private MObject? EvaluateCompoundName(CompoundNameExpressionSyntaxNode expression)
@ -273,9 +313,69 @@ namespace Parser
private MObject? EvaluateAssignmentExpression(AssignmentExpressionSyntaxNode expression) 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(); 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) private MObject? EvaluateLambdaExpression(LambdaExpressionSyntaxNode expression)
{ {
throw new NotImplementedException(); throw new NotImplementedException();

View File

@ -1,4 +1,5 @@
using System.Collections; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -67,6 +68,11 @@ namespace Parser.Internal
Report(span, "Unmatched open parenthesis by the end of file."); Report(span, "Unmatched open parenthesis by the end of file.");
} }
internal void ReportCannotEvaluateExpression(TextSpan span)
{
Report(span, $"Cannot evaluate expression.");
}
public IEnumerator<Diagnostic> GetEnumerator() public IEnumerator<Diagnostic> GetEnumerator()
{ {
return _diagnostics.GetEnumerator(); return _diagnostics.GetEnumerator();
@ -77,5 +83,9 @@ namespace Parser.Internal
return GetEnumerator(); 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); internal abstract Parser.SyntaxNode CreateRed(Parser.SyntaxNode parent, int position);
public virtual object? GetValue()
{
return null;
}
protected int _fullWidth; protected int _fullWidth;
public int FullWidth => _fullWidth; public int FullWidth => _fullWidth;

View File

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

View File

@ -116,6 +116,7 @@ namespace Parser.Internal
_fullWidth = text?.Length ?? 0; _fullWidth = text?.Length ?? 0;
} }
public override void WriteTokenTo(TextWriter writer, bool leading, bool trailing) public override void WriteTokenTo(TextWriter writer, bool leading, bool trailing)
{ {
writer.Write(_text); writer.Write(_text);
@ -126,7 +127,9 @@ namespace Parser.Internal
return new SyntaxTokenWithValue<T>(Kind, _text, _value, diagnostics); 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> internal class SyntaxTokenWithValueAndTrivia<T> : SyntaxTokenWithValue<T>
@ -181,7 +184,7 @@ namespace Parser.Internal
public override GreenNode SetDiagnostics(TokenDiagnostic[] diagnostics) 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); 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 virtual int Width => Text.Length;
public override IReadOnlyList<SyntaxTrivia> LeadingTriviaCore => s_EmptySyntaxTriviaList; 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 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 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 int Position { get; }
public object? Value => _token.GetValue();
public bool Equals(SyntaxToken other) public bool Equals(SyntaxToken other)
{ {
return Equals(_parent, other._parent) && Equals(_token, other._token); return Equals(_parent, other._parent) && Equals(_token, other._token);

View File

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