Work on Diagnostics
This commit is contained in:
parent
ab61a01573
commit
699377cdfc
|
@ -0,0 +1,86 @@
|
|||
using System.Collections.Generic;
|
||||
using Upsilon.Text;
|
||||
|
||||
namespace Upsilon
|
||||
{
|
||||
public class Diagnostics
|
||||
{
|
||||
public SourceText ScriptString { get; }
|
||||
public readonly List<DiagnosticsMessage> Messages = new List<DiagnosticsMessage>();
|
||||
|
||||
public Diagnostics(SourceText scriptString)
|
||||
{
|
||||
ScriptString = scriptString;
|
||||
}
|
||||
|
||||
public void Log(DiagnosticLevel level, string message, TextSpan location)
|
||||
{
|
||||
Messages.Add(new DiagnosticsMessage(this, level, message, location));
|
||||
}
|
||||
|
||||
public void LogError(string message, TextSpan location)
|
||||
{
|
||||
Log(DiagnosticLevel.Error, message, location);
|
||||
}
|
||||
|
||||
public void LogBadCharacter(TextSpan location)
|
||||
{
|
||||
LogError($"Invalid character found", location);
|
||||
}
|
||||
|
||||
public void LogUnknownVariable(TextSpan span, string variable)
|
||||
{
|
||||
LogError($"Unknown variable '{variable}'", span);
|
||||
}
|
||||
|
||||
public void LogNullReferenceError(TextSpan span)
|
||||
{
|
||||
LogError($"Null Reference Encountered", span);
|
||||
}
|
||||
}
|
||||
|
||||
public class DiagnosticsMessage
|
||||
{
|
||||
public Diagnostics Diagnostics { get; }
|
||||
private readonly DiagnosticLevel _diagnosticLevel;
|
||||
public string Message { get; }
|
||||
public TextSpan Span { get; }
|
||||
|
||||
public DiagnosticsMessage(Diagnostics diagnostics, DiagnosticLevel diagnosticLevel, string message, TextSpan span)
|
||||
{
|
||||
_diagnosticLevel = diagnosticLevel;
|
||||
Diagnostics = diagnostics;
|
||||
Message = message;
|
||||
Span = span;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Message} at {Span.Start}\n{Diagnostics.ScriptString.GetSpan(Span)}";
|
||||
}
|
||||
|
||||
public string BeforeError(int i = 5)
|
||||
{
|
||||
return Diagnostics.ScriptString.GetSpan(Span.Start - i, i);
|
||||
}
|
||||
|
||||
public string AtError()
|
||||
{
|
||||
return Diagnostics.ScriptString.GetSpan(Span);
|
||||
}
|
||||
|
||||
public string AfterError(int i = 5)
|
||||
{
|
||||
return Diagnostics.ScriptString.GetSpan(Span.Start + 1, i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public enum DiagnosticLevel
|
||||
{
|
||||
Information,
|
||||
Warning,
|
||||
Error,
|
||||
}
|
||||
|
||||
}
|
|
@ -5,11 +5,13 @@ namespace Upsilon.Evaluator
|
|||
{
|
||||
public class Evaluator
|
||||
{
|
||||
private readonly Diagnostics _diagnostics;
|
||||
public Script Script { get; }
|
||||
public VariableScope Scope { get; }
|
||||
|
||||
public Evaluator(Script script, VariableScope scope)
|
||||
public Evaluator(Script script, VariableScope scope, Diagnostics diagnostics)
|
||||
{
|
||||
_diagnostics = diagnostics;
|
||||
Script = script;
|
||||
Scope = scope;
|
||||
}
|
||||
|
@ -65,26 +67,34 @@ namespace Upsilon.Evaluator
|
|||
{
|
||||
var left = EvaluateExpression(e.Left);
|
||||
var right = EvaluateExpression(e.Right);
|
||||
switch (e.Operator.Kind)
|
||||
try
|
||||
{
|
||||
case SyntaxKind.Plus:
|
||||
return (double)left + (double)right;
|
||||
case SyntaxKind.Minus:
|
||||
return (double)left - (double)right;
|
||||
case SyntaxKind.Star:
|
||||
return (double)left * (double)right;
|
||||
case SyntaxKind.Slash:
|
||||
return (double)left / (double)right;
|
||||
case SyntaxKind.AndKeyword:
|
||||
return (bool)left && (bool)right;
|
||||
case SyntaxKind.OrKeyword:
|
||||
return (bool)left || (bool)right;
|
||||
case SyntaxKind.EqualsEquals:
|
||||
return Equals(left, right);
|
||||
case SyntaxKind.TildeEquals:
|
||||
return !Equals(left, right);
|
||||
default:
|
||||
throw new Exception("Invalid Binary Operator: " + e.Operator.Kind);
|
||||
switch (e.Operator.Kind)
|
||||
{
|
||||
case SyntaxKind.Plus:
|
||||
return (double) left + (double) right;
|
||||
case SyntaxKind.Minus:
|
||||
return (double) left - (double) right;
|
||||
case SyntaxKind.Star:
|
||||
return (double) left * (double) right;
|
||||
case SyntaxKind.Slash:
|
||||
return (double) left / (double) right;
|
||||
case SyntaxKind.AndKeyword:
|
||||
return (bool) left && (bool) right;
|
||||
case SyntaxKind.OrKeyword:
|
||||
return (bool) left || (bool) right;
|
||||
case SyntaxKind.EqualsEquals:
|
||||
return Equals(left, right);
|
||||
case SyntaxKind.TildeEquals:
|
||||
return !Equals(left, right);
|
||||
default:
|
||||
throw new Exception("Invalid Binary Operator: " + e.Operator.Kind);
|
||||
}
|
||||
}
|
||||
catch (NullReferenceException)
|
||||
{
|
||||
_diagnostics.LogNullReferenceError(e.Span);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,7 +120,9 @@ namespace Upsilon.Evaluator
|
|||
{
|
||||
return value;
|
||||
}
|
||||
throw new Exception("Unknown variable: " + varName);
|
||||
|
||||
_diagnostics.LogUnknownVariable(e.Span, varName);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,27 +1,31 @@
|
|||
using System.Collections.Generic;
|
||||
using Upsilon.Parser;
|
||||
using Upsilon.Text;
|
||||
|
||||
namespace Upsilon.Evaluator
|
||||
{
|
||||
public class Script : VariableScope
|
||||
{
|
||||
private string ScriptString { get; }
|
||||
private SourceText ScriptString { get; }
|
||||
private Evaluator Evaluator { get; }
|
||||
public readonly ScriptSyntax Parsed;
|
||||
public Diagnostics Diagnostics { get; }
|
||||
|
||||
public Script(string scriptString)
|
||||
{
|
||||
ScriptString = scriptString;
|
||||
Evaluator = new Evaluator(this, this);
|
||||
Parsed = Parser.Parser.Parse(scriptString);
|
||||
ScriptString = new SourceText(scriptString);
|
||||
Diagnostics = new Diagnostics(ScriptString);
|
||||
Parsed = Parser.Parser.Parse(scriptString, Diagnostics);
|
||||
Evaluator = new Evaluator(this, this, Diagnostics);
|
||||
}
|
||||
|
||||
public Script(string scriptString, Dictionary<string, object> variables = null)
|
||||
:base(variables: variables)
|
||||
{
|
||||
ScriptString = scriptString;
|
||||
Evaluator = new Evaluator(this, this);
|
||||
Parsed = Parser.Parser.Parse(scriptString);
|
||||
ScriptString = new SourceText(scriptString);
|
||||
Diagnostics = new Diagnostics(ScriptString);
|
||||
Evaluator = new Evaluator(this, this, Diagnostics);
|
||||
Parsed = Parser.Parser.Parse(scriptString, Diagnostics);
|
||||
}
|
||||
|
||||
public object Evaluate()
|
||||
|
|
|
@ -47,7 +47,7 @@ namespace Upsilon.Evaluator
|
|||
{
|
||||
return _parentScope.TryGetVariable(key, out result);
|
||||
}
|
||||
throw new Exception("Variable not found: " + key);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Upsilon.Parser
|
||||
{
|
||||
public class BadExpressionSyntax : ExpressionSyntax
|
||||
{
|
||||
public override SyntaxKind Kind => SyntaxKind.BadExpression;
|
||||
public override IEnumerable<SyntaxNode> ChildNodes()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +1,24 @@
|
|||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Text;
|
||||
using Upsilon.Text;
|
||||
|
||||
namespace Upsilon.Parser
|
||||
{
|
||||
public class Lexer
|
||||
{
|
||||
private readonly string _text;
|
||||
private readonly Diagnostics _diagnostics;
|
||||
private int _position;
|
||||
|
||||
private Lexer(string text)
|
||||
private Lexer(string text, Diagnostics diagnostics)
|
||||
{
|
||||
_text = text;
|
||||
_diagnostics = diagnostics;
|
||||
}
|
||||
|
||||
public static ImmutableArray<SyntaxToken> Lex(string text)
|
||||
public static ImmutableArray<SyntaxToken> Lex(string text, Diagnostics diagnostics)
|
||||
{
|
||||
var lexer = new Lexer(text);
|
||||
var lexer = new Lexer(text, diagnostics);
|
||||
return lexer.Lex();
|
||||
}
|
||||
|
||||
|
@ -97,7 +99,8 @@ namespace Upsilon.Parser
|
|||
default:
|
||||
if (char.IsLetter(Current))
|
||||
return LexIdentifierOrKeyword();
|
||||
throw new Exception("Unknown token character: " + Current);
|
||||
_diagnostics.LogBadCharacter(new TextSpan(_position, 1));
|
||||
return new SyntaxToken(SyntaxKind.BadToken, _position, "", null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,7 +116,8 @@ namespace Upsilon.Parser
|
|||
{
|
||||
if (hasDecimalPoint)
|
||||
{
|
||||
throw new Exception("No second decimal allowed there");
|
||||
_diagnostics.LogBadCharacter(new TextSpan(_position, 1));
|
||||
return new SyntaxToken(SyntaxKind.BadToken, _position, "", null);
|
||||
}
|
||||
hasDecimalPoint = true;
|
||||
}
|
||||
|
|
|
@ -1,22 +1,25 @@
|
|||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using Upsilon.Text;
|
||||
|
||||
namespace Upsilon.Parser
|
||||
{
|
||||
public class Parser
|
||||
{
|
||||
private readonly ImmutableArray<SyntaxToken> _tokens;
|
||||
private readonly Diagnostics _diagnostics;
|
||||
private int _position;
|
||||
|
||||
private Parser(ImmutableArray<SyntaxToken> tokens)
|
||||
private Parser(ImmutableArray<SyntaxToken> tokens, Diagnostics diagnostics)
|
||||
{
|
||||
_tokens = tokens;
|
||||
_diagnostics = diagnostics;
|
||||
}
|
||||
|
||||
public static ScriptSyntax Parse(string text)
|
||||
public static ScriptSyntax Parse(string text, Diagnostics diagnostics)
|
||||
{
|
||||
var tokens = Lexer.Lex(text);
|
||||
return new Parser(tokens).ParseScriptSyntax();
|
||||
var tokens = Lexer.Lex(text, diagnostics);
|
||||
return new Parser(tokens, diagnostics).ParseScriptSyntax();
|
||||
}
|
||||
|
||||
private SyntaxToken Current => Get(0);
|
||||
|
@ -41,6 +44,7 @@ namespace Upsilon.Parser
|
|||
if (Current.Kind == kind)
|
||||
return NextToken();
|
||||
|
||||
_diagnostics.LogBadCharacter(new TextSpan(_position, 1));
|
||||
return new SyntaxToken(kind, Current.Span.Start, "", null);
|
||||
}
|
||||
|
||||
|
@ -121,7 +125,9 @@ namespace Upsilon.Parser
|
|||
var token = MatchToken(SyntaxKind.Identifier);
|
||||
return new VariableExpressionSyntax((IdentifierToken) token);
|
||||
default:
|
||||
throw new Exception("Unknown primary expression type: " + Current.Kind);
|
||||
_diagnostics.LogBadCharacter(new TextSpan(_position, 1));
|
||||
NextToken();
|
||||
return new BadExpressionSyntax();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,10 +2,12 @@ namespace Upsilon.Parser
|
|||
{
|
||||
public enum SyntaxKind
|
||||
{
|
||||
// tokens
|
||||
// misc
|
||||
EndOfFile,
|
||||
WhiteSpace,
|
||||
BadToken,
|
||||
|
||||
// tokens
|
||||
Number,
|
||||
Plus,
|
||||
Minus,
|
||||
|
@ -35,6 +37,7 @@ namespace Upsilon.Parser
|
|||
ParenthesizedExpression,
|
||||
AssignmentExpression,
|
||||
VariableExpression,
|
||||
BadExpression,
|
||||
|
||||
// script unit
|
||||
ScriptUnit,
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
|
||||
namespace Upsilon.Text
|
||||
{
|
||||
public class SourceText
|
||||
{
|
||||
private readonly string _text;
|
||||
|
||||
public SourceText(string text)
|
||||
{
|
||||
_text = text;
|
||||
}
|
||||
|
||||
public string GetSpan(int start, int length)
|
||||
{
|
||||
if (start < 0)
|
||||
{
|
||||
length += start;
|
||||
start = 0;
|
||||
};
|
||||
if (start + length >= _text.Length) length = _text.Length - start;
|
||||
return _text.Substring(start, length);
|
||||
}
|
||||
|
||||
public string GetSpan(TextSpan span)
|
||||
{
|
||||
return GetSpan(span.Start, span.Length);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Upsilon;
|
||||
using Upsilon.Evaluator;
|
||||
using Upsilon.Utilities;
|
||||
|
||||
namespace Yc
|
||||
{
|
||||
|
@ -21,10 +21,51 @@ namespace Yc
|
|||
}
|
||||
|
||||
var parsed = new Script(input, variables);
|
||||
//Console.WriteLine(parsed.Parsed.Print());
|
||||
Console.WriteLine(parsed.Evaluate());
|
||||
variables = parsed.Variables;
|
||||
if (parsed.Diagnostics.Messages.Count > 0)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine("Errors were found during parsing");
|
||||
foreach (var diagnosticsMessage in parsed.Diagnostics.Messages)
|
||||
{
|
||||
LogError(diagnosticsMessage);
|
||||
}
|
||||
Console.ResetColor();
|
||||
continue;
|
||||
}
|
||||
|
||||
var evaluate = parsed.Evaluate();
|
||||
if (parsed.Diagnostics.Messages.Count > 0)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine("Errors were found during evaluating");
|
||||
foreach (var diagnosticsMessage in parsed.Diagnostics.Messages)
|
||||
{
|
||||
LogError(diagnosticsMessage);
|
||||
}
|
||||
Console.ResetColor();
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(evaluate);
|
||||
variables = parsed.Variables;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void LogError(DiagnosticsMessage message)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine(message.Message);
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
Console.Write(message.BeforeError());
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.Write(message.AtError());
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
Console.Write(message.AfterError());
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue