Faster number lexing, better handling of exceptions in C# code
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Deukhoofd 2019-04-30 15:28:43 +02:00
parent a9dbb2c1ed
commit c4c3f65074
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
7 changed files with 96 additions and 53 deletions

View File

@ -159,8 +159,7 @@ namespace Upsilon.BaseTypes.ScriptFunction
{ {
Exception exception = e; Exception exception = e;
if (e.InnerException != null) exception = e.InnerException; if (e.InnerException != null) exception = e.InnerException;
throw new EvaluationException(state.Script.FileName, throw new EvaluationException(state.Script.FileName, exception, span, state.Stacktrace);
"An Exception occured while executing a C# function:\n" + exception, span, state.Stacktrace);
} }
return result; return result;
} }

View File

@ -14,10 +14,19 @@ namespace Upsilon.Exceptions
public EvaluationException(string fileName, string message, TextSpan span, Stacktrace stacktrace = null) public EvaluationException(string fileName, string message, TextSpan span, Stacktrace stacktrace = null)
{ {
_stacktrace = stacktrace; _stacktrace = stacktrace;
FileName = fileName; FileName = fileName;
ErrorMessage = message; ErrorMessage = message;
Span = span; Span = span;
}
public EvaluationException(string fileName, Exception inner, TextSpan span, Stacktrace stacktrace = null)
: base("An exception occured in called C# code. See the inner exception for more details.", inner)
{
_stacktrace = stacktrace;
FileName = fileName;
ErrorMessage = Message;
Span = span;
} }
public override string ToString() public override string ToString()

View File

@ -6,7 +6,7 @@ namespace Upsilon.Parser
public class IdentifierToken : SyntaxToken public class IdentifierToken : SyntaxToken
{ {
public IdentifierToken(string name, TextSpan position) public IdentifierToken(string name, TextSpan position)
: base(SyntaxKind.Identifier, position, name, null) : base(SyntaxKind.Identifier, position, null)
{ {
Name = name; Name = name;
} }

View File

@ -88,53 +88,53 @@ namespace Upsilon.Parser
switch (Current) switch (Current)
{ {
case '\0': case '\0':
return new SyntaxToken(SyntaxKind.EndOfFile, new TextSpan(_linePosition, _position, _linePosition, _position + 1), "\0", null); return new SyntaxToken(SyntaxKind.EndOfFile, new TextSpan(_linePosition, _position, _linePosition, _position + 1), null);
case ' ': case '\t': case '\r': case ' ': case '\t': case '\r':
return new SyntaxToken(SyntaxKind.WhiteSpace, new TextSpan(_linePosition, _position, _linePosition, _position + 1), Current.ToString(), null); return new SyntaxToken(SyntaxKind.WhiteSpace, new TextSpan(_linePosition, _position, _linePosition, _position + 1), null);
case '\n': case '\n':
{ {
_linePosition++; _linePosition++;
var pos = _position; var pos = _position;
_position = -1; _position = -1;
return new SyntaxToken(SyntaxKind.WhiteSpace, new TextSpan(_linePosition, pos, _linePosition, pos + 1), "\n", null); return new SyntaxToken(SyntaxKind.WhiteSpace, new TextSpan(_linePosition, pos, _linePosition, pos + 1), null);
} }
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
return LexNumber(); return LexNumber();
case '+': case '+':
return new SyntaxToken(SyntaxKind.Plus, new TextSpan(_linePosition, _position, _linePosition, _position + 1), "+", null); return new SyntaxToken(SyntaxKind.Plus, new TextSpan(_linePosition, _position, _linePosition, _position + 1), null);
case '-': case '-':
if (Next == '-') if (Next == '-')
{ {
_position++; _position++;
return LexComments(); return LexComments();
} }
return new SyntaxToken(SyntaxKind.Minus, new TextSpan(_linePosition, _position, _linePosition, _position + 1), "-", null); return new SyntaxToken(SyntaxKind.Minus, new TextSpan(_linePosition, _position, _linePosition, _position + 1), null);
case '*': case '*':
return new SyntaxToken(SyntaxKind.Star, new TextSpan(_linePosition, _position, _linePosition, _position + 1), "*", null); return new SyntaxToken(SyntaxKind.Star, new TextSpan(_linePosition, _position, _linePosition, _position + 1), null);
case '/': case '/':
return new SyntaxToken(SyntaxKind.Slash, new TextSpan(_linePosition, _position, _linePosition, _position + 1), "/", null); return new SyntaxToken(SyntaxKind.Slash, new TextSpan(_linePosition, _position, _linePosition, _position + 1), null);
case '(': case '(':
return new SyntaxToken(SyntaxKind.OpenParenthesis, new TextSpan(_linePosition, _position, _linePosition, _position + 1), "(", null); return new SyntaxToken(SyntaxKind.OpenParenthesis, new TextSpan(_linePosition, _position, _linePosition, _position + 1), null);
case ')': case ')':
return new SyntaxToken(SyntaxKind.CloseParenthesis, new TextSpan(_linePosition, _position, _linePosition, _position + 1), ")", null); return new SyntaxToken(SyntaxKind.CloseParenthesis, new TextSpan(_linePosition, _position, _linePosition, _position + 1), null);
case '{': case '{':
return new SyntaxToken(SyntaxKind.OpenBrace, new TextSpan(_linePosition, _position, _linePosition, _position + 1), "{", null); return new SyntaxToken(SyntaxKind.OpenBrace, new TextSpan(_linePosition, _position, _linePosition, _position + 1), null);
case '}': case '}':
return new SyntaxToken(SyntaxKind.CloseBrace, new TextSpan(_linePosition, _position, _linePosition, _position + 1), "}", null); return new SyntaxToken(SyntaxKind.CloseBrace, new TextSpan(_linePosition, _position, _linePosition, _position + 1), null);
case '[': case '[':
return new SyntaxToken(SyntaxKind.OpenBracket, new TextSpan(_linePosition, _position, _linePosition, _position + 1), "[", null); return new SyntaxToken(SyntaxKind.OpenBracket, new TextSpan(_linePosition, _position, _linePosition, _position + 1), null);
case ']': case ']':
return new SyntaxToken(SyntaxKind.CloseBracket, new TextSpan(_linePosition, _position, _linePosition, _position + 1), "]", null); return new SyntaxToken(SyntaxKind.CloseBracket, new TextSpan(_linePosition, _position, _linePosition, _position + 1), null);
case '.': case '.':
return new SyntaxToken(SyntaxKind.FullStop, new TextSpan(_linePosition, _position, _linePosition, _position + 1), ".", null); return new SyntaxToken(SyntaxKind.FullStop, new TextSpan(_linePosition, _position, _linePosition, _position + 1), null);
case ',': case ',':
return new SyntaxToken(SyntaxKind.Comma, new TextSpan(_linePosition, _position, _linePosition, _position + 1), ",", null); return new SyntaxToken(SyntaxKind.Comma, new TextSpan(_linePosition, _position, _linePosition, _position + 1), null);
case '#': case '#':
return new SyntaxToken(SyntaxKind.PoundSign, new TextSpan(_linePosition, _position, _linePosition, _position + 1), "#", null); return new SyntaxToken(SyntaxKind.PoundSign, new TextSpan(_linePosition, _position, _linePosition, _position + 1), null);
case '%': case '%':
return new SyntaxToken(SyntaxKind.PercentSign, new TextSpan(_linePosition, _position, _linePosition, _position + 1), "%", null); return new SyntaxToken(SyntaxKind.PercentSign, new TextSpan(_linePosition, _position, _linePosition, _position + 1), null);
case '^': case '^':
return new SyntaxToken(SyntaxKind.RoofSign, new TextSpan(_linePosition, _position, _linePosition, _position + 1), "^", null); return new SyntaxToken(SyntaxKind.RoofSign, new TextSpan(_linePosition, _position, _linePosition, _position + 1), null);
case '"': case '"':
case '\'': case '\'':
return LexString(Current); return LexString(Current);
@ -142,35 +142,35 @@ namespace Upsilon.Parser
if (Next == '=') if (Next == '=')
{ {
_position++; _position++;
return new SyntaxToken(SyntaxKind.EqualsEquals, new TextSpan(_linePosition, _position - 1, _linePosition, _position + 1), "==", null); return new SyntaxToken(SyntaxKind.EqualsEquals, new TextSpan(_linePosition, _position - 1, _linePosition, _position + 1), null);
} }
return new SyntaxToken(SyntaxKind.Equals, new TextSpan(_linePosition, _position, _linePosition, _position + 1), "=", null); return new SyntaxToken(SyntaxKind.Equals, new TextSpan(_linePosition, _position, _linePosition, _position + 1), null);
case '~': case '~':
if (Next == '=') if (Next == '=')
{ {
_position++; _position++;
return new SyntaxToken(SyntaxKind.TildeEquals, new TextSpan(_linePosition, _position - 1, _linePosition, _position + 1), "~=", null); return new SyntaxToken(SyntaxKind.TildeEquals, new TextSpan(_linePosition, _position - 1, _linePosition, _position + 1), null);
} }
return new SyntaxToken(SyntaxKind.Tilde, new TextSpan(_linePosition, _position, _linePosition, _position + 1), "~", null); return new SyntaxToken(SyntaxKind.Tilde, new TextSpan(_linePosition, _position, _linePosition, _position + 1), null);
case '<': case '<':
if (Next == '=') if (Next == '=')
{ {
_position++; _position++;
return new SyntaxToken(SyntaxKind.LessEquals, new TextSpan(_linePosition, _position - 1, _linePosition, _position + 1), "<=", null); return new SyntaxToken(SyntaxKind.LessEquals, new TextSpan(_linePosition, _position - 1, _linePosition, _position + 1), null);
} }
return new SyntaxToken(SyntaxKind.Less, new TextSpan(_linePosition, _position, _linePosition, _position + 1), "<", null); return new SyntaxToken(SyntaxKind.Less, new TextSpan(_linePosition, _position, _linePosition, _position + 1), null);
case '>': case '>':
if (Next == '=') if (Next == '=')
{ {
_position++; _position++;
return new SyntaxToken(SyntaxKind.GreaterEquals, new TextSpan(_linePosition, _position - 1, _linePosition, _position + 1), ">=", null); return new SyntaxToken(SyntaxKind.GreaterEquals, new TextSpan(_linePosition, _position - 1, _linePosition, _position + 1), null);
} }
return new SyntaxToken(SyntaxKind.Greater, new TextSpan(_linePosition, _position, _linePosition, _position + 1), ">", null); return new SyntaxToken(SyntaxKind.Greater, new TextSpan(_linePosition, _position, _linePosition, _position + 1), null);
default: default:
if (char.IsLetter(Current) || Current == '_') if (char.IsLetter(Current) || Current == '_')
return LexIdentifierOrKeyword(); return LexIdentifierOrKeyword();
_diagnostics.LogBadCharacter(new TextSpan(_linePosition, _position, _linePosition, _position + 1), SyntaxKind.Identifier); _diagnostics.LogBadCharacter(new TextSpan(_linePosition, _position, _linePosition, _position + 1), SyntaxKind.Identifier);
return new SyntaxToken(SyntaxKind.BadToken, new TextSpan(_linePosition, _position, _linePosition, _position + 1), "", null); return new SyntaxToken(SyntaxKind.BadToken, new TextSpan(_linePosition, _position, _linePosition, _position + 1), null);
} }
} }
@ -178,30 +178,65 @@ namespace Upsilon.Parser
{ {
var start = _position; var start = _position;
var hasDecimalPoint = false; var hasDecimalPoint = false;
var numStr = new StringBuilder(); long num = ParseChar(Current);
numStr.Append(Current); double floatNum = 0f;
while (char.IsDigit(Next) || Next == '.' || Next == '_') var decimalPlace = 0;
char c = Next;
while (char.IsDigit(c) || c == '.' || c == '_')
{ {
if (Next == '.') if (c == '.')
{ {
if (hasDecimalPoint) if (hasDecimalPoint)
{ {
_diagnostics.LogBadCharacter(new TextSpan(_linePosition, _position, _linePosition, _position + 1), SyntaxKind.Number); _diagnostics.LogBadCharacter(new TextSpan(_linePosition, _position, _linePosition, _position + 1), SyntaxKind.Number);
return new SyntaxToken(SyntaxKind.BadToken, new TextSpan(_linePosition, _position, _linePosition, _position + 1), "", null); return new SyntaxToken(SyntaxKind.BadToken, new TextSpan(_linePosition, _position, _linePosition, _position + 1), null);
} }
hasDecimalPoint = true; hasDecimalPoint = true;
floatNum = Convert.ToDouble(num);
decimalPlace++;
}
else if (c != '_')
{
var parsed = ParseChar(c);
if (hasDecimalPoint)
{
floatNum += parsed / Math.Pow(10, decimalPlace);
decimalPlace++;
}
else
{
num *= 10;
num += parsed;
}
} }
if (Next != '_')
numStr.Append(Next);
_position++; _position++;
c = Next;
} }
object o; object o;
if (hasDecimalPoint) if (hasDecimalPoint)
o = double.Parse(numStr.ToString()); o = floatNum;
else else
o = long.Parse(numStr.ToString()); o = num;
return new SyntaxToken(SyntaxKind.Number, new TextSpan(_linePosition, start, _linePosition, _position + 1), numStr.ToString(), o); return new SyntaxToken(SyntaxKind.Number, new TextSpan(_linePosition, start, _linePosition, _position + 1), o);
}
private static byte ParseChar(char c)
{
switch (c)
{
case '0': return 0;
case '1': return 1;
case '2': return 2;
case '3': return 3;
case '4': return 4;
case '5': return 5;
case '6': return 6;
case '7': return 7;
case '8': return 8;
case '9': return 9;
default: throw new ArgumentException($"Expected digit, got {c}.");
}
} }
private SyntaxToken LexString(char current) private SyntaxToken LexString(char current)
@ -230,8 +265,7 @@ namespace Upsilon.Parser
} }
var res = sb.ToString(); var res = sb.ToString();
return new SyntaxToken(SyntaxKind.String, new TextSpan(_linePosition, start, _linePosition, _position + 1), return new SyntaxToken(SyntaxKind.String, new TextSpan(_linePosition, start, _linePosition, _position + 1), res);
$"\"{res}\"", res);
} }
private SyntaxToken LexIdentifierOrKeyword() private SyntaxToken LexIdentifierOrKeyword()
@ -257,7 +291,7 @@ namespace Upsilon.Parser
{ {
return new ReturnSyntaxToken(new TextSpan(_linePosition, start, _linePosition, _position + 1), Next == Environment.NewLine[0]); return new ReturnSyntaxToken(new TextSpan(_linePosition, start, _linePosition, _position + 1), Next == Environment.NewLine[0]);
} }
return new SyntaxToken(kind, new TextSpan(_linePosition, start, _linePosition, _position + 1), str, null); return new SyntaxToken(kind, new TextSpan(_linePosition, start, _linePosition, _position + 1), null);
} }
private SyntaxToken LexComments() private SyntaxToken LexComments()

View File

@ -29,7 +29,7 @@ namespace Upsilon.Parser
private SyntaxToken Get(int offset) private SyntaxToken Get(int offset)
{ {
if (_position + offset >= _tokens.Length) if (_position + offset >= _tokens.Length)
return new SyntaxToken(SyntaxKind.EndOfFile, _tokens.Last().Span, "\0", null); return new SyntaxToken(SyntaxKind.EndOfFile, _tokens.Last().Span, null);
else else
{ {
return _tokens[_position + offset]; return _tokens[_position + offset];
@ -49,7 +49,7 @@ namespace Upsilon.Parser
return NextToken(); return NextToken();
_diagnostics.LogBadCharacter(Current.Span, kind, Current.Kind); _diagnostics.LogBadCharacter(Current.Span, kind, Current.Kind);
return new SyntaxToken(kind, Current.Span, "", null); return new SyntaxToken(kind, Current.Span, null);
} }
private StatementSyntax ParseScriptSyntax() private StatementSyntax ParseScriptSyntax()

View File

@ -5,7 +5,7 @@ namespace Upsilon.Parser
{ {
public class SyntaxToken : SyntaxNode public class SyntaxToken : SyntaxNode
{ {
public SyntaxToken(SyntaxKind kind, TextSpan position, string text, object value) public SyntaxToken(SyntaxKind kind, TextSpan position, object value)
{ {
Kind = kind; Kind = kind;
Span = position; Span = position;
@ -34,7 +34,7 @@ namespace Upsilon.Parser
public bool FollowedByLineBreak { get; } public bool FollowedByLineBreak { get; }
public ReturnSyntaxToken(TextSpan position, bool followedByLineBreak) public ReturnSyntaxToken(TextSpan position, bool followedByLineBreak)
: base(SyntaxKind.ReturnKeyword, position, "return", null) : base(SyntaxKind.ReturnKeyword, position, null)
{ {
FollowedByLineBreak = followedByLineBreak; FollowedByLineBreak = followedByLineBreak;
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using Upsilon; using Upsilon;
using Upsilon.Exceptions;
using Xunit; using Xunit;
namespace UpsilonTests.StandardLibraryTests namespace UpsilonTests.StandardLibraryTests
@ -14,14 +15,14 @@ namespace UpsilonTests.StandardLibraryTests
public void AssertTest() public void AssertTest()
{ {
Executor.EvaluateScript("assert(true)", Options); Executor.EvaluateScript("assert(true)", Options);
Assert.Throws<Exception>(() => Executor.EvaluateScript("assert(false)", Options)); Assert.Throws<EvaluationException>(() => Executor.EvaluateScript("assert(false)", Options));
} }
[Fact] [Fact]
public void Error() public void Error()
{ {
var e = Assert.Throws<Exception>(() => Executor.EvaluateScript(@"error(""test_error"")", Options)); var e = Assert.Throws<EvaluationException>(() => Executor.EvaluateScript(@"error(""test_error"")", Options));
Assert.Equal("test_error", e.Message); Assert.Equal("test_error", e.InnerException.Message);
} }
[Fact] [Fact]