From 0d35bf194a386cc6f7ad238bb356e65ed45a6062 Mon Sep 17 00:00:00 2001
From: Tavian Barnes <tavianator@gmail.com>
Date: Mon, 23 Nov 2009 16:15:57 -0500
Subject: Begin bison implementation of parser.

---
 .gitignore                     |   4 +-
 dimension/Makefile.am          |   9 +-
 dimension/bison.y              | 760 ++++++++++++++++++++++++++++++++++++++
 dimension/flex.l               | 286 +++++++++++++++
 dimension/parse.c              | 803 -----------------------------------------
 dimension/tokenize.h           | 496 +------------------------
 dimension/tokenize.l           | 796 ----------------------------------------
 tests/dimension/demo.sh        |   2 +-
 tests/dimension/numeric.sh     |   2 +-
 tests/dimension/punctuation.sh |   2 +-
 10 files changed, 1060 insertions(+), 2100 deletions(-)
 create mode 100644 dimension/bison.y
 create mode 100644 dimension/flex.l
 delete mode 100644 dimension/parse.c
 delete mode 100644 dimension/tokenize.l

diff --git a/.gitignore b/.gitignore
index ac542f9..e373695 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,7 +40,9 @@ Makefile.in
 
 # Files created by `make'
 /dimension/dimension
-/dimension/tokenize.c
+/dimension/bison.c
+/dimension/bison.h
+/dimension/flex.c
 
 # Files created by `make check'
 /tests/*/*-test
diff --git a/dimension/Makefile.am b/dimension/Makefile.am
index 617ac52..7d2e11a 100644
--- a/dimension/Makefile.am
+++ b/dimension/Makefile.am
@@ -21,14 +21,17 @@ INCLUDES = -I$(top_srcdir)/libdimension
 
 bin_PROGRAMS = dimension
 
-dimension_SOURCES = main.c                                                     \
-                    parse.c                                                    \
+AM_YFLAGS = -d
+BUILT_SOURCES = bison.h
+
+dimension_SOURCES = bison.y                                                    \
+                    flex.l                                                     \
+                    main.c                                                     \
                     parse.h                                                    \
                     progressbar.c                                              \
                     progressbar.h                                              \
                     realize.c                                                  \
                     realize.h                                                  \
-                    tokenize.l                                                 \
                     tokenize.h                                                 \
                     utility.c                                                  \
                     utility.h
diff --git a/dimension/bison.y b/dimension/bison.y
new file mode 100644
index 0000000..ff50a8c
--- /dev/null
+++ b/dimension/bison.y
@@ -0,0 +1,760 @@
+/*************************************************************************
+ * Copyright (C) 2009 Tavian Barnes <tavianator@gmail.com>               *
+ *                                                                       *
+ * This file is part of Dimension.                                       *
+ *                                                                       *
+ * Dimension is free software; you can redistribute it and/or modify it  *
+ * under the terms of the GNU General Public License as published by the *
+ * Free Software Foundation; either version 3 of the License, or (at     *
+ * your option) any later version.                                       *
+ *                                                                       *
+ * Dimension is distributed in the hope that it will be useful, but      *
+ * WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ * General Public License for more details.                              *
+ *                                                                       *
+ * You should have received a copy of the GNU General Public License     *
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>. *
+ *************************************************************************/
+
+%{
+#include "parse.h"
+#include "tokenize.h"
+#include "utility.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+typedef struct dmnsn_token_iterator {
+  const dmnsn_array *tokens;
+  unsigned int i;
+} dmnsn_token_iterator;
+
+#define YYSTYPE const char *
+
+typedef struct dmnsn_location {
+  const char *first_filename, *last_filename;
+  int first_line, last_line;
+  int first_column, last_column;
+} dmnsn_location;
+
+#define YYLTYPE dmnsn_location
+#define YYLLOC_DEFAULT(Current, Rhs, N)                                 \
+  do {                                                                  \
+    if (N) {                                                            \
+      (Current).first_filename = YYRHSLOC(Rhs, 1).first_filename;       \
+      (Current).first_line     = YYRHSLOC(Rhs, 1).first_line;           \
+      (Current).first_column   = YYRHSLOC(Rhs, 1).first_column;         \
+      (Current).last_filename  = YYRHSLOC(Rhs, N).last_filename;        \
+      (Current).last_line      = YYRHSLOC(Rhs, N).last_line;            \
+      (Current).last_column    = YYRHSLOC(Rhs, N).last_column;          \
+    } else {                                                            \
+      (Current).first_filename = (Current).last_filename =              \
+        YYRHSLOC(Rhs, 0).last_filename;                                 \
+      (Current).first_line     = (Current).last_line     =              \
+        YYRHSLOC(Rhs, 0).last_line;                                     \
+      (Current).first_column   = (Current).last_column   =              \
+        YYRHSLOC(Rhs, 0).last_column;                                   \
+    }                                                                   \
+  } while (0)
+
+static int
+yylex(YYSTYPE *lvalp, YYLTYPE *llocp, dmnsn_token_iterator *iterator)
+{
+  if (iterator->i >= dmnsn_array_size(iterator->tokens)) {
+    return 0;
+  } else {
+    dmnsn_token token;
+    dmnsn_array_get(iterator->tokens, iterator->i, &token);
+    ++iterator->i;
+
+    *lvalp = token.value;
+
+    llocp->first_filename = llocp->last_filename = token.filename;
+    llocp->first_line     = llocp->last_line     = token.line;
+    llocp->first_column   = llocp->last_column   = token.col;
+
+    return token.type;
+  }  
+}
+
+void
+yyerror(YYLTYPE *locp, dmnsn_array *astree, dmnsn_token_iterator *iterator,
+        const char *str)
+{
+  dmnsn_diagnostic(locp->first_filename, locp->first_line, locp->first_column,
+                   "%s", str);
+}
+%}
+
+%define api.pure
+%locations
+%error-verbose
+%token-table
+
+%parse-param {dmnsn_array *astree}
+%parse-param {dmnsn_token_iterator *iterator}
+%lex-param {dmnsn_token_iterator *iterator}
+
+%token END 0 "end-of-file"
+
+/* Punctuation */
+%token DMNSN_T_LBRACE        "{"
+%token DMNSN_T_RBRACE        "}"
+%token DMNSN_T_LPAREN        "("
+%token DMNSN_T_RPAREN        ")"
+%token DMNSN_T_LBRACKET      "["
+%token DMNSN_T_RBRACKET      "]"
+%token DMNSN_T_PLUS          "+"
+%token DMNSN_T_MINUS         "-"
+%token DMNSN_T_STAR          "*"
+%token DMNSN_T_SLASH         "/"
+%token DMNSN_T_COMMA         ","
+%token DMNSN_T_SEMICOLON     ";"
+%token DMNSN_T_QUESTION      "?"
+%token DMNSN_T_COLON         ":"
+%token DMNSN_T_AND           "&"
+%token DMNSN_T_DOT           "."
+%token DMNSN_T_PIPE          "|"
+%token DMNSN_T_LESS          "<"
+%token DMNSN_T_GREATER       ">"
+%token DMNSN_T_BANG          "!"
+%token DMNSN_T_EQUALS        "="
+%token DMNSN_T_LESS_EQUAL    "<="
+%token DMNSN_T_GREATER_EQUAL ">="
+%token DMNSN_T_NOT_EQUAL     "!="
+
+/* Numeric values */
+%token DMNSN_T_INTEGER "integer"
+%token DMNSN_T_FLOAT   "float"
+
+/* Keywords */
+%token DMNSN_T_AA_LEVEL
+%token DMNSN_T_AA_THRESHOLD
+%token DMNSN_T_ABS
+%token DMNSN_T_ABSORPTION
+%token DMNSN_T_ACCURACY
+%token DMNSN_T_ACOS
+%token DMNSN_T_ACOSH
+%token DMNSN_T_ADAPTIVE
+%token DMNSN_T_ADC_BAILOUT
+%token DMNSN_T_AGATE
+%token DMNSN_T_AGATE_TURB
+%token DMNSN_T_ALL
+%token DMNSN_T_ALL_INTERSECTIONS
+%token DMNSN_T_ALPHA
+%token DMNSN_T_ALTITUDE
+%token DMNSN_T_ALWAYS_SAMPLE
+%token DMNSN_T_AMBIENT
+%token DMNSN_T_AMBIENT_LIGHT
+%token DMNSN_T_ANGLE
+%token DMNSN_T_APERTURE
+%token DMNSN_T_APPEND
+%token DMNSN_T_ARC_ANGLE
+%token DMNSN_T_AREA_LIGHT
+%token DMNSN_T_ARRAY
+%token DMNSN_T_ASC
+%token DMNSN_T_ASCII
+%token DMNSN_T_ASIN
+%token DMNSN_T_ASINH
+%token DMNSN_T_ASSUMED_GAMMA
+%token DMNSN_T_ATAN
+%token DMNSN_T_ATAN2
+%token DMNSN_T_ATANH
+%token DMNSN_T_AUTOSTOP
+%token DMNSN_T_AVERAGE
+%token DMNSN_T_B_SPLINE
+%token DMNSN_T_BACKGROUND
+%token DMNSN_T_BEZIER_SPLINE
+%token DMNSN_T_BICUBIC_PATCH
+%token DMNSN_T_BLACK_HOLE
+%token DMNSN_T_BLOB
+%token DMNSN_T_BLUE
+%token DMNSN_T_BLUR_SAMPLES
+%token DMNSN_T_BOUNDED_BY
+%token DMNSN_T_BOX                      "box"
+%token DMNSN_T_BOXED
+%token DMNSN_T_BOZO
+%token DMNSN_T_BRICK
+%token DMNSN_T_BRICK_SIZE
+%token DMNSN_T_BRIGHTNESS
+%token DMNSN_T_BRILLIANCE
+%token DMNSN_T_BUMP_MAP
+%token DMNSN_T_BUMP_SIZE
+%token DMNSN_T_BUMPS
+%token DMNSN_T_CAMERA                   "camera"
+%token DMNSN_T_CAUSTICS
+%token DMNSN_T_CEIL
+%token DMNSN_T_CELLS
+%token DMNSN_T_CHARSET
+%token DMNSN_T_CHECKER
+%token DMNSN_T_CHR
+%token DMNSN_T_CIRCULAR
+%token DMNSN_T_CLIPPED_BY
+%token DMNSN_T_CLOCK
+%token DMNSN_T_CLOCK_DELTA
+%token DMNSN_T_CLOCK_ON
+%token DMNSN_T_COLLECT
+%token DMNSN_T_COLOR                    "color"
+%token DMNSN_T_COLOR_MAP
+%token DMNSN_T_COMPONENT
+%token DMNSN_T_COMPOSITE
+%token DMNSN_T_CONCAT
+%token DMNSN_T_CONE
+%token DMNSN_T_CONFIDENCE
+%token DMNSN_T_CONIC_SWEEP
+%token DMNSN_T_CONSERVE_ENERGY
+%token DMNSN_T_CONTAINED_BY
+%token DMNSN_T_CONTROL0
+%token DMNSN_T_CONTROL1
+%token DMNSN_T_COORDS
+%token DMNSN_T_COS
+%token DMNSN_T_COSH
+%token DMNSN_T_COUNT
+%token DMNSN_T_CRACKLE
+%token DMNSN_T_CRAND
+%token DMNSN_T_CUBE
+%token DMNSN_T_CUBIC
+%token DMNSN_T_CUBIC_SPLINE
+%token DMNSN_T_CUBIC_WAVE
+%token DMNSN_T_CUTAWAY_TEXTURES
+%token DMNSN_T_CYLINDER
+%token DMNSN_T_CYLINDRICAL
+%token DMNSN_T_DEFINED
+%token DMNSN_T_DEGREES
+%token DMNSN_T_DENSITY
+%token DMNSN_T_DENSITY_FILE
+%token DMNSN_T_DENSITY_MAP
+%token DMNSN_T_DENTS
+%token DMNSN_T_DF3
+%token DMNSN_T_DIFFERENCE
+%token DMNSN_T_DIFFUSE
+%token DMNSN_T_DIMENSION_SIZE
+%token DMNSN_T_DIMENSIONS
+%token DMNSN_T_DIRECTION
+%token DMNSN_T_DISC
+%token DMNSN_T_DISPERSION
+%token DMNSN_T_DISPERSION_SAMPLES
+%token DMNSN_T_DIST_EXP
+%token DMNSN_T_DISTANCE
+%token DMNSN_T_DIV
+%token DMNSN_T_DOUBLE_ILLUMINATE
+%token DMNSN_T_ECCENTRICITY
+%token DMNSN_T_EMISSION
+%token DMNSN_T_ERROR_BOUND
+%token DMNSN_T_EVALUATE
+%token DMNSN_T_EXP
+%token DMNSN_T_EXPAND_THRESHOLDS
+%token DMNSN_T_EXPONENT
+%token DMNSN_T_EXTERIOR
+%token DMNSN_T_EXTINCTION
+%token DMNSN_T_FACE_INDICES
+%token DMNSN_T_FACETS
+%token DMNSN_T_FADE_COLOR
+%token DMNSN_T_FADE_DISTANCE
+%token DMNSN_T_FADE_POWER
+%token DMNSN_T_FALLOFF
+%token DMNSN_T_FALLOFF_ANGLE
+%token DMNSN_T_FALSE
+%token DMNSN_T_FILE_EXISTS
+%token DMNSN_T_FILTER
+%token DMNSN_T_FINAL_CLOCK
+%token DMNSN_T_FINAL_FRAME
+%token DMNSN_T_FINISH
+%token DMNSN_T_FISHEYE
+%token DMNSN_T_FLATNESS
+%token DMNSN_T_FLIP
+%token DMNSN_T_FLOOR
+%token DMNSN_T_FOCAL_POINT
+%token DMNSN_T_FOG
+%token DMNSN_T_FOG_ALT
+%token DMNSN_T_FOG_OFFSET
+%token DMNSN_T_FOG_TYPE
+%token DMNSN_T_FORM
+%token DMNSN_T_FRAME_NUMBER
+%token DMNSN_T_FREQUENCY
+%token DMNSN_T_FRESNEL
+%token DMNSN_T_FUNCTION
+%token DMNSN_T_GATHER
+%token DMNSN_T_GIF
+%token DMNSN_T_GLOBAL_LIGHTS
+%token DMNSN_T_GLOBAL_SETTINGS
+%token DMNSN_T_GRADIENT
+%token DMNSN_T_GRANITE
+%token DMNSN_T_GRAY
+%token DMNSN_T_GRAY_THRESHOLD
+%token DMNSN_T_GREEN
+%token DMNSN_T_HEIGHT_FIELD
+%token DMNSN_T_HEXAGON
+%token DMNSN_T_HF_GRAY_16
+%token DMNSN_T_HIERARCHY
+%token DMNSN_T_HYPERCOMPLEX
+%token DMNSN_T_HOLLOW
+%token DMNSN_T_IFF
+%token DMNSN_T_IMAGE_HEIGHT
+%token DMNSN_T_IMAGE_MAP
+%token DMNSN_T_IMAGE_PATTERN
+%token DMNSN_T_IMAGE_WIDTH
+%token DMNSN_T_INITIAL_CLOCK
+%token DMNSN_T_INITIAL_FRAME
+%token DMNSN_T_INSIDE
+%token DMNSN_T_INSIDE_VECTOR
+%token DMNSN_T_INT
+%token DMNSN_T_INTERIOR
+%token DMNSN_T_INTERIOR_TEXTURE
+%token DMNSN_T_INTERNAL
+%token DMNSN_T_INTERPOLATE
+%token DMNSN_T_INTERSECTION
+%token DMNSN_T_INTERVALS
+%token DMNSN_T_INVERSE
+%token DMNSN_T_IOR
+%token DMNSN_T_IRID
+%token DMNSN_T_IRID_WAVELENGTH
+%token DMNSN_T_ISOSURFACE
+%token DMNSN_T_JITTER
+%token DMNSN_T_JPEG
+%token DMNSN_T_JULIA
+%token DMNSN_T_JULIA_FRACTAL
+%token DMNSN_T_LAMBDA
+%token DMNSN_T_LATHE
+%token DMNSN_T_LEOPARD
+%token DMNSN_T_LIGHT_GROUP
+%token DMNSN_T_LIGHT_SOURCE
+%token DMNSN_T_LINEAR_SPLINE
+%token DMNSN_T_LINEAR_SWEEP
+%token DMNSN_T_LN
+%token DMNSN_T_LOAD_FILE
+%token DMNSN_T_LOCATION
+%token DMNSN_T_LOG
+%token DMNSN_T_LOOK_AT
+%token DMNSN_T_LOOKS_LIKE
+%token DMNSN_T_LOW_ERROR_FACTOR
+%token DMNSN_T_MAGNET
+%token DMNSN_T_MAJOR_RADIUS
+%token DMNSN_T_MANDEL
+%token DMNSN_T_MAP_TYPE
+%token DMNSN_T_MARBLE
+%token DMNSN_T_MATERIAL
+%token DMNSN_T_MATERIAL_MAP
+%token DMNSN_T_MATRIX
+%token DMNSN_T_MAX
+%token DMNSN_T_MAX_EXTENT
+%token DMNSN_T_MAX_GRADIENT
+%token DMNSN_T_MAX_INTERSECTIONS
+%token DMNSN_T_MAX_ITERATION
+%token DMNSN_T_MAX_SAMPLE
+%token DMNSN_T_MAX_TRACE
+%token DMNSN_T_MAX_TRACE_LEVEL
+%token DMNSN_T_MEDIA
+%token DMNSN_T_MEDIA_ATTENUATION
+%token DMNSN_T_MEDIA_INTERACTION
+%token DMNSN_T_MERGE
+%token DMNSN_T_MESH
+%token DMNSN_T_MESH2
+%token DMNSN_T_METALLIC
+%token DMNSN_T_METHOD
+%token DMNSN_T_METRIC
+%token DMNSN_T_MIN
+%token DMNSN_T_MIN_EXTENT
+%token DMNSN_T_MINIMUM_REUSE
+%token DMNSN_T_MOD
+%token DMNSN_T_MORTAR
+%token DMNSN_T_NATURAL_SPLINE
+%token DMNSN_T_NEAREST_COUNT
+%token DMNSN_T_NO
+%token DMNSN_T_NO_BUMP_SCALE
+%token DMNSN_T_NO_IMAGE
+%token DMNSN_T_NO_REFLECTION
+%token DMNSN_T_NO_SHADOW
+%token DMNSN_T_NOISE_GENERATOR
+%token DMNSN_T_NORMAL
+%token DMNSN_T_NORMAL_INDICES
+%token DMNSN_T_NORMAL_MAP
+%token DMNSN_T_NORMAL_VECTORS
+%token DMNSN_T_NUMBER_OF_WAVES
+%token DMNSN_T_OBJECT
+%token DMNSN_T_OCTAVES
+%token DMNSN_T_OFF
+%token DMNSN_T_OFFSET
+%token DMNSN_T_OMEGA
+%token DMNSN_T_OMNIMAX
+%token DMNSN_T_ON
+%token DMNSN_T_ONCE
+%token DMNSN_T_ONION
+%token DMNSN_T_OPEN
+%token DMNSN_T_ORIENT
+%token DMNSN_T_ORIENTATION
+%token DMNSN_T_ORTHOGRAPHIC
+%token DMNSN_T_PANORAMIC
+%token DMNSN_T_PARALLEL
+%token DMNSN_T_PARAMETRIC
+%token DMNSN_T_PASS_THROUGH
+%token DMNSN_T_PATTERN
+%token DMNSN_T_PERSPECTIVE
+%token DMNSN_T_PGM
+%token DMNSN_T_PHASE
+%token DMNSN_T_PHONG
+%token DMNSN_T_PHONG_SIZE
+%token DMNSN_T_PHOTONS
+%token DMNSN_T_PI
+%token DMNSN_T_PIGMENT
+%token DMNSN_T_PIGMENT_MAP
+%token DMNSN_T_PIGMENT_PATTERN
+%token DMNSN_T_PLANAR
+%token DMNSN_T_PLANE
+%token DMNSN_T_PNG
+%token DMNSN_T_POINT_AT
+%token DMNSN_T_POLY
+%token DMNSN_T_POLY_WAVE
+%token DMNSN_T_POLYGON
+%token DMNSN_T_POT
+%token DMNSN_T_POW
+%token DMNSN_T_PPM
+%token DMNSN_T_PRECISION
+%token DMNSN_T_PRECOMPUTE
+%token DMNSN_T_PRETRACE_END
+%token DMNSN_T_PRETRACE_START
+%token DMNSN_T_PRISM
+%token DMNSN_T_PROD
+%token DMNSN_T_PROJECTED_THROUGH
+%token DMNSN_T_PWR
+%token DMNSN_T_QUADRATIC_SPLINE
+%token DMNSN_T_QUADRIC
+%token DMNSN_T_QUARTIC
+%token DMNSN_T_QUATERNION
+%token DMNSN_T_QUICK_COLOR
+%token DMNSN_T_QUILTED
+%token DMNSN_T_RADIAL
+%token DMNSN_T_RADIANS
+%token DMNSN_T_RADIOSITY
+%token DMNSN_T_RADIUS
+%token DMNSN_T_RAINBOW
+%token DMNSN_T_RAMP_WAVE
+%token DMNSN_T_RAND
+%token DMNSN_T_RATIO
+%token DMNSN_T_RECIPROCAL
+%token DMNSN_T_RECURSION_LIMIT
+%token DMNSN_T_RED
+%token DMNSN_T_REFLECTION
+%token DMNSN_T_REFLECTION_EXPONENT
+%token DMNSN_T_REFRACTION
+%token DMNSN_T_REPEAT
+%token DMNSN_T_RGB
+%token DMNSN_T_RGBF
+%token DMNSN_T_RGBFT
+%token DMNSN_T_RGBT
+%token DMNSN_T_RIGHT
+%token DMNSN_T_RIPPLES
+%token DMNSN_T_ROTATE
+%token DMNSN_T_ROUGHNESS
+%token DMNSN_T_SAMPLES
+%token DMNSN_T_SAVE_FILE
+%token DMNSN_T_SCALE
+%token DMNSN_T_SCALLOP_WAVE
+%token DMNSN_T_SCATTERING
+%token DMNSN_T_SEED
+%token DMNSN_T_SELECT
+%token DMNSN_T_SHADOWLESS
+%token DMNSN_T_SIN
+%token DMNSN_T_SINE_WAVE
+%token DMNSN_T_SINH
+%token DMNSN_T_SIZE
+%token DMNSN_T_SKY
+%token DMNSN_T_SKY_SPHERE
+%token DMNSN_T_SLICE
+%token DMNSN_T_SLOPE
+%token DMNSN_T_SLOPE_MAP
+%token DMNSN_T_SMOOTH
+%token DMNSN_T_SMOOTH_TRIANGLE
+%token DMNSN_T_SOLID
+%token DMNSN_T_SOR
+%token DMNSN_T_SPACING
+%token DMNSN_T_SPECULAR
+%token DMNSN_T_SPHERE                   "sphere"
+%token DMNSN_T_SPHERE_SWEEP
+%token DMNSN_T_SPHERICAL
+%token DMNSN_T_SPIRAL1
+%token DMNSN_T_SPIRAL2
+%token DMNSN_T_SPLINE
+%token DMNSN_T_SPLIT_UNION
+%token DMNSN_T_SPOTLIGHT
+%token DMNSN_T_SPOTTED
+%token DMNSN_T_SQR
+%token DMNSN_T_SQRT
+%token DMNSN_T_STR
+%token DMNSN_T_STRCMP
+%token DMNSN_T_STRENGTH
+%token DMNSN_T_STRLEN
+%token DMNSN_T_STRLWR
+%token DMNSN_T_STRUPR
+%token DMNSN_T_STURM
+%token DMNSN_T_SUBSTR
+%token DMNSN_T_SUM
+%token DMNSN_T_SUPERELLIPSOID
+%token DMNSN_T_SYS
+%token DMNSN_T_T
+%token DMNSN_T_TAN
+%token DMNSN_T_TANH
+%token DMNSN_T_TARGET
+%token DMNSN_T_TEXT
+%token DMNSN_T_TEXTURE
+%token DMNSN_T_TEXTURE_LIST
+%token DMNSN_T_TEXTURE_MAP
+%token DMNSN_T_TGA
+%token DMNSN_T_THICKNESS
+%token DMNSN_T_THRESHOLD
+%token DMNSN_T_TIFF
+%token DMNSN_T_TIGHTNESS
+%token DMNSN_T_TILE2
+%token DMNSN_T_TILES
+%token DMNSN_T_TOLERANCE
+%token DMNSN_T_TOROIDAL
+%token DMNSN_T_TORUS
+%token DMNSN_T_TRACE
+%token DMNSN_T_TRANSFORM
+%token DMNSN_T_TRANSLATE
+%token DMNSN_T_TRANSMIT
+%token DMNSN_T_TRIANGLE
+%token DMNSN_T_TRIANGLE_WAVE
+%token DMNSN_T_TRUE
+%token DMNSN_T_TTF
+%token DMNSN_T_TURB_DEPTH
+%token DMNSN_T_TURBULENCE
+%token DMNSN_T_TYPE
+%token DMNSN_T_U
+%token DMNSN_T_U_STEPS
+%token DMNSN_T_ULTRA_WIDE_ANGLE
+%token DMNSN_T_UNION
+%token DMNSN_T_UP
+%token DMNSN_T_USE_ALPHA
+%token DMNSN_T_USE_COLOR
+%token DMNSN_T_USE_INDEX
+%token DMNSN_T_UTF8
+%token DMNSN_T_UV_INDICES
+%token DMNSN_T_UV_MAPPING
+%token DMNSN_T_UV_VECTORS
+%token DMNSN_T_V
+%token DMNSN_T_V_STEPS
+%token DMNSN_T_VAL
+%token DMNSN_T_VARIANCE
+%token DMNSN_T_VAXIS_ROTATE
+%token DMNSN_T_VCROSS
+%token DMNSN_T_VDOT
+%token DMNSN_T_VERTEX_VECTORS
+%token DMNSN_T_VLENGTH
+%token DMNSN_T_VNORMALIZE
+%token DMNSN_T_VROTATE
+%token DMNSN_T_VSTR
+%token DMNSN_T_VTURBULENCE
+%token DMNSN_T_WARP
+%token DMNSN_T_WATER_LEVEL
+%token DMNSN_T_WAVES
+%token DMNSN_T_WIDTH
+%token DMNSN_T_WOOD
+%token DMNSN_T_WRINKLES
+%token DMNSN_T_X
+%token DMNSN_T_Y
+%token DMNSN_T_YES
+%token DMNSN_T_Z
+
+/* Directives (#declare etc.) */
+%token DMNSN_T_BREAK
+%token DMNSN_T_CASE
+%token DMNSN_T_DEBUG
+%token DMNSN_T_DECLARE    "#declare"
+%token DMNSN_T_DEFAULT
+%token DMNSN_T_ELSE
+%token DMNSN_T_END
+%token DMNSN_T_ERROR
+%token DMNSN_T_FCLOSE
+%token DMNSN_T_FOPEN
+%token DMNSN_T_IF
+%token DMNSN_T_IFDEF
+%token DMNSN_T_IFNDEF
+%token DMNSN_T_INCLUDE    "#include"
+%token DMNSN_T_LOCAL
+%token DMNSN_T_MACRO
+%token DMNSN_T_RANGE
+%token DMNSN_T_READ
+%token DMNSN_T_RENDER
+%token DMNSN_T_STATISTICS 
+%token DMNSN_T_SWITCH
+%token DMNSN_T_UNDEF
+%token DMNSN_T_VERSION
+%token DMNSN_T_WARNING
+%token DMNSN_T_WHILE
+%token DMNSN_T_WRITE
+
+/* Identifiers */
+%token DMNSN_T_IDENTIFIER "identifier"
+
+/* Strings */
+%token DMNSN_T_STRING "string"
+
+%%
+
+SCENE:   /* empty */
+       | SCENE SCENE_ITEM
+       ;
+
+SCENE_ITEM:   OBJECT
+            ;
+
+OBJECT:   FINITE_SOLID_OBJECT
+        ;
+
+FINITE_SOLID_OBJECT:   BOX | SPHERE
+                     ;
+
+BOX:   "box" "{" "}"
+     ;
+
+SPHERE:   "sphere" "{" "}"
+        ;
+
+%%
+
+dmnsn_array *
+dmnsn_parse(const dmnsn_array *tokens)
+{
+  dmnsn_array *astree = dmnsn_new_array(sizeof(dmnsn_astnode));
+  dmnsn_token_iterator iterator = { .tokens = tokens, .i = 0 };
+
+  if (yyparse(astree, &iterator) != 0) {
+    dmnsn_delete_astree(astree);
+    return NULL;
+  }
+
+  return astree;
+}
+
+void
+dmnsn_delete_astree(dmnsn_array *astree)
+{
+  unsigned int i;
+  dmnsn_astnode node;
+
+  if (astree) {
+    for (i = 0; i < dmnsn_array_size(astree); ++i) {
+      dmnsn_array_get(astree, i, &node);
+      dmnsn_delete_astree(node.children);
+      free(node.ptr);
+    }
+    dmnsn_delete_array(astree);
+  }
+}
+
+static void
+dmnsn_print_astnode(FILE *file, dmnsn_astnode astnode)
+{
+  long ivalue;
+  double dvalue;
+
+  switch (astnode.type) {
+  case DMNSN_AST_INTEGER:
+    ivalue = *(long *)astnode.ptr;
+    fprintf(file, "(%s %ld)", dmnsn_astnode_string(astnode.type), ivalue);
+    break;
+
+  case DMNSN_AST_FLOAT:
+    dvalue = *(double *)astnode.ptr;
+    fprintf(file, "(%s %g)", dmnsn_astnode_string(astnode.type), dvalue);
+    break;
+
+  default:
+    fprintf(file, "%s", dmnsn_astnode_string(astnode.type));
+  }
+}
+
+static void
+dmnsn_print_astree(FILE *file, dmnsn_astnode astnode)
+{
+  unsigned int i;
+  dmnsn_astnode child;
+
+  if (astnode.children && dmnsn_array_size(astnode.children) > 0) {
+    fprintf(file, "(");
+    dmnsn_print_astnode(file, astnode);
+    for (i = 0; i < dmnsn_array_size(astnode.children); ++i) {
+      dmnsn_array_get(astnode.children, i, &child);
+      fprintf(file, " ");
+      dmnsn_print_astree(file, child);
+    }
+    fprintf(file, ")");
+  } else {
+    dmnsn_print_astnode(file, astnode);
+  }
+}
+
+void
+dmnsn_print_astree_sexpr(FILE *file, const dmnsn_array *astree)
+{
+  dmnsn_astnode astnode;
+  unsigned int i;
+
+  if (dmnsn_array_size(astree) == 0) {
+    fprintf(file, "()");
+  } else {
+    fprintf(file, "(");
+    dmnsn_array_get(astree, 0, &astnode);
+    dmnsn_print_astree(file, astnode);
+
+    for (i = 1; i < dmnsn_array_size(astree); ++i) {
+      fprintf(file, " ");
+      dmnsn_array_get(astree, i, &astnode);
+      dmnsn_print_astree(file, astnode);
+    }
+
+    fprintf(file, ")");
+  }
+
+  fprintf(file, "\n");
+}
+
+const char *
+dmnsn_token_string(dmnsn_token_type token_type)
+{
+#define TOKEN_SIZE 256
+  static char token[TOKEN_SIZE];
+
+  unsigned int i = YYTRANSLATE(token_type);
+  if (i > YYNTOKENS) {
+    fprintf(stderr, "Warning: unrecognised token %d.\n", (int)token_type);
+    return "unrecognized-token";
+  }
+
+  if (yytnamerr(NULL, yytname[i]) >= TOKEN_SIZE) {
+    fprintf(stderr, "Warning: name of token %d too long.\n", (int)token_type);
+    return "unrepresentable-token";
+  }
+
+  yytnamerr(token, yytname[i]);
+  return token;
+#undef TOKEN_SIZE
+}
+
+const char *
+dmnsn_astnode_string(dmnsn_astnode_type astnode_type)
+{
+  switch (astnode_type) {
+  /* Macro to shorten this switch */
+#define dmnsn_astnode_map(type, str)                                           \
+  case type:                                                                   \
+    return str;
+
+  dmnsn_astnode_map(DMNSN_AST_FLOAT, "float");
+  dmnsn_astnode_map(DMNSN_AST_INTEGER, "integer");
+  dmnsn_astnode_map(DMNSN_AST_NEGATE, "-");
+  dmnsn_astnode_map(DMNSN_AST_ADD, "+");
+  dmnsn_astnode_map(DMNSN_AST_SUB, "-");
+  dmnsn_astnode_map(DMNSN_AST_MUL, "*");
+  dmnsn_astnode_map(DMNSN_AST_DIV, "/");
+  dmnsn_astnode_map(DMNSN_AST_BOX, "box");
+  dmnsn_astnode_map(DMNSN_AST_VECTOR, "vector");
+  dmnsn_astnode_map(DMNSN_AST_SPHERE, "sphere");
+
+  default:
+    fprintf(stderr, "Warning: unrecognised astnode type %d.\n",
+            (int)astnode_type);
+    return "unrecognized-astnode";
+  }
+}
diff --git a/dimension/flex.l b/dimension/flex.l
new file mode 100644
index 0000000..b470159
--- /dev/null
+++ b/dimension/flex.l
@@ -0,0 +1,286 @@
+/*************************************************************************
+ * Copyright (C) 2009 Tavian Barnes <tavianator@gmail.com>               *
+ *                                                                       *
+ * This file is part of Dimension.                                       *
+ *                                                                       *
+ * Dimension is free software; you can redistribute it and/or modify it  *
+ * under the terms of the GNU General Public License as published by the *
+ * Free Software Foundation; either version 3 of the License, or (at     *
+ * your option) any later version.                                       *
+ *                                                                       *
+ * Dimension is distributed in the hope that it will be useful, but      *
+ * WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ * General Public License for more details.                              *
+ *                                                                       *
+ * You should have received a copy of the GNU General Public License     *
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>. *
+ *************************************************************************/
+
+%option reentrant stack yylineno noyywrap
+
+%{
+#define YY_DECL static int yylex(const char *filename, dmnsn_array *tokens, \
+                                 yyscan_t yyscanner)
+#include "tokenize.h"
+#include "utility.h"
+#include <stdlib.h>
+#include <stdio.h>
+%}
+
+%x DMNSN_BLOCK_COMMENT
+%x DMNSN_LINE_COMMENT
+%x DMNSN_STRING
+%x DMNSN_STRING_ESCAPE
+
+%%
+
+%{
+/* Some helpful macros that set fields of a token correctly, and other stuff */
+
+#define NEW_TOKEN(token_type)                   \
+  do {                                          \
+    token.type     = token_type;                \
+    token.filename = filename;                  \
+    token.line     = yylineno;                  \
+    token.col      = yycolumn;                  \
+    token.value    = NULL;                      \
+  } while (0)
+
+#define CALCULATE_COLUMN() yycolumn += yyleng
+
+#define PUSH()                                  \
+  do {                                          \
+    dmnsn_array_push(tokens, &token);           \
+    CALCULATE_COLUMN();                         \
+  } while (0)
+
+#define PUSH_TOKEN(token_type)                  \
+  do {                                          \
+    NEW_TOKEN(token_type);                      \
+    PUSH();                                     \
+  } while (0)
+
+#define PUSH_VALUE_TOKEN(token_type)            \
+  do {                                          \
+    NEW_TOKEN(token_type);                      \
+    token.value    = strdup(yytext);            \
+    PUSH();                                     \
+  } while (0)
+
+#define STRING_TOKEN()                          \
+  do {                                          \
+    NEW_TOKEN(DMNSN_T_STRING);                  \
+    token.value    = malloc(1);                 \
+    token.value[0] = '\0';                      \
+    string_length  = 0;                         \
+    CALCULATE_COLUMN();                         \
+  } while (0)
+
+#define STRCAT(str, len)                                                \
+  do {                                                                  \
+    token.value = realloc(token.value, string_length + len + 1);        \
+    strncpy(token.value + string_length, str, len);                     \
+    string_length += len;                                               \
+    CALCULATE_COLUMN();                                                 \
+  } while(0)
+
+dmnsn_token token;
+size_t string_length;
+unsigned long wchar;
+%}
+
+(?# Comments)
+
+<INITIAL,DMNSN_BLOCK_COMMENT>"/*"       {
+  yy_push_state(DMNSN_BLOCK_COMMENT, yyscanner);
+  CALCULATE_COLUMN();
+}
+<DMNSN_BLOCK_COMMENT>"*/"       CALCULATE_COLUMN(); yy_pop_state(yyscanner);
+<DMNSN_BLOCK_COMMENT>[^*/\n]*   CALCULATE_COLUMN();
+<DMNSN_BLOCK_COMMENT>"/"        CALCULATE_COLUMN();
+<DMNSN_BLOCK_COMMENT>"*"        CALCULATE_COLUMN();
+<DMNSN_BLOCK_COMMENT>\n         ;
+
+"//"    {
+  yy_push_state(DMNSN_LINE_COMMENT, yyscanner);
+  CALCULATE_COLUMN();
+}
+<DMNSN_LINE_COMMENT>\n          ; yy_pop_state(yyscanner);
+<DMNSN_LINE_COMMENT>[^\n]+      CALCULATE_COLUMN();
+
+(?# Punctuation)
+"{"     PUSH_TOKEN(DMNSN_T_LBRACE);
+"}"     PUSH_TOKEN(DMNSN_T_RBRACE);
+"("     PUSH_TOKEN(DMNSN_T_LPAREN);
+")"     PUSH_TOKEN(DMNSN_T_RPAREN);
+"["     PUSH_TOKEN(DMNSN_T_LBRACKET);
+"]"     PUSH_TOKEN(DMNSN_T_RBRACKET);
+"+"     PUSH_TOKEN(DMNSN_T_PLUS);
+"-"     PUSH_TOKEN(DMNSN_T_MINUS);
+"*"     PUSH_TOKEN(DMNSN_T_STAR);
+"/"     PUSH_TOKEN(DMNSN_T_SLASH);
+","     PUSH_TOKEN(DMNSN_T_COMMA);
+";"     PUSH_TOKEN(DMNSN_T_SEMICOLON);
+"?"     PUSH_TOKEN(DMNSN_T_QUESTION);
+":"     PUSH_TOKEN(DMNSN_T_COLON);
+"&"     PUSH_TOKEN(DMNSN_T_AND);
+"."     PUSH_TOKEN(DMNSN_T_DOT);
+"|"     PUSH_TOKEN(DMNSN_T_PIPE);
+"<"     PUSH_TOKEN(DMNSN_T_LESS);
+">"     PUSH_TOKEN(DMNSN_T_GREATER);
+"!"     PUSH_TOKEN(DMNSN_T_BANG);
+"="     PUSH_TOKEN(DMNSN_T_EQUALS);
+"<="    PUSH_TOKEN(DMNSN_T_LESS_EQUAL);
+">="    PUSH_TOKEN(DMNSN_T_GREATER_EQUAL);
+"!="    PUSH_TOKEN(DMNSN_T_NOT_EQUAL);
+
+(?# Integers)
+[[:digit:]]+                    |
+0(x|X)[[:digit:]aAbBcCdDeEfF]+  PUSH_VALUE_TOKEN(DMNSN_T_INTEGER);
+
+(?# Floats)
+[[:digit:]]*\.?[[:digit:]]+((e|E)(\+|-)?[[:digit:]]+)?  {
+  PUSH_VALUE_TOKEN(DMNSN_T_FLOAT);
+}
+
+(?# Keywords)
+"box"           PUSH_TOKEN(DMNSN_T_BOX);
+"camera"        PUSH_TOKEN(DMNSN_T_CAMERA);
+"color"         PUSH_TOKEN(DMNSN_T_COLOR);
+"colour"        PUSH_TOKEN(DMNSN_T_COLOR);
+"sphere"        PUSH_TOKEN(DMNSN_T_SPHERE);
+
+(?# Directives)
+"#include"      PUSH_TOKEN(DMNSN_T_INCLUDE);
+"#declare"      PUSH_TOKEN(DMNSN_T_DECLARE);
+
+(?# Identifiers)
+[[:alpha:]][[:alnum:]_]*        PUSH_VALUE_TOKEN(DMNSN_T_IDENTIFIER);
+
+(?# Strings)
+
+"\""    STRING_TOKEN(); yy_push_state(DMNSN_STRING, yyscanner);
+<DMNSN_STRING>[^\\\"\n]*        STRCAT(yytext, yyleng);
+<DMNSN_STRING>"\""              PUSH(); yy_pop_state(yyscanner);
+
+(?# String escape sequences)
+
+<DMNSN_STRING>"\\"      {
+  yy_push_state(DMNSN_STRING_ESCAPE, yyscanner);
+  CALCULATE_COLUMN();
+}
+<DMNSN_STRING_ESCAPE>"a"        STRCAT("\a", 1); yy_pop_state(yyscanner);
+<DMNSN_STRING_ESCAPE>"b"        STRCAT("\b", 1); yy_pop_state(yyscanner);
+<DMNSN_STRING_ESCAPE>"f"        STRCAT("\f", 1); yy_pop_state(yyscanner);
+<DMNSN_STRING_ESCAPE>"n"        STRCAT("\n", 1); yy_pop_state(yyscanner);
+<DMNSN_STRING_ESCAPE>"r"        STRCAT("\r", 1); yy_pop_state(yyscanner);
+<DMNSN_STRING_ESCAPE>"t"        STRCAT("\t", 1); yy_pop_state(yyscanner);
+<DMNSN_STRING_ESCAPE>"v"        STRCAT("\v", 1); yy_pop_state(yyscanner);
+<DMNSN_STRING_ESCAPE>"\\"       STRCAT("\\", 1); yy_pop_state(yyscanner);
+<DMNSN_STRING_ESCAPE>"'"        STRCAT("'", 1); yy_pop_state(yyscanner);
+<DMNSN_STRING_ESCAPE>"\""       STRCAT("\"", 1); yy_pop_state(yyscanner);
+<DMNSN_STRING_ESCAPE>"u"[[:digit:]aAbBcCdDeEfF]{4}      {
+  wchar = strtoul(yytext + 1, NULL, 16);
+  STRCAT("", 2);
+  token.value[string_length - 2] = wchar/256;
+  token.value[string_length - 1] = wchar%256;
+  yy_pop_state(yyscanner);
+}
+<DMNSN_STRING_ESCAPE>.          {
+  dmnsn_diagnostic(filename, yylineno, yycolumn,
+                   "WARNING: unrecognised escape sequence '\\%c'",
+                   (int)*yytext);
+  STRCAT(yytext, yyleng);
+  yy_pop_state(yyscanner);
+}
+
+(?# Ignore whitespace)
+[\b\r\t\v ]+    CALCULATE_COLUMN();
+\n              ;
+
+(?# Fall-through)
+.       {
+  dmnsn_diagnostic(filename, yylineno, yycolumn,
+                   "Unrecognized character '%c' (0x%X)",
+                   (int)*yytext, (unsigned int)*yytext);
+  return 1;
+}
+
+%%
+
+dmnsn_array *
+dmnsn_tokenize(const char *filename, FILE *file)
+{
+  dmnsn_array *tokens = dmnsn_new_array(sizeof(dmnsn_token));
+
+  yyscan_t scanner;
+
+  yylex_init(&scanner);
+  yyset_in(file, scanner);
+
+  if (yylex(filename, tokens, scanner) != 0) {
+    dmnsn_delete_tokens(tokens);
+    tokens = NULL;
+  }
+
+  yylex_destroy(scanner);
+
+  return tokens;
+}
+
+void
+dmnsn_delete_tokens(dmnsn_array *tokens)
+{
+  dmnsn_token *token;
+  unsigned int i;
+  for (i = 0; i < dmnsn_array_size(tokens); ++i) {
+    token = dmnsn_array_at(tokens, i);
+    free(token->value);
+  }
+  dmnsn_delete_array(tokens);
+}
+
+static void
+dmnsn_print_token(FILE *file, dmnsn_token token)
+{
+  const char *tname;
+  if (token.type == DMNSN_T_LPAREN) {
+    tname = "\\(";
+  } else if (token.type == DMNSN_T_RPAREN) {
+    tname = "\\)";
+  } else {
+    tname = dmnsn_token_string(token.type);
+  }
+
+  if (token.value) {
+    fprintf(file, "(%s \"%s\")", tname, token.value);
+  } else {
+    fprintf(file, "%s", tname);
+  }
+}
+
+void
+dmnsn_print_token_sexpr(FILE *file, const dmnsn_array *tokens)
+{
+  dmnsn_token token;
+  unsigned int i;
+
+  if (dmnsn_array_size(tokens) == 0) {
+    fprintf(file, "()");
+  } else {
+    fprintf(file, "(");
+    dmnsn_array_get(tokens, 0, &token);
+    dmnsn_print_token(file, token);
+
+    for (i = 1; i < dmnsn_array_size(tokens); ++i) {
+      fprintf(file, " ");
+      dmnsn_array_get(tokens, i, &token);
+      dmnsn_print_token(file, token);
+    }
+
+    fprintf(file, ")");
+  }
+
+  fprintf(file, "\n");
+}
diff --git a/dimension/parse.c b/dimension/parse.c
deleted file mode 100644
index 89bbd6a..0000000
--- a/dimension/parse.c
+++ /dev/null
@@ -1,803 +0,0 @@
-/*************************************************************************
- * Copyright (C) 2009 Tavian Barnes <tavianator@gmail.com>               *
- *                                                                       *
- * This file is part of Dimension.                                       *
- *                                                                       *
- * Dimension is free software; you can redistribute it and/or modify it  *
- * under the terms of the GNU General Public License as published by the *
- * Free Software Foundation; either version 3 of the License, or (at     *
- * your option) any later version.                                       *
- *                                                                       *
- * Dimension is distributed in the hope that it will be useful, but      *
- * WITHOUT ANY WARRANTY; without even the implied warranty of            *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
- * General Public License for more details.                              *
- *                                                                       *
- * You should have received a copy of the GNU General Public License     *
- * along with this program.  If not, see <http://www.gnu.org/licenses/>. *
- *************************************************************************/
-
-#include "parse.h"
-#include "tokenize.h"
-#include "utility.h"
-#include <stdlib.h>
-#include <stdio.h>
-#include <locale.h>
-
-/* Create a new astnode, populating filename, line, and col from a token */
-static dmnsn_astnode
-dmnsn_new_astnode(dmnsn_astnode_type type, dmnsn_token token)
-{
-  dmnsn_astnode astnode = {
-    .type = type,
-    .children = dmnsn_new_array(sizeof(dmnsn_astnode)),
-    .ptr = NULL,
-    .filename = token.filename, .line = token.line, .col = token.col
-  };
-  return astnode;
-}
-
-/* Delete a single, unused astnode */
-static void
-dmnsn_delete_astnode(dmnsn_astnode astnode)
-{
-  dmnsn_delete_astree(astnode.children);
-  free(astnode.ptr);
-}
-
-/* Expect a particular token next, and complain if we see something else */
-static int
-dmnsn_parse_expect(dmnsn_token_type type, const dmnsn_array *tokens,
-                   unsigned int *ip)
-{
-  dmnsn_token token;
-
-  if (*ip < dmnsn_array_size(tokens)) {
-    dmnsn_array_get(tokens, *ip, &token);
-    if (token.type != type) {
-      dmnsn_diagnostic(token.filename, token.line, token.col,
-                       "Expected '%s', found '%s'",
-                       dmnsn_token_string(type),
-                       dmnsn_token_string(token.type));
-      return 1;
-    }
-  } else {
-    fprintf(stderr, "Expected '%s', found end of file\n",
-            dmnsn_token_string(type));
-    return 1;
-  }
-
-  ++*ip;
-  return 0;
-}
-
-/* Parse a number - a float or an integer */
-static int
-dmnsn_parse_number(const dmnsn_array *tokens, unsigned int *ip,
-                   dmnsn_array *astree)
-{
-  unsigned int i = *ip;
-
-  if (i >= dmnsn_array_size(tokens)) {
-    fprintf(stderr, "Expected '%s' or '%s', found end of file\n",
-            dmnsn_token_string(DMNSN_T_INTEGER),
-            dmnsn_token_string(DMNSN_T_FLOAT));
-    return 1;
-  }
-
-  dmnsn_token token;
-  dmnsn_array_get(tokens, i, &token);
-
-  dmnsn_astnode astnode;
-
-  if (token.type == DMNSN_T_INTEGER) {
-    /* An exact integer */
-    astnode = dmnsn_new_astnode(DMNSN_AST_INTEGER, token);
-
-    long *value = malloc(sizeof(double));
-    if (!value)
-      dmnsn_error(DMNSN_SEVERITY_HIGH, "Couldn't allocate room for integer.");
-
-    *value = strtol(token.value, NULL, 0);
-    astnode.ptr = value;
-    ++i;
-  } else if (token.type == DMNSN_T_FLOAT) {
-    /* A floating-point value */
-    astnode = dmnsn_new_astnode(DMNSN_AST_FLOAT, token);
-
-    double *value = malloc(sizeof(double));
-    if (!value)
-      dmnsn_error(DMNSN_SEVERITY_HIGH, "Couldn't allocate room for float.");
-
-    *value = strtod(token.value, NULL);
-    astnode.ptr = value;
-    ++i;
-  } else {
-    dmnsn_diagnostic(token.filename, token.line, token.col,
-                     "Expected '%s' or '%s', found '%s'",
-                     dmnsn_token_string(DMNSN_T_INTEGER),
-                     dmnsn_token_string(DMNSN_T_FLOAT),
-                     dmnsn_token_string(token.type));
-    goto bailout;
-  }
-
-  dmnsn_array_push(astree, &astnode);
-  *ip = i;
-  return 0;
-
- bailout:
-  dmnsn_delete_astree(astnode.children);
-  return 1;
-}
-
-/* Map an operator token to a unary operator node type */
-static dmnsn_astnode_type
-dmnsn_unary_op_map(dmnsn_token_type type)
-{
-  switch (type) {
-  case DMNSN_T_MINUS:
-    return DMNSN_AST_NEGATE;
-
-  default:
-    dmnsn_error(DMNSN_SEVERITY_HIGH, "Invalid token mapped to operator.");
-    return 0; /* Silence compiler warning */
-  }
-}
-
-/* Map an operator token to a binary operator node type */
-static dmnsn_astnode_type
-dmnsn_binary_op_map(dmnsn_token_type type)
-{
-  switch (type) {
-  case DMNSN_T_PLUS:
-    return DMNSN_AST_ADD;
-
-  case DMNSN_T_MINUS:
-    return DMNSN_AST_SUB;
-
-  case DMNSN_T_STAR:
-    return DMNSN_AST_MUL;
-
-  case DMNSN_T_SLASH:
-    return DMNSN_AST_DIV;
-
-  default:
-    dmnsn_error(DMNSN_SEVERITY_HIGH, "Invalid token mapped to operator.");
-    return 0; /* Silence compiler warning */
-  }
-}
-
-/* Return the precedence of a given operator */
-static int
-dmnsn_op_precedence(dmnsn_astnode_type type)
-{
-  switch(type) {
-  case DMNSN_AST_ADD:
-  case DMNSN_AST_SUB:
-    return 0;
-
-  case DMNSN_AST_MUL:
-  case DMNSN_AST_DIV:
-    return 1;
-
-  default:
-    dmnsn_error(DMNSN_SEVERITY_HIGH,
-                "Precedence asked of invalid operator.");
-    return 0; /* Silence compiler warning */
-  }
-}
-
-/* Erhsuate a unnary arithmetic operation */
-static dmnsn_astnode
-dmnsn_eval_unary(dmnsn_astnode_type type, dmnsn_astnode rhs)
-{
-  dmnsn_astnode astnode = rhs; /* Copy filename, etc. from rhs */
-  astnode.children = NULL;
-
-  if (rhs.type == DMNSN_AST_INTEGER) {
-    long n = *(long *)rhs.ptr;
-    long *res = malloc(sizeof(long));
-
-    switch(type) {
-    case DMNSN_AST_NEGATE:
-      *res = -n;
-      break;
-
-    default:
-      dmnsn_error(DMNSN_SEVERITY_HIGH,
-                  "Attempt to evaluate wrong unary operator.");
-    }
-
-    astnode.type = DMNSN_AST_INTEGER;
-    astnode.ptr  = res;
-  } else if (rhs.type == DMNSN_AST_FLOAT) {
-    double n = *(double *)rhs.ptr;
-    double *res = malloc(sizeof(double));
-
-    switch(type) {
-    case DMNSN_AST_NEGATE:
-      *res = -n;
-      break;
-
-    default:
-      dmnsn_error(DMNSN_SEVERITY_HIGH,
-                  "Attempt to evaluate wrong unary operator.");
-    }
-
-    astnode.type = DMNSN_AST_FLOAT;
-    astnode.ptr  = res;
-  } else {
-    dmnsn_error(DMNSN_SEVERITY_HIGH,
-                "Invalid right hand side to unary operator.");
-  }
-
-  return astnode;
-}
-
-/* Evaluate a binary arithmetic operation */
-static dmnsn_astnode
-dmnsn_eval_binary(dmnsn_astnode_type type, dmnsn_astnode lhs, dmnsn_astnode rhs)
-{
-  dmnsn_astnode astnode = lhs; /* Copy filename, etc. from lhs */
-  astnode.children = NULL;
-
-  if (lhs.type == DMNSN_AST_INTEGER && rhs.type == DMNSN_AST_INTEGER
-      && type != DMNSN_AST_DIV) {
-    long l, r;
-    l = *(long *)lhs.ptr;
-    r = *(long *)rhs.ptr;
-
-    long *res = malloc(sizeof(long));
-
-    switch(type) {
-    case DMNSN_AST_ADD:
-      *res = l + r;
-      break;
-    case DMNSN_AST_SUB:
-      *res = l - r;
-      break;
-    case DMNSN_AST_MUL:
-      *res = l*r;
-      break;
-    case DMNSN_AST_DIV:
-      *res = l/r;
-      break;
-
-    default:
-      dmnsn_error(DMNSN_SEVERITY_HIGH,
-                  "Attempt to evaluate wrong binary operator.");
-    }
-
-    astnode.type = DMNSN_AST_INTEGER;
-    astnode.ptr  = res;
-  } else {
-    double l = 0.0, r = 0.0;
-
-    if (lhs.type == DMNSN_AST_INTEGER)
-      l = *(long *)lhs.ptr;
-    else if (lhs.type == DMNSN_AST_FLOAT)
-      l = *(double *)lhs.ptr;
-    else
-      dmnsn_error(DMNSN_SEVERITY_HIGH,
-                  "Invalid left hand side to binary operator.");
-
-    if (rhs.type == DMNSN_AST_INTEGER)
-      r = *(long *)rhs.ptr;
-    else if (rhs.type == DMNSN_AST_FLOAT)
-      r = *(double *)rhs.ptr;
-    else
-      dmnsn_error(DMNSN_SEVERITY_HIGH,
-                  "Invalid right hand side to binary operator.");
-
-    double *res = malloc(sizeof(double));
-
-    switch(type) {
-    case DMNSN_AST_ADD:
-      *res = l + r;
-      break;
-    case DMNSN_AST_SUB:
-      *res = l - r;
-      break;
-    case DMNSN_AST_MUL:
-      *res = l*r;
-      break;
-    case DMNSN_AST_DIV:
-      *res = l/r;
-      break;
-
-    default:
-      dmnsn_error(DMNSN_SEVERITY_HIGH,
-                  "Attempt to evaluate wrong binary operator.");
-    }
-
-    astnode.type = DMNSN_AST_FLOAT;
-    astnode.ptr  = res;
-  }
-
-  return astnode;
-}
-
-/* Parse an arithmetic expression */
-static int
-dmnsn_parse_arithexp(const dmnsn_array *tokens, unsigned int *ip,
-                     dmnsn_array *astree)
-{
-  /*
-   * We use a stack of numbers (numstack), and a stack of operators (opstack).
-   * Each time we see a number (or a parenthetical sub-expression, which is
-   * evaluated recursively), it goes on the numstack.  When we see an operator,
-   * it goes on the opstack, but if it has the same or lower precedence than
-   * the operator below it on the stack, the two top elements of numstack are
-   * folded with the correct operator until either opstack is empty or the
-   * current operator has a higher precedence.  Example:
-   *
-   *   1 - 2 * 3 - 4 * 5 --> ... - 4 * 5 -->  ... - 4 * 5   --> ... - 4 * 5
-   *                         _____       -->  ___________   --> ___________
-   *                         3 | *            6       | -       -5      |
-   *                         2 | -            1       |
-   *                         1 |
-   *
-   *   --> ...
-   *   --> ______
-   *       5  | *
-   *       4  | -
-   *       -5 |
-   *
-   * Then the stack is collapsed from the top down until we have a single value:
-   *
-   *   --> _________________ --> -25
-   *       20 | -
-   *       -5 |
-   */
-  dmnsn_token token;
-  unsigned int i = *ip;
-
-  if (i >= dmnsn_array_size(tokens)) {
-    fprintf(stderr, "Expected arithmetic expression, found end of file\n");
-    return 1;
-  }
-
-  dmnsn_array *numstack = dmnsn_new_array(sizeof(dmnsn_astnode)),
-              *opstack  = dmnsn_new_array(sizeof(dmnsn_astnode_type));
-
-  while (i < dmnsn_array_size(tokens)) {
-    dmnsn_array_get(tokens, i, &token);
-
-    if (token.type == DMNSN_T_INTEGER || token.type == DMNSN_T_FLOAT) {
-      /* Item is a number */
-      dmnsn_parse_number(tokens, &i, numstack);
-    } else if (token.type == DMNSN_T_LPAREN) {
-      ++i; /* Skip past '(' */
-
-      /* Parse the contents of the parentheses */
-      if (dmnsn_parse_arithexp(tokens, &i, numstack) != 0)
-        goto bailout;
-
-      /* Match a closing ')' */
-      if (dmnsn_parse_expect(DMNSN_T_RPAREN, tokens, &i) != 0)
-        goto bailout;
-    } else if (token.type == DMNSN_T_PLUS
-               || token.type == DMNSN_T_MINUS
-               || token.type == DMNSN_T_STAR
-               || token.type == DMNSN_T_SLASH)
-
-    {
-      /* Item is an operator */
-      if (dmnsn_array_size(opstack) == dmnsn_array_size(numstack)) {
-        /* The last item was an operator too, or this is the first item, so it
-           must be a unary operator */
-        if (token.type == DMNSN_T_MINUS) {
-          dmnsn_astnode_type type = dmnsn_unary_op_map(token.type);
-          dmnsn_astnode astnode = dmnsn_new_astnode(type, token);
-
-          ++i;
-          dmnsn_array_get(tokens, i, &token);
-          if (token.type == DMNSN_T_LPAREN) {
-            if (dmnsn_parse_arithexp(tokens, &i, astnode.children) != 0)
-              goto bailout;
-          } else {
-            if (dmnsn_parse_number(tokens, &i, astnode.children) != 0)
-              goto bailout;
-          }
-
-          dmnsn_astnode rhs;
-          dmnsn_array_get(astnode.children, 0, &rhs);
-
-          if (rhs.type == DMNSN_AST_INTEGER || rhs.type == DMNSN_AST_FLOAT) {
-            dmnsn_astnode neg = dmnsn_eval_unary(DMNSN_AST_NEGATE, rhs);
-            dmnsn_array_push(numstack, &neg);
-            dmnsn_delete_astnode(astnode);
-          } else {
-            dmnsn_array_push(numstack, &astnode);
-          }
-        } else {
-          dmnsn_diagnostic(token.filename, token.line, token.col,
-                           "Unexpected '%s' when parsing arithmetic expression",
-                           dmnsn_token_string(token.type));
-          return 1;
-        }
-      } else if (dmnsn_array_size(opstack) == 0) {
-        /* opstack is empty; push the operator */
-        dmnsn_astnode_type type = dmnsn_binary_op_map(token.type);
-        dmnsn_array_push(opstack, &type);
-        ++i;
-      } else {
-        dmnsn_astnode_type type = dmnsn_binary_op_map(token.type), last_type;
-        dmnsn_array_get(opstack, dmnsn_array_size(opstack) - 1, &last_type);
-
-        while (dmnsn_op_precedence(type) <= dmnsn_op_precedence(last_type)) {
-          /* Repeatedly collapse numstack */
-          dmnsn_astnode lhs, rhs, astnode;
-
-          dmnsn_array_pop(numstack, &rhs);
-          dmnsn_array_pop(numstack, &lhs);
-
-          if ((lhs.type == DMNSN_AST_INTEGER || lhs.type == DMNSN_AST_FLOAT)
-              && (rhs.type == DMNSN_AST_INTEGER || rhs.type == DMNSN_AST_FLOAT))
-          {
-            astnode = dmnsn_eval_binary(last_type, lhs, rhs);
-            dmnsn_delete_astnode(lhs);
-            dmnsn_delete_astnode(rhs);
-          } else {
-            astnode = dmnsn_new_astnode(last_type, token);
-            dmnsn_array_push(astnode.children, &lhs);
-            dmnsn_array_push(astnode.children, &rhs);
-          }
-
-          dmnsn_array_push(numstack, &astnode);
-          dmnsn_array_resize(opstack, dmnsn_array_size(opstack) - 1);
-
-          if (dmnsn_array_size(opstack) > 0)
-            dmnsn_array_get(opstack, dmnsn_array_size(opstack) - 1, &last_type);
-          else
-            break;
-        }
-
-        dmnsn_array_push(opstack, &type);
-        ++i;
-      }
-    } else {
-      /* Unrecognized token; arithmetic expression must be over */
-      break;
-    }
-  }
-
-  /* Prevent us from matching nothing */
-  if (dmnsn_array_size(numstack) == 0) {
-    dmnsn_diagnostic(token.filename, token.line, token.col,
-                     "Expected arithmetic expression, found '%s'",
-                     dmnsn_token_string(token.type));
-    goto bailout;
-  }
-
-  while (dmnsn_array_size(numstack) > 1) {
-    /* Collapse numstack */
-    dmnsn_astnode_type type;
-    dmnsn_array_pop(opstack, &type);
-
-    dmnsn_astnode astnode, lhs, rhs;
-
-    dmnsn_array_pop(numstack, &rhs);
-    dmnsn_array_pop(numstack, &lhs);
-
-    if ((lhs.type == DMNSN_AST_INTEGER || lhs.type == DMNSN_AST_FLOAT)
-        && (rhs.type == DMNSN_AST_INTEGER || rhs.type == DMNSN_AST_FLOAT)) {
-      astnode = dmnsn_eval_binary(type, lhs, rhs);
-      dmnsn_delete_astnode(lhs);
-      dmnsn_delete_astnode(rhs);
-    } else {
-      astnode = lhs; /* Steal filname, etc. from lhs */
-      astnode.type     = type;
-      astnode.children = dmnsn_new_array(sizeof(dmnsn_astnode));
-      astnode.ptr      = NULL;
-
-      dmnsn_array_push(astnode.children, &lhs);
-      dmnsn_array_push(astnode.children, &rhs);
-    }
-
-    dmnsn_array_push(numstack, &astnode);
-  }
-
-  dmnsn_array_push(astree, dmnsn_array_at(numstack, 0));
-  dmnsn_delete_array(opstack);
-  dmnsn_delete_array(numstack);
-  *ip = i;
-  return 0;
-
- bailout:
-  dmnsn_delete_array(opstack);
-  dmnsn_delete_astree(numstack);
-  return 1;
-}
-
-/* Parse a vector */
-static int
-dmnsn_parse_vector(const dmnsn_array *tokens, unsigned int *ip,
-                   dmnsn_array *astree)
-{
-  dmnsn_token token;
-  unsigned int i = *ip;
-
-  dmnsn_array_get(tokens, i, &token);
-
-  /* Vectors start with '<' */
-  if (dmnsn_parse_expect(DMNSN_T_LESS, tokens, &i) != 0)
-    return 1;
-
-  dmnsn_astnode astnode = dmnsn_new_astnode(DMNSN_AST_VECTOR, token);
-
-  do {
-    /* Parse comma-separated aritexp's, closed by '>' */
-    if (dmnsn_parse_arithexp(tokens, &i, astnode.children) != 0)
-      goto bailout;
-
-    dmnsn_array_get(tokens, i, &token);
-    if (token.type != DMNSN_T_COMMA && token.type != DMNSN_T_GREATER) {
-      dmnsn_diagnostic(token.filename, token.line, token.col,
-                       "Expected '%s' or '%s'; found '%s'",
-                       dmnsn_token_string(DMNSN_T_COMMA),
-                       dmnsn_token_string(DMNSN_T_GREATER),
-                       dmnsn_token_string(token.type));
-      goto bailout;
-    }
-
-    ++i;
-  } while (token.type != DMNSN_T_GREATER);
-
-  dmnsn_array_push(astree, &astnode);
-  *ip = i;
-  return 0;
-
- bailout:
-  dmnsn_delete_astree(astnode.children);
-  return 1;
-}
-
-/* Parse a box definition */
-static int
-dmnsn_parse_box(const dmnsn_array *tokens, unsigned int *ip,
-                dmnsn_array *astree)
-{
-  dmnsn_token token;
-  unsigned int i = *ip;
-
-  dmnsn_array_get(tokens, i, &token);
-
-  /* Opens with "box {" */
-  if (dmnsn_parse_expect(DMNSN_T_BOX, tokens, &i) != 0)
-    return 1;
-  if (dmnsn_parse_expect(DMNSN_T_LBRACE, tokens, &i) != 0)
-    return 1;
-
-  dmnsn_astnode astnode = dmnsn_new_astnode(DMNSN_AST_BOX, token);
-
-  /* First corner */
-  if (dmnsn_parse_vector(tokens, &i, astnode.children) != 0)
-    goto bailout;
-
-  if (dmnsn_parse_expect(DMNSN_T_COMMA, tokens, &i) != 0)
-    goto bailout;
-
-  /* Second corner */
-  if (dmnsn_parse_vector(tokens, &i, astnode.children) != 0)
-    goto bailout;
-
-  if (dmnsn_parse_expect(DMNSN_T_RBRACE, tokens, &i) != 0)
-    goto bailout;
-
-  dmnsn_array_push(astree, &astnode);
-  *ip = i;
-  return 0;
-
- bailout:
-  dmnsn_delete_astree(astnode.children);
-  return 1;
-}
-
-/* Parse a sphere definition */
-static int
-dmnsn_parse_sphere(const dmnsn_array *tokens, unsigned int *ip,
-                   dmnsn_array *astree)
-{
-  dmnsn_token token;
-  unsigned int i = *ip;
-
-  dmnsn_array_get(tokens, i, &token);
-
-  /* Opens with "sphere {" */
-  if (dmnsn_parse_expect(DMNSN_T_SPHERE, tokens, &i) != 0)
-    return 1;
-  if (dmnsn_parse_expect(DMNSN_T_LBRACE, tokens, &i) != 0)
-    return 1;
-
-  dmnsn_astnode astnode = dmnsn_new_astnode(DMNSN_AST_SPHERE, token);
-
-  /* Center */
-  if (dmnsn_parse_vector(tokens, &i, astnode.children) != 0)
-    goto bailout;
-
-  if (dmnsn_parse_expect(DMNSN_T_COMMA, tokens, &i) != 0)
-    goto bailout;
-
-  /* Radius */
-  if (dmnsn_parse_number(tokens, &i, astnode.children) != 0)
-    goto bailout;
-
-  if (dmnsn_parse_expect(DMNSN_T_RBRACE, tokens, &i) != 0)
-    goto bailout;
-
-  dmnsn_array_push(astree, &astnode);
-  *ip = i;
-  return 0;
-
- bailout:
-  dmnsn_delete_astree(astnode.children);
-  return 1;
-}
-
-dmnsn_array *
-dmnsn_parse(const dmnsn_array *tokens)
-{
-  unsigned int i;
-  dmnsn_array *astree = dmnsn_new_array(sizeof(dmnsn_astnode));
-  dmnsn_token token;
-
-  /* Save the current locale */
-  char *lc_ctype   = strdup(setlocale(LC_CTYPE, NULL));
-  char *lc_numeric = strdup(setlocale(LC_NUMERIC, NULL));
-
-  /* Set the locale to `C' to make strtol(), etc. consistent */
-  setlocale(LC_CTYPE, "C");
-  setlocale(LC_NUMERIC, "C");
-
-  i = 0;
-  while (i < dmnsn_array_size(tokens)) {
-    dmnsn_array_get(tokens, i, &token);
-
-    switch (token.type) {
-    case DMNSN_T_BOX:
-      if (dmnsn_parse_box(tokens, &i, astree) != 0)
-        goto bailout;
-      break;
-
-    case DMNSN_T_SPHERE:
-      if (dmnsn_parse_sphere(tokens, &i, astree) != 0)
-        goto bailout;
-      break;
-
-    default:
-      dmnsn_diagnostic(token.filename, token.line, token.col,
-                       "Unexpected token '%s'",
-                       dmnsn_token_string(token.type));
-      goto bailout;
-    }
-  }
-
-  /* Restore the original locale */
-  setlocale(LC_CTYPE, lc_ctype);
-  setlocale(LC_NUMERIC, lc_numeric);
-  free(lc_ctype);
-  free(lc_numeric);
-
-  return astree;
-
- bailout:
-  /* Restore the original locale */
-  setlocale(LC_CTYPE, lc_ctype);
-  setlocale(LC_NUMERIC, lc_numeric);
-  free(lc_ctype);
-  free(lc_numeric);
-
-  dmnsn_delete_astree(astree);
-  return NULL;
-}
-
-void
-dmnsn_delete_astree(dmnsn_array *astree)
-{
-  unsigned int i;
-  dmnsn_astnode node;
-
-  if (astree) {
-    for (i = 0; i < dmnsn_array_size(astree); ++i) {
-      dmnsn_array_get(astree, i, &node);
-      dmnsn_delete_astree(node.children);
-      free(node.ptr);
-    }
-    dmnsn_delete_array(astree);
-  }
-}
-
-static void
-dmnsn_print_astnode(FILE *file, dmnsn_astnode astnode)
-{
-  long ivalue;
-  double dvalue;
-
-  switch (astnode.type) {
-  case DMNSN_AST_INTEGER:
-    ivalue = *(long *)astnode.ptr;
-    fprintf(file, "(%s %ld)", dmnsn_astnode_string(astnode.type), ivalue);
-    break;
-
-  case DMNSN_AST_FLOAT:
-    dvalue = *(double *)astnode.ptr;
-    fprintf(file, "(%s %g)", dmnsn_astnode_string(astnode.type), dvalue);
-    break;
-
-  default:
-    fprintf(file, "%s", dmnsn_astnode_string(astnode.type));
-  }
-}
-
-static void
-dmnsn_print_astree(FILE *file, dmnsn_astnode astnode)
-{
-  unsigned int i;
-  dmnsn_astnode child;
-
-  if (astnode.children && dmnsn_array_size(astnode.children) > 0) {
-    fprintf(file, "(");
-    dmnsn_print_astnode(file, astnode);
-    for (i = 0; i < dmnsn_array_size(astnode.children); ++i) {
-      dmnsn_array_get(astnode.children, i, &child);
-      fprintf(file, " ");
-      dmnsn_print_astree(file, child);
-    }
-    fprintf(file, ")");
-  } else {
-    dmnsn_print_astnode(file, astnode);
-  }
-}
-
-void
-dmnsn_print_astree_sexpr(FILE *file, const dmnsn_array *astree)
-{
-  dmnsn_astnode astnode;
-  unsigned int i;
-
-  if (dmnsn_array_size(astree) == 0) {
-    fprintf(file, "()");
-  } else {
-    fprintf(file, "(");
-    dmnsn_array_get(astree, 0, &astnode);
-    dmnsn_print_astree(file, astnode);
-
-    for (i = 1; i < dmnsn_array_size(astree); ++i) {
-      fprintf(file, " ");
-      dmnsn_array_get(astree, i, &astnode);
-      dmnsn_print_astree(file, astnode);
-    }
-
-    fprintf(file, ")");
-  }
-
-  fprintf(file, "\n");
-}
-
-const char *
-dmnsn_astnode_string(dmnsn_astnode_type astnode_type)
-{
-  switch (astnode_type) {
-  /* Macro to shorten this switch */
-#define dmnsn_astnode_map(type, str)                                           \
-  case type:                                                                   \
-    return str;
-
-  dmnsn_astnode_map(DMNSN_AST_FLOAT, "float");
-  dmnsn_astnode_map(DMNSN_AST_INTEGER, "integer");
-  dmnsn_astnode_map(DMNSN_AST_NEGATE, "-");
-  dmnsn_astnode_map(DMNSN_AST_ADD, "+");
-  dmnsn_astnode_map(DMNSN_AST_SUB, "-");
-  dmnsn_astnode_map(DMNSN_AST_MUL, "*");
-  dmnsn_astnode_map(DMNSN_AST_DIV, "/");
-  dmnsn_astnode_map(DMNSN_AST_BOX, "box");
-  dmnsn_astnode_map(DMNSN_AST_VECTOR, "vector");
-  dmnsn_astnode_map(DMNSN_AST_SPHERE, "sphere");
-
-  default:
-    fprintf(stderr, "Warning: unrecognised astnode type %d.\n",
-            (int)astnode_type);
-    return "unrecognized-astnode";
-  }
-}
diff --git a/dimension/tokenize.h b/dimension/tokenize.h
index 14010f8..b9eaf04 100644
--- a/dimension/tokenize.h
+++ b/dimension/tokenize.h
@@ -21,501 +21,9 @@
 #define TOKENIZE_H
 
 #include "../libdimension/dimension.h"
+#include "bison.h"
 
-typedef enum {
-  /* Punctuation */
-  DMNSN_T_LBRACE,    /* { */
-  DMNSN_T_RBRACE,    /* } */
-  DMNSN_T_LPAREN,    /* ( */
-  DMNSN_T_RPAREN,    /* ) */
-  DMNSN_T_LBRACKET,  /* [ */
-  DMNSN_T_RBRACKET,  /* ] */
-  DMNSN_T_PLUS,      /* + */
-  DMNSN_T_MINUS,     /* - */
-  DMNSN_T_STAR,      /* * */
-  DMNSN_T_SLASH,     /* / */
-  DMNSN_T_COMMA,     /* , */
-  DMNSN_T_SEMICOLON, /* ; */
-  DMNSN_T_QUESTION,  /* ? */
-  DMNSN_T_COLON,     /* : */
-  DMNSN_T_AND,       /* & */
-  DMNSN_T_DOT,       /* . */
-  DMNSN_T_PIPE,      /* | */
-  DMNSN_T_LESS,      /* < */
-  DMNSN_T_GREATER,   /* > */
-  DMNSN_T_BANG,      /* ! */
-  DMNSN_T_EQUALS,    /* = */
-  DMNSN_T_LESS_EQUAL,    /* <= */
-  DMNSN_T_GREATER_EQUAL, /* >= */
-  DMNSN_T_NOT_EQUAL,     /* != */
-
-  /* Numeric values */
-  DMNSN_T_INTEGER,
-  DMNSN_T_FLOAT,
-
-  /* Keywords */
-  DMNSN_T_AA_LEVEL,
-  DMNSN_T_AA_THRESHOLD,
-  DMNSN_T_ABS,
-  DMNSN_T_ABSORPTION,
-  DMNSN_T_ACCURACY,
-  DMNSN_T_ACOS,
-  DMNSN_T_ACOSH,
-  DMNSN_T_ADAPTIVE,
-  DMNSN_T_ADC_BAILOUT,
-  DMNSN_T_AGATE,
-  DMNSN_T_AGATE_TURB,
-  DMNSN_T_ALL,
-  DMNSN_T_ALL_INTERSECTIONS,
-  DMNSN_T_ALPHA,
-  DMNSN_T_ALTITUDE,
-  DMNSN_T_ALWAYS_SAMPLE,
-  DMNSN_T_AMBIENT,
-  DMNSN_T_AMBIENT_LIGHT,
-  DMNSN_T_ANGLE,
-  DMNSN_T_APERTURE,
-  DMNSN_T_APPEND,
-  DMNSN_T_ARC_ANGLE,
-  DMNSN_T_AREA_LIGHT,
-  DMNSN_T_ARRAY,
-  DMNSN_T_ASC,
-  DMNSN_T_ASCII,
-  DMNSN_T_ASIN,
-  DMNSN_T_ASINH,
-  DMNSN_T_ASSUMED_GAMMA,
-  DMNSN_T_ATAN,
-  DMNSN_T_ATAN2,
-  DMNSN_T_ATANH,
-  DMNSN_T_AUTOSTOP,
-  DMNSN_T_AVERAGE,
-  DMNSN_T_B_SPLINE,
-  DMNSN_T_BACKGROUND,
-  DMNSN_T_BEZIER_SPLINE,
-  DMNSN_T_BICUBIC_PATCH,
-  DMNSN_T_BLACK_HOLE,
-  DMNSN_T_BLOB,
-  DMNSN_T_BLUE,
-  DMNSN_T_BLUR_SAMPLES,
-  DMNSN_T_BOUNDED_BY,
-  DMNSN_T_BOX,
-  DMNSN_T_BOXED,
-  DMNSN_T_BOZO,
-  DMNSN_T_BRICK,
-  DMNSN_T_BRICK_SIZE,
-  DMNSN_T_BRIGHTNESS,
-  DMNSN_T_BRILLIANCE,
-  DMNSN_T_BUMP_MAP,
-  DMNSN_T_BUMP_SIZE,
-  DMNSN_T_BUMPS,
-  DMNSN_T_CAMERA,
-  DMNSN_T_CAUSTICS,
-  DMNSN_T_CEIL,
-  DMNSN_T_CELLS,
-  DMNSN_T_CHARSET,
-  DMNSN_T_CHECKER,
-  DMNSN_T_CHR,
-  DMNSN_T_CIRCULAR,
-  DMNSN_T_CLIPPED_BY,
-  DMNSN_T_CLOCK,
-  DMNSN_T_CLOCK_DELTA,
-  DMNSN_T_CLOCK_ON,
-  DMNSN_T_COLLECT,
-  DMNSN_T_COLOR,
-  DMNSN_T_COLOR_MAP,
-  DMNSN_T_COMPONENT,
-  DMNSN_T_COMPOSITE,
-  DMNSN_T_CONCAT,
-  DMNSN_T_CONE,
-  DMNSN_T_CONFIDENCE,
-  DMNSN_T_CONIC_SWEEP,
-  DMNSN_T_CONSERVE_ENERGY,
-  DMNSN_T_CONTAINED_BY,
-  DMNSN_T_CONTROL0,
-  DMNSN_T_CONTROL1,
-  DMNSN_T_COORDS,
-  DMNSN_T_COS,
-  DMNSN_T_COSH,
-  DMNSN_T_COUNT,
-  DMNSN_T_CRACKLE,
-  DMNSN_T_CRAND,
-  DMNSN_T_CUBE,
-  DMNSN_T_CUBIC,
-  DMNSN_T_CUBIC_SPLINE,
-  DMNSN_T_CUBIC_WAVE,
-  DMNSN_T_CUTAWAY_TEXTURES,
-  DMNSN_T_CYLINDER,
-  DMNSN_T_CYLINDRICAL,
-  DMNSN_T_DEFINED,
-  DMNSN_T_DEGREES,
-  DMNSN_T_DENSITY,
-  DMNSN_T_DENSITY_FILE,
-  DMNSN_T_DENSITY_MAP,
-  DMNSN_T_DENTS,
-  DMNSN_T_DF3,
-  DMNSN_T_DIFFERENCE,
-  DMNSN_T_DIFFUSE,
-  DMNSN_T_DIMENSION_SIZE,
-  DMNSN_T_DIMENSIONS,
-  DMNSN_T_DIRECTION,
-  DMNSN_T_DISC,
-  DMNSN_T_DISPERSION,
-  DMNSN_T_DISPERSION_SAMPLES,
-  DMNSN_T_DIST_EXP,
-  DMNSN_T_DISTANCE,
-  DMNSN_T_DIV,
-  DMNSN_T_DOUBLE_ILLUMINATE,
-  DMNSN_T_ECCENTRICITY,
-  DMNSN_T_EMISSION,
-  DMNSN_T_ERROR_BOUND,
-  DMNSN_T_EVALUATE,
-  DMNSN_T_EXP,
-  DMNSN_T_EXPAND_THRESHOLDS,
-  DMNSN_T_EXPONENT,
-  DMNSN_T_EXTERIOR,
-  DMNSN_T_EXTINCTION,
-  DMNSN_T_FACE_INDICES,
-  DMNSN_T_FACETS,
-  DMNSN_T_FADE_COLOR,
-  DMNSN_T_FADE_DISTANCE,
-  DMNSN_T_FADE_POWER,
-  DMNSN_T_FALLOFF,
-  DMNSN_T_FALLOFF_ANGLE,
-  DMNSN_T_FALSE,
-  DMNSN_T_FILE_EXISTS,
-  DMNSN_T_FILTER,
-  DMNSN_T_FINAL_CLOCK,
-  DMNSN_T_FINAL_FRAME,
-  DMNSN_T_FINISH,
-  DMNSN_T_FISHEYE,
-  DMNSN_T_FLATNESS,
-  DMNSN_T_FLIP,
-  DMNSN_T_FLOOR,
-  DMNSN_T_FOCAL_POINT,
-  DMNSN_T_FOG,
-  DMNSN_T_FOG_ALT,
-  DMNSN_T_FOG_OFFSET,
-  DMNSN_T_FOG_TYPE,
-  DMNSN_T_FORM,
-  DMNSN_T_FRAME_NUMBER,
-  DMNSN_T_FREQUENCY,
-  DMNSN_T_FRESNEL,
-  DMNSN_T_FUNCTION,
-  DMNSN_T_GATHER,
-  DMNSN_T_GIF,
-  DMNSN_T_GLOBAL_LIGHTS,
-  DMNSN_T_GLOBAL_SETTINGS,
-  DMNSN_T_GRADIENT,
-  DMNSN_T_GRANITE,
-  DMNSN_T_GRAY,
-  DMNSN_T_GRAY_THRESHOLD,
-  DMNSN_T_GREEN,
-  DMNSN_T_HEIGHT_FIELD,
-  DMNSN_T_HEXAGON,
-  DMNSN_T_HF_GRAY_16,
-  DMNSN_T_HIERARCHY,
-  DMNSN_T_HYPERCOMPLEX,
-  DMNSN_T_HOLLOW,
-  DMNSN_T_IFF,
-  DMNSN_T_IMAGE_HEIGHT,
-  DMNSN_T_IMAGE_MAP,
-  DMNSN_T_IMAGE_PATTERN,
-  DMNSN_T_IMAGE_WIDTH,
-  DMNSN_T_INITIAL_CLOCK,
-  DMNSN_T_INITIAL_FRAME,
-  DMNSN_T_INSIDE,
-  DMNSN_T_INSIDE_VECTOR,
-  DMNSN_T_INT,
-  DMNSN_T_INTERIOR,
-  DMNSN_T_INTERIOR_TEXTURE,
-  DMNSN_T_INTERNAL,
-  DMNSN_T_INTERPOLATE,
-  DMNSN_T_INTERSECTION,
-  DMNSN_T_INTERVALS,
-  DMNSN_T_INVERSE,
-  DMNSN_T_IOR,
-  DMNSN_T_IRID,
-  DMNSN_T_IRID_WAVELENGTH,
-  DMNSN_T_ISOSURFACE,
-  DMNSN_T_JITTER,
-  DMNSN_T_JPEG,
-  DMNSN_T_JULIA,
-  DMNSN_T_JULIA_FRACTAL,
-  DMNSN_T_LAMBDA,
-  DMNSN_T_LATHE,
-  DMNSN_T_LEOPARD,
-  DMNSN_T_LIGHT_GROUP,
-  DMNSN_T_LIGHT_SOURCE,
-  DMNSN_T_LINEAR_SPLINE,
-  DMNSN_T_LINEAR_SWEEP,
-  DMNSN_T_LN,
-  DMNSN_T_LOAD_FILE,
-  DMNSN_T_LOCATION,
-  DMNSN_T_LOG,
-  DMNSN_T_LOOK_AT,
-  DMNSN_T_LOOKS_LIKE,
-  DMNSN_T_LOW_ERROR_FACTOR,
-  DMNSN_T_MAGNET,
-  DMNSN_T_MAJOR_RADIUS,
-  DMNSN_T_MANDEL,
-  DMNSN_T_MAP_TYPE,
-  DMNSN_T_MARBLE,
-  DMNSN_T_MATERIAL,
-  DMNSN_T_MATERIAL_MAP,
-  DMNSN_T_MATRIX,
-  DMNSN_T_MAX,
-  DMNSN_T_MAX_EXTENT,
-  DMNSN_T_MAX_GRADIENT,
-  DMNSN_T_MAX_INTERSECTIONS,
-  DMNSN_T_MAX_ITERATION,
-  DMNSN_T_MAX_SAMPLE,
-  DMNSN_T_MAX_TRACE,
-  DMNSN_T_MAX_TRACE_LEVEL,
-  DMNSN_T_MEDIA,
-  DMNSN_T_MEDIA_ATTENUATION,
-  DMNSN_T_MEDIA_INTERACTION,
-  DMNSN_T_MERGE,
-  DMNSN_T_MESH,
-  DMNSN_T_MESH2,
-  DMNSN_T_METALLIC,
-  DMNSN_T_METHOD,
-  DMNSN_T_METRIC,
-  DMNSN_T_MIN,
-  DMNSN_T_MIN_EXTENT,
-  DMNSN_T_MINIMUM_REUSE,
-  DMNSN_T_MOD,
-  DMNSN_T_MORTAR,
-  DMNSN_T_NATURAL_SPLINE,
-  DMNSN_T_NEAREST_COUNT,
-  DMNSN_T_NO,
-  DMNSN_T_NO_BUMP_SCALE,
-  DMNSN_T_NO_IMAGE,
-  DMNSN_T_NO_REFLECTION,
-  DMNSN_T_NO_SHADOW,
-  DMNSN_T_NOISE_GENERATOR,
-  DMNSN_T_NORMAL,
-  DMNSN_T_NORMAL_INDICES,
-  DMNSN_T_NORMAL_MAP,
-  DMNSN_T_NORMAL_VECTORS,
-  DMNSN_T_NUMBER_OF_WAVES,
-  DMNSN_T_OBJECT,
-  DMNSN_T_OCTAVES,
-  DMNSN_T_OFF,
-  DMNSN_T_OFFSET,
-  DMNSN_T_OMEGA,
-  DMNSN_T_OMNIMAX,
-  DMNSN_T_ON,
-  DMNSN_T_ONCE,
-  DMNSN_T_ONION,
-  DMNSN_T_OPEN,
-  DMNSN_T_ORIENT,
-  DMNSN_T_ORIENTATION,
-  DMNSN_T_ORTHOGRAPHIC,
-  DMNSN_T_PANORAMIC,
-  DMNSN_T_PARALLEL,
-  DMNSN_T_PARAMETRIC,
-  DMNSN_T_PASS_THROUGH,
-  DMNSN_T_PATTERN,
-  DMNSN_T_PERSPECTIVE,
-  DMNSN_T_PGM,
-  DMNSN_T_PHASE,
-  DMNSN_T_PHONG,
-  DMNSN_T_PHONG_SIZE,
-  DMNSN_T_PHOTONS,
-  DMNSN_T_PI,
-  DMNSN_T_PIGMENT,
-  DMNSN_T_PIGMENT_MAP,
-  DMNSN_T_PIGMENT_PATTERN,
-  DMNSN_T_PLANAR,
-  DMNSN_T_PLANE,
-  DMNSN_T_PNG,
-  DMNSN_T_POINT_AT,
-  DMNSN_T_POLY,
-  DMNSN_T_POLY_WAVE,
-  DMNSN_T_POLYGON,
-  DMNSN_T_POT,
-  DMNSN_T_POW,
-  DMNSN_T_PPM,
-  DMNSN_T_PRECISION,
-  DMNSN_T_PRECOMPUTE,
-  DMNSN_T_PRETRACE_END,
-  DMNSN_T_PRETRACE_START,
-  DMNSN_T_PRISM,
-  DMNSN_T_PROD,
-  DMNSN_T_PROJECTED_THROUGH,
-  DMNSN_T_PWR,
-  DMNSN_T_QUADRATIC_SPLINE,
-  DMNSN_T_QUADRIC,
-  DMNSN_T_QUARTIC,
-  DMNSN_T_QUATERNION,
-  DMNSN_T_QUICK_COLOR,
-  DMNSN_T_QUILTED,
-  DMNSN_T_RADIAL,
-  DMNSN_T_RADIANS,
-  DMNSN_T_RADIOSITY,
-  DMNSN_T_RADIUS,
-  DMNSN_T_RAINBOW,
-  DMNSN_T_RAMP_WAVE,
-  DMNSN_T_RAND,
-  DMNSN_T_RATIO,
-  DMNSN_T_RECIPROCAL,
-  DMNSN_T_RECURSION_LIMIT,
-  DMNSN_T_RED,
-  DMNSN_T_REFLECTION,
-  DMNSN_T_REFLECTION_EXPONENT,
-  DMNSN_T_REFRACTION,
-  DMNSN_T_REPEAT,
-  DMNSN_T_RGB,
-  DMNSN_T_RGBF,
-  DMNSN_T_RGBFT,
-  DMNSN_T_RGBT,
-  DMNSN_T_RIGHT,
-  DMNSN_T_RIPPLES,
-  DMNSN_T_ROTATE,
-  DMNSN_T_ROUGHNESS,
-  DMNSN_T_SAMPLES,
-  DMNSN_T_SAVE_FILE,
-  DMNSN_T_SCALE,
-  DMNSN_T_SCALLOP_WAVE,
-  DMNSN_T_SCATTERING,
-  DMNSN_T_SEED,
-  DMNSN_T_SELECT,
-  DMNSN_T_SHADOWLESS,
-  DMNSN_T_SIN,
-  DMNSN_T_SINE_WAVE,
-  DMNSN_T_SINH,
-  DMNSN_T_SIZE,
-  DMNSN_T_SKY,
-  DMNSN_T_SKY_SPHERE,
-  DMNSN_T_SLICE,
-  DMNSN_T_SLOPE,
-  DMNSN_T_SLOPE_MAP,
-  DMNSN_T_SMOOTH,
-  DMNSN_T_SMOOTH_TRIANGLE,
-  DMNSN_T_SOLID,
-  DMNSN_T_SOR,
-  DMNSN_T_SPACING,
-  DMNSN_T_SPECULAR,
-  DMNSN_T_SPHERE,
-  DMNSN_T_SPHERE_SWEEP,
-  DMNSN_T_SPHERICAL,
-  DMNSN_T_SPIRAL1,
-  DMNSN_T_SPIRAL2,
-  DMNSN_T_SPLINE,
-  DMNSN_T_SPLIT_UNION,
-  DMNSN_T_SPOTLIGHT,
-  DMNSN_T_SPOTTED,
-  DMNSN_T_SQR,
-  DMNSN_T_SQRT,
-  DMNSN_T_STR,
-  DMNSN_T_STRCMP,
-  DMNSN_T_STRENGTH,
-  DMNSN_T_STRLEN,
-  DMNSN_T_STRLWR,
-  DMNSN_T_STRUPR,
-  DMNSN_T_STURM,
-  DMNSN_T_SUBSTR,
-  DMNSN_T_SUM,
-  DMNSN_T_SUPERELLIPSOID,
-  DMNSN_T_SYS,
-  DMNSN_T_T,
-  DMNSN_T_TAN,
-  DMNSN_T_TANH,
-  DMNSN_T_TARGET,
-  DMNSN_T_TEXT,
-  DMNSN_T_TEXTURE,
-  DMNSN_T_TEXTURE_LIST,
-  DMNSN_T_TEXTURE_MAP,
-  DMNSN_T_TGA,
-  DMNSN_T_THICKNESS,
-  DMNSN_T_THRESHOLD,
-  DMNSN_T_TIFF,
-  DMNSN_T_TIGHTNESS,
-  DMNSN_T_TILE2,
-  DMNSN_T_TILES,
-  DMNSN_T_TOLERANCE,
-  DMNSN_T_TOROIDAL,
-  DMNSN_T_TORUS,
-  DMNSN_T_TRACE,
-  DMNSN_T_TRANSFORM,
-  DMNSN_T_TRANSLATE,
-  DMNSN_T_TRANSMIT,
-  DMNSN_T_TRIANGLE,
-  DMNSN_T_TRIANGLE_WAVE,
-  DMNSN_T_TRUE,
-  DMNSN_T_TTF,
-  DMNSN_T_TURB_DEPTH,
-  DMNSN_T_TURBULENCE,
-  DMNSN_T_TYPE,
-  DMNSN_T_U,
-  DMNSN_T_U_STEPS,
-  DMNSN_T_ULTRA_WIDE_ANGLE,
-  DMNSN_T_UNION,
-  DMNSN_T_UP,
-  DMNSN_T_USE_ALPHA,
-  DMNSN_T_USE_COLOR,
-  DMNSN_T_USE_INDEX,
-  DMNSN_T_UTF8,
-  DMNSN_T_UV_INDICES,
-  DMNSN_T_UV_MAPPING,
-  DMNSN_T_UV_VECTORS,
-  DMNSN_T_V,
-  DMNSN_T_V_STEPS,
-  DMNSN_T_VAL,
-  DMNSN_T_VARIANCE,
-  DMNSN_T_VAXIS_ROTATE,
-  DMNSN_T_VCROSS,
-  DMNSN_T_VDOT,
-  DMNSN_T_VERTEX_VECTORS,
-  DMNSN_T_VLENGTH,
-  DMNSN_T_VNORMALIZE,
-  DMNSN_T_VROTATE,
-  DMNSN_T_VSTR,
-  DMNSN_T_VTURBULENCE,
-  DMNSN_T_WARP,
-  DMNSN_T_WATER_LEVEL,
-  DMNSN_T_WAVES,
-  DMNSN_T_WIDTH,
-  DMNSN_T_WOOD,
-  DMNSN_T_WRINKLES,
-  DMNSN_T_X,
-  DMNSN_T_Y,
-  DMNSN_T_YES,
-  DMNSN_T_Z,
-
-  /* Directives (#declare, etc.) */
-  DMNSN_T_BREAK,
-  DMNSN_T_CASE,
-  DMNSN_T_DEBUG,
-  DMNSN_T_DECLARE,
-  DMNSN_T_DEFAULT,
-  DMNSN_T_ELSE,
-  DMNSN_T_END,
-  DMNSN_T_ERROR,
-  DMNSN_T_FCLOSE,
-  DMNSN_T_FOPEN,
-  DMNSN_T_IF,
-  DMNSN_T_IFDEF,
-  DMNSN_T_IFNDEF,
-  DMNSN_T_INCLUDE,
-  DMNSN_T_LOCAL,
-  DMNSN_T_MACRO,
-  DMNSN_T_RANGE,
-  DMNSN_T_READ,
-  DMNSN_T_RENDER,
-  DMNSN_T_STATISTICS,
-  DMNSN_T_SWITCH,
-  DMNSN_T_UNDEF,
-  DMNSN_T_VERSION,
-  DMNSN_T_WARNING,
-  DMNSN_T_WHILE,
-  DMNSN_T_WRITE,
-
-  /* Identifiers */
-  DMNSN_T_IDENTIFIER,
-
-  /* Strings */
-  DMNSN_T_STRING,
-} dmnsn_token_type;
+typedef enum yytokentype dmnsn_token_type;
 
 typedef struct dmnsn_token dmnsn_token;
 
diff --git a/dimension/tokenize.l b/dimension/tokenize.l
deleted file mode 100644
index 222eaa1..0000000
--- a/dimension/tokenize.l
+++ /dev/null
@@ -1,796 +0,0 @@
-/*************************************************************************
- * Copyright (C) 2009 Tavian Barnes <tavianator@gmail.com>               *
- *                                                                       *
- * This file is part of Dimension.                                       *
- *                                                                       *
- * Dimension is free software; you can redistribute it and/or modify it  *
- * under the terms of the GNU General Public License as published by the *
- * Free Software Foundation; either version 3 of the License, or (at     *
- * your option) any later version.                                       *
- *                                                                       *
- * Dimension is distributed in the hope that it will be useful, but      *
- * WITHOUT ANY WARRANTY; without even the implied warranty of            *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
- * General Public License for more details.                              *
- *                                                                       *
- * You should have received a copy of the GNU General Public License     *
- * along with this program.  If not, see <http://www.gnu.org/licenses/>. *
- *************************************************************************/
-
-%option reentrant stack yylineno noyywrap
-
-%{
-#define YY_DECL static int yylex(const char *filename, dmnsn_array *tokens, \
-                                 yyscan_t yyscanner)
-#include "tokenize.h"
-#include "utility.h"
-#include <stdlib.h>
-#include <stdio.h>
-%}
-
-%x DMNSN_BLOCK_COMMENT
-%x DMNSN_LINE_COMMENT
-%x DMNSN_STRING
-%x DMNSN_STRING_ESCAPE
-
-%%
-
-%{
-/* Some helpful macros that set fields of a token correctly, and other stuff */
-
-#define NEW_TOKEN(token_type)                   \
-  do {                                          \
-    token.type     = token_type;                \
-    token.filename = filename;                  \
-    token.line     = yylineno;                  \
-    token.col      = yycolumn;                  \
-    token.value    = NULL;                      \
-  } while (0)
-
-#define CALCULATE_COLUMN() yycolumn += yyleng
-
-#define PUSH()                                  \
-  do {                                          \
-    dmnsn_array_push(tokens, &token);           \
-    CALCULATE_COLUMN();                         \
-  } while (0)
-
-#define PUSH_TOKEN(token_type)                  \
-  do {                                          \
-    NEW_TOKEN(token_type);                      \
-    PUSH();                                     \
-  } while (0)
-
-#define PUSH_VALUE_TOKEN(token_type)            \
-  do {                                          \
-    NEW_TOKEN(token_type);                      \
-    token.value    = strdup(yytext);            \
-    PUSH();                                     \
-  } while (0)
-
-#define STRING_TOKEN()                          \
-  do {                                          \
-    NEW_TOKEN(DMNSN_T_STRING);                  \
-    token.value    = malloc(1);                 \
-    token.value[0] = '\0';                      \
-    string_length  = 0;                         \
-    CALCULATE_COLUMN();                         \
-  } while (0)
-
-#define STRCAT(str, len)                                                \
-  do {                                                                  \
-    token.value = realloc(token.value, string_length + len + 1);        \
-    strncpy(token.value + string_length, str, len);                     \
-    string_length += len;                                               \
-    CALCULATE_COLUMN();                                                 \
-  } while(0)
-
-dmnsn_token token;
-size_t string_length;
-unsigned long wchar;
-%}
-
-(?# Comments)
-
-<INITIAL,DMNSN_BLOCK_COMMENT>"/*"       {
-  yy_push_state(DMNSN_BLOCK_COMMENT, yyscanner);
-  CALCULATE_COLUMN();
-}
-<DMNSN_BLOCK_COMMENT>"*/"       CALCULATE_COLUMN(); yy_pop_state(yyscanner);
-<DMNSN_BLOCK_COMMENT>[^*/\n]*   CALCULATE_COLUMN();
-<DMNSN_BLOCK_COMMENT>"/"        CALCULATE_COLUMN();
-<DMNSN_BLOCK_COMMENT>"*"        CALCULATE_COLUMN();
-<DMNSN_BLOCK_COMMENT>\n         ;
-
-"//"    {
-  yy_push_state(DMNSN_LINE_COMMENT, yyscanner);
-  CALCULATE_COLUMN();
-}
-<DMNSN_LINE_COMMENT>\n          ; yy_pop_state(yyscanner);
-<DMNSN_LINE_COMMENT>[^\n]+      CALCULATE_COLUMN();
-
-(?# Punctuation)
-"{"     PUSH_TOKEN(DMNSN_T_LBRACE);
-"}"     PUSH_TOKEN(DMNSN_T_RBRACE);
-"("     PUSH_TOKEN(DMNSN_T_LPAREN);
-")"     PUSH_TOKEN(DMNSN_T_RPAREN);
-"["     PUSH_TOKEN(DMNSN_T_LBRACKET);
-"]"     PUSH_TOKEN(DMNSN_T_RBRACKET);
-"+"     PUSH_TOKEN(DMNSN_T_PLUS);
-"-"     PUSH_TOKEN(DMNSN_T_MINUS);
-"*"     PUSH_TOKEN(DMNSN_T_STAR);
-"/"     PUSH_TOKEN(DMNSN_T_SLASH);
-","     PUSH_TOKEN(DMNSN_T_COMMA);
-";"     PUSH_TOKEN(DMNSN_T_SEMICOLON);
-"?"     PUSH_TOKEN(DMNSN_T_QUESTION);
-":"     PUSH_TOKEN(DMNSN_T_COLON);
-"&"     PUSH_TOKEN(DMNSN_T_AND);
-"."     PUSH_TOKEN(DMNSN_T_DOT);
-"|"     PUSH_TOKEN(DMNSN_T_PIPE);
-"<"     PUSH_TOKEN(DMNSN_T_LESS);
-">"     PUSH_TOKEN(DMNSN_T_GREATER);
-"!"     PUSH_TOKEN(DMNSN_T_BANG);
-"="     PUSH_TOKEN(DMNSN_T_EQUALS);
-"<="    PUSH_TOKEN(DMNSN_T_LESS_EQUAL);
-">="    PUSH_TOKEN(DMNSN_T_GREATER_EQUAL);
-"!="    PUSH_TOKEN(DMNSN_T_NOT_EQUAL);
-
-(?# Integers)
-[[:digit:]]+                    |
-0(x|X)[[:digit:]aAbBcCdDeEfF]+  PUSH_VALUE_TOKEN(DMNSN_T_INTEGER);
-
-(?# Floats)
-[[:digit:]]*\.?[[:digit:]]+((e|E)(\+|-)?[[:digit:]]+)?  {
-  PUSH_VALUE_TOKEN(DMNSN_T_FLOAT);
-}
-
-(?# Keywords)
-"box"           PUSH_TOKEN(DMNSN_T_BOX);
-"camera"        PUSH_TOKEN(DMNSN_T_CAMERA);
-"color"         PUSH_TOKEN(DMNSN_T_COLOR);
-"colour"        PUSH_TOKEN(DMNSN_T_COLOR);
-"sphere"        PUSH_TOKEN(DMNSN_T_SPHERE);
-
-(?# Directives)
-"#include"      PUSH_TOKEN(DMNSN_T_INCLUDE);
-"#declare"      PUSH_TOKEN(DMNSN_T_DECLARE);
-
-(?# Identifiers)
-[[:alpha:]][[:alnum:]_]*        PUSH_VALUE_TOKEN(DMNSN_T_IDENTIFIER);
-
-(?# Strings)
-
-"\""    STRING_TOKEN(); yy_push_state(DMNSN_STRING, yyscanner);
-<DMNSN_STRING>[^\\\"\n]*        STRCAT(yytext, yyleng);
-<DMNSN_STRING>"\""              PUSH(); yy_pop_state(yyscanner);
-
-(?# String escape sequences)
-
-<DMNSN_STRING>"\\"      {
-  yy_push_state(DMNSN_STRING_ESCAPE, yyscanner);
-  CALCULATE_COLUMN();
-}
-<DMNSN_STRING_ESCAPE>"a"        STRCAT("\a", 1); yy_pop_state(yyscanner);
-<DMNSN_STRING_ESCAPE>"b"        STRCAT("\b", 1); yy_pop_state(yyscanner);
-<DMNSN_STRING_ESCAPE>"f"        STRCAT("\f", 1); yy_pop_state(yyscanner);
-<DMNSN_STRING_ESCAPE>"n"        STRCAT("\n", 1); yy_pop_state(yyscanner);
-<DMNSN_STRING_ESCAPE>"r"        STRCAT("\r", 1); yy_pop_state(yyscanner);
-<DMNSN_STRING_ESCAPE>"t"        STRCAT("\t", 1); yy_pop_state(yyscanner);
-<DMNSN_STRING_ESCAPE>"v"        STRCAT("\v", 1); yy_pop_state(yyscanner);
-<DMNSN_STRING_ESCAPE>"\\"       STRCAT("\\", 1); yy_pop_state(yyscanner);
-<DMNSN_STRING_ESCAPE>"'"        STRCAT("'", 1); yy_pop_state(yyscanner);
-<DMNSN_STRING_ESCAPE>"\""       STRCAT("\"", 1); yy_pop_state(yyscanner);
-<DMNSN_STRING_ESCAPE>"u"[[:digit:]aAbBcCdDeEfF]{4}      {
-  wchar = strtoul(yytext + 1, NULL, 16);
-  STRCAT("", 2);
-  token.value[string_length - 2] = wchar/256;
-  token.value[string_length - 1] = wchar%256;
-  yy_pop_state(yyscanner);
-}
-<DMNSN_STRING_ESCAPE>.          {
-  dmnsn_diagnostic(filename, yylineno, yycolumn,
-                   "WARNING: unrecognised escape sequence '\\%c'",
-                   (int)*yytext);
-  STRCAT(yytext, yyleng);
-  yy_pop_state(yyscanner);
-}
-
-(?# Ignore whitespace)
-[\b\r\t\v ]+    CALCULATE_COLUMN();
-\n              ;
-
-(?# Fall-through)
-.       {
-  dmnsn_diagnostic(filename, yylineno, yycolumn,
-                   "Unrecognized character '%c' (0x%X)",
-                   (int)*yytext, (unsigned int)*yytext);
-  return 1;
-}
-
-%%
-
-dmnsn_array *
-dmnsn_tokenize(const char *filename, FILE *file)
-{
-  dmnsn_array *tokens = dmnsn_new_array(sizeof(dmnsn_token));
-
-  yyscan_t scanner;
-
-  yylex_init(&scanner);
-  yyset_in(file, scanner);
-
-  if (yylex(filename, tokens, scanner) != 0) {
-    dmnsn_delete_tokens(tokens);
-    tokens = NULL;
-  }
-
-  yylex_destroy(scanner);
-
-  return tokens;
-}
-
-void
-dmnsn_delete_tokens(dmnsn_array *tokens)
-{
-  dmnsn_token *token;
-  unsigned int i;
-  for (i = 0; i < dmnsn_array_size(tokens); ++i) {
-    token = dmnsn_array_at(tokens, i);
-    free(token->value);
-  }
-  dmnsn_delete_array(tokens);
-}
-
-static void
-dmnsn_print_token(FILE *file, dmnsn_token token)
-{
-  const char *tname;
-  if (token.type == DMNSN_T_LPAREN) {
-    tname = "\\(";
-  } else if (token.type == DMNSN_T_RPAREN) {
-    tname = "\\)";
-  } else {
-    tname = dmnsn_token_string(token.type);
-  }
-
-  if (token.value) {
-    fprintf(file, "(%s \"%s\")", tname, token.value);
-  } else {
-    fprintf(file, "%s", tname);
-  }
-}
-
-void
-dmnsn_print_token_sexpr(FILE *file, const dmnsn_array *tokens)
-{
-  dmnsn_token token;
-  unsigned int i;
-
-  if (dmnsn_array_size(tokens) == 0) {
-    fprintf(file, "()");
-  } else {
-    fprintf(file, "(");
-    dmnsn_array_get(tokens, 0, &token);
-    dmnsn_print_token(file, token);
-
-    for (i = 1; i < dmnsn_array_size(tokens); ++i) {
-      fprintf(file, " ");
-      dmnsn_array_get(tokens, i, &token);
-      dmnsn_print_token(file, token);
-    }
-
-    fprintf(file, ")");
-  }
-
-  fprintf(file, "\n");
-}
-
-const char *
-dmnsn_token_string(dmnsn_token_type token_type)
-{
-  switch (token_type) {
-  /* Macro to shorten this huge switch */
-#define dmnsn_token_map(type, str)                                             \
-  case type:                                                                   \
-    return str;
-
-  /* Punctuation */
-
-  dmnsn_token_map(DMNSN_T_LBRACE,    "{");
-  dmnsn_token_map(DMNSN_T_RBRACE,    "}")
-  dmnsn_token_map(DMNSN_T_LPAREN,    "(");
-  dmnsn_token_map(DMNSN_T_RPAREN,    ")");
-  dmnsn_token_map(DMNSN_T_LBRACKET,  "[");
-  dmnsn_token_map(DMNSN_T_RBRACKET,  "]");
-  dmnsn_token_map(DMNSN_T_PLUS,      "+");
-  dmnsn_token_map(DMNSN_T_MINUS,     "-");
-  dmnsn_token_map(DMNSN_T_STAR,      "*");
-  dmnsn_token_map(DMNSN_T_SLASH,     "/");
-  dmnsn_token_map(DMNSN_T_COMMA,     ",");
-  dmnsn_token_map(DMNSN_T_SEMICOLON, ";");
-  dmnsn_token_map(DMNSN_T_QUESTION,  "?");
-  dmnsn_token_map(DMNSN_T_COLON,     ":");
-  dmnsn_token_map(DMNSN_T_AND,       "&");
-  dmnsn_token_map(DMNSN_T_DOT,       ".");
-  dmnsn_token_map(DMNSN_T_PIPE,      "|");
-  dmnsn_token_map(DMNSN_T_LESS,      "<");
-  dmnsn_token_map(DMNSN_T_GREATER,   ">");
-  dmnsn_token_map(DMNSN_T_BANG,      "!");
-  dmnsn_token_map(DMNSN_T_EQUALS,    "=");
-
-  dmnsn_token_map(DMNSN_T_LESS_EQUAL,    "<=");
-  dmnsn_token_map(DMNSN_T_GREATER_EQUAL, ">=");
-  dmnsn_token_map(DMNSN_T_NOT_EQUAL,     "!=");
-
-  /* Numeric values */
-  dmnsn_token_map(DMNSN_T_INTEGER, "integer");
-  dmnsn_token_map(DMNSN_T_FLOAT,   "float");
-
-  /* Keywords */
-  dmnsn_token_map(DMNSN_T_AA_LEVEL, "aa_level");
-  dmnsn_token_map(DMNSN_T_AA_THRESHOLD, "aa_threshold");
-  dmnsn_token_map(DMNSN_T_ABS, "abs");
-  dmnsn_token_map(DMNSN_T_ABSORPTION, "absorption");
-  dmnsn_token_map(DMNSN_T_ACCURACY, "accuracy");
-  dmnsn_token_map(DMNSN_T_ACOS, "acos");
-  dmnsn_token_map(DMNSN_T_ACOSH, "acosh");
-  dmnsn_token_map(DMNSN_T_ADAPTIVE, "adaptive");
-  dmnsn_token_map(DMNSN_T_ADC_BAILOUT, "adc_bailout");
-  dmnsn_token_map(DMNSN_T_AGATE, "agate");
-  dmnsn_token_map(DMNSN_T_AGATE_TURB, "agate_turb");
-  dmnsn_token_map(DMNSN_T_ALL, "all");
-  dmnsn_token_map(DMNSN_T_ALL_INTERSECTIONS, "all_intersections");
-  dmnsn_token_map(DMNSN_T_ALPHA, "alpha");
-  dmnsn_token_map(DMNSN_T_ALTITUDE, "altitude");
-  dmnsn_token_map(DMNSN_T_ALWAYS_SAMPLE, "always_sample");
-  dmnsn_token_map(DMNSN_T_AMBIENT, "ambient");
-  dmnsn_token_map(DMNSN_T_AMBIENT_LIGHT, "ambient_light");
-  dmnsn_token_map(DMNSN_T_ANGLE, "angle");
-  dmnsn_token_map(DMNSN_T_APERTURE, "aperture");
-  dmnsn_token_map(DMNSN_T_APPEND, "append");
-  dmnsn_token_map(DMNSN_T_ARC_ANGLE, "arc_angle");
-  dmnsn_token_map(DMNSN_T_AREA_LIGHT, "area_light");
-  dmnsn_token_map(DMNSN_T_ARRAY, "array");
-  dmnsn_token_map(DMNSN_T_ASC, "asc");
-  dmnsn_token_map(DMNSN_T_ASCII, "ascii");
-  dmnsn_token_map(DMNSN_T_ASIN, "asin");
-  dmnsn_token_map(DMNSN_T_ASINH, "asinh");
-  dmnsn_token_map(DMNSN_T_ASSUMED_GAMMA, "assumed_gamma");
-  dmnsn_token_map(DMNSN_T_ATAN, "atan");
-  dmnsn_token_map(DMNSN_T_ATAN2, "atan2");
-  dmnsn_token_map(DMNSN_T_ATANH, "atanh");
-  dmnsn_token_map(DMNSN_T_AUTOSTOP, "autostop");
-  dmnsn_token_map(DMNSN_T_AVERAGE, "average");
-  dmnsn_token_map(DMNSN_T_B_SPLINE, "b_spline");
-  dmnsn_token_map(DMNSN_T_BACKGROUND, "background");
-  dmnsn_token_map(DMNSN_T_BEZIER_SPLINE, "bezier_spline");
-  dmnsn_token_map(DMNSN_T_BICUBIC_PATCH, "bicubic_patch");
-  dmnsn_token_map(DMNSN_T_BLACK_HOLE, "black_hole");
-  dmnsn_token_map(DMNSN_T_BLOB, "blob");
-  dmnsn_token_map(DMNSN_T_BLUE, "blue");
-  dmnsn_token_map(DMNSN_T_BLUR_SAMPLES, "blur_samples");
-  dmnsn_token_map(DMNSN_T_BOUNDED_BY, "bounded_by");
-  dmnsn_token_map(DMNSN_T_BOX, "box");
-  dmnsn_token_map(DMNSN_T_BOXED, "boxed");
-  dmnsn_token_map(DMNSN_T_BOZO, "bozo");
-  dmnsn_token_map(DMNSN_T_BRICK, "brick");
-  dmnsn_token_map(DMNSN_T_BRICK_SIZE, "brick_size");
-  dmnsn_token_map(DMNSN_T_BRIGHTNESS, "brightness");
-  dmnsn_token_map(DMNSN_T_BRILLIANCE, "brilliance");
-  dmnsn_token_map(DMNSN_T_BUMP_MAP, "bump_map");
-  dmnsn_token_map(DMNSN_T_BUMP_SIZE, "bump_size");
-  dmnsn_token_map(DMNSN_T_BUMPS, "bumps");
-  dmnsn_token_map(DMNSN_T_CAMERA, "camera");
-  dmnsn_token_map(DMNSN_T_CAUSTICS, "caustics");
-  dmnsn_token_map(DMNSN_T_CEIL, "ceil");
-  dmnsn_token_map(DMNSN_T_CELLS, "cells");
-  dmnsn_token_map(DMNSN_T_CHARSET, "charset");
-  dmnsn_token_map(DMNSN_T_CHECKER, "checker");
-  dmnsn_token_map(DMNSN_T_CHR, "chr");
-  dmnsn_token_map(DMNSN_T_CIRCULAR, "circular");
-  dmnsn_token_map(DMNSN_T_CLIPPED_BY, "clipped_by");
-  dmnsn_token_map(DMNSN_T_CLOCK, "clock");
-  dmnsn_token_map(DMNSN_T_CLOCK_DELTA, "clock_delta");
-  dmnsn_token_map(DMNSN_T_CLOCK_ON, "clock_on");
-  dmnsn_token_map(DMNSN_T_COLLECT, "collect");
-  dmnsn_token_map(DMNSN_T_COLOR, "color");
-  dmnsn_token_map(DMNSN_T_COLOR_MAP, "color_map");
-  dmnsn_token_map(DMNSN_T_COMPONENT, "component");
-  dmnsn_token_map(DMNSN_T_COMPOSITE, "composite");
-  dmnsn_token_map(DMNSN_T_CONCAT, "concat");
-  dmnsn_token_map(DMNSN_T_CONE, "cone");
-  dmnsn_token_map(DMNSN_T_CONFIDENCE, "confidence");
-  dmnsn_token_map(DMNSN_T_CONIC_SWEEP, "conic_sweep");
-  dmnsn_token_map(DMNSN_T_CONSERVE_ENERGY, "conserve_energy");
-  dmnsn_token_map(DMNSN_T_CONTAINED_BY, "contained_by");
-  dmnsn_token_map(DMNSN_T_CONTROL0, "control0");
-  dmnsn_token_map(DMNSN_T_CONTROL1, "control1");
-  dmnsn_token_map(DMNSN_T_COORDS, "coords");
-  dmnsn_token_map(DMNSN_T_COS, "cos");
-  dmnsn_token_map(DMNSN_T_COSH, "cosh");
-  dmnsn_token_map(DMNSN_T_COUNT, "count");
-  dmnsn_token_map(DMNSN_T_CRACKLE, "crackle");
-  dmnsn_token_map(DMNSN_T_CRAND, "crand");
-  dmnsn_token_map(DMNSN_T_CUBE, "cube");
-  dmnsn_token_map(DMNSN_T_CUBIC, "cubic");
-  dmnsn_token_map(DMNSN_T_CUBIC_SPLINE, "cubic_spline");
-  dmnsn_token_map(DMNSN_T_CUBIC_WAVE, "cubic_wave");
-  dmnsn_token_map(DMNSN_T_CUTAWAY_TEXTURES, "cutaway_textures");
-  dmnsn_token_map(DMNSN_T_CYLINDER, "cylinder");
-  dmnsn_token_map(DMNSN_T_CYLINDRICAL, "cylindrical");
-  dmnsn_token_map(DMNSN_T_DEFINED, "defined");
-  dmnsn_token_map(DMNSN_T_DEGREES, "degrees");
-  dmnsn_token_map(DMNSN_T_DENSITY, "density");
-  dmnsn_token_map(DMNSN_T_DENSITY_FILE, "density_file");
-  dmnsn_token_map(DMNSN_T_DENSITY_MAP, "density_map");
-  dmnsn_token_map(DMNSN_T_DENTS, "dents");
-  dmnsn_token_map(DMNSN_T_DF3, "df3");
-  dmnsn_token_map(DMNSN_T_DIFFERENCE, "difference");
-  dmnsn_token_map(DMNSN_T_DIFFUSE, "diffuse");
-  dmnsn_token_map(DMNSN_T_DIMENSION_SIZE, "dimension_size");
-  dmnsn_token_map(DMNSN_T_DIMENSIONS, "dimensions");
-  dmnsn_token_map(DMNSN_T_DIRECTION, "direction");
-  dmnsn_token_map(DMNSN_T_DISC, "disc");
-  dmnsn_token_map(DMNSN_T_DISPERSION, "dispersion");
-  dmnsn_token_map(DMNSN_T_DISPERSION_SAMPLES, "dispersion_samples");
-  dmnsn_token_map(DMNSN_T_DIST_EXP, "dist_exp");
-  dmnsn_token_map(DMNSN_T_DISTANCE, "distance");
-  dmnsn_token_map(DMNSN_T_DIV, "div");
-  dmnsn_token_map(DMNSN_T_DOUBLE_ILLUMINATE, "double_illuminate");
-  dmnsn_token_map(DMNSN_T_ECCENTRICITY, "eccentricity");
-  dmnsn_token_map(DMNSN_T_EMISSION, "emission");
-  dmnsn_token_map(DMNSN_T_ERROR_BOUND, "error_bound");
-  dmnsn_token_map(DMNSN_T_EVALUATE, "evaluate");
-  dmnsn_token_map(DMNSN_T_EXP, "exp");
-  dmnsn_token_map(DMNSN_T_EXPAND_THRESHOLDS, "expand_thresholds");
-  dmnsn_token_map(DMNSN_T_EXPONENT, "exponent");
-  dmnsn_token_map(DMNSN_T_EXTERIOR, "exterior");
-  dmnsn_token_map(DMNSN_T_EXTINCTION, "extinction");
-  dmnsn_token_map(DMNSN_T_FACE_INDICES, "face_indices");
-  dmnsn_token_map(DMNSN_T_FACETS, "facets");
-  dmnsn_token_map(DMNSN_T_FADE_COLOR, "fade_color");
-  dmnsn_token_map(DMNSN_T_FADE_DISTANCE, "fade_distance");
-  dmnsn_token_map(DMNSN_T_FADE_POWER, "fade_power");
-  dmnsn_token_map(DMNSN_T_FALLOFF, "falloff");
-  dmnsn_token_map(DMNSN_T_FALLOFF_ANGLE, "falloff_angle");
-  dmnsn_token_map(DMNSN_T_FALSE, "false");
-  dmnsn_token_map(DMNSN_T_FILE_EXISTS, "file_exists");
-  dmnsn_token_map(DMNSN_T_FILTER, "filter");
-  dmnsn_token_map(DMNSN_T_FINAL_CLOCK, "final_clock");
-  dmnsn_token_map(DMNSN_T_FINAL_FRAME, "final_frame");
-  dmnsn_token_map(DMNSN_T_FINISH, "finish");
-  dmnsn_token_map(DMNSN_T_FISHEYE, "fisheye");
-  dmnsn_token_map(DMNSN_T_FLATNESS, "flatness");
-  dmnsn_token_map(DMNSN_T_FLIP, "flip");
-  dmnsn_token_map(DMNSN_T_FLOOR, "floor");
-  dmnsn_token_map(DMNSN_T_FOCAL_POINT, "focal_point");
-  dmnsn_token_map(DMNSN_T_FOG, "fog");
-  dmnsn_token_map(DMNSN_T_FOG_ALT, "fog_alt");
-  dmnsn_token_map(DMNSN_T_FOG_OFFSET, "fog_offset");
-  dmnsn_token_map(DMNSN_T_FOG_TYPE, "fog_type");
-  dmnsn_token_map(DMNSN_T_FORM, "form");
-  dmnsn_token_map(DMNSN_T_FRAME_NUMBER, "frame_number");
-  dmnsn_token_map(DMNSN_T_FREQUENCY, "frequency");
-  dmnsn_token_map(DMNSN_T_FRESNEL, "fresnel");
-  dmnsn_token_map(DMNSN_T_FUNCTION, "function");
-  dmnsn_token_map(DMNSN_T_GATHER, "gather");
-  dmnsn_token_map(DMNSN_T_GIF, "gif");
-  dmnsn_token_map(DMNSN_T_GLOBAL_LIGHTS, "global_lights");
-  dmnsn_token_map(DMNSN_T_GLOBAL_SETTINGS, "global_settings");
-  dmnsn_token_map(DMNSN_T_GRADIENT, "gradient");
-  dmnsn_token_map(DMNSN_T_GRANITE, "granite");
-  dmnsn_token_map(DMNSN_T_GRAY, "gray");
-  dmnsn_token_map(DMNSN_T_GRAY_THRESHOLD, "gray_threshold");
-  dmnsn_token_map(DMNSN_T_GREEN, "green");
-  dmnsn_token_map(DMNSN_T_HEIGHT_FIELD, "height_field");
-  dmnsn_token_map(DMNSN_T_HEXAGON, "hexagon");
-  dmnsn_token_map(DMNSN_T_HF_GRAY_16, "hf_gray_16");
-  dmnsn_token_map(DMNSN_T_HIERARCHY, "hierarchy");
-  dmnsn_token_map(DMNSN_T_HYPERCOMPLEX, "hypercomplex");
-  dmnsn_token_map(DMNSN_T_HOLLOW, "hollow");
-  dmnsn_token_map(DMNSN_T_IFF, "iff");
-  dmnsn_token_map(DMNSN_T_IMAGE_HEIGHT, "image_height");
-  dmnsn_token_map(DMNSN_T_IMAGE_MAP, "image_map");
-  dmnsn_token_map(DMNSN_T_IMAGE_PATTERN, "image_pattern");
-  dmnsn_token_map(DMNSN_T_IMAGE_WIDTH, "image_width");
-  dmnsn_token_map(DMNSN_T_INITIAL_CLOCK, "initial_clock");
-  dmnsn_token_map(DMNSN_T_INITIAL_FRAME, "initial_frame");
-  dmnsn_token_map(DMNSN_T_INSIDE, "inside");
-  dmnsn_token_map(DMNSN_T_INSIDE_VECTOR, "inside_vector");
-  dmnsn_token_map(DMNSN_T_INT, "int");
-  dmnsn_token_map(DMNSN_T_INTERIOR, "interior");
-  dmnsn_token_map(DMNSN_T_INTERIOR_TEXTURE, "interior_texture");
-  dmnsn_token_map(DMNSN_T_INTERNAL, "internal");
-  dmnsn_token_map(DMNSN_T_INTERPOLATE, "interpolate");
-  dmnsn_token_map(DMNSN_T_INTERSECTION, "intersection");
-  dmnsn_token_map(DMNSN_T_INTERVALS, "intervals");
-  dmnsn_token_map(DMNSN_T_INVERSE, "inverse");
-  dmnsn_token_map(DMNSN_T_IOR, "ior");
-  dmnsn_token_map(DMNSN_T_IRID, "irid");
-  dmnsn_token_map(DMNSN_T_IRID_WAVELENGTH, "irid_wavelength");
-  dmnsn_token_map(DMNSN_T_ISOSURFACE, "isosurface");
-  dmnsn_token_map(DMNSN_T_JITTER, "jitter");
-  dmnsn_token_map(DMNSN_T_JPEG, "jpeg");
-  dmnsn_token_map(DMNSN_T_JULIA, "julia");
-  dmnsn_token_map(DMNSN_T_JULIA_FRACTAL, "julia_fractal");
-  dmnsn_token_map(DMNSN_T_LAMBDA, "lambda");
-  dmnsn_token_map(DMNSN_T_LATHE, "lathe");
-  dmnsn_token_map(DMNSN_T_LEOPARD, "leopard");
-  dmnsn_token_map(DMNSN_T_LIGHT_GROUP, "light_group");
-  dmnsn_token_map(DMNSN_T_LIGHT_SOURCE, "light_source");
-  dmnsn_token_map(DMNSN_T_LINEAR_SPLINE, "linear_spline");
-  dmnsn_token_map(DMNSN_T_LINEAR_SWEEP, "linear_sweep");
-  dmnsn_token_map(DMNSN_T_LN, "ln");
-  dmnsn_token_map(DMNSN_T_LOAD_FILE, "load_file");
-  dmnsn_token_map(DMNSN_T_LOCATION, "location");
-  dmnsn_token_map(DMNSN_T_LOG, "log");
-  dmnsn_token_map(DMNSN_T_LOOK_AT, "look_at");
-  dmnsn_token_map(DMNSN_T_LOOKS_LIKE, "looks_like");
-  dmnsn_token_map(DMNSN_T_LOW_ERROR_FACTOR, "low_error_factor");
-  dmnsn_token_map(DMNSN_T_MAGNET, "magnet");
-  dmnsn_token_map(DMNSN_T_MAJOR_RADIUS, "major_radius");
-  dmnsn_token_map(DMNSN_T_MANDEL, "mandel");
-  dmnsn_token_map(DMNSN_T_MAP_TYPE, "map_type");
-  dmnsn_token_map(DMNSN_T_MARBLE, "marble");
-  dmnsn_token_map(DMNSN_T_MATERIAL, "material");
-  dmnsn_token_map(DMNSN_T_MATERIAL_MAP, "material_map");
-  dmnsn_token_map(DMNSN_T_MATRIX, "matrix");
-  dmnsn_token_map(DMNSN_T_MAX, "max");
-  dmnsn_token_map(DMNSN_T_MAX_EXTENT, "max_extent");
-  dmnsn_token_map(DMNSN_T_MAX_GRADIENT, "max_gradient");
-  dmnsn_token_map(DMNSN_T_MAX_INTERSECTIONS, "max_intersections");
-  dmnsn_token_map(DMNSN_T_MAX_ITERATION, "max_iteration");
-  dmnsn_token_map(DMNSN_T_MAX_SAMPLE, "max_sample");
-  dmnsn_token_map(DMNSN_T_MAX_TRACE, "max_trace");
-  dmnsn_token_map(DMNSN_T_MAX_TRACE_LEVEL, "max_trace_level");
-  dmnsn_token_map(DMNSN_T_MEDIA, "media");
-  dmnsn_token_map(DMNSN_T_MEDIA_ATTENUATION, "media_attenuation");
-  dmnsn_token_map(DMNSN_T_MEDIA_INTERACTION, "media_interaction");
-  dmnsn_token_map(DMNSN_T_MERGE, "merge");
-  dmnsn_token_map(DMNSN_T_MESH, "mesh");
-  dmnsn_token_map(DMNSN_T_MESH2, "mesh2");
-  dmnsn_token_map(DMNSN_T_METALLIC, "metallic");
-  dmnsn_token_map(DMNSN_T_METHOD, "method");
-  dmnsn_token_map(DMNSN_T_METRIC, "metric");
-  dmnsn_token_map(DMNSN_T_MIN, "min");
-  dmnsn_token_map(DMNSN_T_MIN_EXTENT, "min_extent");
-  dmnsn_token_map(DMNSN_T_MINIMUM_REUSE, "minimum_reuse");
-  dmnsn_token_map(DMNSN_T_MOD, "mod");
-  dmnsn_token_map(DMNSN_T_MORTAR, "mortar");
-  dmnsn_token_map(DMNSN_T_NATURAL_SPLINE, "natural_spline");
-  dmnsn_token_map(DMNSN_T_NEAREST_COUNT, "nearest_count");
-  dmnsn_token_map(DMNSN_T_NO, "no");
-  dmnsn_token_map(DMNSN_T_NO_BUMP_SCALE, "no_bump_scale");
-  dmnsn_token_map(DMNSN_T_NO_IMAGE, "no_image");
-  dmnsn_token_map(DMNSN_T_NO_REFLECTION, "no_reflection");
-  dmnsn_token_map(DMNSN_T_NO_SHADOW, "no_shadow");
-  dmnsn_token_map(DMNSN_T_NOISE_GENERATOR, "noise_generator");
-  dmnsn_token_map(DMNSN_T_NORMAL, "normal");
-  dmnsn_token_map(DMNSN_T_NORMAL_INDICES, "normal_indices");
-  dmnsn_token_map(DMNSN_T_NORMAL_MAP, "normal_map");
-  dmnsn_token_map(DMNSN_T_NORMAL_VECTORS, "normal_vectors");
-  dmnsn_token_map(DMNSN_T_NUMBER_OF_WAVES, "number_of_waves");
-  dmnsn_token_map(DMNSN_T_OBJECT, "object");
-  dmnsn_token_map(DMNSN_T_OCTAVES, "octaves");
-  dmnsn_token_map(DMNSN_T_OFF, "off");
-  dmnsn_token_map(DMNSN_T_OFFSET, "offset");
-  dmnsn_token_map(DMNSN_T_OMEGA, "omega");
-  dmnsn_token_map(DMNSN_T_OMNIMAX, "omnimax");
-  dmnsn_token_map(DMNSN_T_ON, "on");
-  dmnsn_token_map(DMNSN_T_ONCE, "once");
-  dmnsn_token_map(DMNSN_T_ONION, "onion");
-  dmnsn_token_map(DMNSN_T_OPEN, "open");
-  dmnsn_token_map(DMNSN_T_ORIENT, "orient");
-  dmnsn_token_map(DMNSN_T_ORIENTATION, "orientation");
-  dmnsn_token_map(DMNSN_T_ORTHOGRAPHIC, "orthographic");
-  dmnsn_token_map(DMNSN_T_PANORAMIC, "panoramic");
-  dmnsn_token_map(DMNSN_T_PARALLEL, "parallel");
-  dmnsn_token_map(DMNSN_T_PARAMETRIC, "parametric");
-  dmnsn_token_map(DMNSN_T_PASS_THROUGH, "pass_through");
-  dmnsn_token_map(DMNSN_T_PATTERN, "pattern");
-  dmnsn_token_map(DMNSN_T_PERSPECTIVE, "perspective");
-  dmnsn_token_map(DMNSN_T_PGM, "pgm");
-  dmnsn_token_map(DMNSN_T_PHASE, "phase");
-  dmnsn_token_map(DMNSN_T_PHONG, "phong");
-  dmnsn_token_map(DMNSN_T_PHONG_SIZE, "phong_size");
-  dmnsn_token_map(DMNSN_T_PHOTONS, "photons");
-  dmnsn_token_map(DMNSN_T_PI, "pi");
-  dmnsn_token_map(DMNSN_T_PIGMENT, "pigment");
-  dmnsn_token_map(DMNSN_T_PIGMENT_MAP, "pigment_map");
-  dmnsn_token_map(DMNSN_T_PIGMENT_PATTERN, "pigment_pattern");
-  dmnsn_token_map(DMNSN_T_PLANAR, "planar");
-  dmnsn_token_map(DMNSN_T_PLANE, "plane");
-  dmnsn_token_map(DMNSN_T_PNG, "png");
-  dmnsn_token_map(DMNSN_T_POINT_AT, "point_at");
-  dmnsn_token_map(DMNSN_T_POLY, "poly");
-  dmnsn_token_map(DMNSN_T_POLY_WAVE, "poly_wave");
-  dmnsn_token_map(DMNSN_T_POLYGON, "polygon");
-  dmnsn_token_map(DMNSN_T_POT, "pot");
-  dmnsn_token_map(DMNSN_T_POW, "pow");
-  dmnsn_token_map(DMNSN_T_PPM, "ppm");
-  dmnsn_token_map(DMNSN_T_PRECISION, "precision");
-  dmnsn_token_map(DMNSN_T_PRECOMPUTE, "precompute");
-  dmnsn_token_map(DMNSN_T_PRETRACE_END, "pretrace_end");
-  dmnsn_token_map(DMNSN_T_PRETRACE_START, "pretrace_start");
-  dmnsn_token_map(DMNSN_T_PRISM, "prism");
-  dmnsn_token_map(DMNSN_T_PROD, "prod");
-  dmnsn_token_map(DMNSN_T_PROJECTED_THROUGH, "projected_through");
-  dmnsn_token_map(DMNSN_T_PWR, "pwr");
-  dmnsn_token_map(DMNSN_T_QUADRATIC_SPLINE, "quadratic_spline");
-  dmnsn_token_map(DMNSN_T_QUADRIC, "quadric");
-  dmnsn_token_map(DMNSN_T_QUARTIC, "quartic");
-  dmnsn_token_map(DMNSN_T_QUATERNION, "quaternion");
-  dmnsn_token_map(DMNSN_T_QUICK_COLOR, "quick_color");
-  dmnsn_token_map(DMNSN_T_QUILTED, "quilted");
-  dmnsn_token_map(DMNSN_T_RADIAL, "radial");
-  dmnsn_token_map(DMNSN_T_RADIANS, "radians");
-  dmnsn_token_map(DMNSN_T_RADIOSITY, "radiosity");
-  dmnsn_token_map(DMNSN_T_RADIUS, "radius");
-  dmnsn_token_map(DMNSN_T_RAINBOW, "rainbow");
-  dmnsn_token_map(DMNSN_T_RAMP_WAVE, "ramp_wave");
-  dmnsn_token_map(DMNSN_T_RAND, "rand");
-  dmnsn_token_map(DMNSN_T_RATIO, "ratio");
-  dmnsn_token_map(DMNSN_T_RECIPROCAL, "reciprocal");
-  dmnsn_token_map(DMNSN_T_RECURSION_LIMIT, "recursion_limit");
-  dmnsn_token_map(DMNSN_T_RED, "red");
-  dmnsn_token_map(DMNSN_T_REFLECTION, "reflection");
-  dmnsn_token_map(DMNSN_T_REFLECTION_EXPONENT, "reflection_exponent");
-  dmnsn_token_map(DMNSN_T_REFRACTION, "refraction");
-  dmnsn_token_map(DMNSN_T_REPEAT, "repeat");
-  dmnsn_token_map(DMNSN_T_RGB, "rgb");
-  dmnsn_token_map(DMNSN_T_RGBF, "rgbf");
-  dmnsn_token_map(DMNSN_T_RGBFT, "rgbft");
-  dmnsn_token_map(DMNSN_T_RGBT, "rgbt");
-  dmnsn_token_map(DMNSN_T_RIGHT, "right");
-  dmnsn_token_map(DMNSN_T_RIPPLES, "ripples");
-  dmnsn_token_map(DMNSN_T_ROTATE, "rotate");
-  dmnsn_token_map(DMNSN_T_ROUGHNESS, "roughness");
-  dmnsn_token_map(DMNSN_T_SAMPLES, "samples");
-  dmnsn_token_map(DMNSN_T_SAVE_FILE, "save_file");
-  dmnsn_token_map(DMNSN_T_SCALE, "scale");
-  dmnsn_token_map(DMNSN_T_SCALLOP_WAVE, "scallop_wave");
-  dmnsn_token_map(DMNSN_T_SCATTERING, "scattering");
-  dmnsn_token_map(DMNSN_T_SEED, "seed");
-  dmnsn_token_map(DMNSN_T_SELECT, "select");
-  dmnsn_token_map(DMNSN_T_SHADOWLESS, "shadowless");
-  dmnsn_token_map(DMNSN_T_SIN, "sin");
-  dmnsn_token_map(DMNSN_T_SINE_WAVE, "sine_wave");
-  dmnsn_token_map(DMNSN_T_SINH, "sinh");
-  dmnsn_token_map(DMNSN_T_SIZE, "size");
-  dmnsn_token_map(DMNSN_T_SKY, "sky");
-  dmnsn_token_map(DMNSN_T_SKY_SPHERE, "sky_sphere");
-  dmnsn_token_map(DMNSN_T_SLICE, "slice");
-  dmnsn_token_map(DMNSN_T_SLOPE, "slope");
-  dmnsn_token_map(DMNSN_T_SLOPE_MAP, "slope_map");
-  dmnsn_token_map(DMNSN_T_SMOOTH, "smooth");
-  dmnsn_token_map(DMNSN_T_SMOOTH_TRIANGLE, "smooth_triangle");
-  dmnsn_token_map(DMNSN_T_SOLID, "solid");
-  dmnsn_token_map(DMNSN_T_SOR, "sor");
-  dmnsn_token_map(DMNSN_T_SPACING, "spacing");
-  dmnsn_token_map(DMNSN_T_SPECULAR, "specular");
-  dmnsn_token_map(DMNSN_T_SPHERE, "sphere");
-  dmnsn_token_map(DMNSN_T_SPHERE_SWEEP, "sphere_sweep");
-  dmnsn_token_map(DMNSN_T_SPHERICAL, "spherical");
-  dmnsn_token_map(DMNSN_T_SPIRAL1, "spiral1");
-  dmnsn_token_map(DMNSN_T_SPIRAL2, "spiral2");
-  dmnsn_token_map(DMNSN_T_SPLINE, "spline");
-  dmnsn_token_map(DMNSN_T_SPLIT_UNION, "split_union");
-  dmnsn_token_map(DMNSN_T_SPOTLIGHT, "spotlight");
-  dmnsn_token_map(DMNSN_T_SPOTTED, "spotted");
-  dmnsn_token_map(DMNSN_T_SQR, "sqr");
-  dmnsn_token_map(DMNSN_T_SQRT, "sqrt");
-  dmnsn_token_map(DMNSN_T_STR, "str");
-  dmnsn_token_map(DMNSN_T_STRCMP, "strcmp");
-  dmnsn_token_map(DMNSN_T_STRENGTH, "strength");
-  dmnsn_token_map(DMNSN_T_STRLEN, "strlen");
-  dmnsn_token_map(DMNSN_T_STRLWR, "strlwr");
-  dmnsn_token_map(DMNSN_T_STRUPR, "strupr");
-  dmnsn_token_map(DMNSN_T_STURM, "sturm");
-  dmnsn_token_map(DMNSN_T_SUBSTR, "substr");
-  dmnsn_token_map(DMNSN_T_SUM, "sum");
-  dmnsn_token_map(DMNSN_T_SUPERELLIPSOID, "superellipsoid");
-  dmnsn_token_map(DMNSN_T_SYS, "sys");
-  dmnsn_token_map(DMNSN_T_T, "t");
-  dmnsn_token_map(DMNSN_T_TAN, "tan");
-  dmnsn_token_map(DMNSN_T_TANH, "tanh");
-  dmnsn_token_map(DMNSN_T_TARGET, "target");
-  dmnsn_token_map(DMNSN_T_TEXT, "text");
-  dmnsn_token_map(DMNSN_T_TEXTURE, "texture");
-  dmnsn_token_map(DMNSN_T_TEXTURE_LIST, "texture_list");
-  dmnsn_token_map(DMNSN_T_TEXTURE_MAP, "texture_map");
-  dmnsn_token_map(DMNSN_T_TGA, "tga");
-  dmnsn_token_map(DMNSN_T_THICKNESS, "thickness");
-  dmnsn_token_map(DMNSN_T_THRESHOLD, "threshold");
-  dmnsn_token_map(DMNSN_T_TIFF, "tiff");
-  dmnsn_token_map(DMNSN_T_TIGHTNESS, "tightness");
-  dmnsn_token_map(DMNSN_T_TILE2, "tile2");
-  dmnsn_token_map(DMNSN_T_TILES, "tiles");
-  dmnsn_token_map(DMNSN_T_TOLERANCE, "tolerance");
-  dmnsn_token_map(DMNSN_T_TOROIDAL, "toroidal");
-  dmnsn_token_map(DMNSN_T_TORUS, "torus");
-  dmnsn_token_map(DMNSN_T_TRACE, "trace");
-  dmnsn_token_map(DMNSN_T_TRANSFORM, "transform");
-  dmnsn_token_map(DMNSN_T_TRANSLATE, "translate");
-  dmnsn_token_map(DMNSN_T_TRANSMIT, "transmit");
-  dmnsn_token_map(DMNSN_T_TRIANGLE, "triangle");
-  dmnsn_token_map(DMNSN_T_TRIANGLE_WAVE, "triangle_wave");
-  dmnsn_token_map(DMNSN_T_TRUE, "true");
-  dmnsn_token_map(DMNSN_T_TTF, "ttf");
-  dmnsn_token_map(DMNSN_T_TURB_DEPTH, "turb_depth");
-  dmnsn_token_map(DMNSN_T_TURBULENCE, "turbulence");
-  dmnsn_token_map(DMNSN_T_TYPE, "type");
-  dmnsn_token_map(DMNSN_T_U, "u");
-  dmnsn_token_map(DMNSN_T_U_STEPS, "u_steps");
-  dmnsn_token_map(DMNSN_T_ULTRA_WIDE_ANGLE, "ultra_wide_angle");
-  dmnsn_token_map(DMNSN_T_UNION, "union");
-  dmnsn_token_map(DMNSN_T_UP, "up");
-  dmnsn_token_map(DMNSN_T_USE_ALPHA, "use_alpha");
-  dmnsn_token_map(DMNSN_T_USE_COLOR, "use_color");
-  dmnsn_token_map(DMNSN_T_USE_INDEX, "use_index");
-  dmnsn_token_map(DMNSN_T_UTF8, "utf8");
-  dmnsn_token_map(DMNSN_T_UV_INDICES, "uv_indices");
-  dmnsn_token_map(DMNSN_T_UV_MAPPING, "uv_mapping");
-  dmnsn_token_map(DMNSN_T_UV_VECTORS, "uv_vectors");
-  dmnsn_token_map(DMNSN_T_V, "v");
-  dmnsn_token_map(DMNSN_T_V_STEPS, "v_steps");
-  dmnsn_token_map(DMNSN_T_VAL, "val");
-  dmnsn_token_map(DMNSN_T_VARIANCE, "variance");
-  dmnsn_token_map(DMNSN_T_VAXIS_ROTATE, "vaxis_rotate");
-  dmnsn_token_map(DMNSN_T_VCROSS, "vcross");
-  dmnsn_token_map(DMNSN_T_VDOT, "vdot");
-  dmnsn_token_map(DMNSN_T_VERTEX_VECTORS, "vertex_vectors");
-  dmnsn_token_map(DMNSN_T_VLENGTH, "vlength");
-  dmnsn_token_map(DMNSN_T_VNORMALIZE, "vnormalize");
-  dmnsn_token_map(DMNSN_T_VROTATE, "vrotate");
-  dmnsn_token_map(DMNSN_T_VSTR, "vstr");
-  dmnsn_token_map(DMNSN_T_VTURBULENCE, "vturbulence");
-  dmnsn_token_map(DMNSN_T_WARP, "warp");
-  dmnsn_token_map(DMNSN_T_WATER_LEVEL, "water_level");
-  dmnsn_token_map(DMNSN_T_WAVES, "waves");
-  dmnsn_token_map(DMNSN_T_WIDTH, "width");
-  dmnsn_token_map(DMNSN_T_WOOD, "wood");
-  dmnsn_token_map(DMNSN_T_WRINKLES, "wrinkles");
-  dmnsn_token_map(DMNSN_T_X, "x");
-  dmnsn_token_map(DMNSN_T_Y, "y");
-  dmnsn_token_map(DMNSN_T_YES, "yes");
-  dmnsn_token_map(DMNSN_T_Z, "z");
-
-  /* Directives */
-  dmnsn_token_map(DMNSN_T_BREAK,      "#break");
-  dmnsn_token_map(DMNSN_T_CASE,       "#case");
-  dmnsn_token_map(DMNSN_T_DEBUG,      "#debug");
-  dmnsn_token_map(DMNSN_T_DECLARE,    "#declare");
-  dmnsn_token_map(DMNSN_T_DEFAULT,    "#default");
-  dmnsn_token_map(DMNSN_T_ELSE,       "#else");
-  dmnsn_token_map(DMNSN_T_END,        "#end");
-  dmnsn_token_map(DMNSN_T_ERROR,      "#error");
-  dmnsn_token_map(DMNSN_T_FCLOSE,     "#fclose");
-  dmnsn_token_map(DMNSN_T_FOPEN,      "#fopen");
-  dmnsn_token_map(DMNSN_T_IF,         "#if");
-  dmnsn_token_map(DMNSN_T_IFDEF,      "#ifdef");
-  dmnsn_token_map(DMNSN_T_IFNDEF,     "#ifndef");
-  dmnsn_token_map(DMNSN_T_INCLUDE,    "#include");
-  dmnsn_token_map(DMNSN_T_LOCAL,      "#local");
-  dmnsn_token_map(DMNSN_T_MACRO,      "#macro");
-  dmnsn_token_map(DMNSN_T_RANGE,      "#range");
-  dmnsn_token_map(DMNSN_T_READ,       "#read");
-  dmnsn_token_map(DMNSN_T_RENDER,     "#render");
-  dmnsn_token_map(DMNSN_T_STATISTICS, "#statistics");
-  dmnsn_token_map(DMNSN_T_SWITCH,     "#switch");
-  dmnsn_token_map(DMNSN_T_UNDEF,      "#undef");
-  dmnsn_token_map(DMNSN_T_VERSION,    "#version");
-  dmnsn_token_map(DMNSN_T_WARNING,    "#warning");
-  dmnsn_token_map(DMNSN_T_WHILE,      "#while");
-  dmnsn_token_map(DMNSN_T_WRITE,      "#write");
-
-  /* Strings */
-  dmnsn_token_map(DMNSN_T_STRING, "string");
-
-  /* Identifiers */
-  dmnsn_token_map(DMNSN_T_IDENTIFIER, "identifier");
-
-  default:
-    fprintf(stderr, "Warning: unrecognised token %d.\n", (int)token_type);
-    return "unrecognized-token";
-  }
-}
diff --git a/tests/dimension/demo.sh b/tests/dimension/demo.sh
index 816a317..452b9bb 100755
--- a/tests/dimension/demo.sh
+++ b/tests/dimension/demo.sh
@@ -20,7 +20,7 @@
 #########################################################################
 
 demo=$(${top_builddir}/dimension/dimension --tokenize --parse ${srcdir}/demo.pov)
-demo_exp='(box { < - (float "2.0") + (integer "1") , - (integer "2") / (float "2.0") , - (float "1.0") > , < (float "1.0") , \( (float "1.0") + (integer "2") \) * (integer "2") - (integer "5") , (float "1.0") + (integer "2") * (integer "2") - (integer "4") > } sphere { < (integer "1") / (integer "5") + - (integer "1") / (integer "5") , (integer "1") / (float "5.0") - (float "1.0") / (integer "5") , - (integer "1") - - (integer "1") > , (float "1.25") })
+demo_exp='(box { < - (float "2.0") + (integer "1") "," - (integer "2") / (float "2.0") "," - (float "1.0") > "," < (float "1.0") "," \( (float "1.0") + (integer "2") \) * (integer "2") - (integer "5") "," (float "1.0") + (integer "2") * (integer "2") - (integer "4") > } sphere { < (integer "1") / (integer "5") + - (integer "1") / (integer "5") "," (integer "1") / (float "5.0") - (float "1.0") / (integer "5") "," - (integer "1") - - (integer "1") > "," (float "1.25") })
 ((box (vector (float -1) (float -1) (float -1)) (vector (float 1) (float 1) (float 1))) (sphere (vector (float 0) (float 0) (integer 0)) (float 1.25)))'
 
 if [ "$demo" != "$demo_exp" ]; then
diff --git a/tests/dimension/numeric.sh b/tests/dimension/numeric.sh
index d4b4d89..372176f 100755
--- a/tests/dimension/numeric.sh
+++ b/tests/dimension/numeric.sh
@@ -20,7 +20,7 @@
 #########################################################################
 
 numeric=$(${top_builddir}/dimension/dimension --tokenize ${srcdir}/numeric.pov)
-numeric_exp='((integer "1") (integer "123456789") (integer "01234567") (integer "0x123456789") - (integer "0x01") (float ".1") (float "0.1") (float "1.0") (float "0.123456789") - (float "0.123456789") < (integer "1") , (float "2.2") , - (float "3.03") >)'
+numeric_exp='((integer "1") (integer "123456789") (integer "01234567") (integer "0x123456789") - (integer "0x01") (float ".1") (float "0.1") (float "1.0") (float "0.123456789") - (float "0.123456789") < (integer "1") "," (float "2.2") "," - (float "3.03") >)'
 
 if [ "$numeric" != "$numeric_exp" ]; then
   echo "numeric.pov tokenized as \"$numeric\"" >&2
diff --git a/tests/dimension/punctuation.sh b/tests/dimension/punctuation.sh
index 3765ffa..4a91cd7 100755
--- a/tests/dimension/punctuation.sh
+++ b/tests/dimension/punctuation.sh
@@ -20,7 +20,7 @@
 #########################################################################
 
 punctuation=$(${top_builddir}/dimension/dimension --tokenize ${srcdir}/punctuation.pov)
-punctuation_exp='({ } \( \) [ ] + - * / , ; ? : & . | = < > ! <= >= !=)'
+punctuation_exp='({ } \( \) [ ] + - * / "," ; ? : & . | = < > ! <= >= !=)'
 
 if [ "$punctuation" != "$punctuation_exp" ]; then
   echo "punctuation.pov tokenized as \"$punctuation\"" >&2
-- 
cgit v1.2.3