Arithmetic evaluation: Difference between revisions

Content added Content deleted
(added Ursala)
(common lisp arithmetic evaluator)
Line 598: Line 598:
};
};
</lang>
</lang>

=={{header|Common Lisp}}==

The following code parses a string into a sequence of tokens. The sequence of tokens includes <code>:lparen</code> and <code>:rparen</code> indicating left and right parenthesis, respectively. That sequence of tokens is then transformed by replacing subsequences of the form <code>:lparen ... :rparen</code> with a sublist containing the tokens between the <code>:lparen</code> and <code>:rparen</code>. The resulting tree is then simplified by replacing any subsequence of the form <code>A x B y C …</code> with either <code>(A x B) y C …</code> or <code>A x (B y C)</code> depending on the relative precedence of <code>x</code> and <code>y</code>. This produces a syntax tree each of whose elements is either a node representing an integer, <code>(:integer . <var>n</var>)</code>, a list containing a single expression, <code>(<var>exp</var>), or an operation, <code>(<var>e<sub>1</sub></var> <var>op</var> <var>e<sub>2</sub></var>)</code>. Evaluating such a syntax tree is then trivial. This implementation can read integers, and produce integral and rational values.

<lang lisp>(defun tokenize-stream (stream)
(labels ((whitespace-p (char)
(find char #(#\space #\newline #\return #\tab)))
(consume-whitespace ()
(loop while (whitespace-p (peek-char nil stream nil #\a))
do (read-char stream)))
(read-integer ()
(loop while (digit-char-p (peek-char nil stream nil #\space))
collect (read-char stream) into digits
finally (return (parse-integer (coerce digits 'string))))))
(consume-whitespace)
(let ((c (peek-char nil stream nil nil)))
(multiple-value-bind (token value)
(case c
((nil) :eof)
((#\() :lparen)
((#\)) :rparen)
((#\*) :multiply)
((#\/) :divide)
((#\+) :add)
((#\-) :subtract)
(otherwise
(if (digit-char-p c)
(values :integer (read-integer))
(error "Unexpected character ~w encountered." c))))
(unless (find token #(:integer :eof))
(read-char stream))
(if (not (eql token :integer)) token
(cons token value))))))

(defun group-parentheses (tokens &optional (delimited nil))
(do ((new-tokens '()))
((endp tokens) (values (nreverse new-tokens) '()))
(let ((token (pop tokens)))
(case token
((:lparen)
(multiple-value-bind (group remaining-tokens)
(group-parentheses tokens t)
(setf new-tokens (cons group new-tokens)
tokens remaining-tokens)))
((:rparen)
(assert delimited () "Unexpected right parenthesis.")
(return (values (nreverse new-tokens) tokens)))
(otherwise
(push token new-tokens))))))

(defun group-operations (expression)
(flet ((gop (exp) (group-operations exp)))
(if (or (eql (car expression) :integer) ; <integer>
(endp (cdr expression)) ; (e1)
(endp (cdddr expression))) ; (e1 op e2)
expression
(destructuring-bind (A x B y C &rest others) expression
(let ((A (gop A)) (B (gop B)) (C (gop C)))
(if (and (find x #(:add :subtract))
(find y #(:multiply :divide)))
(gop (list* A x (list B y C) others))
(gop (list* (list A x B) y C others))))))))

(defun evaluate-expression (expression)
(cond
((eql (car expression) :integer)
(cdr expression))
((endp (cdr expression))
(evaluate-expression (car expression)))
(t (destructuring-bind (e1 op e2) expression
(let ((v1 (evaluate-expression e1))
(v2 (evaluate-expression e2)))
(ecase op
(:add (+ v1 v2))
(:subtract (- v1 v2))
(:multiply (* v1 v2))
(:divide (/ v1 v2))))))))

(defun evaluate (string)
(with-input-from-string (in string)
(evaluate-expression
(group-operations
(group-parentheses
(loop for token = (tokenize-stream in)
until (eql :eof token)
collect token))))))</lang>

Examples

> (evaluate "1 - 5 * 2 / 20 + 1")
3/2

> (evaluate "(1 - 5) * 2 / (20 + 1)")
-8/21

> (evaluate "2 * (3 + ((5) / (7 - 11)))")
7/2

> (evaluate "(2 + 3) / (10 - 5)")
1


=={{header|D}}==
=={{header|D}}==