started implementing real lexing
This commit is contained in:
parent
a0286194b2
commit
ff05782056
4 changed files with 190 additions and 98 deletions
14
src/Error.cs
Normal file
14
src/Error.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
|
||||||
|
namespace Dungeoneer.Error {
|
||||||
|
|
||||||
|
public sealed class UnmatchedTokenException : Exception {
|
||||||
|
|
||||||
|
public UnmatchedTokenException(string text)
|
||||||
|
: base($"'{text}' is not a valid token!")
|
||||||
|
{ }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
173
src/Lex.cs
Normal file
173
src/Lex.cs
Normal file
|
@ -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<Token> {
|
||||||
|
|
||||||
|
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 "<token>"; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class MetadataToken<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 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<int> 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<int> Roll() {
|
||||||
|
var output = new List<int>();
|
||||||
|
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<int> {
|
||||||
|
|
||||||
|
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); }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
95
src/Lexer.cs
95
src/Lexer.cs
|
@ -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<int> _result = null;
|
|
||||||
public List<int> Result {
|
|
||||||
get {
|
|
||||||
if(_result == null) {
|
|
||||||
_result = new List<int>();
|
|
||||||
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 ""; }
|
|
||||||
|
|
||||||
}*/
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -65,8 +65,8 @@ namespace Dungeoneer {
|
||||||
if(dieCheck.Success) {
|
if(dieCheck.Success) {
|
||||||
var token = new DiceToken(dieCheck);
|
var token = new DiceToken(dieCheck);
|
||||||
|
|
||||||
this.Print += $"{token.Format()} ";
|
this.Print += $"{token} ";
|
||||||
this.Expression += $"{token.Expression()} ";
|
this.Expression += $"{token.Value} ";
|
||||||
} else {
|
} else {
|
||||||
var part = $"{piece} ";
|
var part = $"{piece} ";
|
||||||
this.Expression += part;
|
this.Expression += part;
|
||||||
|
@ -100,7 +100,7 @@ namespace Dungeoneer {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
var result = RollResult.Wrap(success);
|
var result = RollResult.Wrap(success);
|
||||||
Console.WriteLine($"{dice.Format()}\n => {result}");
|
Console.WriteLine($"{dice}\n => {result}");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue