graceful error handling and StringToken class implementation
This commit is contained in:
parent
2349fb743f
commit
e8a8bdde85
6 changed files with 47 additions and 93 deletions
|
@ -9,6 +9,14 @@ namespace Dungeoneer.Error {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class SyntaxException : Exception {
|
||||||
|
|
||||||
|
public SyntaxException(string message)
|
||||||
|
: base(message)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
30
src/Lex.cs
30
src/Lex.cs
|
@ -96,18 +96,34 @@ namespace Dungeoneer.Interpreter {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class MetadataToken<T> : Token {
|
public abstract class DataToken<T> : Token {
|
||||||
public T Value { get; protected set; }
|
public T Value { get; protected set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class ValueToken : Token {
|
public abstract class MetadataToken<T> : DataToken<T> {
|
||||||
public int Value { get; protected set; }
|
public T Value { get; protected set; }
|
||||||
|
|
||||||
public override string ToString() { return Value.ToString(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class OperatorToken : Token {
|
public abstract class ValueToken : DataToken<int> {
|
||||||
public char Value;
|
|
||||||
|
public override string ToString() { return Value.ToString(); }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class StringToken : DataToken<string> {
|
||||||
|
|
||||||
|
private const string Style = "\x1b[92m";
|
||||||
|
internal static readonly Regex Pattern = new Regex(@"^'(.+)'$");
|
||||||
|
|
||||||
|
public StringToken(Match match) { Value = match.Groups[1].Value; }
|
||||||
|
|
||||||
|
public override string ToString() { return $"{Style}'{Value}'{Format.Reset}"; }
|
||||||
|
|
||||||
|
public static Match Match(string text) { return Pattern.Match(text); }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OperatorToken : DataToken<char> {
|
||||||
|
|
||||||
public OperatorToken(char op) { Value = op; }
|
public OperatorToken(char op) { Value = op; }
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@ using System.Dynamic;
|
||||||
|
|
||||||
namespace Dungeoneer.Objects {
|
namespace Dungeoneer.Objects {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class Character {
|
public class Character {
|
||||||
public string Name;
|
public string Name;
|
||||||
|
|
||||||
|
|
12
src/Parse.cs
12
src/Parse.cs
|
@ -1,5 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
|
using Dungeoneer.Error;
|
||||||
|
|
||||||
namespace Dungeoneer.Interpreter {
|
namespace Dungeoneer.Interpreter {
|
||||||
|
|
||||||
public static class Parser {
|
public static class Parser {
|
||||||
|
@ -14,23 +16,23 @@ namespace Dungeoneer.Interpreter {
|
||||||
switch(token) {
|
switch(token) {
|
||||||
case ValueToken value:
|
case ValueToken value:
|
||||||
if(wasValue)
|
if(wasValue)
|
||||||
throw new Exception("value -> value");
|
throw new SyntaxException("Value followed by value");
|
||||||
result = Operate(result, value.Value, operation);
|
result = Operate(result, value.Value, operation);
|
||||||
wasValue = true;
|
wasValue = true;
|
||||||
break;
|
break;
|
||||||
case OperatorToken op:
|
case OperatorToken op:
|
||||||
if(!wasValue)
|
if(!wasValue)
|
||||||
throw new Exception("operator -> operator");
|
throw new SyntaxException("Operator followed by operator");
|
||||||
operation = op.Value;
|
operation = op.Value;
|
||||||
wasValue = false;
|
wasValue = false;
|
||||||
break;
|
break;
|
||||||
case DcToken dc:
|
case DcToken dc:
|
||||||
if(rollDc.HasValue)
|
if(rollDc.HasValue)
|
||||||
throw new Exception("two DC elements");
|
throw new SyntaxException("Duplicate element: DC");
|
||||||
rollDc = dc.Value;
|
rollDc = dc.Value;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception($"invalid token: {token.GetType()}");
|
throw new SyntaxException($"invalid token: {token.GetType()}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -44,7 +46,7 @@ namespace Dungeoneer.Interpreter {
|
||||||
case '/': return (a / b);
|
case '/': return (a / b);
|
||||||
case '%': return (a % b);
|
case '%': return (a % b);
|
||||||
case '^': return (int)Math.Pow(a, b);
|
case '^': return (int)Math.Pow(a, b);
|
||||||
default: throw new Exception("unmatched operator");
|
default: throw new SyntaxException("unmatched operator");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,9 @@ namespace Dungeoneer {
|
||||||
tokens.RemoveAt(0);
|
tokens.RemoveAt(0);
|
||||||
if(first is NameToken) {
|
if(first is NameToken) {
|
||||||
var command = (first as NameToken).Value;
|
var command = (first as NameToken).Value;
|
||||||
ReplCommands[command](tokens);
|
if(ReplCommands.ContainsKey(command))
|
||||||
|
try { ReplCommands[command](tokens); }
|
||||||
|
catch(Exception e) { Console.WriteLine($"error: {e.Message}"); }
|
||||||
} else {
|
} else {
|
||||||
Console.WriteLine($"no command '{raw}' found!");
|
Console.WriteLine($"no command '{raw}' found!");
|
||||||
}
|
}
|
||||||
|
|
84
src/Roll.cs
84
src/Roll.cs
|
@ -5,89 +5,13 @@ using Dungeoneer.Interpreter;
|
||||||
|
|
||||||
namespace Dungeoneer {
|
namespace Dungeoneer {
|
||||||
|
|
||||||
public class RollResult {
|
|
||||||
public dynamic Value { get; private set; }
|
|
||||||
|
|
||||||
private RollResult() { Value = null; }
|
|
||||||
public RollResult(string expression) {
|
|
||||||
Value = Scripting.Expr($"eval('{expression}')");
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static class Style {
|
|
||||||
internal const string Default = "\x1b[1m";
|
|
||||||
|
|
||||||
internal const string BoolTrue = "\x1b[32;1m";
|
|
||||||
internal const string BoolFalse = "\x1b[31;1m";
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString() {
|
|
||||||
string style;
|
|
||||||
switch(Value) {
|
|
||||||
case bool boolValue:
|
|
||||||
style = boolValue ? Style.BoolTrue : Style.BoolFalse;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
style = Style.Default;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $"{style}{Value}{Format.Reset}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RollResult Wrap(dynamic value) {
|
|
||||||
var output = new RollResult();
|
|
||||||
output.Value = value;
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RollExpression {
|
|
||||||
private IList<string> Parts;
|
|
||||||
public string Print { get; private set; }
|
|
||||||
public string Expression { get; private set; }
|
|
||||||
public RollResult Result {
|
|
||||||
get {
|
|
||||||
try { return new RollResult(Expression); }
|
|
||||||
catch { return null; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public RollExpression(IList<string> parts) {
|
|
||||||
this.Parts = parts;
|
|
||||||
this.Print = "";
|
|
||||||
this.Expression = "";
|
|
||||||
|
|
||||||
// build expression from string parts
|
|
||||||
foreach(string piece in Parts) {
|
|
||||||
// die expression substitution
|
|
||||||
var dieCheck = DiceToken.Match(piece);
|
|
||||||
if(dieCheck.Success) {
|
|
||||||
var token = new DiceToken(dieCheck);
|
|
||||||
|
|
||||||
this.Print += $"{token} ";
|
|
||||||
this.Expression += $"{token.Value} ";
|
|
||||||
} else {
|
|
||||||
var part = $"{piece} ";
|
|
||||||
this.Expression += part;
|
|
||||||
this.Print += part;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class RollMacro {
|
public static class RollMacro {
|
||||||
|
|
||||||
public static void Pool(TokenSet input) {
|
public static void Pool(TokenSet input) {
|
||||||
/*
|
if(input.Count != 2)
|
||||||
foreach(var roll in rolls)
|
|
||||||
if(roll > dc.Value) {
|
foreach(var token in input)
|
||||||
success = true;
|
);
|
||||||
break;
|
|
||||||
}
|
|
||||||
var result = RollResult.Wrap(success);
|
|
||||||
Console.WriteLine($"{dice}\n => {result}");*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue