/* input_yacc.y								   */
/*									   */
/* 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".			   */
/* $Id */
%{
#include "rayshade.h"
#include "libsurf/atmosphere.h"
#include "libsurf/surface.h"
#include "libtext/texture.h"
#include "libimage/image.h"
#include "libobj/object.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/grid.h"
#include "libobj/hf.h"
#include "libobj/list.h"
#include "libobj/plane.h"
#include "libobj/poly.h"
#include "libobj/sphere.h"
#include "libobj/torus.h"
#include "libobj/triangle.h"

#include "liblight/point.h"
#include "liblight/infinite.h"
#include "liblight/spot.h"
#include "liblight/jittered.h"
#include "liblight/extended.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/imagetext.h"
#include "libtext/marble.h"
#include "libtext/sky.h"
#include "libtext/stripe.h"
#include "libtext/wood.h"

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

Object *NewAggregate();
char yyfilename[BUFSIZ];			/* Input filename */
ObjList *Defstack;				/* Object definition stack. */
int Npoints = 0;				/* # of points in Polypoints */
Surface *tmpsurf;				/* Working surface */
SurfList *CurSurf;
Texture *CurText;				/* Working list of textures */
ImageText *Imagetext;				/* Working image texture */
Matrix *CurMatrix = NULL, CurIMatrix;		/* Current transformations */
Atmosphere *CurEffect = (Atmosphere *)NULL;	/* Current atmos. effects */ 
PointList *Polypoints;				/* List of vertices */
MetaList *Metapoints, *Metapoint;
extern FILE *yyin;				/* input file pointer */
extern int yylineno;				/* Current line # in file */
extern Medium TopMedium;			/* "air" */
extern void	ObjectAddToDefined(),
		LightAddToDefined(),
		SurfaceAddToDefined();
extern Surface	*SurfaceGetNamed();
extern Object	*ObjectGetNamed();
%}
%union {
	char *c;
	int i;
	Float d;
	Vector v;
	Vec2d uv;
	Color col;
	Atmosphere *atmos;
	Surface *surf;
	Object *obj;
	Texture *text;
	Mapping *map;
	Matrix *mat;
}
%token <d> tFLOAT
%token <c> tSTRING
%token tADAPTIVE tAPERTURE tAPPLYSURF
%token tBACKGROUND tBLOB tBLOTCH tBOX tBUMP tCONE tCYL tDIRECTIONAL tCURSURF
%token tEXTENDED tEYEP tFBM tFBMBUMP tFOCALDIST tFOG tFOV tGLOSS tGRID
%token tHEIGHTFIELD tJITTERED tLIGHT tLIST tLOOKP tMARBLE tMAXDEPTH tMIST
%token tOBJECT tOUTFILE  tSKY tDISC tDIFFERENCE tUNION tINTERSECT
%token tPLANE tPOINT tPOLY tROTATE tSAMPLES tSPOT
%token tSCALE tSCREEN tSPHERE tSURFACE
%token tTHRESH tTRANSLATE tTRANSFORM tTRIANGLE tTRIANGLEUV tUP tEND
%token tTEXTURE tCHECKER tWOOD tCONTRAST tCUTOFF tCLOUD
%token tAMBIENT tDIFFUSE tREFLECT tTRANSP tSPECULAR tSPECPOW
%token tINDEX tATMOSPHERE tNOSHADOW tAREA tTRANSLU tTORUS
%token tEYESEP tSHADOWTRANSP tREPORT tVERBOSE tQUIET tWINDOW tSTRIPE
%token tMAP tUV tSPHERICAL tCYLINDRICAL tLINEAR
%token tIMAGE tSMOOTH tCOMPONENT tTEXTSURF tRANGE tTILE
%token tNAME
%type <d> Expr MExpr
%type <c> tSTRING
%type <v> Vector
%type <uv> Vec2d
%type <col> Color Intensity Lightdef
%type <text> Texturetype
%type <i> SurfCompName IExpr CombineOp
%type <atmos> EffectType
%type <obj> PrimType Primitive Instance TransTextObj
%type <obj> Csg Aggregate Object TransObj ObjType
%type <obj> Blob Box Cone Cylinder Disc HeightField Plane Poly
%type <obj> Sphere Triangle Torus AggregateType List Grid AggregateCreate
%type <obj> NamedObject
%type <surf> Surface OptSurface NamedSurf
%type <surf> SurfSpec ModifyNamedSurf
%type <map> Mapping MapMethod OptMapping
%type <mat> TransformType

%left '+' '-'
%left '*' '/' '%'
%left UMINUS
%right '^'
%%
Items		: /* empty */
		| Items Item
		;
Item		: Eyep
		| Lookp
		| Up
		| Fov
		| Screen
		| Window
		| Report
		| Aperture
		| Focaldist
		| Eyesep
		| Maxdepth
		| Samples
		| Jittered
		| Adaptive
		| Contrast
		| Cutoff
		| Background
		| Shadowtransp
		| Light
		| SurfDef
		| CurSurf
		| Outfile
		| Instance
		| NameObject
		| GlobalEffects
		;
Instance	: TransTextObj
		{
			if ($$) {
				/*
				 * Add instance to current object.
				 */
				$$->next = Defstack->obj->next;
				Defstack->obj->next = $$;
			}
		}
TransTextObj	: TransObj Textures
		{
			if ($$) {
				$$->texture = CurText;
			} /* else we could free CurText, but why bother? */
			CurText = (Texture *)NULL;
		}
		;	
TransObj	: Object Transforms
		{
			$$ = $1;
			if ($$ != (Object *)NULL) {
				if (CurMatrix) {
					/*
					 * Compute the bounding box of the
					 * transformed object.
					 */
					BoundsTransform(CurMatrix,
							$$->bounds);
					MatrixInvert(CurMatrix, &CurIMatrix);
					if ($$->trans) {
						/*
						 * If already has trans,
						 * make a copy if
						 * necessary so that the
						 * stored obj isn't changed.
						 */
						if ($$->name)
							$$->trans =
							  TransCreate(
							  &$$->trans->trans,
							  &$$->trans->itrans);
						MatrixMult(&$$->trans->trans,
							CurMatrix,
							&$$->trans->trans);
						MatrixMult(&$$->trans->itrans,
							&CurIMatrix,
							&$$->trans->itrans);
					} else {
						$$->trans = TransCreate(
							CurMatrix,&CurIMatrix);
					}
				}
			}
			if (CurMatrix) {
				/* can free curmatrix because it is
				 * *copied* into $$->trans.
				 */
				free((voidstar)CurMatrix);
				CurMatrix = (Matrix *)NULL;
			}
		}
		;
Object		: ObjType
		{
			if ($$) {
				ObjectBounds($$);
				StatsAddRep($$);
			}
		}
		| NamedObject
		;
ObjType		: Primitive
		| Aggregate
		;
Primitive	: PrimType
		{
			if ($$)
				$$->prims = 1;	/* one primitive */
		}
		;
PrimType	: Plane
		| Sphere
		| Box
		| Triangle
		| Cylinder
		| Cone
		| Poly
		| HeightField
		| Disc
		| Torus
		| Blob
		;
NameObject	: tNAME tSTRING TransTextObj
		{
			if ($3) {
				$3->name = $2;
				ObjectAddToDefined($3);
			}
		};
Aggdefs		: Aggdefs Aggdef
		|
		;
Aggdef		: Instance
		| SurfDef
		| CurSurf
		| NameObject
		;
Textures	: Textures Texture
		|
		;
Texture		: tTEXTURE Texturetype Transforms
		{
			Texture *tp;
			if ($2 != (Texture *)NULL) {
				/*
				 * Set transformation information.
				 */
				if (CurMatrix) {
					MatrixInvert(CurMatrix, &CurIMatrix);
					/*
					 * Texture trans is the inverse
					 * of what the user specified.
					 */
					$2->trans = TransCreate(
						&CurIMatrix,CurMatrix);
					free((voidstar)CurMatrix);
					CurMatrix = (Matrix *)NULL;
				}
				/*
				 * Walk to the end of list of textures and
				 * append new texture.  This is done so that
				 * textures are applied in the expected order.
				 */
				$2->next = (Texture *)NULL;

				if (CurText) {
					for (tp=CurText;tp->next;tp=tp->next)
							;
					tp->next = $2;
				} else {
					CurText = $2;
				}
			}
		}
		;
Texturetype	: tCHECKER Surface
		{
			$$ = TextCheckerCreate($2);
		}
		| tBLOTCH Expr Surface
		{
			$$ = TextBlotchCreate($2, $3);
		}
		| tBUMP Expr
		{
			$$ = TextBumpCreate($2);
		}
		| tMARBLE
		{
			$$ = TextMarbleCreate((char *)NULL);
		}
		| tMARBLE tSTRING
		{
			$$ = 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 tSTRING
		{
			$$ = 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);
		}
		| ImageText
		{
			/*
			 * Image texturing has so many options
			 * that specification is keyword-based.
			 */
			if (Imagetext->image == (Image *)NULL)
				$$ = (Texture *)NULL;
			else
				$$ = TextCreate(Imagetext, ImageTextApply);
			Imagetext = (ImageText *)NULL;
		}
		| tSTRIPE Surface Expr Expr OptMapping
		{
			$$ = TextStripeCreate($2, $3, $4, $5);
		}
		;
ImageText	: ImageTextType ImageTextOptions
		;
ImageTextType	: tIMAGE tSTRING
		{
			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 tSTRING
		{
			/*
			 * Create a copy of the named object.
			 */
			$$ = ObjectCopyNamed($3);
			$$->surf = $2;
		}
		| tOBJECT tSTRING
		{
			$$ = ObjectCopyNamed($2);
			$$->surf = CurSurf->surf;
		}
		;	
Transforms	: Transforms PostTransform
		| /* empty */
		;
PostTransform	: TransformType
		{
			if (CurMatrix == (Matrix *)NULL)
				CurMatrix = MatrixCreate();
			MatrixMult(CurMatrix, $1, CurMatrix);
			free((voidstar)$1);
		}
		;
TransformType	: tTRANSLATE Vector
		{
			$$ = TranslationMatrix(&$2);
		}
		| tROTATE Vector Expr
		{
			$$ = RotationMatrix(&($2), deg2rad($3));
		}
		| tSCALE Expr Expr Expr
		{
			$$ = ScaleMatrix($2, $3, $4);
		}
		| tTRANSFORM Expr Expr Expr
				Expr Expr Expr
				Expr Expr Expr
		{
			$$ = ArbitraryMatrix(
				$2, $3, $4, $5, $6, $7, $8, $9, $10,
				0., 0., 0.);
		}
		| tTRANSFORM Expr Expr Expr
				Expr Expr Expr
				Expr Expr Expr
				Expr Expr Expr
		{
			$$ = ArbitraryMatrix(
				$2, $3, $4, $5, $6, $7, $8, $9, $10,
				$11, $12, $13);
		}
		;
Eyep		: tEYEP Vector Transforms
		{
			Camera.pos = $2;
			/*
			 * Eye can be transformed...
			 */
			if (CurMatrix) {
				PointTransform(&Camera.pos, CurMatrix);
				free((voidstar)CurMatrix);
				CurMatrix = (Matrix*)NULL;
			}
		}
		;
Lookp		: tLOOKP Vector
		{
			Camera.lookp = $2;
		}
		;
Up		: tUP Vector
		{
			Camera.up = $2;
		}
		;
Fov		: tFOV Expr Expr
		{
			Camera.hfov = $2;
			Camera.vfov = $3;
		}
		| tFOV Expr
		{
			Camera.hfov = $2;
			Camera.vfov = UNSET;
		}
		;
Samples		: tSAMPLES IExpr
		{
			if (!Options.samples_set)
				Options.jit_samples = $2;
		}
		;
Adaptive	: tADAPTIVE IExpr
		{
			if (!Options.adaptive_set &&
			    !Options.jittered_set)
				Options.pixel_div = $2;
		}
		;
Contrast	: tCONTRAST Expr Expr Expr
		{
			if (!Options.contrast_set) {
				Options.contrast.r = $2;
				Options.contrast.g = $3;
				Options.contrast.b = $4;
			}
		}
		;
Cutoff		: tCUTOFF Expr
		{
			if (!Options.cutoff_set)
				Options.cutoff = $2;
		}
		;
Jittered	: tJITTERED
		{
			if (!Options.adaptive_set)
				Options.jittered = TRUE;
		}
		;
Screen		: tSCREEN IExpr IExpr 
		{
			if (!Options.resolution_set) {
				Screen.xres = $2;
				Screen.yres = $3;
			}
		}
		;
Window		: tWINDOW IExpr IExpr IExpr IExpr
		{
			if (!Options.window_set) {
				Screen.minx = $2;
				Screen.miny = $3;
				Screen.maxx = $4;
				Screen.maxy = $5;
			}
		}
Report		: tREPORT Verbose Quiet IExpr tSTRING
		{
			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 tSTRING
		{
			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 = TRUE;
		}
		;
Light		: Lightdef tPOINT Vector
		{
			LightAddToDefined(LightPointCreate(&$1, &$3));
		}
		| Lightdef tDIRECTIONAL Vector
		{
			LightAddToDefined(LightInfiniteCreate(&$1, &$3));
		}
		| Lightdef tEXTENDED Expr Vector
		{
			LightAddToDefined(LightExtendedCreate(&$1, $3, &$4));
		}
		| Lightdef tSPOT Vector Vector Expr
		{
			LightAddToDefined(LightSpotCreate(&$1, &$3, &$4, $5,
					0., 0.));
		}
		| Lightdef tSPOT Vector Vector Expr Expr Expr
		{
			/* light <intens> spot from <to> coef inner_rad
					outer_rad */
			LightAddToDefined(LightSpotCreate(&$1, &$3, &$4, $5,
					$6, $7));
		}
		| Lightdef tAREA Vector Vector IExpr Vector IExpr
		{
			AreaLightCreate(&$1, &$3, &$4, $5, &$6, $7);
		}
		;
Lightdef	: tLIGHT Intensity
		{
			$$ = $2;
		}
		;
CurSurf		: tAPPLYSURF Surface
		{
			CurSurf->surf = $2;
		}
		;
OptSurface	: Surface
		| /* EMPTY */
		{
			$$ = CurSurf->surf;
		}
		;
Surface		: NamedSurf
		| ModifyNamedSurf
		| SurfSpec
		;
NamedSurf	: tSTRING
		{
			$$ = SurfaceGetNamed($1);
		}
		| tCURSURF
		{
			extern Surface DefaultSurface;

			if (CurSurf->surf)
				$$ = CurSurf->surf;
			else
				$$ = &DefaultSurface;
		}
		;
ModifyNamedSurf : CopyNamedSurf SurfComponent SurfComponents
		{
			$$ = tmpsurf;
			tmpsurf = (Surface *)NULL;
		}
		| CopyCurSurf SurfComponent SurfComponents
		{
			$$ = tmpsurf;
			tmpsurf = (Surface *)NULL;
		}
		;
CopyNamedSurf	: tSTRING
		{
			tmpsurf = SurfaceCopy(SurfaceGetNamed($1));
		}
		;
CopyCurSurf	: tCURSURF
		{
			extern Surface DefaultSurface;
			if (CurSurf->surf)
				tmpsurf = SurfaceCopy(CurSurf->surf);
			else
				tmpsurf = SurfaceCopy(&DefaultSurface);
		}
		;
SurfSpec	: SurfComponent SurfComponents
		{
			$$ = tmpsurf;
			tmpsurf = (Surface *)NULL;
		} 
		;
SurfDef		: tSURFACE tSTRING Surface
		{
			tmpsurf = SurfaceCopy($3);
			tmpsurf->name = strsave($2);
			SurfaceAddToDefined(tmpsurf);
			tmpsurf = (Surface *)NULL;
		}
		| tSURFACE tSTRING
		{
			/* black surface */
			tmpsurf = SurfaceCreate();
			tmpsurf->name = strsave($2);
			SurfaceAddToDefined(tmpsurf);
			tmpsurf = (Surface *)NULL;
		}
		;
SurfComponents	: SurfComponents SurfComponent
		| /* EMPTY */
		;
SurfComponent	: Ambient
		| Diffuse
		| Specular
		| Speccoef
		| Reflect
		| Transp
		| Index
		| Translu
		| Noshadow
		| SurfEffect
		;
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;
		}
		;
Speccoef	: tSPECPOW Expr
		{
			if (tmpsurf == (Surface *)NULL)
				tmpsurf = SurfaceCreate();
			tmpsurf->coef = $2;
		}
		;
Reflect		: tREFLECT Expr
		{
			if (tmpsurf == (Surface *)NULL)
				tmpsurf = SurfaceCreate();
			tmpsurf->refl = $2;
		}
		;
Transp		: tTRANSP Expr
		{
			if (tmpsurf == (Surface *)NULL)
				tmpsurf = SurfaceCreate();
			tmpsurf->transp = $2;
		}
		;
Index		: tINDEX Expr
		{
			if (tmpsurf == (Surface *)NULL)
				tmpsurf = SurfaceCreate();
			tmpsurf->kref = $2;
		}
		;
Translu		: tTRANSLU Expr Expr
		{
			if (tmpsurf == (Surface *)NULL)
				tmpsurf = SurfaceCreate();
			tmpsurf->translucency = $2;
			tmpsurf->stcoef = $3;
		}
		;
Noshadow	: tNOSHADOW
		{
			if (tmpsurf == (Surface *)NULL)
				tmpsurf = SurfaceCreate();
			tmpsurf->noshadow = TRUE;
		}
		;
SurfEffect	: Effect
		{
			if (tmpsurf == (Surface *)NULL)
				tmpsurf = SurfaceCreate();
			/* If tmpsurf has an effect, stick this on head. */
			if (tmpsurf->atmos) {
				/* Merge lists of effects */
				/* THIS IS WRONG */
				CurEffect->next = tmpsurf->atmos;
			}
			tmpsurf->atmos = CurEffect;
			CurEffect = (Atmosphere *)NULL;
		}
		;
HeightField	: tHEIGHTFIELD Surface tSTRING
		{
			$$ = ObjHfCreate($3);
			if ($$)
				$$->surf = $2;
		}
		| tHEIGHTFIELD tSTRING
		{
			$$ = ObjHfCreate($2);
		}
		;
Poly		: tPOLY OptSurface Polypoints
		{
			$$ = ObjPolygonCreate(Polypoints, Npoints);
			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++;
		}
		;
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 = ObjStackPop(Defstack);
			/* Pop current surface */
			CurSurf = SurfPop(CurSurf);
		}
		;
AggregateDef	: AggregateCreate Aggdefs tEND
		{
			/* Convert aggregate, pop stacks, etc. */
			if ($1) {
				if (Defstack->obj->next == (Object *)NULL) {
					RLerror(RL_WARN,
						"Null object defined.\n");
					Defstack->obj = (Object *)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 = (Object *)NULL;
					else
						ObjectBounds(Defstack->obj);
				}
			}
		}
		;
AggregateCreate	: AggregateType
		{
			if ($1) {
				Defstack = ObjStackPush($1, Defstack);
				CurSurf = SurfPush((Surface *)NULL, CurSurf);
			}
		};
AggregateType	: List
		| Grid
		| Csg
		;
List		: tLIST
		{
			$$ = ObjListCreate();
		}
		;
Grid		: tGRID IExpr IExpr IExpr
		{
			$$ = ObjGridCreate($2, $3, $4);
		}
		;
Csg		: CombineOp
		{
			$$ = ObjCsgCreate($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 */
				$$ = ObjCylinderCreate($3, &$4, &$6);
			} else
				$$ = ObjConeCreate($3, &$4, $5, &$6);
			if ($$)
				$$->surf = $2;
		}
		;
Cylinder	: tCYL OptSurface Expr Vector Vector
		{
			$$ = ObjCylinderCreate($3, &$4, &$5);
			if ($$)
				$$->surf = $2;
		}
		;
Sphere		: tSPHERE OptSurface Expr Vector
		{
			$$ = ObjSphereCreate($3, &($4));
			if ($$)
				$$->surf = $2;
		}
		;
Disc		: tDISC OptSurface Expr Vector Vector
		{
			$$ = ObjDiscCreate($3, &($4), &($5));
			if ($$)
				$$->surf = $2;
		}
		;
Box		: tBOX OptSurface Vector Vector
		{
			$$ = ObjBoxCreate(&$3, &$4);
			if ($$)
				$$->surf = $2;
		}
		;
Triangle	: tTRIANGLE OptSurface Vector Vector Vector
		{
			$$ = ObjTriangleCreate(FLATTRI, &($3), &($4), &($5),
				(Vector *)NULL, (Vector *)NULL, (Vector *)NULL,
				(Vec2d *)NULL, (Vec2d *)NULL, (Vec2d *)NULL);
			if ($$)
				$$->surf = $2;
		}
		| tTRIANGLE OptSurface  Vector Vector
					Vector Vector
					Vector Vector
		{
			$$ = ObjTriangleCreate(PHONGTRI, &($3), &($5),
				&($7), &($4), &($6), &($8),
				(Vec2d *)NULL, (Vec2d *)NULL, (Vec2d *)NULL);
			if ($$)
				$$->surf = $2;
		}
		| tTRIANGLEUV OptSurface Vector Vector Vec2d
					 Vector Vector Vec2d
					 Vector Vector Vec2d
		{
			$$ = ObjTriangleCreate(PHONGTRI, &($3), &($6), &($9),
						&($4), &($7), &($10),
						&($5), &($8), &($11));
			if ($$)
				$$->surf = $2;
		}
		;
Plane		: tPLANE OptSurface Vector Vector
		{
			$$ = ObjPlaneCreate(&($3), &($4));
			if ($$)
				$$->surf = $2;
		}
		;
Torus		: tTORUS OptSurface Expr Expr Vector Vector
		{
			$$ = ObjTorusCreate($3, $4, &($5), &($6));
			if ($$)
				$$->surf = $2;
		}
		;
Blob		: tBLOB OptSurface Expr MetaPoints
		{
			$$ = ObjBlobCreate($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;
			Metapoint->mvec.rs = $2;
			Metapoint->mvec.x = $3;
			Metapoint->mvec.y = $4;
			Metapoint->mvec.z = $5;
			Metapoint->next = Metapoints;
			Metapoints = Metapoint;
			Npoints++;
		}
		;
Outfile		: tOUTFILE tSTRING
		{
			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
		{
			TopMedium.atmos = CurEffect;
			CurEffect = (Atmosphere *)NULL;
		}
		| tATMOSPHERE IExpr Effects
		{
			if ($2 <= 0.)
				RLerror(RL_PANIC,
				"Index of refraction must be positive.\n");
			TopMedium.index = $2;
			TopMedium.atmos = 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));
		}
		;
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);
		}
		| tLINEAR
		{
			$$ = LinearMappingCreate((Vector *)NULL,
				(Vector *)NULL, (Vector *)NULL);
		}
		| tLINEAR Vector Vector Vector
		{
			/* origin up uaxis */
			$$ = LinearMappingCreate(&$2, &$3, &$4);
		}
		;
SurfCompName	: tAMBIENT
		{
			$$ = AMBIENT;
		}
		| tDIFFUSE
		{
			$$ = DIFFUSE;
		}
		| tREFLECT
		{
			$$ = REFLECT;
		}
		| tTRANSP
		{
			$$ = TRANSP;
		}
		| tSPECULAR
		{
			$$ = SPECULAR;
		}
		| tSPECPOW
		{
			$$ = SPECPOW;
		}
		| tBUMP
		{
			$$ = BUMP;
		}
		;
Intensity	: Expr
		{ $$.r = $$.g = $$.b = $1; }
		| Color
		;
IExpr		: Expr
		{ $$ = (int)$1; }
		;
Expr		: tFLOAT
		| '-' tFLOAT
		{ $$ = - $2; }
		| '+' tFLOAT
		{ $$ = $2; }
		| '(' MExpr ')'
		{
			$$ = $2;
		}
		;
MExpr		: tFLOAT
		| '(' MExpr ')'
		{
			$$ = $2;
		}
		| MExpr '+' MExpr
		{
			$$ = $1 + $3;
		}
		| MExpr '-' MExpr
		{
			$$ = $1 - $3;
		}
		| MExpr '*' MExpr
		{
			$$ = $1 * $3;
		}
		| MExpr '/' MExpr
		{
			$$ = $1 / $3;
		}
		| MExpr '%' MExpr
		{
			$$ = (int)$1 % (int)$3;
		}
		| '-' MExpr %prec UMINUS
		{
			$$ = -$2;
		}
		| '+' MExpr %prec UMINUS
		{
			$$ = $2;
		}
		| MExpr '^' MExpr
		{
			$$ = pow($1, $3);
		}
		;	
%%
/*
 * Issue error message containing filename and line number, and exit.
 */
/*VARARGS1*/
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);
}

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