Data commit
This commit is contained in:
parent
7387c8f97b
commit
cb5bb5e222
199093 changed files with 3378972 additions and 0 deletions
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
|
||||
Loading…
Add table
Add a link
Reference in a new issue