Evaluation in REPL
This commit is contained in:
parent
df0cd4dea3
commit
886b897a4c
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -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;
|
||||
|
50
Parser/MFunctions/MOperations.cs
Normal file
50
Parser/MFunctions/MOperations.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -2,5 +2,9 @@
|
||||
{
|
||||
public abstract class MObject
|
||||
{
|
||||
public static MDoubleNumber CreateDoubleNumber(double value)
|
||||
{
|
||||
return MDoubleNumber.Create(value);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user