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();
00556
00557 else if ( peek( "radius1" ) )
00558 radius1 = parseReal();
00559 else if ( peek( "radius2" ) )
00560 radius2 = parseReal();
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 }