Huey, A Little Language for Colors (version 3)
Motivation
This document specifies an extended version of the Huey language we created for Homework 9, which was motivated by a common web programming task. Then, motivated by a desire to write more complex programs, we extended Huey to include local variables. When you know how to write language interpreters, a language becomes a practical option for solving such problems.
To write more complex programs in Huey, it would be convenient if we were able to "evolve" colors through a sequence of expressions. This requires the ability to change the state of a local variable and have the new value reflected in subsequent expressions.
This version of Huey includes sequences of assignments as color expressions. They will be useful for writing programs that compute more ambitious colors using Huey's operators.
In the text that follows, extensions to the language definition from Homework 10 are marked as new. This includes additions to the BNF description and new sections in the description of semantics.
Language Grammar
BNF Description
The extended version of the language consists of RGB values, variable references, unary expressions, two-color binary expressions, one-color binary expressions, and blocks with local variables. Here is the BNF description of the language:
<color> ::= (rgb <byte> <byte> <byte> )
| <varref>
| ( <unary-op> <color> )
| ( <color> <2color-op> <color> )
| ( <color> <1color-op> <number> )
| ( color <var> = <color> in <color> )
| ( do <assignment>* <color> ) ; new
<assignment> ::= ( <varref> <= <color> ) ; new
<unary-op> ::= invert | darker
<2color-op> ::= + | - | mix
<1color-op> ::= * | shift
A <number> can be any real number,
positive or negative, including integers. As noted above,
a <byte> is an 8-bit integer in
the range [0..255]. The semantics of the values and
operators is defined below.
A <varref> is a Racket symbol.
The symbols rgb, color,
=, in, do, and
<= are keywords.
They cannot be used as the names of variables.
— change
Example Expressions
These are all still legal Huey expressions:
white
black
(rgb 0 255 0)
(invert (rgb 4 4 4))
((rgb 0 255 0) + (rgb 4 4 4))
(rgb 255 0 255) mix ((rgb 0 255 0) + (rgb 4 4 4))
((rgb 255 0 255) * 1.2)
((rgb 255 0 255) shift -10)
(color gray = (white mix black) in
(gray mix white))
(color purple = ((rgb 255 0 0) mix (rgb 0 0 255)) in
(darker purple))
(color green = (rgb 0 255 0) in
(color blue-green = (green mix (rgb 0 0 255)) in
(invert blue-green)))
These are now also legal Huey expressions: — new
(do (rgb 255 0 0))
(color c = (rgb 0 255 0) in
(do (c <= (c mix (rgb 0 0 255)))
(c <= (invert c))
(c shift 5)))
(color c = (rgb 0 255 0) in
(color d = (rgb 0 0 255) in
(do (c <= (c mix d))
(d <= (c mix d))
((c mix d) shift 5))))
As the grammar indicates, expressions nest arbitrarily deeply.
Semantics
RGB Values
Racket does not provide an 8-bit integer type, but we can use Racket's
byte?
type predicate to validate the components of an RGB value.
The constructor for an RGB value should ensure that all
values fall in the legal range. Any value larger than 255
maps to 255, and any value less than 0 maps to 0.
Unary Expressions
-
(invert operand)returns a color whose RGB components are 255 minus the corresponding components ofoperand.
(invert (rgb 150 99 42))equals(rgb 105 156 213). -
(darker operand)returns a color whose RGB components are one-half the corresponding components ofoperand.
(darker (rgb 150 99 42))equals(rgb 75 49 21).
Two-Color Expressions
-
(color1 + color2)returns a color whose RGB components are the sums of the corresponding components of its two operands.
((rgb 150 99 42) + (rgb 50 18 241))equals(rgb 200 117 255). -
(color1 - color2)returns a color whose RGB components are the differences of the corresponding components of its two operands.
((rgb 150 99 42) - (rgb 50 108 21))equals(rgb 100 0 21). -
(color1 mix color2)returns a color whose RGB components are 50-50 blends of the corresponding components of its two operands.
((rgb 150 99 42) mix (rgb 50 108 21))equals(rgb 100 103 31).
One-Color Expressions
-
(color * number)returns a color whose RGB components arenumbertimes the corresponding components of its color operand.
((rgb 150 99 42) * 1.6)equals(rgb 240 158 67). -
(color shift number)returns a color whose RGB components arenumberplus the corresponding components of its color operand.
((rgb 150 99 42) shift -50)equals(rgb 100 49 0).
Primitive Values
-
white, bound to the value(rgb 255 255 255) -
black, bound to the value(rgb 0 0 0)
Blocks with Local Variables
A variable reference is meaningful only within the body of
the color/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
color/in expression is an error.
A color/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.
Sequences with State — new
do expression consists of 0 or more
assignment statements followed by a color expression. The
value of a do expression is the value of that
final color.
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
color/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 color 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 color 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 color expression in the current
environment and return that value as the value of the
doexpression.
Syntactic Sugar
All of the features defined above are part of the core of the Huey language except:
-
(color1 mix color2)
... which is a syntactic abstraction of((color1 * 0.5) + (color2 * 0.5)) -
(darker color)
... which is a syntactic abstraction of(color * 0.5)
In Huey, syntactic abstractions are preprocessed away before expressions are evaluated.