2254 lines
46 KiB
Plaintext
2254 lines
46 KiB
Plaintext
// Filename: parser.y
|
|
// Created by: drose (16Jan99)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
%{
|
|
|
|
#include <pandabase.h>
|
|
#include "parserDefs.h"
|
|
#include "lexerDefs.h"
|
|
#include "eggObject.h"
|
|
#include "eggGroup.h"
|
|
#include "eggVertex.h"
|
|
#include "eggVertexPool.h"
|
|
#include "eggPolygon.h"
|
|
#include "eggPoint.h"
|
|
#include "eggNurbsSurface.h"
|
|
#include "eggNurbsCurve.h"
|
|
#include "eggTable.h"
|
|
#include "eggSAnimData.h"
|
|
#include "eggXfmSAnim.h"
|
|
#include "eggXfmAnimData.h"
|
|
#include "eggTexture.h"
|
|
#include "eggMaterial.h"
|
|
#include "eggComment.h"
|
|
#include "eggCoordinateSystem.h"
|
|
#include "eggExternalReference.h"
|
|
#include "eggData.h"
|
|
|
|
#include <string_utils.h>
|
|
#include <filename.h>
|
|
#include <luse.h>
|
|
#include <lmatrix.h>
|
|
#include <coordinateSystem.h>
|
|
|
|
#include <vector>
|
|
|
|
// Because our token type contains objects of type string, which
|
|
// require correct copy construction (and not simply memcpying), we
|
|
// cannot use bison's built-in auto-stack-grow feature. As an easy
|
|
// solution, we ensure here that we have enough yacc stack to start
|
|
// with, and that it doesn't ever try to grow.
|
|
#define YYINITDEPTH 1000
|
|
#define YYMAXDEPTH 1000
|
|
|
|
// We need a stack of EggObject pointers. Each time we encounter a
|
|
// nested EggObject of some kind, we'll allocate a new one of these
|
|
// and push it onto the stack. At any given time, the top of the
|
|
// stack is the EggObject we are currently scanning.
|
|
|
|
typedef vector<PT(EggObject)> EggStack;
|
|
static EggStack egg_stack;
|
|
|
|
// There's one "top-level" egg node, which is where we should parent
|
|
// things (e.g. textures) encountered in the egg file that don't have
|
|
// an explicit place in the tree. If this is NULL, such things won't
|
|
// be parented anywhere.
|
|
static EggGroupNode *egg_top_node;
|
|
|
|
// We need a table mapping vertex pool names to vertex pools.
|
|
|
|
typedef map<string, PT(EggVertexPool)> VertexPools;
|
|
static VertexPools vertex_pools;
|
|
|
|
// And another one mapping texture names to textures.
|
|
|
|
typedef map<string, PT(EggTexture)> Textures;
|
|
static Textures textures;
|
|
|
|
// And again for material names to materials.
|
|
|
|
typedef map<string, PT(EggMaterial)> Materials;
|
|
static Materials materials;
|
|
|
|
// We need to be able to save the index number requested for a vertex
|
|
// temporarily.
|
|
static int vertex_index;
|
|
|
|
// We need to hold a matrix for a little bit while parsing the
|
|
// <Transform> entries.
|
|
static LMatrix4d matrix_3d;
|
|
static LMatrix3d matrix_2d;
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Defining the interface to the parser.
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
egg_init_parser(istream &in, const string &filename,
|
|
EggObject *tos, EggGroupNode *top_node) {
|
|
egg_init_lexer(in, filename);
|
|
|
|
egg_stack.clear();
|
|
vertex_pools.clear();
|
|
textures.clear();
|
|
materials.clear();
|
|
|
|
egg_stack.push_back(tos);
|
|
egg_top_node = top_node;
|
|
}
|
|
|
|
void
|
|
egg_cleanup_parser() {
|
|
// Clean these out after we're done, so we don't keep big memory
|
|
// structures around needlessly.
|
|
egg_stack.clear();
|
|
vertex_pools.clear();
|
|
textures.clear();
|
|
materials.clear();
|
|
}
|
|
|
|
%}
|
|
|
|
%token <_number> NUMBER
|
|
%token <_string> STRING
|
|
|
|
%token BEZIERCURVE BFACE BILLBOARD BUNDLE CLOSED COLLIDE COMMENT
|
|
%token COORDSYSTEM CV DART
|
|
%token DNORMAL DRGBA DUV DXYZ DCS DISTANCE DTREF
|
|
%token DYNAMICVERTEXPOOL EXTERNAL_FILE
|
|
%token FLIGHT GROUP HIP INTANGENT
|
|
%token JOINT KNOTS INCLUDE
|
|
%token INSTANCE LOOP MATERIAL MATRIX3 MATRIX4 MODEL MREF NORMAL
|
|
%token NURBSCURVE NURBSSURFACE OBJECTTYPE ORDER
|
|
%token OUTTANGENT POINTLIGHT POLYGON REF RGBA ROTATE ROTX ROTY ROTZ
|
|
%token SANIM SCALAR SCALE SEQUENCE SHADING SWITCH SWITCHCONDITION
|
|
%token TABLE TABLE_V TEXLIST TEXTURE TLENGTHS TRANSFORM TRANSLATE
|
|
%token TREF TRIM TXT UKNOTS UV VKNOTS VERTEX VERTEXANIM
|
|
%token VERTEXPOOL VERTEXREF
|
|
%token XFMANIM XFMSANIM
|
|
|
|
/* These special tokens are used to set the starting state of the
|
|
parser. The lexer places the appropriate one of these on the head
|
|
of the input stream. */
|
|
%token START_EGG
|
|
%token START_GROUP_BODY
|
|
|
|
%type <_egg> node
|
|
%type <_egg> coordsystem
|
|
%type <_egg> comment
|
|
%type <_egg> texture
|
|
%type <_egg> material
|
|
%type <_egg> external_reference
|
|
%type <_egg> vertex_pool
|
|
%type <_egg> group
|
|
%type <_egg> joint
|
|
%type <_egg> instance
|
|
%type <_egg> polygon
|
|
%type <_egg> point_light
|
|
%type <_egg> nurbs_surface
|
|
%type <_egg> nurbs_curve
|
|
%type <_egg> table
|
|
%type <_egg> bundle
|
|
%type <_egg> sanim
|
|
%type <_egg> xfmanim
|
|
%type <_egg> xfm_s_anim
|
|
|
|
%type <_string> required_name optional_name
|
|
%type <_string> required_string optional_string
|
|
%type <_string> string
|
|
%type <_string> repeated_string repeated_string_body
|
|
%type <_number> group_vertex_membership
|
|
%type <_number_list> integer_list
|
|
%type <_number_list> real_list
|
|
%type <_egg> texture_name
|
|
%type <_egg> material_name
|
|
%type <_egg> vertex_pool_name
|
|
|
|
%type <_number> real
|
|
%type <_number> integer
|
|
%type <_number> real_or_string
|
|
|
|
%%
|
|
|
|
|
|
grammar:
|
|
START_EGG egg
|
|
| START_GROUP_BODY group_body
|
|
;
|
|
|
|
/*
|
|
* egg
|
|
*
|
|
* enter: TOS is EggData.
|
|
* exit: egg file has been completely read.
|
|
*
|
|
*/
|
|
egg:
|
|
empty
|
|
| egg node
|
|
{
|
|
DCAST(EggData, egg_stack.back())->add_child(DCAST(EggNode, $2));
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* node
|
|
*
|
|
* enter:
|
|
* exit: returns a new EggNode of some kind.
|
|
*
|
|
*/
|
|
node:
|
|
coordsystem
|
|
| comment
|
|
| texture
|
|
| material
|
|
| external_reference
|
|
| vertex_pool
|
|
| group
|
|
| joint
|
|
| instance
|
|
| polygon
|
|
| point_light
|
|
| nurbs_surface
|
|
| nurbs_curve
|
|
| table
|
|
;
|
|
|
|
/*
|
|
* coordsystem
|
|
*
|
|
* enter:
|
|
* exit: returns a new EggCoordinateSystem.
|
|
*
|
|
*/
|
|
coordsystem:
|
|
COORDSYSTEM '{' required_string '}'
|
|
{
|
|
string strval = $3;
|
|
EggCoordinateSystem *cs = new EggCoordinateSystem;
|
|
|
|
CoordinateSystem f = parse_coordinate_system_string(strval);
|
|
if (f == CS_invalid) {
|
|
eggyywarning("Unknown coordinate system " + strval);
|
|
} else {
|
|
cs->set_value(f);
|
|
}
|
|
$$ = cs;
|
|
}
|
|
;
|
|
|
|
/*
|
|
* comment
|
|
*
|
|
* enter:
|
|
* exit: returns a new EggComment.
|
|
*
|
|
*/
|
|
comment:
|
|
COMMENT optional_name '{' repeated_string '}'
|
|
{
|
|
$$ = new EggComment($2, $4);
|
|
}
|
|
;
|
|
|
|
/*
|
|
* texture
|
|
*
|
|
* enter:
|
|
* exit: returns a new EggTexture.
|
|
*
|
|
*/
|
|
texture:
|
|
TEXTURE required_name '{' required_string
|
|
{
|
|
string tref_name = $2;
|
|
Filename filename = $4;
|
|
EggTexture *texture = new EggTexture(tref_name, filename);
|
|
|
|
if (textures.find(tref_name) != textures.end()) {
|
|
eggyywarning("Duplicate texture name " + tref_name);
|
|
}
|
|
textures[tref_name] = texture;
|
|
|
|
egg_stack.push_back(texture);
|
|
}
|
|
texture_body '}'
|
|
{
|
|
$$ = egg_stack.back();
|
|
egg_stack.pop_back();
|
|
}
|
|
;
|
|
|
|
/*
|
|
* texture_body
|
|
*
|
|
* enter: TOS is EggTexture; filename has already been read.
|
|
* exit: texture contents have been filled.
|
|
*
|
|
*/
|
|
texture_body:
|
|
empty
|
|
| texture_body SCALAR required_name '{' real_or_string '}'
|
|
{
|
|
EggTexture *texture = DCAST(EggTexture, egg_stack.back());
|
|
string name = $3;
|
|
double value = $<_number>5;
|
|
string strval = $<_string>5;
|
|
|
|
if (cmp_nocase_uh(name, "format") == 0) {
|
|
EggTexture::Format f = EggTexture::string_format(strval);
|
|
if (f == EggTexture::F_unspecified) {
|
|
eggyywarning("Unknown texture format " + strval);
|
|
} else {
|
|
texture->set_format(f);
|
|
}
|
|
|
|
} else if (cmp_nocase_uh(name, "wrap") == 0) {
|
|
EggTexture::WrapMode w = EggTexture::string_wrap_mode(strval);
|
|
if (w == EggTexture::WM_unspecified) {
|
|
eggyywarning("Unknown texture wrap mode " + strval);
|
|
} else {
|
|
texture->set_wrap_mode(w);
|
|
}
|
|
|
|
} else if (cmp_nocase_uh(name, "wrapu") == 0) {
|
|
EggTexture::WrapMode w = EggTexture::string_wrap_mode(strval);
|
|
if (w == EggTexture::WM_unspecified) {
|
|
eggyywarning("Unknown texture wrap mode " + strval);
|
|
} else {
|
|
texture->set_wrap_u(w);
|
|
}
|
|
|
|
} else if (cmp_nocase_uh(name, "wrapv") == 0) {
|
|
EggTexture::WrapMode w = EggTexture::string_wrap_mode(strval);
|
|
if (w == EggTexture::WM_unspecified) {
|
|
eggyywarning("Unknown texture wrap mode " + strval);
|
|
} else {
|
|
texture->set_wrap_v(w);
|
|
}
|
|
|
|
} else if (cmp_nocase_uh(name, "minfilter") == 0) {
|
|
EggTexture::FilterType f = EggTexture::string_filter_type(strval);
|
|
if (f == EggTexture::FT_unspecified) {
|
|
eggyywarning("Unknown texture filter type " + strval);
|
|
} else {
|
|
texture->set_minfilter(f);
|
|
}
|
|
|
|
} else if (cmp_nocase_uh(name, "magfilter") == 0) {
|
|
EggTexture::FilterType f = EggTexture::string_filter_type(strval);
|
|
if (f == EggTexture::FT_unspecified) {
|
|
eggyywarning("Unknown texture filter type " + strval);
|
|
} else {
|
|
texture->set_magfilter(f);
|
|
}
|
|
|
|
} else if (cmp_nocase_uh(name, "magfilteralpha") == 0) {
|
|
EggTexture::FilterType f = EggTexture::string_filter_type(strval);
|
|
if (f == EggTexture::FT_unspecified) {
|
|
eggyywarning("Unknown texture filter type " + strval);
|
|
} else {
|
|
texture->set_magfilteralpha(f);
|
|
}
|
|
|
|
} else if (cmp_nocase_uh(name, "magfiltercolor") == 0) {
|
|
EggTexture::FilterType f = EggTexture::string_filter_type(strval);
|
|
if (f == EggTexture::FT_unspecified) {
|
|
eggyywarning("Unknown texture filter type " + strval);
|
|
} else {
|
|
texture->set_magfiltercolor(f);
|
|
}
|
|
|
|
} else if (cmp_nocase_uh(name, "anisotropic_degree") == 0) {
|
|
texture->set_anisotropic_degree(value);
|
|
|
|
} else if (cmp_nocase_uh(name, "envtype") == 0) {
|
|
EggTexture::EnvType e = EggTexture::string_env_type(strval);
|
|
if (e == EggTexture::ET_unspecified) {
|
|
eggyywarning("Unknown texture env type " + strval);
|
|
} else {
|
|
texture->set_env_type(e);
|
|
}
|
|
|
|
} else if (cmp_nocase_uh(name, "alpha") == 0) {
|
|
EggAlphaMode::AlphaMode a = EggAlphaMode::string_alpha_mode(strval);
|
|
if (a == EggAlphaMode::AM_unspecified) {
|
|
eggyywarning("Unknown alpha mode " + strval);
|
|
} else {
|
|
texture->set_alpha_mode(a);
|
|
}
|
|
|
|
} else if (cmp_nocase_uh(name, "draw_order") == 0) {
|
|
texture->set_draw_order(value);
|
|
|
|
} else if (cmp_nocase_uh(name, "alpha_file") == 0) {
|
|
texture->set_alpha_file(strval);
|
|
|
|
} else {
|
|
eggyywarning("Unsupported texture scalar: " + name);
|
|
}
|
|
}
|
|
| texture_body transform_2d
|
|
;
|
|
|
|
/*
|
|
* material
|
|
*
|
|
* enter:
|
|
* exit: returns a new EggMaterial.
|
|
*
|
|
*/
|
|
material:
|
|
MATERIAL required_name '{'
|
|
{
|
|
string mref_name = $2;
|
|
EggMaterial *material = new EggMaterial(mref_name);
|
|
|
|
if (materials.find(mref_name) != materials.end()) {
|
|
eggyywarning("Duplicate material name " + mref_name);
|
|
}
|
|
materials[mref_name] = material;
|
|
|
|
egg_stack.push_back(material);
|
|
}
|
|
material_body '}'
|
|
{
|
|
$$ = egg_stack.back();
|
|
egg_stack.pop_back();
|
|
}
|
|
;
|
|
|
|
/*
|
|
* material_body
|
|
*
|
|
* enter: TOS is EggMaterial; filename has already been read.
|
|
* exit: material contents have been filled.
|
|
*
|
|
*/
|
|
material_body:
|
|
empty
|
|
| material_body SCALAR required_name '{' real_or_string '}'
|
|
{
|
|
EggMaterial *material = DCAST(EggMaterial, egg_stack.back());
|
|
string name = $3;
|
|
double value = $<_number>5;
|
|
|
|
if (cmp_nocase_uh(name, "diffr") == 0) {
|
|
Colorf diff = material->get_diff();
|
|
diff[0] = value;
|
|
material->set_diff(diff);
|
|
} else if (cmp_nocase_uh(name, "diffg") == 0) {
|
|
Colorf diff = material->get_diff();
|
|
diff[1] = value;
|
|
material->set_diff(diff);
|
|
} else if (cmp_nocase_uh(name, "diffb") == 0) {
|
|
Colorf diff = material->get_diff();
|
|
diff[2] = value;
|
|
material->set_diff(diff);
|
|
} else {
|
|
eggyywarning("Unsupported material scalar: " + name);
|
|
}
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* external_reference
|
|
*
|
|
* enter:
|
|
* exit: returns a new EggExternalReference.
|
|
*
|
|
*/
|
|
external_reference:
|
|
EXTERNAL_FILE optional_name '{' required_string '}'
|
|
{
|
|
string node_name = $2;
|
|
Filename filename = $4;
|
|
EggExternalReference *ref = new EggExternalReference(node_name, filename);
|
|
$$ = ref;
|
|
}
|
|
| string EXTERNAL_FILE optional_name '{' required_string '}'
|
|
{
|
|
if (cmp_nocase_uh($1, "group") != 0) {
|
|
eggyyerror("keyword 'group' expected");
|
|
}
|
|
string node_name = $3;
|
|
Filename filename = $5;
|
|
EggExternalReference *ref = new EggExternalReference(node_name, filename);
|
|
$$ = ref;
|
|
}
|
|
;
|
|
|
|
/*
|
|
* vertex_pool
|
|
*
|
|
* enter:
|
|
* exit: returns a new EggVertexPool.
|
|
*
|
|
*/
|
|
vertex_pool:
|
|
VERTEXPOOL required_name
|
|
{
|
|
string name = $2;
|
|
EggVertexPool *pool = new EggVertexPool(name);
|
|
|
|
if (vertex_pools.find(name) != vertex_pools.end()) {
|
|
eggyywarning("Duplicate vertex pool name " + name);
|
|
}
|
|
vertex_pools[name] = pool;
|
|
|
|
egg_stack.push_back(pool);
|
|
}
|
|
'{' vertex_pool_body '}'
|
|
{
|
|
$$ = egg_stack.back();
|
|
egg_stack.pop_back();
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* vertex_pool_body
|
|
*
|
|
* enter: TOS is EggVertexPool.
|
|
* exit: vertex pool contents have been filled.
|
|
*
|
|
*/
|
|
vertex_pool_body:
|
|
empty
|
|
| vertex_pool_body vertex
|
|
;
|
|
|
|
/*
|
|
* vertex
|
|
*
|
|
* enter: TOS is EggVertexPool.
|
|
* exit: new vertex has been added to vertex pool.
|
|
*
|
|
*/
|
|
vertex:
|
|
VERTEX
|
|
{
|
|
egg_stack.push_back(new EggVertex);
|
|
}
|
|
'{' vertex_body '}'
|
|
{
|
|
PT(EggVertex) vtx = DCAST(EggVertex, egg_stack.back());
|
|
egg_stack.pop_back();
|
|
|
|
DCAST(EggVertexPool, egg_stack.back())->add_vertex(vtx);
|
|
}
|
|
| VERTEX integer
|
|
{
|
|
vertex_index = (int)$2;
|
|
EggVertexPool *pool = DCAST(EggVertexPool, egg_stack.back());
|
|
|
|
if (vertex_index < 0) {
|
|
ostringstream errmsg;
|
|
errmsg << "Ignoring invalid vertex index " << vertex_index
|
|
<< " in vertex pool " << pool->get_name() << ends;
|
|
eggyywarning(errmsg);
|
|
vertex_index = -1;
|
|
|
|
} else if (pool->get_vertex(vertex_index) != NULL) {
|
|
ostringstream errmsg;
|
|
errmsg << "Ignoring duplicate vertex index " << vertex_index
|
|
<< " in vertex pool " << pool->get_name() << ends;
|
|
eggyywarning(errmsg);
|
|
vertex_index = -1;
|
|
}
|
|
|
|
// Even if we didn't like the vertex index number, we still need to
|
|
// go ahead and parse the vertex. We just won't save it.
|
|
|
|
egg_stack.push_back(new EggVertex);
|
|
}
|
|
'{' vertex_body '}'
|
|
{
|
|
PT(EggVertex) vtx = DCAST(EggVertex, egg_stack.back());
|
|
egg_stack.pop_back();
|
|
|
|
EggVertexPool *pool = DCAST(EggVertexPool, egg_stack.back());
|
|
if (vertex_index != -1) {
|
|
pool->add_vertex(vtx, vertex_index);
|
|
}
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* vertex_body
|
|
*
|
|
* enter: TOS is EggVertex.
|
|
* exit: vertex contents have been filled.
|
|
*
|
|
*/
|
|
vertex_body:
|
|
real
|
|
{
|
|
DCAST(EggVertex, egg_stack.back())->set_pos($1);
|
|
}
|
|
| real real
|
|
{
|
|
DCAST(EggVertex, egg_stack.back())->set_pos(LPoint2d($1, $2));
|
|
}
|
|
| real real real
|
|
{
|
|
DCAST(EggVertex, egg_stack.back())->set_pos(LPoint3d($1, $2, $3));
|
|
}
|
|
| real real real real
|
|
{
|
|
DCAST(EggVertex, egg_stack.back())->set_pos(LPoint4d($1, $2, $3, $4));
|
|
}
|
|
| vertex_body UV '{' vertex_uv_body '}'
|
|
| vertex_body NORMAL '{' vertex_normal_body '}'
|
|
| vertex_body RGBA '{' vertex_color_body '}'
|
|
| vertex_body DXYZ string '{' real real real '}'
|
|
{
|
|
bool inserted = DCAST(EggVertex, egg_stack.back())->_dxyzs.
|
|
insert(EggMorphVertex($3, LVector3d($5, $6, $7))).second;
|
|
if (!inserted) {
|
|
eggyywarning("Ignoring repeated morph name " + $3);
|
|
}
|
|
}
|
|
| vertex_body DXYZ '{' string real real real '}'
|
|
{
|
|
bool inserted = DCAST(EggVertex, egg_stack.back())->_dxyzs.
|
|
insert(EggMorphVertex($4, LVector3d($5, $6, $7))).second;
|
|
if (!inserted) {
|
|
eggyywarning("Ignoring repeated morph name " + $4);
|
|
}
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* vertex_uv_body
|
|
*
|
|
* enter: TOS is EggVertex.
|
|
* exit: vertex UV value has been filled.
|
|
*
|
|
*/
|
|
vertex_uv_body:
|
|
real real
|
|
{
|
|
DCAST(EggVertex, egg_stack.back())->set_uv(TexCoordd($1, $2));
|
|
}
|
|
| vertex_uv_body DUV string '{' real real '}'
|
|
{
|
|
bool inserted = DCAST(EggVertex, egg_stack.back())->_duvs.
|
|
insert(EggMorphTexCoord($3, LVector2d($5, $6))).second;
|
|
if (!inserted) {
|
|
eggyywarning("Ignoring repeated morph name " + $3);
|
|
}
|
|
}
|
|
| vertex_uv_body DUV '{' string real real '}'
|
|
{
|
|
bool inserted = DCAST(EggVertex, egg_stack.back())->_duvs.
|
|
insert(EggMorphTexCoord($4, LVector2d($5, $6))).second;
|
|
if (!inserted) {
|
|
eggyywarning("Ignoring repeated morph name " + $4);
|
|
}
|
|
}
|
|
;
|
|
|
|
/*
|
|
* vertex_normal_body
|
|
*
|
|
* enter: TOS is EggVertex.
|
|
* exit: vertex normal value has been filled.
|
|
*
|
|
*/
|
|
vertex_normal_body:
|
|
real real real
|
|
{
|
|
DCAST(EggVertex, egg_stack.back())->set_normal(Normald($1, $2, $3));
|
|
}
|
|
| vertex_normal_body DNORMAL string '{' real real real '}'
|
|
{
|
|
bool inserted = DCAST(EggVertex, egg_stack.back())->_dnormals.
|
|
insert(EggMorphNormal($3, LVector3d($5, $6, $7))).second;
|
|
if (!inserted) {
|
|
eggyywarning("Ignoring repeated morph name " + $3);
|
|
}
|
|
}
|
|
| vertex_normal_body DNORMAL '{' string real real real '}'
|
|
{
|
|
bool inserted = DCAST(EggVertex, egg_stack.back())->_dnormals.
|
|
insert(EggMorphNormal($4, LVector3d($5, $6, $7))).second;
|
|
if (!inserted) {
|
|
eggyywarning("Ignoring repeated morph name " + $4);
|
|
}
|
|
}
|
|
;
|
|
|
|
/*
|
|
* vertex_color_body
|
|
*
|
|
* enter: TOS is EggVertex.
|
|
* exit: vertex color value has been filled.
|
|
*
|
|
*/
|
|
vertex_color_body:
|
|
real real real real
|
|
{
|
|
DCAST(EggVertex, egg_stack.back())->set_color(Colorf($1, $2, $3, $4));
|
|
}
|
|
| vertex_color_body DRGBA string '{' real real real real '}'
|
|
{
|
|
bool inserted = DCAST(EggVertex, egg_stack.back())->_drgbas.
|
|
insert(EggMorphColor($3, LVector4f($5, $6, $7, $8))).second;
|
|
if (!inserted) {
|
|
eggyywarning("Ignoring repeated morph name " + $3);
|
|
}
|
|
}
|
|
| vertex_color_body DRGBA '{' string real real real real '}'
|
|
{
|
|
bool inserted = DCAST(EggVertex, egg_stack.back())->_drgbas.
|
|
insert(EggMorphColor($4, LVector4f($5, $6, $7, $8))).second;
|
|
if (!inserted) {
|
|
eggyywarning("Ignoring repeated morph name " + $4);
|
|
}
|
|
}
|
|
;
|
|
|
|
/*
|
|
* group
|
|
*
|
|
* enter:
|
|
* exit: returns a new EggGroup.
|
|
*
|
|
*/
|
|
group:
|
|
GROUP optional_name
|
|
{
|
|
EggGroup *group = new EggGroup($2);
|
|
egg_stack.push_back(group);
|
|
}
|
|
'{' group_body '}'
|
|
{
|
|
$$ = egg_stack.back();
|
|
egg_stack.pop_back();
|
|
}
|
|
;
|
|
|
|
/*
|
|
* joint
|
|
*
|
|
* enter:
|
|
* exit: returns a new EggGroup, as a joint.
|
|
*
|
|
*/
|
|
joint:
|
|
JOINT optional_name
|
|
{
|
|
EggGroup *group = new EggGroup($2);
|
|
group->set_group_type(EggGroup::GT_joint);
|
|
egg_stack.push_back(group);
|
|
}
|
|
'{' group_body '}'
|
|
{
|
|
$$ = egg_stack.back();
|
|
egg_stack.pop_back();
|
|
}
|
|
;
|
|
|
|
/*
|
|
* instance
|
|
*
|
|
* enter:
|
|
* exit: returns a new EggGroup, as an instance.
|
|
*
|
|
*/
|
|
instance:
|
|
INSTANCE optional_name
|
|
{
|
|
EggGroup *group = new EggGroup($2);
|
|
group->set_group_type(EggGroup::GT_instance);
|
|
egg_stack.push_back(group);
|
|
}
|
|
'{' group_body '}'
|
|
{
|
|
$$ = egg_stack.back();
|
|
egg_stack.pop_back();
|
|
}
|
|
;
|
|
|
|
/*
|
|
* group_body
|
|
*
|
|
* enter: TOS is EggGroup.
|
|
* exit: group contents have been filled, including children.
|
|
*
|
|
*/
|
|
group_body:
|
|
empty
|
|
| group_body SCALAR required_name '{' real_or_string '}'
|
|
{
|
|
EggGroup *group = DCAST(EggGroup, egg_stack.back());
|
|
string name = $3;
|
|
double value = $<_number>5;
|
|
string strval = $<_string>5;
|
|
|
|
if (cmp_nocase_uh(name, "fps") == 0) {
|
|
group->set_switch_fps(value);
|
|
} else if (cmp_nocase_uh(name, "no_fog") == 0) {
|
|
group->set_nofog_flag(value != 0);
|
|
} else if (cmp_nocase_uh(name, "decal") == 0) {
|
|
group->set_decal_flag(value != 0);
|
|
} else if (cmp_nocase_uh(name, "direct") == 0) {
|
|
group->set_direct_flag(value != 0);
|
|
} else if (cmp_nocase_uh(name, "alpha") == 0) {
|
|
EggAlphaMode::AlphaMode a = EggAlphaMode::string_alpha_mode(strval);
|
|
if (a == EggAlphaMode::AM_unspecified) {
|
|
eggyywarning("Unknown alpha mode " + strval);
|
|
} else {
|
|
group->set_alpha_mode(a);
|
|
}
|
|
} else if (cmp_nocase_uh(name, "draw_order") == 0) {
|
|
group->set_draw_order(value);
|
|
} else if (cmp_nocase_uh(name, "collide_mask") == 0) {
|
|
group->set_collide_mask(value);
|
|
} else if (cmp_nocase_uh(name, "from_collide_mask") == 0) {
|
|
group->set_from_collide_mask(value);
|
|
} else if (cmp_nocase_uh(name, "into_collide_mask") == 0) {
|
|
group->set_into_collide_mask(value);
|
|
} else {
|
|
eggyywarning("Unknown group scalar " + name);
|
|
}
|
|
}
|
|
| group_body BILLBOARD '{' string '}'
|
|
{
|
|
EggGroup *group = DCAST(EggGroup, egg_stack.back());
|
|
string strval = $4;
|
|
|
|
EggGroup::BillboardType f = EggGroup::string_billboard_type(strval);
|
|
if (f == EggGroup::BT_none) {
|
|
eggyywarning("Unknown billboard type " + strval);
|
|
} else {
|
|
group->set_billboard_type(f);
|
|
}
|
|
}
|
|
| group_body COLLIDE optional_name '{' cs_type collide_flags '}'
|
|
{
|
|
EggGroup *group = DCAST(EggGroup, egg_stack.back());
|
|
string name = $3;
|
|
|
|
group->set_collision_name(name);
|
|
}
|
|
| group_body DCS '{' integer '}'
|
|
{
|
|
EggGroup *group = DCAST(EggGroup, egg_stack.back());
|
|
int value = (int)$4;
|
|
group->set_dcs_flag(value!=0);
|
|
}
|
|
| group_body DART '{' integer '}'
|
|
{
|
|
// The traditional flavor of DART, with { 0 } or { 1 }.
|
|
EggGroup *group = DCAST(EggGroup, egg_stack.back());
|
|
int value = (int)$4;
|
|
group->set_dart_type(value!=0 ? EggGroup::DT_default : EggGroup::DT_none);
|
|
}
|
|
| group_body DART '{' STRING '}'
|
|
{
|
|
// The special flavor of DART, with { sync } or { nosync }.
|
|
EggGroup *group = DCAST(EggGroup, egg_stack.back());
|
|
string strval = $4;
|
|
|
|
EggGroup::DartType f = EggGroup::string_dart_type(strval);
|
|
if (f == EggGroup::DT_none) {
|
|
eggyywarning("Unknown dart type " + strval);
|
|
} else {
|
|
group->set_dart_type(f);
|
|
}
|
|
}
|
|
| group_body SWITCH '{' integer '}'
|
|
{
|
|
EggGroup *group = DCAST(EggGroup, egg_stack.back());
|
|
int value = (int)$4;
|
|
group->set_switch_flag(value!=0);
|
|
}
|
|
| group_body OBJECTTYPE '{' required_string '}'
|
|
{
|
|
EggGroup *group = DCAST(EggGroup, egg_stack.back());
|
|
string type = $4;
|
|
group->set_objecttype(type);
|
|
}
|
|
| group_body MODEL '{' integer '}'
|
|
{
|
|
EggGroup *group = DCAST(EggGroup, egg_stack.back());
|
|
int value = (int)$4;
|
|
group->set_model_flag(value!=0);
|
|
}
|
|
| group_body TEXLIST '{' integer '}'
|
|
{
|
|
EggGroup *group = DCAST(EggGroup, egg_stack.back());
|
|
int value = (int)$4;
|
|
group->set_texlist_flag(value!=0);
|
|
}
|
|
| group_body transform_3d
|
|
| group_body group_vertex_ref
|
|
| group_body switchcondition
|
|
| group_body node
|
|
{
|
|
DCAST(EggGroup, egg_stack.back())->add_child(DCAST(EggNode, $2));
|
|
}
|
|
;
|
|
|
|
/*
|
|
* cs_type
|
|
*
|
|
* enter: TOS is EggGroup.
|
|
* exit: group's cs_type value is set according to parsed symbol.
|
|
*
|
|
*/
|
|
cs_type:
|
|
string
|
|
{
|
|
EggGroup *group = DCAST(EggGroup, egg_stack.back());
|
|
string strval = $1;
|
|
|
|
EggGroup::CollisionSolidType f = EggGroup::string_cs_type(strval);
|
|
if (f == EggGroup::CST_none) {
|
|
eggyywarning("Unknown collision solid type " + strval);
|
|
} else {
|
|
group->set_cs_type(f);
|
|
}
|
|
}
|
|
;
|
|
|
|
/*
|
|
* collide_flags
|
|
*
|
|
* enter: TOS is EggGroup.
|
|
* exit: group's collide flags have been set according to parsed flags.
|
|
*
|
|
*/
|
|
collide_flags:
|
|
empty
|
|
| collide_flags string
|
|
{
|
|
EggGroup *group = DCAST(EggGroup, egg_stack.back());
|
|
string strval = $2;
|
|
|
|
EggGroup::CollideFlags f = EggGroup::string_collide_flags(strval);
|
|
if (f == EggGroup::CF_none) {
|
|
eggyywarning("Unknown collision flag " + strval);
|
|
} else {
|
|
group->set_collide_flags(group->get_collide_flags() | f);
|
|
}
|
|
}
|
|
;
|
|
|
|
/*
|
|
* transform_3d
|
|
*
|
|
* enter: TOS is EggGroup.
|
|
* exit: group's transform matrix has been set.
|
|
*
|
|
*/
|
|
transform_3d:
|
|
TRANSFORM
|
|
{
|
|
matrix_3d = LMatrix4d::ident_mat();
|
|
}
|
|
'{' transform_3d_body '}'
|
|
{
|
|
DCAST(EggGroup, egg_stack.back())->set_transform(matrix_3d);
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* transform_3d_body
|
|
*
|
|
* enter: matrix_3d contains some sensible value.
|
|
* exit: matrix_3d has been transformed by the parsed transformation.
|
|
*
|
|
*/
|
|
transform_3d_body:
|
|
empty
|
|
| transform_3d_body matrix3_3d
|
|
| transform_3d_body matrix4_3d
|
|
| transform_3d_body translate_3d
|
|
| transform_3d_body rotx_3d
|
|
| transform_3d_body roty_3d
|
|
| transform_3d_body rotz_3d
|
|
| transform_3d_body rotate_3d
|
|
| transform_3d_body scale_3d
|
|
;
|
|
|
|
translate_3d: TRANSLATE '{' real real real '}'
|
|
{
|
|
matrix_3d *= LMatrix4d::translate_mat($3, $4, $5);
|
|
}
|
|
;
|
|
|
|
rotx_3d: ROTX '{' real '}'
|
|
{
|
|
matrix_3d *= LMatrix4d::rotate_mat($3, LVector3d(1.0, 0.0, 0.0));
|
|
}
|
|
;
|
|
|
|
roty_3d: ROTY '{' real '}'
|
|
{
|
|
matrix_3d *= LMatrix4d::rotate_mat($3, LVector3d(0.0, 1.0, 0.0));
|
|
}
|
|
;
|
|
|
|
rotz_3d: ROTZ '{' real '}'
|
|
{
|
|
matrix_3d *= LMatrix4d::rotate_mat($3, LVector3d(0.0, 0.0, 1.0));
|
|
}
|
|
;
|
|
|
|
rotate_3d: ROTATE '{' real real real real '}'
|
|
{
|
|
matrix_3d *= LMatrix4d::rotate_mat($3, LVector3d($4, $5, $6));
|
|
}
|
|
;
|
|
|
|
scale_3d: SCALE '{' real real real '}'
|
|
{
|
|
matrix_3d *= LMatrix4d::scale_mat($3, $4, $5);
|
|
}
|
|
;
|
|
|
|
matrix4_3d:
|
|
MATRIX4 '{' matrix4_3d_body '}'
|
|
;
|
|
|
|
matrix4_3d_body:
|
|
empty
|
|
| real real real real
|
|
real real real real
|
|
real real real real
|
|
real real real real
|
|
{
|
|
matrix_3d *= LMatrix4d($1, $2, $3, $4,
|
|
$5, $6, $7, $8,
|
|
$9, $10, $11, $12,
|
|
$13, $14, $15, $16);
|
|
}
|
|
;
|
|
|
|
matrix3_3d: MATRIX3 '{' matrix3_3d_body '}'
|
|
;
|
|
|
|
matrix3_3d_body:
|
|
empty
|
|
| real real real
|
|
real real real
|
|
real real real
|
|
{
|
|
matrix_3d *= LMatrix4d(LMatrix3d($1, $2, $3,
|
|
$4, $5, $6,
|
|
$7, $8, $9));
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* transform_2d
|
|
*
|
|
* enter: TOS is EggTexture.
|
|
* exit: texture's transform matrix has been set.
|
|
*
|
|
*/
|
|
transform_2d:
|
|
TRANSFORM
|
|
{
|
|
matrix_2d = LMatrix3d::ident_mat();
|
|
}
|
|
'{' transform_2d_body '}'
|
|
{
|
|
DCAST(EggTexture, egg_stack.back())->set_transform(matrix_2d);
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* transform_2d_body
|
|
*
|
|
* enter: matrix contains some sensible value.
|
|
* exit: matrix has been transformed by the parsed transformation.
|
|
*
|
|
*/
|
|
transform_2d_body:
|
|
empty
|
|
| transform_2d_body matrix3_2d
|
|
| transform_2d_body translate_2d
|
|
| transform_2d_body rotate_2d
|
|
| transform_2d_body scale_2d
|
|
;
|
|
|
|
translate_2d: TRANSLATE '{' real real '}'
|
|
{
|
|
matrix_2d *= LMatrix3d::translate_mat($3, $4);
|
|
}
|
|
;
|
|
|
|
rotate_2d: ROTATE '{' real '}'
|
|
{
|
|
matrix_2d *= LMatrix3d::rotate_mat($3);
|
|
}
|
|
;
|
|
|
|
scale_2d: SCALE '{' real real '}'
|
|
{
|
|
matrix_2d *= LMatrix3d::scale_mat($3, $4);
|
|
}
|
|
;
|
|
|
|
matrix3_2d: MATRIX3 '{' matrix3_2d_body '}'
|
|
;
|
|
|
|
matrix3_2d_body:
|
|
empty
|
|
| real real real
|
|
real real real
|
|
real real real
|
|
{
|
|
matrix_2d *= LMatrix3d($1, $2, $3,
|
|
$4, $5, $6,
|
|
$7, $8, $9);
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* group_vertex_ref
|
|
*
|
|
* enter: TOS is EggGroup.
|
|
* exit: group vertex list has been filled.
|
|
*
|
|
*/
|
|
group_vertex_ref:
|
|
VERTEXREF '{' integer_list group_vertex_membership REF '{' vertex_pool_name '}' '}'
|
|
{
|
|
if ($7 != (EggVertexPool *)NULL) {
|
|
EggVertexPool *pool = DCAST(EggVertexPool, $7);
|
|
EggGroup *group = DCAST(EggGroup, egg_stack.back());
|
|
PTA_double nums = $3;
|
|
double membership = $4;
|
|
|
|
for (int i = 0; i < (int)nums.size(); i++) {
|
|
int index = (int)nums[i];
|
|
EggVertex *vertex = pool->get_vertex(index);
|
|
if (vertex == NULL) {
|
|
ostringstream errmsg;
|
|
errmsg << "No vertex " << index << " in pool " << pool->get_name()
|
|
<< ends;
|
|
eggyyerror(errmsg);
|
|
} else {
|
|
group->ref_vertex(vertex, membership);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* group_vertex_membership
|
|
*
|
|
* enter:
|
|
* exit: returns membership component if specified, or 1.0.
|
|
*
|
|
*/
|
|
group_vertex_membership:
|
|
empty
|
|
{
|
|
$$ = 1.0;
|
|
}
|
|
| group_vertex_membership SCALAR required_name '{' real_or_string '}'
|
|
{
|
|
string name = $3;
|
|
double value = $<_number>5;
|
|
double result = $1;
|
|
|
|
if (cmp_nocase_uh(name, "membership") == 0) {
|
|
result = value;
|
|
} else {
|
|
eggyywarning("Unknown group vertex scalar " + name);
|
|
}
|
|
|
|
$$ = result;
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* switchcondition
|
|
*
|
|
* enter: TOS is EggGroup.
|
|
* exit: group switchcondition value has been filled.
|
|
*
|
|
*/
|
|
switchcondition:
|
|
SWITCHCONDITION '{' switchcondition_body '}'
|
|
;
|
|
|
|
|
|
/*
|
|
* switchcondition_body
|
|
*
|
|
* enter: TOS is EggGroup.
|
|
* exit: group switchcondition value has been filled.
|
|
*
|
|
*/
|
|
switchcondition_body:
|
|
DISTANCE '{' real real VERTEX '{' real real real '}' '}'
|
|
{
|
|
EggGroup *group = DCAST(EggGroup, egg_stack.back());
|
|
group->set_lod(EggSwitchConditionDistance($3, $4, LPoint3d($7, $8, $9)));
|
|
}
|
|
| DISTANCE '{' real real real VERTEX '{' real real real '}' '}'
|
|
{
|
|
EggGroup *group = DCAST(EggGroup, egg_stack.back());
|
|
group->set_lod(EggSwitchConditionDistance($3, $4, LPoint3d($8, $9, $10), $5));
|
|
}
|
|
;
|
|
|
|
|
|
|
|
/*
|
|
* polygon
|
|
*
|
|
* enter:
|
|
* exit: returns a new EggPolygon.
|
|
*
|
|
*/
|
|
polygon:
|
|
POLYGON optional_name
|
|
{
|
|
egg_stack.push_back(new EggPolygon($2));
|
|
}
|
|
'{' primitive_body '}'
|
|
{
|
|
$$ = egg_stack.back();
|
|
egg_stack.pop_back();
|
|
}
|
|
;
|
|
|
|
/*
|
|
* point_light
|
|
*
|
|
* enter:
|
|
* exit: returns a new EggPoint.
|
|
*
|
|
*/
|
|
point_light:
|
|
POINTLIGHT optional_name
|
|
{
|
|
egg_stack.push_back(new EggPoint($2));
|
|
}
|
|
'{' primitive_body '}'
|
|
{
|
|
$$ = egg_stack.back();
|
|
egg_stack.pop_back();
|
|
}
|
|
;
|
|
|
|
/*
|
|
* nurbs_surface
|
|
*
|
|
* enter:
|
|
* exit: returns a new EggNurbsSurface.
|
|
*
|
|
*/
|
|
nurbs_surface:
|
|
NURBSSURFACE optional_name
|
|
{
|
|
egg_stack.push_back(new EggNurbsSurface($2));
|
|
}
|
|
'{' nurbs_surface_body '}'
|
|
{
|
|
$$ = egg_stack.back();
|
|
egg_stack.pop_back();
|
|
}
|
|
;
|
|
|
|
/*
|
|
* nurbs_curve
|
|
*
|
|
* enter:
|
|
* exit: returns a new EggNurbsCurve.
|
|
*
|
|
*/
|
|
nurbs_curve:
|
|
NURBSCURVE optional_name
|
|
{
|
|
egg_stack.push_back(new EggNurbsCurve($2));
|
|
}
|
|
'{' nurbs_curve_body '}'
|
|
{
|
|
$$ = egg_stack.back();
|
|
egg_stack.pop_back();
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* primitive_body
|
|
*
|
|
* enter: TOS is EggPrimitive.
|
|
* exit: primitive attributes and vertices have been filled.
|
|
*
|
|
*/
|
|
primitive_body:
|
|
empty
|
|
| primitive_body TREF '{' primitive_tref_body '}'
|
|
| primitive_body TEXTURE '{' primitive_texture_body '}'
|
|
| primitive_body MREF '{' primitive_material_body '}'
|
|
| primitive_body primitive_vertex_ref
|
|
| primitive_body NORMAL '{' primitive_normal_body '}'
|
|
| primitive_body RGBA '{' primitive_color_body '}'
|
|
| primitive_body BFACE '{' primitive_bface_body '}'
|
|
| primitive_body SCALAR required_name '{' real_or_string '}'
|
|
{
|
|
EggPrimitive *primitive = DCAST(EggPrimitive, egg_stack.back());
|
|
string name = $3;
|
|
double value = $<_number>5;
|
|
string strval = $<_string>5;
|
|
|
|
if (cmp_nocase_uh(name, "alpha") == 0) {
|
|
EggAlphaMode::AlphaMode a = EggAlphaMode::string_alpha_mode(strval);
|
|
if (a == EggAlphaMode::AM_unspecified) {
|
|
eggyywarning("Unknown alpha mode " + strval);
|
|
} else {
|
|
primitive->set_alpha_mode(a);
|
|
}
|
|
} else if (cmp_nocase_uh(name, "draw_order") == 0) {
|
|
primitive->set_draw_order(value);
|
|
} else {
|
|
eggyywarning("Unknown scalar " + name);
|
|
}
|
|
}
|
|
;
|
|
|
|
/*
|
|
* nurbs_surface_body
|
|
*
|
|
* enter: TOS is EggNurbsSurface.
|
|
* exit: primitive attributes and vertices have been filled.
|
|
*
|
|
*/
|
|
nurbs_surface_body:
|
|
empty
|
|
| nurbs_surface_body TREF '{' primitive_tref_body '}'
|
|
| nurbs_surface_body TEXTURE '{' primitive_texture_body '}'
|
|
| nurbs_surface_body MREF '{' primitive_material_body '}'
|
|
| nurbs_surface_body primitive_vertex_ref
|
|
| nurbs_surface_body NORMAL '{' primitive_normal_body '}'
|
|
| nurbs_surface_body RGBA '{' primitive_color_body '}'
|
|
| nurbs_surface_body BFACE '{' primitive_bface_body '}'
|
|
| nurbs_surface_body ORDER '{' nurbs_surface_order_body '}'
|
|
| nurbs_surface_body UKNOTS '{' nurbs_surface_uknots_body '}'
|
|
| nurbs_surface_body VKNOTS '{' nurbs_surface_vknots_body '}'
|
|
| nurbs_surface_body nurbs_curve
|
|
{
|
|
EggNurbsCurve *curve = DCAST(EggNurbsCurve, $2);
|
|
EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, egg_stack.back());
|
|
nurbs->_curves_on_surface.push_back(curve);
|
|
}
|
|
| nurbs_surface_body TRIM '{' nurbs_surface_trim_body '}'
|
|
| nurbs_surface_body SCALAR required_name '{' real_or_string '}'
|
|
{
|
|
EggNurbsSurface *primitive = DCAST(EggNurbsSurface, egg_stack.back());
|
|
string name = $3;
|
|
double value = $<_number>5;
|
|
string strval = $<_string>5;
|
|
|
|
if (cmp_nocase_uh(name, "alpha") == 0) {
|
|
EggAlphaMode::AlphaMode a = EggAlphaMode::string_alpha_mode(strval);
|
|
if (a == EggAlphaMode::AM_unspecified) {
|
|
eggyywarning("Unknown alpha mode " + strval);
|
|
} else {
|
|
primitive->set_alpha_mode(a);
|
|
}
|
|
} else if (cmp_nocase_uh(name, "draw_order") == 0) {
|
|
primitive->set_draw_order(value);
|
|
} else if (cmp_nocase_uh(name, "u_subdiv") == 0) {
|
|
primitive->set_u_subdiv(value);
|
|
} else if (cmp_nocase_uh(name, "v_subdiv") == 0) {
|
|
primitive->set_v_subdiv(value);
|
|
} else {
|
|
eggyywarning("Unknown scalar " + name);
|
|
}
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* nurbs_curve_body
|
|
*
|
|
* enter: TOS is EggNurbsCurve.
|
|
* exit: primitive attributes and vertices have been filled.
|
|
*
|
|
*/
|
|
nurbs_curve_body:
|
|
empty
|
|
| nurbs_curve_body TREF '{' primitive_tref_body '}'
|
|
| nurbs_curve_body TEXTURE '{' primitive_texture_body '}'
|
|
| nurbs_curve_body MREF '{' primitive_material_body '}'
|
|
| nurbs_curve_body primitive_vertex_ref
|
|
| nurbs_curve_body NORMAL '{' primitive_normal_body '}'
|
|
| nurbs_curve_body RGBA '{' primitive_color_body '}'
|
|
| nurbs_curve_body BFACE '{' primitive_bface_body '}'
|
|
| nurbs_curve_body ORDER '{' nurbs_curve_order_body '}'
|
|
| nurbs_curve_body KNOTS '{' nurbs_curve_knots_body '}'
|
|
| nurbs_curve_body SCALAR required_name '{' real_or_string '}'
|
|
{
|
|
EggNurbsCurve *primitive = DCAST(EggNurbsCurve, egg_stack.back());
|
|
string name = $3;
|
|
double value = $<_number>5;
|
|
string strval = $<_string>5;
|
|
|
|
if (cmp_nocase_uh(name, "alpha") == 0) {
|
|
EggAlphaMode::AlphaMode a = EggAlphaMode::string_alpha_mode(strval);
|
|
if (a == EggAlphaMode::AM_unspecified) {
|
|
eggyywarning("Unknown alpha mode " + strval);
|
|
} else {
|
|
primitive->set_alpha_mode(a);
|
|
}
|
|
} else if (cmp_nocase_uh(name, "draw_order") == 0) {
|
|
primitive->set_draw_order(value);
|
|
} else if (cmp_nocase_uh(name, "subdiv") == 0) {
|
|
primitive->set_subdiv(value);
|
|
} else if (cmp_nocase_uh(name, "type") == 0) {
|
|
EggCurve::CurveType a = EggCurve::string_curve_type(strval);
|
|
if (a == EggCurve::CT_none) {
|
|
eggyywarning("Unknown curve type " + strval);
|
|
} else {
|
|
primitive->set_curve_type(a);
|
|
}
|
|
|
|
} else {
|
|
eggyywarning("Unknown scalar " + name);
|
|
}
|
|
}
|
|
;
|
|
|
|
/*
|
|
* primitive_tref_body
|
|
*
|
|
* enter: TOS is EggPrimitive.
|
|
* exit: primitive TREF value has been filled.
|
|
*
|
|
*/
|
|
primitive_tref_body:
|
|
texture_name
|
|
{
|
|
if ($1 != (EggTexture *)NULL) {
|
|
EggTexture *texture = DCAST(EggTexture, $1);
|
|
DCAST(EggPrimitive, egg_stack.back())->set_texture(texture);
|
|
}
|
|
}
|
|
;
|
|
|
|
/*
|
|
* primitive_texture_body
|
|
*
|
|
* enter: TOS is EggPrimitive.
|
|
* exit: primitive texture value has been filled.
|
|
*
|
|
*/
|
|
primitive_texture_body:
|
|
required_name
|
|
{
|
|
EggTexture *texture = NULL;
|
|
|
|
// Defining a texture on-the-fly.
|
|
Filename filename = $1;
|
|
string tref_name = filename.get_basename();
|
|
|
|
Textures::iterator vpi = textures.find(tref_name);
|
|
if (vpi == textures.end()) {
|
|
// The texture was not yet defined. Define it.
|
|
texture = new EggTexture(tref_name, filename);
|
|
textures[tref_name] = texture;
|
|
|
|
if (egg_top_node != NULL) {
|
|
egg_top_node->add_child(texture);
|
|
}
|
|
|
|
} else {
|
|
// The texture already existed. Use it.
|
|
texture = (*vpi).second;
|
|
if (filename != texture->get_filename()) {
|
|
eggyywarning(string("Using previous path: ") +
|
|
texture->get_filename().get_fullpath());
|
|
}
|
|
}
|
|
|
|
nassertr(texture != NULL, 0);
|
|
DCAST(EggPrimitive, egg_stack.back())->set_texture(texture);
|
|
}
|
|
;
|
|
|
|
/*
|
|
* primitive_material_body
|
|
*
|
|
* enter: TOS is EggPrimitive.
|
|
* exit: primitive material value has been filled.
|
|
*
|
|
*/
|
|
primitive_material_body:
|
|
material_name
|
|
{
|
|
if ($1 != (EggMaterial *)NULL) {
|
|
EggMaterial *material = DCAST(EggMaterial, $1);
|
|
DCAST(EggPrimitive, egg_stack.back())->set_material(material);
|
|
}
|
|
}
|
|
;
|
|
|
|
/*
|
|
* primitive_normal_body
|
|
*
|
|
* enter: TOS is EggPrimitive.
|
|
* exit: primitive normal value has been filled.
|
|
*
|
|
*/
|
|
primitive_normal_body:
|
|
real real real
|
|
{
|
|
DCAST(EggPrimitive, egg_stack.back())->set_normal(Normald($1, $2, $3));
|
|
}
|
|
| primitive_normal_body DNORMAL string '{' real real real '}'
|
|
{
|
|
bool inserted = DCAST(EggPrimitive, egg_stack.back())->_dnormals.
|
|
insert(EggMorphNormal($3, LVector3d($5, $6, $7))).second;
|
|
if (!inserted) {
|
|
eggyywarning("Ignoring repeated morph name " + $3);
|
|
}
|
|
}
|
|
| primitive_normal_body DNORMAL '{' string real real real '}'
|
|
{
|
|
bool inserted = DCAST(EggPrimitive, egg_stack.back())->_dnormals.
|
|
insert(EggMorphNormal($4, LVector3d($5, $6, $7))).second;
|
|
if (!inserted) {
|
|
eggyywarning("Ignoring repeated morph name " + $4);
|
|
}
|
|
}
|
|
;
|
|
|
|
/*
|
|
* primitive_color_body
|
|
*
|
|
* enter: TOS is EggPrimitive.
|
|
* exit: primitive color value has been filled.
|
|
*
|
|
*/
|
|
primitive_color_body:
|
|
real real real real
|
|
{
|
|
DCAST(EggPrimitive, egg_stack.back())->set_color(Colorf($1, $2, $3, $4));
|
|
}
|
|
| primitive_color_body DRGBA string '{' real real real real '}'
|
|
{
|
|
bool inserted = DCAST(EggPrimitive, egg_stack.back())->_drgbas.
|
|
insert(EggMorphColor($3, LVector4f($5, $6, $7, $8))).second;
|
|
if (!inserted) {
|
|
eggyywarning("Ignoring repeated morph name " + $3);
|
|
}
|
|
}
|
|
| primitive_color_body DRGBA '{' string real real real real '}'
|
|
{
|
|
bool inserted = DCAST(EggPrimitive, egg_stack.back())->_drgbas.
|
|
insert(EggMorphColor($4, LVector4f($5, $6, $7, $8))).second;
|
|
if (!inserted) {
|
|
eggyywarning("Ignoring repeated morph name " + $4);
|
|
}
|
|
}
|
|
;
|
|
|
|
/*
|
|
* primitive_bface_body
|
|
*
|
|
* enter: TOS is EggPrimitive.
|
|
* exit: primitive BFace value has been filled.
|
|
*
|
|
*/
|
|
primitive_bface_body:
|
|
integer
|
|
{
|
|
EggPrimitive *primitive = DCAST(EggPrimitive, egg_stack.back());
|
|
int value = (int)$1;
|
|
primitive->set_bface_flag(value!=0);
|
|
}
|
|
;
|
|
|
|
/*
|
|
* primitive_vertex_ref
|
|
*
|
|
* enter: TOS is EggPrimitive.
|
|
* exit: primitive vertex list has been filled.
|
|
*
|
|
*/
|
|
primitive_vertex_ref:
|
|
VERTEXREF '{' integer_list REF '{' vertex_pool_name '}' '}'
|
|
{
|
|
if ($6 != (EggVertexPool *)NULL) {
|
|
EggVertexPool *pool = DCAST(EggVertexPool, $6);
|
|
EggPrimitive *prim = DCAST(EggPrimitive, egg_stack.back());
|
|
PTA_double nums = $3;
|
|
|
|
for (int i = 0; i < (int)nums.size(); i++) {
|
|
int index = (int)nums[i];
|
|
EggVertex *vertex = pool->get_vertex(index);
|
|
if (vertex == NULL) {
|
|
ostringstream errmsg;
|
|
errmsg << "No vertex " << index << " in pool " << pool->get_name()
|
|
<< ends;
|
|
eggyyerror(errmsg);
|
|
} else {
|
|
prim->add_vertex(vertex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
;
|
|
|
|
/*
|
|
* nurbs_surface_order_body
|
|
*
|
|
* enter: TOS is EggNurbsSurface.
|
|
* exit: U-order and V-order have been set.
|
|
*
|
|
*/
|
|
nurbs_surface_order_body:
|
|
integer integer
|
|
{
|
|
EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, egg_stack.back());
|
|
int u_order = (int)$1;
|
|
int v_order = (int)$2;
|
|
nurbs->set_u_order(u_order);
|
|
nurbs->set_v_order(v_order);
|
|
}
|
|
;
|
|
|
|
/*
|
|
* nurbs_surface_uknots_body
|
|
*
|
|
* enter: TOS is EggNurbsSurface.
|
|
* exit: U-Knots list has been set.
|
|
*
|
|
*/
|
|
nurbs_surface_uknots_body:
|
|
real_list
|
|
{
|
|
EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, egg_stack.back());
|
|
PTA_double nums = $1;
|
|
|
|
nurbs->set_num_u_knots(nums.size());
|
|
for (int i = 0; i < (int)nums.size(); i++) {
|
|
nurbs->set_u_knot(i, nums[i]);
|
|
}
|
|
}
|
|
;
|
|
|
|
/*
|
|
* nurbs_surface_vknots_body
|
|
*
|
|
* enter: TOS is EggNurbsSurface.
|
|
* exit: V-Knots list has been set.
|
|
*
|
|
*/
|
|
nurbs_surface_vknots_body:
|
|
real_list
|
|
{
|
|
EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, egg_stack.back());
|
|
PTA_double nums = $1;
|
|
|
|
nurbs->set_num_v_knots(nums.size());
|
|
for (int i = 0; i < (int)nums.size(); i++) {
|
|
nurbs->set_v_knot(i, nums[i]);
|
|
}
|
|
}
|
|
;
|
|
|
|
/*
|
|
* nurbs_surface_trim_body
|
|
*
|
|
* enter: TOS is EggNurbsSurface.
|
|
* exit: A trim curve sequence has been defined.
|
|
*
|
|
*/
|
|
nurbs_surface_trim_body:
|
|
empty
|
|
{
|
|
EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, egg_stack.back());
|
|
nurbs->_trims.push_back(EggNurbsSurface::Trim());
|
|
}
|
|
| nurbs_surface_trim_body LOOP '{' nurbs_surface_trim_loop_body '}'
|
|
;
|
|
|
|
/*
|
|
* nurbs_surface_trim_loop_body
|
|
*
|
|
* enter: TOS is EggNurbsSurface, with at least one trim curve started.
|
|
* exit: A trim curve loop has been defined.
|
|
*
|
|
*/
|
|
nurbs_surface_trim_loop_body:
|
|
empty
|
|
{
|
|
EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, egg_stack.back());
|
|
nassertr(!nurbs->_trims.empty(), 0);
|
|
nurbs->_trims.back().push_back(EggNurbsSurface::Loop());
|
|
}
|
|
| nurbs_surface_trim_loop_body nurbs_curve
|
|
{
|
|
EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, egg_stack.back());
|
|
nassertr(!nurbs->_trims.empty(), 0);
|
|
nassertr(!nurbs->_trims.back().empty(), 0);
|
|
EggNurbsCurve *curve = DCAST(EggNurbsCurve, $2);
|
|
nurbs->_trims.back().back().push_back(curve);
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* nurbs_curve_order_body
|
|
*
|
|
* enter: TOS is EggNurbsCurve.
|
|
* exit: U-order and V-order have been set.
|
|
*
|
|
*/
|
|
nurbs_curve_order_body:
|
|
integer
|
|
{
|
|
EggNurbsCurve *nurbs = DCAST(EggNurbsCurve, egg_stack.back());
|
|
int order = (int)$1;
|
|
nurbs->set_order(order);
|
|
}
|
|
;
|
|
|
|
/*
|
|
* nurbs_curve_knots_body
|
|
*
|
|
* enter: TOS is EggNurbsCurve.
|
|
* exit: U-Knots list has been set.
|
|
*
|
|
*/
|
|
nurbs_curve_knots_body:
|
|
real_list
|
|
{
|
|
EggNurbsCurve *nurbs = DCAST(EggNurbsCurve, egg_stack.back());
|
|
PTA_double nums = $1;
|
|
|
|
nurbs->set_num_knots(nums.size());
|
|
for (int i = 0; i < (int)nums.size(); i++) {
|
|
nurbs->set_knot(i, nums[i]);
|
|
}
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* table
|
|
*
|
|
* enter:
|
|
* exit: returns a new EggTable, corresponding to a <Table> entry.
|
|
*
|
|
*/
|
|
table:
|
|
TABLE optional_name
|
|
{
|
|
EggTable *table = new EggTable($2);
|
|
table->set_table_type(EggTable::TT_table);
|
|
egg_stack.push_back(table);
|
|
}
|
|
'{' table_body '}'
|
|
{
|
|
$$ = egg_stack.back();
|
|
egg_stack.pop_back();
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* bundle
|
|
*
|
|
* enter:
|
|
* exit: returns a new EggTable, corresponding to a <Bundle> entry.
|
|
*
|
|
*/
|
|
bundle:
|
|
BUNDLE optional_name
|
|
{
|
|
EggTable *table = new EggTable($2);
|
|
table->set_table_type(EggTable::TT_bundle);
|
|
egg_stack.push_back(table);
|
|
}
|
|
'{' table_body '}'
|
|
{
|
|
$$ = egg_stack.back();
|
|
egg_stack.pop_back();
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* table_body
|
|
*
|
|
* enter: TOS is EggTable.
|
|
* exit: table contents have been filled, including children.
|
|
*
|
|
*/
|
|
table_body:
|
|
empty
|
|
| table_body table
|
|
{
|
|
DCAST(EggTable, egg_stack.back())->add_child(DCAST(EggNode, $2));
|
|
}
|
|
| table_body bundle
|
|
{
|
|
DCAST(EggTable, egg_stack.back())->add_child(DCAST(EggNode, $2));
|
|
}
|
|
| table_body sanim
|
|
{
|
|
DCAST(EggTable, egg_stack.back())->add_child(DCAST(EggNode, $2));
|
|
}
|
|
| table_body xfmanim
|
|
{
|
|
DCAST(EggTable, egg_stack.back())->add_child(DCAST(EggNode, $2));
|
|
}
|
|
| table_body xfm_s_anim
|
|
{
|
|
DCAST(EggTable, egg_stack.back())->add_child(DCAST(EggNode, $2));
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* sanim
|
|
*
|
|
* enter:
|
|
* exit: returns a new EggSAnimData.
|
|
*
|
|
*/
|
|
sanim:
|
|
SANIM optional_name
|
|
{
|
|
EggSAnimData *anim_data = new EggSAnimData($2);
|
|
egg_stack.push_back(anim_data);
|
|
}
|
|
'{' sanim_body '}'
|
|
{
|
|
$$ = egg_stack.back();
|
|
egg_stack.pop_back();
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* sanim_body
|
|
*
|
|
* enter: TOS is EggSAnimData.
|
|
* exit: sanim contents have been filled.
|
|
*
|
|
*/
|
|
sanim_body:
|
|
empty
|
|
| sanim_body SCALAR required_name '{' real_or_string '}'
|
|
{
|
|
EggSAnimData *anim_data = DCAST(EggSAnimData, egg_stack.back());
|
|
string name = $3;
|
|
double value = $<_number>5;
|
|
|
|
if (cmp_nocase_uh(name, "fps") == 0) {
|
|
anim_data->set_fps(value);
|
|
} else {
|
|
eggyywarning("Unsupported S$Anim scalar: " + name);
|
|
}
|
|
}
|
|
| sanim_body TABLE_V '{' real_list '}'
|
|
{
|
|
DCAST(EggSAnimData, egg_stack.back())->set_data($4);
|
|
}
|
|
;
|
|
|
|
/*
|
|
* xfmanim
|
|
*
|
|
* enter:
|
|
* exit: returns a new EggXfmAnimData.
|
|
*
|
|
*/
|
|
xfmanim:
|
|
XFMANIM optional_name
|
|
{
|
|
EggXfmAnimData *anim_data = new EggXfmAnimData($2);
|
|
egg_stack.push_back(anim_data);
|
|
}
|
|
'{' xfmanim_body '}'
|
|
{
|
|
$$ = egg_stack.back();
|
|
egg_stack.pop_back();
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* xfmanim_body
|
|
*
|
|
* enter: TOS is EggXfmAnimData.
|
|
* exit: xfmanim contents have been filled, including children.
|
|
*
|
|
*/
|
|
xfmanim_body:
|
|
empty
|
|
| xfmanim_body SCALAR required_name '{' real_or_string '}'
|
|
{
|
|
EggXfmAnimData *anim_data = DCAST(EggXfmAnimData, egg_stack.back());
|
|
string name = $3;
|
|
double value = $<_number>5;
|
|
string strval = $<_string>5;
|
|
|
|
if (cmp_nocase_uh(name, "fps") == 0) {
|
|
anim_data->set_fps(value);
|
|
} else if (cmp_nocase_uh(name, "order") == 0) {
|
|
anim_data->set_order(strval);
|
|
} else if (cmp_nocase_uh(name, "contents") == 0) {
|
|
anim_data->set_contents(strval);
|
|
} else {
|
|
eggyywarning("Unsupported Xfm$Anim scalar: " + name);
|
|
}
|
|
}
|
|
| xfmanim_body TABLE_V '{' real_list '}'
|
|
{
|
|
DCAST(EggXfmAnimData, egg_stack.back())->set_data($4);
|
|
}
|
|
;
|
|
|
|
/*
|
|
* xfm_s_anim
|
|
*
|
|
* enter:
|
|
* exit: returns a new EggXfmSAnim.
|
|
*
|
|
*/
|
|
xfm_s_anim:
|
|
XFMSANIM optional_name
|
|
{
|
|
EggXfmSAnim *anim_group = new EggXfmSAnim($2);
|
|
egg_stack.push_back(anim_group);
|
|
}
|
|
'{' xfm_s_anim_body '}'
|
|
{
|
|
$$ = egg_stack.back();
|
|
egg_stack.pop_back();
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* xfm_s_anim_body
|
|
*
|
|
* enter: TOS is EggXfmSAnim.
|
|
* exit: xfm_s_anim contents have been filled, including children.
|
|
*
|
|
*/
|
|
xfm_s_anim_body:
|
|
empty
|
|
| xfm_s_anim_body SCALAR required_name '{' real_or_string '}'
|
|
{
|
|
EggXfmSAnim *anim_group = DCAST(EggXfmSAnim, egg_stack.back());
|
|
string name = $3;
|
|
double value = $<_number>5;
|
|
string strval = $<_string>5;
|
|
|
|
if (cmp_nocase_uh(name, "fps") == 0) {
|
|
anim_group->set_fps(value);
|
|
} else if (cmp_nocase_uh(name, "order") == 0) {
|
|
anim_group->set_order(strval);
|
|
} else {
|
|
eggyywarning("Unsupported Xfm$Anim_S$ scalar: " + name);
|
|
}
|
|
}
|
|
| xfm_s_anim_body sanim
|
|
{
|
|
DCAST(EggXfmSAnim, egg_stack.back())->add_child(DCAST(EggNode, $2));
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* integer_list
|
|
*
|
|
* enter:
|
|
* exit: returns a list of parsed integers.
|
|
*
|
|
*/
|
|
integer_list:
|
|
empty
|
|
{
|
|
$$ = PTA_double(0);
|
|
}
|
|
| integer_list integer
|
|
{
|
|
$$.push_back((double)$2);
|
|
}
|
|
;
|
|
|
|
/*
|
|
* real_list
|
|
*
|
|
* enter:
|
|
* exit: returns a list of parsed reals.
|
|
*
|
|
*/
|
|
real_list:
|
|
empty
|
|
{
|
|
$$ = PTA_double(0);
|
|
}
|
|
| real_list real
|
|
{
|
|
$$.push_back($2);
|
|
}
|
|
;
|
|
|
|
/*
|
|
* texture_name
|
|
*
|
|
* enter:
|
|
* exit: Returns an EggTexture pointer, or NULL.
|
|
*
|
|
*/
|
|
texture_name:
|
|
required_name
|
|
{
|
|
string name = $1;
|
|
Textures::iterator vpi = textures.find(name);
|
|
if (vpi == textures.end()) {
|
|
eggyyerror("Unknown texture " + name);
|
|
$$ = PT(EggObject)();
|
|
} else {
|
|
$$ = (*vpi).second;
|
|
}
|
|
}
|
|
;
|
|
|
|
/*
|
|
* material_name
|
|
*
|
|
* enter:
|
|
* exit: Returns an EggMaterial pointer, or NULL.
|
|
*
|
|
*/
|
|
material_name:
|
|
required_name
|
|
{
|
|
string name = $1;
|
|
Materials::iterator vpi = materials.find(name);
|
|
if (vpi == materials.end()) {
|
|
eggyyerror("Unknown material " + name);
|
|
$$ = PT(EggObject)();
|
|
} else {
|
|
$$ = (*vpi).second;
|
|
}
|
|
}
|
|
;
|
|
|
|
/*
|
|
* vertex_pool_name
|
|
*
|
|
* enter:
|
|
* exit: Returns an EggVertexPool pointer, or NULL.
|
|
*
|
|
*/
|
|
vertex_pool_name:
|
|
required_name
|
|
{
|
|
string name = $1;
|
|
VertexPools::iterator vpi = vertex_pools.find(name);
|
|
if (vpi == vertex_pools.end()) {
|
|
eggyyerror("Unknown vertex pool " + name);
|
|
$$ = PT(EggObject)();
|
|
} else {
|
|
$$ = (*vpi).second;
|
|
}
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* required_name
|
|
*
|
|
* enter:
|
|
* exit: Returns a nonempty string as the name of an EggObject.
|
|
*
|
|
*/
|
|
required_name:
|
|
empty
|
|
{
|
|
eggyyerror("Name required.");
|
|
$$ = "";
|
|
}
|
|
| string
|
|
;
|
|
|
|
|
|
/*
|
|
* optional_name
|
|
*
|
|
* enter:
|
|
* exit: Returns a possibly-empty string as the name of an EggObject.
|
|
*
|
|
*/
|
|
optional_name:
|
|
optional_string
|
|
;
|
|
|
|
|
|
/*
|
|
* required_string
|
|
*
|
|
* enter:
|
|
* exit: Returns a nonempty string.
|
|
*
|
|
*/
|
|
required_string:
|
|
empty
|
|
{
|
|
eggyyerror("String required.");
|
|
$$ = "";
|
|
}
|
|
| string
|
|
;
|
|
|
|
/*
|
|
* optional_string
|
|
*
|
|
* enter:
|
|
* exit: Returns a possibly-empty string.
|
|
*
|
|
*/
|
|
optional_string:
|
|
empty
|
|
{
|
|
$$ = "";
|
|
}
|
|
| string
|
|
;
|
|
|
|
/*
|
|
* string
|
|
*
|
|
* enter:
|
|
* exit: Returns a nonempty string. This is different from required_string
|
|
* in that the grammar requires it to be nonempty, so that: (a)
|
|
* error messages are more obtuse, and (b) the grammar is less
|
|
* ambiguous. Use it whenever required_string does not work.
|
|
*
|
|
*/
|
|
string:
|
|
NUMBER
|
|
{
|
|
$$ = $<_string>1;
|
|
}
|
|
| STRING
|
|
;
|
|
|
|
/*
|
|
* repeated_string
|
|
*
|
|
* enter:
|
|
* exit: Returns a possibly-empty string, which might consist of a number
|
|
* of strings or numbers in a row, concatenated together with an
|
|
* implicit newline between.
|
|
*
|
|
*/
|
|
repeated_string:
|
|
empty
|
|
{
|
|
$$ = "";
|
|
}
|
|
| repeated_string_body
|
|
{
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
/*
|
|
* repeated_string_body
|
|
*
|
|
* enter:
|
|
* exit: Returns a nonempty string, which might consist of a number
|
|
* of strings or numbers in a row, concatenated together with an
|
|
* implicit newline between.
|
|
*
|
|
*/
|
|
repeated_string_body:
|
|
string
|
|
{
|
|
$$ = $1;
|
|
}
|
|
| repeated_string_body string
|
|
{
|
|
$$ = $1 + "\n" + $2;
|
|
}
|
|
;
|
|
|
|
/*
|
|
* real
|
|
*
|
|
* enter:
|
|
* exit: Returns an integer or floating-pointer number.
|
|
*
|
|
*/
|
|
real:
|
|
NUMBER
|
|
;
|
|
|
|
/*
|
|
* real_or_string
|
|
*
|
|
* enter:
|
|
* exit: Returns a number as ($<_number>1) or a string (as $<_string>1).
|
|
*
|
|
*/
|
|
real_or_string:
|
|
NUMBER
|
|
{
|
|
$<_number>$ = $1;
|
|
$<_string>$ = $<_string>1;
|
|
}
|
|
| STRING
|
|
{
|
|
$<_number>$ = 0.0;
|
|
$<_string>$ = $1;
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* integer
|
|
*
|
|
* enter:
|
|
* exit: Returns an integer number (stored in a double value).
|
|
*
|
|
*/
|
|
integer:
|
|
NUMBER
|
|
{
|
|
int i = (int)$1;
|
|
if ((double)i != $1) {
|
|
eggyywarning("Integer expected.");
|
|
$$ = (double)i;
|
|
}
|
|
}
|
|
;
|
|
|
|
empty:
|
|
;
|