2016 Update
This commit is contained in:
parent
948b86eafa
commit
dcf5d15da3
7965 changed files with 139854 additions and 31002 deletions
|
|
@ -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.
|
||||
* [[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.
|
||||
<br><br>
|
||||
|
|
|
|||
|
|
@ -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)))))
|
||||
|
|
@ -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!");
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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*"))))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue