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
|
public class Evaluator
|
||||||
{
|
{
|
||||||
|
private readonly Diagnostics _diagnostics;
|
||||||
public Script Script { get; }
|
public Script Script { get; }
|
||||||
public VariableScope Scope { get; }
|
public VariableScope Scope { get; }
|
||||||
|
|
||||||
public Evaluator(Script script, VariableScope scope)
|
public Evaluator(Script script, VariableScope scope, Diagnostics diagnostics)
|
||||||
{
|
{
|
||||||
|
_diagnostics = diagnostics;
|
||||||
Script = script;
|
Script = script;
|
||||||
Scope = scope;
|
Scope = scope;
|
||||||
}
|
}
|
||||||
|
@ -65,26 +67,34 @@ namespace Upsilon.Evaluator
|
||||||
{
|
{
|
||||||
var left = EvaluateExpression(e.Left);
|
var left = EvaluateExpression(e.Left);
|
||||||
var right = EvaluateExpression(e.Right);
|
var right = EvaluateExpression(e.Right);
|
||||||
switch (e.Operator.Kind)
|
try
|
||||||
{
|
{
|
||||||
case SyntaxKind.Plus:
|
switch (e.Operator.Kind)
|
||||||
return (double)left + (double)right;
|
{
|
||||||
case SyntaxKind.Minus:
|
case SyntaxKind.Plus:
|
||||||
return (double)left - (double)right;
|
return (double) left + (double) right;
|
||||||
case SyntaxKind.Star:
|
case SyntaxKind.Minus:
|
||||||
return (double)left * (double)right;
|
return (double) left - (double) right;
|
||||||
case SyntaxKind.Slash:
|
case SyntaxKind.Star:
|
||||||
return (double)left / (double)right;
|
return (double) left * (double) right;
|
||||||
case SyntaxKind.AndKeyword:
|
case SyntaxKind.Slash:
|
||||||
return (bool)left && (bool)right;
|
return (double) left / (double) right;
|
||||||
case SyntaxKind.OrKeyword:
|
case SyntaxKind.AndKeyword:
|
||||||
return (bool)left || (bool)right;
|
return (bool) left && (bool) right;
|
||||||
case SyntaxKind.EqualsEquals:
|
case SyntaxKind.OrKeyword:
|
||||||
return Equals(left, right);
|
return (bool) left || (bool) right;
|
||||||
case SyntaxKind.TildeEquals:
|
case SyntaxKind.EqualsEquals:
|
||||||
return !Equals(left, right);
|
return Equals(left, right);
|
||||||
default:
|
case SyntaxKind.TildeEquals:
|
||||||
throw new Exception("Invalid Binary Operator: " + e.Operator.Kind);
|
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;
|
return value;
|
||||||
}
|
}
|
||||||
throw new Exception("Unknown variable: " + varName);
|
|
||||||
|
_diagnostics.LogUnknownVariable(e.Span, varName);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,31 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Upsilon.Parser;
|
using Upsilon.Parser;
|
||||||
|
using Upsilon.Text;
|
||||||
|
|
||||||
namespace Upsilon.Evaluator
|
namespace Upsilon.Evaluator
|
||||||
{
|
{
|
||||||
public class Script : VariableScope
|
public class Script : VariableScope
|
||||||
{
|
{
|
||||||
private string ScriptString { get; }
|
private SourceText ScriptString { get; }
|
||||||
private Evaluator Evaluator { get; }
|
private Evaluator Evaluator { get; }
|
||||||
public readonly ScriptSyntax Parsed;
|
public readonly ScriptSyntax Parsed;
|
||||||
|
public Diagnostics Diagnostics { get; }
|
||||||
|
|
||||||
public Script(string scriptString)
|
public Script(string scriptString)
|
||||||
{
|
{
|
||||||
ScriptString = scriptString;
|
ScriptString = new SourceText(scriptString);
|
||||||
Evaluator = new Evaluator(this, this);
|
Diagnostics = new Diagnostics(ScriptString);
|
||||||
Parsed = Parser.Parser.Parse(scriptString);
|
Parsed = Parser.Parser.Parse(scriptString, Diagnostics);
|
||||||
|
Evaluator = new Evaluator(this, this, Diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Script(string scriptString, Dictionary<string, object> variables = null)
|
public Script(string scriptString, Dictionary<string, object> variables = null)
|
||||||
:base(variables: variables)
|
:base(variables: variables)
|
||||||
{
|
{
|
||||||
ScriptString = scriptString;
|
ScriptString = new SourceText(scriptString);
|
||||||
Evaluator = new Evaluator(this, this);
|
Diagnostics = new Diagnostics(ScriptString);
|
||||||
Parsed = Parser.Parser.Parse(scriptString);
|
Evaluator = new Evaluator(this, this, Diagnostics);
|
||||||
|
Parsed = Parser.Parser.Parse(scriptString, Diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Evaluate()
|
public object Evaluate()
|
||||||
|
|
|
@ -47,7 +47,7 @@ namespace Upsilon.Evaluator
|
||||||
{
|
{
|
||||||
return _parentScope.TryGetVariable(key, out result);
|
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.Collections.Immutable;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using Upsilon.Text;
|
||||||
|
|
||||||
namespace Upsilon.Parser
|
namespace Upsilon.Parser
|
||||||
{
|
{
|
||||||
public class Lexer
|
public class Lexer
|
||||||
{
|
{
|
||||||
private readonly string _text;
|
private readonly string _text;
|
||||||
|
private readonly Diagnostics _diagnostics;
|
||||||
private int _position;
|
private int _position;
|
||||||
|
|
||||||
private Lexer(string text)
|
private Lexer(string text, Diagnostics diagnostics)
|
||||||
{
|
{
|
||||||
_text = text;
|
_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();
|
return lexer.Lex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +99,8 @@ namespace Upsilon.Parser
|
||||||
default:
|
default:
|
||||||
if (char.IsLetter(Current))
|
if (char.IsLetter(Current))
|
||||||
return LexIdentifierOrKeyword();
|
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)
|
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;
|
hasDecimalPoint = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,25 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
|
using Upsilon.Text;
|
||||||
|
|
||||||
namespace Upsilon.Parser
|
namespace Upsilon.Parser
|
||||||
{
|
{
|
||||||
public class Parser
|
public class Parser
|
||||||
{
|
{
|
||||||
private readonly ImmutableArray<SyntaxToken> _tokens;
|
private readonly ImmutableArray<SyntaxToken> _tokens;
|
||||||
|
private readonly Diagnostics _diagnostics;
|
||||||
private int _position;
|
private int _position;
|
||||||
|
|
||||||
private Parser(ImmutableArray<SyntaxToken> tokens)
|
private Parser(ImmutableArray<SyntaxToken> tokens, Diagnostics diagnostics)
|
||||||
{
|
{
|
||||||
_tokens = tokens;
|
_tokens = tokens;
|
||||||
|
_diagnostics = diagnostics;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ScriptSyntax Parse(string text)
|
public static ScriptSyntax Parse(string text, Diagnostics diagnostics)
|
||||||
{
|
{
|
||||||
var tokens = Lexer.Lex(text);
|
var tokens = Lexer.Lex(text, diagnostics);
|
||||||
return new Parser(tokens).ParseScriptSyntax();
|
return new Parser(tokens, diagnostics).ParseScriptSyntax();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SyntaxToken Current => Get(0);
|
private SyntaxToken Current => Get(0);
|
||||||
|
@ -41,6 +44,7 @@ namespace Upsilon.Parser
|
||||||
if (Current.Kind == kind)
|
if (Current.Kind == kind)
|
||||||
return NextToken();
|
return NextToken();
|
||||||
|
|
||||||
|
_diagnostics.LogBadCharacter(new TextSpan(_position, 1));
|
||||||
return new SyntaxToken(kind, Current.Span.Start, "", null);
|
return new SyntaxToken(kind, Current.Span.Start, "", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +125,9 @@ namespace Upsilon.Parser
|
||||||
var token = MatchToken(SyntaxKind.Identifier);
|
var token = MatchToken(SyntaxKind.Identifier);
|
||||||
return new VariableExpressionSyntax((IdentifierToken) token);
|
return new VariableExpressionSyntax((IdentifierToken) token);
|
||||||
default:
|
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
|
public enum SyntaxKind
|
||||||
{
|
{
|
||||||
// tokens
|
// misc
|
||||||
EndOfFile,
|
EndOfFile,
|
||||||
WhiteSpace,
|
WhiteSpace,
|
||||||
|
BadToken,
|
||||||
|
|
||||||
|
// tokens
|
||||||
Number,
|
Number,
|
||||||
Plus,
|
Plus,
|
||||||
Minus,
|
Minus,
|
||||||
|
@ -35,6 +37,7 @@ namespace Upsilon.Parser
|
||||||
ParenthesizedExpression,
|
ParenthesizedExpression,
|
||||||
AssignmentExpression,
|
AssignmentExpression,
|
||||||
VariableExpression,
|
VariableExpression,
|
||||||
|
BadExpression,
|
||||||
|
|
||||||
// script unit
|
// script unit
|
||||||
ScriptUnit,
|
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Upsilon;
|
||||||
using Upsilon.Evaluator;
|
using Upsilon.Evaluator;
|
||||||
using Upsilon.Utilities;
|
|
||||||
|
|
||||||
namespace Yc
|
namespace Yc
|
||||||
{
|
{
|
||||||
|
@ -21,10 +21,51 @@ namespace Yc
|
||||||
}
|
}
|
||||||
|
|
||||||
var parsed = new Script(input, variables);
|
var parsed = new Script(input, variables);
|
||||||
//Console.WriteLine(parsed.Parsed.Print());
|
if (parsed.Diagnostics.Messages.Count > 0)
|
||||||
Console.WriteLine(parsed.Evaluate());
|
{
|
||||||
variables = parsed.Variables;
|
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