September 2017 Update

This commit is contained in:
Ingy döt Net 2017-09-23 10:01:46 +02:00
parent bba7bfd280
commit ba8067c3b7
14570 changed files with 153136 additions and 63871 deletions

View file

@ -1,302 +0,0 @@
#define system.
#define extensions.
// --- Token ---
#class Token
{
#field theValue.
#constructor new
[
theValue := String new.
]
#constructor new : aValue
[
theValue := String new:aValue.
]
#method ParseOrder = 0.
#method append : aChar
[
theValue += aChar.
]
#method add : aNode
[
^ aNode += self.
]
#method Number = convertor toReal:(theValue value).
}
// --- Node ---
#class Node
{
#field theLeft.
#field theRight.
#field theState.
#method setRight : aNode
[
theRight := aNode.
theState := %appendRight.
]
#method setLeft : aNode
[
theLeft := aNode.
theState := %setRight.
]
#constructor new
[
theState := %setLeft.
]
#method add : aNode
= (self ParseOrder > aNode ParseOrder)
? [
self += aNode.
^ self.
]
! [
aNode += self.
^ aNode.
].
#method appendRight : aNode
[
(theRight ParseOrder > aNode ParseOrder)
? [
theRight += aNode.
]
! [
theRight := aNode += theRight.
].
]
#method append : anObject
= $self~theState eval:anObject.
#method => theState.
}
// --- SummaryNode
#class SummaryNode : Node
{
#method ParseOrder = 2.
#method Number = theLeft Number + theRight Number.
}
// --- DifferenceNode ---
#class DifferenceNode : Node
{
#method ParseOrder = 2.
#method Number = theLeft Number - theRight Number.
}
// --- ProductNode ---
#class ProductNode : Node
{
#method ParseOrder = 1.
#method Number = theLeft Number * theRight Number.
}
// --- FractionNode ---
#class FractionNode : Node
{
#method ParseOrder = 1.
#method Number = theLeft Number / theRight Number.
}
// --- SubExpression ---
#class SubExpression
{
#field theParser.
#field theCounter.
#constructor new
[
theParser := arithmeval'Parser new.
theCounter := Integer new:1.
]
#method ParseOrder = 0.
#method add : aNode
[
^ aNode += self.
]
#method validate
[
(theCounter < 0)
? [ #throw Exception new:"Invalid expression". ].
^ (0 == theCounter).
]
#method append : aChar
[
aChar =>
41 ? [
theCounter -= 1.
]
40 ? [ theCounter += 1 ]
! [ theParser evaluate:aChar ].
]
#method Number
= $self validate
? [ theParser Number ]
! [ #throw Exception new:"Invalid expression". ].
}
// ---- Parser ----
#class Parser : system'routines'BasePattern
{
#field theToken.
#field theTopNode.
#field theState.
#method onBrackets : aChar
[
theToken += aChar.
(theToken validate)
? [
theState := %onDigit.
].
]
#method onStart : aChar
[
aChar =>
40 ? [ // (
theToken := SubExpression new.
theTopNode := theToken.
theState := %onBrackets.
]
45 ? [ // -
theToken := DifferenceNode new add:(Token new:"0").
theTopNode := theToken.
theState := %onOperator.
]
! [
theToken := Token new.
theTopNode := theToken.
theState := %onDigit.
$self appendDigit:aChar.
].
]
#method onOperator : aChar
[
aChar =>
40 ? [
theToken := SubExpression new.
theTopNode += theToken.
theState := %onBrackets.
]
! [
theToken := Token new.
theTopNode += theToken.
theState := %onDigit.
$self appendDigit:aChar.
].
]
#constructor new
[
theState := %onStart.
]
#method Number = theTopNode Number.
#method appendDigit : aChar
[
(aChar >= 48) and:(aChar < 58)
? [
theToken += aChar.
]
! [
#throw Exception new:"Invalid expression".
]
]
#method onDigit : aChar
[
aChar =>
40 ? [ // (
theToken := SubExpression new.
theTopNode := theToken.
theState := %onBrackets.
]
42 ? [ // *
theTopNode := theTopNode + ProductNode new.
theState := %onOperator.
]
43 ? [ // +
theTopNode := theTopNode + SummaryNode new.
theState := %onOperator.
]
45 ? [ // -
theTopNode := theTopNode + DifferenceNode new.
theState := %onOperator.
]
47 ? // /
[
theTopNode := theTopNode + FractionNode new.
theState := %onOperator.
]
! [
$self appendDigit:aChar.
].
]
#method eval : aChar = $self~theState eval:aChar.
}
#symbol program =
[
#var aText := String new.
control while:(consoleEx readLine:aText length > 0) &do:
[
#var aParser := Parser new.
consoleEx writeLine:"=" :(aParser foreach:aText Number)
| if &e:
[
consoleEx writeLine:"Invalid Expression".
].
].
].

View file

@ -1,30 +0,0 @@
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;

View file

@ -1,331 +1,315 @@
#import system.
#import system'routines.
#import extensions.
#import extensions'text.
import system'routines.
import extensions.
import extensions'text.
#class Token
class Token
{
#field theValue.
#field theLevel.
object theValue.
#constructor new &level:aLevel
object rprop level :: theLevel.
constructor new level:aLevel
[
theValue := StringWriter new.
theLevel := aLevel + 9.
]
#method level = theLevel.
#method append : aChar
append : aChar
[
theValue << aChar.
]
#method number = theValue get toReal.
number = theValue get; toReal.
}
#class Node
class Node
{
#field theLeft.
#field theRight.
#field theLevel.
object prop left :: theLeft.
object prop right :: theRight.
object rprop level :: theLevel.
#constructor new &level:aLevel
constructor new level:aLevel
[
theLevel := aLevel.
]
#method level = theLevel.
#method left = theLeft.
#method right = theRight.
#method set &left:anObject [ theLeft := anObject. ]
#method set &right:anObject [ theRight := anObject. ]
}
#class SummaryNode :: Node
class SummaryNode :: Node
{
#constructor new &level:aLevel
<= %new &level:(aLevel + 1).
constructor new level:aLevel
<= new level(aLevel + 1).
#method number = theLeft number + theRight number.
number = theLeft number + theRight number.
}
#class DifferenceNode :: Node
class DifferenceNode :: Node
{
#constructor new &level:aLevel
<= %new &level:(aLevel + 1).
constructor new level:aLevel
<= new level(aLevel + 1).
#method number = theLeft number - theRight number.
number = theLeft number - theRight number.
}
#class ProductNode :: Node
class ProductNode :: Node
{
#constructor new &level:aLevel
<= %new &level:(aLevel + 2).
constructor new level:aLevel
<= new level(aLevel + 2).
#method number = theLeft number * theRight number.
number = theLeft number * theRight number.
}
#class FractionNode :: Node
class FractionNode :: Node
{
#constructor new &level:aLevel
<= %new &level:(aLevel + 2).
constructor new level:aLevel
<= new level(aLevel + 2).
#method number = theLeft number / theRight number.
number = theLeft number / theRight number.
}
#class Expression
class Expression
{
#field theLevel.
#field theTop.
object rprop level :: theLevel.
object prop top :: theTop.
#constructor new &level:aLevel
constructor new level:aLevel
[
theLevel := aLevel.
theLevel := aLevel
]
#method top = theTop.
right = theTop.
#method set &top:aNode [ theTop := aNode. ]
set right:aNode [ theTop := aNode ]
#method right = theTop.
#method set &right:aNode [ theTop := aNode. ]
#method level = theLevel.
#method number => theTop.
number => theTop.
}
#symbol operatorState = (:ch)
operatorState = (:ch)
[
ch =>
#40 ? [ // (
self new &bracket goto &start.
]
$40 [ // (
^ closure newBracket; gotoStarting
];
! [
self new &token append:ch goto &token.
^ closure newToken; append:ch; gotoToken
].
].
#symbol tokenState = (:ch)
tokenState = (:ch)
[
ch =>
#41 ? [ // )
self close &bracket goto &token.
]
#42 ? [ // *
self new &product goto &operator.
]
#43 ? [ // +
self new &summary goto &operator.
]
#45 ? [ // -
self new &difference goto &operator.
]
#47 ? // /
[
self new &fraction goto &operator.
]
$41 [ // )
^ closure closeBracket; gotoToken
];
$42 [ // *
^ closure newProduct; gotoOperator
];
$43 [ // +
^ closure newSummary; gotoOperator
];
$45 [ // -
^ closure newDifference; gotoOperator
];
$47 [ // /
^ closure newFraction; gotoOperator
];
! [
self append:ch.
^ closure append:ch
].
].
#symbol startState = (:ch)
startState = (:ch)
[
ch =>
#40 ? [ // (
self new &bracket goto &start.
]
#45 ? [ // -
self new &token append &literal:"0" new &difference goto &operator.
]
$40 [ // (
^ closure newBracket; gotoStarting
];
$45 [ // -
^ closure newToken; append literal:"0"; newDifference; gotoOperator
];
! [
self new &token append:ch goto &token.
^ closure newToken; append:ch; gotoToken
].
].
#class Scope
class Scope
{
#field theState.
#field theLevel.
#field theParser.
#field theToken.
#field theExpression.
object theState.
object theLevel.
object theParser.
object theToken.
object theExpression.
#constructor new &parser:aParser
constructor new parser:aParser
[
theState := startState.
theLevel := 0.
theExpression := Expression new &level:0.
theExpression := Expression new level:0.
theParser := aParser.
]
#method new &token
newToken
[
theToken := theParser append &token &expression:theExpression &level:theLevel.
theToken := theParser appendToken expression:theExpression level:theLevel.
]
#method new &summary
newSummary
[
theToken := nil.
theParser append &summary &expression:theExpression &level:theLevel.
theParser appendSummary expression:theExpression level:theLevel.
]
#method new &difference
newDifference
[
theToken := nil.
theParser append &difference &expression:theExpression &level:theLevel.
theParser appendDifference expression:theExpression level:theLevel
]
#method new &product
newProduct
[
theToken := nil.
theParser append &product &expression:theExpression &level:theLevel.
theParser appendProduct expression:theExpression level:theLevel
]
#method new &fraction
newFraction
[
theToken := nil.
theParser append &fraction &expression:theExpression &level:theLevel.
theParser appendFraction expression:theExpression level:theLevel
]
#method new &bracket
newBracket
[
theToken := nil.
theLevel := theLevel + 10.
theParser append &subexpression &expression:theExpression &level:theLevel.
theParser appendSubexpression expression:theExpression level:theLevel.
]
#method close &bracket
closeBracket
[
(theLevel < 10)
? [ #throw InvalidArgumentException new &message:"Invalid expression". ].
if (theLevel < 10)
[ InvalidArgumentException new:"Invalid expression"; raise ].
theLevel := theLevel - 10.
theLevel := theLevel - 10
]
#method append:ch
append:ch
[
((ch >= #48) and:(ch < #58))
? [ theToken append:ch. ]
! [ #throw InvalidArgumentException new &message:"Invalid expression". ].
if((ch >= $48) && (ch < $58))
[ theToken append:ch ];
[ InvalidArgumentException new:"Invalid expression"; raise ]
]
#method append &literal:aLiteral
append literal:aLiteral
[
aLiteral run &each: ch [ self append:ch. ].
aLiteral forEach(:ch)[ self append:ch ]
]
#method goto &start
gotoStarting
[
theState := startState.
theState := startState
]
#method goto &token
gotoToken
[
theState := tokenState.
theState := tokenState
]
#method goto &operator
gotoOperator
[
theState := operatorState.
theState := operatorState
]
#method number => theExpression.
number => theExpression.
#method => theState.
dispatch => theState.
}
#class Parser
class Parser
{
#method append &token &expression:anExpression &level:aLevel
appendToken expression:anExpression level:aLevel
[
#var aToken := Token new &level:aLevel.
var aToken := Token new level:aLevel.
anExpression set &top:($self append &last:(anExpression top) &new:aToken).
anExpression set top:($self append last(anExpression top) new:aToken).
^ aToken.
^ aToken
]
#method append &summary &expression:anExpression &level:aLevel
appendSummary expression:anExpression level:aLevel
[
anExpression set &top:($self append &last:(anExpression top) &new:(SummaryNode new &level:aLevel)).
anExpression set top($self append last(anExpression top) new(SummaryNode new level:aLevel)).
]
#method append &difference &expression:anExpression &level:aLevel
appendDifference expression:anExpression level:aLevel
[
anExpression set &top:($self append &last:(anExpression top) &new:(DifferenceNode new &level:aLevel)).
anExpression set top($self append last(anExpression top) new(DifferenceNode new level:aLevel)).
]
#method append &product &expression:anExpression &level:aLevel
appendProduct expression:anExpression level:aLevel
[
anExpression set &top:($self append &last:(anExpression top) &new:(ProductNode new &level:aLevel)).
anExpression set top($self append last(anExpression top) new(ProductNode new level:aLevel)).
]
#method append &fraction &expression:anExpression &level:aLevel
appendFraction expression:anExpression level:aLevel
[
anExpression set &top:($self append &last:(anExpression top) &new:(FractionNode new &level:aLevel)).
anExpression set top($self append last(anExpression top) new(FractionNode new level:aLevel))
]
#method append &subexpression &expression:anExpression &level:aLevel
appendSubexpression expression:anExpression level:aLevel
[
anExpression set &top:($self append &last:(anExpression top) &new:(Expression new &level:aLevel)).
anExpression set top($self append last(anExpression top) new(Expression new level:aLevel)).
]
#method append &last:aLastNode &new:aNewNode
append last:aLastNode new:aNewNode
[
($nil == aLastNode)
? [ ^ aNewNode. ].
if($nil == aLastNode)
[ ^ aNewNode ].
(aNewNode level <= aLastNode level)
? [ aNewNode set &left:aLastNode. ^ aNewNode. ].
if (aNewNode level <= aLastNode level)
[ aNewNode set left:aLastNode. ^ aNewNode ].
#var aParent := aLastNode.
#var aCurrent := aLastNode right.
#loop (($nil != aCurrent) and:[ aNewNode level > aCurrent level ]) ?
var aParent := aLastNode.
var aCurrent := aLastNode right.
while (($nil != aCurrent) && $(aNewNode level > aCurrent level))
[ aParent := aCurrent. aCurrent := aCurrent right. ].
($nil == aCurrent)
? [ aParent set &right:aNewNode. ]
! [ aNewNode set &left:aCurrent. aParent set &right:aNewNode. ].
if ($nil == aCurrent)
[ aParent set right:aNewNode. ];
[ aNewNode set left:aCurrent. aParent set right:aNewNode ].
^ aLastNode.
^ aLastNode
]
#method run : aText
run : aText
[
#var aScope := Scope new &parser:$self.
var aScope := Scope new parser:$self.
aText run &each: ch [ aScope eval:ch. ].
aText forEach(:ch)[ aScope eval:ch ].
^ aScope number.
^ aScope number
]
}
#symbol program =
program =
[
#var aText := String new.
#var aParser := Parser new.
var aText := String new.
var aParser := Parser new.
[ console readLine save &to:aText length > 0] doWhile:
$(console readLine; saveTo:aText; length > 0) doWhile:
[
console writeLine:"=" :(aParser run:aText)
| if &Error:e [
console writeLine:"Invalid Expression".
].
try(console printLine("=",aParser run:aText))
{
on(Exception e)
[
console writeLine:"Invalid Expression"
]
}.
aText clear.
aText clear
].
].

View file

@ -1,28 +1,47 @@
{-# LANGUAGE FlexibleContexts #-}
import Text.Parsec
import Text.Parsec.Expr
import Text.Parsec.Combinator
import Data.Functor
import Data.Function (on)
data Exp = Num Int
| Add Exp Exp
| Sub Exp Exp
| Mul Exp Exp
| Div Exp Exp
data Exp
= Num Int
| Add Exp
Exp
| Sub Exp
Exp
| Mul Exp
Exp
| Div Exp
Exp
expr
:: Stream s m Char
=> ParsecT s u m Exp
expr = buildExpressionParser table factor
where table = [[op "*" (Mul) AssocLeft, op "/" (Div) AssocLeft]
,[op "+" (Add) AssocLeft, op "-" (Sub) AssocLeft]]
op s f assoc = Infix (f <$ string s) assoc
factor = (between `on` char) '(' ')' expr
<|> (Num . read <$> many1 digit)
where
table =
[ [op "*" Mul AssocLeft, op "/" Div AssocLeft]
, [op "+" Add AssocLeft, op "-" Sub AssocLeft]
]
op s f = Infix (f <$ string s)
factor = (between `on` char) '(' ')' expr <|> (Num . read <$> many1 digit)
eval :: Num a => Exp -> a
eval (Num x) = fromIntegral x
eval (Add a b) = eval a + eval b
eval (Sub a b) = eval a - eval b
eval (Mul a b) = eval a * eval b
eval
:: Integral a
=> Exp -> a
eval (Num x) = fromIntegral x
eval (Add a b) = eval a + eval b
eval (Sub a b) = eval a - eval b
eval (Mul a b) = eval a * eval b
eval (Div a b) = eval a `div` eval b
solution :: Num a => String -> a
solution
:: Integral a
=> String -> a
solution = either (const (error "Did not parse")) eval . parse expr ""
main :: IO ()
main = print $ solution "(1+3)*7"

View file

@ -1,138 +1,165 @@
import java.util.Stack;
public class ArithmeticEvaluation
{
public static enum Parentheses { LEFT, RIGHT }
public class ArithmeticEvaluation {
public static enum BinaryOperator
{
ADD('+', 1) {
public BigRational eval(BigRational leftValue, BigRational rightValue) { return leftValue.add(rightValue); }
},
SUB('-', 1) {
public BigRational eval(BigRational leftValue, BigRational rightValue) { return leftValue.subtract(rightValue); }
},
MUL('*', 2) {
public BigRational eval(BigRational leftValue, BigRational rightValue) { return leftValue.multiply(rightValue); }
},
DIV('/', 2) {
public BigRational eval(BigRational leftValue, BigRational rightValue) { return leftValue.divide(rightValue); }
};
public final char symbol;
public final int precedence;
BinaryOperator(char symbol, int precedence)
{
this.symbol = symbol;
this.precedence = precedence;
public interface Expression {
BigRational eval();
}
public abstract BigRational eval(BigRational leftValue, BigRational rightValue);
}
public enum Parentheses {LEFT}
public static class BinaryExpression
{
public Object leftOperand = null;
public BinaryOperator operator = null;
public Object rightOperand = null;
public enum BinaryOperator {
ADD('+', 1),
SUB('-', 1),
MUL('*', 2),
DIV('/', 2);
public BinaryExpression(Object leftOperand, BinaryOperator operator, Object rightOperand)
{
this.leftOperand = leftOperand;
this.operator = operator;
this.rightOperand = rightOperand;
}
public final char symbol;
public final int precedence;
public BigRational eval()
{
BigRational leftValue = (leftOperand instanceof BinaryExpression) ? ((BinaryExpression)leftOperand).eval() : (BigRational)leftOperand;
BigRational rightValue = (rightOperand instanceof BinaryExpression) ? ((BinaryExpression)rightOperand).eval() : (BigRational)rightOperand;
return operator.eval(leftValue, rightValue);
}
public String toString()
{ return "(" + leftOperand + " " + operator.symbol + " " + rightOperand + ")"; }
}
public static void createNewOperand(BinaryOperator operator, Stack<Object> operands)
{
Object rightOperand = operands.pop();
operands.push(new BinaryExpression(operands.pop(), operator, rightOperand));
return;
}
public static Object createExpression(String inputString)
{
int curIndex = 0;
boolean afterOperand = false;
Stack<Object> operands = new Stack<Object>();
Stack<Object> operators = new Stack<Object>();
inputStringLoop:
while (curIndex < inputString.length())
{
int startIndex = curIndex;
char c = inputString.charAt(curIndex++);
if (Character.isWhitespace(c))
continue;
if (afterOperand)
{
if (c == ')')
{
Object operator = null;
while (!operators.isEmpty() && ((operator = operators.pop()) != Parentheses.LEFT))
createNewOperand((BinaryOperator)operator, operands);
continue;
BinaryOperator(char symbol, int precedence) {
this.symbol = symbol;
this.precedence = precedence;
}
afterOperand = false;
for (BinaryOperator operator : BinaryOperator.values())
{
if (c == operator.symbol)
{
while (!operators.isEmpty() && (operators.peek() != Parentheses.LEFT) && (((BinaryOperator)operators.peek()).precedence >= operator.precedence))
createNewOperand((BinaryOperator)operators.pop(), operands);
operators.push(operator);
continue inputStringLoop;
}
public BigRational eval(BigRational leftValue, BigRational rightValue) {
switch (this) {
case ADD:
return leftValue.add(rightValue);
case SUB:
return leftValue.subtract(rightValue);
case MUL:
return leftValue.multiply(rightValue);
case DIV:
return leftValue.divide(rightValue);
}
throw new IllegalStateException();
}
public static BinaryOperator forSymbol(char symbol) {
for (BinaryOperator operator : values()) {
if (operator.symbol == symbol) {
return operator;
}
}
throw new IllegalArgumentException(String.valueOf(symbol));
}
throw new IllegalArgumentException();
}
if (c == '(')
{
operators.push(Parentheses.LEFT);
continue;
}
afterOperand = true;
while (curIndex < inputString.length())
{
c = inputString.charAt(curIndex);
if (((c < '0') || (c > '9')) && (c != '.'))
break;
curIndex++;
}
operands.push(BigRational.valueOf(inputString.substring(startIndex, curIndex)));
}
while (!operators.isEmpty())
{
Object operator = operators.pop();
if (operator == Parentheses.LEFT)
throw new IllegalArgumentException();
createNewOperand((BinaryOperator)operator, operands);
}
Object expression = operands.pop();
if (!operands.isEmpty())
throw new IllegalArgumentException();
return expression;
}
public static class Number implements Expression {
private final BigRational number;
public static void main(String[] args)
{
String[] testExpressions = { "2+3", "2+3/4", "2*3-4", "2*(3+4)+5/6", "2 * (3 + (4 * 5 + (6 * 7) * 8) - 9) * 10", "2*-3--4+-.25" };
for (String testExpression : testExpressions)
{
Object expression = createExpression(testExpression);
System.out.println("Input: \"" + testExpression + "\", AST: \"" + expression + "\", eval=" + (expression instanceof BinaryExpression ? ((BinaryExpression)expression).eval() : expression));
public Number(BigRational number) {
this.number = number;
}
@Override
public BigRational eval() {
return number;
}
@Override
public String toString() {
return number.toString();
}
}
public static class BinaryExpression implements Expression {
public final Expression leftOperand;
public final BinaryOperator operator;
public final Expression rightOperand;
public BinaryExpression(Expression leftOperand, BinaryOperator operator, Expression rightOperand) {
this.leftOperand = leftOperand;
this.operator = operator;
this.rightOperand = rightOperand;
}
@Override
public BigRational eval() {
BigRational leftValue = leftOperand.eval();
BigRational rightValue = rightOperand.eval();
return operator.eval(leftValue, rightValue);
}
@Override
public String toString() {
return "(" + leftOperand + " " + operator.symbol + " " + rightOperand + ")";
}
}
private static void createNewOperand(BinaryOperator operator, Stack<Expression> operands) {
Expression rightOperand = operands.pop();
Expression leftOperand = operands.pop();
operands.push(new BinaryExpression(leftOperand, operator, rightOperand));
}
public static Expression parse(String input) {
int curIndex = 0;
boolean afterOperand = false;
Stack<Expression> operands = new Stack<>();
Stack<Object> operators = new Stack<>();
while (curIndex < input.length()) {
int startIndex = curIndex;
char c = input.charAt(curIndex++);
if (Character.isWhitespace(c))
continue;
if (afterOperand) {
if (c == ')') {
Object operator;
while (!operators.isEmpty() && ((operator = operators.pop()) != Parentheses.LEFT))
createNewOperand((BinaryOperator) operator, operands);
continue;
}
afterOperand = false;
BinaryOperator operator = BinaryOperator.forSymbol(c);
while (!operators.isEmpty() && (operators.peek() != Parentheses.LEFT) && (((BinaryOperator) operators.peek()).precedence >= operator.precedence))
createNewOperand((BinaryOperator) operators.pop(), operands);
operators.push(operator);
continue;
}
if (c == '(') {
operators.push(Parentheses.LEFT);
continue;
}
afterOperand = true;
while (curIndex < input.length()) {
c = input.charAt(curIndex);
if (((c < '0') || (c > '9')) && (c != '.'))
break;
curIndex++;
}
operands.push(new Number(BigRational.valueOf(input.substring(startIndex, curIndex))));
}
while (!operators.isEmpty()) {
Object operator = operators.pop();
if (operator == Parentheses.LEFT)
throw new IllegalArgumentException();
createNewOperand((BinaryOperator) operator, operands);
}
Expression expression = operands.pop();
if (!operands.isEmpty())
throw new IllegalArgumentException();
return expression;
}
public static void main(String[] args) {
String[] testExpressions = {
"2+3",
"2+3/4",
"2*3-4",
"2*(3+4)+5/6",
"2 * (3 + (4 * 5 + (6 * 7) * 8) - 9) * 10",
"2*-3--4+-.25"};
for (String testExpression : testExpressions) {
Expression expression = parse(testExpression);
System.out.printf("Input: \"%s\", AST: \"%s\", value=%s%n", testExpression, expression, expression.eval());
}
}
}
}

View file

@ -0,0 +1,235 @@
expressions = .array~of("2+3", "2+3/4", "2*3-4", "2*(3+4)+5/6", "2 * (3 + (4 * 5 + (6 * 7) * 8) - 9) * 10", "2*-3--4+-.25")
loop input over expressions
expression = createExpression(input)
if expression \= .nil then
say 'Expression "'input'" parses to "'expression~string'" and evaluates to "'expression~evaluate'"'
end
-- create an executable expression from the input, printing out any
-- errors if they are raised.
::routine createExpression
use arg inputString
-- signal on syntax
return .ExpressionParser~parseExpression(inputString)
syntax:
condition = condition('o')
say condition~errorText
say condition~message
return .nil
-- a base class for tree nodes in the tree
-- all nodes return some sort of value. This can be constant,
-- or the result of additional evaluations
::class evaluatornode
-- all evaluation is done here
::method evaluate abstract
-- node for numeric values in the tree
::class constant
::method init
expose value
use arg value
::method evaluate
expose value
return value
::method string
expose value
return value
-- node for a parenthetical group on the tree
::class parens
::method init
expose subexpression
use arg subexpression
::method evaluate
expose subexpression
return subexpression~evaluate
::method string
expose subexpression
return "("subexpression~string")"
-- base class for binary operators
::class binaryoperator
::method init
expose left right
-- the left and right sides are set after the left and right sides have
-- been resolved.
left = .nil
right = .nil
-- base operation
::method evaluate
expose left right
return self~operation(left~evaluate, right~evaluate)
-- the actual operation of the node
::method operation abstract
::method symbol abstract
::method precedence abstract
-- display an operator as a string value
::method string
expose left right
return '('left~string self~symbol right~string')'
::attribute left
::attribute right
::class addoperator subclass binaryoperator
::method operation
use arg left, right
return left + right
::method symbol
return "+"
::method precedence
return 1
::class subtractoperator subclass binaryoperator
::method operation
use arg left, right
return left - right
::method symbol
return "-"
::method precedence
return 1
::class multiplyoperator subclass binaryoperator
::method operation
use arg left, right
return left * right
::method symbol
return "*"
::method precedence
return 2
::class divideoperator subclass binaryoperator
::method operation
use arg left, right
return left / right
::method symbol
return "/"
::method precedence
return 2
-- a class to parse the expression and build an evaluation tree
::class expressionParser
-- create a resolved operand from an operator instance and the top
-- two entries on the operand stack.
::method createNewOperand class
use strict arg operator, operands
-- the operands are a stack, so they are in inverse order current
operator~right = operands~pull
operator~left = operands~pull
-- this goes on the top of the stack now
operands~push(operator)
::method parseExpression class
use strict arg inputString
-- stacks for managing the operands and pending operators
operands = .queue~new
operators = .queue~new
-- this flags what sort of item we expect to find at the current
-- location
afterOperand = .false
loop currentIndex = 1 to inputString~length
char = inputString~subChar(currentIndex)
-- skip over whitespace
if char == ' ' then iterate currentIndex
-- If the last thing we parsed was an operand, then
-- we expect to see either a closing paren or an
-- operator to appear here
if afterOperand then do
if char == ')' then do
loop while \operators~isempty
operator = operators~pull
-- if we find the opening paren, replace the
-- top operand with a paren group wrapper
-- and stop popping items
if operator == '(' then do
operands~push(.parens~new(operands~pull))
leave
end
-- collapse the operator stack a bit
self~createNewOperand(operator, operands)
end
-- done with this character
iterate currentIndex
end
afterOperand = .false
operator = .nil
if char == "+" then operator = .addoperator~new
else if char == "-" then operator = .subtractoperator~new
else if char == "*" then operator = .multiplyoperator~new
else if char == "/" then operator = .divideoperator~new
if operator \= .nil then do
loop while \operators~isEmpty
top = operators~peek
-- start of a paren group stops the popping
if top == '(' then leave
-- or the top operator has a lower precedence
if top~precedence < operator~precedence then leave
-- process this pending one
self~createNewOperand(operators~pull, operands)
end
-- this new operator is now top of the stack
operators~push(operator)
-- and back to the top
iterate currentIndex
end
raise syntax 98.900 array("Invalid expression character" char)
end
-- if we've hit an open paren, add this to the operator stack
-- as a phony operator
if char == '(' then do
operators~push('(')
iterate currentIndex
end
-- not an operator, so we have an operand of some type
afterOperand = .true
startindex = currentIndex
-- allow a leading minus sign on this
if inputString~subchar(currentIndex) == '-' then
currentIndex += 1
-- now scan for the end of numbers
loop while currentIndex <= inputString~length
-- exit for any non-numeric value
if \inputString~matchChar(currentIndex, "0123456789.") then leave
currentIndex += 1
end
-- extract the string value
operand = inputString~substr(startIndex, currentIndex - startIndex)
if \operand~datatype('Number') then
raise syntax 98.900 array("Invalid numeric operand '"operand"'")
-- back this up to the last valid character
currentIndex -= 1
-- add this to the operand stack as a tree element that returns a constant
operands~push(.constant~new(operand))
end
loop while \operators~isEmpty
operator = operators~pull
if operator == '(' then
raise syntax 98.900 array("Missing closing ')' in expression")
self~createNewOperand(operator, operands)
end
-- our entire expression should be the top of the expression tree
expression = operands~pull
if \operands~isEmpty then
raise syntax 98.900 array("Invalid expression")
return expression

View file

@ -12,7 +12,7 @@
[(~or "+" "-" "*" "/") (string->symbol lexeme)]
["(" 'OPEN]
[")" 'CLOSE]
[(~: (~+ numeric) (~? #\. (~* numeric)))
[(~: (~+ numeric) (~? (~: #\. (~* numeric))))
(token-NUM (string->number lexeme))]))
(define parse

View file

@ -0,0 +1,95 @@
(import (scheme base)
(scheme char)
(scheme cxr)
(scheme write)
(srfi 1 lists))
;; convert a string into a list of tokens
(define (string->tokens str)
(define (next-token chars)
(cond ((member (car chars) (list #\+ #\- #\* #\/) char=?)
(values (cdr chars)
(cdr (assq (car chars) ; convert char for op into op procedure, using a look up list
(list (cons #\+ +) (cons #\- -) (cons #\* *) (cons #\/ /))))))
((member (car chars) (list #\( #\)) char=?)
(values (cdr chars)
(if (char=? (car chars) #\()
'open
'close)))
(else ; read a multi-digit positive integer
(let loop ((rem chars)
(res 0))
(if (and (not (null? rem))
(char-numeric? (car rem)))
(loop (cdr rem)
(+ (* res 10)
(- (char->integer (car rem))
(char->integer #\0))))
(values rem
res))))))
;
(let loop ((chars (remove char-whitespace? (string->list str)))
(tokens '()))
(if (null? chars)
(reverse tokens)
(let-values (((remaining-chars token) (next-token chars)))
(loop remaining-chars
(cons token tokens))))))
;; turn list of tokens into an AST
;; -- using recursive descent parsing to obey laws of precedence
(define (parse tokens)
(define (parse-factor tokens)
(if (number? (car tokens))
(values (car tokens) (cdr tokens))
(let-values (((expr rem) (parse-expr (cdr tokens))))
(values expr (cdr rem)))))
(define (parse-term tokens)
(let-values (((left-expr rem) (parse-factor tokens)))
(if (and (not (null? rem))
(member (car rem) (list * /)))
(let-values (((right-expr remr) (parse-term (cdr rem))))
(values (list (car rem) left-expr right-expr)
remr))
(values left-expr rem))))
(define (parse-part tokens)
(let-values (((left-expr rem) (parse-term tokens)))
(if (and (not (null? rem))
(member (car rem) (list + -)))
(let-values (((right-expr remr) (parse-part (cdr rem))))
(values (list (car rem) left-expr right-expr)
remr))
(values left-expr rem))))
(define (parse-expr tokens)
(let-values (((expr rem) (parse-part tokens)))
(values expr rem)))
;
(let-values (((expr rem) (parse-expr tokens)))
(if (null? rem)
expr
(error "Misformed expression"))))
;; evaluate the AST, returning a number
(define (eval-expression ast)
(cond ((number? ast)
ast)
((member (car ast) (list + - * /))
((car ast)
(eval-expression (cadr ast))
(eval-expression (caddr ast))))
(else
(error "Misformed expression"))))
;; parse and evaluate the given string
(define (interpret str)
(eval-expression (parse (string->tokens str))))
;; running some examples
(for-each
(lambda (str)
(display
(string-append str
" => "
(number->string (interpret str))))
(newline))
'("1 + 2" "20+4*5" "1/2+5*(6-3)" "(1+3)/4-1" "(1 - 5) * 2 / (20 + 1)"))

View file

@ -3,54 +3,54 @@ func evalArithmeticExp(s) {
func evalExp(s) {
func operate(s, op) {
s.split(op).map{|c| c.to_num }.reduce(op);
s.split(op).map{|c| Number(c) }.reduce(op)
}
func add(s) {
operate(s.sub(/^\+/,'').sub(/\++/,'+'), '+');
operate(s.sub(/^\+/,'').sub(/\++/,'+'), '+')
}
func subtract(s) {
s.gsub!(/(\+-|-\+)/,'-');
s.gsub!(/(\+-|-\+)/,'-')
if (s ~~ /--/) {
return(add(s.sub(/--/,'+')));
return(add(s.sub(/--/,'+')))
}
var b = s.split('-');
b.len == 3 ? (-1*b[1].to_num - b[2].to_num)
: operate(s, '-');
var b = s.split('-')
b.len == 3 ? (-1*Number(b[1]) - Number(b[2]))
: operate(s, '-')
}
s.gsub!(/[()]/,'').gsub!(/-\+/, '-');
s.gsub!(/[()]/,'').gsub!(/-\+/, '-')
var reM = /\*/;
var reMD = %r"(\d+\.?\d*\s*[*/]\s*[+-]?\d+\.?\d*)";
var reM = /\*/
var reMD = %r"(\d+\.?\d*\s*[*/]\s*[+-]?\d+\.?\d*)"
var reA = /\d\+/;
var reAS = /(-?\d+\.?\d*\s*[+-]\s*[+-]?\d+\.?\d*)/;
var reA = /\d\+/
var reAS = /(-?\d+\.?\d*\s*[+-]\s*[+-]?\d+\.?\d*)/
while (var match = reMD.match(s)) {
match[0] ~~ reM
? s.sub!(reMD, operate(match[0], '*').to_s)
: s.sub!(reMD, operate(match[0], '/').to_s);
: s.sub!(reMD, operate(match[0], '/').to_s)
}
while (var match = reAS.match(s)) {
match[0] ~~ reA
? s.sub!(reAS, add(match[0]).to_s)
: s.sub!(reAS, subtract(match[0]).to_s);
: s.sub!(reAS, subtract(match[0]).to_s)
}
return s;
return s
}
var rePara = /(\([^\(\)]*\))/;
s.split!.join!('').sub!(/^\+/,'');
var rePara = /(\([^\(\)]*\))/
s.split!.join!('').sub!(/^\+/,'')
while (var match = s.match(rePara)) {
s.sub!(rePara, evalExp(match[0]));
s.sub!(rePara, evalExp(match[0]))
}
return evalExp(s).to_num;
return Number(evalExp(s))
}

View file

@ -7,7 +7,7 @@ for expr,res in [
['2*-3--4+-0.25' => -2.25],
['2 * (3 + (4 * 5 + (6 * 7) * 8) - 9) * 10' => 7000],
] { 
var num = evalArithmeticExp(expr);
assert_eq(num, res);
"%-45s == %10g\n".printf(expr, num);
var num = evalArithmeticExp(expr)
assert_eq(num, res)
"%-45s == %10g\n".printf(expr, num)
}

View file

@ -0,0 +1,2 @@
Compiler.Parser.parseText("(1+3)*7").dump();
Compiler.Parser.parseText("1+3*7").dump();

View file

@ -0,0 +1,2 @@
Compiler.Compiler.compileText("(1+3)*7").__constructor(); vm.regX;
Compiler.Compiler.compileText("1+3*7").__constructor(); vm.regX;