From 3da1fe6d2fb07f3f361482d8056e36636dcec3f5 Mon Sep 17 00:00:00 2001
From: Tavian Barnes <tavianator@gmail.com>
Date: Tue, 2 Feb 2010 12:03:26 -0500
Subject: Implement #if.

---
 dimension/common.terminals        |   9 +-
 dimension/directives.declarations |   2 +-
 dimension/directives.nonterminals |   2 +
 dimension/directives.rules        |  51 ++++++++++
 dimension/grammar.epilogue        |   9 ++
 dimension/lexer.l                 |   3 +
 dimension/parse.c                 | 192 +++++++++++++++++++++++++++++++++++---
 dimension/parse.h                 |  11 ++-
 dimension/tokenize.c              | 161 ++++++++++++++++++++++++++++++--
 tests/dimension/directives.pov    |  14 ++-
 tests/dimension/directives.sh     |  14 ++-
 11 files changed, 432 insertions(+), 36 deletions(-)

diff --git a/dimension/common.terminals b/dimension/common.terminals
index 37d3c16..ae5ebdf 100644
--- a/dimension/common.terminals
+++ b/dimension/common.terminals
@@ -47,6 +47,9 @@
 %token DMNSN_T_NOT_EQUAL     "!="
 
 /* Operators */
+%left "|"
+%left "&"
+%left "=" "!=" "<" "<=" ">" ">="
 %left "+" "-"
 %left "*" "/"
 %left "."
@@ -491,12 +494,12 @@
 %token DMNSN_T_DEBUG
 %token DMNSN_T_DECLARE    "#declare"
 %token DMNSN_T_DEFAULT
-%token DMNSN_T_ELSE
-%token DMNSN_T_END
+%token DMNSN_T_ELSE       "#else"
+%token DMNSN_T_END        "#end"
 %token DMNSN_T_ERROR
 %token DMNSN_T_FCLOSE
 %token DMNSN_T_FOPEN
-%token DMNSN_T_IF
+%token DMNSN_T_IF         "#if"
 %token DMNSN_T_IFDEF
 %token DMNSN_T_IFNDEF
 %token DMNSN_T_INCLUDE    "#include"
diff --git a/dimension/directives.declarations b/dimension/directives.declarations
index 0543b3d..3c4da86 100644
--- a/dimension/directives.declarations
+++ b/dimension/directives.declarations
@@ -19,7 +19,7 @@
 
 %name-prefix "dmnsn_ld_yy"
 
-%expect 9
+%expect 10
 
 %parse-param {const char *filename}
 %parse-param {void *yyscanner}
diff --git a/dimension/directives.nonterminals b/dimension/directives.nonterminals
index ac23a0a..702ea47 100644
--- a/dimension/directives.nonterminals
+++ b/dimension/directives.nonterminals
@@ -18,3 +18,5 @@
  *************************************************************************/
 
 %type <astnode> RVALUE
+
+%type <astnode> CONDITIONAL
diff --git a/dimension/directives.rules b/dimension/directives.rules
index 916039b..db47bb9 100644
--- a/dimension/directives.rules
+++ b/dimension/directives.rules
@@ -16,6 +16,18 @@ LANGUAGE_DIRECTIVE: "#declare" "identifier" "=" RVALUE {
                     dmnsn_undef_symbol(symtable, $2);
                     free($2);
                   }
+                  | "#if" "(" CONDITIONAL ")" {
+                    dmnsn_astnode cond = dmnsn_eval($3, symtable);
+                    dmnsn_delete_astnode($3);
+
+                    if (cond.type == DMNSN_AST_NONE) {
+                      dmnsn_delete_astnode(cond);
+                      YYERROR;
+                    }
+
+                    dmnsn_local_symbol(symtable, "__cond__", cond);
+                    dmnsn_delete_astnode(cond);
+                  }
 
 RVALUE: ARITH_EXPR ";" %dprec 2 {
         $$ = dmnsn_eval($1, symtable);
@@ -33,3 +45,42 @@ RVALUE: ARITH_EXPR ";" %dprec 2 {
       | FINISH
       | CAMERA
       | TRANSFORMATION
+
+CONDITIONAL: ARITH_EXPR {
+             /* Force the expression to be evaluated logically */
+             dmnsn_astnode zero = dmnsn_new_astnode(DMNSN_AST_INTEGER, @$);
+             zero.ptr = malloc(sizeof(long));
+             if (!zero.ptr)
+               dmnsn_error(DMNSN_SEVERITY_HIGH,
+                           "Failed to allocate room for integer.");
+             *(long *)zero.ptr = 0;
+
+             $$ = dmnsn_new_astnode2(DMNSN_AST_OR, @$, zero, $1);
+           }
+           | ARITH_EXPR "="  ARITH_EXPR {
+             $$ = dmnsn_new_astnode2(DMNSN_AST_EQUAL, @$, $1, $3);
+           }
+           | ARITH_EXPR "!=" ARITH_EXPR {
+             $$ = dmnsn_new_astnode2(DMNSN_AST_NOT_EQUAL, @$, $1, $3);
+           }
+           | ARITH_EXPR "<"  ARITH_EXPR {
+             $$ = dmnsn_new_astnode2(DMNSN_AST_LESS, @$, $1, $3);
+           }
+           | ARITH_EXPR "<=" ARITH_EXPR {
+             $$ = dmnsn_new_astnode2(DMNSN_AST_LESS_EQUAL, @$, $1, $3);
+           }
+           | ARITH_EXPR ">"  ARITH_EXPR {
+             $$ = dmnsn_new_astnode2(DMNSN_AST_GREATER, @$, $1, $3);
+           }
+           | ARITH_EXPR ">=" ARITH_EXPR {
+             $$ = dmnsn_new_astnode2(DMNSN_AST_GREATER_EQUAL, @$, $1, $3);
+           }
+           | CONDITIONAL "&" CONDITIONAL {
+             $$ = dmnsn_new_astnode2(DMNSN_AST_AND, @$, $1, $3);
+           }
+           | CONDITIONAL "|" CONDITIONAL {
+             $$ = dmnsn_new_astnode2(DMNSN_AST_OR, @$, $1, $3);
+           }
+           | "(" CONDITIONAL ")" {
+             $$ = $2;
+           }
diff --git a/dimension/grammar.epilogue b/dimension/grammar.epilogue
index b62165e..c9e16f3 100644
--- a/dimension/grammar.epilogue
+++ b/dimension/grammar.epilogue
@@ -128,6 +128,15 @@ dmnsn_astnode_string(dmnsn_astnode_type astnode_type)
   dmnsn_astnode_map(DMNSN_AST_MUL, "*");
   dmnsn_astnode_map(DMNSN_AST_DIV, "/");
 
+  dmnsn_astnode_map(DMNSN_AST_EQUAL,         "=" );
+  dmnsn_astnode_map(DMNSN_AST_NOT_EQUAL,     "!=");
+  dmnsn_astnode_map(DMNSN_AST_LESS,          "<" );
+  dmnsn_astnode_map(DMNSN_AST_LESS_EQUAL,    "<=");
+  dmnsn_astnode_map(DMNSN_AST_GREATER,       ">" );
+  dmnsn_astnode_map(DMNSN_AST_GREATER_EQUAL, ">=");
+  dmnsn_astnode_map(DMNSN_AST_AND,           "&" );
+  dmnsn_astnode_map(DMNSN_AST_OR,            "|" );
+
   dmnsn_astnode_map(DMNSN_AST_NEGATE, "-");
   dmnsn_astnode_map(DMNSN_AST_DOT_X, ".x");
   dmnsn_astnode_map(DMNSN_AST_DOT_Y, ".y");
diff --git a/dimension/lexer.l b/dimension/lexer.l
index 9510bcf..cd02da1 100644
--- a/dimension/lexer.l
+++ b/dimension/lexer.l
@@ -208,6 +208,9 @@ unsigned long wchar;
 
 (?# Directives)
 "#declare"      RETURN_TOKEN(DMNSN_T_DECLARE);
+"#else"         RETURN_TOKEN(DMNSN_T_ELSE);
+"#end"          RETURN_TOKEN(DMNSN_T_END);
+"#if"           RETURN_TOKEN(DMNSN_T_IF);
 "#include"      RETURN_TOKEN(DMNSN_T_INCLUDE);
 "#local"        RETURN_TOKEN(DMNSN_T_LOCAL);
 "#undef"        RETURN_TOKEN(DMNSN_T_UNDEF);
diff --git a/dimension/parse.c b/dimension/parse.c
index 9357ef3..6ff5628 100644
--- a/dimension/parse.c
+++ b/dimension/parse.c
@@ -268,6 +268,7 @@ dmnsn_copy_astnode(dmnsn_astnode astnode)
   return copy;
 }
 
+/* 5-element vectors */
 #define DMNSN_VECTOR_NELEM 5
 
 static dmnsn_astnode
@@ -455,32 +456,189 @@ dmnsn_eval_binary(dmnsn_astnode astnode, dmnsn_symbol_table *symtable)
   lhs = dmnsn_eval(lhs, symtable);
   rhs = dmnsn_eval(rhs, symtable);
 
-  dmnsn_astnode ret = dmnsn_copy_astnode(astnode);
+  dmnsn_astnode ret;
 
   if (lhs.type == DMNSN_AST_NONE || rhs.type == DMNSN_AST_NONE) {
+    ret = dmnsn_copy_astnode(astnode);
     ret.type = DMNSN_AST_NONE;
   } else if (lhs.type == DMNSN_AST_VECTOR || rhs.type == DMNSN_AST_VECTOR) {
-    ret.type = DMNSN_AST_VECTOR;
-
     dmnsn_astnode oldlhs = lhs, oldrhs = rhs;
     lhs = dmnsn_vector_promote(lhs, symtable);
     rhs = dmnsn_vector_promote(rhs, symtable);
     dmnsn_delete_astnode(oldlhs);
     dmnsn_delete_astnode(oldrhs);
 
-    dmnsn_astnode op = dmnsn_copy_astnode(astnode);
-    for (i = 0; i < DMNSN_VECTOR_NELEM; ++i) {
-      dmnsn_array_get(lhs.children, i, dmnsn_array_at(op.children, 0));
-      dmnsn_array_get(rhs.children, i, dmnsn_array_at(op.children, 1));
-      dmnsn_astnode temp = dmnsn_eval_binary(op, symtable);
-      dmnsn_array_set(ret.children, i, &temp);
-    }
+    switch (astnode.type) {
+    case DMNSN_AST_EQUAL:
+      {
+        dmnsn_astnode rewrite = dmnsn_copy_astnode(astnode);
+
+        dmnsn_astnode l, r;
+        dmnsn_array_get(lhs.children, 0, &l);
+        dmnsn_array_get(rhs.children, 0, &r);
+        ++*l.refcount;
+        ++*r.refcount;
+        dmnsn_array_push(rewrite.children, &l);
+        dmnsn_array_push(rewrite.children, &r);
+
+        for (i = 1; i < DMNSN_VECTOR_NELEM; ++i) {
+          dmnsn_astnode temp = dmnsn_copy_astnode(astnode);
+          dmnsn_array_get(lhs.children, i, &l);
+          dmnsn_array_get(rhs.children, i, &r);
+          ++*l.refcount;
+          ++*r.refcount;
+          dmnsn_array_push(temp.children, &l);
+          dmnsn_array_push(temp.children, &r);
+
+          dmnsn_astnode next = dmnsn_copy_astnode(astnode);
+          next.type = DMNSN_AST_AND;
+          dmnsn_array_push(next.children, &rewrite);
+          dmnsn_array_push(next.children, &temp);
+          rewrite = next;
+        }
+
+        ret = dmnsn_eval_binary(rewrite, symtable);
+        dmnsn_delete_astnode(rewrite);
+        break;
+      }
+
+    case DMNSN_AST_NOT_EQUAL:
+      {
+        dmnsn_astnode rewrite = dmnsn_copy_astnode(astnode);
+
+        dmnsn_astnode l, r;
+        dmnsn_array_get(lhs.children, 0, &l);
+        dmnsn_array_get(rhs.children, 0, &r);
+        ++*l.refcount;
+        ++*r.refcount;
+        dmnsn_array_push(rewrite.children, &l);
+        dmnsn_array_push(rewrite.children, &r);
+
+        for (i = 1; i < DMNSN_VECTOR_NELEM; ++i) {
+          dmnsn_astnode temp = dmnsn_copy_astnode(astnode);
+          dmnsn_array_get(lhs.children, i, &l);
+          dmnsn_array_get(rhs.children, i, &r);
+          ++*l.refcount;
+          ++*r.refcount;
+          dmnsn_array_push(temp.children, &l);
+          dmnsn_array_push(temp.children, &r);
+
+          dmnsn_astnode next = dmnsn_copy_astnode(astnode);
+          next.type = DMNSN_AST_OR;
+          dmnsn_array_push(next.children, &rewrite);
+          dmnsn_array_push(next.children, &temp);
+          rewrite = next;
+        }
 
-    dmnsn_delete_array(op.children);
-    op.children = NULL;
-    dmnsn_delete_astnode(op);
+        ret = dmnsn_eval_binary(rewrite, symtable);
+        dmnsn_delete_astnode(rewrite);
+        break;
+      }
+
+    case DMNSN_AST_AND:
+      {
+        dmnsn_astnode rewrite = dmnsn_copy_astnode(astnode);
+        rewrite.type = DMNSN_AST_OR;
+
+        dmnsn_astnode l, r;
+        dmnsn_array_get(lhs.children, 0, &l);
+        dmnsn_array_get(rhs.children, 0, &r);
+        ++*l.refcount;
+        ++*r.refcount;
+        dmnsn_array_push(rewrite.children, &l);
+        dmnsn_array_push(rewrite.children, &r);
+
+        for (i = 1; i < DMNSN_VECTOR_NELEM; ++i) {
+          dmnsn_astnode temp = dmnsn_copy_astnode(astnode);
+          temp.type = DMNSN_AST_OR;
+          dmnsn_array_get(lhs.children, i, &l);
+          dmnsn_array_get(rhs.children, i, &r);
+          ++*l.refcount;
+          ++*r.refcount;
+          dmnsn_array_push(temp.children, &l);
+          dmnsn_array_push(temp.children, &r);
+
+          dmnsn_astnode next = dmnsn_copy_astnode(astnode);
+          next.type = DMNSN_AST_AND;
+          dmnsn_array_push(next.children, &rewrite);
+          dmnsn_array_push(next.children, &temp);
+          rewrite = next;
+        }
+
+        ret = dmnsn_eval_binary(rewrite, symtable);
+        dmnsn_delete_astnode(rewrite);
+        break;
+      }
+
+    case DMNSN_AST_OR:
+      {
+        dmnsn_astnode rewrite = dmnsn_copy_astnode(astnode);
+        rewrite.type = DMNSN_AST_OR;
+
+        dmnsn_astnode l, r;
+        dmnsn_array_get(lhs.children, 0, &l);
+        dmnsn_array_get(rhs.children, 0, &r);
+        ++*l.refcount;
+        ++*r.refcount;
+        dmnsn_array_push(rewrite.children, &l);
+        dmnsn_array_push(rewrite.children, &r);
+
+        for (i = 1; i < DMNSN_VECTOR_NELEM; ++i) {
+          dmnsn_astnode temp = dmnsn_copy_astnode(astnode);
+          temp.type = DMNSN_AST_OR;
+          dmnsn_array_get(lhs.children, i, &l);
+          dmnsn_array_get(rhs.children, i, &r);
+          ++*l.refcount;
+          ++*r.refcount;
+          dmnsn_array_push(temp.children, &l);
+          dmnsn_array_push(temp.children, &r);
+
+          dmnsn_astnode next = dmnsn_copy_astnode(astnode);
+          next.type = DMNSN_AST_OR;
+          dmnsn_array_push(next.children, &rewrite);
+          dmnsn_array_push(next.children, &temp);
+          rewrite = next;
+        }
+
+        ret = dmnsn_eval_binary(rewrite, symtable);
+        dmnsn_delete_astnode(rewrite);
+        break;
+      }
+
+    case DMNSN_AST_LESS:
+    case DMNSN_AST_LESS_EQUAL:
+    case DMNSN_AST_GREATER:
+    case DMNSN_AST_GREATER_EQUAL:
+      dmnsn_diagnostic(astnode.filename, astnode.line, astnode.col,
+                       "invalid comparison operator '%s' between vectors",
+                       dmnsn_astnode_string(astnode.type));
+      ret = dmnsn_copy_astnode(astnode);
+      ret.type = DMNSN_AST_NONE;
+      break;
+
+    default:
+      {
+        ret = dmnsn_copy_astnode(astnode);
+        ret.type = DMNSN_AST_VECTOR;
+
+        dmnsn_astnode op = dmnsn_copy_astnode(astnode);
+        for (i = 0; i < DMNSN_VECTOR_NELEM; ++i) {
+          dmnsn_array_get(lhs.children, i, dmnsn_array_at(op.children, 0));
+          dmnsn_array_get(rhs.children, i, dmnsn_array_at(op.children, 1));
+          dmnsn_astnode temp = dmnsn_eval_binary(op, symtable);
+          dmnsn_array_set(ret.children, i, &temp);
+        }
+
+        dmnsn_delete_array(op.children);
+        op.children = NULL;
+        dmnsn_delete_astnode(op);
+        break;
+      }
+    }
   } else if (lhs.type == DMNSN_AST_INTEGER && rhs.type == DMNSN_AST_INTEGER
              && astnode.type != DMNSN_AST_DIV) {
+    ret = dmnsn_copy_astnode(astnode);
+
     long l, r;
     l = *(long *)lhs.ptr;
     r = *(long *)rhs.ptr;
@@ -532,6 +690,8 @@ dmnsn_eval_binary(dmnsn_astnode astnode, dmnsn_symbol_table *symtable)
     ret.type = DMNSN_AST_INTEGER;
     ret.ptr  = res;
   } else {
+    ret = dmnsn_copy_astnode(astnode);
+
     double l = 0.0, r = 0.0;
 
     if (lhs.type == DMNSN_AST_INTEGER) {
@@ -543,7 +703,8 @@ dmnsn_eval_binary(dmnsn_astnode astnode, dmnsn_symbol_table *symtable)
                        "expected %s, %s, or %s; found %s",
                        dmnsn_astnode_string(DMNSN_AST_INTEGER),
                        dmnsn_astnode_string(DMNSN_AST_FLOAT),
-                       dmnsn_astnode_string(DMNSN_AST_VECTOR));
+                       dmnsn_astnode_string(DMNSN_AST_VECTOR),
+                       dmnsn_astnode_string(lhs.type));
       ret.type = DMNSN_AST_NONE;
       dmnsn_delete_astnode(lhs);
       dmnsn_delete_astnode(rhs);
@@ -559,7 +720,8 @@ dmnsn_eval_binary(dmnsn_astnode astnode, dmnsn_symbol_table *symtable)
                        "expected %s, %s, or %s; found %s",
                        dmnsn_astnode_string(DMNSN_AST_INTEGER),
                        dmnsn_astnode_string(DMNSN_AST_FLOAT),
-                       dmnsn_astnode_string(DMNSN_AST_VECTOR));
+                       dmnsn_astnode_string(DMNSN_AST_VECTOR),
+                       dmnsn_astnode_string(rhs.type));
       ret.type = DMNSN_AST_NONE;
       dmnsn_delete_astnode(lhs);
       dmnsn_delete_astnode(rhs);
diff --git a/dimension/parse.h b/dimension/parse.h
index bbc52e7..28997af 100644
--- a/dimension/parse.h
+++ b/dimension/parse.h
@@ -83,11 +83,20 @@ typedef enum {
   DMNSN_AST_DOT_T,
   DMNSN_AST_DOT_TRANSMIT,
 
+  DMNSN_AST_EQUAL,
+  DMNSN_AST_NOT_EQUAL,
+  DMNSN_AST_LESS,
+  DMNSN_AST_LESS_EQUAL,
+  DMNSN_AST_GREATER,
+  DMNSN_AST_GREATER_EQUAL,
+  DMNSN_AST_AND,
+  DMNSN_AST_OR,
+
   DMNSN_AST_COLOR,
 
   DMNSN_AST_IDENTIFIER,
 
-  DMNSN_AST_STRING,
+  DMNSN_AST_STRING
 } dmnsn_astnode_type;
 
 /* Abstract syntax tree node (a dmnsn_array* of these is an AST) */
diff --git a/dimension/tokenize.c b/dimension/tokenize.c
index 5fce280..8e696ed 100644
--- a/dimension/tokenize.c
+++ b/dimension/tokenize.c
@@ -20,6 +20,7 @@
 #include "tokenize.h"
 #include "directives.h"
 #include "utility.h"
+#include <stdbool.h>
 
 typedef struct dmnsn_buffered_token {
   int type;
@@ -39,7 +40,7 @@ typedef struct dmnsn_token_buffer {
 } dmnsn_token_buffer;
 
 static dmnsn_token_buffer *
-dmnsn_new_token_buffer(int type)
+dmnsn_new_token_buffer(int type, dmnsn_token_buffer *prev)
 {
   dmnsn_token_buffer *tbuffer = malloc(sizeof(dmnsn_token_buffer));
   if (!tbuffer) {
@@ -49,7 +50,7 @@ dmnsn_new_token_buffer(int type)
   tbuffer->type = type;
   tbuffer->buffered = dmnsn_new_array(sizeof(dmnsn_buffered_token));
   tbuffer->i = 0;
-  tbuffer->prev = NULL;
+  tbuffer->prev = prev;
   return tbuffer;
 }
 
@@ -73,8 +74,8 @@ dmnsn_declaration_buffer(int token, dmnsn_token_buffer *prev,
                          const char *filename, dmnsn_symbol_table *symtable,
                          void *yyscanner)
 {
-  dmnsn_token_buffer *tbuffer = dmnsn_new_token_buffer(DMNSN_T_LEX_VERBATIM);
-  tbuffer->prev = prev;
+  dmnsn_token_buffer *tbuffer
+    = dmnsn_new_token_buffer(DMNSN_T_LEX_VERBATIM, prev);
 
   /* Buffer the current token */
   dmnsn_buffered_token buffered = {
@@ -133,8 +134,8 @@ dmnsn_undef_buffer(int token, dmnsn_token_buffer *prev,
                    const char *filename, dmnsn_symbol_table *symtable,
                    void *yyscanner)
 {
-  dmnsn_token_buffer *tbuffer = dmnsn_new_token_buffer(DMNSN_T_LEX_VERBATIM);
-  tbuffer->prev = prev;
+  dmnsn_token_buffer *tbuffer
+    = dmnsn_new_token_buffer(DMNSN_T_LEX_VERBATIM, prev);
 
   /* Buffer the current token */
   dmnsn_buffered_token buffered = {
@@ -168,6 +169,140 @@ dmnsn_undef_buffer(int token, dmnsn_token_buffer *prev,
   return tbuffer;
 }
 
+static dmnsn_token_buffer *
+dmnsn_if_buffer(int token, dmnsn_token_buffer *prev,
+                dmnsn_parse_item *lvalp, dmnsn_parse_location *llocp,
+                const char *filename, dmnsn_symbol_table *symtable,
+                void *yyscanner)
+{
+  dmnsn_token_buffer *cond_buffer
+    = dmnsn_new_token_buffer(DMNSN_T_LEX_VERBATIM, prev);
+
+  /* Buffer the current token */
+  dmnsn_buffered_token buffered = {
+    .type = token,
+    .lval = *lvalp,
+    .lloc = *llocp
+  };
+  dmnsn_array_push(cond_buffer->buffered, &buffered);
+
+  /* Grab all the tokens belonging to the #if (...)  */
+  int parenlevel = -1;
+  while (1) {
+    /* Recursive call - permit other directives inside the condition */
+    buffered.type = dmnsn_yylex(&buffered.lval, &buffered.lloc,
+                                filename, symtable, yyscanner);
+
+    if (buffered.type == DMNSN_T_EOF) {
+      dmnsn_diagnostic(filename, buffered.lloc.first_line,
+                       buffered.lloc.first_column,
+                       "syntax error, unexpected end-of-file");
+      dmnsn_delete_token_buffer(cond_buffer);
+      return NULL;
+    } else if (buffered.type == DMNSN_T_LEX_ERROR) {
+      dmnsn_delete_token_buffer(cond_buffer);
+      return NULL;
+    }
+
+    dmnsn_array_push(cond_buffer->buffered, &buffered);
+
+    if (buffered.type == DMNSN_T_LPAREN) {
+      if (parenlevel < 0)
+        parenlevel = 1;
+      else
+        ++parenlevel;
+    } else if (buffered.type == DMNSN_T_RPAREN) {
+      --parenlevel;
+      if (parenlevel == 0) {
+        break;
+      }
+    }
+  }
+
+  /* Fake EOF */
+  buffered.type = DMNSN_T_EOF;
+  dmnsn_array_push(cond_buffer->buffered, &buffered);
+
+  dmnsn_yyset_extra(cond_buffer, yyscanner);
+  if (dmnsn_ld_yyparse(filename, yyscanner, symtable) != 0) {
+    dmnsn_yyset_extra(cond_buffer->prev, yyscanner);
+    dmnsn_delete_token_buffer(cond_buffer);
+    return NULL;
+  }
+
+  dmnsn_yyset_extra(cond_buffer->prev, yyscanner);
+  dmnsn_delete_token_buffer(cond_buffer);
+
+  dmnsn_token_buffer *tbuffer= dmnsn_new_token_buffer(DMNSN_T_IF, prev);
+
+  dmnsn_astnode *cnode = dmnsn_find_symbol(symtable, "__cond__");
+  if (!cnode) {
+    dmnsn_error(DMNSN_SEVERITY_HIGH, "__cond__ unset.");
+  }
+
+  bool cond = false;
+  if (cnode->type == DMNSN_AST_INTEGER) {
+    cond = (*(long *)cnode->ptr) ? true : false;
+  } else if (cnode->type == DMNSN_AST_FLOAT) {
+    cond = (*(double *)cnode->ptr) ? true : false;
+  } else {
+    dmnsn_error(DMNSN_SEVERITY_HIGH, "__cond__ has wrong type.");
+  }
+
+  dmnsn_undef_symbol(symtable, "__cond__");
+
+  int nesting = 1;
+  while (1) {
+    /* Non-recursive call */
+    buffered.type = dmnsn_yylex_impl(&buffered.lval, &buffered.lloc,
+                                     filename, yyscanner);
+
+    if (buffered.type == DMNSN_T_EOF) {
+      dmnsn_diagnostic(filename, buffered.lloc.first_line,
+                       buffered.lloc.first_column,
+                       "syntax error, unexpected end-of-file");
+      dmnsn_delete_token_buffer(tbuffer);
+      return NULL;
+    } else if (buffered.type == DMNSN_T_LEX_ERROR) {
+      dmnsn_delete_token_buffer(tbuffer);
+      return NULL;
+    }
+
+    switch (buffered.type) {
+    case DMNSN_T_IF:
+    case DMNSN_T_IFDEF:
+    case DMNSN_T_IFNDEF:
+    case DMNSN_T_MACRO:
+    case DMNSN_T_SWITCH:
+    case DMNSN_T_WHILE:
+      ++nesting;
+      break;
+
+    case DMNSN_T_END:
+      --nesting;
+      break;
+
+    default:
+      break;
+    }
+
+    if (nesting == 0) {
+      break;
+    } else if (nesting == 1 && buffered.type == DMNSN_T_ELSE) {
+      cond = !cond;
+      continue;
+    }
+
+    if (cond) {
+      dmnsn_array_push(tbuffer->buffered, &buffered);
+    } else {
+      free(buffered.lval.value);
+    }
+  }
+
+  return tbuffer;
+}
+
 int
 dmnsn_yylex(dmnsn_parse_item *lvalp, dmnsn_parse_location *llocp,
             const char *filename, dmnsn_symbol_table *symtable, void *yyscanner)
@@ -260,6 +395,20 @@ dmnsn_yylex(dmnsn_parse_item *lvalp, dmnsn_parse_location *llocp,
         break;
       }
 
+    case DMNSN_T_IF:
+      {
+        dmnsn_token_buffer *tb = dmnsn_if_buffer(
+          token, tbuffer, lvalp, llocp, filename, symtable, yyscanner
+        );
+        if (!tb) {
+          return DMNSN_T_LEX_ERROR;
+        }
+
+        dmnsn_yyset_extra(tb, yyscanner);
+        tbuffer = tb;
+        continue;
+      }
+
     default:
       return token;
     }
diff --git a/tests/dimension/directives.pov b/tests/dimension/directives.pov
index 95ad88a..1bb712d 100644
--- a/tests/dimension/directives.pov
+++ b/tests/dimension/directives.pov
@@ -26,9 +26,13 @@
 #declare Unused = -1;
 #undef Unused
 
-sphere {
-  Center, R
-  pigment {
-    color Color green 1
+#if (#if (1 = 1) 0 #end = 0 & 0)
+  Illegal
+#else
+  sphere {
+    Center, R
+    pigment {
+      color Color green 1
+    }
   }
-}
+#end
diff --git a/tests/dimension/directives.sh b/tests/dimension/directives.sh
index b2c9e9e..955ea15 100755
--- a/tests/dimension/directives.sh
+++ b/tests/dimension/directives.sh
@@ -26,12 +26,16 @@ directives_exp="$(echo -n \
   #local (identifier "Color") = rgb < (integer "1") , (integer "0") , (integer "1") > ;
   #declare (identifier "Unused") = - (integer "1") ;
   #undef (identifier "Unused")
-  sphere {
-    (identifier "Center") , (identifier "R")
-    pigment {
-      color (identifier "Color") green (integer "1")
+  #if \( #if \( (integer "1") = (integer "1") \) (integer "0") #end = (integer "0") & (integer "0") \)
+    (identifier "Illegal")
+  #else
+    sphere {
+      (identifier "Center") , (identifier "R")
+      pigment {
+        color (identifier "Color") green (integer "1")
+      }
     }
-  })' \
+  #end)' \
 | tr '\n' ' ' | sed -r 's/[[:space:]]+/ /g')
 $(echo -n \
 '((sphere
-- 
cgit v1.2.3