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. The best way to appreciate how languages are processed is to write language processors. The best way to appreciate how to implement a language is to implement one.
To write more complex expressions in Boom, it would be convenient to be able to break computations into parts and even to create new kinds of operators. This requires the ability to name an expression that computes a particular value and use the name as a value in a larger expression.
This version of Boom includes named numbers as expressions. They will be useful for writing programs that compute more ambitious numbers using Boom's operators.
Extensions to the language definition from Homework 9 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
The initial version of the language consists of exact numbers and simple arithmetic expressions. Here is the BNF description of the language:
<exp> ::= <number> | <varref> ; new | ( <unary-op> <exp> ) | ( <exp> <binary-op> <exp> ) | ( let <var> = <exp> in <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
, =
, and
in
are keywords in the language.
Example Expressions
These are all 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)))
These are now also legal Boom expressions: — new
z (let z = (x * y) in (- z)) square (let square = (x * x) in (let half = (square / 2) in (half + (4 * x))))
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 — new
- zero, bound to the value 0
- two, bound to the value 2
- ten, bound to the value 10
Blocks with Local Variables — new
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.
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.