//4 empty lines to get replaced by
//the contents of preamble_{compile,csh}.yy
//files depending on whether we build parsers
//for compiling files or for color sytnax highlight (csh)

/*
    This file is part of Msc-generator.
    Copyright (C) 2008-2024 Zoltan Turanyi
    Distributed under GNU Affero General Public License.

    Msc-generator is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Msc-generator is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with Msc-generator.  If not, see <http://www.gnu.org/licenses/>.
*/

%require "3.8.2"
%expect 192
%locations
%define api.pure full
%define api.location.type {SourceLocationRange<C_S_H>}
%param{sv_reader<C_S_H> &input}
%initial-action { @$.set(input.current_pos()); };

//Goes to the parser.h files
%code requires{
    #include "cgen_shapes.h"
    #include "flowcsh.h"
    #include "flowchart.h"
    #include "flow_lexer.h"

    //To allow including both flow_parse_{compile,csh}.h
    #undef C_S_H
    #undef CHAR_IF_CSH

    #ifdef C_S_H_IS_COMPILED
        #define C_S_H true
        #define CHAR_IF_CSH(A) char

    #else
        #define C_S_H false
        #define CHAR_IF_CSH(A) A
    #endif

    using namespace flow;
}

//Goes to the parser.cpp files
%code {
    #include "flow_tokenizer.h"
    #include "flow_lexer.h"

    #ifdef C_S_H_IS_COMPILED
        void flow_csh_error(SourceLocationRange<C_S_H>* pos, Csh &csh, sv_reader<C_S_H>&, const char *msg) {
            csh.AddCSH_Error(*pos, std::string(msg));
        }
        int flow_csh_lex(FLOW_CSH_STYPE *lvalp, SourceLocationRange<C_S_H> *llocp, flow::FlowCsh& csh, sv_reader<C_S_H>& input) {
            return get_token<flow_csh_tokentype>(input, csh, flow::readandclassify<sv_reader<C_S_H>>, *lvalp, *llocp);
        }
    #else
        void flow_compile_error(SourceLocationRange<C_S_H>* pos, Chart &chart, const procedure_parse_helper &, sv_reader<C_S_H>&, const char *msg) {
            chart.Error.Error(*pos, msg);
        }
        int flow_compile_lex(FLOW_COMPILE_STYPE *lvalp, SourceLocationRange<C_S_H> *llocp, FlowChart& chart, sv_reader<C_S_H>& input) {
            return get_token<flow_compile_tokentype>(input, chart, flow::readandclassify<sv_reader<C_S_H>>, *lvalp, *llocp);
        }
    #endif
}

%token TOK_STRING TOK_QSTRING TOK_NUMBER TOK_DASH TOK_EQUAL TOK_COMMA
       TOK_SEMICOLON  TOK_PLUS_PLUS
       TOK_OCBRACKET TOK_CCBRACKET TOK_OSBRACKET TOK_CSBRACKET
       TOK_IF TOK_STEP TOK_REPEAT TOK_START TOK_STOP TOK_GOTO
       TOK_BRANCH TOK_THEN TOK_ELSE TOK_YES TOK_NO
       TOK_SHAPE_COMMAND
       TOK_BYE
       TOK_COLON_STRING TOK_COLON_QUOTED_STRING TOK_COLORDEF
       TOK_COMMAND_DEFSHAPE TOK_COMMAND_DEFCOLOR TOK_COMMAND_DEFSTYLE TOK_COMMAND_DEFDESIGN
       TOK__NEVER__HAPPENS TOK__NEVER__HAPPENS2 TOK_UNRECOGNIZED_CHAR
       TOK_EOF 0
       TOK_WHITESPACE TOK_NEWLINE TOK_COMMENT

%union
{
    str_view                                str;
    multi_segment_string                    multi_str;
    gsl::owner<std::vector<std::string>*>   stringlist;
    ShapeElement::Type                      shapecommand;
    CHAR_IF_CSH(Step::KeywordHelper)        steptype;
    gsl::owner<CHAR_IF_CSH(Shape)*>         shape;
    gsl::owner<CHAR_IF_CSH(ShapeElement)*>  shapeelement;
    gsl::owner<CHAR_IF_CSH(Attribute)*>     attribute;
    gsl::owner<CHAR_IF_CSH(AttributeList)*> attributelist;
    gsl::owner<CHAR_IF_CSH(FlowElement)*>   instruction;
    gsl::owner<CHAR_IF_CSH(FElementList)*>  instruction_list;
    gsl::owner<CHAR_IF_CSH(Branch)*>        branch;
};

%type <str> TOK_STRING TOK_QSTRING  TOK_COLORDEF TOK_NUMBER  TOK_BYE
            TOK_COMMAND_DEFSHAPE TOK_COMMAND_DEFCOLOR TOK_COMMAND_DEFSTYLE TOK_COMMAND_DEFDESIGN
            TOK_IF TOK_STEP TOK_REPEAT TOK_START TOK_STOP TOK_GOTO
            TOK_BRANCH branch_keyword TOK_THEN TOK_ELSE TOK_YES TOK_NO
            entity_string reserved_word_string alpha_string symbol_string string
            color_string
%type <multi_str> TOK_COLON_STRING TOK_COLON_QUOTED_STRING
%type <stringlist> stylenamelist
%type <shapecommand> TOK_SHAPE_COMMAND
%type <steptype> step_types
%type <shapeelement> shapeline
%type <shape> shapedeflist
%type <attribute> attr
%type <attributelist> full_attrlist_with_label full_attrlist attrlist
%type <instruction> instr_needs_semicolon instr_with_semicolon scope_close opt goto goto_with_semicolon
                    actual_instr_needs_semicolon actual_instr
%type <branch> branch_no_content branch_with_semicolon
%type <instruction_list> instrlist braced_instrlist top_level_instrlist optlist
                         several_instructions optlist_with_semicolon

%destructor { }                      <shapecommand> <steptype> <str>
%destructor { $$.destroy(); }        <multi_str>
%destructor {delete $$;}             <stringlist>
%destructor {if (!C_S_H) delete $$;} <*>

%%

chart_with_bye: chart eof
{
	YYACCEPT;
}
      | chart error eof
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_Error(@2, "Syntax error.");
  #else
    chart.Error.Error(@2, "Syntax error.");
  #endif
	YYACCEPT;
};


eof:   TOK_EOF
      | TOK_BYE
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
	csh.AddCSH_AllCommentBeyond(@1);
  #else
  #endif
    (void)$1;
}
      | TOK_BYE TOK_SEMICOLON
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_SEMICOLON);
	csh.AddCSH_AllCommentBeyond(@2);
  #else
  #endif
    (void)$1;
}

chart:
{
  //Add here what to do for an empty chart
  #ifdef C_S_H_IS_COMPILED
    csh.AddLineBeginToHints();
    csh.hintStatus = HINT_READY;
    csh.hintSource = EHintSourceType::LINE_START;
    csh.hintsForcedOnly = true;
  #else
    //no action for empty file
  #endif
}
      | top_level_instrlist
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckLineStartHintBefore(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
#else
    chart.AddElements($1);
  #endif
};

/* This instruction list allows an extra closing brace and provides an error msg for it */
top_level_instrlist: instrlist
      | instrlist TOK_CCBRACKET
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_Error(@2, "Closing brace missing its opening pair.");
  #else
    $$ = $1;
    chart.Error.Error(@2, "Unexpected '}'.");
  #endif
}
      | instrlist TOK_CCBRACKET top_level_instrlist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_Error(@2, "Closing brace missing its opening pair.");
  #else
    //Merge $3 into $1
    ($1)->splice(($1)->end(), *($3));
    delete ($3);
    $$ = $1;
    chart.Error.Error(@3, "Unexpected '}'.");
  #endif
};


braced_instrlist: scope_open instrlist scope_close
{
  #ifdef C_S_H_IS_COMPILED
    csh.BracePairs.push_back(@$);
  #else
    if ($3) ($2)->Append($3); //Append any potential CommandNumbering
    $$ = $2;
  #endif
}
      | scope_open scope_close
{
  #ifdef C_S_H_IS_COMPILED
    csh.BracePairs.push_back(@$);
  #else
    $$ = new FElementList;
    //scope_close should not return here with a CommandNumbering
    //but just in case
    if ($2)
        delete($2);
  #endif
}
      | scope_open instrlist error scope_close
{
  #ifdef C_S_H_IS_COMPILED
    csh.BracePairs.push_back(@$);
    csh.AddCSH_Error(@3, "Could not recognize this as a valid line.");
  #else
    if ($4) ($2)->Append($4);
    $$ = $2;
    chart.Error.Error(@3, "Syntax error.");
  #endif
    yyerrok;
}
      | scope_open instrlist error TOK_EOF
{
  #ifdef C_S_H_IS_COMPILED
	csh.AddOpenBracePair(@$);
    csh.AddCSH_Error(@3, "Could not recognize this as a valid line.");
  #else
    $$ = $2;
    //Do not pop context, as the missing scope_close would have done
    chart.Error.Error(@3, "Missing '}'.");
    chart.Error.Error(@1, @3, "Here is the corresponding '{'.");
  #endif
}
      | scope_open instrlist TOK_EOF
{
  #ifdef C_S_H_IS_COMPILED
	csh.AddOpenBracePair(@$);
    csh.AddCSH_ErrorAfter(@2, "Missing a closing brace ('}').");
  #else
    $$ = $2;
    //Do not pop context, as the missing scope_close would have done
    chart.Error.Error(@2.after(), "Missing '}'.");
    chart.Error.Error(@1, @2.after(), "Here is the corresponding '{'.");
  #endif
}
      | scope_open TOK_EOF
{
  #ifdef C_S_H_IS_COMPILED
	csh.AddOpenBracePair(@$);
    csh.AddCSH_ErrorAfter(@1, "Missing a closing brace ('}').");
  #else
    $$ = nullptr;
    //Do not pop context, as the missing scope_close would have done
    chart.Error.Error(@1.after(), "Missing a corresponding '}'.");
  #endif
}
      | scope_open instrlist TOK_BYE
{
  #ifdef C_S_H_IS_COMPILED
    csh.BracePairs.push_back(@$);
    csh.AddCSH_Error(@3, "The command 'bye' can only be used at the top level.");
  #else
    $$ = $2;
    //Do not pop context, as the missing scope_close would have done
    chart.Error.Error(@3, "The command 'bye' can not be used between curly braces '{' and '}'.");
    chart.Error.Error(@1, @3, "Here is the opening '{'.");
  #endif
  (void)$3;
}
      | scope_open TOK_BYE
{
  #ifdef C_S_H_IS_COMPILED
    csh.BracePairs.push_back(@$);
    csh.AddCSH_Error(@2, "The command 'bye' can only be used at the top level and not inside curly braces '{' and '}'.");
  #else
    $$ = nullptr;
    //Do not pop context, as the missing scope_close would have done
    chart.Error.Error(@2, "The command 'bye' can not be used between curly braces '{' and '}'.");
  #endif
  (void)$2;
};


instrlist:    instr_with_semicolon
{
  #ifndef C_S_H_IS_COMPILED
    if ($1)
        $$ = (new FElementList)->Append($1); /* New list */
    else
        $$ = new FElementList;
  #endif
}
      | instrlist instr_with_semicolon
{
  #ifndef C_S_H_IS_COMPILED
    if ($2) ($1)->Append($2);     /* Add to existing list */
    $$ = ($1);
  #endif
}
      | several_instructions
      | instrlist several_instructions
{
  #ifndef C_S_H_IS_COMPILED
    //TODO: Add a nested instructionlist to another instructionslist
    if ($2) ($1)->Append($2);     /* Add to existing list */
    $$ = ($1);
  #endif
};

several_instructions: optlist_with_semicolon
      | braced_instrlist
{
  #ifdef C_S_H_IS_COMPILED
    //Standalone braced instruction lists are instructions - for the purpose of indentation
    csh.AddInstruction(@1);
  #endif
  $$ = $1;
};

//Also instructions ending in closing brace (and needing no semicolon)
instr_with_semicolon: instr_needs_semicolon
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstruction(@$);
    csh.AddCSH_ErrorAfter(@1, "Missing semicolon (';').");
  #else
    //if ($1) ($1)->SetLineEnd(@$);
    $$=$1;
    chart.Error.Error(@1.after(), "Missing a semicolon ';'.");
  #endif
}
      | instr_needs_semicolon TOK_SEMICOLON
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstruction(@$);
    csh.AddCSH(@2, COLOR_SEMICOLON);
    if (csh.CheckLineStartHintAfter(@2)) {
       csh.AddLineBeginToHints();
       csh.hintStatus = HINT_READY;
    }
  #else
    //if ($1) ($1)->SetLineEnd(@$);
    $$=$1;
  #endif
}
/* Dummy rule to force a lookahead, so that previous rule has a valid
 * lookahead location in yylloc */
      | instr_needs_semicolon TOK_SEMICOLON TOK__NEVER__HAPPENS
      | instr_needs_semicolon error TOK_SEMICOLON TOK__NEVER__HAPPENS
      | TOK_SEMICOLON
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstruction(@$);
    csh.AddCSH(@1, COLOR_SEMICOLON);
    if (csh.CheckLineStartHintAfter(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    $$=nullptr;
  #endif
}
      | instr_needs_semicolon error TOK_SEMICOLON
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstruction(@$);
    csh.AddCSH(@3, COLOR_SEMICOLON);
    csh.AddCSH_Error(@2, "I am not sure what is coming here.");
    if (csh.CheckLineStartHintAfter(@3)) {
       csh.AddLineBeginToHints();
       csh.hintStatus = HINT_READY;
    }
  #else
    //if ($1) ($1)->SetLineEnd(@$);
    $$=$1;
    chart.Error.Error(@1.after(), "Missing a semicolon ';'.");
    chart.Error.Error(@1, @1.after(), "Here is the beginning of the command as I understood it.");
  #endif
}
      | branch_with_semicolon
{
    $$ = $1; //to suppress warning of incompatible types.
}
      | actual_instr
{
  #ifdef C_S_H_IS_COMPILED
  #else
    $$ = $1;
  #endif
}
      | entity_string actual_instr
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_MARKERNAME);
  #else
    $$ = $2;
    if ($1 && $$)
        $$->AddRefName($1);
  #endif
    (void)$1;
}
      | entity_string error actual_instr
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_MARKERNAME);
    csh.AddCSH_Error(@2, "I am not sure what is this.");
  #else
    $$ = $3;
    if ($1 && $$)
        $$->AddRefName($1);
    chart.Error.Error(@2, "Syntax error here.");
  #endif
    (void)$1;
}
      | goto_with_semicolon;



optlist_with_semicolon: optlist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstruction(@$);
    csh.AddCSH_ErrorAfter(@1, "Missing semicolon (';').");
  #else
    $$=$1;
    chart.Error.Error(@1.after(), "Missing a semicolon ';'.");
  #endif
}
      | optlist TOK_SEMICOLON
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstruction(@$);
    csh.AddCSH(@2, COLOR_SEMICOLON);
    if (csh.CheckLineStartHintAfter(@2)) {
       csh.AddLineBeginToHints();
       csh.hintStatus = HINT_READY;
    }
  #else
    $$=$1;
  #endif
}
/* Dummy rule to force a lookahead, so that previous rule has a valid
 * lookahead location in yylloc */
      | optlist TOK_SEMICOLON TOK__NEVER__HAPPENS
      | optlist error TOK_SEMICOLON TOK__NEVER__HAPPENS
      | optlist error TOK_SEMICOLON
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstruction(@$);
    csh.AddCSH(@3, COLOR_SEMICOLON);
    csh.AddCSH_Error(@2, "I am not sure what is coming here.");
    if (csh.CheckLineStartHintAfter(@3)) {
       csh.AddLineBeginToHints();
       csh.hintStatus = HINT_READY;
    }
  #else
    $$=$1;
    chart.Error.Error(@1.after(), "Missing a semicolon ';' after option(s).");
    chart.Error.Error(@1, @1.after(), "Here is the beginning of the option list as I understood it.");
  #endif
};



instr_needs_semicolon:         TOK_COMMAND_DEFSHAPE shapedef
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.SkipContent())
        csh.AddCSH_Error(@1, "Cannot define designs inside a procedure.");
    else
        csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    if (chart.SkipContent())
        chart.Error.Error(@1, "Cannot define designs inside a procedure.");
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_COMMAND_DEFSHAPE
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.SkipContent())
        csh.AddCSH_Error(@1, "Cannot define designs inside a procedure.");
    else {
        csh.AddCSH(@1, COLOR_KEYWORD);
        csh.AddCSH_ErrorAfter(@$, "Missing shape name and definition.");
    }
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    if (chart.SkipContent())
        chart.Error.Error(@1, "Cannot define shapes inside a procedure.");
    else
        chart.Error.Error(@1.after(), "Missing shape name and definition.");
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_COMMAND_DEFCOLOR colordeflist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@1, @2, EHintSourceType::ATTR_VALUE)) {
        csh.AddColorValuesToHints(true);
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_COMMAND_DEFCOLOR
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@1, EHintSourceType::ATTR_VALUE)) {
        csh.AddColorValuesToHints(true);
        csh.hintStatus = HINT_READY;
	}
	csh.AddCSH_ErrorAfter(@$, "Missing color name to (re-)define.");
  #else
    chart.Error.Error(@$.after(), "Missing a color name to (re-)define.");
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_COMMAND_DEFSTYLE styledeflist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@1, @2, EHintSourceType::ATTR_VALUE)) {
        csh.AddStylesToHints(true, true);
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_COMMAND_DEFSTYLE
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@1, EHintSourceType::ATTR_VALUE)) {
        csh.AddStylesToHints(true, true);
        csh.hintStatus = HINT_READY;
	}
	csh.AddCSH_ErrorAfter(@$, "Missing style name to (re-)define.");
  #else
    chart.Error.Error(@$.after(), "Missing a style name to (re-)define.");
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_COMMAND_DEFDESIGN designdef
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.SkipContent())
        csh.AddCSH_Error(@1, "Cannot define designs inside a procedure.");
    else
        csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    if (chart.SkipContent())
        chart.Error.Error(@1, "Cannot define designs inside a procedure.");
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_COMMAND_DEFDESIGN
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.SkipContent())
        csh.AddCSH_Error(@1, "Cannot define designs inside a procedure.");
    else {
        csh.AddCSH(@1, COLOR_KEYWORD);
        csh.AddCSH_ErrorAfter(@$, "Missing design name to (re-)define.");
    }
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    if (chart.SkipContent())
        chart.Error.Error(@1, "Cannot define designs inside a procedure.");
    else
        chart.Error.Error(@$.after(), "Missing a design name to (re-)define.");
    $$ = nullptr;
  #endif
    (void)$1;
};

step_types: TOK_STEP
{
  #ifdef C_S_H_IS_COMPILED
     $$ = true;
     (void)$1;
  #else
     $$ = {Step::BOX, $1, true};
  #endif
}
      | TOK_IF
{
  #ifdef C_S_H_IS_COMPILED
     $$ = true;
     (void)$1;
  #else
     $$ = {Step::IF, $1, true};
  #endif
}
      | TOK_START
{
  #ifdef C_S_H_IS_COMPILED
     $$ = false;
     (void)$1;
  #else
     $$ = {Step::START, $1, false};
  #endif
}
      | TOK_STOP
{
  #ifdef C_S_H_IS_COMPILED
     $$ = false;
     (void)$1;
  #else
     $$ = {Step::STOP, $1, false};
  #endif
};


actual_instr_needs_semicolon:    step_types
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if ($1)
        csh.AddCSH_ErrorAfter(@$, "Missing label.");
  #else
    if ($1.must_have_label) {
        $$ = nullptr;
        chart.Error.Error(@$.after(), "Missing label. Ignoring it.");
    } else {
        $$ = new Step(&chart, $1.type);
        $$->SetLineEnd(@$);
        AttributeList *list = new AttributeList;
        list->Append(std::make_unique<Attribute>("label", $1.keyword, @1, @1, true));
        $$->AddAttributeList(list); //destroys list
    }
  #endif
}
      | step_types full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        Step::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        Step::AttributeValues(csh.hintAttrName, csh);
  #else
    $$ = new Step(&chart, $1.type);
    $$->SetLineEnd(@$);
    $$->AddAttributeList($2);
  #endif
}
      | step_types error full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_Error(@2, "I am not sure what is this.");
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @3))
        Step::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @3))
        Step::AttributeValues(csh.hintAttrName, csh);
  #else
    $$ = new Step(&chart, $1.type);
    $$->SetLineEnd(@$);
    $$->AddAttributeList($3);
    chart.Error.Error(@2, "Syntax error here.");
  #endif
}
      | TOK_REPEAT
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@$, "Missing steps to repeat.");
  #else
    $$ = nullptr;
    chart.Error.Error(@$.after(), "Missing steps to repeat. Ignoring 'repeat'.");
  #endif
    (void)$1;
}
      | TOK_REPEAT full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@$, "Missing steps to repeat.");
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        Arrow::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        Arrow::AttributeValues(csh.hintAttrName, csh);
  #else
    $$ = nullptr;
    chart.Error.Error(@$.after(), "Missing steps to repeat. Ignoring 'repeat'.");
    delete $2;
  #endif
    (void)$1;
};

actual_instr: actual_instr_needs_semicolon
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstruction(@$);
    csh.AddCSH_ErrorAfter(@1, "Missing semicolon (';').");
  #else
    //if ($1) ($1)->SetLineEnd(@$);
    $$=$1;
    chart.Error.Error(@1.after(), "Missing a semicolon ';'.");
  #endif
}
      | actual_instr_needs_semicolon TOK_SEMICOLON
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstruction(@$);
    csh.AddCSH(@2, COLOR_SEMICOLON);
    if (csh.CheckLineStartHintAfter(@2)) {
       csh.AddLineBeginToHints();
       csh.hintStatus = HINT_READY;
    }
  #else
    //if ($1) ($1)->SetLineEnd(@$);
    $$=$1;
  #endif
}
      | TOK_REPEAT full_attrlist_with_label braced_instrlist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        Arrow::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        Arrow::AttributeValues(csh.hintAttrName, csh);
  #else
    $$ = new Repeat(&chart, $3);
    $$->SetLineEnd(@1 + @2);
    $$->AddAttributeList($2);
  #endif
    (void)$1;
}
      | TOK_REPEAT braced_instrlist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
  #else
    $$ = new Repeat(&chart, $2);
    $$->SetLineEnd(@1);
    $$->AddAttributeList(nullptr);
  #endif
    (void)$1;
};

goto:      TOK_GOTO
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@$, "Missing step name.");
  #else
    $$ = nullptr;
    chart.Error.Error(@$.after(), "Missing step name. Ignoring 'goto'.");
  #endif
    (void)$1;
}
      | TOK_GOTO entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_MARKERNAME);
  #else
    $$ = new Goto(&chart, $2);
    $$->SetLineEnd(@$);
    $$->AddAttributeList(nullptr);
  #endif
    (void)$1;
    (void)$2;
}
      | TOK_GOTO full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@1, "Missing step name.");
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        Arrow::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        Arrow::AttributeValues(csh.hintAttrName, csh);
  #else
    $$ = nullptr;
    chart.Error.Error(@$.after(), "Missing step name. Ignoring 'goto'.");
    delete $2;
  #endif
    (void)$1;
}
      | TOK_GOTO entity_string full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_MARKERNAME);
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @3))
        Arrow::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @3))
        Arrow::AttributeValues(csh.hintAttrName, csh);
  #else
    $$ = new Goto(&chart, $2);
    $$->SetLineEnd(@$);
    $$->AddAttributeList($3);
  #endif
    (void)$1;
    (void)$2;
};

//needs no semicolon
goto_with_semicolon: goto
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstruction(@$);
    csh.AddCSH_ErrorAfter(@1, "Missing semicolon (';').");
  #else
    //if ($1) ($1)->SetLineEnd(@$);
    $$=$1;
    chart.Error.Error(@1.after(), "Missing a semicolon ';'.");
  #endif
}
      | goto TOK_SEMICOLON
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstruction(@$);
    csh.AddCSH(@2, COLOR_SEMICOLON);
    if (csh.CheckLineStartHintAfter(@2)) {
       csh.AddLineBeginToHints();
       csh.hintStatus = HINT_READY;
    }
  #else
    //if ($1) ($1)->SetLineEnd(@$);
    $$=$1;
  #endif
};


//needs no semicolon
branch_with_semicolon:       branch_no_content
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstruction(@$);
    csh.AddCSH_ErrorAfter(@1, "Missing semicolon (';').");
  #else
    //if ($1) ($1)->SetLineEnd(@$);
    $$=$1;
    chart.Error.Error(@1.after(), "Missing a semicolon ';'.");
  #endif
}
      | branch_no_content TOK_SEMICOLON
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstruction(@$);
    csh.AddCSH(@2, COLOR_SEMICOLON);
    if (csh.CheckLineStartHintAfter(@2)) {
       csh.AddLineBeginToHints();
       csh.hintStatus = HINT_READY;
    }
  #else
    //if ($1) ($1)->SetLineEnd(@$);
    $$=$1;
  #endif
}
      | branch_no_content goto_with_semicolon
{
  #ifdef C_S_H_IS_COMPILED
  #else
    $$ = $1->AddStep($2);
  #endif
}
      | branch_no_content braced_instrlist
{
  #ifdef C_S_H_IS_COMPILED
  #else
    $$ = $1->AddSteps($2);
  #endif
};

branch_keyword: TOK_BRANCH
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
  #else
    $$.init();
    (void)$1;
  #endif
}
      | TOK_THEN
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
  #else
    $$.set("yes");
    (void)$1;
  #endif
}
      | TOK_YES
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
  #else
    $$.set("yes");
    (void)$1;
  #endif
}
      | TOK_ELSE
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
  #else
    $$.set("no");
    (void)$1;
  #endif
}
      | TOK_NO
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
  #else
    $$.set("no");
    (void)$1;
  #endif
};

branch_no_content:         branch_keyword
{
  #ifdef C_S_H_IS_COMPILED
  #else
    $$ = new Branch(&chart);
    $$->SetLineEnd(@$);
    $$->AddAttributeList(nullptr);
  #endif
    (void)$1;
}
      | branch_keyword entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_ATTRVALUE);
  #else
    $$ = new Branch(&chart);
    $$->AddLabel($2);
    $$->SetLineEnd(@$);
    $$->AddAttributeList(nullptr);
  #endif
    (void)$1;
    (void)$2;
}
      | branch_keyword full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        Arrow::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        Arrow::AttributeValues(csh.hintAttrName, csh);
  #else
    $$ = new Branch(&chart);
    $$->AddAttributeList($2);
    $$->SetLineEnd(@$);
  #endif
    (void)$1;
}
      | branch_keyword entity_string full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_ATTRVALUE);
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @3))
        Arrow::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @3))
        Arrow::AttributeValues(csh.hintAttrName, csh);
  #else
    $$ = new Branch(&chart);
    $$->AddLabel($2)->AddAttributeList($3);
    $$->SetLineEnd(@$);
  #endif
    (void)$1;
    (void)$2;
};


optlist:     opt
{
  #ifndef C_S_H_IS_COMPILED
    if ($1)
        $$ = (new FElementList)->Append($1); /* New list */
    else
        $$ = new FElementList;
  #endif
}
      | optlist TOK_COMMA opt
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    if (csh.CheckHintBetween(@2, @3, EHintSourceType::ATTR_NAME)) {
        csh.AddOptionsToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
  #ifndef C_S_H_IS_COMPILED
    if ($3) ($1)->Append($3);     /* Add to existing list */
    $$ = ($1);
  #endif
  #endif
}
      | optlist TOK_COMMA
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    if (csh.CheckHintAfter(@2, EHintSourceType::ATTR_NAME)) {
        csh.AddOptionsToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = $1;
    chart.Error.Error(@2.after(), "Expecting an option here.");
  #endif
}
      | optlist TOK_COMMA TOK__NEVER__HAPPENS
      | optlist TOK_COMMA error
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    if (csh.CheckHintBetween(@2, @3, EHintSourceType::ATTR_NAME)) {
        csh.AddOptionsToHints();
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH_Error(@3, "An option expected here.");
  #else
    $$ = $1;
    chart.Error.Error(@3, "I am not sure what is coming here.");
  #endif
};


opt:         entity_string TOK_EQUAL TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_AttrName(@1, $1, COLOR_OPTIONNAME);
    csh.AddCSH(@2, COLOR_EQUAL);
    csh.AddCSH(@3, COLOR_ATTRVALUE);
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddOptionsToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE, $1)) {
        FlowChart::AttributeValues($1, csh);
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = chart.AddAttribute(Attribute($1, $3, @1, @3));
  #endif
    (void)$1;
    (void)$3;
}
      | entity_string TOK_EQUAL string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_AttrName(@1, $1, COLOR_OPTIONNAME);
    csh.AddCSH(@2, COLOR_EQUAL);
    csh.AddCSH_AttrValue_CheckAndAddEscapeHint(@3, $3, $1);
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddOptionsToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE, $1)) {
        FlowChart::AttributeValues($1, csh);
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = chart.AddAttribute(Attribute($1, $3, @1, @3));
  #endif
    (void)$1;
    (void)$3;
}
      | entity_string TOK_EQUAL
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_AttrName(@1, $1, COLOR_OPTIONNAME);
    csh.AddCSH(@2, COLOR_EQUAL);
    csh.AddCSH_ErrorAfter(@2, "Missing option value.");
    if (csh.CheckHintAfter(@2, EHintSourceType::ATTR_VALUE, $1)) {
        FlowChart::AttributeValues($1, csh);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddOptionsToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@2.after(), "Missing option value.");
    $$ = nullptr;
  #endif
    (void)$1;
};

styledeflist: styledef
      | styledeflist TOK_COMMA styledef
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    if (csh.CheckHintBetween(@2, @3, EHintSourceType::ATTR_VALUE)) {
        csh.AddStylesToHints(true, true);
        csh.hintStatus = HINT_READY;
    }
  #endif
}
      | styledeflist TOK_COMMA
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    if (csh.CheckHintAfter(@2, EHintSourceType::ATTR_NAME)) {
        csh.AddStylesToHints(true, true);
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@$.after(), "Missing style definition here.", "Try just removing the comma.");
#endif
};

styledef: stylenamelist full_attrlist
{
  #ifdef C_S_H_IS_COMPILED
    for (auto &str : *($1))
        if (csh.ForbiddenStyles.find(str) == csh.ForbiddenStyles.end())
            csh.CurrentContext().StyleNames.insert(str);
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        FlowStyle().AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        FlowStyle().AttributeValues(csh.hintAttrName, csh);
  #else
	chart.AddAttributeListToStyleList($2, $1); //deletes $2, as well
  #endif
    delete($1);
}
      | stylenamelist
{
  #ifdef C_S_H_IS_COMPILED
	csh.AddCSH_ErrorAfter(@$, "Missing attribute definitions in square brackets ('[' and ']').");
  #else
    chart.Error.Error(@$.after(), "Missing attribute definitions in square brackets ('[' and ']').");
  #endif
    delete($1);
};

stylenamelist: string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_STYLENAME);
    $$ = new std::vector<string>;
    if ($1.view() == "emphasis")
        ($$)->push_back("box");
    else if ($1.view() == "emptyemphasis")
        ($$)->push_back("emptybox");
    else ($$)->push_back($1.str());
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
	csh.AddStylesToHints(true, true);
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = new std::vector<string>;
    if ($1)
        ($$)->emplace_back($1);
  #endif
    (void)$1;
}
      | stylenamelist TOK_COMMA
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
	csh.AddCSH_ErrorAfter(@2, "Missing a style name to (re-)define.");
    if (csh.CheckHintAfter(@1, EHintSourceType::ATTR_NAME)) {
		csh.AddStylesToHints(true, true);
        csh.hintStatus = HINT_READY;
    }
	$$ = $1;
  #else
    $$ = $1;
    chart.Error.Error(@$.after(), "Missing a style name to (re-)define.");
  #endif
};
      | stylenamelist TOK_COMMA string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    csh.AddCSH(@3, COLOR_STYLENAME);
    $$ = $1;
    if ($3.view() == "emphasis")
        ($$)->push_back("box");
    else if ($3.view() == "emptyemphasis")
        ($$)->push_back("emptybox");
    else ($$)->push_back($3.str());
    if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_NAME)) {
		csh.AddStylesToHints(true, true);
        csh.hintStatus = HINT_READY;
    }
  #else
    if ($3)
        ($1)->emplace_back($3);
    $$ = $1;
  #endif
    (void)$3;
};

shapedef: entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_ATTRVALUE);
    csh.AddCSH_ErrorAfter(@$, "Here should come a shape definition beginning with '{'. Ignoring this malformed shape definition for '"+string($1) +"'.");
  #else
    chart.Error.Error(@$.after(), "Here should come a shape definition beginning with '{'. Ignoring this malformed shape definition for '"+string($1) +"'.");
  #endif
  (void)$1;
}
		| entity_string TOK_OCBRACKET
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddOpenBracePair(@2);
    csh.AddCSH(@1, COLOR_ATTRVALUE);
    csh.AddCSH(@2, COLOR_BRACE);
    csh.AddCSH_ErrorAfter(@$, "Here should come a shape definition beginning with 'T', 'H', 'M', 'L', 'C', 'S', 'P' or 'E'. Ignoring this malformed shape definition for '"+string($1) +"'.");
  #else
    chart.Error.Error(@$.after(), "Here should come a shape definition beginning with 'T', 'H', 'M', 'L', 'C', 'S', 'P' or 'E'. Ignoring this malformed shape definition for '"+string($1)+"'.");
  #endif
  (void)$1;
}
		| entity_string TOK_OCBRACKET shapedeflist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddOpenBracePair(@2+@3);
    csh.AddCSH(@1, COLOR_ATTRVALUE);
    csh.AddCSH(@2, COLOR_BRACE);
    csh.AddCSH_ErrorAfter(@3, "Missing a closing brace ('}').");
	csh.AddShapeName($1);
  #else
    chart.Error.Error(@3.after(), "Missing '}'.");
    chart.Error.Error(@2, @3.after(), "Here is the corresponding '{'.");
    if ($3) {
        if (!chart.SkipContent())
            chart.Shapes.Add(std::string($1), @1, chart.file_url, chart.file_info, std::move(*$3), chart.Error);
	delete $3;
    }
  #endif
  (void)$1;
}
		| entity_string TOK_OCBRACKET shapedeflist TOK_CCBRACKET
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_ATTRVALUE);
    csh.AddCSH(@2, COLOR_BRACE);
    csh.AddCSH(@4, COLOR_BRACE);
	csh.AddShapeName($1);
    csh.BracePairs.push_back(@2+@4);
  #else
    if ($3) {
        if (!chart.SkipContent())
            chart.Shapes.Add(std::string($1), @1, chart.file_url, chart.file_info, std::move(*$3), chart.Error);
	delete $3;
    }
  #endif
  (void)$1;
}
		| entity_string TOK_OCBRACKET shapedeflist error TOK_CCBRACKET
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_ATTRVALUE);
    csh.AddCSH(@2, COLOR_BRACE);
    csh.AddCSH(@5, COLOR_BRACE);
    csh.BracePairs.push_back(@2+@5);
	csh.AddShapeName($1);
    csh.AddCSH_Error(@4, "Only numbers can come after shape commands.");
  #else
    if ($3) {
        if (!chart.SkipContent())
            chart.Shapes.Add(std::string($1), @1, chart.file_url, chart.file_info, std::move(*$3), chart.Error);
	delete $3;
    }
  #endif
  (void)$1;
};

shapedeflist: shapeline TOK_SEMICOLON
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SEMICOLON);
  #else
    $$ = new Shape;
	if ($1) {
		($$)->Add(std::move(*($1)));
		delete $1;
	}
  #endif
}
      | error shapeline TOK_SEMICOLON
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_Error(@1, "I do not understand this.");
    csh.AddCSH(@3, COLOR_SEMICOLON);
#else
    $$ = new Shape;
	if ($2) {
		($$)->Add(std::move(*($2)));
		delete $2;
	}
  #endif
}
      | shapedeflist shapeline TOK_SEMICOLON
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@3, COLOR_SEMICOLON);
  #else
	if ($2) {
		($1)->Add(std::move(*($2)));
		delete $2;
	}
    $$ = $1;
  #endif
}
      | shapedeflist error
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_Error(@2, "Only numbers can come after shape commands.");
  #else
    $$ = $1;
  #endif
};

shapeline: TOK_SHAPE_COMMAND
{
    const int num_args = 0;
	const int should_args = ShapeElement::GetNumArgs($1);
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
	if (should_args != num_args)
		csh.AddCSH_ErrorAfter(@$, ShapeElement::ErrorMsg($1, num_args));
  #else
	$$ = nullptr;
	if (should_args != num_args)
		chart.Error.Error(@$.after(), ShapeElement::ErrorMsg($1, num_args).append(" Ignoring line."));
	else
	    $$ = new ShapeElement($1);
  #endif
}
      | TOK_SHAPE_COMMAND TOK_NUMBER
{
    const int num_args = 1;
	const int should_args = ShapeElement::GetNumArgs($1);
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
	if (should_args != num_args) {
		string msg = ShapeElement::ErrorMsg($1, num_args);
	    switch (ShapeElement::GetNumArgs($1)) {
		case 0:  csh.AddCSH_Error(@2, std::move(msg)); break;
		default: csh.AddCSH_ErrorAfter(@$, std::move(msg)); break;
		}
	} else if ($1==ShapeElement::SECTION_BG && ($2.len!=1 || $2[0]<'0' || $2[0]>'3'))
		csh.AddCSH_Error(@2, "S (section) commands require an integer between 0 and 3.");
  #else
	$$ = nullptr;
	const double a = to_double($2);
	if (should_args > num_args)
		chart.Error.Error(@2.after(), ShapeElement::ErrorMsg($1, num_args).append(" Ignoring line."));
	else if ($1==ShapeElement::SECTION_BG && (a!=0 && a!=1 && a!=2 && a!=3))
		chart.Error.Error(@2, "S (section) commands require an integer between 0 and 3. Ignoring line.");
	else if ($1==ShapeElement::SECTION_BG)
	    $$ = new ShapeElement(ShapeElement::Type($1 + unsigned(a)));
	else
		$$ = new ShapeElement($1, a);
  #endif
  (void)$2;
}
      | TOK_SHAPE_COMMAND TOK_NUMBER TOK_NUMBER
{
    const int num_args = 2;
	const int should_args = ShapeElement::GetNumArgs($1);
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
	if (should_args != num_args) {
		string msg = ShapeElement::ErrorMsg($1, num_args);
	    switch (ShapeElement::GetNumArgs($1)) {
		case 0:  csh.AddCSH_Error(@2 + @3, std::move(msg)); break;
		case 1:  csh.AddCSH_Error(@3, std::move(msg)); break;
		default: csh.AddCSH_ErrorAfter(@$, std::move(msg)); break;
		}
	}
  #else
	$$ = nullptr;
	if (should_args > num_args)
		chart.Error.Error(@$.after(), ShapeElement::ErrorMsg($1, num_args).append(" Ignoring line."));
	else
		$$ = new ShapeElement($1, to_double($2), to_double($3));
  #endif
  (void)$2;
  (void)$3;
}
      | TOK_SHAPE_COMMAND TOK_NUMBER TOK_NUMBER entity_string
{
    const int num_args = 3;
	  const int should_args = ShapeElement::GetNumArgs($1);
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
	  if (should_args != num_args) {
        std::string msg = ShapeElement::ErrorMsg($1, num_args);
        switch (ShapeElement::GetNumArgs($1)) {
        case 0:  csh.AddCSH_Error(@2 + @4, std::move(msg)); break;
        case 1:  csh.AddCSH_Error(@3 + @4, std::move(msg)); break;
        case 2:  csh.AddCSH_Error(@4, std::move(msg)); break;
        default: csh.AddCSH_ErrorAfter(@$, std::move(msg)); break;
        }
  	} else if ($1!=ShapeElement::PORT)
        csh.AddCSH_Error(@4, "You need to specify a number here.");
  #else
  	$$ = nullptr;
	  if (should_args > num_args)
	      chart.Error.Error(@$.after(), ShapeElement::ErrorMsg($1, num_args).append(" Ignoring line."));
   	else if ($1!=ShapeElement::PORT)
	      chart.Error.Error(@4, "Expecting a number here. Ignoring line.");
    else
		    $$ = new ShapeElement($1, to_double($2), to_double($3), to_double($4));
  #endif
  (void)$2;
  (void)$3;
  (void)$4;
}
      | TOK_SHAPE_COMMAND TOK_NUMBER TOK_NUMBER TOK_NUMBER
{
    const int num_args = 3;
	const int should_args = ShapeElement::GetNumArgs($1);
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
	if (should_args != num_args) {
		std::string msg = ShapeElement::ErrorMsg($1, num_args);
	    switch (ShapeElement::GetNumArgs($1)) {
		case 0:  csh.AddCSH_Error(@2 + @4, std::move(msg)); break;
		case 1:  csh.AddCSH_Error(@3 + @4, std::move(msg)); break;
		case 2:  csh.AddCSH_Error(@4, std::move(msg)); break;
		default: csh.AddCSH_ErrorAfter(@$, std::move(msg)); break;
		}
	} else if ($1==ShapeElement::PORT)
        csh.AddCSH_Error(@4, "You need to specify a port name here starting with a letter.");
  #else
	$$ = nullptr;
	if (should_args > num_args)
		chart.Error.Error(@$.after(), ShapeElement::ErrorMsg($1, num_args).append(" Ignoring line."));
	else if ($1==ShapeElement::PORT)
		chart.Error.Error(@4, "Expecting a port name here. Ignoring line.");
    else
		$$ = new ShapeElement($1, to_double($2), to_double($3), to_double($4));
  #endif
  (void)$2;
  (void)$3;
  (void)$4;
}
      | TOK_SHAPE_COMMAND TOK_NUMBER TOK_NUMBER TOK_NUMBER TOK_NUMBER
{
    const int num_args = 4;
	const int should_args = ShapeElement::GetNumArgs($1);
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
	if (should_args != num_args) {
		string msg = ShapeElement::ErrorMsg($1, num_args);
	    switch (ShapeElement::GetNumArgs($1)) {
		case 0:  csh.AddCSH_Error(@2 + @5, std::move(msg)); break;
		case 1:  csh.AddCSH_Error(@3 + @5, std::move(msg)); break;
		case 2:  csh.AddCSH_Error(@4 + @5, std::move(msg)); break;
		case 3:  csh.AddCSH_Error(@5, std::move(msg)); break;
		default: csh.AddCSH_ErrorAfter(@$, std::move(msg)); break;
		}
	}
  #else
	$$ = nullptr;
	if (should_args > num_args)
		chart.Error.Error(@$.after(), ShapeElement::ErrorMsg($1, num_args).append(" Ignoring line."));
	else
		$$ = new ShapeElement($1, to_double($2), to_double($3), to_double($4), to_double($5));
  #endif
  (void)$2;
  (void)$3;
  (void)$4;
  (void)$5;
}
      | TOK_SHAPE_COMMAND TOK_NUMBER TOK_NUMBER TOK_NUMBER TOK_NUMBER TOK_NUMBER
{
    const int num_args = 5;
	const int should_args = ShapeElement::GetNumArgs($1);
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
	if (should_args != num_args) {
		std::string msg = ShapeElement::ErrorMsg($1, num_args);
	    switch (ShapeElement::GetNumArgs($1)) {
		case 0:  csh.AddCSH_Error(@2 + @6, std::move(msg)); break;
		case 1:  csh.AddCSH_Error(@3 + @6, std::move(msg)); break;
		case 2:  csh.AddCSH_Error(@4 + @6, std::move(msg)); break;
		case 3:  csh.AddCSH_Error(@5 + @6, std::move(msg)); break;
		case 4:  csh.AddCSH_Error(@6, std::move(msg)); break;
		default: csh.AddCSH_ErrorAfter(@$, std::move(msg)); break;
		}
	}
  #else
	$$ = nullptr;
	if (should_args > num_args)
		chart.Error.Error(@$.after(), ShapeElement::ErrorMsg($1, num_args).append(" Ignoring line."));
	else
		$$ = new ShapeElement($1, to_double($2), to_double($3), to_double($4), to_double($5), to_double($6));
  #endif
  (void)$2;
  (void)$3;
  (void)$4;
  (void)$5;
  (void)$6;
}
      | TOK_SHAPE_COMMAND TOK_NUMBER TOK_NUMBER TOK_NUMBER TOK_NUMBER TOK_NUMBER TOK_NUMBER
{
    const int num_args = 6;
	const int should_args = ShapeElement::GetNumArgs($1);
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
	if (should_args != num_args) {
		std::string msg = ShapeElement::ErrorMsg($1, num_args);
	    switch (ShapeElement::GetNumArgs($1)) {
		case 0:  csh.AddCSH_Error(@2 + @7, std::move(msg)); break;
		case 1:  csh.AddCSH_Error(@3 + @7, std::move(msg)); break;
		case 2:  csh.AddCSH_Error(@4 + @7, std::move(msg)); break;
		case 3:  csh.AddCSH_Error(@5 + @7, std::move(msg)); break;
		case 4:  csh.AddCSH_Error(@6 + @7, std::move(msg)); break;
		case 5:  csh.AddCSH_Error(@7, std::move(msg)); break;
		default: csh.AddCSH_ErrorAfter(@$, std::move(msg)); break;
		}
	}
  #else
	$$ = nullptr;
	if (should_args > num_args)
		chart.Error.Error(@$.after(), ShapeElement::ErrorMsg($1, num_args).append(" Ignoring line."));
	else
		$$ = new ShapeElement($1, to_double($2), to_double($3), to_double($4), to_double($5), to_double($6), to_double($7));
  #endif
  (void)$2;
  (void)$3;
  (void)$4;
  (void)$5;
  (void)$6;
  (void)$7;
};

colordeflist: colordef
      | colordeflist TOK_COMMA colordef
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_NAME)) {
        csh.AddColorValuesToHints(true);
        csh.hintStatus = HINT_READY;
	}
  #endif
}
      | colordeflist TOK_COMMA
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    if (csh.CheckHintAfter(@2, EHintSourceType::ATTR_NAME)) {
        csh.AddColorValuesToHints(true);
        csh.hintStatus = HINT_READY;
	}
	csh.AddCSH_ErrorAfter(@$, "Missing color name to (re-)define.");
  #else
    chart.Error.Error(@$.after(), "Missing a color name to (re-)define.");
  #endif
};

color_string: TOK_COLORDEF | string;

colordef: alpha_string TOK_EQUAL color_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_COLORNAME);
    csh.AddCSH(@2, COLOR_EQUAL);
    csh.AddCSH(@3, COLOR_COLORDEF);
    ColorType color = csh.CurrentContext().Colors.GetColor($3);
    if (color.type!=ColorType::INVALID)
        csh.CurrentContext().Colors[$1.str()] = color;
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddColorValuesToHints(true);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE)) {
        csh.AddColorValuesToHints(false);
        csh.hintStatus = HINT_READY;
    }
  #else
    if (!chart.SkipContent())
        chart.MyCurrentContext().colors.AddColor($1, $3, chart.Error, @$);
  #endif
    (void)$1;
    (void)$3;
}
      |alpha_string TOK_EQUAL TOK_PLUS_PLUS color_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_COLORNAME);
    csh.AddCSH(@2, COLOR_EQUAL);
    csh.AddCSH(@3, COLOR_COLORDEF);
    csh.AddCSH(@4, COLOR_COLORDEF);
    ColorType color = csh.CurrentContext().Colors.GetColor("++"+string($4));
    if (color.type!=ColorType::INVALID)
        csh.CurrentContext().Colors[$1.str()] = color;
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddColorValuesToHints(true);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@2, @4, EHintSourceType::ATTR_VALUE)) {
        csh.AddColorValuesToHints(false);
        csh.hintStatus = HINT_READY;
    }
  #else
    if (!chart.SkipContent())
        chart.MyCurrentContext().colors.AddColor($1, "++"+string($4), chart.Error, @$);
  #endif
    (void)$1;
    (void)$4;
}
      | alpha_string TOK_EQUAL
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_COLORNAME);
    csh.AddCSH(@2, COLOR_EQUAL);
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddColorValuesToHints(true);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@2, EHintSourceType::ATTR_VALUE)) {
        csh.AddColorValuesToHints(false);
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH_ErrorAfter(@$, "Missing color definition.");
  #else
    chart.Error.Error(@$.after(), "Missing color definition.");
  #endif
    (void)$1;
}
      | alpha_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_COLORNAME);
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddColorValuesToHints(true);
        csh.hintStatus = HINT_READY;
	}
	csh.AddCSH_ErrorAfter(@$, "Missing equal sign ('=') and a color definition.");
  #else
    chart.Error.Error(@$.after(), "Missing equal sign ('=') and a color definition.");
  #endif
    (void)$1;
};



designdef : TOK_STRING scope_open_empty designelementlist TOK_SEMICOLON TOK_CCBRACKET
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_DESIGNNAME);
    csh.AddCSH(@4, COLOR_SEMICOLON);
    csh.AddCSH(@5, COLOR_BRACE);
    csh.BracePairs.push_back(@2+@5);
    if (!csh.SkipContent()) {
        auto &d = csh.CurrentContext().IsFull() ? csh.FullDesigns : csh.PartialDesigns;
        auto i = d.find($1.view());
        if (i == d.end())
            d.emplace($1, csh.CurrentContext());
        else
            i->second += csh.CurrentContext();
    }
    csh.PopContext();
    if ((csh.CheckHintBetween(@2, @3, EHintSourceType::LINE_START) ||
         csh.CheckHintBetween(@4, @5, EHintSourceType::LINE_START)) ) {
        csh.AddDesignLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    if (!chart.SkipContent()) {
        //'scope_open_empty' pushed an empty color & style set onto the stack
        //then designelementlist added color & style definitions, procedures, etc., now we harvest those.
        //This is either a lookup if the design exists or creates a new empty design of this name
        auto i = chart.Designs.emplace(std::piecewise_construct,
                                       std::forward_as_tuple($1),
                                       std::forward_as_tuple(chart.MyCurrentContext().IsFull(),
                                                             EContextParse::NORMAL,
                                                             EContextCreate::EMPTY,
                                                             @2));
        //we apply the content from current context all the same if inserted or existing
        i.first->second.ApplyContextContent(std::move(chart.MyCurrentContext())); //we can move, will pop below
    }
    chart.PopContext();
  #endif
    (void)$1;
}
      |TOK_STRING scope_open_empty designelementlist TOK_SEMICOLON error TOK_CCBRACKET
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_DESIGNNAME);
    csh.AddCSH(@4, COLOR_SEMICOLON);
    csh.AddCSH_Error(@5, "Could not recognize this as part of a design definition.");
    csh.AddCSH(@6, COLOR_BRACE);
    csh.BracePairs.push_back(@2+@6);
    if (!csh.SkipContent()) {
        auto &d = csh.CurrentContext().IsFull() ? csh.FullDesigns : csh.PartialDesigns;
        auto i = d.find($1.view());
        if (i == d.end())
            d.emplace($1, csh.CurrentContext());
        else
            i->second += csh.CurrentContext();
    }
    csh.PopContext();
    if ((csh.CheckHintBetween(@2, @3, EHintSourceType::LINE_START) ||
         csh.CheckHintBetween(@4, @5, EHintSourceType::LINE_START))) {
        csh.AddDesignLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
#else
    if (!chart.SkipContent()) {
        //if closing brace missing, still do the design definition
        //'scope_open_empty' pushed an empty color & style set onto the stack
        //then designelementlist added color & style definitions, procedures, etc., now we harvest those.
        //This is either a lookup if the design exists or creates a new empty design of this name
        auto i = chart.Designs.emplace(std::piecewise_construct,
                                       std::forward_as_tuple($1),
                                       std::forward_as_tuple(chart.MyCurrentContext().IsFull(),
                                                             EContextParse::NORMAL,
                                                             EContextCreate::EMPTY,
                                                             @2));
        //we apply the content from current context all the same if inserted or existing
        i.first->second.ApplyContextContent(std::move(chart.MyCurrentContext())); //we can move, will pop below
    }
    chart.PopContext();
  #endif
};


scope_open_empty: TOK_OCBRACKET
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_BRACE);
    csh.PushContext(true, EContextParse::NORMAL);
  #else
    //push empty color & style sets for design definition
    chart.PushContext(@1, EContextParse::NORMAL, EContextCreate::CLEAR);
  #endif
};

designelementlist: designelement
      | designelementlist TOK_SEMICOLON designelement
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SEMICOLON);
    if (csh.CheckHintBetween(@2, @3, EHintSourceType::LINE_START)) {
        csh.AddDesignLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
#endif
};

designelement: TOK_COMMAND_DEFCOLOR colordeflist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddDesignLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@1, @2, EHintSourceType::ATTR_NAME)) {
        csh.AddColorValuesToHints(true);
        csh.hintStatus = HINT_READY;
    }
  #endif
    (void)$1;
}
      | TOK_COMMAND_DEFCOLOR
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddDesignLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddColorValuesToHints(true);
        csh.hintStatus = HINT_READY;
	}
	csh.AddCSH_ErrorAfter(@$, "Missing color name to (re-)define.");
  #else
    chart.Error.Error(@$.after(), "Missing a color name to (re-)define.");
  #endif
    (void)$1;
}
			  | TOK_COMMAND_DEFSTYLE styledeflist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddDesignLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@1, @2, EHintSourceType::ATTR_NAME)) {
        csh.AddStylesToHints(true, true);
        csh.hintStatus = HINT_READY;
	}
  #endif
    (void)$1;
}
      | TOK_COMMAND_DEFSTYLE
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddDesignLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddStylesToHints(true, true);
        csh.hintStatus = HINT_READY;
	}
	csh.AddCSH_ErrorAfter(@$, "Missing style name to (re-)define.");
  #else
    chart.Error.Error(@$.after(), "Missing a style name to (re-)define.");
  #endif
    (void)$1;
}
      | designoptlist;

designoptlist: designopt
      | designoptlist TOK_COMMA designopt
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    if (csh.CheckHintBetween(@2, @3, EHintSourceType::ATTR_NAME)) {
        csh.AddDesignOptionsToHints();
        csh.hintStatus = HINT_READY;
    }
  #endif
}
      | designoptlist TOK_COMMA
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    if (csh.CheckHintAfter(@2, EHintSourceType::ATTR_NAME)) {
        csh.AddDesignOptionsToHints();
        csh.hintStatus = HINT_READY;
    }
  #endif
}
      | designoptlist error
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_Error(@2, "Extra stuff after design options. Maybe missing a comma?");
  #endif
};

designopt:         entity_string TOK_EQUAL TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_AttrName(@1, $1, COLOR_OPTIONNAME);
    csh.AddCSH(@2, COLOR_EQUAL);
    csh.AddCSH(@3, COLOR_ATTRVALUE);
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddDesignOptionsToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE, $1)) {
        FlowChart::AttributeValues($1, csh);
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.AddDesignAttribute(Attribute($1, $3, @$, @3));
  #endif
    (void)$1;
    (void)$3;
}
      | entity_string TOK_EQUAL string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_AttrName(@1, $1, COLOR_OPTIONNAME);
    csh.AddCSH(@2, COLOR_EQUAL);
    csh.AddCSH(@3, COLOR_ATTRVALUE);
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddDesignOptionsToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE, $1)) {
        FlowChart::AttributeValues($1, csh);
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.AddDesignAttribute(Attribute($1, $3, @1, @3));
  #endif
    (void)$1;
    (void)$3;
}
      | entity_string TOK_EQUAL
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_AttrName(@1, $1, COLOR_OPTIONNAME);
    csh.AddCSH(@2, COLOR_EQUAL);
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddDesignOptionsToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@2, EHintSourceType::ATTR_VALUE, $1)) {
        FlowChart::AttributeValues($1, csh);
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@2.after(), "Missing option value. Ignoring this.");
#endif
    (void)$1;
};

full_attrlist_with_label: TOK_COLON_STRING
{
  #ifdef C_S_H_IS_COMPILED
  #else
        $$ = (new AttributeList)->Append(std::make_unique<Attribute>("label", $1, @$, @$.IncStartCol(), true));
  #endif
    (void)$1;
}
      | TOK_COLON_STRING full_attrlist
{
  #ifdef C_S_H_IS_COMPILED
  #else
        $$ = ($2)->Prepend(std::make_unique<Attribute>("label", $1, @1, @1.IncStartCol(), true));
  #endif
    (void)$1;
}
      | full_attrlist TOK_COLON_STRING full_attrlist
{
  #ifdef C_S_H_IS_COMPILED
  #else
        $$ = ($1)->Append(std::make_unique<Attribute>("label", $2, @2, @2.IncStartCol(), true));
        //Merge $3 at the end of $1
        ($1)->splice(($1)->end(), *($3));
        delete ($3); //empty list now
        $$ = $1;
  #endif
    (void)$2;
}
      | full_attrlist TOK_COLON_STRING
{
  #ifdef C_S_H_IS_COMPILED
  #else
        $$ = ($1)->Append(std::make_unique<Attribute>("label", $2, @2, @2.IncStartCol(), true));
  #endif
    (void)$2;
}
      | full_attrlist;


full_attrlist: TOK_OSBRACKET TOK_CSBRACKET
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_BRACKET);
    csh.AddCSH(@2, COLOR_BRACKET);
	csh.SqBracketPairs.push_back(@$);
    csh.CheckHintBetween(@1, @2, EHintSourceType::ATTR_NAME);
  #else
    $$ = new AttributeList;
  #endif
}
      | TOK_OSBRACKET attrlist TOK_CSBRACKET
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_BRACKET);
	csh.AddCSH(@3, COLOR_BRACKET);
	csh.SqBracketPairs.push_back(@$);
	csh.CheckHintBetween(@1, @2, EHintSourceType::ATTR_NAME);
  #else
    $$ = $2;
  #endif
}
      | TOK_OSBRACKET attrlist error TOK_CSBRACKET
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_BRACKET);
    csh.AddCSH_Error(@3, "Extra stuff after an attribute list. Maybe missing a comma?");
    csh.AddCSH(@4, COLOR_BRACKET);
	csh.SqBracketPairs.push_back(@$);
	csh.CheckHintBetween(@1, @2, EHintSourceType::ATTR_NAME);
  #else
    $$ = $2;
  #endif
}
      | TOK_OSBRACKET error TOK_CSBRACKET
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_BRACKET);
    csh.AddCSH_Error(@2, "Could not recognize this as an attribute or style name.");
    csh.AddCSH(@3, COLOR_BRACKET);
	csh.SqBracketPairs.push_back(@$);
	csh.CheckHintBetween(@1, @2, EHintSourceType::ATTR_NAME);
  #else
    $$ = new AttributeList;
    chart.Error.Error(@2, "Expecting an attribute or style name. Ignoring all until the closing square bracket (']').");
#endif
}
      | TOK_OSBRACKET attrlist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_BRACKET);
    csh.AddCSH_ErrorAfter(@2, "Missing a square bracket (']').");
    csh.CheckHintBetween(@1, @2, EHintSourceType::ATTR_NAME);
	csh.SqBracketPairs.push_back(@$);
  #else
    $$ = $2;
    chart.Error.Error(@2.after(), "Missing ']'.");
  #endif
}
      | TOK_OSBRACKET attrlist error
{
  #ifdef C_S_H_IS_COMPILED
	csh.AddCSH(@1, COLOR_BRACKET);
    csh.AddCSH_Error(@3, "Missing a ']'.");
	csh.SqBracketPairs.push_back(@$);
	csh.CheckHintBetween(@1, @2, EHintSourceType::ATTR_NAME);
  #else
    $$ = $2;
    chart.Error.Error(@3, "Missing ']'.");
  #endif
}
      | TOK_OSBRACKET
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_BRACKET);
    csh.AddCSH_ErrorAfter(@1, "Missing a square bracket (']').");
	csh.SqBracketPairs.push_back(@$);
    csh.CheckHintAfter(@1, EHintSourceType::ATTR_NAME);
  #else
    $$ = new AttributeList;
    chart.Error.Error(@1.after(), "Missing ']'.");
  #endif
}
/* Dummy rule to force a lookahead, so that previous rule has a valid
 * lookahead location in yylloc */
      | TOK_OSBRACKET TOK__NEVER__HAPPENS
{
    $$ = nullptr;
}
      | TOK_OSBRACKET error
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_BRACKET);
    csh.AddCSH_Error(@2, "Missing a ']'.");
	csh.SqBracketPairs.push_back(@$);
    csh.CheckHintBetween(@1, @2, EHintSourceType::ATTR_NAME);
  #else
    $$ = new AttributeList;
    chart.Error.Error(@2, "Missing ']'.");
  #endif
};

attrlist:    attr
{
  #ifdef C_S_H_IS_COMPILED
  #else
    $$ = (new AttributeList)->Append($1);
  #endif
}
      | attrlist TOK_COMMA attr
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    csh.CheckHintBetween(@2, @3, EHintSourceType::ATTR_NAME);
  #else
    $$ = ($1)->Append($3);
  #endif
}
      | attrlist TOK_COMMA
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    csh.CheckHintAfter(@2, EHintSourceType::ATTR_NAME);
    csh.AddCSH_ErrorAfter(@2, "Missing attribute or style name.");
  #else
    $$ = $1;
    chart.Error.Error(@2.after(), "Expecting an attribute or style name here.");
  #endif
}
      | attrlist TOK_COMMA TOK__NEVER__HAPPENS;


attr:         alpha_string TOK_EQUAL color_string
{
  //string=string
  #ifdef C_S_H_IS_COMPILED
        csh.AddCSH_AttrName(@1, $1, COLOR_ATTRNAME);
        csh.AddCSH(@2, COLOR_EQUAL);
        csh.AddCSH_AttrValue_CheckAndAddEscapeHint(@3, $3, $1);
        csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME);
        csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE, $1);
  #else
        $$ = new Attribute($1, $3, @1, @3);
  #endif
    (void)$1;
    (void)$3;
}
      | alpha_string TOK_EQUAL TOK_PLUS_PLUS color_string
{
  //string=string
  #ifdef C_S_H_IS_COMPILED
        csh.AddCSH_AttrName(@1, $1, COLOR_ATTRNAME);
        csh.AddCSH(@2, COLOR_EQUAL);
        csh.AddCSH_AttrValue_CheckAndAddEscapeHint(@3+@4, "++"+$4.str(), $1);
        csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME);
        csh.CheckHintBetweenAndAt(@2, @3+@4, EHintSourceType::ATTR_VALUE, $1);
  #else
        $$ = new Attribute($1, "++"+$4.str(), @1, @3 + @4);
  #endif
    (void)$1;
    (void)$4;
}
      | alpha_string TOK_EQUAL TOK_PLUS_PLUS
{
  //string=string
  #ifdef C_S_H_IS_COMPILED
        csh.AddCSH_AttrName(@1, $1, COLOR_ATTRNAME);
        csh.AddCSH(@2, COLOR_EQUAL);
        csh.AddCSH_AttrValue_CheckAndAddEscapeHint(@3, "++", $1);
		csh.AddCSH_ErrorAfter(@3, "Continue with a color name or definition.");
        csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME);
        csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE, $1);
  #else
        $$ = new Attribute($1, "++", @1, @3);
  #endif
    (void)$1;
}
      | alpha_string TOK_EQUAL TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
        csh.AddCSH_AttrName(@1, $1, COLOR_ATTRNAME);
        csh.AddCSH(@2, COLOR_EQUAL);
        csh.AddCSH(@3, COLOR_ATTRVALUE);
        csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME);
        csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE, $1);
  #else
        $$ = new Attribute($1, $3, @$, @3);
  #endif
    (void)$1;
    (void)$3;
}
      | alpha_string TOK_EQUAL
{
  #ifdef C_S_H_IS_COMPILED
        csh.AddCSH_AttrName(@1, $1, COLOR_ATTRNAME);
        csh.AddCSH(@2, COLOR_EQUAL);
        csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME);
        csh.CheckHintAfter(@2, EHintSourceType::ATTR_VALUE, $1);
  #else
        $$ = new Attribute($1, {}, @$, @$);
  #endif
    (void)$1;
}
/* Dummy rule to force a lookahead, so that previous rule has a valid
 * lookahead location in yylloc */
      | string TOK_EQUAL TOK__NEVER__HAPPENS
{
    $$ = nullptr;
    (void)$1;
}
      | string
{
  //here we accept non alpha strings for "->" and similar style names
  #ifdef C_S_H_IS_COMPILED
        csh.AddCSH_StyleOrAttrName(@1, $1);
        csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME);
  #else
        $$ = new Attribute($1, @$);
  #endif
    (void)$1;
}
 /* 'string' does not match "++", so we list it separately
     UNCOMMENT if you have ++ as a style
      | TOK_PLUS_PLUS
{
  //here we accept non alpha strings for "->" and similar style names
  #ifdef C_S_H_IS_COMPILED
        csh.AddCSH_StyleOrAttrName(@1, "++");
        csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME);
  #else
        $$ = new Attribute("++", @$);
  #endif
}*/
;



//will not be a reserved word, symbol or style name
entity_string: TOK_STRING
      | TOK_QSTRING
{
  #ifdef C_S_H_IS_COMPILED
	csh.AddQuotedString(@1);
  #endif
  $$ = $1;
}
      | TOK_SHAPE_COMMAND { $$.set(std::string_view{ShapeElement::act_code + $1, 1}); };


/* Add all your keywords here (that parse to a separate token of their own)
 * so that they can be used as style names*/
reserved_word_string: TOK_IF | TOK_STEP | TOK_REPEAT | TOK_BRANCH | TOK_START | TOK_STOP | TOK_GOTO
      | TOK_COMMAND_DEFSHAPE | TOK_COMMAND_DEFCOLOR | TOK_COMMAND_DEFSTYLE | TOK_COMMAND_DEFDESIGN;

/* List here all your symbols, such as -> that are not alpahnumeric but have default style names.*/
symbol_string: TOK__NEVER__HAPPENS2 { $$.init(); };

alpha_string: entity_string | reserved_word_string;

string: alpha_string | symbol_string;

scope_open: TOK_OCBRACKET
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_BRACE);
    csh.PushContext();
    if (csh.CheckHintAfter(@1, EHintSourceType::LINE_START)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.PushContext(@1);
  #endif
}
/* Dummy rule to force a lookahead, so that previous rule has a valid
 * lookahead location in yylloc */
      | TOK_OCBRACKET TOK__NEVER__HAPPENS;

scope_close: TOK_CCBRACKET
{
  #ifdef C_S_H_IS_COMPILED
    $$ = nullptr;
    csh.PopContext();
    csh.AddCSH(@1, COLOR_BRACE);
  #else
    $$ = chart.PopContext().release();
  #endif
};


%%


/* END OF FILE */
