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

View file

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

View file

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

View file

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

View file

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