September Morn Update
This commit is contained in:
parent
4e2d22a71d
commit
aac6731f2c
6856 changed files with 141342 additions and 21127 deletions
|
|
@ -1,324 +1,343 @@
|
|||
import system'routines.
|
||||
import extensions.
|
||||
import extensions'text.
|
||||
import system'routines;
|
||||
import extensions;
|
||||
import extensions'text;
|
||||
|
||||
class Token
|
||||
{
|
||||
object theValue.
|
||||
object theValue;
|
||||
|
||||
int rprop level :: theLevel.
|
||||
rprop int Level;
|
||||
|
||||
constructor new(int aLevel)
|
||||
[
|
||||
theValue := StringWriter new.
|
||||
theLevel := aLevel + 9.
|
||||
]
|
||||
constructor new(int level)
|
||||
{
|
||||
theValue := new StringWriter();
|
||||
Level := level + 9;
|
||||
}
|
||||
|
||||
append : aChar
|
||||
[
|
||||
theValue << aChar.
|
||||
]
|
||||
append(ch)
|
||||
{
|
||||
theValue.write(ch)
|
||||
}
|
||||
|
||||
number = theValue toReal.
|
||||
Number = theValue.toReal();
|
||||
}
|
||||
|
||||
class Node
|
||||
{
|
||||
object prop left :: theLeft.
|
||||
object prop right :: theRight.
|
||||
int rprop level :: theLevel.
|
||||
prop object Left;
|
||||
prop object Right;
|
||||
rprop int Level;
|
||||
|
||||
constructor new(int aLevel)
|
||||
[
|
||||
theLevel := aLevel.
|
||||
]
|
||||
constructor new(int level)
|
||||
{
|
||||
Level := level
|
||||
}
|
||||
}
|
||||
|
||||
class SummaryNode :: Node
|
||||
class SummaryNode : Node
|
||||
{
|
||||
constructor new(int aLevel)
|
||||
<= new(aLevel + 1).
|
||||
constructor new(int level)
|
||||
<= new(level + 1);
|
||||
|
||||
number = theLeft number + theRight number.
|
||||
Number = Left.Number + Right.Number;
|
||||
}
|
||||
|
||||
class DifferenceNode :: Node
|
||||
class DifferenceNode : Node
|
||||
{
|
||||
constructor new(int aLevel)
|
||||
<= new(aLevel + 1).
|
||||
constructor new(int level)
|
||||
<= new(level + 1);
|
||||
|
||||
number = theLeft number - theRight number.
|
||||
Number = Left.Number - Right.Number;
|
||||
}
|
||||
|
||||
class ProductNode :: Node
|
||||
class ProductNode : Node
|
||||
{
|
||||
constructor new(int aLevel)
|
||||
<= new(aLevel + 2).
|
||||
constructor new(int level)
|
||||
<= new(level + 2);
|
||||
|
||||
number = theLeft number * theRight number.
|
||||
Number = Left.Number * Right.Number;
|
||||
}
|
||||
|
||||
class FractionNode :: Node
|
||||
class FractionNode : Node
|
||||
{
|
||||
constructor new(int aLevel)
|
||||
<= new(aLevel + 2).
|
||||
constructor new(int level)
|
||||
<= new(level + 2);
|
||||
|
||||
number = theLeft number / theRight number.
|
||||
Number = Left.Number / Right.Number;
|
||||
}
|
||||
|
||||
class Expression
|
||||
{
|
||||
int rprop level :: theLevel.
|
||||
object prop top :: theTop.
|
||||
rprop int Level;
|
||||
prop object Top;
|
||||
|
||||
constructor new(int aLevel)
|
||||
[
|
||||
theLevel := aLevel
|
||||
]
|
||||
constructor new(int level)
|
||||
{
|
||||
Level := level
|
||||
}
|
||||
|
||||
right = theTop.
|
||||
prop object Right
|
||||
{
|
||||
get() = Top;
|
||||
|
||||
set right:aNode [ theTop := aNode ]
|
||||
set(object node)
|
||||
{
|
||||
Top := node
|
||||
}
|
||||
}
|
||||
|
||||
number => theTop.
|
||||
get Number() => Top;
|
||||
}
|
||||
|
||||
singleton operatorState
|
||||
{
|
||||
eval(ch)
|
||||
[
|
||||
{
|
||||
ch =>
|
||||
$40 [ // (
|
||||
^ target newBracket; gotoStarting
|
||||
];
|
||||
! [
|
||||
^ target newToken; append:ch; gotoToken
|
||||
].
|
||||
]
|
||||
$40 { // (
|
||||
^ __target.newBracket().gotoStarting()
|
||||
}
|
||||
: {
|
||||
^ __target.newToken().append(ch).gotoToken()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
singleton tokenState
|
||||
{
|
||||
eval(ch)
|
||||
[
|
||||
{
|
||||
ch =>
|
||||
$41 [ // )
|
||||
^ target closeBracket; gotoToken
|
||||
];
|
||||
$42 [ // *
|
||||
^ target newProduct; gotoOperator
|
||||
];
|
||||
$43 [ // +
|
||||
^ target newSummary; gotoOperator
|
||||
];
|
||||
$45 [ // -
|
||||
^ target newDifference; gotoOperator
|
||||
];
|
||||
$47 [ // /
|
||||
^ target newFraction; gotoOperator
|
||||
];
|
||||
! [
|
||||
^ target append:ch
|
||||
].
|
||||
]
|
||||
$41 { // )
|
||||
^ __target.closeBracket().gotoToken()
|
||||
}
|
||||
$42 { // *
|
||||
^ __target.newProduct().gotoOperator()
|
||||
}
|
||||
$43 { // +
|
||||
^ __target.newSummary().gotoOperator()
|
||||
}
|
||||
$45 { // -
|
||||
^ __target.newDifference().gotoOperator()
|
||||
}
|
||||
$47 { // /
|
||||
^ __target.newFraction().gotoOperator()
|
||||
}
|
||||
: {
|
||||
^ __target.append:ch
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
singleton startState
|
||||
{
|
||||
eval(ch)
|
||||
[
|
||||
{
|
||||
ch =>
|
||||
$40 [ // (
|
||||
^ target newBracket; gotoStarting
|
||||
];
|
||||
$45 [ // -
|
||||
^ target newToken; append:"0"; newDifference; gotoOperator
|
||||
];
|
||||
! [
|
||||
^ target newToken; append:ch; gotoToken
|
||||
].
|
||||
]
|
||||
$40 { // (
|
||||
^ __target.newBracket().gotoStarting()
|
||||
}
|
||||
$45 { // -
|
||||
^ __target.newToken().append("0").newDifference().gotoOperator()
|
||||
}
|
||||
: {
|
||||
^ __target.newToken().append:ch.gotoToken()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Scope
|
||||
{
|
||||
object theState.
|
||||
int theLevel.
|
||||
object theParser.
|
||||
object theToken.
|
||||
object theExpression.
|
||||
object theState;
|
||||
int theLevel;
|
||||
object theParser;
|
||||
object theToken;
|
||||
object theExpression;
|
||||
|
||||
constructor new:aParser
|
||||
[
|
||||
theState := startState.
|
||||
theLevel := 0.
|
||||
theExpression := Expression new(0).
|
||||
theParser := aParser.
|
||||
]
|
||||
constructor new(parser)
|
||||
{
|
||||
theState := startState;
|
||||
theLevel := 0;
|
||||
theExpression := Expression.new(0);
|
||||
theParser := parser
|
||||
}
|
||||
|
||||
newToken
|
||||
[
|
||||
theToken := theParser appendToken(theExpression, theLevel).
|
||||
]
|
||||
newToken()
|
||||
{
|
||||
theToken := theParser.appendToken(theExpression, theLevel)
|
||||
}
|
||||
|
||||
newSummary
|
||||
[
|
||||
theToken := nil.
|
||||
newSummary()
|
||||
{
|
||||
theToken := nil;
|
||||
|
||||
theParser appendSummary(theExpression, theLevel).
|
||||
]
|
||||
theParser.appendSummary(theExpression, theLevel)
|
||||
}
|
||||
|
||||
newDifference
|
||||
[
|
||||
theToken := nil.
|
||||
newDifference()
|
||||
{
|
||||
theToken := nil;
|
||||
|
||||
theParser appendDifference(theExpression, theLevel)
|
||||
]
|
||||
theParser.appendDifference(theExpression, theLevel)
|
||||
}
|
||||
|
||||
newProduct
|
||||
[
|
||||
theToken := nil.
|
||||
newProduct()
|
||||
{
|
||||
theToken := nil;
|
||||
|
||||
theParser appendProduct(theExpression, theLevel)
|
||||
]
|
||||
theParser.appendProduct(theExpression, theLevel)
|
||||
}
|
||||
|
||||
newFraction
|
||||
[
|
||||
theToken := nil.
|
||||
newFraction()
|
||||
{
|
||||
theToken := nil;
|
||||
|
||||
theParser appendFraction(theExpression, theLevel)
|
||||
]
|
||||
theParser.appendFraction(theExpression, theLevel)
|
||||
}
|
||||
|
||||
newBracket
|
||||
[
|
||||
theToken := nil.
|
||||
newBracket()
|
||||
{
|
||||
theToken := nil;
|
||||
|
||||
theLevel := theLevel + 10.
|
||||
theLevel := theLevel + 10;
|
||||
|
||||
theParser appendSubexpression(theExpression, theLevel).
|
||||
]
|
||||
theParser.appendSubexpression(theExpression, theLevel)
|
||||
}
|
||||
|
||||
closeBracket
|
||||
[
|
||||
closeBracket()
|
||||
{
|
||||
if (theLevel < 10)
|
||||
[ InvalidArgumentException new:"Invalid expression"; raise ].
|
||||
{ InvalidArgumentException.new:"Invalid expression".raise() };
|
||||
|
||||
theLevel := theLevel - 10
|
||||
]
|
||||
}
|
||||
|
||||
append:ch
|
||||
[
|
||||
if((ch >= $48) && (ch < $58))
|
||||
[ theToken append:ch ];
|
||||
[ InvalidArgumentException new:"Invalid expression"; raise ]
|
||||
]
|
||||
append(ch)
|
||||
{
|
||||
if(ch >= $48 && ch < $58)
|
||||
{
|
||||
theToken.append:ch
|
||||
}
|
||||
else
|
||||
{
|
||||
InvalidArgumentException.new:"Invalid expression".raise()
|
||||
}
|
||||
}
|
||||
|
||||
append(literal aLiteral)
|
||||
[
|
||||
aLiteral forEach(:ch)[ self append:ch ]
|
||||
]
|
||||
append(string s)
|
||||
{
|
||||
s.forEach:(ch){ self.append:ch }
|
||||
}
|
||||
|
||||
gotoStarting
|
||||
[
|
||||
gotoStarting()
|
||||
{
|
||||
theState := startState
|
||||
]
|
||||
}
|
||||
|
||||
gotoToken
|
||||
[
|
||||
gotoToken()
|
||||
{
|
||||
theState := tokenState
|
||||
]
|
||||
}
|
||||
|
||||
gotoOperator
|
||||
[
|
||||
gotoOperator()
|
||||
{
|
||||
theState := operatorState
|
||||
]
|
||||
}
|
||||
|
||||
number => theExpression.
|
||||
get Number() => theExpression;
|
||||
|
||||
dispatch => theState.
|
||||
dispatch() => theState;
|
||||
}
|
||||
|
||||
class Parser
|
||||
{
|
||||
appendToken(object anExpression, int aLevel)
|
||||
[
|
||||
var aToken := Token new(aLevel).
|
||||
appendToken(object expression, int level)
|
||||
{
|
||||
var token := Token.new(level);
|
||||
|
||||
anExpression top := self append(anExpression top, aToken).
|
||||
expression.Top := self.append(expression.Top, token);
|
||||
|
||||
^ aToken
|
||||
]
|
||||
^ token
|
||||
}
|
||||
|
||||
appendSummary(object anExpression, int aLevel)
|
||||
[
|
||||
anExpression top := self append(anExpression top, SummaryNode new(aLevel)).
|
||||
]
|
||||
appendSummary(object expression, int level)
|
||||
{
|
||||
expression.Top := self.append(expression.Top, SummaryNode.new(level))
|
||||
}
|
||||
|
||||
appendDifference(object anExpression, int aLevel)
|
||||
[
|
||||
anExpression top := self append(anExpression top, DifferenceNode new(aLevel)).
|
||||
]
|
||||
appendDifference(object expression, int level)
|
||||
{
|
||||
expression.Top := self.append(expression.Top, DifferenceNode.new(level))
|
||||
}
|
||||
|
||||
appendProduct(object anExpression, int aLevel)
|
||||
[
|
||||
anExpression top := self append(anExpression top, ProductNode new(aLevel)).
|
||||
]
|
||||
appendProduct(object expression, int level)
|
||||
{
|
||||
expression.Top := self.append(expression.Top, ProductNode.new(level))
|
||||
}
|
||||
|
||||
appendFraction(object anExpression, int aLevel)
|
||||
[
|
||||
anExpression top := self append(anExpression top, FractionNode new(aLevel))
|
||||
]
|
||||
appendFraction(object expression, int level)
|
||||
{
|
||||
expression.Top := self.append(expression.Top, FractionNode.new(level))
|
||||
}
|
||||
|
||||
appendSubexpression(object anExpression, int aLevel)
|
||||
[
|
||||
anExpression top := self append(anExpression top, Expression new(aLevel)).
|
||||
]
|
||||
appendSubexpression(object expression, int level)
|
||||
{
|
||||
expression.Top := self.append(expression.Top, Expression.new(level))
|
||||
}
|
||||
|
||||
append(object aLastNode, object aNewNode)
|
||||
[
|
||||
if(nil == aLastNode)
|
||||
[ ^ aNewNode ].
|
||||
append(object lastNode, object newNode)
|
||||
{
|
||||
if(nil == lastNode)
|
||||
{ ^ newNode };
|
||||
|
||||
if (aNewNode level <= aLastNode level)
|
||||
[ aNewNode left := aLastNode. ^ aNewNode ].
|
||||
if (newNode.Level <= lastNode.Level)
|
||||
{ newNode.Left := lastNode; ^ newNode };
|
||||
|
||||
var aParent := aLastNode.
|
||||
var aCurrent := aLastNode right.
|
||||
while ((nil != aCurrent) && (aNewNode level > aCurrent level))
|
||||
[ aParent := aCurrent. aCurrent := aCurrent right. ].
|
||||
var parent := lastNode;
|
||||
var current := lastNode.Right;
|
||||
while (nil != current && newNode.Level > current.Level)
|
||||
{ parent := current; current := current.Right };
|
||||
|
||||
if (nil == aCurrent)
|
||||
[ aParent right := aNewNode. ];
|
||||
[ aNewNode left := aCurrent. aParent right := aNewNode ].
|
||||
if (nil == current)
|
||||
{
|
||||
parent.Right := newNode
|
||||
}
|
||||
else
|
||||
{
|
||||
newNode.Left := current; parent.Right := newNode
|
||||
};
|
||||
|
||||
^ aLastNode
|
||||
]
|
||||
^ lastNode
|
||||
}
|
||||
|
||||
run : aText
|
||||
[
|
||||
var aScope := Scope new(self).
|
||||
run(text)
|
||||
{
|
||||
var scope := Scope.new(self);
|
||||
|
||||
aText forEach(:ch)[ aScope eval:ch ].
|
||||
text.forEach:(ch){ scope.eval:ch };
|
||||
|
||||
^ aScope number
|
||||
]
|
||||
^ scope.Number
|
||||
}
|
||||
}
|
||||
|
||||
public program
|
||||
[
|
||||
var aText := StringWriter new.
|
||||
var aParser := Parser new.
|
||||
public program()
|
||||
{
|
||||
var text := new StringWriter();
|
||||
var parser := new Parser();
|
||||
|
||||
$(console readLine; saveTo:aText; length > 0) doWhile:
|
||||
[
|
||||
try(console printLine("=",aParser run:aText))
|
||||
while (console.readLine().saveTo(text).Length > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
on(Exception e)
|
||||
[
|
||||
console writeLine:"Invalid Expression"
|
||||
]
|
||||
}.
|
||||
console.printLine("=",parser.run:text)
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
console.writeLine(e.Printable)
|
||||
|
||||
aText clear
|
||||
]
|
||||
]
|
||||
//console.writeLine:"Invalid Expression"
|
||||
};
|
||||
|
||||
text.clear()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
151
Task/Arithmetic-evaluation/Nim/arithmetic-evaluation.nim
Normal file
151
Task/Arithmetic-evaluation/Nim/arithmetic-evaluation.nim
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
import strutils
|
||||
import os
|
||||
|
||||
#--
|
||||
# Lexer
|
||||
#--
|
||||
|
||||
type
|
||||
TokenKind = enum
|
||||
tokNumber
|
||||
tokPlus = "+", tokMinus = "-", tokStar = "*", tokSlash = "/"
|
||||
tokLPar, tokRPar
|
||||
tokEnd
|
||||
Token = object
|
||||
case kind: TokenKind
|
||||
of tokNumber: value: float
|
||||
else: discard
|
||||
|
||||
proc lex(input: string): seq[Token] =
|
||||
# Here we go through the entire input string and collect all the tokens into
|
||||
# a sequence.
|
||||
var pos = 0
|
||||
while pos < input.len:
|
||||
case input[pos]
|
||||
of '0'..'9':
|
||||
# Digits consist of three parts: the integer part, the delimiting decimal
|
||||
# point, and the decimal part.
|
||||
var numStr = ""
|
||||
while pos < input.len and input[pos] in Digits:
|
||||
numStr.add(input[pos])
|
||||
inc(pos)
|
||||
if pos < input.len and input[pos] == '.':
|
||||
numStr.add('.')
|
||||
inc(pos)
|
||||
while pos < input.len and input[pos] in Digits:
|
||||
numStr.add(input[pos])
|
||||
inc(pos)
|
||||
result.add(Token(kind: tokNumber, value: numStr.parseFloat()))
|
||||
of '+': inc(pos); result.add(Token(kind: tokPlus))
|
||||
of '-': inc(pos); result.add(Token(kind: tokMinus))
|
||||
of '*': inc(pos); result.add(Token(kind: tokStar))
|
||||
of '/': inc(pos); result.add(Token(kind: tokSlash))
|
||||
of '(': inc(pos); result.add(Token(kind: tokLPar))
|
||||
of ')': inc(pos); result.add(Token(kind: tokRPar))
|
||||
of ' ': inc(pos)
|
||||
else: raise newException(ArithmeticError,
|
||||
"Unexpected character '" & input[pos] & '\'')
|
||||
# We append an 'end' token to the end of our token sequence, to mark where the
|
||||
# sequence ends.
|
||||
result.add(Token(kind: tokEnd))
|
||||
|
||||
#--
|
||||
# Parser
|
||||
#--
|
||||
|
||||
type
|
||||
ExprKind = enum
|
||||
exprNumber
|
||||
exprBinary
|
||||
Expr = ref object
|
||||
case kind: ExprKind
|
||||
of exprNumber: value: float
|
||||
of exprBinary:
|
||||
left, right: Expr
|
||||
operator: TokenKind
|
||||
|
||||
proc `$`(ex: Expr): string =
|
||||
# This proc returns a lisp representation of the expression.
|
||||
case ex.kind
|
||||
of exprNumber: $ex.value
|
||||
of exprBinary: '(' & $ex.operator & ' ' & $ex.left & ' ' & $ex.right & ')'
|
||||
|
||||
var
|
||||
# The input to the program is provided via command line parameters.
|
||||
tokens = lex(commandLineParams().join(" "))
|
||||
pos = 0
|
||||
|
||||
# This table stores the precedence level of each infix operator. For tokens
|
||||
# this does not apply to, the precedence is set to 0.
|
||||
const Precedence: array[low(TokenKind)..high(TokenKind), int] = [
|
||||
tokNumber: 0,
|
||||
tokPlus: 1,
|
||||
tokMinus: 1,
|
||||
tokStar: 2,
|
||||
tokSlash: 2,
|
||||
tokLPar: 0,
|
||||
tokRPar: 0,
|
||||
tokEnd: 0
|
||||
]
|
||||
|
||||
# We use a Pratt parser, so the two primary components are the prefix part, and
|
||||
# the infix part. We start with a prefix token, and when we're done, we continue
|
||||
# with an infix token.
|
||||
|
||||
proc parse(prec = 0): Expr
|
||||
|
||||
proc parseNumber(token: Token): Expr =
|
||||
result = Expr(kind: exprNumber, value: token.value)
|
||||
|
||||
proc parseParen(token: Token): Expr =
|
||||
result = parse()
|
||||
if tokens[pos].kind != tokRPar:
|
||||
raise newException(ArithmeticError, "Unbalanced parenthesis")
|
||||
inc(pos)
|
||||
|
||||
proc parseBinary(left: Expr, token: Token): Expr =
|
||||
result = Expr(kind: exprBinary, left: left, right: parse(),
|
||||
operator: token.kind)
|
||||
|
||||
proc parsePrefix(token: Token): Expr =
|
||||
case token.kind
|
||||
of tokNumber: result = parseNumber(token)
|
||||
of tokLPar: result = parseParen(token)
|
||||
else: discard
|
||||
|
||||
proc parseInfix(left: Expr, token: Token): Expr =
|
||||
case token.kind
|
||||
of tokPlus, tokMinus, tokStar, tokSlash: result = parseBinary(left, token)
|
||||
else: discard
|
||||
|
||||
proc parse(prec = 0): Expr =
|
||||
# This procedure is the heart of a Pratt parser, it puts the whole expression
|
||||
# together into one abstract syntax tree, properly dealing with precedence.
|
||||
var token = tokens[pos]
|
||||
inc(pos)
|
||||
result = parsePrefix(token)
|
||||
while prec < Precedence[tokens[pos].kind]:
|
||||
token = tokens[pos]
|
||||
if token.kind == tokEnd:
|
||||
# When we hit the end token, we're done.
|
||||
break
|
||||
inc(pos)
|
||||
result = parseInfix(result, token)
|
||||
|
||||
let ast = parse()
|
||||
|
||||
proc `==`(ex: Expr): float =
|
||||
# This proc recursively evaluates the given expression.
|
||||
result =
|
||||
case ex.kind
|
||||
of exprNumber: ex.value
|
||||
of exprBinary:
|
||||
case ex.operator
|
||||
of tokPlus: ==ex.left + ==ex.right
|
||||
of tokMinus: ==ex.left - ==ex.right
|
||||
of tokStar: ==ex.left * ==ex.right
|
||||
of tokSlash: ==ex.left / ==ex.right
|
||||
else: 0.0
|
||||
|
||||
# In the end, we print out the result.
|
||||
echo ==ast
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
#lang racket
|
||||
|
||||
(require parser-tools/yacc parser-tools/lex
|
||||
(require parser-tools/yacc
|
||||
parser-tools/lex
|
||||
(prefix-in ~ parser-tools/lex-sre))
|
||||
|
||||
(define-tokens value-tokens (NUM))
|
||||
|
|
@ -30,6 +31,6 @@
|
|||
|
||||
(define (calc str)
|
||||
(define i (open-input-string str))
|
||||
(displayln (parse (λ() (lex i)))))
|
||||
(displayln (parse (λ () (lex i)))))
|
||||
|
||||
(calc "(1 + 2 * 3) - (1+2)*-3")
|
||||
|
|
|
|||
172
Task/Arithmetic-evaluation/Rust/arithmetic-evaluation.rust
Normal file
172
Task/Arithmetic-evaluation/Rust/arithmetic-evaluation.rust
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
//! Simple calculator parser and evaluator
|
||||
|
||||
|
||||
/// Binary operator
|
||||
#[derive(Debug)]
|
||||
pub enum Operator {
|
||||
Add,
|
||||
Substract,
|
||||
Multiply,
|
||||
Divide
|
||||
}
|
||||
|
||||
/// A node in the tree
|
||||
#[derive(Debug)]
|
||||
pub enum Node {
|
||||
Value(f64),
|
||||
SubNode(Box<Node>),
|
||||
Binary(Operator, Box<Node>,Box<Node>),
|
||||
}
|
||||
|
||||
/// parse a string into a node
|
||||
pub fn parse(txt :&str) -> Option<Node> {
|
||||
let chars = txt.chars().filter(|c| *c != ' ').collect();
|
||||
parse_expression(&chars, 0).map(|(_,n)| n)
|
||||
}
|
||||
|
||||
/// parse an expression into a node, keeping track of the position in the character vector
|
||||
fn parse_expression(chars: &Vec<char>, pos: usize) -> Option<(usize,Node)> {
|
||||
match parse_start(chars, pos) {
|
||||
Some((new_pos, first)) => {
|
||||
match parse_operator(chars, new_pos) {
|
||||
Some((new_pos2,op)) => {
|
||||
if let Some((new_pos3, second)) = parse_expression(chars, new_pos2) {
|
||||
Some((new_pos3, combine(op, first, second)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
None => Some((new_pos,first)),
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// combine nodes to respect associativity rules
|
||||
fn combine(op: Operator, first: Node, second: Node) -> Node {
|
||||
match second {
|
||||
Node::Binary(op2,v21,v22) => if precedence(&op)>=precedence(&op2) {
|
||||
Node::Binary(op2,Box::new(combine(op,first,*v21)),v22)
|
||||
} else {
|
||||
Node::Binary(op,Box::new(first),Box::new(Node::Binary(op2,v21,v22)))
|
||||
},
|
||||
_ => Node::Binary(op,Box::new(first),Box::new(second)),
|
||||
}
|
||||
}
|
||||
|
||||
/// a precedence rank for operators
|
||||
fn precedence(op: &Operator) -> usize {
|
||||
match op{
|
||||
Operator::Multiply | Operator::Divide => 2,
|
||||
_ => 1
|
||||
}
|
||||
}
|
||||
|
||||
/// try to parse from the start of an expression (either a parenthesis or a value)
|
||||
fn parse_start(chars: &Vec<char>, pos: usize) -> Option<(usize,Node)> {
|
||||
match start_parenthesis(chars, pos){
|
||||
Some (new_pos) => {
|
||||
let r = parse_expression(chars, new_pos);
|
||||
end_parenthesis(chars, r)
|
||||
},
|
||||
None => parse_value(chars, pos),
|
||||
}
|
||||
}
|
||||
|
||||
/// match a starting parentheseis
|
||||
fn start_parenthesis(chars: &Vec<char>, pos: usize) -> Option<usize>{
|
||||
if pos<chars.len() && chars[pos] == '(' {
|
||||
Some(pos+1)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// match an end parenthesis, if successful will create a sub node contained the wrapped expression
|
||||
fn end_parenthesis(chars: &Vec<char>, wrapped :Option<(usize,Node)>) -> Option<(usize,Node)>{
|
||||
match wrapped {
|
||||
Some((pos, node)) => if pos<chars.len() && chars[pos] == ')' {
|
||||
Some((pos+1,Node::SubNode(Box::new(node))))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// parse a value: an decimal with an optional minus sign
|
||||
fn parse_value(chars: &Vec<char>, pos: usize) -> Option<(usize,Node)>{
|
||||
let mut new_pos = pos;
|
||||
if new_pos<chars.len() && chars[new_pos] == '-' {
|
||||
new_pos = new_pos+1;
|
||||
}
|
||||
while new_pos<chars.len() && (chars[new_pos]=='.' || (chars[new_pos] >= '0' && chars[new_pos] <= '9')) {
|
||||
new_pos = new_pos+1;
|
||||
}
|
||||
if new_pos>pos {
|
||||
if let Ok(v) = dbg!(chars[pos..new_pos].iter().collect::<String>()).parse() {
|
||||
Some((new_pos,Node::Value(v)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// parse an operator
|
||||
fn parse_operator(chars: &Vec<char>, pos: usize) -> Option<(usize,Operator)> {
|
||||
if pos<chars.len() {
|
||||
let ops_with_char = vec!(('+',Operator::Add),('-',Operator::Substract),('*',Operator::Multiply),('/',Operator::Divide));
|
||||
for (ch,op) in ops_with_char {
|
||||
if chars[pos] == ch {
|
||||
return Some((pos+1, op));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// eval a string
|
||||
pub fn eval(txt :&str) -> f64 {
|
||||
match parse(txt) {
|
||||
Some(t) => eval_term(&t),
|
||||
None => panic!("Cannot parse {}",txt),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// eval a term, recursively
|
||||
fn eval_term(t: &Node) -> f64 {
|
||||
match t {
|
||||
Node::Value(v) => *v,
|
||||
Node::SubNode(t) => eval_term(t),
|
||||
Node::Binary(Operator::Add,t1,t2) => eval_term(t1) + eval_term(t2),
|
||||
Node::Binary(Operator::Substract,t1,t2) => eval_term(t1) - eval_term(t2),
|
||||
Node::Binary(Operator::Multiply,t1,t2) => eval_term(t1) * eval_term(t2),
|
||||
Node::Binary(Operator::Divide,t1,t2) => eval_term(t1) / eval_term(t2),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_eval(){
|
||||
assert_eq!(2.0,eval("2"));
|
||||
assert_eq!(4.0,eval("2+2"));
|
||||
assert_eq!(11.0/4.0, eval("2+3/4"));
|
||||
assert_eq!(2.0, eval("2*3-4"));
|
||||
assert_eq!(3.0, eval("1+2*3-4"));
|
||||
assert_eq!(89.0/6.0, eval("2*(3+4)+5/6"));
|
||||
assert_eq!(14.0, eval("2 * (3 -1) + 2 * 5"));
|
||||
assert_eq!(7000.0, eval("2 * (3 + (4 * 5 + (6 * 7) * 8) - 9) * 10"));
|
||||
assert_eq!(-9.0/4.0, eval("2*-3--4+-.25"));
|
||||
assert_eq!(1.5, eval("1 - 5 * 2 / 20 + 1"));
|
||||
assert_eq!(3.5, eval("2 * (3 + ((5) / (7 - 11)))"));
|
||||
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue