/parse-arithmetic

Library for parsing and using user-provided infix arithmetic expressions

Primary LanguageCommon Lisp

parse-arithmetic

Parse-arithmetic is a library for parsing user-provided infix arithmetic expressions, allowing a CL program to use that input in a meaningful way.

Introduction

The aim of this library is not to change CL's syntax to use infix operators, which is obviously a wrong and evil thing to do. Rather, the use case is for a program that requires the user to input an arithmetic expression, which the program can use for further calculations. A good example would be a console calculator, where the user will input the expression (a string) the way he or she is used to from many other programs: with infix notation and without our beloved parentheses.

Functions defined by the user in string expression form can be funcall'ed, and variables can be accessed.

This was hacked together in a few hours, because I wanted to try my hand at parsing user input to interact with a CL program, and without using eval. The result is quite rudimentary, not thoroughly tested, and not optimized in any way, and the documentation isn't really clear.

Examples

We can evaluate numerical expressions, with proper operator precedence and associativity:

(parse-and-evaluate "2+3*4") => 14.0

(let ((*arithmetic-float-format* 'double-float))
  (parse-and-evaluate "2^3^2")) => 512.0d0

We can also use pre-defined functions:

(parse-and-evaluate "cos(pi/4)") => 0.7071067811865476d0

(let ((*arithmetic-angle-unit* :degrees))
  (parse-and-evaluate "cos(45)")) => 0.7071067811865476d0

Assign and access variables:

(parse-and-evaluate "a=1") => 1.0

(parse-and-evaluate "a+1") => 2.0

; access the variable in CL
(+ (arithmetic-variable "a") 1) => 2.0

; set the variable in CL
(setf (arithmetic-variable "a") 2.0) => 2.0

(parse-and-evaluate "a*sin(pi/4)/sqrt(2)") => 1.0000000171142713d0

Assign and access functions:

(parse-and-evaluate "f(x)=x^2+a") => #<CLOSURE (LAMBDA (&REST PARSE-ARITHMETIC::ARGS) :IN COMPILE-EXPRESSION) {1004F758BB}>

(parse-and-evaluate "f(3)") => 11.0

; funcall the result in CL
(funcall ** 3) => 11.0

; or find the function by name and funcall it
(funcall (arithmetic-function "f") 3) => 11.0

CL Functions and variables

Special variables

*ARITHMETIC-FLOAT-FORMAT*
The float type into which all numbers are parsed. Default value is *DEFAULT-READ-FLOAT-FORMAT*.

*ARITHMETIC-ANGLE-UNIT*
The unit of angle which is used by trigonometric functions. Possible values are:

  • :radians or :rad for radians (the default value),
  • :degrees or :deg for degrees,
  • :gradians, :grades, :grads or :grad for gradians,
  • :turns, :cycles or :revolutions for turns,
  • :quadrants for quadrants.

Number expressions

expression-number-p expression => boolean
Test if an expression object represents a number. In fact, the object is a number.

(parse-expression "2.2") => 2.2

Variable expressions

expression-variable-p expression => boolean
Test if an expression object represents a variable. A variable expression is a list two items: the keyword symbol :variable, and a string that is the name of the variable.

(parse-expression "x") => (:variable "x")

expression-variable-name expression => string
Returns the name of the variable described by expression, or nil if expression did not represent a variable.

arithmetic-variable name => value
(setf (arithmetic-variable name) new-value)
Return or set the value of the variable with the given name. When setf'ing, if the variable name didn't exist, it is created.

Function expressions

expression-function-p expression => boolean
Test if an expression object represents a call to a function. A function expression is a list multiple items: the keyword symbol :function and a string that is the name of the function, followed by the function's arguments, each of which is an expression in itself.

(parse-expression "f(x,3)") => (:function "f" (:variable "x") 3)

expression-function-name expression => string
Returns the name of the function described by expression, or nil if expression did not represent a function.

expression-function-arguments expression => expressions
Returns a list of expressions, each representing an argument to the function described by expression, or nil if expression did not describe a function.

arithmetic-function name => lambda
(setf (arithmetic-function name) new-lambda)
Return or set the lambda for an arithmetic function with the given name. When setf'ing, if the function name didn't exist, it is created.

Parsing and evaluation

parse-expression string &key start end sub-expression function-argument => expression, pos
Parse a string expression into an expression object (as described above), and return that. Also returns the bounding index where parsing stopped. If sub-expression is true, parsing is assumed to start just inside a sub-expression, i.e. after an opening parenthesis in string, and parsing will end when a closing parenthesis is encountered. If function-argument is true, parsing is assumed to start just inside a function's argument, and parsing will end when either a comma or a closing parenthesis is encountered.

evaluate-expression expression => number
Evaluate expression, which results in a number. If not all functions and/or variables are defined, and thus expression contains unknowns, this causes an error.

compile-expression expression &optional argument-names => lambda
Compile an expression into a lambda that can be funcalled. This is normally not needed.

parse-assignment string &key start end => object-expression, definition-expression
Parse an assignment in string, bounded by indices start and end. Returns the expression object-expression before the assignment character #=, and the expression definition-expression after it.

evaluate-assignment object-expression definition-expression => object
Evaluate an assignment, by assigning the result of definition-expression to object-expression. object-expression must either be a variable expression, or a function expression of which all arguments are variable expressions. If a variable, definition-expression must evaluate to a number, which is then returned as object. If a function, definition-expression will be compiled to a lambda, which is returned as object.

parse-and-evaluate string &key start end => object
Convenience function that parses and evaluates expressions and assignments.

Authors

Sumant S. R. Oemrawsingh