Compare commits
No commits in common. "main" and "real-lexing" have entirely different histories.
main
...
real-lexin
7 changed files with 101 additions and 110 deletions
|
@ -9,14 +9,6 @@ namespace Dungeoneer.Error {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class SyntaxException : Exception {
|
|
||||||
|
|
||||||
public SyntaxException(string message)
|
|
||||||
: base(message)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
43
src/Lex.cs
43
src/Lex.cs
|
@ -76,19 +76,11 @@ namespace Dungeoneer.Interpreter {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** <summary> convenience object class for tokenized input </summary> */
|
|
||||||
public class TokenSet : List<Token> {
|
public class TokenSet : List<Token> {
|
||||||
|
|
||||||
public TokenSet() : base() { }
|
public TokenSet() : base() { }
|
||||||
public TokenSet(List<Token> set) : base(set) { }
|
public TokenSet(List<Token> set) : base(set) { }
|
||||||
|
|
||||||
/** <summary> returns the first token in the set and removes it. </summary> */
|
|
||||||
public Token Pop() {
|
|
||||||
Token output = this[0];
|
|
||||||
RemoveAt(0);
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString() {
|
public override string ToString() {
|
||||||
var output = "";
|
var output = "";
|
||||||
foreach(Token token in this)
|
foreach(Token token in this)
|
||||||
|
@ -104,39 +96,18 @@ namespace Dungeoneer.Interpreter {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** <summary> a token that contains a value. </summary> */
|
public abstract class MetadataToken<T> : Token {
|
||||||
public abstract class DataToken<T> : Token {
|
|
||||||
public T Value { get; protected set; }
|
public T Value { get; protected set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** <summary> a token that is ignored during arithmetic evaluation. </summary> */
|
public abstract class ValueToken : Token {
|
||||||
public abstract class MetadataToken<T> : DataToken<T> {
|
public int Value { get; protected set; }
|
||||||
public T Value { get; protected set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/** <summary> a token with a numerical value </summary> */
|
|
||||||
public abstract class ValueToken : DataToken<int> {
|
|
||||||
|
|
||||||
public override string ToString() { return Value.ToString(); }
|
public override string ToString() { return Value.ToString(); }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** <summary> a token with a string value </summary> */
|
public class OperatorToken : Token {
|
||||||
public abstract class StringToken : DataToken<string> {
|
public char Value;
|
||||||
|
|
||||||
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); }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/** <summary> a token representing an arithmetic operator. </summary> */
|
|
||||||
public class OperatorToken : DataToken<char> {
|
|
||||||
|
|
||||||
public OperatorToken(char op) { Value = op; }
|
public OperatorToken(char op) { Value = op; }
|
||||||
|
|
||||||
|
@ -144,7 +115,6 @@ namespace Dungeoneer.Interpreter {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** <summary> a token representing a numeric literal </summary> */
|
|
||||||
public class NumberToken : ValueToken {
|
public class NumberToken : ValueToken {
|
||||||
|
|
||||||
internal static readonly Regex Pattern = new Regex(@"^(\d+)$");
|
internal static readonly Regex Pattern = new Regex(@"^(\d+)$");
|
||||||
|
@ -155,7 +125,8 @@ namespace Dungeoneer.Interpreter {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class NameToken : DataToken<string> {
|
public class NameToken : Token {
|
||||||
|
public string Value { get; private set; }
|
||||||
|
|
||||||
internal static readonly Regex Pattern = new Regex(@"^[A-Za-z][A-Za-z\d\-_]+$");
|
internal static readonly Regex Pattern = new Regex(@"^[A-Za-z][A-Za-z\d\-_]+$");
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,6 @@ 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,7 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
using Dungeoneer.Error;
|
|
||||||
|
|
||||||
namespace Dungeoneer.Interpreter {
|
namespace Dungeoneer.Interpreter {
|
||||||
|
|
||||||
public static class Parser {
|
public static class Parser {
|
||||||
|
@ -16,23 +14,23 @@ namespace Dungeoneer.Interpreter {
|
||||||
switch(token) {
|
switch(token) {
|
||||||
case ValueToken value:
|
case ValueToken value:
|
||||||
if(wasValue)
|
if(wasValue)
|
||||||
throw new SyntaxException("Value followed by value");
|
throw new Exception("value -> 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 SyntaxException("Operator followed by operator");
|
throw new Exception("operator -> 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 SyntaxException("Duplicate element: DC");
|
throw new Exception("two DC elements");
|
||||||
rollDc = dc.Value;
|
rollDc = dc.Value;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new SyntaxException($"invalid token: {token.GetType()}");
|
throw new Exception($"invalid token: {token.GetType()}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -46,7 +44,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 SyntaxException("unmatched operator");
|
default: throw new Exception("unmatched operator");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,9 +35,7 @@ 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;
|
||||||
if(ReplCommands.ContainsKey(command))
|
ReplCommands[command](tokens);
|
||||||
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!");
|
||||||
}
|
}
|
||||||
|
|
105
src/Roll.cs
105
src/Roll.cs
|
@ -1,44 +1,93 @@
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
using Dungeoneer.Error;
|
|
||||||
using Dungeoneer.Interpreter;
|
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)
|
/*
|
||||||
throw new SyntaxException($"expected 2 arguments ({input.Count} given)");
|
foreach(var roll in rolls)
|
||||||
|
if(roll > dc.Value) {
|
||||||
DiceToken dicePool = null;
|
|
||||||
DcToken target = null;
|
|
||||||
foreach(var token in input) {
|
|
||||||
switch(token) {
|
|
||||||
case DiceToken dice:
|
|
||||||
if(dicePool != null)
|
|
||||||
throw new SyntaxException("two dice");
|
|
||||||
dicePool = dice;
|
|
||||||
break;
|
|
||||||
case DcToken dc:
|
|
||||||
if(target != null)
|
|
||||||
throw new SyntaxException("two dc values");
|
|
||||||
target = dc;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool success = false;
|
|
||||||
foreach(var roll in dicePool.Result) {
|
|
||||||
if(roll > target.Value) {
|
|
||||||
success = true;
|
success = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
var result = RollResult.Wrap(success);
|
||||||
|
Console.WriteLine($"{dice}\n => {result}");*/
|
||||||
string message = success ? $"{Format.Green}pass" : $"{Format.Red}fail";
|
|
||||||
Console.WriteLine($"{dicePool} > {target}\n => {message}{Format.Reset}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,51 +19,36 @@ namespace Dungeoneer {
|
||||||
Scope = Engine.CreateScope();
|
Scope = Engine.CreateScope();
|
||||||
dynamic builtin = Engine.GetBuiltinModule();
|
dynamic builtin = Engine.GetBuiltinModule();
|
||||||
|
|
||||||
|
// set up imports
|
||||||
|
var paths = Engine.GetSearchPaths();
|
||||||
|
paths.Add("/usr/lib/python3.12/");
|
||||||
|
Engine.SetSearchPaths(paths);
|
||||||
|
|
||||||
// set up python environment
|
// set up python environment
|
||||||
builtin.SetVariable("input", (Func<string, string>)Input); // `input` in IP is broken; swap for our implementation
|
builtin.SetVariable("input", (Func<string, string>)Input);
|
||||||
builtin.RemoveVariable("open"); // remove 'open' builtin; don't trust scripts with file i/o
|
builtin.RemoveVariable("open");
|
||||||
|
|
||||||
// helper vars
|
// helper vars
|
||||||
Scope.repl_commands = Program.ReplCommands; // repl command dictionary
|
Scope.repl_commands = Program.ReplCommands;
|
||||||
Scope.roll_macros = Program.RollMacros; // roll macro dictionary
|
Scope.roll_macros = Program.RollMacros;
|
||||||
|
|
||||||
// interpreter api object
|
|
||||||
Scope.interpret = new ExpandoObject();
|
Scope.interpret = new ExpandoObject();
|
||||||
Scope.interpret.tokenize = (Func<string, TokenSet>)Lexer.Tokenize;
|
Scope.interpret.tokenize = (Func<string, TokenSet>)Lexer.Tokenize;
|
||||||
Scope.interpret.input = (Func<string,TokenSet>)LexInput;
|
|
||||||
Scope.interpret.first_token = (Func<string, Token>)Lexer.First;
|
Scope.interpret.first_token = (Func<string, Token>)Lexer.First;
|
||||||
Scope.interpret.parse_roll = (Func<string, int>)Interpreter.Parser.ParseRoll;
|
// Scope.interpret.parse_roll = (Func<string, string>)Interpreter.Parser.ParseRoll;
|
||||||
|
|
||||||
// helper functions
|
// helper functions
|
||||||
Scope.roll = (Func<int, int>)Dungeoneer.Util.Roll;
|
Scope.roll = (Func<int, int>)Dungeoneer.Util.Roll;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** <summary> Executes a Python file. </summary> */
|
|
||||||
public static void Run(string file) { Engine.ExecuteFile(file, Scope); }
|
public static void Run(string file) { Engine.ExecuteFile(file, Scope); }
|
||||||
|
public static dynamic Expr(string expression) { return Engine.Execute(expression, Scope); }
|
||||||
|
|
||||||
/** <summary> used for the python interpreter repl. don't use this!!! </summary> */
|
|
||||||
public static dynamic Expression(string input) { return Engine.Execute(input, Scope); }
|
|
||||||
|
|
||||||
/** <summary>
|
|
||||||
* reimplementation of the Python builtin `input`.
|
|
||||||
* the default builtin doesn't work correctly.
|
|
||||||
* </summary>
|
|
||||||
*/
|
|
||||||
public static string Input(string prompt) {
|
public static string Input(string prompt) {
|
||||||
Console.Write(prompt);
|
Console.Write(prompt);
|
||||||
return Console.ReadLine();
|
return Console.ReadLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** <summary>
|
|
||||||
* implementation of the `interpret.input` helper object method. tokenizes
|
|
||||||
* the result of the `input` builtin.
|
|
||||||
* </summary>
|
|
||||||
*/
|
|
||||||
public static TokenSet LexInput(string prompt) {
|
|
||||||
var input = Input(prompt);
|
|
||||||
return Interpreter.Lexer.Tokenize(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue