/* yacc.y,v 1.3 1995/04/08 19:53:07 explorer Exp */

/* Copyright (C) 1989, 1991, Craig E. Kolb				   */
/* All rights reserved.							   */
/*									   */
/* This software may be freely copied, modified, and redistributed,	   */
/* provided that this copyright notice is preserved on all copies.	   */
/*									   */
/* You may not distribute this software, in whole or in part, as part of   */
/* any commercial product without the express consent of the authors.	   */
/* 									   */
/* There is no warranty or other guarantee of fitness of this software	   */
/* for any purpose.  It is provided solely "as is".			   */

%{
#include "rayshade.h"

#include "symtab.h"
#include "builtin.h"

#include "libsurf/atmosphere.h"
#include "libsurf/surface.h"
#include "libtext/texture.h"
#include "libimage/image.h"
#include "libobj/geom.h"
#include "liblight/light.h"
#include "options.h"
#include "stats.h"
#include "viewing.h"

#include "libobj/blob.h"
#include "libobj/box.h"
#include "libobj/cone.h"
#include "libobj/csg.h"
#include "libobj/cylinder.h"
#include "libobj/disc.h"
#include "libobj/flame.h"    /* MANI */
#include "libobj/grid.h"
#include "libobj/hf.h"
#include "libobj/fl.h"
#include "libobj/instance.h"
#include "libobj/list.h"
#include "libobj/plane.h"
#include "libobj/poly.h"
#include "libobj/sphere.h"
#include "libobj/sweptsph.h"
#include "libobj/torus.h"
#include "libobj/triangle.h"
#include "libobj/rotspline.h"
#include "libobj/ifs.h"
#include "libobj/mountain.h"
#include "libobj/fractalobject.h"
#include "libobj/allocmatrix.h"
#include "libobj/patch.h"
#include "libobj/cubicspin.h"

#include "liblight/point.h"
#include "liblight/infinite.h"
#include "liblight/spot.h"
#include "liblight/jittered.h"
#include "liblight/extended.h"
#include "liblight/projector.h"

#include "libtext/blotch.h"
#include "libtext/bump.h"
#include "libtext/checker.h"
#include "libtext/cloud.h"
#include "libtext/fbm.h"
#include "libtext/fbmbump.h"
#include "libtext/gloss.h"
#include "libtext/gradient.h"
#include "libtext/imagetext.h"
#include "libtext/marble.h"
#include "libtext/mount.h"
#include "libtext/sky.h"
#include "libtext/stripe.h"
#include "libtext/windy.h"
#include "libtext/wood.h"

#include "libsurf/fog.h"
#include "libsurf/fogdeck.h"
#include "libsurf/mist.h"

#include "libcommon/rotate.h"
#include "libcommon/scale.h"
#include "libcommon/translate.h"
#include "libcommon/xform.h"


char yyfilename[BUFSIZ];			/* Input filename */
GeomList *Defstack;				/* Geom definition stack. */
int Npoints = 0;				/* # of points in Polypoints */
float last_red, last_blue, last_green;          /* most recent color spec. */
Surface *tmpsurf;				/* Working surface */
SurfList *CurSurf;
Texture *CurText;				/* Working list of textures */
ImageText *Imagetext;				/* Working image texture */
Trans *TransHead, *TransTail;			/* Linked list of current transformations */
Atmosphere *CurEffect = (Atmosphere *)NULL;	/* Current atmos. effects */ 
PointList *Polypoints;				/* List of vertices */
MetaList *Metapoints, *Metapoint, *CMetapoints, *CMetapoint;
PointPool *Pointpool;
TrianglePool *Trianglepool;
EntityPool *Entitypool;
FractalEntity *CurrEntity;
FractalTriangle *CurrTriangle;
FractalPoint *CurrPoint;
int    count;

extern FILE *yyin;				/* input file pointer */

#ifdef HAVE_YYLINENO
extern int yylineno;				/* Current line # in file */
#else
int        yylineno;                            /* roll our own I suppose */
#endif

extern Atmosphere *AtmosEffects;		/* atmospheric effects */
extern Medium TopMedium;			/* "air" */
static IfsOptions ifsOptions;                   /* options for IFS object */
static int ifsTransformRang;                    /* rang of IFS map */
extern Surface DefaultSurface;

void yyerror _PROTO((/* char *, char *, char * */));

Geom *NewAggregate _PROTO((Geom *));

int itemp;
Float *Rcoeffs;                         /* Coeffs for Rotspline */
Vector cent[4];				/* Points for SweptSph Curve */
Float rad[4];				/* Equation for SweptSph Radius */
%}

%union {
	char *c;
	int i;
	Float d;
	Vector v;
	Vec2d uv;
	Color col;
	Atmosphere *atmos;
	Light *light;
	Surface *surf;
	Geom *obj;
	Texture *text;
	Mapping *map;
	Trans *trans;
	Expr *e;
	SymtabEntry *sym;
	IfsTransList *translist;
        HeightValue   *heightvalues;
        StartGridStruct *startgrid;
        FractalClosure *fractalclosure;
        PointPool *Fpointpool;
        TrianglePool *Ftrianglepool;
        EntityPool *Fentitypool;
        TriangleList *Fentitytriangles;
	AnimVal *val;
}

%token <d> tFLOAT
%token <c> tIDENT tFILENAME tTEXT tENVVAR
%token tAPERTURE tAPPLYSURF
%token tBACKGROUND tBLOB tCBLOB tBLOTCH tBOX tBUMP tCONE tCYL tDIRECTIONAL tCURSURF
%token tEXTENDED tEYEP tFBM tFBMBUMP tFLAME tFOCALDIST tFOG tFOGDECK tFOV tGLOSS tGRID
%token tHEIGHTFIELD tLIGHT tLIST tLOOKP tMARBLE tMAXDEPTH tMIST tFRACLAND
%token tJITTER tNOJITTER tDEFINE
%token tOBJECT tOUTFILE  tSKY tDISC tDIFFERENCE tUNION tINTERSECT
%token tPLANE tPOINT tPOLY tROTATE tSPOT tPRINT
%token tSCALE tSCREEN tSPHERE tSURFACE
%token tTHRESH tTRANSLATE tTRANSFORM tTRIANGLE tTRIANGLEUV tUP tEND
%token tTEXTURE tCHECKER tWOOD tCONTRAST tCUTOFF tCLOUD tGRADIENT
%token tAMBIENT tDIFFUSE tREFLECT tTRANSP tSPECULAR tSPECPOW
%token tINDEX tATMOSPHERE tNOSHADOW tAREA tTRANSLU tTORUS
%token tEYESEP tSHADOWTRANSP tREPORT tVERBOSE tQUIET tWINDOW tCROP tSTRIPE
%token tMAP tUV tSPHERICAL tCYLINDRICAL tPLANAR
%token tIMAGE tSMOOTH tCOMPONENT tTEXTSURF tRANGE tTILE tSTARTTIME tFRAMELENGTH
%token tNAME tFILTER tGAUSS tBODY tSAMPLE tEXTINCT tWINDY tMOUNT
%token tSHUTTER tFRAMES
%token tPROJECTOR tTRUE tFALSE
%token tROTSPLINE tCOEFFS
%token tLINEAR tLOG tRADIAL tREVLOG tRANDOM
%token tSWEPTSPH tBEZIER tXBEZIER tYBEZIER tZBEZIER tXCOEFFS tYCOEFFS tZCOEFFS
%token tIFS tMINSIZE tBOUNDING
%token tNORMALWEIGHTING tLOWPASS tHIGHPASS tCONSTANT 
%token tMOUNTAIN
%token tFRACTALOBJECT tFPOINTS tFTRIANGLES tFENTITIES tENTITY
%token tPATCH tMATRIX tHERMITE tCARDINAL tBSPLINE tBEZIER tCUBICSPIN
%token tAT
%token tEQUAL tNOTEQUAL tLESSTHAN tLESSEQUAL tGREATERTHAN tGREATEREQUAL
%token tAND tOR

%type <i> IfsNormalWeightingMethod
%type <c> Filename
%type <e> AnimExpr MExpr ParenExpr
%type <d> Expr Float GradStart GradStop
%type <v> Vector
%type <uv> Vec2d
%type <col> Color Intensity Lightdef
%type <text> Texturetype
%type <i> SurfCompName IExpr CombineOp
%type <i> BExpr
%type <i> GradientType GradientShape GradientRand
%type <i> MatrixType
%type <atmos> EffectType
%type <light> LightType
%type <obj> PrimType Primitive TransTextObj
%type <obj> Csg Aggregate Object TransObj ObjType
%type <obj> Blob CBlob Box Cone Cylinder Disc Flame HeightField Plane Poly FracLand
%type <obj> Sphere Triangle Torus AggregateType List Grid AggregateCreate
%type <obj> Patch
%type <obj> NamedObject
%type <obj> Rotspline
%type <obj> SweptSph
%type <obj> CubicSpin
%type <surf> Surface OptSurface NamedSurf
%type <surf> SurfSpec ModifyNamedSurf
%type <map> Mapping MapMethod OptMapping
%type <trans> TransformType
%type <sym> Symtabent
%type <obj> IfsCondensation IfsLeaves Ifs CIfs
%type <translist> IfsTransforms
%type <obj> Mountain
%type <obj> FractalObject
%type <heightvalues> HeightValues
%type <startgrid> StartGrid
%type <Fpointpool> FPointPool
%type <Ftrianglepool> FTrianglePool
%type <Fentitypool> FEntityPool
%type <Fentitytriangles> FEntityTriangles
%type <fractalclosure> FFractalClosure
%type <e> Mode
%type <val> AnimVals                                                   
%type <d> Sync

%right '?'
%left tAND tOR
%left '+' '-'
%left '*' '/' '%'
%left tEQUAL tNOTEQUAL tLESSTHAN tLESSEQUAL tGREATERTHAN tGREATEREQUAL
%left UMINUS
%right '^'

%%
Items		: /* empty */
		| Items Item
		;
Item		: Eyep
		| Lookp
		| Up
		| Fov
		| Screen
		| Window
		| Crop
		| Report
		| Aperture
		| Focaldist
		| Eyesep
		| Maxdepth
		| Sample
		| Filter
		| Contrast
		| Cutoff
		| Background
		| Shadowtransp
		| Light
		| SurfDef
		| CurSurf
		| Outfile
		| Instance
		| NameObject
		| GlobalEffects
		| Define
		| Frames
		| Starttime
		| Shutter
		| Framelength
    		| Print
		;
Instance	: TransTextObj
		{
			if ($1) {
				/*
				 * Add instance to current object.
				 */
				$1->next = Defstack->obj->next;
				Defstack->obj->next = $1;
			}
		}
TransTextObj	: TransObj Textures
		{
			if ($$ && CurText) {
				$$->texture = TextAppend(CurText, $$->texture);
			}
			CurText = (Texture *)NULL;
		}
		;	
TransObj	: Object Transforms
		{
			$$ = $1;
			if ($$ != (Geom *)NULL) {
				if (TransHead) {
					$$->trans = TransHead;
					$$->transtail = TransTail;
					/*
					 * We compose non-animated tranformation lists,
					 * so we're only animated if it's one long,
					 * or it's animated itself.
					 */
					if ($$->trans->assoc || $$->trans->next)
						/* geometry is animated...*/
						$$->animtrans = TRUE;
				}
			}
		}
		;
Object		: ObjType
		{
			if ($$)
				StatsAddRep($$);
		}
		| NamedObject
		;
ObjType		: Primitive
		| Aggregate
		;
Primitive	: PrimType
		{
			if ($$)
				$$->prims = 1;	/* one primitive */
		}
		;
PrimType	: Plane
		| Sphere
		| Box
		| Triangle
		| Cylinder
		| Cone
		| Poly
		| HeightField
		| Disc
		| Torus
		| Blob
                | CBlob
                | FracLand
                | Rotspline
		| SweptSph
		| Flame
                | Ifs
                | CIfs
                | Mountain
                | FractalObject
                | Patch
                | CubicSpin
		;
NameObject	: tNAME tIDENT TransTextObj
		{
			if ($3) {
				$3->name = $2;
				GeomAddToDefined($3);
			}
		};
Aggdefs		: Aggdefs Aggdef
		|
		;
Aggdef		: Instance
		| SurfDef
		| CurSurf
		| NameObject
		;
Textures	: Textures Texture
		|
		;
Texture		: tTEXTURE Texturetype Transforms
		{
			if ($2 != (Texture *)NULL) {
				/*
				 * Set transformation information.
				 */
				if (TransHead) {
					$2->trans = TransHead;
					/*
					 * We compose non-animated tranformation lists,
					 * so we're only animated if it's one long,
					 * or it's animated itself.
					 */
					if ($2->trans->assoc || $2->trans->next)
						/* texture transformation is animated...*/
						$2->animtrans = TRUE;
				}
				/*
				 * Walk to the end of list of textures and
				 * append new texture.  This is done so that
				 * textures are applied in the expected order.
				 */
				CurText = TextAppend($2, CurText);
			}
		}
		;
Texturetype	: tCHECKER Surface
		{
			$$ = TextCheckerCreate($2);
		}
		| tBLOTCH Expr Surface
		{
			$$ = TextBlotchCreate($2, $3);
		}
		| tBUMP Expr
		{
			$$ = TextBumpCreate($2);
		}
		| tMARBLE
		{
			$$ = TextMarbleCreate((char *)NULL);
		}
		| tMARBLE Filename
		{
			$$ = TextMarbleCreate($2);
		}
		| tFBM Expr Expr Expr Expr IExpr Expr
		{
			$$ = TextFBmCreate($2, $3, $4, $5, $6, $7,
						(char *)NULL);
		}
		| tFBM Expr Expr Expr Expr IExpr Expr Filename
		{
			$$ = TextFBmCreate($2, $3, $4, $5, $6, $7, $8);
		}
		| tFBMBUMP Expr Expr Expr Expr IExpr 
		{
			$$ = TextFBmBumpCreate($2, $3, $4, $5, $6);
		}
		| tWOOD
		{
			$$ = TextWoodCreate();
		}
		| tGLOSS Expr 
		{
			$$ = TextGlossCreate($2);
		}
		| tCLOUD Expr Expr Expr IExpr Expr Expr Expr
		{
			$$ = TextCloudCreate($2, $3, $4, $5, $6, $7, $8);
		}
		| tSKY Expr Expr Expr IExpr Expr Expr
		{
			$$ = TextSkyCreate($2, $3, $4, $5, $6, $7);
		}
		| ImageTexture
		{
			/*
			 * Image texturing has so many options
			 * that specification is keyword-based.
			 */
			if (Imagetext->image == (Image *)NULL)
				$$ = (Texture *)NULL;
			else
				$$ = TextCreate((TextRef)Imagetext,
						ImageTextApply);
			Imagetext = (ImageText *)NULL;
		}
		| tSTRIPE Surface Expr Expr OptMapping
		{
			$$ = TextStripeCreate($2, $3, $4, $5);
		}
		| tWINDY Expr Expr Expr Expr IExpr Expr Expr Expr
		{
			$$ = TextWindyCreate($2, $3, $4, $5, $6, $7, $8, $9);
		}
		| tMOUNT Filename Expr Expr
		{
			$$ = TextMountCreate($2, $3, $4);
		}
                | tGRADIENT NamedSurf NamedSurf GradientShape GradientType GradientRand
                {
                        $$ = TextGradientCreate($2, $3, $4, 0.0, 1.0, $5, $6);
                }
		| tGRADIENT NamedSurf NamedSurf GradientShape GradStart GradStop GradientType GradientRand
		{
			$$ = TextGradientCreate($2, $3, $4, $5, $6, $7, $8);
		}
		;
GradientType	:/* empty */
		{
			$$ = LINEAR;
		}
		| tLINEAR
		{
			$$ = LINEAR;
		}
		| tLOG
		{
			$$ = LOG;
		}
		| tREVLOG
		{
			$$ = REVLOG;
		}
		;
GradientShape	: /* empty */
		{
			$$ = PLANAR;
		}
		| tPLANAR
		{
			$$ = PLANAR;
		}
		| tRADIAL
		{
			$$ = RADIAL;
		}
		| tSPHERICAL
		{
			$$ = SPHERICAL;
		}
		;
GradStart	: Expr
		{
			$$ = $1;
		}
		;
GradStop	: Expr
		{
			$$ = $1;
		}
		;
GradientRand	: /* empty */
		{
			$$ = NO;
		}
		| tRANDOM
		{
			$$ = YES;
		}
		;
ProjectorText   : ProjectorTextType ProjectorTextOptions
                ;
ProjectorTextType: tPROJECTOR Filename
                {
                        Imagetext = ImageTextCreate($2);
                }
                ;
ProjectorTextOptions: ProjectorTextOptions ProjectorTextOption
		| /* EMPTY */
		;
ProjectorTextOption: tTILE Expr Expr
		{
			Imagetext->tileu = $2;
			Imagetext->tilev = $3;
		}
		| tSMOOTH
		{
			Imagetext->smooth = TRUE;
		};
ImageTexture	: ImageTextType ImageTextOptions
		;
ImageTextType	: tIMAGE Filename
		{
			Imagetext = ImageTextCreate($2);
		}
		;
ImageTextOptions: ImageTextOptions ImageTextOption
		| /* EMPTY */
		;
ImageTextOption: tCOMPONENT SurfCompName
		{
			/* set texture to modify given component */	
			ImageTextSetComponent(Imagetext, $2);
		}
		| tTILE Expr Expr
		{
			Imagetext->tileu = $2;
			Imagetext->tilev = $3;
		}
		| tTEXTSURF Surface
		{
			Imagetext->surf = $2;
		}
		| tRANGE Expr Expr
		{
			Imagetext->hi = $2;
			Imagetext->lo = $3;
		}
		| tSMOOTH
		{
			Imagetext->smooth = TRUE;
		}
		| Mapping
		{
			Imagetext->mapping = $1;
		};
NamedObject	: tOBJECT Surface tIDENT
		{
			Geom *otmp;
			/*
			 * Create an instance of the named object.
			 */
			otmp = GeomGetNamed($3);
			if (otmp == (Geom *)NULL)
				RLerror(RL_PANIC,
				  "There is no object named \"%s\".", $3);
			$$ = GeomInstanceCreate(otmp);
			$$->surf = $2;
			$$->prims = otmp->prims;
		}
		| tOBJECT tIDENT
		{
			Geom *otmp;

			otmp = GeomGetNamed($2);
			if (otmp == (Geom *)NULL)
				RLerror(RL_PANIC,
				  "There is no object named \"%s\".", $2);
			$$ = GeomInstanceCreate(otmp);
			$$->surf = CurSurf->surf;
			$$->prims = otmp->prims;
		};
Transforms	: Transforms PostTransform
		| /* empty */
		{
			TransHead = TransTail = (Trans *)NULL;
		};
PostTransform	: TransformType
		{
			if (TransHead == (Trans *)NULL) {
				/* we're the list, head and tail */
				TransHead = TransTail = $1;
			} else {
				if ($1->animated || TransTail->animated) {
					/* new tail */
					$1->prev = TransTail;
					TransTail->next = $1;
					TransTail = $1;
				} else {
					/* collapse with tail */
					TransCompose(TransTail, $1, TransTail);
					TransFree($1);
				}
			}
		}
		;
TransformType	: tSCALE AnimExpr AnimExpr AnimExpr
		{
			$$ = TransScaleCreate();
			TransScaleSetX($$, $2);
			TransScaleSetY($$, $3);
			TransScaleSetZ($$, $4);
			if (!$$->animated)
				TransPropagate($$);
				
		}
		| tTRANSLATE AnimExpr AnimExpr AnimExpr
		{
			$$ = TransTranslateCreate();
			TransTranslateSetX($$, $2);
			TransTranslateSetY($$, $3);
			TransTranslateSetZ($$, $4);
			if (!$$->animated)
				TransPropagate($$);
		}
		| tROTATE AnimExpr AnimExpr AnimExpr AnimExpr
		{
			$$ = TransRotateCreate();
			TransRotateSetX($$, $2);
			TransRotateSetY($$, $3);
			TransRotateSetZ($$, $4);
			TransRotateSetTheta($$, $5);
			if (!$$->animated)
				TransPropagate($$);
		}
		| tTRANSFORM	AnimExpr AnimExpr AnimExpr
				AnimExpr AnimExpr AnimExpr
				AnimExpr AnimExpr AnimExpr
		{
			$$ = TransXformCreate();
			TransXformSetX0($$, $2);
			TransXformSetY0($$, $3);
			TransXformSetZ0($$, $4);
			TransXformSetX1($$, $5);
			TransXformSetY1($$, $6);
			TransXformSetZ1($$, $7);
			TransXformSetX2($$, $8);
			TransXformSetY2($$, $9);
			TransXformSetZ2($$, $10);
			if (!$$->animated)
				TransPropagate($$);
		}
		| tTRANSFORM	AnimExpr AnimExpr AnimExpr
				AnimExpr AnimExpr AnimExpr
				AnimExpr AnimExpr AnimExpr
				AnimExpr AnimExpr AnimExpr
		{
			$$ = TransXformCreate();
			TransXformSetX0($$, $2);
			TransXformSetY0($$, $3);
			TransXformSetZ0($$, $4);
			TransXformSetX1($$, $5);
			TransXformSetY1($$, $6);
			TransXformSetZ1($$, $7);
			TransXformSetX2($$, $8);
			TransXformSetY2($$, $9);
			TransXformSetZ2($$, $10);
			TransXformSetXt($$, $11);
			TransXformSetYt($$, $12);
			TransXformSetZt($$, $13);
			if (!$$->animated)
				TransPropagate($$);
		};
Eyep		: tEYEP Vector Transforms
		{
			Camera.pos = $2;
			/*
			 * Eye can be transformed...
			 */
			if (TransHead) {
				/*
				 * We compose non-animated tranformation lists,
				 * so we're only animated if it's one long,
				 * or it's animated itself.
				 */
				if (TransHead->assoc || TransHead->next)
				{
					/* eyep transformation is animated...*/
					Camera.pos_trans = TransHead;
					Camera.pos_orig = Camera.pos;
				}
				else
				{
					PointTransform(&Camera.pos,
						&(TransHead->trans));
					free((voidstar)TransHead);
					TransHead = (Trans *)NULL;
				}
			}
		}
		;
Lookp		: tLOOKP Vector Transforms
		{
			Camera.lookp = $2;
			/*
			 * Look position can be transformed...
			 */
			if (TransHead) {
				/*
				 * We compose non-animated tranformation lists,
				 * so we're only animated if it's one long,
				 * or it's animated itself.
				 */
				if (TransHead->assoc || TransHead->next)
				{
					/* lookp transformation is animated...*/
					Camera.lookp_trans = TransHead;
					Camera.lookp_orig = Camera.lookp;
				}
				else
				{
					PointTransform(&Camera.lookp,
						&(TransHead->trans));
					free((voidstar)TransHead);
					TransHead = (Trans *)NULL;
				}
			}
		}
		;
Up		: tUP Vector Transforms
		{
			Camera.up = $2;
			/*
			 * Up direction can be transformed...
			 */
			if (TransHead) {
				/*
				 * We compose non-animated tranformation lists,
				 * so we're only animated if it's one long,
				 * or it's animated itself.
				 */
				if (TransHead->assoc || TransHead->next)
				{
					/* up transformation is animated...*/
					Camera.up_trans = TransHead;
					Camera.up_orig = Camera.up;
				}
				else
				{
					PointTransform(&Camera.up,
						&(TransHead->trans));
					free((voidstar)TransHead);
					TransHead = (Trans *)NULL;
				}
			}
		}
		;
Fov		: tFOV Expr Expr
		{
			Camera.hfov = $2;
			Camera.vfov = $3;
		}
		| tFOV Expr
		{
			Camera.hfov = $2;
			Camera.vfov = UNSET;
		}
		;
Sample		: tSAMPLE IExpr tJITTER
		{
			if (!Options.samples_set)
				Options.samples = $2;
			if (!Options.jitter_set)
				Options.jitter = TRUE;
		}
		| tSAMPLE IExpr tNOJITTER
		{
			if (!Options.samples_set)
				Options.samples = $2;
			if (!Options.jitter_set)
				Options.jitter = FALSE;
		}
		| tSAMPLE IExpr
		{
			if (!Options.samples_set)
				Options.samples = $2;
		}
		;
Filter		: tFILTER tBOX Expr
		{
			Options.gaussian = FALSE;
			Options.filterwidth = $3;
		}
		| tFILTER tBOX
		{
			Options.gaussian = FALSE;
		}	
		| tFILTER tGAUSS Expr
		{
			Options.gaussian = TRUE;
			Options.filterwidth = $3;
		}
		| tFILTER tGAUSS
		{
			Options.gaussian = TRUE;
		};
Starttime	: tSTARTTIME Expr
		{
			Options.starttime = $2;
		};
Frames		: tFRAMES IExpr
		{
			if (!Options.totalframes_set)
				Options.totalframes = $2;
		};
Framelength	: tFRAMELENGTH Expr
		{
			Options.framelength = $2;
		};
Shutter		: tSHUTTER Expr
		{
			Options.shutterspeed = $2;
		};
Contrast	: tCONTRAST Expr Expr Expr
		{
			if (!Options.contrast_set) {
				Options.contrast.r = $2;
				Options.contrast.g = $3;
				Options.contrast.b = $4;
			}
		}
		;
Cutoff		: tCUTOFF Intensity
		{
			if (!Options.cutoff_set)
				Options.cutoff = $2;
		}
		;
Screen		: tSCREEN IExpr IExpr 
		{
			if (!Options.resolution_set) {
				Screen.xres = $2;
				Screen.yres = $3;
			}
		}
		;
Window		: tWINDOW IExpr IExpr IExpr IExpr
		{
			if (!Options.window_set) {
				Options.window[LOW][X] = $2;
				Options.window[HIGH][X] = $3;
				Options.window[LOW][Y] = $4;
				Options.window[HIGH][Y] = $5;
				/*
				 * We must let ViewingSetup know
				 * that a window has been defined.
				 */
				Options.window_set = TRUE;
			}
		}
		;
Crop		: tCROP Expr Expr Expr Expr
		{
			if (!Options.crop_set) {
				Options.crop[LOW][X] = $2;
				Options.crop[HIGH][X] = $3;
				Options.crop[LOW][Y] = $4;
				Options.crop[HIGH][Y] = $5;
			}
		}
		;
Report		: tREPORT Verbose Quiet IExpr Filename
		{
			if (!Options.freq_set)
				Options.report_freq = $4;
			if (Options.statsname == (char *)NULL)
				Options.statsname = strsave($5);
		}
		| tREPORT Verbose Quiet IExpr
		{
			if (!Options.freq_set)
				Options.report_freq = $4;
		}
		| tREPORT Verbose Quiet Filename
		{
			if (Options.statsname == (char *)NULL)
				Options.statsname = strsave($4);
		}
		| tREPORT Verbose Quiet
		;
Verbose		: tVERBOSE
		{ Options.verbose = TRUE; }
		|
		;
Quiet		: tQUIET
		{ Options.quiet = TRUE; }
		|
		;
Aperture	: tAPERTURE Expr
		{
			Camera.aperture = $2;
		}
		;
Focaldist	: tFOCALDIST Expr
		{
			Camera.focaldist = $2;
		}
		;
Eyesep		: tEYESEP Expr
		{
			if (!Options.eyesep_set)
				Options.eyesep = $2;
		}
		;
Maxdepth	: tMAXDEPTH IExpr
		{
			if (!Options.maxdepth_set)
				Options.maxdepth = $2;
		}
		;
Background	: tBACKGROUND Color
		{
			Screen.background = $2;
		}
		;
Shadowtransp	: tSHADOWTRANSP
		{
			Options.shadowtransp = !Options.shadowtransp;
		}
		;
Light		: LightType
		{
			LightAddToDefined($1);
		}
		| LightType tNOSHADOW
		{
			$1->shadow = FALSE;
			LightAddToDefined($1);
		}
		| tLIGHT Intensity tAMBIENT
		{
			Options.ambient = $2;
		}
		| Lightdef tAREA Vector Vector IExpr Vector IExpr
		{
			/* Area light is strange in that the
			 * Creation routine does the installation.
			 */
			AreaLightCreate(&$1, &$3, &$4, $5, &$6, $7, TRUE);
		}
		| Lightdef tAREA Vector Vector IExpr Vector IExpr tNOSHADOW
		{
			/* Area light is strange in that the
			 * Creation routine does the installation.
			 */
			AreaLightCreate(&$1, &$3, &$4, $5, &$6, $7, FALSE);
		};
LightType	: Lightdef tPOINT Vector
		{
			$$ = LightPointCreate(&$1, &$3);
		}
		| Lightdef tDIRECTIONAL Vector
		{
			$$ = LightInfiniteCreate(&$1, &$3);
		}
		| Lightdef tEXTENDED Expr Vector
		{
			$$ = LightExtendedCreate(&$1, $3, &$4);
		}
		| Lightdef tSPOT Vector Vector Expr
		{
			$$ = LightSpotCreate(&$1, &$3, &$4, $5, 0., 0.);
		}
		| Lightdef tSPOT Vector Vector Expr Expr Expr
		{
			/* light <intens> spot from <to> coef inner_rad
					outer_rad */
			$$ = LightSpotCreate(&$1, &$3, &$4, $5, $6, $7);
		}
                | Lightdef ProjectorText Vector Vector Vector Expr Expr BExpr
                {
                /* light projector <name> <from> <to> <up> <uangle> <vangle> <atten> */
                        /*
                         * Image texturing has so many options
                         * that specification is keyword-based.
                         */
                        if (Imagetext->image == (Image *)NULL) {
                            RLerror(RL_WARN,
                            "Spotlight substituted for degenerate image light.\n");
                            $$=LightSpotCreate(&$1, &$3, &$4,
                                    2.0, 0., 0.);
                        }
                        else {
                            $$=LightProjectorCreate(&$1,
				&$3,&$4,&$5,Imagetext,$6,$7,$8);
                        }

                        Imagetext = (ImageText *)NULL;
                }
		;
Lightdef	: tLIGHT Intensity
		{
			$$ = $2;
		}
		;
CurSurf		: tAPPLYSURF Surface
		{
			CurSurf->surf = $2;
		}
		;
OptSurface	: Surface
		| /* EMPTY */
		{
			$$ = CurSurf->surf;
		}
		;
Surface		: NamedSurf
		| ModifyNamedSurf
		| SurfSpec
		;
NamedSurf	: tIDENT
		{
			$$ = SurfaceGetNamed($1);
			/*
			 * Free up memory allocated for surf name.
			 * We bother doing this because for large models
			 * converted from 3.0, surfnames this can account
			 * for lots o' bytes.
			 */
			free((voidstar)$1);
		}
		| tCURSURF
		{

			if (CurSurf->surf)
				$$ = CurSurf->surf;
			else
				$$ = &DefaultSurface;
		}
		;
ModifyNamedSurf : CopyNamedSurf SurfComponent SurfComponents
		{
			$$ = tmpsurf;
			tmpsurf = (Surface *)NULL;
		}
		| CopyCurSurf SurfComponent SurfComponents
		{
			$$ = tmpsurf;
			tmpsurf = (Surface *)NULL;
		}
		;
CopyNamedSurf	: tIDENT
		{
			tmpsurf = SurfaceCopy(SurfaceGetNamed($1));
		}
		;
CopyCurSurf	: tCURSURF
		{
			if (CurSurf->surf)
				tmpsurf = SurfaceCopy(CurSurf->surf);
			else
				tmpsurf = SurfaceCopy(&DefaultSurface);
		}
		;
SurfSpec	: SurfComponent SurfComponents
		{
			$$ = tmpsurf;
			tmpsurf = (Surface *)NULL;
		} 
		;
SurfDef		: tSURFACE tIDENT Surface
		{
			tmpsurf = SurfaceCopy($3);
			tmpsurf->name = strsave($2);
			SurfaceAddToDefined(tmpsurf);
			tmpsurf = (Surface *)NULL;
		}
		| tSURFACE tIDENT
		{
			/* black surface */
			tmpsurf = SurfaceCreate();
			tmpsurf->name = strsave($2);
			SurfaceAddToDefined(tmpsurf);
			tmpsurf = (Surface *)NULL;
		}
		;
SurfComponents	: SurfComponents SurfComponent
		| /* EMPTY */
		;
SurfComponent	: Ambient
		| Diffuse
		| Specular
		| Specpow
		| Body
		| Reflect
		| Transp
		| Extinct
		| Index
		| Translu
		| Noshadow
		;
Ambient		: tAMBIENT Color
		{
			if (tmpsurf == (Surface *)NULL)
				tmpsurf = SurfaceCreate();
			tmpsurf->amb = $2;
		}
		;
Diffuse		: tDIFFUSE Color
		{
			if (tmpsurf == (Surface *)NULL)
				tmpsurf = SurfaceCreate();
			tmpsurf->diff = $2;
		}
		;
Specular	: tSPECULAR Color
		{
			if (tmpsurf == (Surface *)NULL)
				tmpsurf = SurfaceCreate();
			tmpsurf->spec = $2;
		}
		;
Body		: tBODY Color
		{
			if (tmpsurf == (Surface *)NULL)
				tmpsurf = SurfaceCreate();
			tmpsurf->body = $2;
		};
Extinct		: tEXTINCT Expr
		{
			if (tmpsurf == (Surface *)NULL)
				tmpsurf = SurfaceCreate();
			tmpsurf->statten = $2;
		};
Specpow		: tSPECPOW Expr
		{
			if (tmpsurf == (Surface *)NULL)
				tmpsurf = SurfaceCreate();
			tmpsurf->srexp = $2;
		}
		;
Reflect		: tREFLECT Expr
		{
			if (tmpsurf == (Surface *)NULL)
				tmpsurf = SurfaceCreate();
			tmpsurf->reflect = $2;
		}
		;
Transp		: tTRANSP Expr
		{
			if (tmpsurf == (Surface *)NULL)
				tmpsurf = SurfaceCreate();
			tmpsurf->transp = $2;
		}
		;
Index		: tINDEX Expr
		{
			if (tmpsurf == (Surface *)NULL)
				tmpsurf = SurfaceCreate();
			tmpsurf->index = $2;
		}
		;
Translu		: tTRANSLU Expr Color Expr
		{
			if (tmpsurf == (Surface *)NULL)
				tmpsurf = SurfaceCreate();
			tmpsurf->translucency = $2;
			tmpsurf->translu = $3;
			tmpsurf->stexp = $4;
		}
		;
Noshadow	: tNOSHADOW
		{
			if (tmpsurf == (Surface *)NULL)
				tmpsurf = SurfaceCreate();
			tmpsurf->noshadow = TRUE;
		}
		;
SweptSph	: tSWEPTSPH OptSurface SweptSphCenter SweptSphRadius
		{
			$$ = GeomSweptSphCreate(cent, rad);
			if ($$)
				$$->surf = $2;
			{
				int i;
				for (i = 0; i < 4; i++){
					cent[i].x = 0.0;
					cent[i].y = 0.0;
					cent[i].z = 0.0;
				}
			}
		}
		;
SweptSphCenter	: /* empty */
		| tBEZIER Expr Expr Expr  Expr Expr Expr  Expr Expr Expr  Expr Expr Expr
		{

			/* equations for Bezier curve */
		

			cent[0].x = $2;
			cent[0].y = $3;
			cent[0].z = $4;

			cent[1].x = -3*$2 + 3*$5;
			cent[1].y = -3*$3 + 3*$6;
			cent[1].z = -3*$4 + 3*$7;

			cent[2].x = 3*$2 - 6*$5 + 3*$8;
			cent[2].y = 3*$3 - 6*$6 + 3*$9;
			cent[2].z = 3*$4 - 6*$7 + 3*$10;

			cent[3].x = -$2 + 3*$5 - 3*$8 + $11;
			cent[3].y = -$3 + 3*$6 - 3*$9 + $12;
			cent[3].z = -$4 + 3*$7 - 3*$10 + $13;

		}
		| tCOEFFS Expr Expr Expr Expr   Expr Expr Expr Expr  Expr Expr Expr Expr
		{

			cent[0].x = $2;
			cent[1].x = $3;
			cent[2].x = $4;
			cent[3].x = $5;

			cent[0].y = $6;
			cent[1].y = $7;
			cent[2].y = $8;
			cent[3].y = $9;

			cent[0].z = $10;
			cent[1].z = $11;
			cent[2].z = $12;
			cent[3].z = $13;
		}
		| tXBEZIER Expr Expr Expr Expr SweptSphCenter
		{
			cent[0].x = $2;
			cent[1].x = -3*$2 + 3*$3;
			cent[2].x = 3*$2 - 6*$3 + 3*$4;
			cent[3].x = -$2 + 3*$3 - 3*$4 + $5;
		}
		| tYBEZIER Expr Expr Expr Expr SweptSphCenter
		{
			cent[0].y = $2;
			cent[1].y = -3*$2 + 3*$3;
			cent[2].y = 3*$2 - 6*$3 + 3*$4;
			cent[3].y = -$2 + 3*$3 - 3*$4 + $5;
		}
		| tZBEZIER Expr Expr Expr Expr SweptSphCenter
		{
			cent[0].z = $2;
			cent[1].z = -3*$2 + 3*$3;
			cent[2].z = 3*$2 - 6*$3 + 3*$4;
			cent[3].z = -$2 + 3*$3 - 3*$4 + $5;
		}
		| tXCOEFFS Expr Expr Expr Expr SweptSphCenter
		{
			cent[0].x = $2;
			cent[1].x = $3;
			cent[2].x = $4;
			cent[3].x = $5;
		}
		| tYCOEFFS Expr Expr Expr Expr SweptSphCenter
		{
			cent[0].y = $2;
			cent[1].y = $3;
			cent[2].y = $4;
			cent[3].y = $5;
		}
		| tZCOEFFS Expr Expr Expr Expr SweptSphCenter
		{
			cent[0].z = $2;
			cent[1].z = $3;
			cent[2].z = $4;
			cent[3].z = $5;
		}
		;
SweptSphRadius	: Expr Expr Expr Expr
		{
			rad[0] = $1;
			rad[1] = $2;
			rad[2] = $3;
			rad[3] = $4;
		}
		;
HeightField	: tHEIGHTFIELD Surface Filename
		{
			$$ = GeomHfCreate($3);
			if ($$)
				$$->surf = $2;
		}
		| tHEIGHTFIELD Filename
		{
			$$ = GeomHfCreate($2);
		}
		;
FracLand        : tFRACLAND Surface IExpr IExpr
                {
                        $$ = GeomFlCreate($3,$4);
                        if ($$)
                                $$->surf = $2;
                }
                | tFRACLAND IExpr IExpr
                {
                        $$ = GeomFlCreate($2,$3);
                }
                ;
Poly		: tPOLY OptSurface Polypoints
		{
			$$ = GeomPolygonCreate(Polypoints, Npoints,
				Options.flipnorm);
			if ($$)
				$$->surf = $2;
			Polypoints = (PointList *)NULL;
			Npoints = 0;
		}
		;
Polypoints	: /* empty */
		| Polypoints Polypoint
		;
Polypoint	: Vector
		{
			PointList *ptmp;

			ptmp = (PointList *)Malloc(sizeof(PointList));
			ptmp->vec = $1;
			ptmp->next = Polypoints;
			Polypoints = ptmp;
			Npoints++;
		}
		;
Mountain        : tMOUNTAIN OptSurface Vec2d Vec2d Float Float Float Float Float StartGrid

                {
                        $$ = GeomMountainCreate($3, $4, (int)$5, $6, (int)$7, (int)$8, $9, $10);
                        if ($$)
                            $$->surf = $2;
                }
                ;
HeightValues    : HeightValues Float
                {
                       $$ = (HeightValue *)Malloc(sizeof(HeightValue));
                       $$->Next  = $1;
                       $$->Value = $2;
                }
                | Float
                {
                     $$ = (HeightValue *)Malloc(sizeof(HeightValue));
                     $$->Value = $1;
                      $$->Next  = NULL;
                }
                ;
StartGrid       : HeightValues
                {
                       int    i,j,k;
                       HeightValue  *CurrHV, *CurrHV2;
                       for (i = 0, CurrHV = $1; CurrHV != NULL; CurrHV = CurrHV->Next, i++);
                       for (j = 1; j*j < i; j++);
                       if (j*j != i) RLerror(RL_ABORT, "Invalid number of startgridvalues: %d\n", i);
                       else {
                           $$ = (StartGridStruct *)share_malloc(sizeof(StartGridStruct));
                           if ($$) {
                               $$->Size = j-1;
                               CurrHV = $1;
                               $$->Data = AllocMatrix(Float, j, j);
                               for (i = 0; i < j; i++)
                                  for (k = 0; k < j; k++) {
                                        $$->Data[i][k] = CurrHV->Value;
                                        CurrHV2 = CurrHV;
                                        CurrHV = CurrHV->Next;
                                        free(CurrHV2);
                                  }
                           }
                       }
                }
                ;

FractalObject:  tFRACTALOBJECT OptSurface Float Float Float Float FFractalClosure
                {
                   $$ = GeomFractalObjCreate((int)$3, $4, $5, $6, $7);
                   if ($$)
                       $$->surf = $2;
                }
                ;
FFractalClosure: FPointPool FTrianglePool FEntityPool
                {
                    $$ = (FractalClosure *)share_malloc(sizeof(FractalClosure));
                    $$->Pointpool = $1;
                    $$->Trianglepool = $2;
                    $$->Entitypool = $3;
                }
                ;
FPointPool:  tFPOINTS 
             {
                Pointpool = (PointPool *)share_malloc(sizeof(PointPool));
                Pointpool->Points = (FractalPoint *)share_malloc(PointsToAllocate * sizeof(FractalPoint));
                Pointpool->NumberOfPoints = 0;
                Pointpool->PointsAllocated = PointsToAllocate;
             }
             FPoints
             {
                $$ = Pointpool;
             }
             ;
FPoints:   FPoint
           {
           }
           | FPoint FPoints
           {
           }
           ;
FPoint:  Float Float Float
         {
             CheckPoints(Pointpool->Points, Pointpool->NumberOfPoints, Pointpool->PointsAllocated);
             CurrPoint = &Pointpool->Points[Pointpool->NumberOfPoints++];
             CurrPoint->x = $1;
             CurrPoint->y = $2;
             CurrPoint->z = $3;
         }
         ;
FTrianglePool:  tFTRIANGLES
             {
                Trianglepool = (TrianglePool *)share_malloc(sizeof(TrianglePool));
                Trianglepool->Triangles = (FractalTriangle *)share_malloc(TrianglesToAllocate * sizeof(FractalTriangle));
                Trianglepool->NumberOfTriangles = 0;
                Trianglepool->TrianglesAllocated = TrianglesToAllocate;
             }
             FTriangles
             {
                $$ = Trianglepool;
             }
             ;
FTriangles: FTriangle
            {
            }
            | FTriangle FTriangles
            {
            }
            ;
FTriangle:  Float Float Float
            {
                CheckTriangles(Trianglepool->Triangles, Trianglepool->NumberOfTriangles,
                               Trianglepool->TrianglesAllocated);
                CurrTriangle = &Trianglepool->Triangles[Trianglepool->NumberOfTriangles++];
                CurrTriangle->Point[0] = $1 - 1;
                CurrTriangle->Point[1] = $2 - 1;
                CurrTriangle->Point[2] = $3 - 1;
            }
            ;
FEntityPool:  tFENTITIES
             {
                Entitypool = (EntityPool *)share_malloc(sizeof(EntityPool));
                Entitypool->Entities = (FractalEntity *)share_malloc(EntitiesToAllocate * sizeof(FractalEntity));
                Entitypool->NumberOfEntities = 0;
                Entitypool->EntitiesAllocated = EntitiesToAllocate;
             } 
             FEntities
             {
                $$ = Entitypool;
             } 
             ;
FEntities: FEntity
           {
           }
           | FEntity FEntities
           {
           }
           ;
FEntity:  tENTITY Float Float Float FEntityTriangles
          {
              CheckEntities(Entitypool->Entities, Entitypool->NumberOfEntities,
                               Entitypool->EntitiesAllocated);
              CurrEntity = &Entitypool->Entities[Entitypool->NumberOfEntities++];
              CurrEntity->XSize = $2;
              CurrEntity->YSize = $3;
              CurrEntity->ZSize = $4;
              CurrEntity->Triangles = $5;
          }
          ;
FEntityTriangles: Float
                  {
                      $$ = (TriangleList *)share_malloc(sizeof(TriangleList));
                      $$->TriangleNumber = $1 - 1;
                      $$->Next = NULL;
                  }
                  | FEntityTriangles Float
                  {
                      $$ = (TriangleList *)share_malloc(sizeof(TriangleList));
                      $$->TriangleNumber = $2 - 1;
                      $$->Next = $1;
                  }
                  ;

Aggregate	: AggregateDef
		{
			if (Defstack->obj) {
				/*
				 * Set object texture to current texture.
				 */
				Defstack->obj->texture = CurText;
			}
			CurText = (Texture *)NULL;
			/*
			 * Pop topmost object on stack.
			 */
			$$ = Defstack->obj;
			Defstack = GeomStackPop(Defstack);
			/* Pop current surface */
			CurSurf = SurfPop(CurSurf);
			/* Make current default surf aggregate's default */
			$$->surf = CurSurf->surf;
		}
		;
AggregateDef	: AggregateCreate Aggdefs tEND
		{
			/* Convert aggregate, pop stacks, etc. */
			if ($1) {
				if (Defstack->obj->next == (Geom *)NULL) {
					RLerror(RL_WARN,
						"Null object defined.\n");
					Defstack->obj = (Geom *)NULL;
				} else {
					/*
					 * Convert the linked list of objects
					 * associated with the topmost object
					 * to the appropriate aggregate type.
					 */
					Defstack->obj->prims=AggregateConvert(
						Defstack->obj,
						Defstack->obj->next);
					/*
					 * Make sure conversion worked OK.
					 */
					if (Defstack->obj->prims <= 0)
						Defstack->obj = (Geom *)NULL;
				}
			}
		}
		;
AggregateCreate	: AggregateType
		{
			if ($1) {
				Defstack = GeomStackPush($1, Defstack);
				CurSurf = SurfPush((Surface *)NULL, CurSurf);
			}
		};
AggregateType	: List
		| Grid
		| Csg
		;
List		: tLIST
		{
			$$ = GeomListCreate();
		}
		;
Grid		: tGRID IExpr IExpr IExpr
		{
			$$ = GeomGridCreate($2, $3, $4);
		}
		;
Csg		: CombineOp
		{
			$$ = GeomCsgCreate($1);
			Options.csg = TRUE;
		}
		;
CombineOp	: tUNION
		{
		    $$ = CSG_UNION;
		}
		| tINTERSECT
		{
		    $$ = CSG_INTERSECT;
		}
		| tDIFFERENCE
		{
		    $$ = CSG_DIFFERENCE;
		}
    		;
Cone		: tCONE OptSurface Expr Vector Expr Vector
		{
			if (equal($3, $5)) {
				/* It's really a cylinder */
				$$ = GeomCylinderCreate($3, &$4, &$6);
			} else
				$$ = GeomConeCreate($3, &$4, $5, &$6);
			if ($$)
				$$->surf = $2;
		}
		;
Cylinder	: tCYL OptSurface Expr Vector Vector
		{
			$$ = GeomCylinderCreate($3, &$4, &$5);
			if ($$)
				$$->surf = $2;
		}
		;
Sphere		: tSPHERE OptSurface Expr Vector
		{
			$$ = GeomSphereCreate($3, &($4));
			if ($$)
				$$->surf = $2;
		}
		;
Flame		: tFLAME Expr Vector Expr Expr
		{
			$$ = GeomFlameCreate($2, &($3), (int)$4, $5);
		}
		;
Disc		: tDISC OptSurface Expr Vector Vector
		{
			$$ = GeomDiscCreate($3, &($4), &($5));
			if ($$)
				$$->surf = $2;
		}
		;
Box		: tBOX OptSurface Vector Vector
		{
			$$ = GeomBoxCreate(&$3, &$4);
			if ($$)
				$$->surf = $2;
		}
		;
Triangle	: tTRIANGLE OptSurface Vector Vector Vector
		{
			$$ = GeomTriangleCreate(FLATTRI, &($3), &($4), &($5),
				(Vector *)NULL, (Vector *)NULL, (Vector *)NULL,
				(Vec2d *)NULL, (Vec2d *)NULL, (Vec2d *)NULL,
				Options.flipnorm);
			if ($$)
				$$->surf = $2;
		}
		| tTRIANGLE OptSurface  Vector Vector
					Vector Vector
					Vector Vector
		{
			$$ = GeomTriangleCreate(PHONGTRI, &($3), &($5),
				&($7), &($4), &($6), &($8),
				(Vec2d *)NULL, (Vec2d *)NULL, (Vec2d *)NULL,
				Options.flipnorm);
			if ($$)
				$$->surf = $2;
		}
		| tTRIANGLEUV OptSurface Vector Vector Vec2d
					 Vector Vector Vec2d
					 Vector Vector Vec2d
		{
			$$ = GeomTriangleCreate(PHONGTRI, &($3), &($6), &($9),
						&($4), &($7), &($10),
						&($5), &($8), &($11),
						Options.flipnorm);
			if ($$)
				$$->surf = $2;
		}
		;
Plane		: tPLANE OptSurface Vector Vector
		{
			$$ = GeomPlaneCreate(&($3), &($4));
			if ($$)
				$$->surf = $2;
		}
		;
Torus		: tTORUS OptSurface Expr Expr Vector Vector
		{
			$$ = GeomTorusCreate($3, $4, &($5), &($6));
			if ($$)
				$$->surf = $2;
		}
		;
Blob		: tBLOB OptSurface Expr MetaPoints
		{
			$$ = GeomBlobCreate($2, $3, Metapoints, Npoints);
			if ($$)
				$$->surf = $2;
			Metapoints = (MetaList *)NULL;
			Npoints = 0;
		}
		;
MetaPoints	: /* empty */
		| MetaPoints MetaPoint
		;
MetaPoint	: Expr Expr Expr Expr Expr
		{
			Metapoint = (MetaList *)Malloc(sizeof(MetaList));
			Metapoint->mvec.c0 = $1; /* strength */
			Metapoint->mvec.rs = $2; /* radius**2 */
			Metapoint->mvec.x = $3;  /* coordinates of metaball */
			Metapoint->mvec.y = $4;  /* centers.                */
			Metapoint->mvec.z = $5;
			Metapoint->mvec.surf = NULL;
			/*Metapoint->mvec.mcolor.red = 1.0; */
			/*Metapoint->mvec.mcolor.green = 1.0; */
			/*Metapoint->mvec.mcolor.blue = 1.0; */
			Metapoint->next = Metapoints;
			Metapoints = Metapoint;
			Npoints++;
		}
		;
Ifs		: IfsKey OptSurface IfsTransforms IfsOptions tEND
		{
			$$ = GeomIfsCreate($3, &ifsOptions);
			if ($$) 
				$$->surf = $2;
		}
		;
CIfs		: IfsKey OptSurface IfsCondensation IfsLeaves IfsTransforms IfsOptions tEND
		{
			$$ = GeomCIfsCreate($5, $3, $4, &ifsOptions);
			if ($$) 
				$$->surf = $2;
		}
		;
IfsKey          : tIFS  /* set option defaults */
                {
                        ifsTransformRang = 1;
                        ifsOptions.depth = 99999999;
                        ifsOptions.size = 0.;
                        ifsOptions.normalweighting = HIGHPASS_NORMAL_WEIGHTING;
			ifsOptions.boxbounding = ifsOptions.spherebounding = FALSE;
			ifsOptions.listfilename = NULL;
                }
                ;
IfsCondensation	: TransTextObj ','
		{
			$$ = $1;
		}
		;
IfsLeaves       : TransTextObj ',' /* Oppenheimer trees */
                {
                        $$ = $1;
                }
                | /* no leaves -> a true IFS */
                {
                        $$ = (Geom *)NULL;
                }
                ;
IfsTransforms	: OptIfsTransformRang IfsTransform
		{
			$$ = (IfsTransList *)Malloc(sizeof(IfsTransList));
			$$->trans = TransHead;
			$$->transtail = TransTail;
			$$->next = (IfsTransList *)NULL;
                        $$->rang = ifsTransformRang;
		}
		| IfsTransforms ',' OptIfsTransformRang IfsTransform
		{
			$$ = (IfsTransList *)Malloc(sizeof(IfsTransList));
			$$->trans = TransHead;
			$$->transtail = TransTail;
			$$->next = $1;
                        $$->rang = ifsTransformRang;
		}
		;
IfsTransform	: TransformType
		{
			TransHead = TransTail = $1;
		}
		| IfsTransform PostTransform
		;
OptIfsTransformRang : IExpr
                {
                        ifsTransformRang = $1;
                }
                | /* no rang specified */
                ;
IfsOptions      : /* no options */
                | IfsOptions tMINSIZE Expr
                {
                        ifsOptions.size = $3;
                }
                | IfsOptions tMAXDEPTH IExpr
                {
                        ifsOptions.depth = $3;
                }
                | IfsOptions tNORMALWEIGHTING IfsNormalWeightingMethod
                {
                        ifsOptions.normalweighting = $3;
		}
		| IfsOptions tBOUNDING IfsBoundingVolumeType
		{
		}
		| IfsOptions tLIST Filename
		{
			ifsOptions.listfilename = strsave($3);
		}
                ;
IfsNormalWeightingMethod: tCONSTANT
                {
                        $$ = CONSTANT_NORMAL_WEIGHTING;
                }
                | tLOWPASS
                {
                        $$ = LOWPASS_NORMAL_WEIGHTING;
                }
                | tHIGHPASS
                {
                        $$ = HIGHPASS_NORMAL_WEIGHTING;
                }
                ;
IfsBoundingVolumeType:	tBOX
		{
			ifsOptions.boxbounding = TRUE;
			ifsOptions.spherebounding = FALSE;
		}
		| tSPHERE
		{
			ifsOptions.boxbounding = FALSE;
			ifsOptions.spherebounding = TRUE;
		}
		;
CBlob		: tCBLOB OptSurface Expr CMetaPoints
		{
			$$ = GeomBlobCreate($2, $3, CMetapoints, Npoints);
			if ($$)
				$$->surf = $2;
			CMetapoints = (MetaList *)NULL;
			Npoints = 0;
		}
		;
CMetaPoints	: /* empty */
		| CMetaPoints CMetaPoint
		;
CMetaPoint	: Expr Expr Expr Expr Expr OptSurface
		{
			CMetapoint = (MetaList *)Malloc(sizeof(MetaList));
			CMetapoint->mvec.c0 = $1; /* strength */
			CMetapoint->mvec.rs = $2; /* radius**2 */
			CMetapoint->mvec.x = $3;  /* coordinates of metaball */
			CMetapoint->mvec.y = $4;  /* centers.                */
			CMetapoint->mvec.z = $5;
			CMetapoint->mvec.surf = $6; /*OptSurface */
			/*CMetapoint->mvec.mcolor.red = $6; */
			/*CMetapoint->mvec.mcolor.green = $7; */
			/*CMetapoint->mvec.mcolor.blue = $8; */
			CMetapoint->next = CMetapoints;
			CMetapoints = CMetapoint;
			Npoints++;
		}
		;
Rotspline       : tROTSPLINE OptSurface Vector Vector
                        tCOEFFS Expr Expr Expr Expr
                {
                        Rcoeffs = (Float *)Calloc(4, sizeof(Float));
                        Rcoeffs[0] = $9;
                        Rcoeffs[1] = $8;
                        Rcoeffs[2] = $7;
                        Rcoeffs[3] = $6;
                        $$ = GeomRotsplineCreate(&($3), 0.0, 0.0,
                                                &($4), 0.0, 0.0, Rcoeffs);
                        if ($$)
                                $$->surf = $2;
                        Rcoeffs = (Float *)NULL;
                }
                | tROTSPLINE OptSurface Vector Expr Expr
                                        Vector Expr Expr
                {
                        $$ = GeomRotsplineCreate(&($3), $4, $5,
                                                &($6), $7, $8, (Float *)NULL);
                        if ($$)
                                $$->surf = $2;
                }
                ;
Patch           : tPATCH OptSurface 
                         Vector Vector Vector Vector
                         Vector Vector Vector Vector
                         Vector Vector Vector Vector
                         Vector Vector Vector Vector
                         MatrixType
                  {
                         Float geomx[4][4], geomy[4][4], geomz[4][4];
                         Vector pos[16];
                         int i, j, type;
 
                         pos[0]  = $3;
                         pos[1]  = $4;
                         pos[2]  = $5;
                         pos[3]  = $6;
                         pos[4]  = $7;
                         pos[5]  = $8;
                         pos[6]  = $9;
                         pos[7]  = $10;
                         pos[8]  = $11;
                         pos[9]  = $12;
                         pos[10] = $13;
                         pos[11] = $14;
                         pos[12] = $15;
                         pos[13] = $16;
                         pos[14] = $17;
                         pos[15] = $18;
 
                         for(i=0; i<4; i++) {
                           for(j=0; j<4; j++) {
                             geomx[i][j] = pos[4*i + j].x;
                             geomy[i][j] = pos[4*i + j].y;
                             geomz[i][j] = pos[4*i + j].z;
                           }
                         }
 
                         type = $19;
 
                         $$ = GeomPatchCreate(type,geomx,geomy,geomz,DIV);
 
                         if ($$)
                           $$->surf = $2;
                  };
 MatrixType      : /* empty */
                  {
                         $$ = BEZIER;
                  }
                 | tMATRIX tBEZIER 
                  {
                         $$ = BEZIER;
                  }
                 | tMATRIX tHERMITE
                  {
                         $$ = HERMITE;
                  }
                 | tMATRIX tBSPLINE
                  {
                         $$ = BSPLINE;
                  }  
                 | tMATRIX tCARDINAL
                  {
                         $$ = CARDINAL;
                  }
                  ;

CubicSpin       : tCUBICSPIN OptSurface Vector Vector Expr Expr Expr Expr Expr
		{
		   $$ = GeomCubicSpinCreate(&($3), &($4), $5, $6, $7, $8, $9);
		   if ($$) $$->surf = $2;
		}
		;

Outfile		: tOUTFILE Filename
		{
			if (Options.imgname != (char *)NULL)
				/* Already set on command line. */
				RLerror(RL_WARN,
					"Ignoring output file name \"%s\".\n",
					$2);
			else
				Options.imgname = strsave($2);
		}
		;
GlobalEffects	: tATMOSPHERE Effects
		{
			AtmosEffects = CurEffect;
			CurEffect = (Atmosphere *)NULL;
		}
		| tATMOSPHERE IExpr Effects
		{
			if ($2 <= 0.)
				RLerror(RL_PANIC,
				"Index of refraction must be positive.\n");
			TopMedium.index = $2;
			AtmosEffects = CurEffect;
			CurEffect = (Atmosphere *)NULL;
		}
		;
Effects		: Effects Effect
		|
		;
Effect		: EffectType
		{
			$1->next = CurEffect;
			CurEffect = $1;
		}
		;
EffectType	: tMIST Color Color Expr Expr
		{
			$$ = AtmosMistCreate(&($2), &($3), $4, $5);
		}
		| tFOG Color Color
		{
			$$ = AtmosFogCreate(&($2), &($3));
		}
		| tFOGDECK Expr Expr Vector Expr IExpr Color Color
		{
			$$ = AtmosFogdeckCreate($2, $3, &$4, $5, $6, &$7, &$8);
		}
		;
Color		: Expr Expr Expr
		{
			$$.r = $1;
			$$.g = $2;
			$$.b = $3;
		}
		;
Vector		: Expr Expr Expr
		{
			$$.x = $1;
			$$.y = $2;
			$$.z = $3;
		}
		;
Vec2d		: Expr Expr 
		{
			$$.u = $1;
			$$.v = $2;
		}
		;
OptMapping	: Mapping
		| /* EMPTY */
		{
			$$ = UVMappingCreate();
		}
		;
Mapping		: tMAP MapMethod
		{
			$$ = $2;
		}
		;
MapMethod	: tUV
		{
			$$ = UVMappingCreate();
		}
		| tSPHERICAL
		{
			$$ = SphereMappingCreate((Vector *)NULL,
				(Vector *)NULL, (Vector *)NULL);
		}
		| tSPHERICAL Vector Vector Vector
		{
			/* origin up uaxis */
			$$ = SphereMappingCreate(&$2, &$3, &$4);
		}
		| tCYLINDRICAL
		{
			$$ = CylMappingCreate((Vector *)NULL,
				(Vector *)NULL, (Vector *)NULL);
		}
		| tCYLINDRICAL Vector Vector Vector
		{
			/* origin up uaxis */
			$$ = CylMappingCreate(&$2, &$3, &$4);
		}
		| tPLANAR
		{
			$$ = LinearMappingCreate((Vector *)NULL,
				(Vector *)NULL, (Vector *)NULL);
		}
		| tPLANAR Vector Vector Vector
		{
			/* origin up uaxis */
			$$ = LinearMappingCreate(&$2, &$3, &$4);
		}
		;
SurfCompName	: tAMBIENT
		{
			$$ = AMBIENT;
		}
		| tDIFFUSE
		{
			$$ = DIFFUSE;
		}
		| tBODY
		{
			$$ = BODY;
		}
		| tSPECULAR
		{
			$$ = SPECULAR;
		}
		| tREFLECT
		{
			$$ = REFLECT;
		}
		| tTRANSP
		{
			$$ = TRANSP;
		}
		| tSPECPOW
		{
			$$ = SPECPOW;
		}
		| tBUMP
		{
			$$ = BUMP;
		}
		| tINDEX
		{
			$$ = INDEX;
		}
		;
Intensity	: Expr
		{ $$.r = $$.g = $$.b = $1; }
		| Color
		;

Print		: tPRINT PrintArgs
		;
PrintArgs	: PrintArg PrintArgs
		| /* empty */
		{
			putc('\n', stderr);
		}
PrintArg	: tTEXT
		{
			fputs($1, stderr);
		}
		| Expr
		{
			fprintf(stderr,"%g", (double)$1);
		}
		;
IExpr		: Expr
		{ $$ = (int)$1; }
		;
BExpr		: tTRUE
		{ $$ = 1; }
		| tFALSE
		{ $$ = 0; }
		;
Expr		: Float
		| ParenExpr
		{
			if (!$1->timevary) {
				$$ = ExprEval($1);
			} else {
				RLerror(RL_PANIC, "Illegal animated expression use.\n");
			}
		}
		;
AnimExpr	: Float
		{
			$$ = ExprReuseFloatCreate($1);
		}
		| ParenExpr
		;
ParenExpr	: '(' MExpr ')'
		{
			$$ = $2;
		};
MExpr		: tFLOAT
		{
			$$ = ExprFloatCreate($1, FALSE);
		}
		| tIDENT
		{
			$$ = ExprFloatSymtabFind($1);
		}
		| Symtabent '(' MExpr ')'
		{
			if ($1->params != 1)
				RLerror("Incorrect number of arguments\n");
			$$ = ExprResolve1($3, $1->value.fp, $1->timevary);
		}
		| Symtabent '(' MExpr ',' MExpr ')'
		{
			if ($1->params != 2)
				RLerror("Incorrect number of arguments\n");
			$$ = ExprResolve2($3, $5,
					$1->value.fp,
					$1->timevary);
		}
		| Symtabent '(' MExpr ',' MExpr ',' MExpr ')'
		{
			if ($1->params != 3)
				RLerror("Incorrect number of arguments\n");
			$$ = ExprResolve3($3, $5, $7, 
					$1->value.fp,
					$1->timevary);
		}
		| Symtabent '(' MExpr ',' MExpr ',' MExpr ',' MExpr ')'
		{
			if ($1->params != 4)
				RLerror("Incorrect number of arguments\n");
			$$ = ExprResolve4($3, $5, $7, $9, 
					$1->value.fp,
					$1->timevary);
		}
		| Symtabent
			'(' MExpr ',' MExpr ',' MExpr ',' MExpr ',' MExpr ')'
		{
			if ($1->params != 5)
				RLerror("Incorrect number of arguments\n");
			$$ = ExprResolve5($3, $5, $7, $9, $11,
					$1->value.fp,
					$1->timevary);
		}
		| '(' MExpr ')'
		{
			$$ = $2;
		}
		| MExpr '+' MExpr
		{
			$$ = ExprResolve2($1, $3, SumExpr, FALSE);
		}
		| MExpr '-' MExpr
		{
			$$ = ExprResolve2($1, $3, DiffExpr, FALSE);
		}
		| MExpr '*' MExpr
		{
			$$ = ExprResolve2($1, $3, MultExpr, FALSE);
		}
		| MExpr '/' MExpr
		{
			$$ = ExprResolve2($1, $3, DivideExpr, FALSE);
		}
		| MExpr '%' MExpr
		{
			$$ = ExprResolve2($1, $3, ModExpr, FALSE);
		}
		| '-' MExpr %prec UMINUS
		{
			$$ = ExprResolve1($2, NegateExpr, FALSE);
		}
		| '+' MExpr %prec UMINUS
		{
			$$ = $2;
		}
		| MExpr '^' MExpr
		{
			$$ = ExprResolve2($1, $3, pow, FALSE);
		}
		| MExpr '?' MExpr ':' MExpr
		{
			$$ = ExprResolve3($1, $3, $5, CondExpr, FALSE);
		}
		| MExpr tEQUAL MExpr
		{
			$$ = ExprResolve2($1, $3, EqualExpr, FALSE);
		}
		| MExpr tNOTEQUAL MExpr
		{
			$$ = ExprResolve2($1, $3, UnequalExpr, FALSE);
		}
		| MExpr tLESSTHAN MExpr
		{
			$$ = ExprResolve2($1, $3, LessExpr, FALSE);
		}
		| MExpr tLESSEQUAL MExpr
		{
			$$ = ExprResolve2($1, $3, LessEqualExpr, FALSE);
		}
		| MExpr tGREATERTHAN MExpr
		{
			$$ = ExprResolve2($1, $3, GreaterExpr, FALSE);
		}
		| MExpr tGREATEREQUAL MExpr
		{
			$$ = ExprResolve2($1, $3, GreaterEqualExpr, FALSE);
		}
		| MExpr tAND MExpr
		{
			$$ = ExprResolve2($1, $3, AndExpr, FALSE);
		}
		| MExpr tOR MExpr
		{
			$$ = ExprResolve2($1, $3, OrExpr, FALSE);
		}
		;
Float		: tFLOAT
		| '-' tFLOAT
		{ $$ = -$2; }
		| '+' tFLOAT
		{ $$ = $2; }
                | tENVVAR
                {
			char	*str;

			if ((str = (char *)getenv($1)) != NULL)
				$$ = atof(str);
			else
				$$ = 0.0;
                };
Filename	: tIDENT
		| tFILENAME
		;
Symtabent	: tIDENT
		{
			$$ = SymtabBuiltinFind($1);
		}
		| tLINEAR
		{
			$$ = SymtabBuiltinFind(strsave("linear"));
		};

Define		: tDEFINE tIDENT AnimExpr Sync Mode AnimVals
		{
			/* Just a normal variable */
			if ($4 == FAR_AWAY && $5 == NULL && $6 == NULL) {
				SymtabAddEntry($2, $3->type, $3, NULL,
						$3->timevary, 0);

			/* An animated variable cannot have animated
			 * expressions in its values.
			 */
			} else if ($3->timevary) {
				RLerror(RL_ABORT,
					"Illegal animated expression use\n");

			/* Animated variable */
			} else {
				AnimVal	*v;

				v = AddVal(ExprEval($3), $4, $5, $6);
				v = CheckVal(v);
				SymtabAddEntry($2, FLOAT_EXPR,
					ExprAnimCreate(v), NULL, TRUE, 0);
			}
		}
		;

Mode		: /* Empty */
		{
			$$ = (Expr *)NULL;
		}
		| tIDENT
		{
			$$ = ExprFloatSymtabFind($1);
		}
		| tLINEAR
		{
			$$ = ExprFloatSymtabFind("linear");
		}
		;

Sync		: /* Empty */
		{
			$$ = FAR_AWAY;
		}
		| tAT Expr
		{
			$$ = $2;
		}
		;

AnimVals	: /* Empty */
		{
			$$ = (AnimVal *)NULL;
		}
		| Expr Sync Mode AnimVals
		{
			$$ = AddVal($1, $2, $3, $4);
		}
		;

%%

/*
 * Issue error message containing filename and line number, and exit.
 */
/*VARARGS1*/
void
yyerror(s, pat1, pat2)
  char *s, *pat1, *pat2;
{
  fprintf(stderr,"%s: Error: %s: line %d: ", Options.progname,
		yyfilename, yylineno);
  fprintf(stderr, s, pat1, pat2);
  if (*s && s[strlen(s) -1] != '\n')
    /* YACC doesn't put newlines on error messages. */
    fprintf(stderr,"\n");	
  fflush(stderr);
  exit(1);
}

Geom *
NewAggregate(obj)
  Geom	*obj;
{
  obj->name = Defstack->obj->name;
  obj->next = Defstack->obj->next;
  return obj;
}
