Parser.cc

Go to the documentation of this file.
00001 
00005 #include "Parser.h"
00006 #include "PinholeCamera.h"
00007 #include "ConstantBackground.h"
00008 #include "PointLight.h"
00009 #include "LambertianMaterial.h"
00010 #include "Group.h"
00011 #include "Plane.h"
00012 #include "Sphere.h"
00013 #include "Scene.h"
00014 #include "Cyl.h"
00015 #include "Triangle.h"
00016 #include "Box.h"
00017 #include "Ring.h"
00018 #include "Image.h"
00019 #include <cmath>
00020 #include <iostream>
00021 #include <istream>
00022 #include <sstream>
00023 #include <string>
00024 #include <cstdlib>
00025 using namespace std;
00026 
00027 void Parser::throwParseException(std::string const &message ) const
00028 {
00029     cerr << next_token.line_number << ":" << next_token.column_number << ": " << message << endl;
00030     exit( 1 );
00031 }
00032 
00033 void Parser::readNextToken()
00034 {
00035     int state = 0;
00036     long long mantissa = 0;
00037     int exponent = 0;
00038     int exponent_adjustment = 0;
00039     bool negative_mantissa = false;
00040     bool negative_exponent = false;
00041     for ( ; ; ) {
00042         int character = input.get();
00043         switch ( state ) {
00044         case 0:
00045             next_token.line_number = line_number;
00046             next_token.column_number = column_number;
00047             if ( input.eof() ) {
00048                 next_token.token_type = Token::end_of_file;
00049                 return;
00050             } else if ( character == ' ' || character == '\t' ||
00051                         character == '\r' || character == '\n' )
00052                 state = 0;
00053             else if ( character == '/' )
00054                 state = 1;
00055             else if ( character == '+' || character == '-' ) {
00056                 negative_mantissa = character == '-';
00057                 state = 3;
00058             } else if ( character >= '0' && character <= '9' ) {
00059                 mantissa = character - '0';
00060                 state = 4;
00061             } else if ( character == '.' )
00062                 state = 5;
00063             else if ( character == '"' ) {
00064                 next_token.string_value.clear();
00065                 state = 10;
00066             } else if ( character >= 'A' && character <= 'Z' ||
00067                         character >= 'a' && character <= 'z' ||
00068                         character == '_' ) {
00069                 next_token.string_value = static_cast< char >( character );
00070                 state = 12;
00071             } else if ( character == ',' ) {
00072                 ++column_number;
00073                 next_token.token_type = Token::comma;
00074                 return;
00075             } else if ( character == '{' ) {
00076                 ++column_number;
00077                 next_token.token_type = Token::left_brace;
00078                 return;
00079             } else if ( character == '}' ) {
00080                 ++column_number;
00081                 next_token.token_type = Token::right_brace;
00082                 return;
00083             } else if ( character == '[' ) {
00084                 ++column_number;
00085                 next_token.token_type = Token::left_bracket;
00086                 return;
00087             } else if ( character == ']' ) {
00088                 ++column_number;
00089                 next_token.token_type = Token::right_bracket;
00090                 return;
00091             } else
00092                 throwParseException( "Unexpected character" );
00093             break;
00094         case 1:
00095             if ( character == '/' )
00096                 state = 2;
00097             else
00098                 throwParseException( "Malformed comment" );
00099             break;
00100         case 2:
00101             if ( character == '\n' || input.eof() )
00102                 state = 0;
00103             break;
00104         case 3:
00105             if ( character >= '0' && character <= '9' ) {
00106                 mantissa = character - '0';
00107                 state = 4;
00108             } else if ( character == '.' )
00109                 state = 5;
00110             else
00111                 throwParseException( "Invalid number" );
00112             break;
00113         case 4:
00114             if ( character >= '0' && character <= '9' )
00115                 mantissa = mantissa * 10 + character - '0';
00116             else if ( character == '.' )
00117                 state = 6;
00118             else if ( character == 'E' || character == 'e' )
00119                 state = 7;
00120             else {
00121                 input.putback( static_cast< char >( character ) );
00122                 next_token.integer_value = ( static_cast< int >( mantissa ) *
00123                                              ( negative_mantissa ? -1 : 1 ) );
00124                 next_token.token_type = Token::integer;
00125                 return;
00126             }
00127             break;
00128         case 5:
00129             if ( character >= '0' && character <= '9' ) {
00130                 mantissa = character - '0';
00131                 exponent_adjustment = 1;
00132                 state = 6;
00133             } else
00134                 throwParseException( "Invalid number" );
00135             break;
00136         case 6:
00137             if ( character >= '0' && character <= '9' ) {
00138                 mantissa = mantissa * 10 + character - '0';
00139                 ++exponent_adjustment;
00140             } else if ( character == 'E' || character == 'e' )
00141                 state = 7;
00142             else {
00143                 input.putback( static_cast< char >( character ) );
00144                 next_token.real_value = ( mantissa * ( negative_mantissa ? -1 : 1 ) *
00145                                           pow( 10.0, -exponent_adjustment ) );
00146                 next_token.token_type = Token::real;
00147                 return;
00148             }
00149             break;
00150         case 7:
00151             if ( character == '+' || character == '-' ) {
00152                 negative_exponent = character == '-';
00153                 state = 8;
00154             } else if ( character >= '0' && character <= '9' ) {
00155                 exponent = character - '0';
00156                 state = 9;
00157             } else
00158                 throwParseException( "Invalid number" );
00159             break;
00160         case 8:
00161             if ( character >= '0' && character <= '9' ) {
00162                 exponent = character - '0';
00163                 state = 9;
00164             } else
00165                 throwParseException( "Invalid number" );
00166             break;
00167         case 9:
00168             if ( character >= '0' && character <= '9' )
00169                 exponent = exponent * 10 + character - '0';
00170             else {
00171                 input.putback( static_cast< char >( character ) );
00172                 next_token.real_value = ( mantissa * ( negative_mantissa ? -1 : 1 ) *
00173                                           pow( 10.0, ( exponent * ( negative_exponent ? -1 : 1 ) -
00174                                                        exponent_adjustment ) ) );
00175                 next_token.token_type = Token::real;
00176                 return;
00177             }
00178             break;
00179         case 10:
00180             if ( input.eof() || character == '\n' )
00181                 throwParseException( "Unterminated string" );
00182             else if ( character == '\\' )
00183                 state = 11;
00184             else if ( character == '"' ) {
00185                 ++column_number;
00186                 next_token.token_type = Token::string;
00187                 return;
00188             } else
00189                 next_token.string_value.push_back( static_cast< char >( character ) );
00190             break;
00191         case 11:
00192             if ( input.eof() )
00193                 throwParseException( "Unterminated string" );
00194             else if ( character == '\n' )
00195                 next_token.string_value.push_back( '\n' );
00196             else if ( character == '\\' )
00197                 next_token.string_value.push_back( '\\' );
00198             else if ( character == '"' )
00199                 next_token.string_value.push_back( '"' );
00200             else
00201                 next_token.string_value.push_back( static_cast< char >( character ) );
00202             state = 10;
00203             break;
00204         case 12:
00205             if ( character >= '0' && character <= '9' ||
00206                  character >= 'A' && character <= 'Z' ||
00207                  character >= 'a' && character <= 'z' ||
00208                  character == '_' )
00209                 next_token.string_value.push_back( static_cast< char >( character ) );
00210             else {
00211                 input.putback( static_cast< char >( character ) );
00212                 next_token.token_type = Token::string;
00213                 return;
00214             }
00215             break;
00216         }
00217         if ( character == '\n' )
00218             {
00219                 ++line_number;
00220                 column_number = 0;
00221             }
00222         else if ( character == '\t' )
00223             column_number = ( column_number + 8 ) / 8 * 8;
00224         else
00225             ++column_number;
00226     }
00227 }
00228 
00229 bool Parser::peek(Parser::Token::type const type )
00230 {
00231     bool matched = next_token.token_type == type;
00232     if ( matched )
00233         readNextToken();
00234     return matched;
00235 }
00236 
00237 bool Parser::peek(std::string const &keyword )
00238 {
00239     bool matched = ( next_token.token_type == Token::string &&
00240                      next_token.string_value == keyword );
00241     if ( matched )
00242         readNextToken();
00243     return matched;
00244 }
00245 
00246 Parser::Token Parser::match(
00247                             Parser::Token::type const type,
00248                             std::string const &failure_message )
00249 {
00250     if ( next_token.token_type != type )
00251         throwParseException( failure_message );
00252     Token current_token( next_token );
00253     readNextToken();
00254     return current_token;
00255 }
00256 
00257 Parser::Token Parser::match(
00258                             std::string const &keyword,
00259                             std::string const &failure_message )
00260 {
00261     if ( next_token.token_type != Token::string ||
00262          next_token.string_value != keyword )
00263         throwParseException( failure_message );
00264     Token current_token( next_token );
00265     readNextToken();
00266     return current_token;
00267 }
00268 
00269 string Parser::parseString()
00270 {
00271     Token next( match( Token::string, "Expected a string" ) );
00272     return next.string_value;
00273 }
00274 
00275 bool Parser::parseBoolean()
00276 {
00277     if ( peek( "true" ) )
00278         return true;
00279     else if ( peek( "false" ) )
00280         return false;
00281     else
00282         throwParseException( "Expected `true' or `false'." );
00283     return false;
00284 }
00285 
00286 int Parser::parseInteger()
00287 {
00288     Token next( match( Token::integer, "Expected an integer" ) );
00289     return next.integer_value;
00290 }
00291 
00292 double Parser::parseReal()
00293 {
00294     if ( next_token.token_type == Token::integer ) {
00295         Token next( match( Token::integer, "Expected an integer or real" ) );
00296         return static_cast< double >( next.integer_value );
00297     }
00298     Token next( match( Token::real, "Expected an integer or real" ) );
00299     return next.real_value;
00300 }
00301 
00302 Vector const Parser::parseVector()
00303 {
00304     match( Token::left_bracket, "Expected a left bracket" );
00305     double x = parseReal();
00306     match( Token::comma, "Expected a comma" );
00307     double y = parseReal();
00308     match( Token::comma, "Expected a comma" );
00309     double z = parseReal();
00310     match( Token::right_bracket, "Expected a right bracket" );
00311     return Vector( x, y, z );
00312 }
00313 
00314 Point const Parser::parsePoint()
00315 {
00316     match( Token::left_bracket, "Expected a left bracket" );
00317     double x = parseReal();
00318     match( Token::comma, "Expected a comma" );
00319     double y = parseReal();
00320     match( Token::comma, "Expected a comma" );
00321     double z = parseReal();
00322     match( Token::right_bracket, "Expected a right bracket" );
00323     return Point( x, y, z );
00324 }
00325 
00326 Color const Parser::parseColor()
00327 {
00328     if ( peek( Token::left_bracket ) ) {
00329         double r = parseReal();
00330         match( Token::comma, "Expected a comma" );
00331         double g = parseReal();
00332         match( Token::comma, "Expected a comma" );
00333         double b = parseReal();
00334         match( Token::right_bracket, "Expected a right bracket" );
00335         return Color( r, g, b );
00336     }
00337     double v = parseReal();
00338     return Color( v, v, v );
00339 }
00340 
00341 Camera *Parser::parsePinholeCamera()
00342 {
00343     Point eye( 0.0, 0.0, 0.0 );
00344     Point lookat( 0.0, 1.0, 0.0 );
00345     Vector up( 0.0, 0.0, 1.0 );
00346     double hfov = 90.0;
00347     if ( peek( Token::left_brace ) )
00348         for ( ; ; )
00349             {
00350                 if ( peek( "eye" ) )
00351                     eye = parsePoint();
00352                 else if ( peek( "lookat" ) )
00353                     lookat = parsePoint();
00354                 else if ( peek( "up" ) )
00355                     up = parseVector();
00356                 else if ( peek( "hfov" ) )
00357                     hfov = parseReal();
00358                 else if ( peek( Token::right_brace ) )
00359                     break;
00360                 else
00361                     throwParseException( "Expected `eye', `lookat', `up', `hfov' or }." );
00362             }
00363     return new PinholeCamera( eye, lookat, up, hfov );
00364 }
00365 
00366 Camera *Parser::parseCamera()
00367 {
00368     if ( peek( "pinhole" ) )
00369         return parsePinholeCamera();
00370     throwParseException( "Expected a camera type." );
00371     return 0;
00372 }
00373 
00374 Background *Parser::parseConstantBackground()
00375 {
00376     Color color( 0.0, 0.0, 0.0 );
00377     if ( peek( Token::left_brace ) )
00378         for ( ; ; )
00379             {
00380                 if ( peek( "color" ) )
00381                     color = parseColor();
00382                 else if ( peek( Token::right_brace ) )
00383                     break;
00384                 else
00385                     throwParseException( "Expected `color' or }." );
00386             }
00387     return new ConstantBackground( color );
00388 }
00389 
00390 Background *Parser::parseBackground()
00391 {
00392     if ( peek( "constant" ) )
00393         return parseConstantBackground();
00394     throwParseException( "Expected a background type." );
00395     return 0;
00396 }
00397 
00398 Light *Parser::parsePointLight()
00399 {
00400     Point position( 0.0, 0.0, 10.0 );
00401     Color color( 1.0, 1.0, 1.0 );
00402     if ( peek( Token::left_brace ) )
00403         for ( ; ; )
00404             {
00405                 if ( peek( "position" ) )
00406                     position = parsePoint();
00407                 else if ( peek( "color" ) )
00408                     color = parseColor();
00409                 else if ( peek( Token::right_brace ) )
00410                     break;
00411                 else
00412                     throwParseException( "Expected `position', `color' or }." );
00413             }
00414     return new PointLight( position, color );
00415 }
00416 
00417 Light *Parser::parseLight()
00418 {
00419     if ( peek( "point" ) )
00420         return parsePointLight();
00421     throwParseException( "Expected a light type." );
00422     return 0;
00423 }
00424 
00425 Material *Parser::parseLambertianMaterial()
00426 {
00427     Color color( 1.0, 1.0, 1.0 );
00428     double Kd = 0.6;
00429     double Ka = 0.3;
00430     if ( peek( Token::left_brace ) )
00431         for ( ; ; )
00432             {
00433                 if ( peek( "color" ) )
00434                     color = parseColor();
00435                 else if ( peek( "Kd" ) )
00436                     Kd = parseReal();
00437                 else if ( peek( "Ka" ) )
00438                     Ka = parseReal();
00439                 else if ( peek( Token::right_brace ) )
00440                     break;
00441                 else
00442                     throwParseException( "Expected `color', `Kd', `Ka' or }." );
00443             }
00444     return new LambertianMaterial( color, Kd, Ka );
00445 }
00446 
00447 Material *Parser::parseMaterial()
00448 {
00449     if ( peek( "lambertian" ) )
00450         return parseLambertianMaterial();
00451     else if ( next_token.token_type == Token::string )
00452         {
00453             map< string, Material * >::iterator found = defined_materials.find( parseString() );
00454             if ( found != defined_materials.end() )
00455                 return ( *found ).second;
00456         }
00457     throwParseException( "Expected an material type." );
00458     return 0;
00459 }
00460 
00461 Object *Parser::parseGroupObject()
00462 {
00463     Group *group = new Group();
00464     match( Token::left_brace, "Expected a left brace" );
00465     while ( !peek( Token::right_brace ) )
00466         group->addObject( parseObject() );
00467     return group;
00468 }
00469 
00470 Object *Parser::parsePlaneObject()
00471 {
00472     Material *material = default_material;
00473     Vector normal( 0.0, 0.0, 1.0 );
00474     Point point( 0.0, 0.0, 0.0 );
00475     if ( peek( Token::left_brace ) )
00476         for ( ; ; )
00477             {
00478                 if ( peek( "material" ) )
00479                     material = parseMaterial();
00480                 else if ( peek( "normal" ) )
00481                     normal = parseVector();
00482                 else if ( peek( "point" ) )
00483                     point = parsePoint();
00484                 else if ( peek( Token::right_brace ) )
00485                     break;
00486                 else
00487                     throwParseException( "Expected `material', `point', `normal' or }." );
00488             }
00489     return new Plane( material, normal, point );
00490 }
00491 
00492 Object *Parser::parseSphereObject()
00493 {
00494     Material *material = default_material;
00495     Point center( 0.0, 0.0, 0.0 );
00496     double radius = 0.5;
00497     if ( peek( Token::left_brace ) )
00498         for ( ; ; )
00499             {
00500                 if ( peek( "material" ) )
00501                     material = parseMaterial();
00502                 else if ( peek( "center" ) )
00503                     center = parsePoint();
00504                 else if ( peek( "radius" ) )
00505                     radius = parseReal();
00506                 else if ( peek( Token::right_brace ) )
00507                     break;
00508                 else
00509                     throwParseException( "Expected `material', `center', `radius' or }." );
00510             }
00511     return new Sphere( material, center, radius );
00512 }
00513 
00514 Object *Parser::parseBoxObject()
00515 {
00516     Material *material = default_material;
00517     Point low( 0.0, 0.0, 0.0 );
00518     Point hi( 1.0, 1.0, 1.0 );
00519     if ( peek( Token::left_brace ) )
00520         for ( ; ; )
00521             {
00522                 if ( peek( "material" ) )
00523                     material = parseMaterial();
00524                 else if ( peek( "corner1" ) )
00525                     low = parsePoint();
00526                 else if ( peek( "corner2" ) )
00527                     hi = parsePoint();
00528                 else if ( peek( Token::right_brace ) )
00529                     break;
00530                 else
00531                     throwParseException( "Expected `material', `corner1', `corner2' or }." );
00532             }
00533 
00534     Box *b = new Box( material, low, hi);
00535 
00536     return b;
00537 }
00538 
00539 Object *Parser::parseRingObject()
00540 {
00541     Material *material = default_material;
00542     Point center( 0.0, 0.0, 0.0 );
00543     Vector normal( 0.0, 0.0, 1.0 );
00544     double radius1 = 0.0;
00545     double radius2 = 0.0;
00546 
00547     if ( peek( Token::left_brace ) )
00548         for ( ; ; )
00549             {
00550                 if ( peek( "material" ) )
00551                     material = parseMaterial();
00552                 else if ( peek( "center" ) )
00553                     center = parsePoint();
00554                 else if ( peek( "radius" ) )
00555                     radius2 = parseReal(); // disk
00556 
00557                 else if ( peek( "radius1" ) )
00558                     radius1 = parseReal(); // ring
00559                 else if ( peek( "radius2" ) )
00560                     radius2 = parseReal(); // ring
00561                 else if ( peek( "normal" ) )
00562                     normal = parseVector();
00563                 else if ( peek( Token::right_brace ) )
00564                     break;
00565                 else
00566                     throwParseException( "Expected `material', `center', `radius' or }." );
00567             }
00568     return new Ring( material, center, normal, radius1, radius2 );
00569 }
00570 
00571 Object *Parser::parseCylObject()
00572 {
00573     Material *material = default_material;
00574     Point base( 0, 0, 0 );
00575     Point top( 0, 0, 1.0 );
00576     float radius = 0.125;
00577 
00578     if ( peek( Token::left_brace ) ) {
00579         for ( ; ; ) {
00580             if ( peek( "material" ) )
00581                 material = parseMaterial();
00582             else if ( peek( "base" ) )
00583                 base = parsePoint();
00584             else if ( peek( "top" ) )
00585                 top = parsePoint();
00586             else if ( peek( "radius" ) )
00587                 radius = parseReal();
00588 
00589             else if ( peek( Token::right_brace ) )
00590                 break;
00591             else
00592                 throwParseException( "Expected `material', `base', `top', `radius' or }." );
00593         }
00594     }
00595     return new Cyl( material, base, top, radius);
00596 }
00597 
00598 Object *Parser::parseTriangleObject()
00599 {
00600     Material *material = default_material;
00601     Point A( 0.0, 0.0, 0.0 );
00602     Point B( 1.0, 0.0, 0.0 );
00603     Point C( 0.0, 1.0, 0.0 );
00604 
00605     if ( peek( Token::left_brace ) )
00606         for ( ; ; )
00607             {
00608                 if ( peek( "material" ) )
00609                     material = parseMaterial();
00610                 else if ( peek( "corner1" ) )
00611                     A = parsePoint();
00612                 else if ( peek( "corner2" ) )
00613                     B = parsePoint();
00614                 else if ( peek( "corner3" ) )
00615                     C = parsePoint();
00616                 else if ( peek( Token::right_brace ) )
00617                     break;
00618                 else
00619                     throwParseException( "Expected `material', `corner[123]' or }." );
00620             }
00621     return new Triangle( material, A, B, C);
00622 }
00623 
00624 
00625 Object *Parser::parseObject()
00626 {
00627     if ( peek( "group" ) )
00628         return parseGroupObject();
00629     else if ( peek( "plane" ) )
00630         return parsePlaneObject();
00631     else if ( peek( "sphere" ) )
00632         return parseSphereObject();
00633     else if ( peek( "cyl" ) )
00634         return parseCylObject();
00635     else if ( peek( "triangle") )
00636         return parseTriangleObject();
00637     else if ( peek( "disk" ) )
00638         return parseRingObject();
00639     else if ( peek( "ring" ) )
00640         return parseRingObject();
00641     else if ( peek( "box" ) )
00642         return parseBoxObject();
00643     else if ( next_token.token_type == Token::string )
00644         {
00645             map< string, Object * >::iterator found = defined_objects.find( parseString() );
00646             if ( found != defined_objects.end() )
00647                 return ( *found ).second;
00648         }
00649     throwParseException( "Expected an object type." );
00650     return 0;
00651 }
00652 
00653 Parser::Parser(
00654                std::istream &input )
00655     : input( input ),
00656       line_number( 1 ),
00657       column_number( 0 ),
00658       default_material( new LambertianMaterial( Color( 1.0, 1.0, 1.0 ), 0.6, 0.3 ) )
00659 {
00660     readNextToken();
00661 }
00662 
00663 Scene *Parser::parseScene(
00664                           std::string &filename )
00665 {
00666     int xres = 512;
00667     int yres = 512;
00668     Scene *scene = new Scene();
00669     for ( ; ; ) {
00670         if ( peek( "filename" ) )
00671             filename = parseString();
00672         else if ( peek( "xres" ) )
00673             xres = parseInteger();
00674         else if ( peek( "yres" ) )
00675             yres = parseInteger();
00676         else if ( peek( "maxraydepth" ) )
00677             scene->setMaxRayDepth( parseInteger() );
00678         else if ( peek( "minattenuation" ) )
00679             scene->setMinAttenuation( parseReal() );
00680         else if ( peek( "camera" ) )
00681             scene->setCamera( parseCamera() );
00682         else if ( peek( "background" ) )
00683             scene->setBackground( parseBackground() );
00684         else if ( peek( "ambient" ) )
00685             scene->setAmbient( parseColor() );
00686         else if ( peek( "light" ) )
00687             scene->addLight( parseLight() );
00688         else if ( peek( "scene" ) )
00689             scene->setObject( parseObject() );
00690         else if ( peek( "define" ) ) {
00691             if ( peek( "material" ) ) {
00692                 string name( parseString() );
00693                 defined_materials.insert( pair< string, Material * >( name, parseMaterial() ) );
00694             } else if ( peek( "object" ) ) {
00695                 string name( parseString() );
00696                 defined_objects.insert( pair< string, Object * >( name, parseObject() ) );
00697             } else
00698                 throwParseException( "Expected `material', or `object'" );
00699         }
00700         else if ( peek( Token::end_of_file ) )
00701             break;
00702         else
00703             throwParseException( "Expected `filename', `xres', `yres', `maxraydepth', `minattenuation', "
00704                                  "`camera', `background', `ambient', `light', `scene', or `define'." );
00705     }
00706     scene->setImage( new Image( xres, yres ) );
00707     return scene;
00708 }

Generated on Tue Jan 29 21:34:53 2008 for specter by  doxygen 1.4.6