This commit is contained in:
Ingy döt Net 2013-04-10 16:57:12 -07:00
parent 518da4a923
commit 764da6cbbb
6144 changed files with 83610 additions and 11 deletions

View file

@ -0,0 +1,116 @@
#include <boost/spirit.hpp>
#include <boost/spirit/tree/ast.hpp>
#include <string>
#include <cassert>
#include <iostream>
#include <istream>
#include <ostream>
using boost::spirit::rule;
using boost::spirit::parser_tag;
using boost::spirit::ch_p;
using boost::spirit::real_p;
using boost::spirit::tree_node;
using boost::spirit::node_val_data;
// The grammar
struct parser: public boost::spirit::grammar<parser>
{
enum rule_ids { addsub_id, multdiv_id, value_id, real_id };
struct set_value
{
set_value(parser const& p): self(p) {}
void operator()(tree_node<node_val_data<std::string::iterator,
double> >& node,
std::string::iterator begin,
std::string::iterator end) const
{
node.value.value(self.tmp);
}
parser const& self;
};
mutable double tmp;
template<typename Scanner> struct definition
{
rule<Scanner, parser_tag<addsub_id> > addsub;
rule<Scanner, parser_tag<multdiv_id> > multdiv;
rule<Scanner, parser_tag<value_id> > value;
rule<Scanner, parser_tag<real_id> > real;
definition(parser const& self)
{
using namespace boost::spirit;
addsub = multdiv
>> *((root_node_d[ch_p('+')] | root_node_d[ch_p('-')]) >> multdiv);
multdiv = value
>> *((root_node_d[ch_p('*')] | root_node_d[ch_p('/')]) >> value);
value = real | inner_node_d[('(' >> addsub >> ')')];
real = leaf_node_d[access_node_d[real_p[assign_a(self.tmp)]][set_value(self)]];
}
rule<Scanner, parser_tag<addsub_id> > const& start() const
{
return addsub;
}
};
};
template<typename TreeIter>
double evaluate(TreeIter const& i)
{
double op1, op2;
switch (i->value.id().to_long())
{
case parser::real_id:
return i->value.value();
case parser::value_id:
case parser::addsub_id:
case parser::multdiv_id:
op1 = evaluate(i->children.begin());
op2 = evaluate(i->children.begin()+1);
switch(*i->value.begin())
{
case '+':
return op1 + op2;
case '-':
return op1 - op2;
case '*':
return op1 * op2;
case '/':
return op1 / op2;
default:
assert(!"Should not happen");
}
default:
assert(!"Should not happen");
}
return 0;
}
// the read/eval/write loop
int main()
{
parser eval;
std::string line;
while (std::cout << "Expression: "
&& std::getline(std::cin, line)
&& !line.empty())
{
typedef boost::spirit::node_val_data_factory<double> factory_t;
boost::spirit::tree_parse_info<std::string::iterator, factory_t> info =
boost::spirit::ast_parse<factory_t>(line.begin(), line.end(),
eval, boost::spirit::space_p);
if (info.full)
{
std::cout << "Result: " << evaluate(info.trees.begin()) << std::endl;
}
else
{
std::cout << "Error in expression." << std::endl;
}
}
};

View file

@ -0,0 +1,86 @@
(defun tokenize-stream (stream)
(labels ((whitespace-p (char)
(find char #(#\space #\newline #\return #\tab)))
(consume-whitespace ()
(loop while (whitespace-p (peek-char nil stream nil #\a))
do (read-char stream)))
(read-integer ()
(loop while (digit-char-p (peek-char nil stream nil #\space))
collect (read-char stream) into digits
finally (return (parse-integer (coerce digits 'string))))))
(consume-whitespace)
(let ((c (peek-char nil stream nil nil)))
(let ((token (case c
((nil) nil)
((#\() :lparen)
((#\)) :rparen)
((#\*) '*)
((#\/) '/)
((#\+) '+)
((#\-) '-)
(otherwise
(unless (digit-char-p c)
(cerror "Skip it." "Unexpected character ~w." c)
(read-char stream)
(return-from tokenize-stream
(tokenize-stream stream)))
(read-integer)))))
(unless (or (null token) (integerp token))
(read-char stream))
token))))
(defun group-parentheses (tokens &optional (delimited nil))
(do ((new-tokens '()))
((endp tokens)
(when delimited
(cerror "Insert it." "Expected right parenthesis."))
(values (nreverse new-tokens) '()))
(let ((token (pop tokens)))
(case token
((:lparen)
(multiple-value-bind (group remaining-tokens)
(group-parentheses tokens t)
(setf new-tokens (cons group new-tokens)
tokens remaining-tokens)))
((:rparen)
(if (not delimited)
(cerror "Ignore it." "Unexpected right parenthesis.")
(return (values (nreverse new-tokens) tokens))))
(otherwise
(push token new-tokens))))))
(defun group-operations (expression)
(flet ((gop (exp) (group-operations exp)))
(if (integerp expression)
expression
(destructuring-bind (a &optional op1 b op2 c &rest others)
expression
(unless (member op1 '(+ - * / nil))
(error "syntax error: in expr ~a expecting operator, not ~a"
expression op1))
(unless (member op2 '(+ - * / nil))
(error "syntax error: in expr ~a expecting operator, not ~a"
expression op2))
(cond
((not op1) (gop a))
((not op2) `(,(gop a) ,op1 ,(gop b)))
(t (let ((a (gop a)) (b (gop b)) (c (gop c)))
(if (and (member op1 '(+ -)) (member op2 '(* /)))
(gop `(,a ,op1 (,b ,op2 ,c) ,@others))
(gop `((,a ,op1 ,b) ,op2 ,c ,@others))))))))))
(defun infix-to-prefix (expression)
(if (integerp expression)
expression
(destructuring-bind (a op b) expression
`(,op ,(infix-to-prefix a) ,(infix-to-prefix b)))))
(defun evaluate (string)
(with-input-from-string (in string)
(eval
(infix-to-prefix
(group-operations
(group-parentheses
(loop for token = (tokenize-stream in)
until (null token)
collect token)))))))

View file

@ -0,0 +1,219 @@
import std.stdio, std.string, std.ascii, std.conv, std.array,
std.exception, std.traits;
struct Stack(T) {
T[] data;
alias data this;
void push(T top) pure nothrow { data ~= top; }
T pop(bool discard = true)() pure {
if (data.empty)
throw new Exception("Stack Empty");
auto top = data.back;
static if (discard)
data.popBack();
return top;
}
}
enum Type { Num, OBkt, CBkt, Add, Sub, Mul, Div }
immutable opChar = ["#", "(", ")", "+", "-", "*", "/"];
immutable opPrec = [ 0, -9, -9, 1, 1, 2, 2];
abstract class Visitor { void visit(XP e); }
final class XP {
immutable Type type;
immutable string str;
immutable int pos; // Optional, to dispaly AST struct.
XP LHS, RHS;
this(string s=")", int p = -1) nothrow {
str = s;
pos = p;
type = Type.Num;
foreach_reverse (immutable t; [EnumMembers!Type[1 .. $]])
if (opChar[t] == s)
type = t;
}
override int opCmp(Object other) pure {
auto rhs = cast(XP)other;
enforce(rhs !is null);
return opPrec[type] - opPrec[rhs.type];
}
void accept(Visitor v) { v.visit(this); }
}
final class AST {
XP root;
Stack!XP opr, num;
string xpr, token;
int xpHead, xpTail;
void joinXP(XP x) pure {
x.RHS = num.pop();
x.LHS = num.pop();
num.push(x);
}
string nextToken() pure {
while (xpHead < xpr.length && xpr[xpHead] == ' ')
xpHead++; // Skip spc.
xpTail = xpHead;
if (xpHead < xpr.length) {
token = xpr[xpTail .. xpTail + 1];
switch (token) {
case "(", ")", "+", "-", "*", "/": // Valid non-number.
xpTail++;
return token;
default: // Should be number.
if (token[0].isDigit()) {
while (xpTail < xpr.length && xpr[xpTail].isDigit())
xpTail++;
return xpr[xpHead .. xpTail];
} // Else may be error.
} // End switch.
}
if (xpTail < xpr.length)
throw new Exception("Invalid Char <" ~ xpr[xpTail] ~ ">");
return null;
} // End nextToken.
AST parse(in string s) {
bool expectingOP;
xpr = s;
try {
xpHead = xpTail = 0;
num = opr = null;
root = null;
opr.push(new XP); // CBkt, prevent evaluate null OP precedence.
while ((token = nextToken()) !is null) {
XP tokenXP = new XP(token, xpHead);
if (expectingOP) { // Process OP-alike XP.
switch (token) {
case ")":
while (opr.pop!false().type != Type.OBkt)
joinXP(opr.pop());
opr.pop();
expectingOP = true;
break;
case "+", "-", "*", "/":
while (tokenXP <= opr.pop!false())
joinXP(opr.pop());
opr.push(tokenXP);
expectingOP = false;
break;
default:
throw new Exception("Expecting Operator or ), not <"
~ token ~ ">");
}
} else { // Process Num-alike XP.
switch (token) {
case "+", "-", "*", "/", ")":
throw new Exception("Expecting Number or (, not <"
~ token ~ ">");
case "(":
opr.push(tokenXP);
expectingOP = false;
break;
default: // Number.
num.push(tokenXP);
expectingOP = true;
}
}
xpHead = xpTail;
} // End while.
while (opr.length > 1) // Join pending Op.
joinXP(opr.pop());
} catch(Exception e) {
writefln("%s\n%s\n%s^", e.msg, xpr, " ".replicate(xpHead));
root = null;
return this;
}
if (num.length != 1) { // Should be one XP left.
writefln("Parse Error...");
root = null;
} else {
root = num.pop();
}
return this;
} // End Parse.
} // End class AST.
// To display AST fancy struct.
void ins(ref char[][] s, in string v, in int p, in int l)
pure nothrow {
if (l + 1 > s.length)
s.length++;
while (s[l].length < p + v.length + 1)
s[l] ~= " ";
s[l][p .. p + v.length] = v;
}
final class CalcVis : Visitor {
int result, level;
string resultStr;
char[][] Tree;
static void opCall(AST a) {
if (a && a.root) {
auto c = new CalcVis;
a.root.accept(c);
foreach (immutable i; 1 .. c.Tree.length) { // More fancy.
bool flipflop = false;
enum char mk = '.';
foreach (immutable j; 0 .. c.Tree[i].length) {
while (j >= c.Tree[i - 1].length)
c.Tree[i - 1] ~= " ";
immutable c1 = c.Tree[i][j];
immutable c2 = c.Tree[i - 1][j];
if (flipflop && (c1 == ' ') && c2 == ' ')
c.Tree[i - 1][j] = mk;
if (c1 != mk && c1 != ' ' &&
(j == 0 || !isDigit(c.Tree[i][j - 1])))
flipflop = !flipflop;
}
}
foreach (const t; c.Tree)
writefln(t);
writefln("\n%s ==>\n%s = %s", a.xpr, c.resultStr, c.result);
} else
writefln("Evalute invalid or null Expression.");
}
// Calc. the value, display AST struct and eval order.
override void visit(XP xp) {
ins(Tree, xp.str, xp.pos, level);
level++;
if (xp.type == Type.Num) {
resultStr ~= xp.str;
result = to!int(xp.str);
} else {
resultStr ~= "(";
xp.LHS.accept(this);
immutable int lhs = result;
resultStr ~= opChar[xp.type];
xp.RHS.accept(this);
resultStr ~= ")";
switch (xp.type) {
case Type.Add: result = lhs + result; break;
case Type.Sub: result = lhs - result; break;
case Type.Mul: result = lhs * result; break;
case Type.Div: result = lhs / result; break;
default: throw new Exception("Invalid type");
}
}
level--;
}
}
void main(string[] args) {
immutable exp0 = "1 + 2*(3 - 2*(3 - 2)*((2 - 4)*5" ~
" - 22/(7 + 2*(3 - 1)) - 1)) + 1";
immutable exp = args.length > 1 ? args[1 .. $].join(" ") : exp0;
(new AST).parse(exp).CalcVis(); // Should be 60.
}

View file

@ -0,0 +1,18 @@
def eParser := <elang:syntax.makeEParser>
def LiteralExpr := <elang:evm.makeLiteralExpr>.asType()
def arithEvaluate(expr :String) {
def ast := eParser(expr)
def evalAST(ast) {
return switch (ast) {
match e`@a + @b` { evalAST(a) + evalAST(b) }
match e`@a - @b` { evalAST(a) - evalAST(b) }
match e`@a * @b` { evalAST(a) * evalAST(b) }
match e`@a / @b` { evalAST(a) / evalAST(b) }
match e`-@a` { -(evalAST(a)) }
match l :LiteralExpr { l.getValue() }
}
}
return evalAST(ast)
}

View file

@ -0,0 +1,8 @@
? arithEvaluate("1 + 2")
# value: 3
? arithEvaluate("(1 + 2) * 10 / 100")
# value: 0.3
? arithEvaluate("(1 + 2 / 2) * (5 + 5)")
# value: 20.0

View file

@ -0,0 +1,285 @@
#define std'dictionary'*.
#define std'basic'*.
#define std'patterns'*.
#define ext'io'*.
#define ext'convertors'*.
#subject parse_order.
#class Token
{
#field theValue.
#initializer
[
theValue := String.
]
#method parse_order'get = 0.
#method += aChar
[
theValue += aChar.
]
#method + aNode
[
^ aNode += self.
]
#method numeric = Real64Value &&literal:theValue &:erealformatter.
}
#class Node
{
#field theLeft.
#field theRight.
#role LeftAssigned
{
#method += aNode
[
theRight := aNode.
#shift.
]
}
#role Empty
{
#method += aNode
[
theLeft := aNode.
#shift LeftAssigned.
]
}
#initializer
[
#shift Empty.
]
#method + aNode
[
#if (self parse_order > aNode parse_order)?
[
self += aNode.
]
| [
aNode += self.
^ aNode.
].
]
#method += aNode
[
#if (theRight parse_order > aNode parse_order)?
[
theRight += aNode.
]
| [
theRight := aNode += theRight.
].
]
}
#class SummaryNode (Node)
{
#method parse_order'get = 2.
#method numeric = theLeft numeric + theRight numeric.
}
#class DifferenceNode (Node)
{
#method parse_order'get = 2.
#method numeric = theLeft numeric - theRight numeric.
}
#class ProductNode (Node)
{
#method parse_order'get = 1.
#method numeric = theLeft numeric * theRight numeric.
}
#class FractionNode (Node)
{
#method parse_order'get = 1.
#method numeric = theLeft numeric / theRight numeric.
}
#class SubExpression
{
#field theParser.
#field theCounter.
#role EOF
{
#method available'is [ control fail. ]
#method += aChar [ $self fail. ]
}
#initializer
[
theParser := arithmeval'Parser.
theCounter := Integer << 1.
]
#method available'is []
#method parse_order'get = 0.
#method + aNode
[
^ aNode += self.
]
#method append : aChar
[
#if control if:(aChar == 41)
[
theCounter -= 1.
]
| if:(aChar == 40)
[
theCounter += 1.
].
#if (theCounter == 0)?
[ #shift EOF. ^ $self. ].
theParser evaluate:aChar.
]
#method numeric = theParser numeric.
}
#class Parser
{
#field theToken.
#field theTopNode.
#role Brackets
{
#method evaluate : aChar
[
theToken += aChar.
#if theToken available'is
| [
#shift.
].
]
}
#role Start
{
#method evaluate : aChar
[
#if (40 == aChar)?
[
theToken := SubExpression.
theTopNode := theToken.
#shift Brackets.
]
| [
theToken := Token.
theTopNode := theToken.
theToken += aChar.
#shift.
].
]
}
#role Operator
{
#method evaluate : aChar
[
#if Control if:(48 < aChar) if:(58 > aChar)
[
theToken := (Token += aChar).
theTopNode += theToken.
#shift.
]
| if:(40 == aChar)
[
theToken := SubExpression.
theTopNode += theToken.
#shift Brackets.
]
| [ $self fail. ].
]
}
#initializer
[
#shift Start.
]
#method numeric = theTopNode numeric.
#method evaluate : aChar
[
#if Control if:(48 < aChar) if:(58 > aChar)
[
theToken += aChar.
]
| if:(42 == aChar) // *
[
theTopNode := theTopNode + ProductNode.
#shift Operator.
]
| if:(47 == aChar) // /
[
theTopNode := theTopNode + FractionNode.
#shift Operator.
]
| if:(43 == aChar) // +
[
theTopNode := theTopNode + SummaryNode.
#shift Operator.
]
| if:(45 == aChar) // -
[
theTopNode := theTopNode + DifferenceNode.
#shift Operator.
]
| if:(40 == aChar)
[
theToken := SubExpression.
theTopNode := theToken.
#shift Brackets.
]
| [ $self fail. ].
]
#method start : aProcess
[
aProcess run:self.
^ self numeric.
]
}
#symbol Program =
[
#var aText := String.
#while ((Console >> aText) length > 0)?
[
#var aParser := Parser.
Console << "=" << aParser start:Scan::aText | ^^"Invalid Expression".
Console << "%n".
].
].

View file

@ -0,0 +1,30 @@
number ::= $numeric;
numeric ::= "(" sub_expr;
numeric ::= number;
factor ::= number factor_r;
factor ::= "(" sub_expr;
sum ::= "+" factor ;
difference ::= "-" factor ;
multiply ::= "*" numeric;
divide ::= "/" numeric;
factor_r ::= multiply factor_r;
factor_r ::= divide factor_r;
factor_r ::= $eps;
expr_r ::= sum expr_r;
expr_r ::= difference expr_r;
expr_r ::= $eps;
neg_r ::= factor_r expr_r;
sub_expr ::= expression sub_expr_r;
sub_expr_r ::= ")" factor_r;
neg_expression ::= $numeric neg_r;
expression ::= factor expr_r;
expression ::= "-" neg_expression;
print ::= "?" expression;
start ::= print;
print => &nil 'program'output $body ^write;
multiply => $body ^multiply;
divide => $body ^divide;
sum => $body ^add;
difference => $body ^subtract;
neg_expression => 0 $terminal ^subtract $body;
number => $terminal $body;