Created
October 26, 2015 20:03
-
-
Save Timwi/dcb40ad1906b7286cfdf to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using RT.Generexes; | |
using RT.Util; | |
using RT.Util.Consoles; | |
using RT.Util.ExtensionMethods; | |
using S = RT.Generexes.Stringerex; | |
using SN = RT.Generexes.Stringerex<Timwi.Temp.GenerexBasedParser.ExpressionNode>; | |
namespace Timwi.Temp | |
{ | |
using GenerexBasedParser; | |
partial class Program | |
{ | |
private static int ExpressionParser() | |
{ | |
var unaryFunctionsRaw = new Dictionary<string, Func<double, double>> | |
{ | |
{ "sin", Math.Sin }, | |
{ "cos", Math.Cos }, | |
{ "tan", Math.Tan }, | |
{ "sinh", Math.Sinh}, | |
{ "cosh", Math.Cosh }, | |
{ "tanh", Math.Tanh }, | |
{ "sqrt", Math.Sqrt }, | |
{ "sqr", x => x * x } | |
}; | |
var unaryFunctions = unaryFunctionsRaw.Select(kvp => new S(kvp.Key).Process(m => kvp.Value)); | |
var unaryOperatorsRaw = new Dictionary<string, Func<double, double>> | |
{ | |
{ "+", d => d }, | |
{ "-", d => -d } | |
}; | |
var unaryOperators = unaryOperatorsRaw.Select(kvp => new S(kvp.Key).Process(m => kvp.Value)); | |
var digit = (S) (ch => ch >= '0' && ch <= '9'); | |
var number = digit.RepeatGreedy().Then(new S('.').Then(digit.RepeatGreedy(1)).OptionalGreedy()).Where(m => m.Length > 0).Process(m => (ExpressionNode) new NumberNode { Number = Convert.ToDouble(m.Match) }); | |
var functionNameFirstCharacter = (S) (ch => ch == '_' || char.IsLetter(ch)); | |
var functionNameCharacter = (S) (ch => ch == '_' || char.IsLetterOrDigit(ch)); | |
var functionName = functionNameFirstCharacter.Then(functionNameCharacter.RepeatGreedy()).Process(m => m.Match); | |
var expression = SN.Recursive(expr => | |
{ | |
var leftAssociativeBinaryOperators = Ut.Lambda((SN higherPrecedence, Stringerex<Func<double, double, double>> operators) => | |
higherPrecedence.ThenRaw(operators.ThenRaw(higherPrecedence, (op, node) => new { Op = op, ExpressionNode = node }).RepeatGreedy(), | |
(firstOperand, operands) => operands.Aggregate(firstOperand, (prev, next) => (ExpressionNode) new BinaryOperatorNode { Left = prev, Right = next.ExpressionNode, Operation = next.Op }))); | |
var rightAssociativeBinaryOperators = Ut.Lambda((SN higherPrecedence, Stringerex<Func<double, double, double>> operators) => | |
higherPrecedence.ThenRaw(operators, (node, op) => new { Op = op, ExpressionNode = node }).RepeatGreedy().ThenRaw(higherPrecedence, | |
(operands, lastOperand) => operands.Reverse().Aggregate(lastOperand, (prev, next) => (ExpressionNode) new BinaryOperatorNode { Left = next.ExpressionNode, Right = prev, Operation = next.Op }))); | |
var expectCloseParen = ((S) ')') | |
.Or(S.End.Where(m => { throw new ParseException(m.Index, "Unexpected end of expression: ')' missing."); })) | |
.Or(new S().Where(m => { throw new ParseException(m.Index, "Unrecognized operator."); })); | |
var expectOpenParen = ((S) '(') | |
.Or(S.End.Where(m => { throw new ParseException(m.Index, "Unexpected end of expression: '(' missing."); })) | |
.Or(new S().Where(m => { throw new ParseException(m.Index, "'(' expected."); })); | |
var power = SN.Recursive(pwr => | |
{ | |
var primaryExpr = Stringerex.Ors( | |
number, | |
((S) '(').Then(expr).Then(expectCloseParen), | |
S.Ors(unaryFunctions).Then(expectOpenParen).ThenRaw(expr, (func, node) => (ExpressionNode) new UnaryOperatorNode { Child = node, Operation = func }).Then(expectCloseParen), | |
S.Ors(unaryOperators).ThenRaw(pwr, (func, node) => (ExpressionNode) new UnaryOperatorNode { Child = node, Operation = func }), | |
S.End.Process<ExpressionNode>(m => { throw new ParseException(m.Index, "Unexpected end of expression: operand missing."); }), | |
((S) ')').Process<ExpressionNode>(m => { throw new ParseException(m.Index, "Expected a number, parenthesised expression, unary operator or function name."); }), | |
new SN(null).Where(m => { throw new ParseException(m.Index, "Unrecognized unary operator or function name."); }) | |
).Atomic(); | |
return rightAssociativeBinaryOperators(primaryExpr, ((S) '^').Process(m => new Func<double, double, double>(Math.Pow))); | |
}); | |
var multiplicative = leftAssociativeBinaryOperators(power, Stringerex.Ors( | |
((S) '*').Process(m => Ut.Lambda((double a, double b) => a * b)), | |
((S) '/').Process(m => Ut.Lambda((double a, double b) => a / b)))); | |
var additive = leftAssociativeBinaryOperators(multiplicative, Stringerex.Ors( | |
((S) '+').Process(m => Ut.Lambda((double a, double b) => a + b)), | |
((S) '-').Process(m => Ut.Lambda((double a, double b) => a - b)) | |
)); | |
return additive; | |
}).Then(Stringerex.Ors( | |
((S) ')').LookAhead().Where(m => { throw new ParseException(m.Index, "Extraneous closing parenthesis."); }), | |
S.End.LookAheadNegative().Where(m => { throw new ParseException(m.Index, "Missing operator."); }), | |
new S() | |
)); | |
while (true) | |
{ | |
ConsoleUtil.Write("> ".Color(ConsoleColor.Cyan)); | |
var line = Console.ReadLine(); | |
if (string.IsNullOrEmpty(line)) | |
continue; | |
if (line == "exit") | |
return 0; | |
try | |
{ | |
var parsed = expression.RawMatchExact(line.ToCharArray()); | |
if (parsed == null) | |
ConsoleUtil.WriteLine("Parse error.".Color(ConsoleColor.Yellow)); | |
else | |
ConsoleUtil.WriteLine(("= " + parsed.Evaluate()).Color(ConsoleColor.Green)); | |
} | |
catch (ParseException p) | |
{ | |
ConsoleUtil.WriteLine(new string(' ', p.Index + 2) + "^".Color(ConsoleColor.Red)); | |
ConsoleUtil.WriteLine(p.Message.Color(ConsoleColor.Magenta)); | |
} | |
} | |
} | |
} | |
namespace GenerexBasedParser | |
{ | |
sealed class ParseException : Exception | |
{ | |
public int Index { get; private set; } | |
public ParseException(int index, string message) | |
: base(message) | |
{ | |
Index = index; | |
} | |
} | |
abstract class ExpressionNode | |
{ | |
public abstract double Evaluate(); | |
} | |
sealed class UnaryOperatorNode : ExpressionNode | |
{ | |
public ExpressionNode Child; | |
public Func<double, double> Operation; | |
public override double Evaluate() { return Operation(Child.Evaluate()); } | |
} | |
sealed class BinaryOperatorNode : ExpressionNode | |
{ | |
public ExpressionNode Left; | |
public ExpressionNode Right; | |
public Func<double, double, double> Operation; | |
public override double Evaluate() { return Operation(Left.Evaluate(), Right.Evaluate()); } | |
} | |
sealed class NumberNode : ExpressionNode | |
{ | |
public double Number; | |
public override double Evaluate() { return Number; } | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment