Boom, A Little Language for Arithmetic
Motivation
This document defines an extended version of the Boom language we created for Homework 9, which was motivated by a common programming task. Then, motivated by the ability to write more complex programs, we extended Boom for Homework 10 to include local variables. When you know how to write language interpreters, you can make a language that meets your own needs.
To write more complex programs in Boom, it will be convenient if we are able to "evolve" numbers through a sequence of expressions. This requires the ability to change the state of a local variable and to have the new value reflected in subsequent expressions.
This version of Boom includes sequences of assignments as number expressions. They will be useful for writing programs that compute numbers that require more complex uses of Boom's operators.
Extensions to the language definition from Homework 10 are marked in this document as new. This includes additions to the BNF description and new sections in the description of semantics.
Language Grammar
BNF Description
This version of the language consists of exact numbers, both literal and named, simple arithmetic expressions, and sequences of assignment statements. Here is the BNF description of the language:
<exp> ::= <number> | <varref> | ( <unary-op> <exp> ) | ( <exp> <binary-op> <exp> ) | ( let <var> = <exp> in <exp> ) | ( do <assignment>* <exp> ) ; new <assignment> ::= ( <varref> := <exp> ) ; new <unary-op> ::= - | sq <binary-op> ::= + | - | * | / | % | @ | ^ | <<
<number>
can be any exact number, positive
or negative, including integers and rationals. The semantics of
the values and operators are defined below.
The symbols let
, =
, in
,
do
, and :=
are keywords in the
language. — change
Example Expressions
These are all still legal Boom expressions:
2 (2 + 4) 20 (4 * (10 ^ 3)) (sq 16) (2 * (4 / (sq 6))) (- (2 + 4)) ((2 * 14) + (4 << 6)) (8 << 3) ((8 % 3) + ((- 4) * (6 @ 10))) z (let z = (x * y) in (- z)) square (let square = (x * x) in (let half = (square / 2) in (half + (4 * x))))
These are now also legal Boom expressions: — new
(do 0) ; no vars or assignments (let x = 26 in ; one variable (do (x := (x * 2)) ; one assignment (x @ 30))) (let c = 10 in ; one variable (do (c := ((sq c) + c)) ; two assignments (c := (c / 10)) (c - 1))) (let left = 0 in ; two variables (let right = 12 in (do (left := (left @ right)) ; two assignments (right := (left @ right)) (left * right))))
As the grammar indicates, expressions nest arbitrarily deeply.
Semantics
Unary Expressions
-
(- operand)
returns the negation of its operand.
(- (1 + 2))
equals-3
. -
(sq operand)
returns the square of its operand.
(sq (1 + 2))
equals9
.
Binary Expressions
-
The first three —
+
,-
, and*
— mean what you expect them to mean from past experience: addition, subtraction, and multiplication. -
(exp1 / exp2)
computes integer division.
(17 / 4)
equals4
. -
(exp1 % exp2)
computes the remainder from integer division.
(17 % 4)
equals1
. -
(exp1 @ exp2)
computes the average of its operands.
(8 % 4)
equals6
, and(17 % 4)
equals10
. -
(m ^ n)
raisesm
to the power ofn
.
(2 ^ 4)
equals16
, and(10 ^ 3)
equals1000
. -
(exp << n)
shiftsexp
to the left byn
places, padding with 0s.
(8 << 3)
equals8000
.
Primitive Values
- zero, bound to the value 0
- two, bound to the value 2
- ten, bound to the value 10
Blocks with Local Variables
let/in
expression that declares the variable.
It is not meaningful in the value being assigned to
the variable.
A reference to a variable that has not been defined either in
the initial environment or in a containing
let/in
expression is an error.
A let/in
expression behaves as follows:
- First, the value of the variable is evaluated in the current environment.
- Then, a new environment is created by extending the current environment with the new variable/value pair.
- Finally, the body of the expression is evaluated in the new environment.
... just as local variables work in Racket.
Sequences with State — new
do
expression consists of 0 or more assignment
statements followed by a number expression. The value of a
do
expression is the value of that final number.
An assignment statement changes the value of an existing
variable. An attempt to assign a value to a variable that
has not been defined by a containing let/in
expression is an error.
Note that the :=
operator does not behave like
other operators in the language, because the variable on the
left hand side of the expression is not
evaluated. Instead, it is the target of the assignment.
The number expression on the right hand side of the statement
is evaluated, including any variables it contains.
A do
expression behaves as follows:
-
Evaluate the assignment statements in order. For each:
- evaluate the number expression on the right hand side of the statement, and
- update the value of the variable on the left hand side in the current environment.
-
Evaluate the final number expression in the current
environment and return that value as the value of the
do
expression.
Syntactic Sugar
All of the features defined above are part of the core of the Boom language except:
-
(sq num)
... which is a syntactic abstraction of(num * num)
-
(x @ y)
... which is a syntactic abstraction of((x + y) / 2)
-
(m << n)
... which is a syntactic abstraction of(m * (10 ^ n))
In Boom, syntactic abstractions are preprocessed away before expressions are evaluated.
The remaining operators, both unary and boolean, are part of the core of the language. The behavior of the core operators is implemented directly in the Boom interpreter.