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