From ff0578205665707643f2df8dfb4b7f1f6cbd9616 Mon Sep 17 00:00:00 2001 From: Valerie Date: Tue, 2 Apr 2024 21:05:08 -0400 Subject: [PATCH] started implementing real lexing --- src/Error.cs | 14 +++++ src/Lex.cs | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/Lexer.cs | 95 ---------------------------- src/Roll.cs | 6 +- 4 files changed, 190 insertions(+), 98 deletions(-) create mode 100644 src/Error.cs create mode 100644 src/Lex.cs delete mode 100644 src/Lexer.cs diff --git a/src/Error.cs b/src/Error.cs new file mode 100644 index 0000000..e681af1 --- /dev/null +++ b/src/Error.cs @@ -0,0 +1,14 @@ + +namespace Dungeoneer.Error { + + public sealed class UnmatchedTokenException : Exception { + + public UnmatchedTokenException(string text) + : base($"'{text}' is not a valid token!") + { } + + } + +} + + diff --git a/src/Lex.cs b/src/Lex.cs new file mode 100644 index 0000000..b24f35b --- /dev/null +++ b/src/Lex.cs @@ -0,0 +1,173 @@ +using System.Text.RegularExpressions; + +using Dungeoneer.Error; + +namespace Dungeoneer.Lexing { + + public static class Lexer { + + public static TokenSet Tokenize(string text) { + var output = new TokenSet(); + var parts = text.Trim().Split(" ", 0x11); + foreach(string part in parts) { + + var diceMatch = DiceToken.Match(part); + if(diceMatch.Success) { + output.Add(new DiceToken(diceMatch)); + continue; + } + + var numberMatch = NumberToken.Match(part); + if(numberMatch.Success) { + output.Add(new NumberToken(numberMatch)); + continue; + } + + var dcMatch = DcToken.Match(part); + if(dcMatch.Success) { + output.Add(new DcToken(dcMatch)); + continue; + } + + var operatorMatch = OperatorToken.Match(part); + if(operatorMatch.Success) { + output.Add(new OperatorToken(operatorMatch)); + continue; + } + + var varMatch = VarToken.Match(part); + if(varMatch.Success) { + output.Add(new VarToken(varMatch)); + continue; + } + + throw new UnmatchedTokenException(part); + + } + return output; + } + + } + + public class TokenSet : List { + + public TokenSet() : base() { } + + public override string ToString() { + var output = ""; + foreach(Token token in this) + output += $"{token} "; + return output; + } + + } + + public abstract class Token { + + public override string ToString() { return ""; } + + } + + public abstract class MetadataToken : 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 class OperatorToken : Token { + public string dummy; + + internal static readonly Regex Pattern = new Regex(@"([\+\-\*\/])"); + + public OperatorToken(Match match) { + dummy = match.Groups[1].Value; + } + + public override string ToString() { return dummy; } + + public static Match Match(string text) { return Pattern.Match(text); } + + } + + public class NumberToken : ValueToken { + + internal static readonly Regex Pattern = new Regex(@"^(\d+)$"); + + public NumberToken(Match match) { Value = int.Parse(match.Groups[1].Value); } + + public static Match Match(string text) { return Pattern.Match(text); } + + } + + public class VarToken : ValueToken { + + private const string Style = "\x1b[33;1m"; + internal static readonly Regex Pattern = new Regex(@"^\$(.+)"); + + public VarToken(Match match) { Value = Scripting.Scope.GetVariable(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 DiceToken : ValueToken { + public int Count { get; private set; } + public int Sides { get; private set; } + public List Result { get; internal set; } + + public DiceToken(Match match) { + // parse captured values + Count = int.Parse(match.Groups[1].Value); + Sides = int.Parse(match.Groups[2].Value); + + // get calculated fields + Result = Roll(); + Value = Result.Sum(); + } + + private const string Style = "\x1b[34;1m"; + internal static readonly Regex Pattern = new Regex(@"(\d+)d(\d+)"); + + public List Roll() { + var output = new List(); + for(int i = 0; i < Count; i++) + output.Add(Util.Roll(Sides)); + return output; + } + + public override string ToString() { + var output = $"{Style}( "; + var result = Result; + for(int i = 0; i < Count; i++) + if(i < Count - 1) + output += $"{result[i]}, "; + else + output += $"{result[i]} ){Dungeoneer.Format.Reset}"; + return output; + } + + public static Match Match(string text) { return Pattern.Match(text); } + + } + + public class DcToken : MetadataToken { + + private const string Style = "\x1b[31m"; + internal static readonly Regex Pattern = new Regex(@"[Dd][Cc]\:(\d+)"); + + public DcToken(Match match) { Value = int.Parse(match.Groups[1].Value); } + + public override string ToString() { return $"{Style}DC: {Value}{Format.Reset}"; } + + public static Match Match(string text) { return Pattern.Match(text); } + + } + +} + diff --git a/src/Lexer.cs b/src/Lexer.cs deleted file mode 100644 index c57a38c..0000000 --- a/src/Lexer.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System.Text.RegularExpressions; - -namespace Dungeoneer.Lexing { - - public abstract class Token { - - public virtual string Format() { return Expression(); } - public abstract string Expression(); - - } - - public class LiteralToken : Token { - public string Content { get; private set; } - - public LiteralToken(string text) { Content = text; } - - public override string Expression() { return Content; } - - } - - public class DiceToken : Token { - public int Count { get; private set; } - public int Sides { get; private set; } - private List _result = null; - public List Result { - get { - if(_result == null) { - _result = new List(); - for(int i = 0; i < Count; i++) - _result.Add(Util.Roll(Sides)); - } - return _result; - } - } - public int? _sum = null; - public int Sum { - get { - if(!_sum.HasValue) { - _sum = 0; - foreach(var roll in Result) - _sum += roll; - } - return _sum.Value; - } - } - - public DiceToken(int count, int sides) { - Count = count; - Sides = sides; - } - public DiceToken(Match match) { - Count = int.Parse(match.Groups[1].Value); - Sides = int.Parse(match.Groups[2].Value); - } - public DiceToken(string text) { - var match = Match(text); - if(match.Success) { - Count = int.Parse(match.Groups[1].Value); - Sides = int.Parse(match.Groups[2].Value); - } else - throw new Exception("fuck you"); - } - - private const string Style = "\x1b[34;1m"; - private static readonly Regex Pattern = new Regex(@"(\d+)d(\d+)"); - - public override string Format() { - var output = $"{Style}( "; - var result = Result; - for(int i = 0; i < Count; i++) - if(i < Count - 1) - output += $"{result[i]}, "; - else - output += $"{result[i]} ){Dungeoneer.Format.Reset}"; - return output; - } - public override string Expression() { return Sum.ToString(); } - - public static Match Match(string text) { return Pattern.Match(text); } - - } - - /*public class DcToken : Token { - - private const string Style = "\x1b[31m"; - private static readonly Regex Pattern = new Regex(@"dc\:(\d+)"); - - public static Match Match(string text) { return Pattern.Match(text); } - - public override string Expression() { return ""; } - - }*/ - -} - diff --git a/src/Roll.cs b/src/Roll.cs index 9558d32..c9cdda1 100644 --- a/src/Roll.cs +++ b/src/Roll.cs @@ -65,8 +65,8 @@ namespace Dungeoneer { if(dieCheck.Success) { var token = new DiceToken(dieCheck); - this.Print += $"{token.Format()} "; - this.Expression += $"{token.Expression()} "; + this.Print += $"{token} "; + this.Expression += $"{token.Value} "; } else { var part = $"{piece} "; this.Expression += part; @@ -100,7 +100,7 @@ namespace Dungeoneer { break; } var result = RollResult.Wrap(success); - Console.WriteLine($"{dice.Format()}\n => {result}"); + Console.WriteLine($"{dice}\n => {result}"); } }