Work on Diagnostics

This commit is contained in:
Deukhoofd 2018-11-11 10:26:52 +01:00
parent ab61a01573
commit 699377cdfc
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
10 changed files with 244 additions and 45 deletions

86
Upsilon/Diagnostics.cs Normal file
View File

@ -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,
}
}

View File

@ -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,20 +67,22 @@ namespace Upsilon.Evaluator
{ {
var left = EvaluateExpression(e.Left); var left = EvaluateExpression(e.Left);
var right = EvaluateExpression(e.Right); var right = EvaluateExpression(e.Right);
try
{
switch (e.Operator.Kind) switch (e.Operator.Kind)
{ {
case SyntaxKind.Plus: case SyntaxKind.Plus:
return (double)left + (double)right; return (double) left + (double) right;
case SyntaxKind.Minus: case SyntaxKind.Minus:
return (double)left - (double)right; return (double) left - (double) right;
case SyntaxKind.Star: case SyntaxKind.Star:
return (double)left * (double)right; return (double) left * (double) right;
case SyntaxKind.Slash: case SyntaxKind.Slash:
return (double)left / (double)right; return (double) left / (double) right;
case SyntaxKind.AndKeyword: case SyntaxKind.AndKeyword:
return (bool)left && (bool)right; return (bool) left && (bool) right;
case SyntaxKind.OrKeyword: case SyntaxKind.OrKeyword:
return (bool)left || (bool)right; return (bool) left || (bool) right;
case SyntaxKind.EqualsEquals: case SyntaxKind.EqualsEquals:
return Equals(left, right); return Equals(left, right);
case SyntaxKind.TildeEquals: case SyntaxKind.TildeEquals:
@ -87,6 +91,12 @@ namespace Upsilon.Evaluator
throw new Exception("Invalid Binary Operator: " + e.Operator.Kind); throw new Exception("Invalid Binary Operator: " + e.Operator.Kind);
} }
} }
catch (NullReferenceException)
{
_diagnostics.LogNullReferenceError(e.Span);
return null;
}
}
private object EvaluateAssignmentExpression(AssignmentExpressionSyntax e) private object EvaluateAssignmentExpression(AssignmentExpressionSyntax e)
{ {
@ -110,7 +120,9 @@ namespace Upsilon.Evaluator
{ {
return value; return value;
} }
throw new Exception("Unknown variable: " + varName);
_diagnostics.LogUnknownVariable(e.Span, varName);
return null;
} }
} }

View File

@ -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()

View File

@ -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;
} }
} }
} }

View File

@ -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();
}
}
}

View File

@ -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;
} }

View File

@ -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();
} }
} }

View File

@ -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,

View File

@ -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);
}
}
}

View File

@ -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()); {
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; 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();
}
}
} }