graceful error handling and StringToken class implementation

This commit is contained in:
Valerie Wolfe 2024-04-03 13:46:23 -04:00
parent 2349fb743f
commit e8a8bdde85
6 changed files with 47 additions and 93 deletions

View file

@ -9,6 +9,14 @@ namespace Dungeoneer.Error {
}
public sealed class SyntaxException : Exception {
public SyntaxException(string message)
: base(message)
{ }
}
}

View file

@ -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 abstract class ValueToken : Token {
public int Value { get; protected set; }
public override string ToString() { return Value.ToString(); }
public abstract class MetadataToken<T> : DataToken<T> {
public T Value { get; protected set; }
}
public class OperatorToken : Token {
public char Value;
public abstract class ValueToken : DataToken<int> {
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; }

View file

@ -2,6 +2,8 @@ using System.Dynamic;
namespace Dungeoneer.Objects {
public class Character {
public string Name;

View file

@ -1,5 +1,7 @@
using System;
using Dungeoneer.Error;
namespace Dungeoneer.Interpreter {
public static class Parser {
@ -14,23 +16,23 @@ namespace Dungeoneer.Interpreter {
switch(token) {
case ValueToken value:
if(wasValue)
throw new Exception("value -> value");
throw new SyntaxException("Value followed by value");
result = Operate(result, value.Value, operation);
wasValue = true;
break;
case OperatorToken op:
if(!wasValue)
throw new Exception("operator -> operator");
throw new SyntaxException("Operator followed by operator");
operation = op.Value;
wasValue = false;
break;
case DcToken dc:
if(rollDc.HasValue)
throw new Exception("two DC elements");
throw new SyntaxException("Duplicate element: DC");
rollDc = dc.Value;
break;
default:
throw new Exception($"invalid token: {token.GetType()}");
throw new SyntaxException($"invalid token: {token.GetType()}");
}
}
return result;
@ -44,7 +46,7 @@ namespace Dungeoneer.Interpreter {
case '/': return (a / b);
case '%': return (a % b);
case '^': return (int)Math.Pow(a, b);
default: throw new Exception("unmatched operator");
default: throw new SyntaxException("unmatched operator");
}
}

View file

@ -35,7 +35,9 @@ namespace Dungeoneer {
tokens.RemoveAt(0);
if(first is NameToken) {
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 {
Console.WriteLine($"no command '{raw}' found!");
}

View file

@ -5,89 +5,13 @@ using Dungeoneer.Interpreter;
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 void Pool(TokenSet input) {
/*
foreach(var roll in rolls)
if(roll > dc.Value) {
success = true;
break;
}
var result = RollResult.Wrap(success);
Console.WriteLine($"{dice}\n => {result}");*/
if(input.Count != 2)
foreach(var token in input)
);
}
}