June 2018 Update
This commit is contained in:
parent
ba8067c3b7
commit
22f33d4004
5278 changed files with 84726 additions and 14379 deletions
|
|
@ -0,0 +1,130 @@
|
|||
\ Convert infix expression to postfix, using 'shunting-yard' algorithm
|
||||
\ https://en.wikipedia.org/wiki/Shunting-yard_algorithm
|
||||
|
||||
|
||||
\ precedence of infix tokens. negative means 'right-associative', otherwise left:
|
||||
with: n
|
||||
{
|
||||
"+" : 2,
|
||||
"-" : 2,
|
||||
"/" : 3,
|
||||
"*" : 3,
|
||||
"^" : -4,
|
||||
"(" : 1,
|
||||
")" : -1
|
||||
} var, tokens
|
||||
|
||||
: precedence \ s -- s n
|
||||
tokens @ over m:@ nip
|
||||
null? if drop 0 then ;
|
||||
|
||||
var ops
|
||||
var out
|
||||
|
||||
: >out \ x --
|
||||
out @ swap
|
||||
a:push drop ;
|
||||
|
||||
: >ops \ op prec --
|
||||
2 a:close
|
||||
ops @ swap
|
||||
a:push drop ;
|
||||
|
||||
: a:peek -1 a:@ ;
|
||||
|
||||
\ Check the array for items with greater or equal precedence,
|
||||
\ and move them to the out queue:
|
||||
: pop-ops \ op prec ops -- op prec ops
|
||||
\ empty array, do nothing:
|
||||
a:len not if ;; then
|
||||
|
||||
\ Look at top of ops stack:
|
||||
a:peek a:open \ op p ops[] op2 p2
|
||||
|
||||
\ if the 'p2' is not less p (meaning item on top of stack is greater or equal
|
||||
\ in precedence), then pop the item from the ops stack and push onto the out:
|
||||
3 pick \ p2 p
|
||||
< not if
|
||||
\ op p ops[] op2
|
||||
>out a:pop drop recurse ;;
|
||||
then
|
||||
drop ;
|
||||
|
||||
|
||||
: right-paren
|
||||
"RIGHTPAREN" . cr
|
||||
2drop
|
||||
\ move non-left-paren from ops and move to out:
|
||||
ops @
|
||||
repeat
|
||||
a:len not if
|
||||
break
|
||||
else
|
||||
a:pop a:open
|
||||
1 = if
|
||||
2drop ;;
|
||||
else
|
||||
>out
|
||||
then
|
||||
then
|
||||
again drop ;
|
||||
|
||||
: .state \ n --
|
||||
drop \ "Token: %s\n" s:strfmt .
|
||||
"Out: " .
|
||||
out @ ( . space drop ) a:each drop cr
|
||||
"ops: " . ops @ ( 0 a:@ . space 2drop ) a:each drop cr cr ;
|
||||
|
||||
: handle-number \ s n --
|
||||
"NUMBER " . over . cr
|
||||
drop >out ;
|
||||
|
||||
: left-paren \ s n --
|
||||
"LEFTPAREN" . cr
|
||||
>ops ;
|
||||
|
||||
: handle-op \ s n --
|
||||
"OPERATOR " . over . cr
|
||||
\ op precedence
|
||||
\ Is the current op left-associative?
|
||||
dup sgn 1 = if
|
||||
\ it is, so check the ops array for items with greater or equal precedence,
|
||||
\ and move them to the out queue:
|
||||
ops @ pop-ops drop
|
||||
then
|
||||
\ push the operator
|
||||
>ops ;
|
||||
|
||||
: handle-token \ s --
|
||||
precedence dup not if
|
||||
\ it's a number:
|
||||
handle-number
|
||||
else
|
||||
dup 1 = if left-paren
|
||||
else dup -1 = if right-paren
|
||||
else handle-op
|
||||
then then
|
||||
then ;
|
||||
|
||||
: infix>postfix \ s -- s
|
||||
/\s+/ s:/ \ split to indiviual whitespace-delimited tokens
|
||||
|
||||
\ Initialize our data structures
|
||||
a:new ops ! a:new out !
|
||||
|
||||
(
|
||||
nip dup >r
|
||||
handle-token
|
||||
r> .state
|
||||
) a:each drop
|
||||
\ remove all remaining ops and put on output:
|
||||
out @
|
||||
ops @ a:rev
|
||||
( nip a:open drop a:push ) a:each drop
|
||||
\ final string:
|
||||
" " a:join ;
|
||||
|
||||
"3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3" infix>postfix . cr
|
||||
|
||||
"Expected: \n" . "3 4 2 * 1 5 - 2 3 ^ ^ / +" . cr
|
||||
bye
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
import std.container;
|
||||
import std.stdio;
|
||||
|
||||
void main() {
|
||||
string infix = "3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3";
|
||||
writeln("infix: ", infix);
|
||||
writeln("postfix: ", infixToPostfix(infix));
|
||||
}
|
||||
|
||||
string infixToPostfix(string infix) {
|
||||
import std.array;
|
||||
|
||||
/* To find out the precedence, we take the index of the
|
||||
token in the ops string and divide by 2 (rounding down).
|
||||
This will give us: 0, 0, 1, 1, 2 */
|
||||
immutable ops = ["+", "-", "/", "*", "^"];
|
||||
|
||||
auto sb = appender!string;
|
||||
SList!int stack;
|
||||
|
||||
// split the input on whitespace
|
||||
foreach (token; infix.split) {
|
||||
if (token.empty) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int idx = ops.indexOf(token);
|
||||
|
||||
// check for operator
|
||||
if (idx != -1) {
|
||||
while (!stack.empty) {
|
||||
int prec2 = stack.peek / 2;
|
||||
int prec1 = idx / 2;
|
||||
if (prec2 > prec1 || (prec2 == prec1 && token != "^")) {
|
||||
sb.put(ops[stack.pop]);
|
||||
sb.put(' ');
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
stack.push(idx);
|
||||
} else if (token == "(") {
|
||||
stack.push(-2); // -2 stands for '('
|
||||
} else if (token == ")") {
|
||||
// until '(' on stack, pop operators.
|
||||
while (stack.peek != -2) {
|
||||
sb.put(ops[stack.pop]);
|
||||
sb.put(' ');
|
||||
}
|
||||
stack.pop();
|
||||
} else {
|
||||
sb.put(token);
|
||||
sb.put(' ');
|
||||
}
|
||||
}
|
||||
|
||||
while (!stack.empty) {
|
||||
sb.put(ops[stack.pop]);
|
||||
sb.put(' ');
|
||||
}
|
||||
|
||||
return sb.data;
|
||||
}
|
||||
|
||||
// Find the first index of the specified value, or -1 if not found.
|
||||
int indexOf(T)(const T[] a, const T v) {
|
||||
foreach(i,e; a) {
|
||||
if (e == v) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Convienience for adding a new element
|
||||
void push(T)(ref SList!T s, T v) {
|
||||
s.insertFront(v);
|
||||
}
|
||||
|
||||
// Convienience for accessing the top element
|
||||
auto peek(T)(SList!T s) {
|
||||
return s.front;
|
||||
}
|
||||
|
||||
// Convienience for removing and returning the top element
|
||||
auto pop(T)(ref SList!T s) {
|
||||
auto v = s.front;
|
||||
s.removeFront;
|
||||
return v;
|
||||
}
|
||||
|
|
@ -65,7 +65,5 @@ for (var i = 0; i < infix.length; i++) {
|
|||
s.pop(); // pop (, but not onto the output queue
|
||||
}
|
||||
}
|
||||
while (s.length()>0){
|
||||
postfix += s.pop() + " ";
|
||||
}
|
||||
postfix += s.dataStore.reverse().join(" ");
|
||||
print(postfix);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
function parseinfix2rpn(s)
|
||||
outputq = []
|
||||
opstack = []
|
||||
infix = split(s)
|
||||
for tok in infix
|
||||
if all(isnumber, tok)
|
||||
push!(outputq, tok)
|
||||
elseif tok == "("
|
||||
push!(opstack, tok)
|
||||
elseif tok == ")"
|
||||
while !isempty(opstack) && (op = pop!(opstack)) != "("
|
||||
push!(outputq, op)
|
||||
end
|
||||
else # operator
|
||||
while !isempty(opstack)
|
||||
op = pop!(opstack)
|
||||
if Base.operator_precedence(Symbol(op)) > Base.operator_precedence(Symbol(tok)) ||
|
||||
(Base.operator_precedence(Symbol(op)) ==
|
||||
Base.operator_precedence(Symbol(tok)) && op != "^")
|
||||
push!(outputq, op)
|
||||
else
|
||||
push!(opstack, op) # undo peek
|
||||
break
|
||||
end
|
||||
end
|
||||
push!(opstack, tok)
|
||||
end
|
||||
println("The working output stack is $outputq")
|
||||
end
|
||||
while !isempty(opstack)
|
||||
if (op = pop!(opstack)) == "("
|
||||
throw("mismatched parentheses")
|
||||
else
|
||||
push!(outputq, op)
|
||||
end
|
||||
end
|
||||
outputq
|
||||
end
|
||||
|
||||
teststring = "3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3"
|
||||
println("\nResult: $teststring becomes $(join(parseinfix2rpn(teststring), ' '))")
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
// version 1.2.0
|
||||
|
||||
import java.util.Stack
|
||||
|
||||
/* To find out the precedence, we take the index of the
|
||||
token in the OPS string and divide by 2 (rounding down).
|
||||
This will give us: 0, 0, 1, 1, 2 */
|
||||
const val OPS = "-+/*^"
|
||||
|
||||
fun infixToPostfix(infix: String): String {
|
||||
val sb = StringBuilder()
|
||||
val s = Stack<Int>()
|
||||
val rx = Regex("""\s""")
|
||||
for (token in infix.split(rx)) {
|
||||
if (token.isEmpty()) continue
|
||||
val c = token[0]
|
||||
val idx = OPS.indexOf(c)
|
||||
|
||||
// check for operator
|
||||
if (idx != - 1) {
|
||||
if (s.isEmpty()) {
|
||||
s.push(idx)
|
||||
}
|
||||
else {
|
||||
while (!s.isEmpty()) {
|
||||
val prec2 = s.peek() / 2
|
||||
val prec1 = idx / 2
|
||||
if (prec2 > prec1 || (prec2 == prec1 && c != '^')) {
|
||||
sb.append(OPS[s.pop()]).append(' ')
|
||||
}
|
||||
else break
|
||||
}
|
||||
s.push(idx)
|
||||
}
|
||||
}
|
||||
else if (c == '(') {
|
||||
s.push(-2) // -2 stands for '('
|
||||
}
|
||||
else if (c == ')') {
|
||||
// until '(' on stack, pop operators.
|
||||
while (s.peek() != -2) sb.append(OPS[s.pop()]).append(' ')
|
||||
s.pop()
|
||||
}
|
||||
else {
|
||||
sb.append(token).append(' ')
|
||||
}
|
||||
}
|
||||
while (!s.isEmpty()) sb.append(OPS[s.pop()]).append(' ')
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val es = listOf(
|
||||
"3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3",
|
||||
"( ( 1 + 2 ) ^ ( 3 + 4 ) ) ^ ( 5 + 6 )"
|
||||
)
|
||||
for (e in es) {
|
||||
println("Infix : $e")
|
||||
println("Postfix : ${infixToPostfix(e)}\n")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
type Number = f64;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
struct Operator {
|
||||
token: char,
|
||||
operation: fn(Number, Number) -> Number,
|
||||
precedence: u8,
|
||||
is_left_associative: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
enum Token {
|
||||
Digit(Number),
|
||||
Operator(Operator),
|
||||
LeftParen,
|
||||
RightParen,
|
||||
}
|
||||
|
||||
impl Operator {
|
||||
fn new_token(
|
||||
token: char,
|
||||
precedence: u8,
|
||||
is_left_associative: bool,
|
||||
operation: fn(Number, Number) -> Number,
|
||||
) -> Token {
|
||||
Token::Operator(Operator {
|
||||
token: token,
|
||||
operation: operation,
|
||||
precedence: precedence,
|
||||
is_left_associative,
|
||||
})
|
||||
}
|
||||
|
||||
fn apply(&self, x: Number, y: Number) -> Number {
|
||||
(self.operation)(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
trait Stack<T> {
|
||||
fn top(&self) -> Option<T>;
|
||||
}
|
||||
|
||||
impl<T: Clone> Stack<T> for Vec<T> {
|
||||
fn top(&self) -> Option<T> {
|
||||
if self.is_empty() {
|
||||
return None;
|
||||
}
|
||||
self.get(self.len() - 1).map(|value| value.clone())
|
||||
}
|
||||
}
|
||||
fn lex_token(input: char) -> Result<Token, char> {
|
||||
match input {
|
||||
'0'...'9' => Ok(Token::Digit(input.to_digit(10).unwrap() as Number)),
|
||||
'+' => Ok(Operator::new_token('+', 1, true, |x, y| x + y)),
|
||||
'-' => Ok(Operator::new_token('-', 1, true, |x, y| x - y)),
|
||||
'*' => Ok(Operator::new_token('*', 2, true, |x, y| x * y)),
|
||||
'/' => Ok(Operator::new_token('/', 2, true, |x, y| x / y)),
|
||||
'^' => Ok(Operator::new_token('^', 3, false, |x, y| x.powf(y))),
|
||||
'(' => Ok(Token::LeftParen),
|
||||
')' => Ok(Token::RightParen),
|
||||
_ => Err(input),
|
||||
}
|
||||
}
|
||||
|
||||
fn lex(input: String) -> Result<Vec<Token>, char> {
|
||||
input
|
||||
.chars()
|
||||
.filter(|c| !c.is_whitespace())
|
||||
.map(lex_token)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn tilt_until(operators: &mut Vec<Token>, output: &mut Vec<Token>, stop: Token) -> bool {
|
||||
while let Some(token) = operators.pop() {
|
||||
if token == stop {
|
||||
return true;
|
||||
}
|
||||
output.push(token)
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn shunting_yard(tokens: Vec<Token>) -> Result<Vec<Token>, String> {
|
||||
let mut output: Vec<Token> = Vec::new();
|
||||
let mut operators: Vec<Token> = Vec::new();
|
||||
|
||||
for token in tokens {
|
||||
match token {
|
||||
Token::Digit(_) => output.push(token),
|
||||
Token::LeftParen => operators.push(token),
|
||||
Token::Operator(operator) => {
|
||||
while let Some(top) = operators.top() {
|
||||
match top {
|
||||
Token::LeftParen => break,
|
||||
Token::Operator(top_op) => {
|
||||
let p = top_op.precedence;
|
||||
let q = operator.precedence;
|
||||
if (p > q) || (p == q && operator.is_left_associative) {
|
||||
output.push(operators.pop().unwrap());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
_ => unreachable!("{:?} must not be on operator stack", token),
|
||||
}
|
||||
}
|
||||
operators.push(token);
|
||||
}
|
||||
Token::RightParen => {
|
||||
if !tilt_until(&mut operators, &mut output, Token::LeftParen) {
|
||||
return Err(String::from("Mismatched ')'"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if tilt_until(&mut operators, &mut output, Token::LeftParen) {
|
||||
return Err(String::from("Mismatched '('"));
|
||||
}
|
||||
|
||||
assert!(operators.is_empty());
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
fn calculate(postfix_tokens: Vec<Token>) -> Result<Number, String> {
|
||||
let mut stack = Vec::new();
|
||||
|
||||
for token in postfix_tokens {
|
||||
match token {
|
||||
Token::Digit(number) => stack.push(number),
|
||||
Token::Operator(operator) => {
|
||||
if let Some(y) = stack.pop() {
|
||||
if let Some(x) = stack.pop() {
|
||||
stack.push(operator.apply(x, y));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return Err(format!("Missing operand for operator '{}'", operator.token));
|
||||
}
|
||||
_ => unreachable!("Unexpected token {:?} during calculation", token),
|
||||
}
|
||||
}
|
||||
|
||||
assert!(stack.len() == 1);
|
||||
Ok(stack.pop().unwrap())
|
||||
}
|
||||
|
||||
fn run(input: String) -> Result<Number, String> {
|
||||
let tokens = match lex(input) {
|
||||
Ok(tokens) => tokens,
|
||||
Err(c) => return Err(format!("Invalid character: {}", c)),
|
||||
};
|
||||
let postfix_tokens = match shunting_yard(tokens) {
|
||||
Ok(tokens) => tokens,
|
||||
Err(message) => return Err(message),
|
||||
};
|
||||
|
||||
calculate(postfix_tokens)
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
#!/bin/sh
|
||||
|
||||
getopprec() {
|
||||
case "$1" in
|
||||
'+') echo 2;;
|
||||
'-') echo 2;;
|
||||
'*') echo 3;;
|
||||
'/') echo 4;;
|
||||
'%') echo 4;;
|
||||
'^') echo 4;;
|
||||
'(') echo 5;;
|
||||
esac
|
||||
}
|
||||
|
||||
getopassoc() {
|
||||
case "$1" in
|
||||
'^') echo r;;
|
||||
*) echo l;;
|
||||
esac
|
||||
}
|
||||
|
||||
showstacks() {
|
||||
[ -n "$1" ] && echo "Token: $1" || echo "End parsing"
|
||||
echo -e "\tOutput: `tr $'\n' ' ' <<< "$out"`"
|
||||
echo -e "\tOperators: `tr $'\n' ' ' <<< "$ops"`"
|
||||
}
|
||||
|
||||
infix() {
|
||||
local out="" ops=""
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
grep -qE '^[0-9]+$' <<< "$1"
|
||||
if [ "$?" -eq 0 ]; then
|
||||
out="`sed -e '$a'"$1" -e '/^$/d' <<< "$out"`"
|
||||
|
||||
showstacks "$1"
|
||||
shift && continue
|
||||
fi
|
||||
|
||||
grep -q '^[-+*/^%]$' <<< "$1"
|
||||
if [ "$?" -eq 0 ]; then
|
||||
if [ -n "$ops" ]; then
|
||||
thispred=`getopprec "$1"`
|
||||
thisassoc=`getopassoc "$1"`
|
||||
topop="`sed -n '$p' <<< "$ops"`"
|
||||
thatpred=`getopprec "$topop"`
|
||||
thatassoc=`getopassoc "$topop"`
|
||||
while [ $thatpred -gt $thispred ] 2> /dev/null || ( [ \
|
||||
$thatpred -eq $thispred ] 2> /dev/null && [ $thisassoc = \
|
||||
'l' ] 2> /dev/null ); do # To /dev/null 'cus u r fake news
|
||||
|
||||
[ "$topop" = '(' ] && break
|
||||
|
||||
op="`sed -n '$p' <<< "$ops"`"
|
||||
out="`sed -e '$a'"$op" -e '/^$/d' <<< "$out"`"
|
||||
ops="`sed '$d' <<< "$ops"`"
|
||||
|
||||
topop="`sed -n '$p' <<< "$ops"`"
|
||||
thatpred=`getopprec "$topop"`
|
||||
thatassoc=`getopassoc "$topop"`
|
||||
done
|
||||
fi
|
||||
ops="`sed -e '$a'"$1" -e '/^$/d' <<< "$ops"`"
|
||||
|
||||
showstacks "$1"
|
||||
shift && continue
|
||||
fi
|
||||
|
||||
if [ "$1" = '(' ]; then
|
||||
ops="`sed -e '$a'"$1" -e '/^$/d' <<< "$ops"`"
|
||||
|
||||
showstacks "$1"
|
||||
shift && continue
|
||||
fi
|
||||
|
||||
if [ "$1" = ')' ]; then
|
||||
grep -q '^($' <<< "`sed -n '$p' <<< "$ops"`"
|
||||
while [ "$?" -ne 0 ]; do
|
||||
op="`sed -n '$p' <<< "$ops"`"
|
||||
out="`sed -e '$a'"$op" -e '/^$/d' <<< "$out"`"
|
||||
ops="`sed '$d' <<< "$ops"`"
|
||||
|
||||
grep -q '^($' <<< "`sed '$p' <<< "$ops"`"
|
||||
done
|
||||
ops="`sed '$d' <<< "$ops"`"
|
||||
|
||||
showstacks "$1"
|
||||
shift && continue
|
||||
fi
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
while [ -n "$ops" ]; do
|
||||
op="`sed -n '$p' <<< "$ops"`"
|
||||
out="`sed -e '$a'"$op" -e '/^$/d' <<< "$out"`"
|
||||
ops="`sed '$d' <<< "$ops"`"
|
||||
done
|
||||
|
||||
showstacks "$1"
|
||||
}
|
||||
|
||||
infix 3 + 4 \* 2 / \( 1 - 5 \) ^ 2 ^ 3
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
Token: 3
|
||||
Output: 3
|
||||
Operators:
|
||||
Token: +
|
||||
Output: 3
|
||||
Operators: +
|
||||
Token: 4
|
||||
Output: 3 4
|
||||
Operators: +
|
||||
Token: *
|
||||
Output: 3 4
|
||||
Operators: + *
|
||||
Token: 2
|
||||
Output: 3 4 2
|
||||
Operators: + *
|
||||
Token: /
|
||||
Output: 3 4 2
|
||||
Operators: + * /
|
||||
Token: (
|
||||
Output: 3 4 2
|
||||
Operators: + * / (
|
||||
Token: 1
|
||||
Output: 3 4 2 1
|
||||
Operators: + * / (
|
||||
Token: -
|
||||
Output: 3 4 2 1
|
||||
Operators: + * / ( -
|
||||
Token: 5
|
||||
Output: 3 4 2 1 5
|
||||
Operators: + * / ( -
|
||||
Token: )
|
||||
Output: 3 4 2 1 5 -
|
||||
Operators: + * /
|
||||
Token: ^
|
||||
Output: 3 4 2 1 5 -
|
||||
Operators: + * / ^
|
||||
Token: 2
|
||||
Output: 3 4 2 1 5 - 2
|
||||
Operators: + * / ^
|
||||
Token: ^
|
||||
Output: 3 4 2 1 5 - 2
|
||||
Operators: + * / ^ ^
|
||||
Token: 3
|
||||
Output: 3 4 2 1 5 - 2 3
|
||||
Operators: + * / ^ ^
|
||||
End parsing
|
||||
Output: 3 4 2 1 5 - 2 3 ^ ^ / * +
|
||||
Operators:
|
||||
Loading…
Add table
Add a link
Reference in a new issue