Huey, A Little Language for Colors
Motivation
All links in this section are optional.
The light we see can be thought of as a wave of energy. What we perceive as different colors are light waves with different wavelengths. Our perception of color is determined by three sensors that trigger at wavelengths corresponding roughly to what we think of as red, green, and blue. All other colors are composites created by our brains based on the values returned by these three sensors.
We can thus record a color by encoding the values returned by these sensors in a triple, known as an RGB value. The first value represents the amount of red in the color, as returned by the 'red' sensor. The second and third represent the amount of green and blue in the color, respectively, as returned by the corresponding sensors.
Each of these values is a byte, an eight-bit integer in the range [0..255]. When all three values are set to 255, we perceive white. When all three are set to 0, we perceive black. All other colors correspond to other combinations of red, green, and blue values.
This RGB color model is used in many systems to represent and manipulate colors, including HTML and CSS. You can experiment with RGB values and their corresponding colors using MDN's color wheel or W3's color picker.
When you know how to write language interpreters, they become a practical option for solving a larger set of programming problems, including ones like manipulating colors. Over the next few homework assignments, we will put this idea into practice. We will make a language called Huey that is designed for the express purpose of computing solutions to this problem. The language turns out to be useful enough that we could extend it for use in a large set of graphical programs.
Language Grammar
BNF Description
The initial version of the language consists of RGB values, unary expressions, two-color binary expressions, and one-color binary expressions. Here is the BNF description of the language:
<color> ::= (rgb <byte> <byte> <byte> )
| ( <unary-op> <color> )
| ( <color> <2color-op> <color> )
| ( <color> <1color-op> <number> )
<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.
Example Expressions
These are all legal Huey expressions:
(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)
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).
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.