1// cfdg.y 2// this file is part of Context Free 3// --------------------- 4// Copyright (C) 2005-2008 Mark Lentczner - markl@glyphic.com 5// Copyright (C) 2005-2013 John Horigan - john@glyphic.com 6// Copyright (C) 2005 Chris Coyne - ccoyne77@gmail.com 7// 8// This program is free software; you can redistribute it and/or 9// modify it under the terms of the GNU General Public License 10// as published by the Free Software Foundation; either version 2 11// of the License, or (at your option) any later version. 12// 13// This program is distributed in the hope that it will be useful, 14// but WITHOUT ANY WARRANTY; without even the implied warranty of 15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16// GNU General Public License for more details. 17// 18// You should have received a copy of the GNU General Public License 19// along with this program; if not, write to the Free Software 20// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 21// 22// John Horigan can be contacted at john@glyphic.com or at 23// John Horigan, 1209 Villa St., Mountain View, CA 94041-1123, USA 24// 25// Mark Lentczner can be contacted at markl@glyphic.com or at 26// Mark Lentczner, 1209 Villa St., Mountain View, CA 94041-1123, USA 27// 28// 29 30 31%skeleton "lalr1.cc" /* -*- C++ -*- */ 32%require "3.0" 33%defines 34%define api.parser.class {CfdgParser} 35%locations 36%expect 3 37//%debug 38%parse-param {class Builder& driver} 39 40%code requires { 41#include "builder.h" 42#include <string> 43} 44 45%union 46{ 47 unsigned modToken; 48 std::string* string; 49 CFG cfgenum; 50 AST::ASTexpression* expression; 51 AST::ASTmodTerm* term; 52 AST::ASTmodification* mod; 53 AST::ASTreplacement* component; 54 AST::ASTloop* loopObj; 55 AST::ASTif* ifObj; 56 AST::ASTswitch* switchObj; 57 AST::ASTruleSpecifier* ruleSpec; 58 AST::ASTrule* ruleObj; 59 AST::ASTrepContainer* bodyObj; 60 AST::ASTdefine* defObj; 61}; 62 63%code top { 64#include "astreplacement.h" 65#include "astexpression.h" 66#define USE(VALUE) /*empty*/ 67 68 using namespace AST; 69} 70 71%token STARTSHAPE 72%token CFDG2 73%token CFDG3 74%token SHAPE 75%token RULE 76%token PATH 77%token DEFINE 78%token BECOMES 79%token LOOP 80%token FINALLY 81%token IF 82%token ELSE 83%token SWITCH 84%token CASE 85%token CLONE 86%token LET 87%token <modToken> MODTYPE 88%token <modToken> PARAM 89%token BACKGROUND 90%token BADEOF /* passed when EOF/EOL is unexpected */ 91%token GOODEOF /* passed at end of include file */ 92%token RANGEOP 93%token PLUSMINUSOP 94%token <string> USER_STRING 95%token <string> USER_INTEGER 96%token <string> USER_RATIONAL 97%token <string> USER_FILENAME 98%token <string> USER_QSTRING 99%token <string> USER_ARRAYNAME 100%token INCLUDE 101%token IMPORT 102%token TILE 103%token <term> PARAMETERS 104%token <string> USER_PATHOP 105%token <term> STROKEWIDTH 106%token LE 107%token LT 108%token GE 109%token GT 110%token EQ 111%token NEQ 112%token NOT 113%token AND 114%token OR 115%token XOR 116%token CF_INFINITY 117%right ',' 118%left XOR 119%left OR 120%left AND 121%left EQ NEQ 122%left LE LT GE GT 123%left RANGEOP PLUSMINUSOP 124%left '-' '+' '_' 125%left '*' '/' 126%left NOT NEG POS /* negation--unary minus, unary identity */ 127%right '^' /* exponentiation */ 128%type <expression> exp exp2 exp3 expfunc explist arglist parameter_spec letBody 129%type <mod> modification modification_v2 buncha_adjustments 130%type <term> adjustment 131%type <component> element element_simple 132%type <component> pathOp_v2 pathOp_simple_v2 replacement_v2 replacement_simple_v2 133/* %type <definitions> buncha_definitions buncha_parameters parameter_list buncha_global_definitions */ 134%type <component> statement statement_v2 initialization initialization_v2 directive_v2 rule path shape_singleton transHeader rule_v2 path_v2 135%type <loopObj> loopHeader element_loop loopHeader_v2 136%type <ifObj> ifHeader ifElseHeader 137%type <switchObj> switchHeader 138%type <ruleObj> rule_header path_header rule_header_v2 path_header_v2 shape_singleton_header 139%type <string> fileString fileNameSpace shapeName 140%type <cfgenum> directive_num 141%type <modToken> caseHeader 142%type <bodyObj> letHeader 143%type <defObj> definition_header global_definition_header function_definition_header global_definition definition 144 145%destructor { delete $$; } USER_STRING USER_FILENAME USER_QSTRING USER_PATHOP USER_ARRAYNAME fileString fileNameSpace shapeName 146%destructor { } directive_num 147%destructor { delete $$; } definition_header global_definition_header function_definition_header global_definition definition 148%destructor { delete $$; } exp exp2 exp3 expfunc explist arglist parameter_spec letBody 149%destructor { delete $$; } adjustment 150%destructor { delete $$; } modification modification_v2 buncha_adjustments 151%destructor { driver.pop_repContainer(nullptr); delete $$; } loopHeader_v2 152%destructor { driver.pop_repContainer(nullptr); delete $$; } element_loop loopHeader 153%destructor { delete $$; } statement statement_v2 initialization initialization_v2 directive_v2 rule path element element_simple shape_singleton 154%destructor { driver.pop_repContainer(nullptr); delete $$; } shape_singleton_header transHeader 155%destructor { delete $$; } pathOp_v2 pathOp_simple_v2 replacement_v2 replacement_simple_v2 156%destructor { driver.pop_repContainer(nullptr); delete $$; } rule_header path_header 157%destructor { driver.pop_repContainer(nullptr); delete $$; } rule_header_v2 path_header_v2 158%destructor { driver.pop_repContainer(nullptr); delete $$; } ifHeader ifElseHeader 159%destructor { delete $$; } switchHeader 160%destructor { driver.pop_repContainer(nullptr); } caseHeader 161%destructor { driver.pop_repContainer(nullptr); delete $$; } letHeader 162 163%code provides { 164#include "scanner.h" 165#include "builder.h" 166#include <cassert> 167 168#undef yylex 169#define yylex driver.lexer->lex 170} 171 172%% 173 174choose: 175 CFDG2 cfdg2 | CFDG3 cfdg3; 176 177cfdg2: 178 cfdg2 statement_v2 { 179 if ($statement_v2) { 180 driver.push_rep($statement_v2, true); 181 } 182 } 183 | 184 ; 185 186cfdg3: 187 cfdg3 statement { 188 if ($statement) { 189 driver.push_rep($statement, true); 190 } 191 } 192 | 193 ; 194 195statement: 196 initialization 197 | import { $statement = nullptr; } 198 | eof { $statement = nullptr; } 199 | rule 200 | path 201 | shape { $statement = nullptr; } 202 | shape_singleton 203 | global_definition { $statement = static_cast<ASTreplacement*>($global_definition); } 204 | v2stuff { 205 error(@v2stuff, "Illegal mixture of old and new elements"); 206 $statement = nullptr; 207 } 208 ; 209 210statement_v2: 211 initialization_v2 212 | directive_v2 213 | inclusion { $statement_v2 = nullptr; } 214 | eof { $statement_v2 = nullptr; } 215 | rule_v2 216 | path_v2 217 | v3clues { 218 if (driver.lexer->maybeVersion == token::CFDG2) { 219 error(@v3clues, "Illegal mixture of old and new elements"); 220 } else { 221 driver.lexer->maybeVersion = token::CFDG3; 222 } 223 $statement_v2 = nullptr; 224 YYABORT; 225 } 226 ; 227 228v3clues: 229 USER_STRING BECOMES { delete $1; } 230 | MODTYPE BECOMES {} 231 | PARAM BECOMES {} 232 | USER_STRING '(' { delete $1; } 233 | USER_STRING USER_STRING '(' { delete $1; delete $2; } 234 | IMPORT 235 | SHAPE 236 | PATH USER_STRING '(' { delete $2; } 237 | STARTSHAPE USER_STRING '(' { delete $2; } 238 | STARTSHAPE USER_STRING '[' { delete $2; } 239 | STARTSHAPE USER_ARRAYNAME '[' { delete $2; } 240 ; 241 242v2stuff: 243 BACKGROUND modification_v2 { delete $2; } 244 | TILE modification_v2 { delete $2; } 245 | MODTYPE modification_v2 { delete $2; } 246 | INCLUDE fileString { delete $2; } 247 | rule_header_v2 { delete $1; } 248 ; 249 250inclusion: 251 INCLUDE fileString { 252 str_ptr file($fileString); $fileString = nullptr; 253 driver.lexer->maybeVersion = token::CFDG2; 254 driver.SetShape(nullptr); 255 driver.IncludeFile(*file); 256 } 257 ; 258 259import: 260 IMPORT fileNameSpace fileString { 261 str_ptr file($fileString); $fileString = nullptr; 262 str_ptr nm($fileNameSpace); $fileNameSpace = nullptr; 263 driver.SetShape(nullptr); 264 driver.IncludeFile(*file); 265 if (nm) 266 driver.PushNameSpace(std::move(nm), @fileNameSpace); 267 } 268 ; 269 270eof: 271 GOODEOF { 272 if (driver.EndInclude()) 273 YYACCEPT; 274 } 275 ; 276 277fileString: 278 USER_FILENAME | USER_QSTRING; 279 280fileNameSpace: 281 '@' USER_STRING { $fileNameSpace = $USER_STRING; } 282 | { $fileNameSpace = nullptr; } 283 ; 284 285initialization: 286 STARTSHAPE USER_STRING[shapeName] parameter_spec modification { 287 str_ptr name($shapeName); $shapeName = nullptr; 288 exp_ptr p($parameter_spec); $parameter_spec = nullptr; 289 mod_ptr mod($modification); $modification = nullptr; 290 driver.SetShape(nullptr); 291 $initialization = driver.MakeDefinition(CFG::StartShape, @initialization, 292 driver.MakeRuleSpec(*name, std::move(p), @shapeName, std::move(mod), true)); 293 } 294 | 295 STARTSHAPE USER_ARRAYNAME[shapeName] modification { 296 str_ptr name($shapeName); $shapeName = nullptr; 297 mod_ptr mod($modification); $modification = nullptr; 298 driver.SetShape(nullptr); 299 $initialization = driver.MakeDefinition(CFG::StartShape, @initialization, 300 driver.MakeRuleSpec(*name, nullptr, @shapeName, std::move(mod), true)); 301 } 302 | 303 STARTSHAPE USER_STRING[shapeName] parameter_spec { 304 str_ptr name($shapeName); $shapeName = nullptr; 305 exp_ptr p($parameter_spec); $parameter_spec = nullptr; 306 driver.SetShape(nullptr); 307 $initialization = driver.MakeDefinition(CFG::StartShape, @initialization, 308 driver.MakeRuleSpec(*name, std::move(p), @shapeName, nullptr, true)); 309 } 310 ; 311 312initialization_v2: 313 STARTSHAPE USER_STRING[shapeName] { 314 str_ptr name($shapeName); $shapeName = nullptr; 315 driver.SetShape(nullptr); 316 $initialization_v2 = driver.MakeDefinition(CFG::StartShape, @initialization_v2, 317 driver.MakeRuleSpec(*name, nullptr, @shapeName, nullptr, true)); 318 } 319 ; 320 321directive_v2: 322 directive_num modification_v2 { 323 exp_ptr mod($modification_v2); $modification_v2 = nullptr; 324 driver.lexer->maybeVersion = token::CFDG2; 325 $directive_v2 = driver.MakeDefinition($directive_num, @directive_v2, std::move(mod)); 326 } 327 ; 328 329directive_num: 330 BACKGROUND { $directive_num = CFG::Background; } 331 | 332 TILE { $directive_num = CFG::Tile; } 333 | 334 MODTYPE { 335 switch ($1) { 336 case ASTmodTerm::size: 337 $directive_num = CFG::Size; 338 break; 339 case ASTmodTerm::time: 340 $directive_num = CFG::Time; 341 break; 342 default: 343 $directive_num = CFG::Size; 344 error(@MODTYPE, "Syntax error"); 345 break; 346 } 347 } 348 ; 349 350global_definition: 351 global_definition_header exp2 { 352 def_ptr var($global_definition_header); $global_definition_header = nullptr; 353 exp_ptr exp($exp2); $exp2 = nullptr; 354 if (var) { 355 switch (var->mDefineType) { 356 case ASTdefine::ConfigDefine: 357 var->mExpression = std::move(exp); 358 driver.CheckConfig(var.get()); 359 break; 360 case ASTdefine::StackDefine: 361 if (ASTmodification* mod = dynamic_cast<ASTmodification*>(exp.get())) 362 var->mChildChange.grab(mod); // emptied ASTmod gets deleted 363 else 364 var->mExpression = std::move(exp); 365 break; 366 case ASTdefine::LetDefine: 367 assert(false); 368 break; 369 case ASTdefine::FunctionDefine: 370 driver.pop_repContainer(nullptr); 371 driver.mParamDecls.mParameters.clear(); 372 driver.mParamSize = 0; 373 // fall through 374 default: 375 var->mExpression = std::move(exp); 376 break; 377 } 378 $global_definition = var.release(); 379 } else { 380 $global_definition = nullptr; 381 } 382 } 383 ; 384 385function_definition_header: 386 SHAPE USER_STRING[funcName] function_parameter_list BECOMES { 387 str_ptr name($funcName); $funcName = nullptr; 388 $function_definition_header = driver.MakeDefinition(std::move(name), @function_definition_header, true); 389 if ($function_definition_header) { 390 $function_definition_header->mType = RuleType; 391 $function_definition_header->mTuplesize = 1; 392 } 393 } 394 | 395 USER_STRING[funcName] function_parameter_list BECOMES { 396 str_ptr name($funcName); $funcName = nullptr; 397 $function_definition_header = driver.MakeDefinition(std::move(name), @function_definition_header, true); 398 if ($function_definition_header) { 399 $function_definition_header->mType = NumericType; 400 $function_definition_header->mTuplesize = 1; 401 } 402 } 403 | 404 USER_STRING[funcType] USER_STRING[funcName] function_parameter_list BECOMES { 405 str_ptr type($funcType); $funcType = nullptr; 406 str_ptr name($funcName); $funcName = nullptr; 407 $function_definition_header = driver.MakeDefinition(std::move(name), @function_definition_header, true); 408 if ($function_definition_header) 409 $function_definition_header->mType = AST::decodeType(*type, $function_definition_header->mTuplesize, 410 $function_definition_header->isNatural, @funcType); 411 } 412 | 413 SHAPE MODTYPE function_parameter_list BECOMES { 414 error(@MODTYPE, "Reserved keyword: adjustment"); 415 $function_definition_header = nullptr; 416 } 417 | 418 MODTYPE function_parameter_list BECOMES { 419 error(@MODTYPE, "Reserved keyword: adjustment"); 420 $function_definition_header = nullptr; 421 } 422 | 423 USER_STRING[funcType] MODTYPE function_parameter_list BECOMES { 424 str_ptr type($funcType); $funcType = nullptr; 425 error(@MODTYPE, "Reserved keyword: adjustment"); 426 $function_definition_header = nullptr; 427 } 428 ; 429 430global_definition_header: 431 function_definition_header { 432 if ($function_definition_header) { 433 assert($function_definition_header->mDefineType == ASTdefine::FunctionDefine); 434 driver.push_repContainer(driver.mParamDecls); 435 } else { 436 // An error occurred 437 driver.mParamDecls.mParameters.clear(); 438 driver.mParamSize = 0; 439 } 440 $global_definition_header = $function_definition_header; 441 } 442 | 443 definition_header { 444 $global_definition_header = $definition_header; 445 } 446 ; 447 448definition_header: 449 USER_STRING[defnName] BECOMES { 450 str_ptr name($defnName); $defnName = nullptr; 451 $definition_header = driver.MakeDefinition(std::move(name), @definition_header, false); 452 } 453 | MODTYPE BECOMES { 454 error(@MODTYPE, "Reserved keyword: adjustment"); 455 $definition_header = nullptr; 456 } 457 ; 458 459definition: 460 definition_header exp2 { 461 def_ptr var($definition_header); $definition_header = nullptr; 462 exp_ptr exp($exp2); $exp2 = nullptr; 463 if (var) { 464 if (ASTmodification* mod = dynamic_cast<ASTmodification*>(exp.get())) { 465 mod->modData.mRand64Seed.seed(); 466 var->mChildChange.grab(mod); 467 } else { 468 var->mExpression = std::move(exp); 469 } 470 $definition = var.release(); 471 } else { 472 $definition = nullptr; 473 } 474 } 475 ; 476 477shape: 478 SHAPE USER_STRING[shapeName] parameter_list { 479 str_ptr name($shapeName); $shapeName = nullptr; 480 driver.SetShape(name.get(), @shapeName); 481 } 482 ; 483 484shape_singleton_header: 485 shape '{' { 486 driver.mInPathContainer = false; 487 $shape_singleton_header = new ASTrule(-1, @shape); 488 driver.AddRule($shape_singleton_header); 489 driver.push_repContainer($shape_singleton_header->mRuleBody); 490 } 491 492shape_singleton: 493 shape_singleton_header buncha_elements '}' { 494 $shape_singleton = $shape_singleton_header; 495 driver.pop_repContainer($shape_singleton_header); 496 driver.mInPathContainer = false; 497 } 498 ; 499 500rule_header_v2: 501 RULE USER_STRING[shapeName] { 502 str_ptr name($shapeName); $shapeName = nullptr; 503 driver.SetShape(nullptr); 504 $rule_header_v2 = new ASTrule(driver.StringToShape(*name, @shapeName, false), @rule_header_v2); 505 driver.AddRule($rule_header_v2); 506 driver.push_repContainer($rule_header_v2->mRuleBody); 507 } 508 | 509 RULE USER_STRING[shapeName] USER_RATIONAL[ruleWeight] { 510 str_ptr name($shapeName); $shapeName = nullptr; 511 str_ptr weight($ruleWeight); $ruleWeight = nullptr; 512 driver.SetShape(nullptr); 513 $rule_header_v2 = new ASTrule(driver.StringToShape(*name, @shapeName, false), 514 CFatof(weight->c_str()), 515 weight->find_first_of('%') != std::string::npos, 516 @rule_header_v2); 517 driver.AddRule($rule_header_v2); 518 driver.push_repContainer($rule_header_v2->mRuleBody); 519 } 520 ; 521 522rule_v2: 523 rule_header_v2 '{' buncha_replacements_v2 '}' { 524 driver.lexer->maybeVersion = token::CFDG2; 525 $rule_v2 = $rule_header_v2; 526 driver.pop_repContainer($rule_header_v2); 527 } 528 ; 529 530rule_header: 531 RULE { 532 driver.mInPathContainer = false; 533 $rule_header = new ASTrule(-1, @RULE); 534 driver.AddRule($rule_header); 535 driver.push_repContainer($rule_header->mRuleBody); 536 } 537 | 538 RULE USER_RATIONAL[ruleWeight] { 539 driver.mInPathContainer = false; 540 str_ptr weight($ruleWeight); $ruleWeight = nullptr; 541 $rule_header = new ASTrule(-1, CFatof(weight->c_str()), 542 weight->find_first_of('%') != std::string::npos, 543 @rule_header); 544 driver.AddRule($rule_header); 545 driver.push_repContainer($rule_header->mRuleBody); 546 } 547 ; 548 549path_header: 550 PATH USER_STRING[pathName] parameter_list { 551 str_ptr name($pathName); $pathName = nullptr; 552 driver.SetShape(name.get(), @pathName, true); 553 driver.mInPathContainer = true; 554 $path_header = new ASTrule(-1, @path_header); 555 $path_header->isPath = true; 556 driver.AddRule($path_header); 557 driver.push_repContainer($path_header->mRuleBody); 558 } 559 ; 560 561rule: 562 rule_header '{' buncha_elements '}' { 563 $rule = $rule_header; 564 driver.pop_repContainer($rule_header); 565 driver.mInPathContainer = false; 566 } 567 ; 568 569path: 570 path_header '{' buncha_elements '}' { 571 $path = $path_header; 572 driver.pop_repContainer($path_header); 573 driver.mInPathContainer = false; 574 driver.SetShape(nullptr); 575 } 576 ; 577 578path_header_v2: 579 PATH USER_STRING[pathName] { 580 str_ptr name($pathName); $pathName = nullptr; 581 driver.SetShape(nullptr); 582 $path_header_v2 = new ASTrule(driver.StringToShape(*name, @pathName, false), @path_header_v2); 583 $path_header_v2->isPath = true; 584 driver.AddRule($path_header_v2); 585 driver.push_repContainer($path_header_v2->mRuleBody); 586 driver.mInPathContainer = true; 587 } 588 ; 589 590path_v2: 591 path_header_v2 '{' buncha_pathOps_v2 '}' { 592 $path_v2 = $path_header_v2; 593 driver.pop_repContainer($path_header_v2); 594 } 595 ; 596 597parameter: 598 USER_STRING[paramType] USER_STRING[paramName] { 599 str_ptr type($paramType); $paramType = nullptr; 600 str_ptr var($paramName); $paramName = nullptr; 601 driver.NextParameterDecl(*type, *var, @paramType, @paramName); 602 } 603 | 604 SHAPE USER_STRING[paramName] { 605 static std::string shapeStr("shape"); 606 str_ptr var($paramName); $paramName = nullptr; 607 driver.NextParameterDecl(shapeStr, *var, @SHAPE, @paramName); 608 } 609 | 610 USER_STRING MODTYPE { 611 delete $USER_STRING; 612 error(@MODTYPE, "Reserved keyword: adjustment"); 613 } 614 | 615 SHAPE MODTYPE { 616 error(@MODTYPE, "Reserved keyword: adjustment"); 617 } 618 | 619 USER_STRING[paramName] { 620 static const std::string numtype("number"); 621 str_ptr var($paramName); $paramName = nullptr; 622 driver.NextParameterDecl(numtype, *var, @paramName, @paramName); 623 } 624 | 625 MODTYPE { 626 error(@MODTYPE, "Reserved keyword: adjustment"); 627 } 628 ; 629 630buncha_parameters: 631 buncha_parameters ',' parameter 632 | parameter 633 ; 634 635parameter_list: 636 '(' buncha_parameters ')' 637 | 638 ; 639 640function_parameter_list: 641 '(' buncha_parameters ')' 642 | '(' ')' 643 ; 644 645parameter_spec: 646 '(' arglist ')' { $parameter_spec = $arglist;} 647 | '(' BECOMES ')' { $parameter_spec = new ASTexpression(@parameter_spec, false, false, AST::ReuseType); } 648 | '(' ')' { $parameter_spec = nullptr; } 649 | { $parameter_spec = nullptr; } 650 ; 651 652buncha_elements: 653 buncha_elements element { 654 driver.push_rep($element); 655 } 656 | 657 ; 658 659buncha_pathOps_v2: 660 buncha_pathOps_v2 pathOp_v2 { 661 driver.push_rep($pathOp_v2); 662 } 663 | 664 ; 665 666pathOp_simple_v2: 667 USER_PATHOP '{' buncha_adjustments '}' { 668 str_ptr pop($USER_PATHOP); $USER_PATHOP = nullptr; 669 mod_ptr mod($buncha_adjustments); $buncha_adjustments = nullptr; 670 driver.lexer->maybeVersion = token::CFDG2; 671 $pathOp_simple_v2 = new ASTpathOp(*pop, std::move(mod), @pathOp_simple_v2); 672 } 673 | 674 shapeName modification_v2 { 675 str_ptr cmd($shapeName); $shapeName = nullptr; 676 mod_ptr mod($modification_v2); $modification_v2 = nullptr; 677 driver.lexer->maybeVersion = token::CFDG2; 678 $pathOp_simple_v2 = new ASTpathCommand(*cmd, std::move(mod), nullptr, @pathOp_simple_v2); 679 } 680 ; 681 682element_simple: 683 USER_PATHOP '(' exp2 ')' { 684 str_ptr pop($USER_PATHOP); $USER_PATHOP = nullptr; 685 exp_ptr mod($exp2); $exp2 = nullptr; 686 $element_simple = new ASTpathOp(*pop, std::move(mod), @element_simple); 687 } 688 | 689 USER_PATHOP '(' ')' { 690 str_ptr pop($USER_PATHOP); $USER_PATHOP = nullptr; 691 exp_ptr mod; 692 $element_simple = new ASTpathOp(*pop, std::move(mod), @element_simple); 693 } 694 | 695 shapeName parameter_spec modification { 696 str_ptr cmd($shapeName); $shapeName = nullptr; 697 exp_ptr p($parameter_spec); $parameter_spec = nullptr; 698 mod_ptr mod($modification); $modification = nullptr; 699 $element_simple = driver.MakeElement(std::move(cmd), std::move(mod), 700 std::move(p), @element_simple, false); 701 } 702 | 703 IF '(' exp2 ')' modification { 704 exp_ptr args($exp2); $exp2 = nullptr; 705 mod_ptr mod($modification); $modification = nullptr; 706 str_ptr func(new std::string("if")); 707 args.reset(driver.MakeFunction(std::move(func), std::move(args), @IF, @2 + @4, false)); 708 func.reset(new std::string("if")); 709 $element_simple = driver.MakeElement(std::move(func), std::move(mod), std::move(args), @element_simple, false); 710 } 711 | 712 letHeader letBody modification { 713 driver.pop_repContainer(nullptr); 714 cont_ptr vars($letHeader); $letHeader = nullptr; 715 exp_ptr exp($letBody); $letBody = nullptr; 716 mod_ptr mod($modification); $modification = nullptr; 717 exp.reset(driver.MakeLet(@1, std::move(vars), std::move(exp))); // must do unconditionally 718 str_ptr newlet(new std::string("let")); 719 $element_simple = driver.MakeElement(std::move(newlet), std::move(mod), std::move(exp), @element_simple, false); 720 } 721 | 722 PATH shapeName parameter_spec modification { 723 str_ptr cmd($shapeName); $shapeName = nullptr; 724 exp_ptr p($parameter_spec); $parameter_spec = nullptr; 725 mod_ptr mod($modification); $modification = nullptr; 726 $element_simple = driver.MakeElement(std::move(cmd), std::move(mod), std::move(p), @element_simple, true); 727 } 728 ; 729 730one_or_more_elements: 731 '{' buncha_elements '}' { } 732 | 733 element { 734 driver.push_rep($element); 735 } 736 ; 737 738one_or_more_pathOp_v2: 739 '{' buncha_pathOps_v2 '}' { } 740 | 741 pathOp_simple_v2 { 742 driver.push_rep($pathOp_simple_v2); 743 } 744 ; 745 746caseBody: 747 caseBody caseBody_element 748 | 749 ; 750 751caseBody_element: 752 caseHeader one_or_more_elements { 753 driver.pop_repContainer(driver.switchStack.top()); 754 USE($caseHeader); 755 } 756 ; 757 758element: 759 element_simple { 760 $element = $element_simple; 761 } 762 | 763 definition { 764 $element = $definition; 765 } 766 | 767 element_loop { 768 $element = $element_loop; 769 driver.pop_repContainer($element_loop); 770 if ($element_loop->mRepType == 0) { 771 delete $element_loop; 772 $element = nullptr; 773 } 774 } 775 | 776 element_loop FINALLY { 777 driver.pop_repContainer($element_loop); 778 driver.push_repContainer($element_loop->mFinallyBody); 779 } one_or_more_elements { 780 driver.pop_repContainer($element_loop); 781 $element = $element_loop; 782 if ($element_loop->mRepType == 0) { 783 delete $element_loop; 784 $element = nullptr; 785 } 786 } 787 | 788 ifHeader one_or_more_elements { 789 $element = $ifHeader; 790 driver.pop_repContainer($ifHeader); 791 if ($ifHeader->mRepType == 0) { 792 delete $ifHeader; 793 $element = nullptr; 794 } 795 } 796 | 797 ifElseHeader one_or_more_elements { 798 $element = $ifElseHeader; 799 driver.pop_repContainer($ifElseHeader); 800 if ($ifElseHeader->mRepType == 0) { 801 delete $ifElseHeader; 802 $element = nullptr; 803 } 804 } 805 | 806 transHeader one_or_more_elements { 807 $element = $transHeader; 808 driver.pop_repContainer($transHeader); 809 if ($transHeader->mRepType == 0) { 810 delete $transHeader; 811 $element = nullptr; 812 } 813 } 814 | 815 switchHeader '{' caseBody '}' 816 { 817 $element = $switchHeader; 818 $switchHeader->unify(); 819 driver.switchStack.pop(); 820 } 821 | 822 element_v2clue { 823 error(@element_v2clue, "Illegal mixture of old and new elements"); 824 $element = nullptr; 825 } 826 ; 827 828element_v2clue: 829 USER_RATIONAL '*' { delete $1; } 830 | USER_STRING '{' { delete $1; } 831 | USER_PATHOP '{' { delete $1; } 832 ; 833 834pathOp_v2: 835 pathOp_simple_v2 { $pathOp_v2 = $pathOp_simple_v2; } 836 | 837 loopHeader_v2 one_or_more_pathOp_v2 { 838 $pathOp_v2 = $loopHeader_v2; 839 driver.pop_repContainer($loopHeader_v2); 840 if ($loopHeader_v2->mRepType == 0) { 841 delete $loopHeader_v2; 842 $pathOp_v2 = nullptr; 843 } 844 } 845 | pathOp_v3clues { 846 if (driver.lexer->maybeVersion == token::CFDG2) { 847 error(@pathOp_v3clues, "Illegal mixture of old and new elements"); 848 } else { 849 driver.lexer->maybeVersion = token::CFDG3; 850 } 851 $pathOp_v2 = nullptr; 852 YYABORT; 853 } 854 ; 855 856pathOp_v3clues: 857 USER_PATHOP '(' { delete $1; } 858 | USER_STRING '(' { delete $1; } 859 | PATH 860 | LOOP 861 | USER_STRING BECOMES { delete $1; } 862 | MODTYPE BECOMES 863 | IF 864 | MODTYPE 865 | SWITCH 866 ; 867 868element_loop: 869 loopHeader modification one_or_more_elements { 870 // parse loop mod and loop body with loop index in scope 871 $loopHeader->mLoopModHolder.reset($modification); $modification = nullptr; 872 $element_loop = $loopHeader; 873 // loopmod gets deleted 874 } 875 ; 876 877buncha_replacements_v2: 878 buncha_replacements_v2 replacement_v2 { 879 driver.push_rep($replacement_v2); 880 } 881 | 882 ; 883 884one_or_more_replacements_v2: 885 '{' buncha_replacements_v2 '}' { } 886 | 887 replacement_simple_v2 { 888 driver.push_rep($replacement_simple_v2); 889 } 890 ; 891 892replacement_simple_v2: 893 shapeName modification_v2 { 894 str_ptr name($shapeName); $shapeName = nullptr; 895 mod_ptr mod($modification_v2); $modification_v2 = nullptr; 896 ruleSpec_ptr r(driver.MakeRuleSpec(*name, nullptr, @shapeName)); 897 $replacement_simple_v2 = new ASTreplacement(std::move(r), std::move(mod), @replacement_simple_v2); 898 } 899 ; 900 901replacement_v2: 902 replacement_simple_v2 { $replacement_v2 = $replacement_simple_v2; } 903 | 904 loopHeader_v2 one_or_more_replacements_v2 { 905 $replacement_v2 = $loopHeader_v2; 906 driver.pop_repContainer($loopHeader_v2); 907 if ($loopHeader_v2->mRepType == 0) { 908 delete $loopHeader_v2; 909 $replacement_v2 = nullptr; 910 } 911 } 912 ; 913 914loopHeader_v2: 915 USER_RATIONAL[loopCount] '*' { ++driver.mLocalStackDepth; } modification_v2 { 916 str_ptr cstr($loopCount); $loopCount = nullptr; 917 exp_ptr count(new ASTreal(*cstr, @loopCount)); 918 mod_ptr mod($modification_v2); $modification_v2 = nullptr; 919 std::string dummyvar("~~inaccessiblevar~~"); 920 --driver.mLocalStackDepth; 921 driver.lexer->maybeVersion = token::CFDG2; 922 $loopHeader_v2 = new ASTloop(driver.StringToShape(dummyvar, @loopCount, false), 923 dummyvar, @loopCount, std::move(count), 924 @loopHeader_v2, std::move(mod)); 925 driver.push_repContainer($loopHeader_v2->mLoopBody); 926 } 927 ; 928 929loopHeader: 930 LOOP USER_STRING[indexVar] BECOMES exp2 { 931 str_ptr var($indexVar); $indexVar = nullptr; 932 exp_ptr index($exp2); $exp2 = nullptr; 933 int nameIndex = driver.StringToShape(*var, @indexVar, false); 934 $loopHeader = new ASTloop(nameIndex, *var, @indexVar, std::move(index), @exp2, nullptr); 935 driver.push_repContainer($loopHeader->mLoopBody); 936 } 937 | 938 LOOP MODTYPE BECOMES exp2 { 939 exp_ptr index($exp2); $exp2 = nullptr; 940 std::string dummyvar("~~inaccessiblevar~~"); 941 $loopHeader = new ASTloop(driver.StringToShape(dummyvar, @LOOP, false), 942 dummyvar, @MODTYPE, std::move(index), @exp2, nullptr); 943 driver.push_repContainer($loopHeader->mLoopBody); 944 error(@MODTYPE, "Reserved keyword: adjustment"); 945 } 946 | 947 LOOP exp2 { 948 exp_ptr count($exp2); $exp2 = nullptr; 949 std::string dummyvar("~~inaccessiblevar~~"); 950 $loopHeader = new ASTloop(driver.StringToShape(dummyvar, @LOOP, false), 951 dummyvar, @LOOP, std::move(count), @exp2, nullptr); 952 driver.push_repContainer($loopHeader->mLoopBody); 953 } 954 ; 955 956ifHeader: 957 IF '(' exp2 ')' { 958 exp_ptr cond($exp2); $exp2 = nullptr; 959 $ifHeader = new ASTif(std::move(cond), @exp2); 960 driver.push_repContainer($ifHeader->mThenBody); 961 } 962 ; 963 964ifElseHeader: 965 ifHeader one_or_more_elements ELSE { 966 driver.pop_repContainer($ifHeader); 967 driver.push_repContainer($ifHeader->mElseBody); 968 $ifElseHeader = $ifHeader; 969 } 970 ; 971 972transHeader: 973 MODTYPE exp2 { 974 exp_ptr mods($exp2); $exp2 = nullptr; 975 if ($MODTYPE != ASTmodTerm::transform) 976 error(@MODTYPE, "Syntax error"); 977 ASTtransform* trans = new ASTtransform(@transHeader, std::move(mods)); 978 driver.push_repContainer(trans->mBody); 979 $transHeader = trans; 980 } 981 | CLONE exp2 { 982 exp_ptr mods($exp2); $exp2 = nullptr; 983 ASTtransform* trans = new ASTtransform(@transHeader, std::move(mods)); 984 driver.push_repContainer(trans->mBody); 985 trans->mClone = true; 986 $transHeader = trans; 987 } 988 ; 989 990switchHeader: 991 SWITCH '(' exp2 ')' { 992 exp_ptr caseVal($exp2); $exp2 = nullptr; 993 $switchHeader = new ASTswitch(std::move(caseVal), @exp2); 994 driver.switchStack.push($switchHeader); 995 } 996 ; 997 998caseHeader: 999 CASE exp2 ':' { 1000 exp_ptr valExp($exp2); $exp2 = nullptr; 1001 1002 ASTswitch* _switch = driver.switchStack.top(); 1003 cont_ptr caseBody(new ASTrepContainer()); 1004 driver.push_repContainer(*caseBody); 1005 _switch->mCases.emplace_back(std::move(valExp), std::move(caseBody)); 1006 1007 $caseHeader = 0; 1008 } 1009 | 1010 ELSE ':' { 1011 if (!driver.switchStack.top()->mElseBody.mBody.empty()) { 1012 driver.error(@$, "There can only be one 'else:' clause"); 1013 } else { 1014 driver.push_repContainer(driver.switchStack.top()->mElseBody); 1015 } 1016 $caseHeader = 0; 1017 } 1018 ; 1019 1020modification_v2: 1021 '{' buncha_adjustments '}' { 1022 mod_ptr mod($buncha_adjustments); $buncha_adjustments = nullptr; 1023 $modification_v2 = driver.MakeModification(std::move(mod), @modification_v2, true); 1024 } 1025 | 1026 '[' buncha_adjustments ']' { 1027 mod_ptr mod($buncha_adjustments); $buncha_adjustments = nullptr; 1028 $modification_v2 = driver.MakeModification(std::move(mod), @modification_v2, false); 1029 } 1030 ; 1031 1032modification: 1033 '[' buncha_adjustments ']' { 1034 mod_ptr mod($buncha_adjustments); $buncha_adjustments = nullptr; 1035 $modification = driver.MakeModification(std::move(mod), @modification, true); 1036 } 1037 | 1038 '[' '[' buncha_adjustments ']' ']' { 1039 mod_ptr mod($buncha_adjustments); $buncha_adjustments = nullptr; 1040 $modification = driver.MakeModification(std::move(mod), @modification, false); 1041 } 1042 ; 1043 1044buncha_adjustments[ASTmodOut]: 1045 buncha_adjustments[ASTmodIn] adjustment { 1046 term_ptr mod($adjustment); $adjustment = nullptr; 1047 driver.MakeModTerm($ASTmodIn->modExp, std::move(mod)); 1048 $ASTmodOut = $ASTmodIn; 1049 } 1050 | { 1051 static const yy::location def; 1052 $ASTmodOut = new ASTmodification(def); 1053 } 1054 ; 1055 1056adjustment: 1057 MODTYPE explist { 1058 $adjustment = new ASTmodTerm(static_cast<ASTmodTerm::modTypeEnum>($MODTYPE), $explist, @adjustment); 1059 } 1060 | 1061 MODTYPE exp '|' { 1062 exp_ptr mod($exp); $exp = nullptr; 1063 if ($MODTYPE < ASTmodTerm::hue || $MODTYPE > ASTmodTerm::alpha) { 1064 error(@adjustment, "The target operator can only be applied to color adjustments"); 1065 $adjustment = nullptr; 1066 } else { 1067 $adjustment = new ASTmodTerm(static_cast<ASTmodTerm::modTypeEnum>($MODTYPE + 4), mod.release(), @adjustment); 1068 } 1069 } 1070 | 1071 PARAM USER_STRING { 1072 str_ptr p($USER_STRING); $USER_STRING = nullptr; 1073 $adjustment = new ASTmodTerm(ASTmodTerm::param, *p, @adjustment); 1074 } 1075 | 1076 PARAM USER_QSTRING { 1077 str_ptr p($USER_QSTRING); $USER_QSTRING = nullptr; 1078 $adjustment = new ASTmodTerm(ASTmodTerm::param, *p, @adjustment); 1079 } 1080 ; 1081 1082letHeader: 1083 LET { 1084 $letHeader = new ASTrepContainer(); 1085 driver.push_repContainer(*$letHeader); 1086 } 1087 ; 1088 1089letBody: 1090 '(' letVariables ';' exp2 ')' { 1091 $letBody = $exp2; 1092 } 1093 ; 1094 1095letVariables: 1096 letVariables ';' letVariable 1097 | 1098 letVariable 1099 ; 1100 1101letVariable: 1102 definition { 1103 driver.push_rep($definition); 1104 } 1105 ; 1106 1107explist[listOut]: 1108 explist[listIn] exp { 1109 $listOut = ASTexpression::Append($listIn, $exp); 1110 } 1111 | 1112 exp { 1113 $listOut = $exp; 1114 } 1115 ; 1116 1117 1118arglist[listOut]: 1119 arglist[listIn] ',' exp3 { 1120 $listOut = $listIn->append(new ASTparen($exp3)); 1121 } 1122 | 1123 exp3 { 1124 $listOut = new ASTcons{ new ASTparen($exp3) }; 1125 } 1126 ; 1127 1128exp[res]: 1129 USER_RATIONAL { $res = new ASTreal(*$USER_RATIONAL, @res); delete $USER_RATIONAL; $USER_RATIONAL = nullptr; } 1130 | 1131 CF_INFINITY { $res = new ASTreal(Renderer::Infinity, @res); } 1132 | 1133 '(' exp2 ')' { $res = new ASTparen($exp2); } 1134 | 1135 expfunc { $res = $expfunc; } 1136 | 1137 USER_STRING[funcName] '(' arglist ')' { 1138 str_ptr func($funcName); $funcName = nullptr; 1139 exp_ptr args($arglist); $arglist = nullptr; 1140 $res = driver.MakeFunction(std::move(func), std::move(args), @funcName, @2 + @4, true); 1141 } 1142 | 1143 '-' exp[unop] { $res = new ASToperator('N', $unop, nullptr);; } 1144 | 1145 '+' exp[unop] { $res = new ASToperator('P', $unop, nullptr);; } 1146 | 1147 exp[start] RANGEOP exp[end] { 1148 exp_ptr pair($start->append($end)); $start = nullptr; $end = nullptr; 1149 $res = new ASTfunction("rand.", std::move(pair), driver.mSeed, @RANGEOP, @start + @end, &driver); 1150 } 1151 | 1152 exp[center] PLUSMINUSOP exp[range] { 1153 exp_ptr pair($center->append($range)); $center = nullptr; $range = nullptr; 1154 $res = new ASTfunction("rand+/-", std::move(pair), driver.mSeed, @PLUSMINUSOP, @center + @range, &driver); 1155 } 1156 ; 1157 1158exp2[res]: 1159 exp2[l] ',' exp3[r] { $res = $l->append($r); } 1160 | 1161 exp3 { $res = $exp3; } 1162 ; 1163 1164exp3[res]: 1165 USER_RATIONAL { $res = new ASTreal(*$USER_RATIONAL, @res); delete $USER_RATIONAL; $USER_RATIONAL = nullptr; } 1166 | 1167 CF_INFINITY { $res = new ASTreal(Renderer::Infinity, @res); } 1168 | 1169 expfunc { $res = $expfunc; } 1170 | 1171 USER_STRING[funcName] '(' arglist ')' { 1172 str_ptr func($funcName); $funcName = nullptr; 1173 exp_ptr args($arglist); $arglist = nullptr; 1174 $res = driver.MakeFunction(std::move(func), std::move(args), @funcName, @2 + @4, false); 1175 } 1176 | 1177 exp3[start] RANGEOP exp3[end] { 1178 exp_ptr pair($start->append($end)); $start = nullptr; $end = nullptr; 1179 $res = new ASTfunction("rand.", std::move(pair), driver.mSeed, @RANGEOP, @start + @end, &driver); 1180 } 1181 | 1182 exp3[center] PLUSMINUSOP exp3[range] { 1183 exp_ptr pair($center->append($range)); $center = nullptr; $range = nullptr; 1184 $res = new ASTfunction("rand+/-", std::move(pair), driver.mSeed, @PLUSMINUSOP, @center + @range, &driver); 1185 } 1186 | 1187 exp3[l] '+' exp3[r] { $res = new ASToperator('+', $l, $r); } 1188 | 1189 exp3[l] '-' exp3[r] { $res = new ASToperator('-', $l, $r); } 1190 | 1191 exp3[l] '_' exp3[r] { $res = new ASToperator('_', $l, $r); } 1192 | 1193 exp3[l] '*' exp3[r] { $res = new ASToperator('*', $l, $r); } 1194 | 1195 exp3[l] '/' exp3[r] { $res = new ASToperator('/', $l, $r); } 1196 | 1197 '-' exp3[un] %prec NEG { $res = new ASToperator('N', $un, nullptr); } 1198 | 1199 '+' exp3[un] %prec POS { $res = new ASToperator('P', $un, nullptr); } 1200 | 1201 NOT exp3[un] { $res = new ASToperator('!', $un, nullptr); } 1202 | 1203 exp3[l] '^' exp3[r] { $res = new ASToperator('^', $l, $r); } 1204 | 1205 exp3[l] LT exp3[r] { $res = new ASToperator('<', $l, $r); } 1206 | 1207 exp3[l] LE exp3[r] { $res = new ASToperator('L', $l, $r); } 1208 | 1209 exp3[l] GT exp3[r] { $res = new ASToperator('>', $l, $r); } 1210 | 1211 exp3[l] GE exp3[r] { $res = new ASToperator('G', $l, $r); } 1212 | 1213 exp3[l] EQ exp3[r] { $res = new ASToperator('=', $l, $r); } 1214 | 1215 exp3[l] NEQ exp3[r] { $res = new ASToperator('n', $l, $r); } 1216 | 1217 exp3[l] AND exp3[r] { $res = new ASToperator('&', $l, $r); } 1218 | 1219 exp3[l] OR exp3[r] { $res = new ASToperator('|', $l, $r); } 1220 | 1221 exp3[l] XOR exp3[r] { $res = new ASToperator('X', $l, $r); } 1222 | 1223 '(' exp2[e] ')' { $res = new ASTparen($e); } 1224 | 1225 modification { $res = $modification; } 1226 ; 1227 1228expfunc: 1229 USER_STRING[funcName] '(' ')' { 1230 str_ptr func($funcName); $funcName = nullptr; 1231 $expfunc = driver.MakeFunction(std::move(func), nullptr, @funcName, @2 + @3, false); 1232 } 1233 | 1234 USER_ARRAYNAME '[' exp2 ']' { 1235 str_ptr func($USER_ARRAYNAME); $USER_ARRAYNAME = nullptr; 1236 exp_ptr args($exp2); $exp2 = nullptr; 1237 $expfunc = driver.MakeArray(std::move(func), std::move(args), @USER_ARRAYNAME, @2 + @4); 1238 } 1239 | 1240 IF '(' exp2 ')' { 1241 str_ptr func(new std::string("if")); 1242 exp_ptr args($exp2); $exp2 = nullptr; 1243 $expfunc = driver.MakeFunction(std::move(func), std::move(args), @IF, @2 + @4, false); 1244 } 1245 | 1246 USER_STRING '(' BECOMES ')' { 1247 str_ptr func($USER_STRING); $USER_STRING = nullptr; 1248 exp_ptr args(new ASTexpression(@$, false, false, AST::ReuseType)); 1249 $expfunc = driver.MakeFunction(std::move(func), std::move(args), @USER_STRING, @2 + @4, false); 1250 } 1251 | 1252 letHeader letBody { 1253 driver.pop_repContainer(nullptr); 1254 cont_ptr vars($letHeader); $letHeader = nullptr; 1255 exp_ptr exp($letBody); $letBody = nullptr; 1256 $expfunc = driver.MakeLet(@letHeader, std::move(vars), std::move(exp)); 1257 } 1258 | 1259 USER_STRING { 1260 str_ptr var($USER_STRING); $USER_STRING = nullptr; 1261 $expfunc = driver.MakeVariable(*var, @USER_STRING); 1262 } 1263 ; 1264 1265shapeName: 1266 USER_STRING { $shapeName = $USER_STRING; } 1267 | 1268 USER_ARRAYNAME { $shapeName = $USER_ARRAYNAME; } 1269 ; 1270 1271%% 1272 1273void yy::CfdgParser::error(const CfdgParser::location_type& l, const std::string& m) 1274{ 1275 driver.error(l, m); 1276} 1277