2016 Update

This commit is contained in:
Tina Müller 2016-12-05 22:15:40 +01:00
parent 948b86eafa
commit dcf5d15da3
7965 changed files with 139854 additions and 31002 deletions

View file

@ -1,3 +1,4 @@
;Task:
Create a program that takes an [[wp:Reverse Polish notation|RPN]] representation of an expression formatted as a space separated sequence of tokens and generates the equivalent expression in [[wp:Infix notation|infix notation]].
* Assume an input of a correct, space separated, string of tokens
@ -12,26 +13,25 @@ Create a program that takes an [[wp:Reverse Polish notation|RPN]] representation
| <code>1 2 + 3 4 + ^ 5 6 + ^</code>|| <code>( ( 1 + 2 ) ^ ( 3 + 4 ) ) ^ ( 5 + 6 )</code>
|}
* Operator precedence is given in this table:
* Operator precedence and operator associativity is given in this table:
:{| class="wikitable"
! operator !! [[wp:Order_of_operations|precedence]] !! [[wp:Operator_associativity|associativity]]
! operator !! [[wp:Order_of_operations|precedence]] !! [[wp:Operator_associativity|associativity]] !! operation
|- || align="center"
| ^ || 4 || Right
| <big><big> ^ </big></big> || 4 || right || exponentiation
|- || align="center"
| * || 3 || Left
| <big><big> * </big></big> || 3 || left || multiplication
|- || align="center"
| / || 3 || Left
| <big><big> / </big></big> || 3 || left || division
|- || align="center"
| + || 2 || Left
| <big><big> + </big></big> || 2 || left || addition
|- || align="center"
| - || 2 || Left
| <big><big> - </big></big> || 2 || left || subtraction
|}
;Note:
* '^' means exponentiation.
;See also:
* [[Parsing/Shunting-yard algorithm]] for a method of generating an RPN from an infix expression.
* [[Parsing/RPN calculator algorithm]] for a method of calculating a final value from this output RPN expression.
* [http://www.rubyquiz.com/quiz148.html Postfix to infix] from the RubyQuiz site.
* &nbsp; [[Parsing/Shunting-yard algorithm]] &nbsp; for a method of generating an RPN from an infix expression.
* &nbsp; [[Parsing/RPN calculator algorithm]] &nbsp; for a method of calculating a final value from this output RPN expression.
* &nbsp; [http://www.rubyquiz.com/quiz148.html Postfix to infix] &nbsp; from the RubyQuiz site.
<br><br>

View file

@ -0,0 +1,59 @@
;;;; Parsing/RPN to infix conversion
(defstruct (node (:print-function print-node)) opr infix)
(defun print-node (node stream depth)
(format stream "opr:=~A infix:=\"~A\"" (node-opr node) (node-infix node)))
(defconstant OPERATORS '((#\^ . 4) (#\* . 3) (#\/ . 3) (#\+ . 2) (#\- . 2)))
;;; (char,char[,boolean])->boolean
(defun higher-p (opp opc &optional (left-node-p nil))
(or (> (cdr (assoc opp OPERATORS)) (cdr (assoc opc OPERATORS)))
(and left-node-p (char= opp #\^) (char= opc #\^))))
;;; string->list
(defun string-split (expr)
(let ((p (position #\Space expr)))
(if (null p) (list expr)
(append (list (subseq expr 0 p))
(string-split (subseq expr (1+ p)))))))
;;; string->string
(defun parse (expr)
(let ((stack '()))
(format t "TOKEN STACK~%")
(dolist (tok (string-split expr))
(if (assoc (char tok 0) OPERATORS) ; operator?
(push (make-node :opr (char tok 0) :infix (infix (char tok 0) (pop stack) (pop stack))) stack)
(push tok stack))
;; print stack at each token
(format t "~3,A" tok)
(dotimes (i (length stack)) (format t "~8,T[~D] ~A~%" i (nth i stack))))
;; print final infix expression
(if (= (length stack) 1)
(format nil "~A" (node-infix (first stack)))
(format nil "syntax error in ~A" expr))))
;;; (char,node,node)->string
(defun infix (operator rightn leftn)
;; (char,node[,boolean]->string
(defun string-node (operator anode &optional (left-node-p nil))
(if (stringp anode) anode
(if (higher-p operator (node-opr anode) left-node-p)
(format nil "( ~A )" (node-infix anode)) (node-infix anode))))
(concatenate 'string
(string-node operator leftn t)
(format nil " ~A " operator)
(string-node operator rightn)))
;;; nil->[printed infix expressions]
(defun main ()
(let ((expressions '("3 4 2 * 1 5 - 2 3 ^ ^ / +"
"1 2 + 3 4 + ^ 5 6 + ^"
"3 4 ^ 2 9 ^ ^ 2 5 ^ ^")))
(dolist (expr expressions)
(format t "~%Parsing:\"~A\"~%" expr)
(format t "RPN:\"~A\" INFIX:\"~A\"~%" expr (parse expr)))))

View file

@ -0,0 +1,59 @@
const Associativity = {
/** a / b / c = (a / b) / c */
left: 0,
/** a ^ b ^ c = a ^ (b ^ c) */
right: 1,
/** a + b + c = (a + b) + c = a + (b + c) */
both: 2,
};
const operators = {
'+': { precedence: 2, associativity: Associativity.both },
'-': { precedence: 2, associativity: Associativity.left },
'*': { precedence: 3, associativity: Associativity.both },
'/': { precedence: 3, associativity: Associativity.left },
'^': { precedence: 4, associativity: Associativity.right },
};
class NumberNode {
constructor(text) { this.text = text; }
toString() { return this.text; }
}
class InfixNode {
constructor(fnname, operands) {
this.fnname = fnname;
this.operands = operands;
}
toString(parentPrecedence = 0) {
const op = operators[this.fnname];
const leftAdd = op.associativity === Associativity.right ? 0.01 : 0;
const rightAdd = op.associativity === Associativity.left ? 0.01 : 0;
if (this.operands.length !== 2) throw Error("invalid operand count");
const result = this.operands[0].toString(op.precedence + leftAdd)
+` ${this.fnname} ${this.operands[1].toString(op.precedence + rightAdd)}`;
if (parentPrecedence > op.precedence) return `( ${result} )`;
else return result;
}
}
function rpnToTree(tokens) {
const stack = [];
console.log(`input = ${tokens}`);
for (const token of tokens.split(" ")) {
if (token in operators) {
const op = operators[token], arity = 2; // all of these operators take 2 arguments
if (stack.length < arity) throw Error("stack error");
stack.push(new InfixNode(token, stack.splice(stack.length - arity)));
}
else stack.push(new NumberNode(token));
console.log(`read ${token}, stack = [${stack.join(", ")}]`);
}
if (stack.length !== 1) throw Error("stack error " + stack);
return stack[0];
}
const tests = [
["3 4 2 * 1 5 - 2 3 ^ ^ / +", "3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3"],
["1 2 + 3 4 + ^ 5 6 + ^", "( ( 1 + 2 ) ^ ( 3 + 4 ) ) ^ ( 5 + 6 )"],
["1 2 3 + +", "1 + 2 + 3"] // test associativity (1+(2+3)) == (1+2+3)
];
for (const [inp, oup] of tests) {
const realOup = rpnToTree(inp).toString();
console.log(realOup === oup ? "Correct!" : "Incorrect!");
}

View file

@ -16,7 +16,7 @@ sub rpm-to-infix($string) {
when '^' { @stack.push: 4 => ~(p($x,5), $_, p($y,4)) }
when '*' | '/' { @stack.push: 3 => ~(p($x,3), $_, p($y,3)) }
when '+' | '-' { @stack.push: 2 => ~(p($x,2), $_, p($y,2)) }
LEAVE { say @stack }
# LEAVE { say @stack } # phaser not yet implemented in this context
}
say "-----------------";
@stack».value;

View file

@ -1,64 +1,63 @@
@(do
;; alias for circumflex, which is reserved syntax
(defvar exp (intern "^"))
;; alias for circumflex, which is reserved syntax
(defvar exp (intern "^"))
(defvar *prec* ^((,exp . 4) (* . 3) (/ . 3) (+ . 2) (- . 2)))
(defvar *prec* ^((,exp . 4) (* . 3) (/ . 3) (+ . 2) (- . 2)))
(defvar *asso* ^((,exp . :right) (* . nil)
(/ . :left) (+ . nil) (- . :left)))
(defvar *asso* ^((,exp . :right) (* . nil)
(/ . :left) (+ . nil) (- . :left)))
(defun debug-print (label val)
(format t "~a: ~a\n" label val)
val)
(defun debug-print (label val)
(format t "~a: ~a\n" label val)
val)
(defun rpn-to-lisp (rpn)
(let (stack)
(each ((term rpn))
(if (symbolp (debug-print "rpn term" term))
(let ((right (pop stack))
(left (pop stack)))
(push ^(,term ,left ,right) stack))
(push term stack))
(debug-print "stack" stack))
(if (rest stack)
(return-from error "*excess stack elements*"))
(debug-print "lisp" (pop stack))))
(defun rpn-to-lisp (rpn)
(let (stack)
(each ((term rpn))
(if (symbolp (debug-print "rpn term" term))
(let ((right (pop stack))
(left (pop stack)))
(push ^(,term ,left ,right) stack))
(push term stack))
(debug-print "stack" stack))
(if (rest stack)
(return-from error "*excess stack elements*"))
(debug-print "lisp" (pop stack))))
(defun prec (term)
(or (cdr (assoc term *prec*)) 99))
(defun prec (term)
(or (cdr (assoc term *prec*)) 99))
(defun asso (term dfl)
(or (cdr (assoc term *asso*)) dfl))
(defun asso (term dfl)
(or (cdr (assoc term *asso*)) dfl))
(defun inf-term (op term left-or-right)
(if (atom term)
`@term`
(let ((pt (prec (car term)))
(po (prec op))
(at (asso (car term) left-or-right))
(ao (asso op left-or-right)))
(cond
((< pt po) `(@(lisp-to-infix term))`)
((> pt po) `@(lisp-to-infix term)`)
((and (eq at ao) (eq left-or-right ao)) `@(lisp-to-infix term)`)
(t `(@(lisp-to-infix term))`)))))
(defun inf-term (op term left-or-right)
(if (atom term)
`@term`
(let ((pt (prec (car term)))
(po (prec op))
(at (asso (car term) left-or-right))
(ao (asso op left-or-right)))
(cond
((< pt po) `(@(lisp-to-infix term))`)
((> pt po) `@(lisp-to-infix term)`)
((and (eq at ao) (eq left-or-right ao)) `@(lisp-to-infix term)`)
(t `(@(lisp-to-infix term))`)))))
(defun lisp-to-infix (lisp)
(tree-case lisp
((op left right) (let ((left-inf (inf-term op left :left))
(right-inf (inf-term op right :right)))
`@{left-inf} @op @{right-inf}`))
(() (return-from error "*stack underflow*"))
(else `@lisp`)))
(defun lisp-to-infix (lisp)
(tree-case lisp
((op left right) (let ((left-inf (inf-term op left :left))
(right-inf (inf-term op right :right)))
`@{left-inf} @op @{right-inf}`))
(() (return-from error "*stack underflow*"))
(else `@lisp`)))
(defun string-to-rpn (str)
(debug-print "rpn"
(mapcar (do if (int-str @1) (int-str @1) (intern @1))
(tok-str str #/[^ \t]+/))))
(defun string-to-rpn (str)
(debug-print "rpn"
(mapcar (do if (int-str @1) (int-str @1) (intern @1))
(tok-str str #/[^ \t]+/))))
(debug-print "infix"
(block error
(tree-case *args*
((a b . c) "*excess args*")
((a) (lisp-to-infix (rpn-to-lisp (string-to-rpn a))))
(else "*arg needed*")))))
(debug-print "infix"
(block error
(tree-case *args*
((a b . c) "*excess args*")
((a) (lisp-to-infix (rpn-to-lisp (string-to-rpn a))))
(else "*arg needed*"))))