June 2018 Update

This commit is contained in:
Ingy döt Net 2018-06-22 20:57:24 +00:00
parent ba8067c3b7
commit 22f33d4004
5278 changed files with 84726 additions and 14379 deletions

View file

@ -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

View file

@ -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;
}

View file

@ -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);

View file

@ -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), ' '))")

View file

@ -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")
}
}

View file

@ -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)
}

View file

@ -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

View file

@ -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: