// Filename: parser.y // Created by: drose (16Jan99) // //////////////////////////////////////////////////////////////////// %{ #include #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 #include #include #include #include #include // 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 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 VertexPools; static VertexPools vertex_pools; // And another one mapping texture names to textures. typedef map Textures; static Textures textures; // And again for material names to materials. typedef map 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 // 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 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 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: ;