September 2017 Update
This commit is contained in:
parent
bba7bfd280
commit
ba8067c3b7
14570 changed files with 153136 additions and 63871 deletions
|
|
@ -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".
|
||||
].
|
||||
].
|
||||
].
|
||||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
].
|
||||
].
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
235
Task/Arithmetic-evaluation/OoRexx/arithmetic-evaluation.rexx
Normal file
235
Task/Arithmetic-evaluation/OoRexx/arithmetic-evaluation.rexx
Normal 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
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
[(~or "+" "-" "*" "/") (string->symbol lexeme)]
|
||||
["(" 'OPEN]
|
||||
[")" 'CLOSE]
|
||||
[(~: (~+ numeric) (~? #\. (~* numeric)))
|
||||
[(~: (~+ numeric) (~? (~: #\. (~* numeric))))
|
||||
(token-NUM (string->number lexeme))]))
|
||||
|
||||
(define parse
|
||||
|
|
|
|||
95
Task/Arithmetic-evaluation/Scheme/arithmetic-evaluation.ss
Normal file
95
Task/Arithmetic-evaluation/Scheme/arithmetic-evaluation.ss
Normal 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)"))
|
||||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
Compiler.Parser.parseText("(1+3)*7").dump();
|
||||
Compiler.Parser.parseText("1+3*7").dump();
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
Compiler.Compiler.compileText("(1+3)*7").__constructor(); vm.regX;
|
||||
Compiler.Compiler.compileText("1+3*7").__constructor(); vm.regX;
|
||||
Loading…
Add table
Add a link
Reference in a new issue