//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 1719
%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 "blockcsh.h"
    #include "blockchart.h"
    #include "block_lexer.h"

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

    #ifdef C_S_H_IS_COMPILED
        #define C_S_H true
        #define CHAR_IF_CSH(A) char
        #define FIRST_IF_CSH(A, B) A
    #else
        #define C_S_H false
        #define CHAR_IF_CSH(A) A
        #define FIRST_IF_CSH(A, B) B
    #endif

    using namespace block;
}

//Goes to the parser.cpp files
%code {
    #include "block_tokenizer.h"
    #include "block_lexer.h"

    #ifdef C_S_H_IS_COMPILED
        void block_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 block_csh_lex(BLOCK_CSH_STYPE *lvalp, SourceLocationRange<C_S_H> *llocp, block::BlockCsh& csh, sv_reader<C_S_H>& input) {
            return get_token<block_csh_tokentype>(input, csh, block::readandclassify<sv_reader<C_S_H>>, *lvalp, *llocp);
        }
    #else
        void block_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 block_compile_lex(BLOCK_COMPILE_STYPE *lvalp, SourceLocationRange<C_S_H> *llocp, BlockChart& chart, sv_reader<C_S_H>& input) {
            return get_token<block_compile_tokentype>(input, chart, block::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_PLUS TOK_MINUS TOK_ATSYMBOL TOK_ASTERISK TOK_PERCENT
       TOK_OCBRACKET TOK_CCBRACKET TOK_OSBRACKET TOK_CSBRACKET
       TOK_BOX TOK_BOXCOL TOK_ROW TOK_COLUMN TOK_TEXT TOK_SHAPE TOK_CELL
       TOK_SHAPE_COMMAND TOK_ALIGN_MODIFIER TOK_BREAK_COMMAND TOK_SPACE_COMMAND
       TOK_MULTI_COMMAND TOK_AROUND_COMMAND TOK_JOIN_COMMAND
       TOK_COPY_COMMAND TOK_AS TOK_MARK_COMMAND TOK_EXTEND_COMMAND TOK_TEMPLATE_COMMAND
       TOK_COMP_OP
       TOK_COLON_STRING TOK_COLON_QUOTED_STRING
       TOK_COMMAND_DEFSHAPE TOK_COMMAND_DEFCOLOR TOK_COMMAND_DEFSTYLE TOK_COMMAND_DEFDESIGN
       TOK_COMMAND_USEDESIGN TOK_COMMAND_USESTYLE
       TOK_COMMAND_ARROWS TOK_COMMAND_BLOCKS TOK_CLONE_MODIFIER_RECURSIVE
       TOK_CLONE_MODIFIER_ADD TOK_CLONE_MODIFIER_MOVE TOK_CLONE_MODIFIER_DROP
       TOK_CLONE_MODIFIER_UPDATE TOK_CLONE_MODIFIER_REPLACE TOK_CLONE_MODIFIER_BEFORE
       TOK_TILDE TOK_PARAM_NAME TOK_OPARENTHESIS TOK_CPARENTHESIS
       TOK_COMMAND_DEFPROC TOK_COMMAND_REPLAY TOK_COMMAND_SET TOK_BYE
       TOK_IF TOK_THEN TOK_ELSE TOK_COMMAND_INCLUDE
       TOK_UNRECOGNIZED_CHAR TOK__NEVER__HAPPENS TOK_EOF 0
       TOK_ARROW_FW TOK_ARROW_BW TOK_ARROW_BIDIR TOK_ARROW_NO
       TOK_WHITESPACE TOK_NEWLINE TOK_COMMENT
       TOK_OINLINE TOK_CINLINE TOK_INLINE TOK_UNMATCHED_INLINE TOK_CHART
%union
{
    str_view                                                   str;
    ShapeElement::Type                                         shapecommand;
    gsl::owner<FIRST_IF_CSH(std::vector<string>,Shape)*>       shape;
    FIRST_IF_CSH(multi_segment_string, gsl::owner<ShapeElement*>) shapeelement; //csh: the port name
    FIRST_IF_CSH(int, gsl::owner<Attribute*>)                  attribute;       //-2 if none, -1<= if a shape= attr
    FIRST_IF_CSH(int, gsl::owner<AttributeList*>)              attributelist;   //-2 if none, -1<= if a shape= attr is present
    EBlockType                                                 eblocktype;
    AlignModifier                                              alignmodifier;
    gsl::owner<FIRST_IF_CSH(CshStringWithPosList,BlockBlockList)*> blockblocklist;
    gsl::owner<CHAR_IF_CSH(BlockInstruction)*>                 instruction;
    gsl::owner<CHAR_IF_CSH(BlockInstrList)*>                   instruction_list;
    const char*                                                input_text_ptr;
    int                                                        condition; //0:false, 1:true, 2:had_error
    ECompareOperator                                           compare_op;
    EUseKeywords                                               use_keywords;
    multi_segment_string                                       multi_str;
    gsl::owner<CHAR_IF_CSH(Procedure)*>                        procedure;
    CHAR_IF_CSH(const Procedure)*                              cprocedure;
    gsl::owner<CHAR_IF_CSH(ProcParamDef)*>                     procparamdef;
    gsl::owner<CHAR_IF_CSH(ProcParamDefList)*>                 procparamdeflist;
    gsl::owner<CHAR_IF_CSH(ProcParamInvocation)*>              procparaminvoc;
    gsl::owner<CHAR_IF_CSH(ProcParamInvocationList)*>          procparaminvoclist;
    gsl::owner<CHAR_IF_CSH(ProcDefParseHelper<AttributeList>)*>procdefhelper;
    EArrowStyle                                                arrowstyle;
    ArrowType                                                  arrowtype;
    gsl::owner<FIRST_IF_CSH(CshStringWithPosList, StringWithPosList)*> stringposlist;
    gsl::owner<FIRST_IF_CSH(CshStringViewWithPosList, StringWithPosList)*> stringviewposlist; //A string view only in CSH mode.
    gsl::owner<CHAR_IF_CSH(StringWithPosList)*>                stringposlist_nocsh;
    FIRST_IF_CSH(multi_segment_string, gsl::owner<CopyParseHelper*>) copystruct;
    gsl::owner<CHAR_IF_CSH(AlignmentAttr)*>                    alignment_attr;
    gsl::owner<CHAR_IF_CSH(ArrowLabel)*>                       arrowlabel;
    CHAR_IF_CSH(ArrowAttrHelper)                               arrowattrs;
    gsl::owner<CHAR_IF_CSH(CloneAction)*>                      cloneaction;
    gsl::owner<CHAR_IF_CSH(CloneActionList)*>                  cloneactionlist;
    FIRST_IF_CSH(char*, UseCommandParseHelper)                 usecommandhelper;
};

%type <multi_str>  entity_string string alpha_string alpha_string_or_percent edgepos string_or_num
                   entity_string_single_or_param
                   multi_string_continuation tok_param_name_as_multi
                   blocknames_plus_number_multi alignment_attrvalue attrvalue_simple attrvalue
                   coord coord_hinted coord_hinted_with_dir coord_hinted_with_dir_and_dist
                   arrow_end arrow_end_block_port_compass arrow_end_block_port_compass_distance distance_for_arrow_end
                   TOK_COLON_STRING TOK_COLON_QUOTED_STRING thing_to_update
%type <str> TOK_STRING TOK_QSTRING
            TOK_NUMBER  TOK_BYE TOK_PARAM_NAME
            TOK_COMMAND_DEFSHAPE TOK_COMMAND_DEFCOLOR TOK_COMMAND_DEFSTYLE TOK_COMMAND_DEFDESIGN
            TOK_COMMAND_DEFPROC TOK_COMMAND_REPLAY TOK_COMMAND_SET
            TOK_IF TOK_THEN TOK_ELSE TOK_COMMAND_INCLUDE
            TOK_COMMAND_USEDESIGN TOK_COMMAND_USESTYLE
            TOK_BOX TOK_BOXCOL TOK_ROW TOK_COLUMN TOK_TEXT TOK_SHAPE TOK_CELL
            TOK_BREAK_COMMAND TOK_SPACE_COMMAND TOK_MULTI_COMMAND TOK_AROUND_COMMAND TOK_JOIN_COMMAND
            TOK_COPY_COMMAND TOK_AS TOK_MARK_COMMAND TOK_EXTEND_COMMAND TOK_TEMPLATE_COMMAND
            TOK_COMMAND_ARROWS TOK_COMMAND_BLOCKS TOK_CLONE_MODIFIER_RECURSIVE
            TOK_CLONE_MODIFIER_ADD TOK_CLONE_MODIFIER_MOVE TOK_CLONE_MODIFIER_DROP
            TOK_CLONE_MODIFIER_UPDATE TOK_CLONE_MODIFIER_REPLACE TOK_CLONE_MODIFIER_BEFORE
            TOK_INLINE TOK_CHART inlined_chart_header
            include opt_before
            entity_string_single string_single alpha_string_single
            reserved_word_string symbol_string
%type <shapecommand> TOK_SHAPE_COMMAND
%type <shapeelement> shapeline
%type <shape> shapedeflist
%type <attribute> attr
%type <attributelist> full_attrlist_with_label full_attrlist attrlist
%type <eblocktype> block_keyword
%type <alignmodifier> TOK_ALIGN_MODIFIER
%type <blockblocklist> block_def full_block_def full_block_def_with_pre full_block_def_enclose total_full_block_def
%type <instruction> instr_req_semicolon instr_ending_brace opt command complete_instr
%type <instruction_list> instrlist braced_instrlist top_level_instrlist optlist
                         multi_instr_req_semicolon multi_instr
                         scope_close ifthenbranch ifthen
                         styledeflist styledef
%type <arrowattrs> full_attrlist_with_arrow_labels
%type <arrowlabel> arrow_label_number arrow_label_number_with_extend arrow_label
%type <condition> condition ifthen_condition else shape_block_header multi_command opt_percent tok_plus_or_minus
%type <compare_op> comp TOK_COMP_OP
%type <use_keywords> usestylemodifier usestylemode
%type <input_text_ptr> TOK_OCBRACKET TOK_CCBRACKET scope_open_proc_body scope_close_proc_body
%type <cprocedure> proc_invocation
%type <procedure> procedure_body
%type <procparamdeflist> proc_def_param_list proc_def_arglist proc_def_arglist_tested
%type <procparamdef> proc_def_param
%type <procparaminvoclist> proc_param_list proc_invoc_param_list
%type <procparaminvoc> proc_invoc_param
%type <procdefhelper> defprochelp1 defprochelp2 defprochelp3 defprochelp4
%type <arrowstyle> TOK_ARROW_FW TOK_ARROW_BW TOK_ARROW_BIDIR TOK_ARROW_NO
%type <arrowtype> arrowsymbol
%type <stringviewposlist>
    stylenameposlist blocknames_plus blocknames_plus_number blocknames_plus_number_or_number
    blocknameposlist
    arrowend_list arrowend_list_solo
    clone_modifier_replace_with_name
    things_to_update opt_things_to_update
%type <stringposlist> colordef colordeflist
%type <stringposlist_nocsh> arrow_cont arrow_needs_semi arrow_no_semi
%type <copystruct> copy_header copy_header_with_pre copy_header_resolved copy_attr
%type <alignment_attr> alignment_modifiers
%type <cloneaction> clone_action_req_semicolon clone_action_no_semicolon clone_action
%type <cloneactionlist> braced_clone_action_list clone_action_list
%type <usecommandhelper> usestyle

%destructor { }                      <shapecommand> <eblocktype> <arrowstyle> <arrowtype> <alignmodifier>
                                     <cprocedure> <input_text_ptr> <condition> <compare_op> <use_keywords> <str>
%destructor {delete $$;}             <stringposlist> <stringviewposlist> <blockblocklist>
%destructor {
  #ifndef C_S_H_IS_COMPILED
    delete chart.ExtractStashed(); //to clear any arrows we may have collected in BlockChart::StashedElement
    delete $$;
  #endif
} arrow_cont arrow_needs_semi arrow_no_semi
%destructor {
  #ifndef C_S_H_IS_COMPILED
    delete chart.ExtractStashed(); //to clear any arrows we may have collected in BlockChart::StashedElement
  #endif
    delete $$;
} arrowend_list arrowend_list_solo
%destructor {
  #ifndef C_S_H_IS_COMPILED
    delete $$;
  #endif
} <*>
%destructor {if (!C_S_H) delete $$;} <procparamdeflist> <procparamdef> <procparaminvoc> <procparaminvoclist> <procdefhelper> <procedure> <alignment_attr>
%destructor { $$.destroy(); } <multi_str>
%destructor {
  #ifdef C_S_H_IS_COMPILED
  #else
    $$->destroy();
  #endif
} <copystruct>
%destructor {
  #ifdef C_S_H_IS_COMPILED
    csh.PopContext();
  #else
    chart.PopContext();
  #endif
} ifthen_condition else
%destructor {
  #ifdef C_S_H_IS_COMPILED
  #else
    $$.Free();
  #endif
} <arrowattrs>
%destructor {
  #ifdef C_S_H_IS_COMPILED
  #else
    delete $$.attrs;
  #endif
} <usecommandhelper>

%printer { std::ostringstream o; o << @$; fprintf(yyo, "%s %s", o.str().data(), $$.str().data()); } <str> <multi_str>
%%

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
    //Add the instructionlist to the chart
    chart.Blocks.SetContent($1);
    delete $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
    (void) $2; //suppress
}
      | 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
    (void) $2; //suppress
};


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
        delete $3;
    }
    $$ = $2;
  #endif
}
      | scope_open scope_close
{
  #ifdef C_S_H_IS_COMPILED
    csh.BracePairs.push_back(@$);
  #else
    $$ = new BlockInstrList;
    //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);
        delete $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.");
    csh.PopContext();
  #else
    $$ = $2;
    $$->Append(chart.PopContext().release()); //will be empty list of nodes anyway.
    chart.Error.Error(@3, "Syntax error.");
  #endif
}
      | scope_open instrlist TOK_EOF
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddOpenBracePair(@$);
    csh.AddCSH_ErrorAfter(@2, "Missing a closing brace ('}').");
    csh.PopContext();
  #else
    $$ = $2;
    $$->Append(chart.PopContext().release()); //will be empty list of nodes anyway.
    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 ('}').");
    csh.PopContext();
  #else
    $$ = chart.PopContext().release(); //will be empty list of nodes anyway.
    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.");
    csh.PopContext();
  #else
    $$ = $2;
    $$->Append(chart.PopContext().release()); //will be empty list of nodes anyway.
    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 '}'.");
    csh.PopContext();
  #else
    $$ = chart.PopContext().release(); //will be empty list of nodes anyway.
    chart.Error.Error(@2, "The command 'bye' can not be used between curly braces '{' and '}'.");
  #endif
  (void)$2;
};


instrlist:    complete_instr
{
  #ifndef C_S_H_IS_COMPILED
    $$ = chart.AppendInstructionToList(nullptr, $1);
  #endif
}
      | instrlist complete_instr
{
  #ifndef C_S_H_IS_COMPILED
    $$ = chart.AppendInstructionToList($1, $2);
  #endif
}
      | multi_instr
      | instrlist multi_instr
{
  #ifndef C_S_H_IS_COMPILED
    if ($1==nullptr)
        $1 = new BlockInstrList;
    if ($2) {
        ($1)->Append($2);
        delete $2;
    }
    $$ = $1;
  #endif
};

multi_instr: multi_instr_req_semicolon
{
  #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
}
      | multi_instr_req_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
    $$=$1;
  #endif
}
      | multi_instr_req_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
    $$=$1;
    chart.Error.Error(@1.after(), "Missing a semicolon ';'.");
    chart.Error.Error(@1, @1.after(), "Here is the beginning of the instruction as I understood it.");
  #endif
}
      | ifthen
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstruction(@$);
    csh.IfThenElses.push_back(@$);
  #endif
    $$ = $1;
}
      | braced_instrlist
{
  #ifdef C_S_H_IS_COMPILED
    //Standalone braced instruction lists are instructions - for the purpose of indentation
    csh.AddInstruction(@1);
  #endif
  $$ = $1;
}
      | arrow_no_semi
{
  #ifdef C_S_H_IS_COMPILED
	csh.AddInstruction(@1);
  #else
    $$ = chart.ExtractStashed();
    if ($1) delete $1;
  #endif
}
      | total_full_block_def braced_instrlist
{
  #ifdef C_S_H_IS_COMPILED
	csh.AddInstruction(@$);
    csh.parent_name = csh.GetCurrentPrefix(); //we dont have content: restore prefix to that of our surrounding context
    csh.CreateBlocksFromLast($1); //Copy the content of the last block to all in the $1 list
  #else
    chart.parent_style.Empty();
    chart.current_parent = chart.MyCurrentContext().parent; //we are done with content: restore prefix to that of our surrounding context
    //Take care not to make changes to the chart if we are storing a procedure
    if (!chart.SkipContent() && $1 && $1->size())  {
        if ($2) {
			$1->front()->SetContent($2);
            delete $2;
			for (auto i = std::next($1->begin()); i!=$1->end(); i++) {
                //Create a cloned list of the content of the first block
                FileLineColRange inclusion((*i)->file_pos.TopOfStack());
                auto c = std::make_unique<BlockInstrList>();
                for (auto &pBlock: $1->front()->content)
                    c->Append(pBlock->Clone(inclusion, (*i)->name_full, nullptr, nullptr, nullptr));
				(*i)->SetContent(c.get());
			}
		} else {
			for (auto &pBlock : *$1)
				pBlock->SetContent(nullptr);
		}
        $$ = new BlockInstrList;
		for (auto &pBlock : *$1)
			$$->Append(std::move(pBlock));
    } else  {
        $$ = nullptr;
        delete $2;
    }
  #endif
    delete $1;
}
      | TOK_TEMPLATE_COMMAND total_full_block_def braced_instrlist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstruction(@$);
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.parent_name = csh.GetCurrentPrefix(); //we dont have content: restore prefix to that of our surrounding context
    csh.CreateBlocksFromLast($2);
    csh.Templatize($2);
  #else
    chart.parent_style.Empty();
    chart.current_parent = chart.MyCurrentContext().parent; //we are done with content: restore prefix to that of our surrounding context
    //Take care not to make changes to the chart if we are storing a procedure
    if (!chart.SkipContent() && $2 && $2->size())  {
		if ($2->size()>1)
			chart.Error.Error((*std::next($2->begin()))->file_pos.start,
			                  "Template definitions commands can only define one template at a time. Ignoring the rest.");
		$2->front()->SetContent($3);
        delete $3;
        chart.CreateTemplate($2->front().release());
		delete $2;
    } else  {
        delete $2;
        delete $3;
    }
    $$ = nullptr;
  #endif
    (void)$1;
};



multi_instr_req_semicolon: arrow_needs_semi
{
  #ifdef C_S_H_IS_COMPILED
	csh.AddInstruction(@1);
  #else
    $$ = chart.ExtractStashed();
	//If arrow_needs semi has a non-empty value (the list of the
	//last comma separaed arrow end strings) and NO stashed arrows,
	//it means we just had a list of comma separated arrow_end values
	//(unless we skip content), of course.
	if ($$->size()==0 && $1 && $1->size()>0 && !chart.SkipContent()) {
		delete $$;
		$$ = chart.UpdateBlock($1, nullptr).release();
	}
    if ($1) delete $1;
  #endif
}
      | total_full_block_def
{
  #ifdef C_S_H_IS_COMPILED
    csh.parent_name = csh.GetCurrentPrefix(); //we dont have content: restore prefix to that of our surrounding context
    csh.CreateBlocksFromLast($1); //Here we probably have no content to copy, but need to add multi block names
  #else
    chart.parent_style.Empty();
    chart.current_parent = chart.MyCurrentContext().parent; //we are done with content: restore prefix to that of our surrounding context
    //Take care not to make changes to the chart if we are storing a procedure
    if (!chart.SkipContent() && $1 && $1->size()) {
		for (auto &pBlock : *$1)
			pBlock->SetContent(nullptr);
        $$ = new BlockInstrList;
        for (auto &pBlock : *$1)
            $$->Append(std::move(pBlock));
    } else
        $$ = nullptr;
  #endif
    delete $1;
}
      | total_full_block_def TOK_INLINE
{
  #ifdef C_S_H_IS_COMPILED
    csh.parent_name = csh.GetCurrentPrefix(); //we dont have content: restore prefix to that of our surrounding context
    csh.CreateBlocksFromLast($1); //Here we probably have no content to copy, but need to add multi block names
    csh.AddInLine($2, @2, csh.Context2.back().pedantic);
  #else
    chart.parent_style.Empty();
    chart.current_parent = chart.MyCurrentContext().parent; //we are done with content: restore prefix to that of our surrounding context
    //Take care not to make changes to the chart if we are storing a procedure
    if (!chart.SkipContent() && $1 && $1->size()) {
		for (auto &pBlock : *$1)
			pBlock->SetContent(StringWithPos{$2,@2});
        $$ = new BlockInstrList;
        for (auto &pBlock : *$1)
            $$->Append(std::move(pBlock));
    } else
        $$ = nullptr;
  #endif
    delete $1;
}
      | optlist
      | 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
    $$ = $2;
  #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;
}
      | arrowend_list full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.UpdateBlocks($1);
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        BlockBlock::AttributeNames(EBlockType::Box, csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        BlockBlock::AttributeValues(EBlockType::Box, csh.hintAttrName, csh);
  #else
	if ($1 && $2 && !chart.SkipContent())
		$$ = chart.UpdateBlock($1, $2).release();
	else
		$$ = nullptr;
	delete $2;
  #endif
	delete $1;
}
      | alignment_modifiers arrowend_list
{
  #ifdef C_S_H_IS_COMPILED
    csh.UpdateBlocks($2);
  #else
    if ($2 && !chart.SkipContent())
        $$ = chart.UpdateBlock($2, nullptr, $1).release();
    else
        $$ = nullptr;
    delete $1;
  #endif
    delete $2;
}
      | alignment_modifiers arrowend_list full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.UpdateBlocks($2);
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @3))
        BlockBlock::AttributeNames(EBlockType::Box, csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @3))
        BlockBlock::AttributeValues(EBlockType::Box, csh.hintAttrName, csh);
  #else
  	if ($2 && $3 && !chart.SkipContent())
	    $$ = chart.UpdateBlock($2, $3, $1).release();
	  else
		  $$ = nullptr;
    delete $1;
    delete $3;
  #endif
    delete $2;
};



complete_instr: instr_ending_brace
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstruction(@$);
    if (csh.CheckLineStartHintAfter(@1)) {
       csh.AddLineBeginToHints();
       csh.hintStatus = HINT_READY;
    }
  #else
    //if ($1) ($1)->SetLineEnd(@$);
    $$=$1;
  #endif
}
      | instr_req_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_req_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_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_req_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
}
      | proc_invocation TOK_SEMICOLON
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SEMICOLON);
  #else
    if ($1)
        if (auto ctx = $1->MatchParameters(nullptr, @1.after(), &chart)) {
            input.push($1->text, EInclusionReason::PROCEDURE, $1->file_pos, @$);
            proc_helper.last_procedure = $1;
            proc_helper.last_procedure_params = std::move(*ctx);
            proc_helper.open_context_mode = EScopeOpenMode::PROC_REPLAY;
        }
    $$ = nullptr;
  #endif
}
      | proc_invocation error TOK_SEMICOLON
{
  #ifdef C_S_H_IS_COMPILED
    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
    chart.Error.Error(@1.after(), "Missing a semicolon TOK_SEMICOLON.");
    chart.Error.Error(@1, @1.after(), "Here is the beginning of the command as I understood it.");
    if ($1)
        if (auto ctx = $1->MatchParameters(nullptr, @1.after(), &chart)) {
            input.push($1->text, EInclusionReason::PROCEDURE, $1->file_pos, @$);
            proc_helper.last_procedure = $1;
            proc_helper.last_procedure_params = std::move(*ctx);
            proc_helper.open_context_mode = EScopeOpenMode::PROC_REPLAY;
        }
    $$ = nullptr;
  #endif
}
      | proc_invocation
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_ErrorAfter(@1, "Missing semicolon.");
  #else
    chart.Error.Error(@1.after(), "Missing a semicolon TOK_SEMICOLON.");
    chart.Error.Error(@1, @1.after(), "Here is the beginning of the command as I understood it.");
    if ($1)
        if (auto ctx = $1->MatchParameters(nullptr, @1.after(), &chart)) {
            input.push($1->text, EInclusionReason::PROCEDURE, $1->file_pos, @$);
            proc_helper.last_procedure = $1;
            proc_helper.last_procedure_params = std::move(*ctx);
            proc_helper.open_context_mode = EScopeOpenMode::PROC_REPLAY;
        }
    $$ = nullptr;
  #endif
}
      | proc_invocation proc_param_list TOK_SEMICOLON
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@3, COLOR_SEMICOLON);
  #else
    if ($1 && $2) {
        if (auto ctx = $1->MatchParameters($2, @2.end(), &chart)) {
            input.push($1->text, EInclusionReason::PROCEDURE, $1->file_pos, @$);
            proc_helper.last_procedure = $1;
            proc_helper.last_procedure_params = std::move(*ctx);
            proc_helper.open_context_mode = EScopeOpenMode::PROC_REPLAY;
        }
    } else {
        delete $2;
    }
    $$ = nullptr;
  #endif
}
      | proc_invocation proc_param_list error TOK_SEMICOLON
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@4, COLOR_SEMICOLON);
    csh.AddCSH_Error(@3, "I am not sure what is coming here.");
    if (csh.CheckLineStartHintAfter(@4)) {
       csh.AddLineBeginToHints();
       csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@2.after(), "Missing a semicolon TOK_SEMICOLON.");
    chart.Error.Error(@1, @2.after(), "Here is the beginning of the command as I understood it.");
    if ($1 && $2) {
        if (auto ctx = $1->MatchParameters($2, @2.end(), &chart)) {
            input.push($1->text, EInclusionReason::PROCEDURE, $1->file_pos, @$);
            proc_helper.last_procedure = $1;
            proc_helper.last_procedure_params = std::move(*ctx);
            proc_helper.open_context_mode = EScopeOpenMode::PROC_REPLAY;
        }
    } else {
        delete $2;
    }
    $$ = nullptr;
  #endif
}
      | proc_invocation proc_param_list
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_ErrorAfter(@2, "Missing semicolon.");
  #else
    chart.Error.Error(@2.after(), "Missing a semicolon TOK_SEMICOLON.");
    chart.Error.Error(@1, @2.after(), "Here is the beginning of the command as I understood it.");
    if ($1 && $2) {
        if (auto ctx = $1->MatchParameters($2, @2.end(), &chart)) {
            input.push($1->text, EInclusionReason::PROCEDURE, $1->file_pos, @$);
            proc_helper.last_procedure = $1;
            proc_helper.last_procedure_params = std::move(*ctx);
            proc_helper.open_context_mode = EScopeOpenMode::PROC_REPLAY;
        }
    } else
        delete $2;
    $$ = nullptr;
  #endif
}
      | include TOK_SEMICOLON
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SEMICOLON);
  #else
    if ($1) {
        auto text = chart.Include($1, @1);
        if (text.first && text.first->length() && text.second.IsValid())
            input.push(*text.first, EInclusionReason::INCLUDE, text.second, @$);
    }
    $$ = nullptr;
  #endif
}
      | include
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_ErrorAfter(@1, "Missing semicolon.");
  #else
    if ($1) {
        auto text = chart.Include($1, @1);
        if (text.first && text.first->length() && text.second.IsValid())
            input.push(*text.first, EInclusionReason::INCLUDE, text.second, @$);
    }
    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.");
    $$ = nullptr;
  #endif
}
      | include error TOK_SEMICOLON
{
  #ifdef C_S_H_IS_COMPILED
    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
    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.");
    if ($1) {
        auto text = chart.Include($1, @1);
        if (text.first && text.first->length() && text.second.IsValid())
            input.push(*text.first, EInclusionReason::INCLUDE, text.second, @$);
    }
    $$ = nullptr;
  #endif
};

include: TOK_COMMAND_INCLUDE
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@1, "Missing a file name to include. You must use quotation marks ('\"').");
    if (csh.CheckHintAfter(@1, EHintSourceType::ATTR_VALUE))
        csh.AddIncludeFilesToHints();
  #else
    chart.Error.Error(@1.after(), "Missing a file name to include. You must use quotation marks ('\"').");
  #endif
    $$.init();
    (void)$1;
}
      | TOK_COMMAND_INCLUDE TOK_QSTRING
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_INCLUDEFILE);
    if (csh.CheckHintBetween(@1, @2, EHintSourceType::ATTR_VALUE))
        csh.AddIncludeFilesToHints();
    else if(csh.CheckHintAt(@2, EHintSourceType::ATTR_VALUE))
        csh.AddIncludeFilesToHints($2, @2);
  #endif
    $$ = $2;
    (void)$1;
};


instr_req_semicolon:         TOK_COMMAND_DEFCOLOR colordeflist_actioned
{
  #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;
}
      | usedesign
{
  #ifndef C_S_H_IS_COMPILED
    $$ = nullptr;
  #endif
}
      | usestyle
{
  #ifndef C_S_H_IS_COMPILED
    if (!chart.SkipContent())
        chart.AddAttributeListToRunningStyle($1.attrs, $1.keywords);
    else
        delete $1.attrs;
    $$ = nullptr;
  #endif
}
      | command
      | set
{
  #ifdef C_S_H_IS_COMPILED
  #else
    $$ = nullptr;
  #endif
}
      | TOK_TEMPLATE_COMMAND total_full_block_def
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.parent_name = csh.GetCurrentPrefix(); //we dont have content: restore prefix to that of our surrounding context
    csh.CreateBlocksFromLast($2);
    csh.Templatize($2);
  #else
    chart.parent_style.Empty();
    chart.current_parent = chart.MyCurrentContext().parent; //we are done with content: restore prefix to that of our surrounding context
    //Take care not to make changes to the chart if we are storing a procedure
    if (!chart.SkipContent() && $2 && $2->size())  {
		if ($2->size()>1)
			chart.Error.Error((*std::next($2->begin()))->file_pos.start,
			                  "Template definitions commands can only define one template at a time. Ignoring the rest.");
        $2->front()->SetContent(nullptr);
        chart.CreateTemplate($2->front().release());
    }
    delete $2;
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_TEMPLATE_COMMAND
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintAfter(@1, EHintSourceType::KEYWORD))
        csh.AddKeywordsToHints(true, false, true);
  #else
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_TEMPLATE_COMMAND copy_attr
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (!$2.had_error)
        csh.Templatize($2, @2);
    $2.destroy();
  #else
    _ASSERT($2); //copy_header should never return null
    //Take care not to make changes to the chart if we are storing a procedure
    if (chart.SkipContent())
        $2->destroy();
    else
        chart.CreateTemplate(chart.CreateCopy($2));
    $$ = nullptr;
  #endif
    (void)$1;
}
      | copy_attr
{
  #ifdef C_S_H_IS_COMPILED
    $1.destroy();
  #else
    _ASSERT($1); //copy_header should never return null
    //Take care not to make changes to the chart if we are storing a procedure
    if (chart.SkipContent()) {
        $1->destroy();
        $$ = nullptr;
    } else
        $$ = chart.CreateCopy($1);
  #endif
}
      | arrow_label
{
  $$ = $1;
}
      | arrow_end full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
	    csh.UpdateCSH_ArrowEndAtLineBegin(@1, $1);
    if (csh.CheckHintLocated(EHintSourceType::LINE_START, @1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        BlockBlock::AttributeNames(EBlockType::Box, csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        BlockBlock::AttributeValues(EBlockType::Box, csh.hintAttrName, csh);
  #else
	if (!$1.had_error && $2 && !chart.SkipContent())
		$$ = chart.UpdateBlock($1, @1, $2).release();
	else
		$$ = nullptr;
	delete $2;
  #endif
    $1.destroy();
}
      | arrow_end
{
    /* This is where we handle an unrecognized/partial keyword alone at the beginning of a line.*/
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
	    csh.UpdateCSH_ArrowEndAtLineBegin(@1, $1);
    if (csh.CheckHintLocated(EHintSourceType::LINE_START, @1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
	if (!$1.had_error && !chart.SkipContent())
		$$ = chart.UpdateBlock($1, @1, nullptr).release();
	else
		$$ = nullptr;
  #endif
    $1.destroy();
}
      | alignment_modifiers arrow_end full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::LINE_START, @1))
        csh.hintStatus = HINT_READY;
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @3))
        BlockBlock::AttributeNames(EBlockType::Box, csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @3))
        BlockBlock::AttributeValues(EBlockType::Box, csh.hintAttrName, csh);
  #else
    if (!$2.had_error && $3 && !chart.SkipContent())
        $$ = chart.UpdateBlock($2, @2, $3, $1).release();
    else
        $$ = nullptr;
    delete $1;
    delete $3;
  #endif
    $2.destroy();
}
      | alignment_modifiers arrow_end
{
    /* This is where we handle an unrecognized/partial keyword alone at the beginning of a line.*/
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::LINE_START, @1)) {
        csh.hintStatus = HINT_READY;
    }
  #else
	if (!$2.had_error && !chart.SkipContent())
		$$ = chart.UpdateBlock($2, @2, nullptr, $1).release();
	else
		$$ = nullptr;
    delete $1;
  #endif
    $2.destroy();
};


instr_ending_brace: TOK_COMMAND_DEFSHAPE shapedef
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.Contexts.back().SkipContent())
        csh.AddCSH_Error(@1, "Cannot define shapes as part of 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 shapes as part of a procedure.");
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_COMMAND_DEFSHAPE
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.Contexts.back().SkipContent())
        csh.AddCSH_Error(@1, "Cannot define shapes as part of a procedure.");
    else
        csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH_ErrorAfter(@$, "Missing shape name and definition.");
  #else
    if (chart.SkipContent())
        chart.Error.Error(@1, "Cannot define shapes as part of a procedure.");
    else
        chart.Error.Error(@1, "Missing shape name and definition.");
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_COMMAND_DEFDESIGN designdef
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.Contexts.back().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
    if (csh.Contexts.back().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;
}
      | defproc
{
  #ifndef C_S_H_IS_COMPILED
    $$ = nullptr;
  #endif
}
      | copy_attr braced_clone_action_list
{
  #ifdef C_S_H_IS_COMPILED
    $1.destroy();
  #else
    _ASSERT($1); //copy_header should never return null
    $1->modifiers = $2;
    //Take care not to make changes to the chart if we are storing a procedure
    if (chart.SkipContent()) {
        $1->destroy();
        $$ = nullptr;
    } else
        $$ = chart.CreateCopy($1);
  #endif
}
      | TOK_TEMPLATE_COMMAND copy_attr braced_clone_action_list
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (!$2.had_error)
        csh.Templatize($2, @2);
    $2.destroy();
  #else
    _ASSERT($2); //copy_header should never return null
    $2->modifiers = $3;
    //Take care not to make changes to the chart if we are storing a procedure
    if (chart.SkipContent())
        $2->destroy();
    else
        chart.CreateTemplate(chart.CreateCopy($2));
    $$ = nullptr;
  #endif
    (void)$1;
};

braced_clone_action_list: scope_open clone_action_list scope_close
{
  #ifdef C_S_H_IS_COMPILED
    csh.BracePairs.push_back(@$);
    if (csh.IsCursorAtLineBegin() && (csh.CheckHintBetween(@1, @2, EHintSourceType::KEYWORD) ||
        csh.CheckHintBetween(@2, @3, EHintSourceType::KEYWORD))) {
        csh.ClearHints(); //clone action list may end in an instruction after which we get the regular line begin hints.
        csh.AddCloneActionKeywordsToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = $2;
    delete $3;
  #endif
}
      | scope_open scope_close
{
  #ifdef C_S_H_IS_COMPILED
    csh.BracePairs.push_back(@$);
    if (csh.IsCursorAtLineBegin() && csh.CheckHintBetween(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddCloneActionKeywordsToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = nullptr;
    delete $2;
  #endif
}
      | scope_open clone_action_list 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.");
    if (csh.IsCursorAtLineBegin() && (csh.CheckHintBetween(@1, @2, EHintSourceType::KEYWORD) ||
        csh.CheckHintBetween(@2, @3, EHintSourceType::KEYWORD))) {
        csh.ClearHints(); //clone action list may end in an instruction after which we get the regular line begin hints.
        csh.AddCloneActionKeywordsToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = $2;
    delete $4;
    chart.Error.Error(@3, "Syntax error.");
  #endif
    yyerrok;
}
      | scope_open clone_action_list error TOK_EOF
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddOpenBracePair(@$);
    csh.AddCSH_Error(@3, "Could not recognize this as a valid line.");
    csh.PopContext();
    if (csh.IsCursorAtLineBegin() && (csh.CheckHintBetween(@1, @2, EHintSourceType::KEYWORD) ||
        csh.CheckHintBetween(@2, @3, EHintSourceType::KEYWORD))) {
        csh.ClearHints(); //clone action list may end in an instruction after which we get the regular line begin hints.
        csh.AddCloneActionKeywordsToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = $2;
    chart.PopContext();
    chart.Error.Error(@3, "Syntax error.");
  #endif
}
      | scope_open clone_action_list TOK_EOF
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddOpenBracePair(@$);
    csh.AddCSH_ErrorAfter(@2, "Missing a closing brace ('}').");
    csh.PopContext();
    if (csh.IsCursorAtLineBegin() && (csh.CheckHintBetween(@1, @2, EHintSourceType::KEYWORD) ||
        csh.CheckHintAfter(@2, EHintSourceType::KEYWORD))) {
        csh.ClearHints(); //clone action list may end in an instruction after which we get the regular line begin hints.
        csh.AddCloneActionKeywordsToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = $2;
    chart.PopContext();
    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 ('}').");
    csh.PopContext();
    if (csh.IsCursorAtLineBegin() && csh.CheckHintAfter(@1, EHintSourceType::KEYWORD)) {
        csh.AddCloneActionKeywordsToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = nullptr;
    chart.PopContext();
    chart.Error.Error(@1.after(), "Missing a corresponding '}'.");
  #endif
}
      | scope_open clone_action_list 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.");
    csh.PopContext();
    if (csh.IsCursorAtLineBegin() && (csh.CheckHintBetween(@1, @2, EHintSourceType::KEYWORD) ||
        csh.CheckHintBetween(@2, @3, EHintSourceType::KEYWORD))) {
        csh.ClearHints(); //clone action list may end in an instruction after which we get the regular line begin hints.
        csh.AddCloneActionKeywordsToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = $2;
    chart.PopContext();
    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 '}'.");
    csh.PopContext();
    if (csh.IsCursorAtLineBegin() && csh.CheckHintBetween(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddCloneActionKeywordsToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = nullptr;
    chart.PopContext();
    chart.Error.Error(@2, "The command 'bye' can not be used between curly braces '{' and '}'.");
  #endif
  (void)$2;
};

clone_action_list: clone_action
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintAfter(@1, EHintSourceType::KEYWORD)) {
        csh.AddUseKeywordsToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = new CloneActionList;
    $$->Append($1);
  #endif
}
      | clone_action_list clone_action
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintBetween(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddUseKeywordsToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    $1->Append($2);
    $$ = $1;
  #endif
};

clone_action: clone_action_no_semicolon
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstruction(@$);
  #else
    $$ = $1;
  #endif
}
      | clone_action_req_semicolon TOK_SEMICOLON
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SEMICOLON);
    csh.AddInstruction(@$);
  #else
    $$ = $1;
  #endif
}
      | clone_action_req_semicolon error TOK_SEMICOLON
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@3, COLOR_SEMICOLON);
    csh.AddCSH_Error(@3, "Syntax error.");
    csh.AddInstruction(@$);
  #else
    chart.Error.Error(@3, "Syntax error.");
    $$ = $1;
  #endif
}
      | clone_action_req_semicolon
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_ErrorAfter(@1, "Missing semicolon (';').");
    csh.AddInstruction(@$);
  #else
    chart.Error.Error(@1.after(), "Missing semicolon (';').");
    $$ = $1;
  #endif
};

thing_to_update: entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error) {
        if (CaseInsensitiveEqual("all", $1))
            csh.AddCSH(@1, COLOR_KEYWORD);
        else
            csh.AddCSH_LocalBlockName(@1, $1);
    }
  #endif
    $$ = $1;
}
      | TOK_COMMAND_BLOCKS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
  #endif
    $$.set($1);
}
      | TOK_COMMAND_ARROWS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
  #endif
    $$.set($1);
};

things_to_update: thing_to_update
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD))
        csh.AddWhatToUpdateToHints(true);
    $$ = new CshStringViewWithPosList;
    if (!$1.empty())
        $$->push_back({$1, @1});
  #else
    $$ = new StringWithPosList;
    if (!$1.empty())
        $$->push_back({$1, @1});
  #endif
}
      | things_to_update TOK_COMMA
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    csh.AddCSH_ErrorAfter(@2, "Missing, a block name, 'blocks', 'arrows' or 'all'.");
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD))
        csh.AddWhatToUpdateToHints(true);
    else if(csh.CheckHintAfter(@2, EHintSourceType::KEYWORD))
        csh.AddWhatToUpdateToHints(false);
  #else
    chart.Error.Error(@2.after(), "Missing, a block name, 'blocks', 'arrows' or 'all'.");
  #endif
    $$ = $1;
}
      | things_to_update TOK_COMMA thing_to_update
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD))
        csh.AddWhatToUpdateToHints(true);
    else if(csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::KEYWORD))
        csh.AddWhatToUpdateToHints(false);
    if (!$3.empty())
        $1->push_back({$3, @3});
  #else
    if (!$3.empty())
        $1->push_back({$3, @3});
  #endif
    $$ = $1;
}


opt_things_to_update: things_to_update | /*empty*/ {$$ = nullptr;}


clone_action_req_semicolon: entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintAt(@1,  EHintSourceType::KEYWORD)) {
        csh.AddCloneActionKeywordsToHints();
        csh.hintStatus = HINT_READY;
    } else {
        csh.AddCSH_Error(@1, "Expecting 'add', 'drop', 'move', 'update', 'replace' or 'use', 'blocks use', 'arrows use'.");
    }
  #else
    $$ = nullptr;
    chart.Error.Error(@1, "Expecting 'add', 'drop', 'move', 'update', 'replace' or 'use', 'blocks use', 'arrows use'. Ignoring this line.");
  #endif
    $1.destroy();
}
      | usestyle
{
  #ifndef C_S_H_IS_COMPILED
    //update running style as normal
    chart.AddAttributeListToRunningStyle($1.attrs, $1.keywords);
    $$ = nullptr;
  #endif
}
      | TOK_CLONE_MODIFIER_RECURSIVE
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintAfter(@1, EHintSourceType::KEYWORD)) {
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_KEYWORD) + "update",
                               "Update attributes of all blocks, all arrows or all elements recursively.",
                               EHintType::KEYWORD));
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH_Error(@1, "Expecting 'update'.");
  #else
    chart.Error.Error(@1.after(), "Expecting 'update'.");
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_CLONE_MODIFIER_RECURSIVE entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_KEYWORD) + "update",
                               "Update attributes of all blocks, all arrows or all elements recursively.",
                               EHintType::KEYWORD));
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH_Error(@2, "Expecting 'update'.");
  #else
    chart.Error.Error(@2, "Expecting 'update'.");
    $$ = nullptr;
  #endif
    (void)$1;
    $2.destroy();
}
      | TOK_CLONE_MODIFIER_RECURSIVE TOK_CLONE_MODIFIER_UPDATE opt_things_to_update
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_KEYWORD);
    if ($3==nullptr)
        csh.AddCSH_ErrorAfter(@2, "Missing what to update.");
    else
        csh.AddCSH_ErrorAfter(@$, "Expecting an attribute list.");
    if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ENTITY) || csh.CheckHintAfter(@$, EHintSourceType::ENTITY)) {
        csh.AddWhatToUpdateToHints(true);
        csh.hintStatus = HINT_READY;
    }
  #else
    if ($2)
        chart.Error.Error(@$.after(), "Expecting an attribute list.");
    else
        chart.Error.Error(@$.after(), "Missing what to update ('all', 'blocks', 'arrows' or a block name). Ignoring update attempt.");
    $$ = nullptr;
  #endif
    (void)$1;
    (void)$2;
    delete $3;
}
      | TOK_CLONE_MODIFIER_UPDATE opt_things_to_update
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if ($2==nullptr)
        csh.AddCSH_ErrorAfter(@1, "Missing what to update.");
    else
        csh.AddCSH_ErrorAfter(@$, "Expecting an attribute list.");
    if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::ENTITY) || csh.CheckHintAfter(@$, EHintSourceType::ENTITY)) {
        csh.AddWhatToUpdateToHints(true);
        csh.hintStatus = HINT_READY;
    }
  #else
    if ($2)
        chart.Error.Error(@$.after(), "Expecting an attribute list.");
    else
        chart.Error.Error(@$.after(), "Missing what to update ('all', 'blocks', 'arrows' or a block name). Ignoring update attempt.");
    $$ = nullptr;
  #endif
    (void)$1;
    delete $2;
}
      | TOK_CLONE_MODIFIER_RECURSIVE TOK_CLONE_MODIFIER_UPDATE opt_things_to_update full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_KEYWORD);
    if ($3==nullptr)
        csh.AddCSH_ErrorAfter(@2, "Missing what to update.");
    if (csh.CheckHintBetween(@2, @4, EHintSourceType::ENTITY)) {
        csh.AddWhatToUpdateToHints(true);
        csh.hintStatus = HINT_READY;
    }
    delete $3;
  #else
    if ($3) {
        $$ = new CloneAction(@1+@2, $3, @3, $4, nullptr);
        $$->recursive = true;
    } else {
        chart.Error.Error(@2.after(), "Missing what to update ('all', 'blocks', 'arrows' or a block name). Ignoring update attempt.");
        $$ = nullptr;
    }
  #endif
    (void)$1;
    (void)$2;
}
      | TOK_CLONE_MODIFIER_UPDATE opt_things_to_update full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if ($2==nullptr)
        csh.AddCSH_ErrorAfter(@1, "Missing what to update.");
    if (csh.CheckHintBetween(@1, @3, EHintSourceType::ENTITY)) {
        csh.AddWhatToUpdateToHints(true);
        csh.hintStatus = HINT_READY;
    }
    delete $2;
  #else
    if ($2)
        $$ = new CloneAction(@1, $2, @2, $3, nullptr);
    else {
        chart.Error.Error(@1.after(), "Missing what to update ('all', 'blocks', 'arrows' or a block name). Ignoring update attempt.");
        $$ = nullptr;
    }
  #endif
    (void)$1;
}
      | TOK_CLONE_MODIFIER_DROP opt_things_to_update
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddCloneActionKeywordsToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::ENTITY) || csh.CheckHintAfter(@$, EHintSourceType::ENTITY)) {
        csh.AddWhatToUpdateToHints(true);
        csh.hintStatus = HINT_READY;
    }
    if ($2)
        for (auto &[name, file_pos] : *$2)
            csh.DeleteBlock(name);
    delete $2;
  #else
    $$ = new CloneAction(@1, $2);
  #endif
    (void)$1;
}
      | TOK_CLONE_MODIFIER_MOVE opt_things_to_update opt_before
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintAt(@1,  EHintSourceType::KEYWORD)) {
        csh.AddCloneActionKeywordsToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@2, @3, EHintSourceType::KEYWORD)) {
        csh.AddBeforeToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@1, @3, EHintSourceType::ENTITY) || csh.CheckHintAfter(@1, EHintSourceType::ENTITY)) {
        csh.AddWhatToUpdateToHints(true);
        csh.hintStatus = HINT_READY;
    }
    if ($2)
        for (auto &[name, file_pos] : *$2)
            csh.DeleteBlock(name);
    delete $2;
  #else
    $$ = new CloneAction(@1, $2, $3, @3);
  #endif
    (void)$1;
};

clone_modifier_replace_with_name: TOK_CLONE_MODIFIER_REPLACE entity_string {
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_LocalBlockName(@2, $2);
    if (csh.CheckHintAt(@1,  EHintSourceType::KEYWORD)) {
        csh.AddCloneActionKeywordsToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::ENTITY)) {
        csh.AddWhatToUpdateToHints(false);
        csh.hintStatus = HINT_READY;
    }
    csh.DeleteBlock($2);
    $$ = new CshStringViewWithPosList{ {$2, @2} };
  #else
    $$ = new StringWithPosList{ {$2, @2} };
  #endif
    (void)$1;
}
      | TOK_CLONE_MODIFIER_REPLACE {
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintAfter(@1, EHintSourceType::ENTITY)) {
        csh.AddWhatToUpdateToHints(false);
        csh.hintStatus = HINT_READY;
    }
    $$ = new CshStringViewWithPosList;
  #else
    $$ = new StringWithPosList;
  #endif
    (void)$1;
}

clone_action_no_semicolon: TOK_CLONE_MODIFIER_UPDATE opt_things_to_update full_attrlist_with_label braced_clone_action_list
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintAt(@1,  EHintSourceType::KEYWORD)) {
        csh.AddCloneActionKeywordsToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@1, @3, EHintSourceType::KEYWORD))
        csh.AddWhatToUpdateToHints(true);
    delete $2;
  #else
    if ($2==nullptr) {
        chart.Error.Error(@2.after(), "Missing what to update (a block name). Ignoring update attempt.");
        $$ = nullptr;
    } else
        $$ = new CloneAction(@1, $2, @2, $3, $4);
  #endif
    (void)$1;
}
      | TOK_CLONE_MODIFIER_UPDATE opt_things_to_update braced_clone_action_list
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintAt(@1,  EHintSourceType::KEYWORD)) {
        csh.AddCloneActionKeywordsToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@1, @3, EHintSourceType::KEYWORD))
        csh.AddWhatToUpdateToHints(true);
    delete $2;
  #else
    if ($2==nullptr) {
        chart.Error.Error(@2.after(), "Missing what to update (a block name). Ignoring update attempt.");
        $$ = nullptr;
    } else
        $$ = new CloneAction(@1, $2, @2, nullptr, $3);
  #endif
    (void)$1;
}
      | TOK_CLONE_MODIFIER_ADD opt_before complete_instr
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintAt(@1,  EHintSourceType::KEYWORD)) {
        csh.AddCloneActionKeywordsToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddBeforeToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = new CloneAction(@1, $3, $2, @2);
  #endif
    (void)$1;
}
      | TOK_CLONE_MODIFIER_ADD opt_before multi_instr
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintAt(@1,  EHintSourceType::KEYWORD)) {
        csh.AddCloneActionKeywordsToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddBeforeToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = new CloneAction(@1, $3, $2, @2);
  #endif
    (void)$1;
}
      | clone_modifier_replace_with_name complete_instr
{
  #ifdef C_S_H_IS_COMPILED
  #else
    if ($1 && $1->size())
        $$ = new CloneAction(@1, $1->front().name, $1->front().file_pos, $2);
    else
        $$ = nullptr;
  #endif
    delete $1;
}
      | clone_modifier_replace_with_name multi_instr
{
  #ifdef C_S_H_IS_COMPILED
  #else
    if ($1 && $1->size())
        $$ = new CloneAction(@1, $1->front().name, $1->front().file_pos, $2);
    else
        $$ = nullptr;
  #endif
    delete $1;
};


opt_before: /*empty*/
{
    $$.init();
}
      | entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintAt(@1,  EHintSourceType::KEYWORD)) {
        csh.AddBeforeToHints();
        csh.hintStatus = HINT_READY;
    }
  #endif
    $$.init();
    $1.destroy();
}
      | TOK_CLONE_MODIFIER_BEFORE
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@1, "Missing block name.");
    if (csh.CheckHintAfter(@1, EHintSourceType::ENTITY)) {
        csh.AddWhatToUpdateToHints(false);
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@1.after(), "Missing block name. Ignoring 'before' clause.");
  #endif
    $$.init();
    (void)$1;
}
      | TOK_CLONE_MODIFIER_BEFORE entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_LocalBlockName(@2, $2);
  #else
  #endif
    $$.set($2);
    (void)$1;
};



/*************************************************************************
 * Blocks
 *************************************************************************/

total_full_block_def: full_block_def_enclose | full_block_def_with_pre;

full_block_def_enclose: TOK_AROUND_COMMAND
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@1, "Missing block definition.");
    if (csh.CheckHintAfter(@1, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Enclose block %s in another block.");
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@1.after(), "Missing block name. Ignoring this.");
  #endif
    $$ = nullptr;
    (void)$1;
}
      | TOK_AROUND_COMMAND blocknames_plus
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_NameList($2, @2, COLOR_ENTITYNAME, COLOR_COMMA, COLOR_ERROR, COLOR_ENTITYNAME);
    csh.AddCSH_Error(@2, "Missing block definition.");
    if (csh.CheckHintLocated(@2)) {
        csh.AddEntityNamesAtTheEnd("Enclose block %s in another block.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Enclose block %s in another block.");
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@2, "Missing block definition. Ignoring this.");
  #endif
    $$ = nullptr;
    (void)$1;
    delete $2;
}
      | TOK_AROUND_COMMAND blocknames_plus block_def
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_NameList($2, @2, COLOR_ENTITYNAME, COLOR_COMMA, COLOR_ERROR, COLOR_ENTITYNAME);
    csh.parent_name = csh.GetCurrentPrefix(); //we dont have content: restore prefix to that of our surrounding context
    if (csh.CheckHintLocated(@2)) {
        csh.AddEntityNamesAtTheEnd("Enclose block %s in another block.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Enclose block %s in another block.");
        csh.hintStatus = HINT_READY;
    }
    delete $2;
	if ($3 && $3->size()>1)
		csh.AddCSH_Error(std::next($3->begin())->file_pos,
			             "Around commands can only define one block.");
    $$ = $3;
  #else
    if ($3 && !chart.SkipContent()) {
		if ($3->size()>1) {
			chart.Error.Error((*std::next($3->begin()))->file_pos.start,
			                  "Around commands can only define one block. Ignoring the rest.");
			$3->erase(std::next($3->begin()), $3->end());
		}
		if ($3->size())
			$3->front()->AddAround($2, @2);
        $$ = $3;
    } else {
        $$ = nullptr;
        delete $2;
        delete $3;
    }
  #endif
    (void)$1;
}
      | TOK_AROUND_COMMAND block_def
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.parent_name = csh.GetCurrentPrefix(); //we dont have content: restore prefix to that of our surrounding context
    csh.AddCSH_ErrorAfter(@1, "Missing block name to be around.");
    if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddEntityNamesAtTheEnd("Enclose block %s in another block.");
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@1.after(), "Missing block name to be around. Ignoring 'around' clause.");
  #endif
    $$ = $2;
    (void)$1;
};


alignment_modifiers: TOK_ALIGN_MODIFIER
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = BlockBlock::TranslatePre(chart, $1, @1);
  #endif
}
      | TOK_ALIGN_MODIFIER TOK_ALIGN_MODIFIER
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_KEYWORD);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@1, @2, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Position the block under definition in relation to block %s.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAt(@2, EHintSourceType::KEYWORD)) {
        csh.AddKeywordsToHints(true, false, true);
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = BlockBlock::TranslatePre(chart, $1, $2, @1, @2);
  #endif
}
      | TOK_ALIGN_MODIFIER blocknames_plus_number_or_number
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_NameList($2, @2, COLOR_ENTITYNAME, COLOR_COMMA, COLOR_ATTRVALUE, COLOR_ENTITYNAME);
    if (csh.CheckHintLocated(@2)) {
        csh.HandleBlockNamePlus("modifier");
    } else if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddKeywordsToHints(true, false, true);
        csh.AddEntityNamesAtTheEnd("Position the block under definition in relation to block %s.");
        csh.hintStatus = HINT_READY;
    }
    delete $2;
  #else
    $$ = BlockBlock::TranslatePre(chart, $1, @1, $2);
  #endif
}
      | TOK_ALIGN_MODIFIER coord_hinted
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = BlockBlock::TranslatePre(chart, $1, @1, $2, @2);
  #endif
  $2.destroy();
}
      | TOK_ALIGN_MODIFIER TOK_ALIGN_MODIFIER blocknames_plus_number_or_number
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_KEYWORD);
    csh.AddCSH_NameList($3, @3, COLOR_ENTITYNAME, COLOR_COMMA, COLOR_ATTRVALUE, COLOR_ENTITYNAME);
    if (csh.CheckHintLocated(@3)) {
        csh.HandleBlockNamePlus("modifier");
    } else if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAt(@2, EHintSourceType::KEYWORD)) {
        csh.AddKeywordsToHints(true, false, true);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@2, @3, EHintSourceType::KEYWORD)) {
        csh.AddKeywordsToHints(false, false, true);
        csh.AddEntityNamesAtTheEnd("Position the block under definition in relation to block %s.");
        csh.hintStatus = HINT_READY;
    }
    delete $3;
  #else
    $$ = BlockBlock::TranslatePre(chart, $1, $2, @1, @2, $3);
  #endif
}
      | TOK_ALIGN_MODIFIER TOK_ALIGN_MODIFIER coord_hinted
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_KEYWORD);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAt(@2, EHintSourceType::KEYWORD)) {
        csh.AddKeywordsToHints(true, false, true);
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = BlockBlock::TranslatePre(chart, $1, $2, @1, @2, $3, @3);
  #endif
    $3.destroy();
}
      | TOK_ALIGN_MODIFIER blocknames_plus_number_or_number TOK_ALIGN_MODIFIER
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_NameList($2, @2, COLOR_ENTITYNAME, COLOR_COMMA, COLOR_ATTRVALUE, COLOR_ENTITYNAME);
    csh.AddCSH(@3, COLOR_KEYWORD);
    if (csh.CheckHintLocated(@2)) {
        csh.HandleBlockNamePlus("modifier");
    } else if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@1, @2, EHintSourceType::ENTITY)) {
        csh.AddKeywordsToHints(true, false, true);
        csh.AddEntityNamesAtTheEnd("Position the block under definition in relation to block %s.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAt(@3, EHintSourceType::KEYWORD)) {
        csh.AddKeywordsToHints(false, false, true);
        csh.hintStatus = HINT_READY;
    }
    delete $2;
  #else
    $$ = BlockBlock::TranslatePre(chart, $1, $3, @1, @3, $2);
  #endif
}
      | TOK_ALIGN_MODIFIER coord_hinted TOK_ALIGN_MODIFIER
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@3, COLOR_KEYWORD);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAt(@3, EHintSourceType::KEYWORD)) {
        csh.AddKeywordsToHints(false, false, true);
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = BlockBlock::TranslatePre(chart, $1, $3, @1, @3, $2, @2);
  #endif
    $2.destroy();
};

full_block_def_with_pre: full_block_def
      | alignment_modifiers
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_ErrorAfter(@1, "Missing block definition.");
  #else
    chart.Error.Error(@1.after(), "Missing block definition. Ignoring this.");
    delete $1;
  #endif
    $$ = nullptr;
}
      | alignment_modifiers full_block_def
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintBetween(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddKeywordsToHints(true, false, true);
        csh.AddEntityNamesAtTheEnd("Position the block under definition in relation to block %s.");
        csh.hintStatus = HINT_READY;
    }
  #else
    //Take care not to make changes to the chart if we are storing a procedure
    if ($2 && $2->size())
        $2->front()->ApplyPre($1);
    else
        delete $1;
  #endif
    $$ = $2;
};


multi_command: TOK_MULTI_COMMAND
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAfter(@1)) {
        csh.AddKeywordsToHints(false, false, false);
        csh.hintStatus = HINT_READY;
    }
  #else
  #endif
    (void)$1;
    $$ = 3;
}
      | TOK_MULTI_COMMAND TOK_NUMBER
{
    const double d = to_double($2);
    int num = d==floor(d) && d>0 && d<=10 ? int(d) : 0;
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAfter(@1)) {
        csh.AddKeywordsToHints(false, false, false);
        csh.hintStatus = HINT_READY;
    }
    if (num)
        csh.AddCSH(@2, COLOR_ATTRVALUE);
    else
        csh.AddCSH_Error(@2, "Expecting number between [1..10].");
  #else
    if (num==0) {
        chart.Error.Error(@2, "Expecting number between [1..10]. Assuming 3.");
        num=3;
    }
  #endif
    (void)$1;
    (void)$2;
    $$ = num;
}
      | TOK_MULTI_COMMAND tok_param_name_as_multi
{
    int num = 3;
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAfter(@1)) {
        csh.AddKeywordsToHints(false, false, false);
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH(@2, COLOR_ATTRVALUE);
  #else
    if (!chart.SkipContent()) {
        if (!$2.had_error) {
            if (from_chars($2, num) || num<1 || num>10) {
                chart.Error.Error(@2,
                    StrCat("Expecting number between [1..10], instead of '", $2,
                           "'. Assuming 3."));
                num = 3;
            }
        }
    }
  #endif
    (void)$1;
    $2.destroy();
    $$ = num;
};


full_block_def: block_def
      | multi_command block_def
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintAfter(@1, EHintSourceType::KEYWORD)) {
        csh.AddKeywordsToHints(false, false, false);
        csh.hintStatus = HINT_READY;
    }
    $$ = csh.MakeMulti($1, $2);
  #else
    //Create multi silently does nothing if $2 is  null - SkipContent() is handled that way
    $$ = chart.CreateMulti($1, $2, @$);
  #endif
}
      | multi_command
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintAfter(@1, EHintSourceType::KEYWORD)) {
        csh.AddKeywordsToHints(false, false, false);
        csh.hintStatus = HINT_READY;
    }
  #endif
    $$ = nullptr;
    (void)$1; //to make bison stop complaining
}
      | multi_command entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_AfterMultiPartial(@2, $2);
    if (csh.CheckHintAt(@2, EHintSourceType::KEYWORD)) {
        csh.AddKeywordsToHints(false, false, false);
        csh.hintStatus = HINT_READY;
    }
  #endif
    $$ = nullptr;
    (void)$1; //to make bison stop complaining
    $2.destroy();
};


block_def: block_keyword
{
  #ifdef C_S_H_IS_COMPILED
    if ($1!=EBlockType::Chart)
        csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_NewBlock(-1);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddBlockKeywordsToHints();
        csh.hintStatus = HINT_READY;
    }
    $$ = new CshStringWithPosList{{"", @$}};
  #else
    //Take care not to make changes to the chart if we are storing a procedure
    if (!chart.SkipContent()) {
	    $$ = new BlockBlockList;
        $$->Append(std::make_unique<BlockBlock>(chart, $1, @$));
        $$->back()->AddAttributeList(nullptr);
    } else
        $$ = nullptr;
  #endif
}
      | block_keyword blocknameposlist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    for (auto &sp: *$2)
        csh.AddCSH_NewBlock(sp.file_pos, sp.name, -1);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddBlockKeywordsToHints();
        csh.hintStatus = HINT_READY;
    }
    $$ = new CshStringWithPosList($2->begin(), $2->end());
    delete $2;
  #else
    //Take care not to make changes to the chart if we are storing a procedure
    if (!chart.SkipContent()) {
	    $$ = new BlockBlockList;
        for (auto &sp: *$2) {
            $$->Append(std::make_unique<BlockBlock>(chart, $1,
                                                    $2->size()== 1 ? @$ : sp.file_pos,
                                                    sp.name));
            $$->back()->AddAttributeList(nullptr);
        }
        if ($$->size())
            chart.current_parent = $$->front().get();
    } else
        $$ = nullptr;
    delete $2;
  #endif
}
      | block_keyword full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_NewBlock(-1);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddBlockKeywordsToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        //block_keyword returns a character specific to the type of block we have
        //we use it to filter attributes
        BlockBlock::AttributeNames($1, csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        BlockBlock::AttributeValues($1, csh.hintAttrName, csh);
    $$ = new CshStringWithPosList{{"", @$}};
  #else
    //Take care not to make changes to the chart if we are storing a procedure
    if (!chart.SkipContent())  {
	    $$ = new BlockBlockList;
        $$->Append(std::make_unique<BlockBlock>(chart, $1, @$));
        $$->back()->AddAttributeList($2);
    } else {
        $$ = nullptr;
        delete $2;
    }
  #endif
}
      | block_keyword blocknameposlist full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    for (auto &sp: *$2)
        csh.AddCSH_NewBlock(sp.file_pos, sp.name, -1);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddBlockKeywordsToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @3))
        //block_keyword returns a character specific to the type of block we have
        //we use it to filter attributes
        BlockBlock::AttributeNames($1, csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @3))
        BlockBlock::AttributeValues($1, csh.hintAttrName, csh);
    $$ = new CshStringWithPosList($2->begin(), $2->end());
    delete $2;
  #else
    //Take care not to make changes to the chart if we are storing a procedure
    if (!chart.SkipContent())  {
	    $$ = new BlockBlockList;
        for (auto &sp: *$2) {
            $$->Append(std::make_unique<BlockBlock>(chart, $1,
                                                    $2->size()== 1 ? @$ : sp.file_pos,
                                                    sp.name));
            if (&sp == &$2->back())
                $$->back()->AddAttributeList($3);
            else
                $$->back()->AddAttributeList(Duplicate(*$3).release());
        }
        if ($$->size())
            chart.current_parent = $$->front().get();
    } else {
        $$ = nullptr;
        delete $3;
    }
    delete $2;
  #endif
}
      | shape_block_header
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_NewBlock($1);
    $$ = new CshStringWithPosList{{"", @$}};
  #else
    //Take care not to make changes to the chart if we are storing a procedure
    if (!chart.SkipContent() && $1>=0) {
	    $$ = new BlockBlockList;
        $$->Append(std::make_unique<BlockBlock>(chart, $1, @$));
        $$->back()->AddAttributeList(nullptr);
    } else
        $$ = nullptr;
  #endif
}
      | shape_block_header blocknameposlist
{
  #ifdef C_S_H_IS_COMPILED
    for (auto &sp: *$2)
        csh.AddCSH_NewBlock(sp.file_pos, sp.name, $1);
    $$ = new CshStringWithPosList($2->begin(), $2->end());
    delete $2;
  #else
    //Take care not to make changes to the chart if we are storing a procedure
    if (!chart.SkipContent() && $1>=0) {
	    $$ = new BlockBlockList;
        for (auto &sp: *$2) {
            $$->Append(std::make_unique<BlockBlock>(chart, $1,
                                                    $2->size()== 1 ? @$ : sp.file_pos,
                                                    sp.name));
            $$->back()->AddAttributeList(nullptr);
        }
        if ($$->size())
            chart.current_parent = $$->front().get();
    } else
        $$ = nullptr;
    delete $2;
  #endif
}
      | shape_block_header full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_NewBlock($1);
    //block_keyword returns a character specific to the type of block we have
    //we use it to filter attributes
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        BlockBlock::AttributeNames(EBlockType::Shape, csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        BlockBlock::AttributeValues(EBlockType::Shape, csh.hintAttrName, csh);
    $$ = new CshStringWithPosList{{"", @$}};
  #else
    //Take care not to make changes to the chart if we are storing a procedure
    if (!chart.SkipContent() && $1>=0)  {
	    $$ = new BlockBlockList;
        $$->Append(std::make_unique<BlockBlock>(chart, $1, @$));
        $$->back()->AddAttributeList($2);
    } else {
        $$ = nullptr;
        delete $2;
    }
  #endif
}
      | shape_block_header blocknameposlist full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    for (auto &sp: *$2)
        csh.AddCSH_NewBlock(sp.file_pos, sp.name, $1);
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @3))
        BlockBlock::AttributeNames(EBlockType::Shape, csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @3))
        BlockBlock::AttributeValues(EBlockType::Shape, csh.hintAttrName, csh);
    $$ = new CshStringWithPosList($2->begin(), $2->end());
    delete $2;
  #else
    //Take care not to make changes to the chart if we are storing a procedure
    if (!chart.SkipContent() && $1>=0)  {
	    $$ = new BlockBlockList;
        for (auto &sp: *$2) {
            $$->Append(std::make_unique<BlockBlock>(chart, $1,
                                                    $2->size()== 1 ? @$ : sp.file_pos,
                                                    sp.name));
            if (&sp == &$2->back())
                $$->back()->AddAttributeList($3);
            else
                $$->back()->AddAttributeList(Duplicate(*$3).release());
        }
        if ($$->size())
            chart.current_parent = $$->front().get();
    } else {
        $$ = nullptr;
        delete $3;
    }
    delete $2;
  #endif
}
      | TOK_SPACE_COMMAND
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_KEYWORD);
    if (csh.CheckHintAfter(@1, EHintSourceType::ATTR_VALUE)) {
        csh.AddToHints(CshHint(csh.HintPrefixNonSelectable() + "<number>",
                               "Specify the size of the empty space in pixels (defaults to 10).",
                               EHintType::KEYWORD, false));
        csh.hintStatus = HINT_READY;
    }
    $$ = new CshStringWithPosList{{"", @$}};
  #else
    if (!chart.SkipContent()) {
  		$$ = new BlockBlockList;
      $$->Append(std::make_unique<BlockSpace>(chart, @1));
    } else
        $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_SPACE_COMMAND TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_ATTRVALUE);
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_VALUE)) {
        csh.AddToHints(CshHint(csh.HintPrefixNonSelectable() + "<number>",
                               "Specify the size of the empty space in pixels (defaults to 10).",
                               EHintType::KEYWORD, false));
        csh.hintStatus = HINT_READY;
    }
    $$ = new CshStringWithPosList{{"", @$}};
 #else
    if (!chart.SkipContent()) {
	    $$ = new BlockBlockList;
      $$->Append(std::make_unique<BlockSpace>(chart, @1, to_int($2)));
    } else
        $$ = nullptr;
  #endif
    (void)$1;
    (void)$2;
}
      | TOK_SPACE_COMMAND tok_param_name_as_multi
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_ATTRVALUE);
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_VALUE)) {
        csh.AddToHints(CshHint(csh.HintPrefixNonSelectable() + "<number>",
                               "Specify the size of the empty space in pixels (defaults to 10).",
                               EHintType::KEYWORD));
        csh.hintStatus = HINT_READY;
    }
    $$ = new CshStringWithPosList{{"", @$}};
  #else
    if (!chart.SkipContent()) {
        double s = -1;
        if (!$2.had_error) {
            if (from_chars($2, s) || s<0) {
                s = -1;
                chart.Error.Error(@2,
                    StrCat("A positive number is needed here, instead of '", $2,
                           "'. Ignoring value."));
            }
        }
	    $$ = new BlockBlockList;
        $$->Append(std::make_unique<BlockSpace>(chart, @1, s));
    } else
        $$ = nullptr;
  #endif
    (void)$1;
    $2.destroy();
}
      | inlined_chart_header 
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_NewBlock(-1);
    $$ = new CshStringWithPosList{{"", @$}};
  #else
    //Take care not to make changes to the chart if we are storing a procedure
    if (!chart.SkipContent()) {
	    $$ = new BlockBlockList;
        $$->Append(std::make_unique<BlockInlinedChart>(chart, $1, @$));
        $$->back()->AddAttributeList(nullptr);
    } else
        $$ = nullptr;
  #endif
}
      | inlined_chart_header blocknameposlist
{
  #ifdef C_S_H_IS_COMPILED
    for (auto &sp: *$2)
        csh.AddCSH_NewBlock(sp.file_pos, sp.name, -1);
    $$ = new CshStringWithPosList($2->begin(), $2->end());
    delete $2;
  #else
    //Take care not to make changes to the chart if we are storing a procedure
    if (!chart.SkipContent()) {
	    $$ = new BlockBlockList;
        for (auto &sp: *$2) {
            $$->Append(std::make_unique<BlockInlinedChart>(chart, $1,
                                                          $2->size()== 1 ? @$ : sp.file_pos,
                                                          sp.name));
            $$->back()->AddAttributeList(nullptr);
        }
        if ($$->size())
            chart.current_parent = $$->front().get();
    } else
        $$ = nullptr;
    delete $2;
  #endif
}
      | inlined_chart_header full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_NewBlock(-1);
    //block_keyword returns a character specific to the type of block we have
    //we use it to filter attributes
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        BlockBlock::AttributeNames(EBlockType::Chart, csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        BlockBlock::AttributeValues(EBlockType::Chart, csh.hintAttrName, csh);
    $$ = new CshStringWithPosList{{"", @$}};
  #else
    //Take care not to make changes to the chart if we are storing a procedure
    if (!chart.SkipContent())  {
	    $$ = new BlockBlockList;
        $$->Append(std::make_unique<BlockInlinedChart>(chart, $1, @$));
        $$->back()->AddAttributeList($2);
    } else {
        $$ = nullptr;
        delete $2;
    }
  #endif
}
      | inlined_chart_header blocknameposlist full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    for (auto &sp: *$2)
        csh.AddCSH_NewBlock(sp.file_pos, sp.name, -1);
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @3))
        BlockBlock::AttributeNames(EBlockType::Chart, csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @3))
        BlockBlock::AttributeValues(EBlockType::Chart, csh.hintAttrName, csh);
    $$ = new CshStringWithPosList($2->begin(), $2->end());
    delete $2;
  #else
    //Take care not to make changes to the chart if we are storing a procedure
    if (!chart.SkipContent())  {
	    $$ = new BlockBlockList;
        for (auto &sp: *$2) {
            $$->Append(std::make_unique<BlockInlinedChart>(chart, $1,
                                                          $2->size()== 1 ? @$ : sp.file_pos,
                                                          sp.name));
            if (&sp == &$2->back())
                $$->back()->AddAttributeList($3);
            else
                $$->back()->AddAttributeList(Duplicate(*$3).release());
        }
        if ($$->size())
            chart.current_parent = $$->front().get();
    } else {
        $$ = nullptr;
        delete $3;
    }
    delete $2;
  #endif
};



block_keyword: TOK_BOX    { (void)$1; $$=EBlockType::Box; }
      | TOK_BOXCOL { (void)$1; $$=EBlockType::Boxcol; }
      | TOK_ROW    { (void)$1; $$=EBlockType::Row; }
      | TOK_COLUMN { (void)$1; $$=EBlockType::Column; }
      | TOK_TEXT   { (void)$1; $$=EBlockType::Text;}
      | TOK_CELL   { (void)$1; $$=EBlockType::Cell;};


shape_block_header: TOK_SHAPE
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@1, "Missing shape name to use.");
    if (csh.CheckHintAfter(@1, EHintSourceType::ATTR_VALUE)) {
        csh.AddShapesToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@1, "Missing shape name to use. Ignoring this shape.");
  #endif
    $$ = -1;
    (void)$1;
}
      | TOK_SHAPE entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::ATTR_VALUE)) {
        csh.AddShapesToHints();
        csh.hintStatus = HINT_READY;
    }
    if ($2.had_error)  {
        $$ = -1;
    } else {
        $$ = csh.GetShapeNum($2);
        if ($$>=0)
            csh.AddCSH(@2, COLOR_ATTRVALUE_EMPH);
        else
            csh.AddCSH_Error(@2, "Unrecognized shape name.");
    }
  #else
    if ($2.had_error)
        $$ = -1;
    else {
        $$ = chart.Shapes.GetShapeNo($2);
        if ($$==-1)
            chart.Error.Error(@2, "Unrecognized shape name. Ignoring this shape.");
    }
  #endif
    (void)$1;
    $2.destroy();
}
      | TOK_ASTERISK
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@1, "Missing shape name to use.");
    if (csh.CheckHintAfter(@1, EHintSourceType::ATTR_VALUE)) {
        csh.AddShapesToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@1, "Missing shape name to use. Ignoring this shape.");
  #endif
    $$ = -1;
}
      | TOK_ASTERISK entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::ATTR_VALUE)) {
        csh.AddShapesToHints();
        csh.hintStatus = HINT_READY;
    }
    if ($2.had_error)  {
        $$ = -1;
    } else {
        $$ = csh.GetShapeNum($2);
        if ($$>=0)
            csh.AddCSH(@2, COLOR_ATTRVALUE_EMPH);
        else
            csh.AddCSH_Error(@2, "Unrecognized shape name.");
    }
  #else
    if ($2.had_error)
        $$ = -1;
    else {
        $$ = chart.Shapes.GetShapeNo($2);
        if ($$==-1)
            chart.Error.Error(@2, "Unrecognized shape name. Ignoring this shape.");
    }
  #endif
    $2.destroy();
};


//returns the language or "" if invalid (error emietted)
 inlined_chart_header: TOK_CHART  
{ 
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.NoteInliningLanguage("", {});
    csh.AddCSH_ErrorAfter(@1, "Missing chart language.");
    if (csh.CheckHintAfter(@1, EHintSourceType::ATTR_VALUE)) {
        csh.AddLanguagesToHints("language name (and potential attributes)");
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@1.after(), "Missing chart language.", "Use one of "+chart.languages->GetLanguageNames()+".");
  #endif
    $$ = {};
    (void)$1; 
}
      | TOK_CHART entity_string_single
{ 
    $$ = {};
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.NoteInliningLanguage($2, @2)) {
        csh.AddCSH(@2, COLOR_ATTRNAME);
        $$ = $2;
    } else
        csh.AddCSH_Error(@2, "Invalid chart language. Use one of "+csh.languages->GetLanguageNames()+".");
    if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::ATTR_VALUE)) {
        csh.AddLanguagesToHints("language name (and potential attributes)");
        csh.hintStatus = HINT_READY;
    }
  #else
    if (chart.languages->GetLanguage($2))
        $$ = $2;
    else
        chart.Error.Error(@2.start(), "Invalid chart language.", "Use one of "+chart.languages->GetLanguageNames()+".");
  #endif
    (void)$1; 
};

blocknameposlist: entity_string
{
  #ifdef C_S_H_IS_COMPILED
    $$ = new CshStringViewWithPosList;
    if (!$1.had_error && !$1.empty())
        $$->push_back({$1, @1});
  #else
    $$ = new StringWithPosList;
    if (!$1.had_error && !$1.empty())
        $$->push_back({$1, @1});
  #endif
    $1.destroy();
}
      | stylenameposlist TOK_COMMA
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_ErrorAfter(@2, "Missing a block name to define.");
  #else
    chart.Error.Error(@$.after(), "Missing a style name to (re)define.");
  #endif
    $$ = $1;
}
      | stylenameposlist TOK_COMMA entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (!$3.had_error && !$3.empty())
        $1->push_back({$3, @3});
  #else
    if (!$3.had_error && !$3.empty())
        $1->push_back({$3, @3});
  #endif
    $$ = $1;
    $3.destroy();
};


/*************************************************************************
 * Commands
 *************************************************************************/

command: TOK_BREAK_COMMAND
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_KEYWORD);
  #else
    if (!chart.SkipContent()) {
        auto p = new BlockBreak(chart, @1);
        p->SetContent(nullptr);
        $$ = p;
    } else
        $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_JOIN_COMMAND
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@1, "Missing block definition.");
    if (csh.CheckHintAfter(@1, EHintSourceType::KEYWORD)) {
        csh.AddEntityNamesAtTheEnd("Join block %s with other blocks.");
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@1.after(), "Missing block names to join. Ignoring 'join' keyword here.");
  #endif
    $$ = nullptr;
    (void)$1;
}
      | TOK_JOIN_COMMAND blocknames_plus
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_NameList($2, @2, COLOR_ENTITYNAME, COLOR_COMMA);
    if (csh.CheckHintLocated(@2)) {
        csh.AddEntityNamesAtTheEnd("Join block %s with other blocks.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@1, EHintSourceType::KEYWORD)) {
        csh.AddEntityNamesAtTheEnd("Join block %s with other blocks.");
        csh.hintStatus = HINT_READY;
    }
    delete $2;
  #else
    if (!chart.SkipContent())
        $$ = chart.CreateJoin(@$, $2);
    else {
        $$ = nullptr;
        delete $2;
    }
  #endif
    (void)$1;
}
      | TOK_JOIN_COMMAND full_attrlist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintAfter(@1, EHintSourceType::KEYWORD)) {
        csh.AddEntityNamesAtTheEnd("Join block %s with other blocks.");
        csh.hintStatus = HINT_READY;
    }
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        JoinCommand::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        JoinCommand::AttributeValues(csh.hintAttrName, csh);
  #else
    chart.Error.Error(@1.after(), "Missing block names to join. Ignoring command.");
    delete $2;
  #endif
    $$ = nullptr;
    (void)$1;
}
      | TOK_JOIN_COMMAND blocknames_plus full_attrlist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_NameList($2, @2, COLOR_ENTITYNAME, COLOR_COMMA);
    if (csh.CheckHintLocated(@2)) {
        csh.AddEntityNamesAtTheEnd("Join block %s with other blocks.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @3))
        JoinCommand::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @3))
        JoinCommand::AttributeValues(csh.hintAttrName, csh);
    else if (csh.CheckHintAfter(@1, EHintSourceType::KEYWORD)) {
        csh.AddEntityNamesAtTheEnd("Join block %s with other blocks.");
        csh.hintStatus = HINT_READY;
    }
    delete $2;
  #else
    if (!chart.SkipContent())
        $$ = chart.CreateJoin(@$, $2, $3);
    else {
        $$ = nullptr;
        delete $2;
    }
  #endif
    (void)$1;
};

copy_header: TOK_COPY_COMMAND
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@1, "Missing block name to copy.");
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@1, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Create a copy of block %s.");
        csh.hintStatus = HINT_READY;
    }
    $$.init();
  #else
    $$ = new CopyParseHelper;
    $$->init();
    $$->file_pos = @$;
    chart.Error.Error(@1.after(), "Missing block name to copy.");
  #endif
    (void)$1;
}
      | TOK_COPY_COMMAND entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.hadProcReplay = true;
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAt(@2, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Create a copy of block %s.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@2, EHintSourceType::KEYWORD)) {
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_KEYWORD) + "as",
                               "Optionally continue with 'as' to specify the name of the block or with attributes or content.",
                               EHintType::KEYWORD));
        csh.hintStatus = HINT_READY;
    }
    if (!$2.had_error)
        csh.Copy($2, $2, @2, @2);
    $$ = $2;
  #else
    $$ = new CopyParseHelper;
    $$->init();
    if (!$2.had_error) {
        $$->file_pos = @$;
        $$->block_name = $2;
        $$->block_pos = @2;
    } else
        $2.destroy();
  #endif
    (void)$1;
}
      | TOK_COPY_COMMAND entity_string TOK_AS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@3, COLOR_KEYWORD);
    csh.hadProcReplay = true;
    csh.AddCSH_ErrorAfter(@3, "Missing a name for the copy.");
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAt(@2, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Create a copy of block %s.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAt(@3, EHintSourceType::KEYWORD)) {
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_KEYWORD) + "as",
                               "Optionally continue with 'as' to specify the name of the block or with attributes or content.",
                               EHintType::KEYWORD));
        csh.hintStatus = HINT_READY;
    }
    if (!$2.had_error)
        csh.Copy($2, $2, @2, @2);
    $$ = $2;
  #else
    $$ = new CopyParseHelper;
    $$->init();
    if (!$2.had_error) {
        $$->file_pos = @$;
        $$->block_name = $2;
        $$->block_pos = @2;
    }
    chart.Error.Error(@3.after(), "Missing a name for the copy.");
  #endif
    (void)$1;
    (void)$3;
}
      | TOK_COPY_COMMAND entity_string TOK_AS entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@3, COLOR_KEYWORD);
    csh.hadProcReplay = true;
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAt(@2, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Create a copy of block %s.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAt(@3, EHintSourceType::KEYWORD)) {
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_KEYWORD) + "as",
                               "Optionally continue with 'as' to specify the name of the block or with attributes or content.",
                               EHintType::KEYWORD));
        csh.hintStatus = HINT_READY;
    }
    if (!$2.had_error && !$4.had_error)
        csh.Copy($2, $4, @2, @4);
    $2.destroy();
    $$ = $4;
  #else
    $$ = new CopyParseHelper;
    $$->init();
    if (!$2.had_error) {
        $$->file_pos = @$;
        $$->block_name = $2;
        $$->block_pos = @2;
    } else
        $2.destroy();
    if (!$4.had_error)
        $$->copy_name = $4;
    else
        $4.destroy();
  #endif
    (void)$1;
    (void)$3;
};

copy_header_resolved: copy_header
{
  #ifdef C_S_H_IS_COMPILED
    $$ = $1;
  #else
    $$ = chart.ResolveCopyHelper($1);
  #endif
};

copy_header_with_pre: copy_header_resolved
      | alignment_modifiers copy_header_resolved
{
  #ifndef C_S_H_IS_COMPILED
    _ASSERT($1); //copy_header should never return null
    $2->align = $1;
  #endif
    $$ = $2;
}

copy_attr: copy_header_with_pre
      | copy_header_with_pre full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        BlockBlock::AttributeNames(EBlockType::Box, csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        BlockBlock::AttributeValues(EBlockType::Box, csh.hintAttrName, csh);
    $$ = $1;
  #else
    _ASSERT($1); //copy_header should never return null
    $$ = $1;
    $$->attributes = $2;
  #endif
};


/*************************************************************************
 * Arrows
 *************************************************************************/

arrowsymbol:    TOK_ARROW_FW    {$$ = {$1, true,  false};}
      | TOK_ARROW_BW    {$$ = {$1, false, true};}
      | TOK_ARROW_BIDIR {$$ = {$1, true,  true};}
      | TOK_ARROW_NO    {$$ = {$1, false, false};}
      | TOK_PLUS_PLUS   {$$ = {EArrowStyle::DASHED, false, false};}
      | TOK_COMP_OP
{
    switch ($1) {
    case ECompareOperator::SMALLER:          $$ =  {EArrowStyle::DOTTED, false, true};  break;
    case ECompareOperator::SMALLER_OR_EQUAL: $$ =  {EArrowStyle::DOUBLE, false, true};  break;
    case ECompareOperator::EQUAL:            $$ =  {EArrowStyle::DOTTED, false, false}; break;
    case ECompareOperator::NOT_EQUAL:        $$ =  {EArrowStyle::DOTTED, true,  true};  break;
    default: _ASSERT(0); FALLTHROUGH;
    case ECompareOperator::GREATER_OR_EQUAL: $$ =  {EArrowStyle::DOUBLE, true,  false}; break;
    case ECompareOperator::GREATER:          $$ =  {EArrowStyle::DOTTED, true,  false}; break;
    }
};

coord_hinted_with_dir: coord_hinted
      | coord_hinted TOK_ATSYMBOL
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_ATTRVALUE);
    if (csh.CheckHintAfter(@2, EHintSourceType::ENTITY)) { //hint source for ports and compass points is entity
        csh.AddCompassPointsToHints();
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH_ErrorAfter(@$, "Missing direction or compass point.");
  #else
    chart.Error.Error(@$.after(), "Missing direction or compass point.");
  #endif
    $$ = $1;
}
      | coord_hinted TOK_ATSYMBOL alpha_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_ATTRVALUE);
    if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ENTITY)) { //hint source for ports and compass points is entity
        csh.AddCompassPointsToHints();
        csh.hintStatus = HINT_READY;
    }
  #endif
    $$.CombineThemToMe($1, '@', $3);
}
      | coord_hinted TOK_ATSYMBOL TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_ATTRVALUE);
    if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ENTITY)) { //hint source for ports and compass points is entity
        csh.AddCompassPointsToHints();
        csh.hintStatus = HINT_READY;
    }
  #endif
    $$.CombineThemToMe($1, '@', $3);
};

coord_hinted_with_dir_and_dist: coord_hinted_with_dir
      | coord_hinted_with_dir TOK_ATSYMBOL
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_ATTRVALUE);
    csh.AddCSH_ErrorAfter(@$, "Missing distance value for the direction.");
  #else
    chart.Error.Error(@$.after(), "Missing distance value for the direction.");
  #endif
    $$ = $1;
}
      | coord_hinted_with_dir TOK_ATSYMBOL TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_ATTRVALUE);
    if ($3[0]=='-'||$3[0]=='+')
        $$.CombineThemToMe($1, '@', $3);
    else
        $$.CombineThemToMe($1, "@+", $3);
  #else
    if ($3[0]=='-'||$3[0]=='+')
        $$.CombineThemToMe($1, '@', @3.start().Print(), $3);
    else
        $$.CombineThemToMe($1, '@', @3.start().Print(), '+', $3);
  #endif
};


/* This will leave the hintstatus located and in LINE start if the hint is in the leading
 * entity_string.*/
arrow_end: coord_hinted_with_dir_and_dist
      | coord_hinted_with_dir_and_dist distance_for_arrow_end
{
    $$.CombineThemToMe($1, $2);
}
      | arrow_end_block_port_compass_distance
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::ENTITY, @1)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_LOCATED;
        csh.hintSource = EHintSourceType::LINE_START;
    }
  #endif
    $$ = $1;
};

tok_plus_or_minus: TOK_PLUS {$$=+1;} | TOK_MINUS {$$=-1;};

arrow_end_block_port_compass_distance: arrow_end_block_port_compass
      | arrow_end_block_port_compass_distance distance_for_arrow_end
{
    $$.CombineThemToMe($1, $2);
}
      | entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_BlockNameOrNew(@1, $1);
    csh.CheckHintAt(@1, EHintSourceType::ENTITY); //We need to hint later, when we know the context
    //Hinting is left in HINT_LOCATED
  #endif
    $$ = $1;
}
      | entity_string distance_for_arrow_end
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_BlockNameOrNew(@1, $1);
    csh.CheckHintAt(@1, EHintSourceType::ENTITY); //We need to hint later, when we know the context
    //Hinting is left in HINT_LOCATED
  #endif
    $$.CombineThemToMe($1, $2);
};

 /* Will always start with a sign.*/
distance_for_arrow_end: tok_plus_or_minus
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_ATTRVALUE);
    csh.AddCSH_Error(@1, "Missing <num>, <num>%, x<num>, y<num>, x<num>% or y<num>%.");
    $$.set(str_view{$1==+1 ? "+" : "-"});
  #else
    chart.Error.Error(@$.after(), "Missing <num>, <num>%, x<num>, y<num>, x<num>% or y<num>%.");
    $$.init();
    (void)$1;
  #endif
}
      | TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_ATTRVALUE);
    $$.set($1);
  #else
    if ($1[0]=='-' || $1[0]=='+')
        $$.CombineThemToMe(@1.start().Print(), $1);
    else
        $$.CombineThemToMe("+", @1.start().Print(), $1);
  #endif
}
      | tok_plus_or_minus TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_ATTRVALUE);
    $$.CombineThemToMe($1==+1 ? '+' : '-', $2);
  #else
    if ($1 == +1) {
        if ($2[0]=='-' || $2[0]=='+')
            $$.CombineThemToMe(@2.start().Print(), $2);
        else
            $$.CombineThemToMe('+', @2.start().Print(), $2);
    } else {
        //An extra minus sign
        if ($2[0]=='-')
            $$.CombineThemToMe(@2.start().Print(), '+', $2.view().substr(1));
        else if ($2[0]=='+')
            $$.CombineThemToMe(@2.start().Print(), '-', $2.view().substr(1));
        else
            $$.CombineThemToMe(@2.start().Print(), '-', $2);
    }
  #endif
}
      | tok_plus_or_minus TOK_STRING
{
    _ASSERT($2); //a non-empty string
	double dummy;
    if ($2 && ($2[0]=='y' || $2[0]=='Y' || $2[0]=='x' || $2[0]=='X') &&
		!from_chars($2.view().substr(1), dummy)) {
#ifdef C_S_H_IS_COMPILED
		csh.AddCSH(@1, COLOR_ATTRVALUE);
		csh.AddCSH(CshPos(@2.first_pos, @2.first_pos.ShiftASCII(1)), COLOR_ATTRVALUE_EMPH);
		csh.AddCSH(CshPos{@2.first_pos.ShiftASCII(1), @2.end_pos}, COLOR_ATTRVALUE);

		$$.CombineThemToMe($1==+1 ? '+' : '-', $2);
#else
	    $$.CombineThemToMe($1==+1 ? '+' : '-', @2.start().Print(), $2);
#endif
	} else {
#ifdef C_S_H_IS_COMPILED
	    csh.AddCSH_Error(@2, "Expecting <num>, <num>%, x<num>, y<num>, x<num>% or y<num>%.");
#else
	    chart.Error.Error(@2, "Expecting <num>, <num>%, x<num>, y<num>, x<num>% or y<num>%. Ignoring this.");
#endif
	    $$.init();
	}
}
      | tok_plus_or_minus TOK_STRING TOK_PERCENT
{
    _ASSERT($2); //a non-empty string
	double dummy;
    if ($2 && ($2[0]=='y' || $2[0]=='Y' || $2[0]=='x' || $2[0]=='X') &&
		!from_chars($2.view().substr(1), dummy)) {
#ifdef C_S_H_IS_COMPILED
		csh.AddCSH(@1, COLOR_ATTRVALUE);
		csh.AddCSH(CshPos(@2.first_pos, @2.first_pos.ShiftASCII(1)), COLOR_ATTRVALUE_EMPH);
		csh.AddCSH(CshPos{@2.first_pos.ShiftASCII(1), @3.end_pos}, COLOR_ATTRVALUE);
		
        $$.CombineThemToMe($1==+1 ? '+' : '-', $2, '%');
#else
	    $$.CombineThemToMe($1==+1 ? '+' : '-', @2.start().Print(), $2, '%');
#endif
	} else {
#ifdef C_S_H_IS_COMPILED
	    csh.AddCSH_Error(@2, "Expecting <num>, <num>%, x<num>, y<num>, x<num>% or y<num>%.");
#else
	    chart.Error.Error(@2, "Expecting <num>, <num>%, x<num>, y<num>, x<num>% or y<num>%. Ignoring this.");
#endif
	    $$.init();
	}
};

arrow_end_block_port_compass: entity_string TOK_ATSYMBOL
{
  #ifdef C_S_H_IS_COMPILED
    auto shape = csh.AddCSH_BlockNameOrNew(@1, $1);
    csh.AddCSH(@2, COLOR_ATTRVALUE);
    if (csh.CheckHintAt(@1, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@2, EHintSourceType::ENTITY)) { //hint source for ports and compass points is entity
        csh.AddPortsToHints(shape);
        csh.AddCompassPointsToHints();
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH_ErrorAfter(@$, "Missing portname or compass point.");
  #else
    chart.Error.Error(@$.after(), "Missing portname or compass point.");
  #endif
    $$ = $1;
}
      | entity_string TOK_ATSYMBOL alpha_string
{
  #ifdef C_S_H_IS_COMPILED
    auto shape = csh.AddCSH_BlockNameOrNew(@1, $1);
    csh.AddCSH(@2+@3, COLOR_ATTRVALUE);
    if (csh.CheckHintAt(@1, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ENTITY)) { //hint source for ports and compass points is entity
        csh.AddPortsToHints(shape);
        csh.AddCompassPointsToHints();
        csh.hintStatus = HINT_READY;
    }

    $$.CombineThemToMe($1, '@', $3);
  #else
    $$.CombineThemToMe($1, '@', @3.start().Print(), $3);
  #endif
}
      | entity_string TOK_ATSYMBOL TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    auto shape = csh.AddCSH_BlockNameOrNew(@1, $1);
    csh.AddCSH(@2+@3, COLOR_ATTRVALUE);
    if (csh.CheckHintAt(@1, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ENTITY)) { //hint source for ports and compass points is entity
        csh.AddPortsToHints(shape);
        csh.AddCompassPointsToHints();
        csh.hintStatus = HINT_READY;
    }
    if ($3[0]=='-'||$3[0]=='+')
        $$.CombineThemToMe($1, '@', $3);
    else
        $$.CombineThemToMe($1, "@+", $3);
  #else
    if ($3[0]=='-'||$3[0]=='+')
        $$.CombineThemToMe($1, '@', @3.start().Print(), $3);
    else
        $$.CombineThemToMe($1, '@', @3.start().Print(), '+', $3);
  #endif
}
      | entity_string TOK_ATSYMBOL alpha_string TOK_ATSYMBOL
{
  #ifdef C_S_H_IS_COMPILED
    auto shape = csh.AddCSH_BlockNameOrNew(@1, $1);
    csh.AddCSH(@2+@4, COLOR_ATTRVALUE);
    if (csh.CheckHintAt(@1, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
    } else  if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ENTITY)) { //hint source for ports and compass points is entity
        csh.AddPortsToHints(shape);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@4, EHintSourceType::ENTITY)) { //hint source for ports and compass points is entity
        csh.AddCompassPointsToHints();
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH_ErrorAfter(@$, "Missing compass point.");
    $$.CombineThemToMe($1, '@', $3);
  #else
    $$.CombineThemToMe($1, '@', @3.start().Print(), $3);
    chart.Error.Error(@$.after(), "Missing compass point.");
  #endif
}
      | entity_string TOK_ATSYMBOL TOK_ATSYMBOL
{
  #ifdef C_S_H_IS_COMPILED
    auto shape = csh.AddCSH_BlockNameOrNew(@1, $1);
    csh.AddCSH(@2+@3, COLOR_ATTRVALUE);
    if (csh.CheckHintAt(@1, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@2, @3, EHintSourceType::ENTITY)) { //hint source for ports and compass points is entity
        csh.AddPortsToHints(shape);
        csh.AddCompassPointsToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@3, EHintSourceType::ENTITY)) { //hint source for ports and compass points is entity
        csh.AddCompassPointsToHints();
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH_ErrorAfter(@2, "Missing portname.");
    csh.AddCSH_ErrorAfter(@$, "Missing compass point.");
  #else
    chart.Error.Error(@2.after(), "Missing portname.");
    chart.Error.Error(@$.after(), "Missing compass point.");
  #endif
    $$ = $1;
}
      | entity_string TOK_ATSYMBOL TOK_ATSYMBOL entity_string
{
  #ifdef C_S_H_IS_COMPILED
    auto shape = csh.AddCSH_BlockNameOrNew(@1, $1);
    csh.AddCSH(@2+@4, COLOR_ATTRVALUE);
    if (csh.CheckHintAt(@1, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@2, @3, EHintSourceType::ENTITY)) { //hint source for ports and compass points is entity
        csh.AddPortsToHints(shape);
        csh.AddCompassPointsToHints();
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH_ErrorAfter(@2, "Missing portname.");
    $$.CombineThemToMe($1, '@', $4);
  #else
    $$.CombineThemToMe($1, '@', @4.start().Print(), $4);
    chart.Error.Error(@2.after(), "Missing portname.");
  #endif
}
      | entity_string TOK_ATSYMBOL TOK_ATSYMBOL TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    auto shape = csh.AddCSH_BlockNameOrNew(@1, $1);
    csh.AddCSH(@2+@4, COLOR_ATTRVALUE);
    if (csh.CheckHintAt(@1, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@2, @3, EHintSourceType::ENTITY)) { //hint source for ports and compass points is entity
        csh.AddPortsToHints(shape);
        csh.AddCompassPointsToHints();
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH_ErrorAfter(@2, "Missing portname.");
    if ($4[0]=='-'||$4[0]=='+')
        $$.CombineThemToMe($1, '@', $4);
    else
        $$.CombineThemToMe($1, "@+", $4);
  #else
    if ($4[0]=='-'||$4[0]=='+')
        $$.CombineThemToMe($1, '@', @4.start().Print(), $4);
    else
        $$.CombineThemToMe($1, '@', @4.start().Print(), '+', $4);
    chart.Error.Error(@2.after(), "Missing portname.");
  #endif
}
      | entity_string TOK_ATSYMBOL alpha_string TOK_ATSYMBOL entity_string
{
  #ifdef C_S_H_IS_COMPILED
    auto shape = csh.AddCSH_BlockNameOrNew(@1, $1);
    csh.AddCSH(@2+@5, COLOR_ATTRVALUE);
    if (csh.CheckHintAt(@1, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
    }  if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ENTITY)) { //hint source for ports and compass points is entity
        csh.AddPortsToHints(shape);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@4, @5, EHintSourceType::ENTITY)) { //hint source for ports and compass points is entity
        csh.AddCompassPointsToHints();
        csh.hintStatus = HINT_READY;
    }
    $$.CombineThemToMe($1, '@', $3, '@', $5);
  #else
    $$.CombineThemToMe($1, '@', @3.start().Print(), $3, '@', @5.start().Print(), $5);
  #endif
}
      | entity_string TOK_ATSYMBOL alpha_string TOK_ATSYMBOL TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    auto shape = csh.AddCSH_BlockNameOrNew(@1, $1);
    csh.AddCSH(@2+@5, COLOR_ATTRVALUE);
    if (csh.CheckHintAt(@1, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
    }  if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ENTITY)) { //hint source for ports and compass points is entity
        csh.AddPortsToHints(shape);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@4, @5, EHintSourceType::ENTITY)) { //hint source for ports and compass points is entity
        csh.AddCompassPointsToHints();
        csh.hintStatus = HINT_READY;
    }
    if ($5[0]=='-'||$5[0]=='+')
        $$.CombineThemToMe($1, '@', $3, '@', $5);
    else
        $$.CombineThemToMe($1, '@', $3, "@+", $5);
  #else
    if ($5[0]=='-'||$5[0]=='+')
        $$.CombineThemToMe($1, '@', @3.start().Print(), $3, '@', @5.start().Print(), $5);
    else
        $$.CombineThemToMe($1, '@', @3.start().Print(), $3, '@', @5.start().Print(), '+', $5);
  #endif
};


/* This will leave the hintstatus located and in LINE start if the hint is in the leading
 * entity_string.*/
arrowend_list: arrow_end TOK_COMMA
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    csh.AddCSH_ErrorAfter(@2, "Missing a block name or coordinate here.");
    if (csh.CheckHintLocated(EHintSourceType::LINE_START, @1)) {
        //leave in HINT_LOCATED
    } else if (csh.CheckHintAfter(@2, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
    }
    $$ = new CshStringViewWithPosList;
    if (!$1.had_error)
        $$->push_back({$1, @1});
  #else
    chart.Error.Error(@$.after(), "Missing a block name or coordinate here.");
    $$ = new StringWithPosList;
    if (!$1.had_error)
        $$->push_back({$1, @1});
  #endif
    $1.destroy();
}
      |  arrow_end TOK_COMMA arrow_end
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    if (csh.CheckHintLocated(EHintSourceType::LINE_START, @1)) {
        //leave in HINT_LOCATED
    } else if (csh.CheckHintLocated(EHintSourceType::LINE_START, @3)) {
        csh.hintStatus = HINT_READY;
        csh.hintSource = EHintSourceType::ENTITY;
    }
    $$ = new CshStringViewWithPosList;
    if (!$1.had_error)
        $$->push_back({$1, @1});
    if (!$3.had_error)
        $$->push_back({$3, @3});
  #else
    $$ = new StringWithPosList;
    if (!$1.had_error)
        $$->push_back({$1, @1});
    if (!$3.had_error)
        $$->push_back({$3, @3});
  #endif
    $1.destroy();
    $3.destroy();
}
      | arrowend_list TOK_COMMA
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
      csh.AddCSH_ErrorAfter(@2, "Missing a block name or coordinate here.");
    if (csh.CheckHintAfter(@2, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@$.after(), "Missing a block name or coordinate here.");
  #endif
    $$ = $1;
};
      | arrowend_list TOK_COMMA arrow_end
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    if (csh.CheckHintBetween(@2, @3, EHintSourceType::ATTR_NAME)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::LINE_START, @3)) {
        csh.hintStatus = HINT_READY;
        csh.hintSource = EHintSourceType::ENTITY;
    }
    if (!$3.had_error)
        $1->push_back({$3, @3});
  #else
    if ($1 && !$3.had_error)
        $1->push_back({$3, @3});
  #endif
    $$ = $1;
    $3.destroy();
};

/* This will leave the hintstatus located and in LINE start if the hint is in the leading
 * entity_string.*/
arrowend_list_solo: arrowend_list
      | arrow_end
{
  #ifdef C_S_H_IS_COMPILED
    //possinbly leave in HINT_LOCATED
    $$ = new CshStringViewWithPosList;
    if (!$1.had_error)
        $$->push_back({$1, @1});
  #else
    $$ = new StringWithPosList;
    if (!$1.had_error)
        $$->push_back({$1, @1});
  #endif
    $1.destroy();
};


 //This returns the list of the last comma separated arrow-end-strings
 //If we have NO stashed arrows, this means it was just a single list of
 //arrow-end-strings (more than one, a solo empty string is matched at
 //arrow_end_block_port_compass_distance), that is, the first rule below.

 /* Note that arrow_end, arrowend_list and arrowend_list_solo will leave
  * the hintstatus located and in LINE start if the hint is in the leading
  * entity_string of them. We need to handle that here.
  * Note that arrow_cont will have it already handled.*/

arrow_needs_semi: arrowend_list
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::LINE_START, @1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
    delete $1;
   //csh.AddCSH_ErrorAfter(@1, "Missing an arrow symbol or attributes here.");
  #else
    //chart.Error.Error(@$.after(), "Missing an arrow or attributes symbol here.");
	//Keep list of arrow_end strings
    $$ = $1;
  #endif
}
      | arrowend_list arrowsymbol
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SYMBOL);
    csh.AddCSH_ErrorAfter(@2, "Missing a block name or coordinate here.");
    if (csh.CheckHintLocated(EHintSourceType::LINE_START, @1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@2, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
    }
    delete $1;
  #else
    chart.Error.Error(@$.after(), "Missing a block name or coordinate here.");
    $$ = new StringWithPosList;
    delete $1;
  #endif
    (void)$2;
}
      | arrow_end arrowsymbol  /*arrowend_list does not cover a solo entity_string*/
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SYMBOL);
    csh.AddCSH_ErrorAfter(@2, "Missing a block name or coordinate here.");
    if (csh.CheckHintLocated(EHintSourceType::LINE_START, @1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@2, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@$.after(), "Missing a block name or coordinate here.");
    $$ = new StringWithPosList;
    $1.destroy();
  #endif
    (void)$2;
}
      | arrowend_list arrowsymbol arrowend_list_solo
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SYMBOL);
    if (csh.CheckHintLocated(EHintSourceType::LINE_START, @1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@2, @3, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::LINE_START, @3)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
        csh.hintSource = EHintSourceType::ENTITY;
    }
    delete $3;
  #else
    if (!chart.SkipContent())
        chart.StashArrows($1, $2, $3, {nullptr, nullptr}, @2);
    $$ = $3;
  #endif
    delete $1;
}
      | arrow_end arrowsymbol arrowend_list_solo
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SYMBOL);
    if (csh.CheckHintLocated(EHintSourceType::LINE_START, @1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@2, @3, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::LINE_START, @3)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
        csh.hintSource = EHintSourceType::ENTITY;
    }
    delete $3;
  #else
    if (!chart.SkipContent()) {
        auto one = std::make_unique<StringWithPosList>();
        if (!$1.had_error && !$1.empty())
            one->push_back({$1, @1});
        chart.StashArrows(one.get(), $2, $3, {nullptr, nullptr}, @2);
    }
    $$ = $3;
  #endif
    $1.destroy();
}
      | arrowend_list arrowsymbol arrowend_list_solo full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SYMBOL);
    if (csh.CheckHintLocated(EHintSourceType::LINE_START, @1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @4))
        Arrow::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @4))
        Arrow::AttributeValues(csh.hintAttrName, csh);
    else if (csh.CheckHintBetween(@2, @3, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::LINE_START, @3)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
        csh.hintSource = EHintSourceType::ENTITY;
    }
    delete $1;
    delete $3;
  #else
    if (!chart.SkipContent())
        chart.StashArrows($1, $2, $3, {$4, nullptr}, @2);
    else
       delete $4;
    delete $1;
    $$ = $3;
  #endif
}
      | arrow_end arrowsymbol arrowend_list_solo full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SYMBOL);
    if (csh.CheckHintLocated(EHintSourceType::LINE_START, @1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @4))
        Arrow::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @4))
        Arrow::AttributeValues(csh.hintAttrName, csh);
    else if (csh.CheckHintAt(@1, EHintSourceType::ENTITY) ||
        csh.CheckHintBetween(@2, @3, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::LINE_START, @3)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
        csh.hintSource = EHintSourceType::ENTITY;
    }
    delete $3;
  #else
    if (!chart.SkipContent()) {
        auto one = std::make_unique<StringWithPosList>();
        if (!$1.had_error && !$1.empty())
            one->push_back({$1, @1});
        chart.StashArrows(one.get(), $2, $3, {$4, nullptr}, @2);
    } else
       delete $4;
    $$ = $3;
  #endif
    $1.destroy();
}
      | arrow_cont arrowsymbol arrowend_list_solo
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SYMBOL);
    if (csh.CheckHintBetween(@2, @3, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::LINE_START, @3)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
        csh.hintSource = EHintSourceType::ENTITY;
    }
    delete $3;
  #else
    if (!chart.SkipContent())
        chart.StashArrows($1, $2, $3, {nullptr, nullptr}, @2);
    delete $1;
    $$ = $3;
  #endif
}
      | arrow_cont arrowsymbol arrowend_list_solo full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SYMBOL);
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @4))
        Arrow::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @4))
        Arrow::AttributeValues(csh.hintAttrName, csh);
    else if (csh.CheckHintBetween(@2, @3, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::LINE_START, @3)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
        csh.hintSource = EHintSourceType::ENTITY;
    }
    delete $3;
  #else
    if (!chart.SkipContent())
        chart.StashArrows($1, $2, $3, {$4, nullptr}, @2);
    else
       delete $4;
    delete $1;
    $$ = $3;
  #endif
};

arrow_no_semi: arrowend_list arrowsymbol arrowend_list_solo full_attrlist_with_arrow_labels
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SYMBOL);
    if (csh.CheckHintBetween(@2, @3, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::LINE_START, @3)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
        csh.hintSource = EHintSourceType::ENTITY;
    }
    delete $3;
  #else
    if (!chart.SkipContent())
        chart.StashArrows($1, $2, $3, std::move($4), @2);
    else
       $4.Free();
    $$ = $3;
  #endif
    delete $1;
}
      | arrow_end arrowsymbol arrowend_list_solo full_attrlist_with_arrow_labels
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SYMBOL);
        if (csh.CheckHintLocated(EHintSourceType::LINE_START, @1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@2, @3, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::LINE_START, @3)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
        csh.hintSource = EHintSourceType::ENTITY;
    }
    delete $3;
  #else
    if (!chart.SkipContent()) {
        auto one = std::make_unique<StringWithPosList>();
        if (!$1.had_error && !$1.empty())
            one->push_back({$1, @1});
        chart.StashArrows(one.get(), $2, $3, std::move($4), @2);
    } else
       $4.Free();
    $$ = $3;
  #endif
    $1.destroy();
}
      | arrow_cont arrowsymbol arrowend_list_solo full_attrlist_with_arrow_labels
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SYMBOL);
    if (csh.CheckHintBetween(@2, @3, EHintSourceType::ENTITY)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::LINE_START, @3)) {
        csh.AddEntityNamesAtTheEnd("Make the arrow or line start or end at block %s.");
        csh.hintStatus = HINT_READY;
        csh.hintSource = EHintSourceType::ENTITY;
    }
    delete $3;
  #else
    if (!chart.SkipContent())
        chart.StashArrows($1, $2, $3, std::move($4), @2);
    else
       $4.Free();
    delete $1;
    $$ = $3;
  #endif
};

arrow_cont: arrow_needs_semi | arrow_no_semi;


full_attrlist_with_arrow_labels: full_attrlist_with_label braced_instrlist
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @1))
        Arrow::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @1))
        Arrow::AttributeValues(csh.hintAttrName, csh);
    //return 1 if we had a braced arclist
    $$ = 1;
  #else
    $$.al = $1;
    $$.bl = $2;
  #endif
}
      | braced_instrlist
{
  #ifdef C_S_H_IS_COMPILED
    //return 1 if we had a braced arclist
    $$ = 1;
  #else
    $$.al = nullptr;
    $$.bl = $1;
  #endif
}

opt_percent:  /* empty */
{
    $$ = 0;
}
      | TOK_PERCENT
{
    $$ = 1;
};

arrow_label_number: TOK_NUMBER opt_percent TOK_PLUS TOK_NUMBER opt_percent
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_ATTRVALUE);
  #else
    auto num = ArrowLabel::ConvertPosition(chart, $1, $2, @1+@2, $4, $5, @4+@5);
    if (num)
        $$ = new ArrowLabel(chart, num.value().first, num.value().second, @$);
    else
        $$ = nullptr;
  #endif
}
      | TOK_NUMBER opt_percent TOK_MINUS TOK_NUMBER opt_percent
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_ATTRVALUE);
    (void)$1;
    (void)$4;
  #else
    auto num = ArrowLabel::ConvertPosition(chart, $1, $2, @1+@2, $4, $5, @4+@5);
    if (num)
        $$ = new ArrowLabel(chart, num.value().first, -num.value().second, @$);
    else
        $$ = nullptr;
  #endif
}
      | TOK_NUMBER opt_percent TOK_NUMBER opt_percent
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_ATTRVALUE);
    (void)$1;
    (void)$3;
  #else
    auto num = ArrowLabel::ConvertPosition(chart, $1, $2, @1+@2, $3, $4, @3+@4);
    if (num)
        $$ = new ArrowLabel(chart, num.value().first, num.value().second, @$);
    else
        $$ = nullptr;
  #endif
}
      | TOK_NUMBER opt_percent TOK_PLUS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_ATTRVALUE);
    csh.AddCSH_Error(@3, "Need digits after sign.");
    (void)$1;
  #else
    auto num = ArrowLabel::ConvertPosition(chart, $1, $2, @1+@2);
    if (num)
        $$ = new ArrowLabel(chart, num.value().first, num.value().second, @$);
    else
        $$ = nullptr;
    chart.Error.Error(@3, "Need digits after sign.");
  #endif
}
      | TOK_NUMBER opt_percent TOK_MINUS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_ATTRVALUE);
    csh.AddCSH_Error(@3, "Need digits after sign.");
    (void)$1;
  #else
    auto num = ArrowLabel::ConvertPosition(chart, $1, $2, @1+@2);
    if (num)
        $$ = new ArrowLabel(chart, num.value().first, num.value().second, @$);
    else
        $$ = nullptr;
    chart.Error.Error(@3, "Need digits after sign.");
  #endif
}
      | TOK_NUMBER opt_percent
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_ATTRVALUE);
    (void)$1;
  #else
    auto num = ArrowLabel::ConvertPosition(chart, $1, $2, @1+@2);
    if (num)
        $$ = new ArrowLabel(chart, num.value().first, num.value().second, @$);
    else
        $$ = nullptr;
  #endif
};

arrow_label_number_with_extend: arrow_label_number
      | TOK_EXTEND_COMMAND arrow_label_number
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
  #else
    if ($2)
        $2->is_extension = true;
  #endif
    (void)$1;
    $$ = $2;
}
      | TOK_EXTEND_COMMAND
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@1, "Missing number for arrow label.");
  #else
    chart.Error.Error(@1.after(), "Missing number for arrow label. Ignoring arrow label.");
  #endif
    (void)$1;
    $$ = nullptr;
}

arrow_label: arrow_label_number_with_extend full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        ArrowLabel::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        ArrowLabel::AttributeValues(csh.hintAttrName, csh);
  #else
    if ($1 && $2)
        $1->AddAttributeList($2);
    else
        delete $2;
    $$ = $1;
  #endif
}
      |  TOK_MARK_COMMAND arrow_label_number_with_extend full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @3))
        ArrowLabel::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @3))
        ArrowLabel::AttributeValues(csh.hintAttrName, csh);
  #else
    if ($2 && $3)
        $2->AddAttributeList($3);
    else
        delete $3;
    if ($2)
        $2->has_marker = true;
    $$ = $2;
  #endif
    (void)$1;
}
      |  TOK_MARK_COMMAND arrow_label_number_with_extend
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
  #else
    if ($2)
        $2->has_marker = true;
    $$ = $2;
  #endif
    (void)$1;
}
      |  TOK_MARK_COMMAND
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintAfter(@1, EHintSourceType::KEYWORD)) {
        csh.AddMarkExtendToHints(false, true);
		csh.hintStatus = HINT_READY;
	}
    csh.AddCSH_ErrorAfter(@1, "Missing label position.");
  #else
    $$ = nullptr;
    chart.Error.Error(@1.after(), "Missing label position.");
  #endif
    (void)$1;
};



/*************************************************************************
 * Options
 *************************************************************************/


optlist:     opt
{
  #ifndef C_S_H_IS_COMPILED
    if ($1)
        $$ = (new BlockInstrList)->Append($1); /* New list */
    else
        $$ = new BlockInstrList;
  #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 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 attrvalue
{
  //$3 may contain position escapes and need handling of csh.addEntityNamesAtEnd
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
        csh.AddCSH_AttrName(@1, $1, COLOR_OPTIONNAME);
    csh.AddCSH(@2, COLOR_EQUAL);
    if (!$1.had_error && !$3.had_error)
        csh.AddCSH_AttrValue_CheckAndAddEscapeHint(@3, $3, $1);
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddOptionsToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(@3)) {
        if (!csh.HandleBlockNamePlus($1, $3, EHintSourceType::ATTR_VALUE)) {
            csh.hintStatus = HINT_NONE;
            if (csh.CheckHintAt(@3, EHintSourceType::ATTR_VALUE, $1)) {
                BlockChart::AttributeValues($1, csh);
                csh.hintStatus = HINT_READY;
            }
        }
    } else if (!$1.had_error && csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE, $1)) {
        BlockChart::AttributeValues($1, csh);
        csh.hintStatus = HINT_READY;
    }
	if (CaseInsensitiveEqual($1, "pedantic")) {
		if (CaseInsensitiveEqual($3, "yes"))
			csh.Context2.back().pedantic = true;
		else if (CaseInsensitiveEqual($3, "no"))
			csh.Context2.back().pedantic = false;
	}
  #else
    if ($1.had_error || $3.had_error || (chart.SkipContent() && ($1.had_param || $3.had_param))) {
    } else if (Attribute *p = chart.CreateAttribute($1, $3, @1, @3)) {
        chart.AddAttribute(*p);
        delete p;
    }
    $$ = nullptr;
  #endif
    $1.destroy();
    $3.destroy();
}
      | entity_string TOK_EQUAL
{
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
        csh.AddCSH_AttrName(@1, $1, COLOR_OPTIONNAME);
    csh.AddCSH(@2, COLOR_EQUAL);
    csh.AddCSH_ErrorAfter(@2, "Missing option value.");
    if (!$1.had_error && csh.CheckHintAfter(@2, EHintSourceType::ATTR_VALUE, $1)) {
        BlockChart::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
    $1.destroy();
};


/*************************************************************************
 * Styles
 *************************************************************************/

usestylemodifier: TOK_COMMAND_BLOCKS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
  #endif
    $$ = USE_KEYWORD_BLOCKS;
    (void)$1;
}
      | TOK_COMMAND_ARROWS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
  #endif
    $$ = USE_KEYWORD_ARROWS;
    (void)$1;
}
      | TOK_COMMAND_ARROWS TOK_COMMAND_BLOCKS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_KEYWORD);
  #endif
    $$ = EUseKeywords(USE_KEYWORD_BLOCKS | USE_KEYWORD_ARROWS);
    (void)$1;
    (void)$2;
}
      | TOK_COMMAND_BLOCKS TOK_COMMAND_ARROWS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_KEYWORD);
  #endif
    $$ = EUseKeywords(USE_KEYWORD_BLOCKS | USE_KEYWORD_ARROWS);
    (void)$1;
    (void)$2;
};


usestylemode: TOK_COMMAND_USESTYLE
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
  #endif
    $$ = USE_KEYWORD_ALL;
    (void)$1;
}
      | usestylemodifier TOK_COMMAND_USESTYLE
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_KEYWORD);
    if (csh.CheckHintBetween(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddUseKeywordsToHints($1);
        csh.hintStatus = HINT_READY;
    }
  #endif
    $$ = $1;
    (void)$2;
}
      | usestylemodifier entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddUseKeywordsToHints($1);
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH_Error(@2, "Missing 'use' keyword.");
  #else
    chart.Error.Error(@2, "Missing 'use' keyword. Ignoring this line.");
  #endif
    $$ = USE_KEYWORD_NONE;
    $2.destroy();
}
      | usestylemodifier
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_ErrorAfter(@1, "Missing 'use' keyword.");
    if (csh.CheckHintAfter(@1, EHintSourceType::KEYWORD)) {
        csh.AddUseKeywordsToHints($1);
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@1.after(), "Missing 'use' keyword. Ignoring this line.");
  #endif
    $$ = USE_KEYWORD_NONE;
};

usestyle: usestylemode attrlist
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        BlockStyle(EBlockStyleType::Running, ToEBlockStyleTarget($1), EColorMeaning::FILL).AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        BlockStyle(EBlockStyleType::Running, ToEBlockStyleTarget($1), EColorMeaning::FILL).AttributeValues(csh.hintAttrName, csh);
	//If use includes blocks and attrlist contained a shape==xxx
	//we set the default shape for this context
	if ($2>=-1 && ($1 & USE_KEYWORD_BLOCKS))
		csh.Context2.back().def_shape = $2;
    csh.DropBlockMentions(@2); //No chart option takes a block name as value in any form
  #else
    $$.keywords = $1;
    $$.attrs = $2;
  #endif
}
      | usestylemode
{
  #ifdef C_S_H_IS_COMPILED
    if ($1)
        csh.AddCSH_ErrorAfter(@1, "Missing style or attribute name to apply.");
    if (csh.CheckHintAfter(@1, EHintSourceType::ATTR_VALUE)) {
        BlockStyle(EBlockStyleType::Running, ToEBlockStyleTarget($1), EColorMeaning::FILL).AttributeNames(csh);
        csh.hintStatus = HINT_READY; //dont add linebegin hints later
    }
  #else
    if ($1)
        chart.Error.Error(@1.after(), "Missing style or attribute name to apply. Ignoring statement.");
    $$.keywords = $1;
    $$.attrs = nullptr;
  #endif
};



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

styledef: stylenameposlist full_attrlist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_NameList($1, @1, COLOR_STYLENAME, COLOR_COMMA);
    if (!csh.Contexts.back().SkipContent())
        for (auto &str : *($1))
            if (str.name.length() && csh.ForbiddenStyles.find(str.name) == csh.ForbiddenStyles.end())
                csh.Contexts.back().StyleNames.insert(string(str.name));
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        BlockStyle(EBlockStyleType::Unspecified, EBlockStyleTarget::Any, EColorMeaning::FILL).AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        BlockStyle(EBlockStyleType::Unspecified, EBlockStyleTarget::Any, EColorMeaning::FILL).AttributeValues(csh.hintAttrName, csh);
  #else
    if (chart.SkipContent())
        delete $2;
    else
        chart.AddAttributeListToStyleListWithFilePos($2, $1); //deletes $2
    $$ = nullptr;
  #endif
    delete($1);
}
      | stylenameposlist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_NameList($1, @1, COLOR_STYLENAME, COLOR_COMMA);
    csh.AddCSH_ErrorAfter(@$, "Missing attribute definitions in square brackets ('[' and ']').");
  #else
    chart.Error.Error(@$.after(), "Missing attribute definitions in square brackets ('[' and ']').");
    $$ = nullptr;
  #endif
    delete($1);
};

stylenameposlist: string
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddStylesToHints(true, true);
        csh.hintStatus = HINT_READY;
    }
    $$ = new CshStringViewWithPosList;
    if (!$1.had_error)
        $$->push_back({$1, @1});
  #else
    $$ = new StringWithPosList;
    if (!$1.had_error)
        $$->push_back({$1, @1});
  #endif
    $1.destroy();
}
      | stylenameposlist TOK_COMMA
{
  #ifdef C_S_H_IS_COMPILED
    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;
    }
  #else
    chart.Error.Error(@$.after(), "Missing a style name to (re)define.");
  #endif
    $$ = $1;
}
      | stylenameposlist TOK_COMMA string
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_NAME)) {
        csh.AddStylesToHints(true, true);
        csh.hintStatus = HINT_READY;
    }
    if (!$3.had_error)
        $1->push_back({$3, @3});
  #else
    if (!$3.had_error)
        $1->push_back({$3, @3});
  #endif
    $$ = $1;
    $3.destroy();
};

/*************************************************************************
 * Shapes
 *************************************************************************/


shapedef: entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
        csh.AddCSH(@1, COLOR_ATTRVALUE);
    csh.AddCSH_ErrorAfter(@$, "Here should come a shape definition beginning with '{'. Ignoring this malformed shape definition for '"+$1.str()+"'.");
  #else
    chart.Error.Error(@$.after(), "Here should come a shape definition beginning with '{'. Ignoring this malformed shape definition for '"+$1.str()+"'.");
  #endif
    $1.destroy();
}
      | entity_string TOK_OCBRACKET
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddOpenBracePair(@2);
    if (!$1.had_error)
        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 '"+$1.str()+"'.");
  #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 '"+$1.str()+"'.");
  #endif
    $1.destroy();
    (void) $2; //suppress
}
      | entity_string TOK_OCBRACKET shapedeflist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddOpenBracePair(@2+@3);
    if (!$1.had_error)
        csh.AddCSH(@1, COLOR_ATTRVALUE);
    csh.AddCSH(@2, COLOR_BRACE);
    csh.AddCSH_ErrorAfter(@3, "Missing a closing brace ('}').");
    if (!$1.had_error)
        csh.AddShapeName($1, $3);
    delete $3;
  #else
    chart.Error.Error(@3.after(), "Missing '}'.");
    chart.Error.Error(@2, @3.after(), "Here is the corresponding '{'.");
    if (!$1.had_error && !$1.empty() && $3 && !chart.SkipContent())
        chart.Shapes.Add(std::string($1), @1, chart.file_url, chart.file_info, std::move(*$3), chart.Error);
    delete $3;
  #endif
    $1.destroy();
    (void) $2; //suppress
}
      | entity_string TOK_OCBRACKET shapedeflist TOK_CCBRACKET
{
  #ifdef C_S_H_IS_COMPILED
    csh.BracePairs.push_back(@2+@4);
    if (!$1.had_error)
        csh.AddCSH(@1, COLOR_ATTRVALUE);
    csh.AddCSH(@2, COLOR_BRACE);
    csh.AddCSH(@4, COLOR_BRACE);
    if (!$1.had_error)
        csh.AddShapeName($1, $3);
    delete $3;
  #else
    if (!$1.had_error && !$1.empty() && $3 && !chart.SkipContent())
        chart.Shapes.Add(std::string($1), @1, chart.file_url, chart.file_info, std::move(*$3), chart.Error);
    delete $3;
  #endif
    $1.destroy();
    (void) $2; //suppress
    (void) $4; //suppress
}
      | entity_string TOK_OCBRACKET shapedeflist error TOK_CCBRACKET
{
  #ifdef C_S_H_IS_COMPILED
    csh.BracePairs.push_back(@2+@5);
    if (!$1.had_error)
        csh.AddCSH(@1, COLOR_ATTRVALUE);
    csh.AddCSH(@2, COLOR_BRACE);
    csh.AddCSH(@5, COLOR_BRACE);
    if (!$1.had_error)
        csh.AddShapeName($1, $3);
    delete $3;
    csh.AddCSH_Error(@4, "Only numbers can come after shape commands.");
  #else
    if (!$1.had_error && !$1.empty() && $3 && !chart.SkipContent())
       chart.Shapes.Add(std::string($1), @1, chart.file_url, chart.file_info, std::move(*$3), chart.Error);
    delete $3;
  #endif
    $1.destroy();
    (void) $2; //suppress
    (void) $5; //suppress
};

shapedeflist: shapeline TOK_SEMICOLON
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SEMICOLON);
    if (!$1.empty()) {
        $$ = new std::vector<std::string>;
        $$->emplace_back($1.str());
        $1.destroy();
    } else
        $$ = nullptr;
  #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);
    if (!$2.empty()) {
        $$ = new std::vector<std::string>;
        $$->emplace_back($2.str());
        $2.destroy();
    } else
        $$ = nullptr;
#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);
    if (!$2.empty()) {
        if ($1==nullptr)
            $$ = new std::vector<std::string>;
        $$->emplace_back($2.str());
        $2.destroy();
    } else
        $$ = $1;
  #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
  #endif
    $$ = $1;
};

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));
    $$.init(); //no port name
  #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) {
        std::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.");
    $$.init(); //no port name
  #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) {
        std::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;
        }
    }
    $$.init(); //no port name
  #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 alpha_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if ($1==ShapeElement::PORT) {
        $$ = $4;
    } else {
        csh.AddCSH_Error(@4, "You need to specify a number here.");
        $$.init(); //no port name
        $4.destroy();
    }
  #else
    $$ = nullptr;
    if ($1!=ShapeElement::PORT)
        chart.Error.Error(@4, "Expecting a number here. Ignoring line.");
    else
        $$ = new ShapeElement(to_double($2), to_double($3), $4);
    $4.destroy();
  #endif
  (void)$2;
  (void)$3;
}
      | TOK_SHAPE_COMMAND TOK_NUMBER TOK_NUMBER alpha_string TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if ($1==ShapeElement::PORT) {
        $$ = $4;
    } else {
        csh.AddCSH_Error(@4, "You need to specify a number here.");
        $$.init(); //no port name
        $4.destroy();
    }
  #else
    $$ = nullptr;
    if ($1!=ShapeElement::PORT)
        chart.Error.Error(@4, "Expecting a number here. Ignoring line.");
    else
        $$ = new ShapeElement(to_double($2), to_double($3), $4, to_double($5));
    $4.destroy();
  #endif
  (void)$2;
  (void)$3;
  (void)$5;
}
      | 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.");
    $$.init(); //no port name
  #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) {
        std::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;
        }
    }
    $$.init(); //no port name
  #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;
        }
    }
    $$.init(); //no port name
  #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;
        }
    }
    $$.init(); //no port name
  #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;
};

/*************************************************************************
 * Colors
 *************************************************************************/

colordeflist_actioned: colordeflist
{
    if ($1) for (auto i = $1->begin(); i!=$1->end(); i++) {
        auto j = i++; //j equals name, i equals value
        if (i==$1->end()) break;
  #ifdef C_S_H_IS_COMPILED
        ColorType color = csh.Contexts.back().Colors.GetColor(i->name);
        if (color.type!=ColorType::INVALID)
            csh.Contexts.back().Colors[j->name] = color;
  #else
        if (!chart.SkipContent())
            chart.MyCurrentContext().colors.AddColor(j->name, i->name, chart.Error, FileLineColRange(j->file_pos.start, i->file_pos.end));
  #endif
    }
    delete $1;
};


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
    if ($3) {
        _ASSERT($3->size()==2);
        if ($3->front().name.length()==0) {
            if ($1) {
                $1->back().name.append(",").append($3->back().name);
                $1->back().file_pos += $3->back().file_pos;
            } else {
  #ifdef C_S_H_IS_COMPILED
                csh.AddCSH_Error($3->back().file_pos, "Missing color name to (re)define.");
  #else
                chart.Error.Error($3->back().file_pos.start, "Missing color name to (re)define. Ignoring this number here.");
  #endif
                delete $3;
                $3 = nullptr;
            }
        } else if ($1) {
            $1->insert($1->end(), std::make_move_iterator($3->begin()), std::make_move_iterator($3->end()));
        }
    }
    if ($1) {
        delete $3;
        $$ = $1;
    } else
        $$ = $3;
}
      | 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
    $$ = $1;
};

string_or_num: string
      | TOK_NUMBER { $$.set($1); };

colordef: alpha_string TOK_EQUAL string_or_num
{
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error) {
        csh.AddCSH(@1, COLOR_COLORNAME);
        $$ = new CshStringWithPosList({CshStringWithPos($1, @1), CshStringWithPos($3, @3)});
    } else
        $$ = nullptr;
    csh.AddCSH(@2, COLOR_EQUAL);
    csh.AddCSH(@3, COLOR_COLORDEF);
    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 (!$1.had_error)
        $$ = new StringWithPosList({StringWithPos($1, @1), StringWithPos($3, @3)});
    else
        $$ = nullptr;
  #endif
    $1.destroy();
    $3.destroy();
}
      | alpha_string TOK_EQUAL TOK_PLUS string_or_num
{
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error) {
        csh.AddCSH(@1, COLOR_COLORNAME);
        $$ = new CshStringWithPosList({{$1, @1}, {"++"+string($4), @4}});
    } else
        $$ = nullptr;
    csh.AddCSH(@2, COLOR_EQUAL);
    csh.AddCSH(@3, COLOR_COLORDEF);
    csh.AddCSH(@4, COLOR_COLORDEF);
    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 (!$1.had_error)
        $$ = new StringWithPosList({{$1, @1}, {"++"+string($4), @4}});
    else
        $$ = nullptr;
  #endif
    $1.destroy();
    $4.destroy();
}
      | alpha_string TOK_EQUAL
{
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error) {
        csh.AddCSH(@1, COLOR_COLORNAME);
        $$ = new CshStringWithPosList({{$1, @1}, {"", @2}});
    } else
        $$ = nullptr;
    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.");
    if (!$1.had_error)
        $$ = new StringWithPosList({{$1, @1}, {"", @2}});
    else
        $$ = nullptr;
  #endif
    $1.destroy();
}
      | alpha_string
{
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
        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
    $1.destroy();
    $$ = nullptr;
}
      | TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_COLORDEF);
    $$ = new CshStringWithPosList({{"", @1}, {$1, @1}});
  #else
    $$ = new StringWithPosList({{"", @1}, {$1, @1}});
  #endif
    (void)$1;
}
      | colordef TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
  #endif
    if ($1 && $1->size()) {
        if ($2[0]=='+' || $2[0]=='-')
            $$->back().name.append($2);
        else
            $$->back().name.append("+").append($2);
  #ifdef C_S_H_IS_COMPILED
        $$->back().file_pos.end_pos = @2.end_pos;
  #else
        $$->back().file_pos.end = @2.end();
  #endif
    }
    (void)$2;
    $$ = $1;
};

/*************************************************************************
 * Designs
 *************************************************************************/

usedesign: TOK_COMMAND_USEDESIGN string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (!$2.had_error)
        csh.AddCSH(@2, COLOR_ATTRVALUE);
    if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::ATTR_VALUE)) {
        csh.AddDesignsToHints(true);
        csh.AddDesignsToHints(false);
        csh.hintStatus = HINT_READY; //dont add linebegin hints later
    }
  #else
    if (!$2.had_error) {
        auto i = chart.Designs.find($2.view());
        if (i==chart.Designs.end())
            chart.Error.Error(@2, "Unknown design. Ignoring it.");
        else
            chart.MyCurrentContext().ApplyContextContent(i->second);
    }
  #endif
    (void)$1;
    $2.destroy();
}
      | TOK_COMMAND_USEDESIGN
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@1, "Missing design name to apply.");
    if (csh.CheckHintAfter(@1, EHintSourceType::ATTR_VALUE)) {
        csh.AddDesignsToHints(true);
        csh.AddDesignsToHints(false);
        csh.hintStatus = HINT_READY; //dont add linebegin hints later
    }
  #else
    chart.Error.Error(@1.after(), "Missing design name to apply. Ignoring statement.");
  #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.Contexts.back().SkipContent()) {
        auto &d = csh.Contexts.back().IsFull() ? csh.FullDesigns : csh.PartialDesigns;
        auto i = d.find($1.view());
        if (i == d.end())
            d.emplace($1, csh.Contexts.back());
        else
            i->second += csh.Contexts.back();
    }
    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;
    (void) $5; //supress
}
      |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.Contexts.back().SkipContent()) {
        auto &d = csh.Contexts.back().IsFull() ? csh.FullDesigns : csh.PartialDesigns;
        auto i = d.find($1.view());
        if (i == d.end())
            d.emplace($1, csh.Contexts.back());
        else
            i->second += csh.Contexts.back();
    }
    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
    (void) $6; //supress
};


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
    (void) $1; //supress
};

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_actioned
{
  #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;
    }
  #else
    delete $2;
  #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
      | usedesign;

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
    if (!$1.had_error)
        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 (!$1.had_error && csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE, $1)) {
        BlockChart::AttributeValues($1, csh);
        csh.hintStatus = HINT_READY;
    }
  #else
    if (!$1.had_error)
        chart.AddDesignAttribute(Attribute($1, $3, @$, @3));
  #endif
    $1.destroy();
    (void)$3;
}
      | entity_string TOK_EQUAL string
{
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
        csh.AddCSH_AttrName(@1, $1, COLOR_OPTIONNAME);
    csh.AddCSH(@2, COLOR_EQUAL);
    if (!$3.had_error)
        csh.AddCSH(@3, COLOR_ATTRVALUE);
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddDesignOptionsToHints();
        csh.hintStatus = HINT_READY;
    } else if (!$1.had_error && csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE, $1)) {
        BlockChart::AttributeValues($1, csh);
        csh.hintStatus = HINT_READY;
    }
  #else
    if (!$1.had_error)
        if (!chart.AddDesignAttribute(Attribute($1, $3, @1, @3)))
            chart.Error.Error(@1, "Unrecognized design option. Ignoring it.");
  #endif
    $1.destroy();
    $3.destroy();
}
      | entity_string TOK_EQUAL
{
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
        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 (!$1.had_error && csh.CheckHintAfter(@2, EHintSourceType::ATTR_VALUE, $1)) {
        BlockChart::AttributeValues($1, csh);
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@2.after(), "Missing option value. Ignoring this.");
#endif
    $1.destroy();
};


/*************************************************************************
 * Procedures
 *************************************************************************/


defproc: defprochelp1
{
  #ifdef C_S_H_IS_COMPILED
  #else
    if ($1) {
        if (chart.SkipContent()) {
            chart.Error.Error(@$, "Cannot define procedures inside a procedure.");
        } else if ($1->name.had_error) {
            //do nothing, error already reported
        } else if ($1->name.empty()) {
            chart.Error.Error($1->linenum_name, "Missing a procedure name to (re-)define. Ignoring this.");
        } else if (!$1->had_error && $1->body) {
            if (chart.MyCurrentContext().num_error != chart.Error.GetErrorNum(true, false)) {
                chart.Error.Error(@$, "There are warnings or errors inside the procedure definition. Ignoring it.");
                auto &proc = chart.MyCurrentContext().Procedures[$1->name.str()];
                proc.name = $1->name.str();
                proc.status = EDefProcResult::PROBLEM;
                proc.file_pos = $1->linenum_body;
            } else if ($1->body->status==EDefProcResult::OK || $1->body->status==EDefProcResult::EMPTY) {
                if ($1->parameters) {
                    auto &p = chart.MyCurrentContext().Procedures[$1->name.str()] = *$1->body;
                    p.name = $1->name.str();
                    p.parameters = std::move(*$1->parameters);
                    if ($1->attrs) for (auto &a : *$1->attrs)
                        p.AddAttribute(*a, chart);
                    if ($1->body->status==EDefProcResult::EMPTY)
                        chart.Error.Warning($1->linenum_body, "Empty procedure. Is this what you want?");
                } else {
                     chart.Error.Error(@$, "Ill-formed procedure parameter list. Ignoring this procedure definition.");
                }
            } else {
                 chart.Error.Error(@$, "Ill-formed procedure body. Ignoring this procedure definition.");
            }
        }
        delete $1;
    }
  #endif
}


defprochelp1: TOK_COMMAND_DEFPROC
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.Contexts.back().SkipContent()) {
        csh.AddCSH_Error(@1, "Cannot define procedures inside a procedure.");
    } else {
        csh.AddCSH(@1, COLOR_KEYWORD);
        csh.AddCSH_ErrorAfter(@$, "Missing procedure name to (re-)define.");
    }
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = new ProcDefParseHelper<AttributeList>;
    $$->linenum_name = @$.after();
  #endif
    (void)$1;
}
      | TOK_COMMAND_DEFPROC defprochelp2
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.Contexts.back().SkipContent()) {
        csh.AddCSH_Error(@1, "Cannot define procedures inside a procedure.");
    } else {
        csh.AddCSH(@1, COLOR_KEYWORD);
    }
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = $2;
  #endif
    (void)$1;
};

defprochelp2: alpha_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PROCNAME);
    csh.AddCSH_ErrorAfter(@1, "Missing a procedure definition starting with '{'.");
    $1.destroy();
  #else
    $$ = new ProcDefParseHelper<AttributeList>;
    $$->name = $1;
    $$->linenum_name = @1;
  #endif
}
      | alpha_string defprochelp3
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PROCNAME);
    $1.destroy();
  #else
    $$ = $2;
    $$->name = $1;
    $$->linenum_name = @1;
  #endif
}
      | defprochelp3
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_Error(CshPos(@1.first_pos, @1.first_pos), "Missing procedure name.");
  #else
    $$ = $1;
    $$->linenum_name = @1;
  #endif
};

defprochelp3: proc_def_arglist_tested
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_ErrorAfter(@1, "Missing a procedure definition starting with '{'.");
  #else
    $$ = new ProcDefParseHelper<AttributeList>;
    $$->parameters = $1;
  #endif
}
      | proc_def_arglist_tested defprochelp4
{
  #ifdef C_S_H_IS_COMPILED
  #else
    $$ = $2;
    $$->parameters = $1;
  #endif
}
      | defprochelp4
{
  #ifdef C_S_H_IS_COMPILED
  #else
    $$ = $1;
    $$->parameters = new ProcParamDefList;
  #endif
};

defprochelp4: full_attrlist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_ErrorAfter(@1, "Missing a procedure definition starting with '{'.");
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @1))
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRNAME) + "export",
            "Set if styles and colors defined in the procedure remain valid after calling it.",
            EHintType::ATTR_NAME));
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @1))
        csh.AddYesNoToHints();
  #else
    $$ = new ProcDefParseHelper<AttributeList>;
    $$->attrs = $1;
  #endif
}
      | full_attrlist procedure_body
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @1))
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRNAME) + "export",
            "Set if styles and colors defined in the procedure remain valid after calling it.",
            EHintType::ATTR_NAME));
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @1))
        csh.AddYesNoToHints();
  #else
    $$ = new ProcDefParseHelper<AttributeList>;
    $$->body = $2;
    $$->linenum_body = @2;
    $$->attrs = $1;
  #endif
}
      | procedure_body
{
  #ifdef C_S_H_IS_COMPILED
  #else
    $$ = new ProcDefParseHelper<AttributeList>;
    $$->body = $1;
    $$->linenum_body = @1;
  #endif
};


scope_open_proc_body: TOK_OCBRACKET
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_BRACE);
    csh.PushContext(true, EContextParse::SKIP_CONTENT);
  #else
    chart.MyCurrentContext().num_error = chart.Error.GetErrorNum(true, false);
    chart.PushContext(@1, EContextParse::SKIP_CONTENT, EContextCreate::EMPTY);  //We have all the styles, but empty
    chart.MyCurrentContext().parameters = std::move(proc_helper.last_procedure_params);
    chart.MyCurrentContext().starts_procedure = true;
    _ASSERT(proc_helper.open_context_mode == EScopeOpenMode::NORMAL);
    proc_helper.open_context_mode = EScopeOpenMode::NORMAL;
  #endif
    $$ = $1;
};

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

proc_def_arglist_tested: proc_def_arglist
{
  #ifdef C_S_H_IS_COMPILED
  #else
    if ($1) {
        auto pair = Procedure::AreAllParameterNamesUnique(*$1);
        if (pair.first) {
            chart.Error.Error(pair.second->linenum_name, "This parameter name is already used.");
            chart.Error.Error(pair.first->linenum_name, pair.second->linenum_name, "This parameter name is already used.");
            delete $1;
            $$ = nullptr;
        } else {
            //Also copy to proc_helper.last_procedure_params and set open_context_mode
            auto &store = proc_helper.last_procedure_params;
            store.clear();
            for (const auto &p : *$1)
                store.emplace(p->name, ProcParamResolved(std::string(), FileLineCol(), true));
            $$ = $1;
        }
    } else
        $$ = nullptr;
  #endif
};

proc_def_arglist: TOK_OPARENTHESIS TOK_CPARENTHESIS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PARENTHESIS);
    csh.AddCSH(@2, COLOR_PARENTHESIS);
  #else
    $$ = new ProcParamDefList;
  #endif
}
      | TOK_OPARENTHESIS error TOK_CPARENTHESIS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PARENTHESIS);
    csh.AddCSH_Error(@2, "Invalid parameter definitions.");
    csh.AddCSH(@3, COLOR_PARENTHESIS);
  #else
    chart.Error.Error(@2, "Invalid parameter definitions.", "Say something like '($first, $second=default)'.");
    $$ = nullptr;
  #endif
}
      | TOK_OPARENTHESIS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PARENTHESIS);
    csh.AddCSH_ErrorAfter(@1, "Missing parameter list closed by a parenthesis ')'.");
  #else
    chart.Error.Error(@1.after(), "Missing parameter list closed by a parenthesis ')'.");
    $$ = nullptr;
  #endif
}
      | TOK_OPARENTHESIS proc_def_param_list error TOK_CPARENTHESIS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PARENTHESIS);
    csh.AddCSH_Error(@3, "Invalid parameter definitions.");
    csh.AddCSH(@4, COLOR_PARENTHESIS);
  #else
    chart.Error.Error(@3, "Invalid parameter definitions.");
    delete $2;
    $$ = nullptr;
  #endif
}
      | TOK_OPARENTHESIS proc_def_param_list
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PARENTHESIS);
    csh.AddCSH_ErrorAfter(@2, "Missing closing parenthesis ')'.");
  #else
    chart.Error.Error(@2.after(), "Missing closing parenthesis ')'.");
    delete $2;
    $$ = nullptr;
  #endif
}
      | TOK_OPARENTHESIS proc_def_param_list TOK_CPARENTHESIS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PARENTHESIS);
    csh.AddCSH(@3, COLOR_PARENTHESIS);
  #else
    $$ = $2;
  #endif
};

proc_def_param_list: proc_def_param
{
  #ifdef C_S_H_IS_COMPILED
  #else
    if ($1) {
        $$ = new ProcParamDefList;
        ($$)->Append($1);
    } else
        $$= nullptr;
  #endif
}
      | proc_def_param_list TOK_COMMA
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    csh.AddCSH_ErrorAfter(@2, "Missing parameter after the comma.");
  #else
    chart.Error.Error(@2.after(), "Missing parameter after the comma.");
    delete $1;
    $$= nullptr;
  #endif
}
      | proc_def_param_list TOK_COMMA proc_def_param
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
  #else
    if ($1 && $3) {
        ($1)->Append($3);
        $$ = $1;
    } else {
        delete $1;
        delete $3;
        $$= nullptr;
    }
  #endif
};

proc_def_param: TOK_PARAM_NAME
{
  #ifdef C_S_H_IS_COMPILED
    if ($1 && $1[0]=='$' && $1[1])
        csh.AddCSH(@1, COLOR_PARAMNAME);
    else
        csh.AddCSH_Error(@1, "Need name after the '$' sign.");
    $$ = nullptr; //no value
  #else
    if ($1 && $1[0]=='$' && $1[1]) {
        $$ = new ProcParamDef($1, @1);
    } else {
        chart.Error.Error(@1, "Need name after the '$' sign.");
        $$ = nullptr;
    }
  #endif
    (void)$1;
}
      | TOK_PARAM_NAME TOK_EQUAL
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_EQUAL);
    if ($1 && $1[0]=='$' && $1[1])
        csh.AddCSH(@1, COLOR_PARAMNAME);
    else
        csh.AddCSH_Error(@1, "Need name after the '$' sign.");
    $$ = nullptr; //no value
  #else
    if ($1 && $1[0]=='$' && $1[1]) {
        $$ = new ProcParamDef($1, @1);
    } else {
        chart.Error.Error(@1, "Need name after the '$' sign.");
        $$ = nullptr;
    }
  #endif
    (void)$1;
}
      | TOK_PARAM_NAME TOK_EQUAL string
{
  #ifdef C_S_H_IS_COMPILED
    if ($1 && $1[0]=='$' && $1[1])
        csh.AddCSH(@1, COLOR_PARAMNAME);
    else
        csh.AddCSH_Error(@1, "Need name after the '$' sign.");
    csh.AddCSH(@2, COLOR_EQUAL);
    csh.AddCSH_ParamOrCond(@3, $3);
    $$ = (char*)1; //has value
  #else
    if ($3.had_error) {
        $$ = nullptr;
    } else if ($1 && $1[0]=='$' && $1[1]) {
        $$ = new ProcParamDef($1, @1, $3, @3);
    } else {
        chart.Error.Error(@1, "Need name after the '$' sign.");
        $$ = nullptr;
    }
  #endif
    (void)$1;
    $3.destroy();
}
      | TOK_PARAM_NAME TOK_EQUAL TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    if ($1 && $1[0]=='$' && $1[1])
        csh.AddCSH(@1, COLOR_PARAMNAME);
    else
        csh.AddCSH_Error(@1, "Need name after the '$' sign.");
    csh.AddCSH(@2, COLOR_EQUAL);
    csh.AddCSH(@3, COLOR_ATTRVALUE);
    $$ = (char*)1; //has value
  #else
    if ($1 && $1[0]=='$' && $1[1]) {
        $$ = new ProcParamDef($1, @1, $3, @3);
    } else {
        chart.Error.Error(@1, "Need name after the '$' sign.");
        $$ = nullptr;
    }
  #endif
    (void)$1;
    (void)$3;
};


procedure_body: scope_open_proc_body instrlist scope_close_proc_body
{
  #ifdef C_S_H_IS_COMPILED
    csh.BracePairs.push_back(@$);
  #else
    auto tmp = new Procedure;
    tmp->status = EDefProcResult::OK;
    tmp->text = std::string(($1), ($3)+1)+";";
    tmp->file_pos = @$;
    if ($2)
        delete $2;
    $$ = tmp;
  #endif
}
      | scope_open_proc_body scope_close_proc_body
{
  #ifdef C_S_H_IS_COMPILED
    csh.BracePairs.push_back(@$);
  #else
    auto tmp = new Procedure;
    tmp->status = EDefProcResult::EMPTY;
    tmp->file_pos = @$;
    $$ = tmp;
  #endif
  (void) $1;
  (void) $2;
}
      | scope_open_proc_body instrlist error scope_close_proc_body
{
  #ifdef C_S_H_IS_COMPILED
    csh.BracePairs.push_back(@$);
    csh.AddCSH_Error(@3, "Could not recognize this as a valid line.");
  #else
    auto tmp = new Procedure;
    tmp->status = EDefProcResult::PROBLEM;
    tmp->file_pos = @$;
    $$ = tmp;
    chart.Error.Error(@3, "syntax error.");
    if ($2)
        delete $2;
  #endif
    yyerrok;
  (void) $1;
  (void) $4;
}
      | scope_open_proc_body instrlist error TOK_EOF
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddOpenBracePair(@$);
    csh.AddCSH_Error(@3, "Could not recognize this as a valid line.");
    csh.PopContext();
  #else
    auto tmp = new Procedure;
    tmp->status = EDefProcResult::PROBLEM;
    tmp->file_pos = @$;
    $$ = tmp;
    chart.PopContext();
    chart.Error.Error(@3, "Missing '}'.");
    chart.Error.Error(@1, @3, "Here is the corresponding '{'.");
    if ($2)
        delete $2;
  #endif
  (void) $1;
}
      | scope_open_proc_body instrlist TOK_EOF
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddOpenBracePair(@$);
    csh.AddCSH_ErrorAfter(@2, "Missing a closing brace ('}').");
    csh.PopContext();
  #else
    auto tmp = new Procedure;
    tmp->status = EDefProcResult::PROBLEM;
    tmp->file_pos = @$;
    $$ = tmp;
    chart.PopContext();
    chart.Error.Error(@2.after(), "Missing '}'.");
    chart.Error.Error(@1, @2.after(), "Here is the corresponding '{'.");
    if ($2)
        delete $2;
  #endif
  (void) $1;
}
      | scope_open_proc_body TOK_EOF
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddOpenBracePair(@$);
    csh.AddCSH_ErrorAfter(@1, "Missing a closing brace ('}').");
    csh.PopContext();
  #else
    auto tmp = new Procedure;
    tmp->status = EDefProcResult::PROBLEM;
    tmp->file_pos = @$;
    $$ = tmp;
    chart.PopContext();
    chart.Error.Error(@1.after(), "Missing a corresponding '}'.");
  #endif
  (void) $1;
}
      | scope_open_proc_body 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.");
    csh.PopContext();
  #else
    auto tmp = new Procedure;
    tmp->status = EDefProcResult::PROBLEM;
    tmp->file_pos = @$;
    $$ = tmp;
    chart.PopContext();
    chart.Error.Error(@3, "The command 'bye' can not be used between curly braces '{' and '}'.");
    chart.Error.Error(@1, @3, "Here is the opening '{'.");
    if ($2)
        delete $2;
  #endif
    (void) $1;
    (void)$3;
}
      | scope_open_proc_body 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 '}'.");
    csh.PopContext();
  #else
    auto tmp = new Procedure;
    tmp->status = EDefProcResult::PROBLEM;
    tmp->file_pos = @$;
    $$ = tmp;
    chart.PopContext();
    chart.Error.Error(@2, "The command 'bye' can not be used between curly braces '{' and '}'.");
  #endif
    (void) $1;
    (void)$2;
};

set: TOK_COMMAND_SET proc_def_param
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (!$2)
        csh.AddCSH_ErrorAfter(@2, "Missing value.");
  #else
    if (!chart.SkipContent())
        chart.SetVariable($2, @$);
    else
        delete $2;
  #endif
    (void)$1;
}
      | TOK_COMMAND_SET
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@1, "Missing variable or parameter name to set.");
  #else
    chart.Error.Error(@1.after(), "Missing variable or parameter name to set.");
  #endif
    (void)$1;
};


proc_invocation: TOK_COMMAND_REPLAY
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@1, "Missing procedure name.");
  #else
    chart.Error.Error(@1.after(), "Missing procedure name.");
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_COMMAND_REPLAY alpha_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_PROCNAME);
    csh.hadProcReplay = true;
  #else
    $$ = nullptr;
    if (!$2.had_error) {
        auto proc = chart.GetProcedure($2);
        if (proc==nullptr)
            chart.Error.Error(@2, "Undefined procedure. Ignoring procedure call.");
        else if (proc->status==EDefProcResult::PROBLEM)
            chart.Error.Error(@2, "Ill-formed procedure. Ignoring procedure call.");
        else if (proc->status==EDefProcResult::OK) {
            //Only return the procedure if we are not inside a procedure definition
            if (!chart.SkipContent())
                $$ = proc;
            //else just move on parsing - we do not reparse procedure replays during
            //the definition of an outer procedure.
        }
        //else return null, emit no error for EMPTY
    }
  #endif
    (void)$1;
    $2.destroy();
};

proc_param_list: TOK_OPARENTHESIS TOK_CPARENTHESIS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PARENTHESIS);
    csh.AddCSH(@2, COLOR_PARENTHESIS);
  #else
    $$ = new ProcParamInvocationList;
  #endif
}
      | TOK_OPARENTHESIS error TOK_CPARENTHESIS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PARENTHESIS);
    csh.AddCSH_Error(@2, "Invalid parameter syntax.");
    csh.AddCSH(@3, COLOR_PARENTHESIS);
  #else
    chart.Error.Error(@2, "Invalid parameter syntax. Ignoring procedure call.");
    $$ = nullptr;
  #endif
}
      | TOK_OPARENTHESIS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PARENTHESIS);
    csh.AddCSH_ErrorAfter(@1, "Missing parameter list closed by a parenthesis ')'.");
  #else
    chart.Error.Error(@1.after(), "Missing parameter list closed by a parenthesis ')'. Ignoring procedure call.");
    $$ = nullptr;
  #endif
}
      | TOK_OPARENTHESIS proc_invoc_param_list error TOK_CPARENTHESIS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PARENTHESIS);
    csh.AddCSH_Error(@3, "Invalid parameter syntax.");
    csh.AddCSH(@4, COLOR_PARENTHESIS);
  #else
    chart.Error.Error(@3, "Invalid parameter syntax. Ignoring procedure call.");
    delete $2;
    $$ = nullptr;
  #endif
}
      | TOK_OPARENTHESIS proc_invoc_param_list
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PARENTHESIS);
    csh.AddCSH_ErrorAfter(@2, "Missing closing parenthesis ')'.");
  #else
    chart.Error.Error(@2.after(), "Missing closing parenthesis ')'. Ignoring procedure call.");
    delete $2;
    $$ = nullptr;
  #endif
}
      | TOK_OPARENTHESIS proc_invoc_param_list TOK_CPARENTHESIS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PARENTHESIS);
    csh.AddCSH(@3, COLOR_PARENTHESIS);
  #else
    $$ = $2;
  #endif
};

proc_invoc_param_list: proc_invoc_param
{
  #ifdef C_S_H_IS_COMPILED
  #else
    if ($1) {
        $$ = new ProcParamInvocationList;
        ($$)->Append($1);
    } else
        $$= nullptr;
  #endif
}
      | TOK_COMMA
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_COMMA);
  #else
    $$ = new ProcParamInvocationList;
    ($$)->Append(std::make_unique<ProcParamInvocation>(@1));
  #endif
}
      | TOK_COMMA proc_invoc_param
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_COMMA);
  #else
    if ($2) {
        $$ = new ProcParamInvocationList;
        ($$)->Append(std::make_unique<ProcParamInvocation>(@1));
        ($$)->Append($2);
    } else
        $$= nullptr;
  #endif
}
      | proc_invoc_param_list TOK_COMMA
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
  #else
    if ($1)
        ($1)->Append(std::make_unique<ProcParamInvocation>(@2.after()));
    $$ = $1;
  #endif
}
      | proc_invoc_param_list TOK_COMMA proc_invoc_param
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
  #else
    if ($1 && $3) {
        ($1)->Append($3);
        $$ = $1;
    } else {
        delete $1;
        delete $3;
        $$= nullptr;
    }
  #endif
};

proc_invoc_param: string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_ParamOrCond(@1, $1);
  #else
    if ($1.had_error)
        $$ = nullptr;
    else
        $$ = new ProcParamInvocation($1, @1);
  #endif
    $1.destroy();
}
      | TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_ATTRVALUE);
  #else
    $$ = new ProcParamInvocation($1, @1);
  #endif
    (void)$1;
};

/*************************************************************************
 * Ifthenelse
 *************************************************************************/

ifthenbranch: multi_instr
      | complete_instr
{
  #ifndef C_S_H_IS_COMPILED
    if ($1)
        $$ = (new BlockInstrList)->Append($1); /* New list */
    else
        $$ = new BlockInstrList;
  #endif
};



comp: TOK_COMP_OP;

condition: string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_ParamOrCond(@1, $1);
  #endif
    $$ = $1.had_error ? 2 : !$1.empty();
    $1.destroy();
}
      | string comp
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_ParamOrCond(@1, $1);
    csh.AddCSH(@2, COLOR_EQUAL);
    csh.AddCSH_ErrorAfter(@2, "Missing string to compare to.");
  #else
    chart.Error.Error(@2.after(), "Missing string to compare to.");
  #endif
    $$ = 2;
    $1.destroy();
    (void) $2;
}
      | string comp string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_ParamOrCond(@1, $1);
    if ($2!=ECompareOperator::INVALID) {
        csh.AddCSH(@2, COLOR_EQUAL);
        $$ = $1.Compare($2, $3);
    } else {
        csh.AddCSH_Error(@2, "Bad comparison operator. Use one of '==', '<>', '<=', '=>', '<' or '>'.");
        $$ = 2;
    }
    csh.AddCSH_ParamOrCond(@3, $3);
  #else
    if ($2!=ECompareOperator::INVALID)
        $$ = $1.Compare($2, $3);
    else {
        chart.Error.Error(@2, "Bad comparison operator. Use one of '==', '<>', '<=', '=>', '<' or '>'.");
        $$ = 2;
    }
  #endif
    $1.destroy();
    $3.destroy();
}
      | string error string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_ParamOrCond(@1, $1);
    csh.AddCSH_Error(@2, "Bad comparison operator. Use one of '==', '<>', '<=', '=>', '<' or '>'.");
    csh.AddCSH_ParamOrCond(@3, $3);
  #else
     chart.Error.Error(@2, "Bad comparison operator. Use one of '==', '<>', '<=', '=>', '<' or '>'.");
  #endif
    $1.destroy();
    $3.destroy();
    $$ = 2;
};

ifthen_condition: TOK_IF condition TOK_THEN
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@3, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAfter(@3)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
    $$ = csh.Contexts.back().if_condition = $2;
    const bool cond_true = $2==1;
    if (cond_true)
        csh.PushContext();
    else
        csh.PushContext(true, EContextParse::SKIP_CONTENT);
  #else
    $$ = chart.MyCurrentContext().if_condition = $2;
    const bool cond_true = $2==1;
    if (cond_true)
        chart.PushContext(@1);
    else
        chart.PushContext(@1, EContextParse::SKIP_CONTENT);
    chart.MyCurrentContext().export_colors = cond_true;
    chart.MyCurrentContext().export_styles = cond_true;
  #endif
    (void)$1;
    (void)$3;
}
      | TOK_IF condition
{
  #ifdef C_S_H_IS_COMPILED
    $$ = csh.Contexts.back().if_condition = $2;
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@2, "Missing 'then' keyword.");
    if (csh.CheckHintAfter(@2, EHintSourceType::KEYWORD)) {
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_KEYWORD) + "then",
            "Continue the 'if' statement with 'then'.",
            EHintType::KEYWORD, true));
        csh.hintStatus = HINT_READY;
    }
    csh.PushContext(true, EContextParse::SKIP_CONTENT);
  #else
    $$ = chart.MyCurrentContext().if_condition = $2;
    chart.PushContext(@1, EContextParse::SKIP_CONTENT);
    chart.Error.Error(@2.after(), "Missing 'then' keyword.");
  #endif
    (void)$1;
    (void) $2; //to supress warnings
}
      | TOK_IF
{
  #ifdef C_S_H_IS_COMPILED
    $$ = csh.Contexts.back().if_condition = 2;
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@1, "Missing condition.");
    csh.PushContext(true, EContextParse::SKIP_CONTENT);
  #else
    $$ = chart.MyCurrentContext().if_condition = 2;
    chart.PushContext(@1, EContextParse::SKIP_CONTENT);
    chart.Error.Error(@1.after(), "Missing condition after 'if'.");
  #endif
    (void)$1;
}
      | TOK_IF error
{
  #ifdef C_S_H_IS_COMPILED
    $$ = csh.Contexts.back().if_condition = 2;
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_Error(@2, "Missing condition instead of this.");
    csh.PushContext(true, EContextParse::SKIP_CONTENT);
  #else
    $$ = chart.MyCurrentContext().if_condition = 2;
    chart.PushContext(@1, EContextParse::SKIP_CONTENT);
    chart.Error.Error(@2, "Missing condition after 'if'.");
  #endif
    (void)$1;
}
      | TOK_IF error TOK_THEN
{
  #ifdef C_S_H_IS_COMPILED
    $$ = csh.Contexts.back().if_condition = 2;
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_Error(@2, "Missing condition instead of this.");
    csh.PushContext(true, EContextParse::SKIP_CONTENT);
    if (csh.CheckLineStartHintAfter(@3)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = chart.MyCurrentContext().if_condition = 2;
    chart.PushContext(@1, EContextParse::SKIP_CONTENT);
    chart.Error.Error(@2, "Missing condition after 'if'.");
  #endif
    (void)$1;
    (void)$3;
};


else: TOK_ELSE
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAfter(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
    csh.PopContext();
    const bool cond_false = csh.Contexts.back().if_condition==0;
    //Return value not used, just there so that nonterminal 'else' can have destructor
    $$ = csh.Contexts.back().if_condition;
    if (cond_false)
        csh.PushContext();
    else
        csh.PushContext(true, EContextParse::SKIP_CONTENT);
  #else
    //kill previous context and open new one - set laterreparse if error or if condition was true
    //this will ignore everything in the else clause
    chart.PopContext();
    const bool cond_false = chart.MyCurrentContext().if_condition==0;
    //Return value not used, just there so that nonterminal 'else' can have destructor
    $$ = chart.MyCurrentContext().if_condition;
    if (cond_false)
        chart.PushContext(@1);
    else
        chart.PushContext(@1, EContextParse::SKIP_CONTENT);
    chart.MyCurrentContext().export_colors = cond_false;
    chart.MyCurrentContext().export_styles = cond_false;
  #endif
    (void)$1;
};

ifthen: ifthen_condition ifthenbranch
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstructionIfNotBrace(@2);
    csh.PopContext();
    if (csh.CheckHintAfter(@2, EHintSourceType::KEYWORD)) {
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_KEYWORD) + "else",
            "Continue the 'if/then' statement with 'else'.",
            EHintType::KEYWORD, true));
        csh.hintStatus = HINT_READY;
    }
  #else
    if ($1==1) {
        $$ = $2;
    } else {
        $$ = nullptr;
        delete $2;
    }
    chart.PopContext();
  #endif
}
      | ifthen_condition
{
  #ifdef C_S_H_IS_COMPILED
    if ($1!=2)
        csh.AddCSH_ErrorAfter(@1, "Missing command after 'then'.");
    csh.PopContext();
  #else
    if ($1!=2)
        chart.Error.Error(@1.after(), "Missing a well-formed command after 'then'. Ignoring 'if' clause.");
    chart.PopContext();
    $$ = nullptr;
  #endif
    (void) $1; //suppress
}
      | ifthen_condition error
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_Error(@2, "Missing command after 'then'.");
    csh.PopContext();
  #else
    chart.Error.Error(@2, "Missing a well-formed command. Ignoring 'if' clause.");
    chart.PopContext();
    $$ = nullptr;
  #endif
    (void) $1; //suppress
}
      | ifthen_condition ifthenbranch else
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstructionIfNotBrace(@2);
    csh.AddCSH_ErrorAfter(@3, "Missing command after 'else'.");
    csh.PopContext();
  #else
    delete $2;
    $$ = nullptr;
    chart.Error.Error(@3.after(), "Missing command after 'else'. Ignoring 'if' clause.");
    chart.PopContext();
  #endif
    (void) $1; (void) $3; //suppress
}
      | ifthen_condition ifthenbranch error else
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstructionIfNotBrace(@2);
    csh.AddCSH_Error(@3, "I am not sure what is coming here.");
    csh.AddCSH_ErrorAfter(@4, "Missing command after 'else'.");
    csh.PopContext();
  #else
    delete $2;
    $$ = nullptr;
    chart.Error.Error(@3, "I am not sure what is coming here.");
    chart.Error.Error(@4.after(), "Missing command after 'else'. Ignoring 'if' clause.");
    chart.PopContext();
  #endif
    (void) $1; (void) $4; //suppress
}
      | ifthen_condition error else
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_Error(@2, "I am not sure what is coming here.");
    csh.AddCSH_ErrorAfter(@3, "Missing command after 'else'.");
    csh.PopContext();
  #else
    $$ = nullptr;
    chart.Error.Error(@2, "I am not sure what is coming here.");
    chart.Error.Error(@3.after(), "Missing command after 'else'. Ignoring 'if' clause.");
    chart.PopContext();
  #endif
    (void) $1; (void) $3; //suppress
}
      | ifthen_condition ifthenbranch else ifthenbranch
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstructionIfNotBrace(@2);
    csh.AddInstructionIfNotBrace(@4);
    csh.PopContext();
  #else
    switch ($1) {
    case 1: //original condition was true
        $$ = $2;   //take 'then' branch
        delete $4; //delete 'else' branch
        break;
    case 0: //original condition was false
        $$ = $4; //take 'else' branch
        delete $2; //delete 'then' branch
        break;
    default:
        _ASSERT(0);
        FALLTHROUGH;
    case 2: //we had an error, but has reported the error - delete both branches
        $$ = nullptr;
        delete $2;
        delete $4;
        break;
    }
    chart.PopContext();
  #endif
    (void) $3; //suppress
}
      | ifthen_condition ifthenbranch error else ifthenbranch
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstructionIfNotBrace(@2);
    csh.AddInstructionIfNotBrace(@5);
    csh.AddCSH_Error(@3, "I am not sure what is coming here.");
    csh.PopContext();
  #else
    $$ = nullptr;
    delete $2;
    delete $5;
    chart.Error.Error(@3, "I am not sure what is coming here. Ignoring 'if' command.");
    chart.PopContext();
  #endif
    (void) $1; (void) $4; //suppress
}
      | ifthen_condition error else ifthenbranch
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstructionIfNotBrace(@4);
    csh.AddCSH_Error(@2, "I am not sure what is coming here.");
    csh.PopContext();
  #else
    $$ = nullptr;
    delete $4;
    chart.Error.Error(@2, "I am not sure what is coming here. Ignoring 'if' command.");
    chart.PopContext();
  #endif
    (void) $1; (void) $3; //suppress
};


/*************************************************************************
 * Attributes
 *************************************************************************/

full_attrlist_with_label: TOK_COLON_STRING
{
  #ifdef C_S_H_IS_COMPILED
    $$ = -2; //we do not contain any shape=xxx attr.
  #else
    $$ = (new AttributeList)->Append(std::make_unique<Attribute>("label", $1, @$, @$.IncStartCol()));
  #endif
  $1.destroy();
}
      | TOK_COLON_STRING full_attrlist
{
  #ifdef C_S_H_IS_COMPILED
    $$ = $2; //copy any potential shape attr
  #else
    $$ = $2->Prepend(std::make_unique<Attribute>("label", $1, @1, @1.IncStartCol()));
  #endif
  $1.destroy();
}
      | full_attrlist TOK_COLON_STRING full_attrlist
{
  #ifdef C_S_H_IS_COMPILED
    if ($3>=-1) $$ = $3;
    else $$ = $1; //copy any potential shape attr
  #else
    $$ = $1->Append(std::make_unique<Attribute>("label", $2, @2, @2.IncStartCol()));
    //Merge $3 at the end of $1
    $1->splice($1->end(), *$3);
    delete ($3); //empty list now
    $$ = $1;
  #endif
  $2.destroy();
}
      | full_attrlist TOK_COLON_STRING
{
  #ifdef C_S_H_IS_COMPILED
    $$ = $1; //copy any potential shape attr
  #else
    $$ = $1->Append(std::make_unique<Attribute>("label", $2, @2, @2.IncStartCol()));
  #endif
  $2.destroy();
}
      | 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);
	$$ = -2; //we do not contain any shape=xxx attr.
  #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);
	$$ = $2; //copy any potential shape attr
  #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);
	$$ = $2; //copy any potential shape attr
  #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);
	$$ = -2; //we do not contain any shape=xxx attr.
  #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(@$);
	$$ = $2; //copy any potential shape attr
  #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);
	$$ = $2; //copy any potential shape attr
  #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);
	$$ = -2; //we do not contain any shape=xxx attr.
  #else
    $$ = new AttributeList;
    chart.Error.Error(@1.after(), "Missing ']'.");
  #endif
}
      | 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);
	$$ = -2; //we do not contain any shape=xxx attr.
  #else
    $$ = new AttributeList;
    chart.Error.Error(@2, "Missing ']'.");
  #endif
};

attrlist:    attr
{
  #ifdef C_S_H_IS_COMPILED
	$$ = $1; //copy any potential shape number
  #else
    if ($1 && $1->name.length()==0) {
        chart.Error.Error(@$, "Missing attribute name.");
        delete $1;
        $$ = new AttributeList;
    } 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);
	if ($3>=-1)
		$$ = $3;
	else
		$$ = $1; //copy any potential shape number
  #else
    if ($3 && $3->name.length()==0) {
        if ($1->size()==0)
            chart.Error.Error(@3, "Missing attribute name.");
        else {
            $1->back()->value += StrCat(',', @3.start().Print(), $3->value);
            $1->back()->type = EAttrType::STRING;
        }
        delete $3;
        $$ = $1;
    } 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.");
	$$ = $1; //copy any potential shape number
  #else
    $$ = $1;
    chart.Error.Error(@2.after(), "Expecting an attribute or style name here.");
  #endif
};


attr:         alpha_string TOK_EQUAL attrvalue
{
  //string=string, alignment attributes
  //$3 may contain position escapes and need handling of csh.addEntityNamesAtEnd
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
        csh.AddCSH_AttrName(@1, $1, COLOR_ATTRNAME);
    csh.AddCSH(@2, COLOR_EQUAL);
    if (!$1.had_error && !$3.had_error)
        csh.AddCSH_AttrValue_CheckAndAddEscapeHint(@3, $3, $1);
    if (csh.CheckHintLocated(@3)) {
        if (!csh.HandleBlockNamePlus($1, $3, EHintSourceType::ATTR_VALUE)) {
            csh.hintStatus = HINT_NONE;
            csh.CheckHintAt(@3, EHintSourceType::ATTR_VALUE, $1);
        }
    } else {
        if (!$1.had_error && !$3.had_error)
            csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE, $1);
        if (!$1.had_error)
            csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME);
    }
	if (!$1.had_error && !$3.had_error) {
		if (CaseInsensitiveEqual($1, "shape")) {
			$$ = csh.GetShapeNum($3);
			if ($$==-1 && !CaseInsensitiveEqual($3, "box"))
				$$ = -2; //not found
		} else {
			$$ = -2; //we are not a valid shape=xxx attr.
		}
	}
  #else
    if ($1.had_error || (chart.SkipContent() && $1.had_param))
        $$ = nullptr;
    else
        $$ = chart.CreateAttribute($1, $3, @1, @3);
  #endif
    $1.destroy();
    $3.destroy();
}
      | alpha_string TOK_EQUAL TOK_PLUS entity_string
{
  //string=string
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
        csh.AddCSH_AttrName(@1, $1, COLOR_ATTRNAME);
    csh.AddCSH(@2, COLOR_EQUAL);
    if (!$1.had_error)
        csh.AddCSH_AttrValue_CheckAndAddEscapeHint(@3+@4, "++"+$4.str(), $1);
    csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME);
    if (!$1.had_error)
        csh.CheckHintBetweenAndAt(@2, @3+@4, EHintSourceType::ATTR_VALUE, $1);
	$$ = -2; //we are not a valid shape=xxx attr.
  #else
    if ($1.had_error || (chart.SkipContent() && $1.had_param))
        $$ = nullptr;
    else
        $$ = new Attribute($1, "++"+$4.str(), @1, @3+@4);
  #endif
    $1.destroy();
    $4.destroy();
}
      | alpha_string TOK_EQUAL TOK_PLUS
{
  //string=string
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
    csh.AddCSH_AttrName(@1, $1, COLOR_ATTRNAME);
    csh.AddCSH(@2, COLOR_EQUAL);
    if (!$1.had_error)
        csh.AddCSH_AttrValue_CheckAndAddEscapeHint(@3, "++", $1);
    csh.AddCSH_ErrorAfter(@3, "Continue with a color name or definition.");
    csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME);
    if (!$1.had_error)
        csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE, $1);
	$$ = -2; //we are not a valid shape=xxx attr.
  #else
    if ($1.had_error || (chart.SkipContent() && $1.had_param))
        $$ = nullptr;
    else
        $$ = new Attribute($1, "++", @1, @3);
  #endif
    $1.destroy();
}
      | alpha_string TOK_EQUAL
{
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
        csh.AddCSH_AttrName(@1, $1, COLOR_ATTRNAME);
    csh.AddCSH(@2, COLOR_EQUAL);
    csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME);
    if (!$1.had_error)
        csh.CheckHintAfter(@2, EHintSourceType::ATTR_VALUE, $1);
	$$ = -2; //we are not a valid shape=xxx attr.
  #else
    if ($1.had_error || (chart.SkipContent() && $1.had_param))
        $$ = nullptr;
    else
        $$ = new Attribute($1, {}, @$, @$);
  #endif
    $1.destroy();
}
      | string
{
  //here we accept non alpha strings for "->" and similar style names
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
        csh.AddCSH_StyleOrAttrName(@1, $1);
    csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME);
	$$ = -2; //we are not a valid shape=xxx attr.
  #else
    if ($1.had_error || (chart.SkipContent() && $1.had_param))
        $$ = nullptr;
    else
        $$ = new Attribute($1, @$);
  #endif
    $1.destroy();
}
      | TOK_NUMBER
{
  //This may come as, e.g., size=12,12
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_ATTRVALUE);
	$$ = -2; //we are not a valid shape=xxx attr.
  #else
    //An attribute of empty name is to be concatenated to the previous one.
    $$ = new Attribute("", $1, @$, @$);
  #endif
    (void)$1;
};

 //This will leave an "AddEntities" in csh.addEntityNamesAtEnd, when we need to hint an entity
 //This will leave an "AddAfter@" in csh.addEntityNamesAtEnd, when we need to hint a string after the @
 //Result may contain position escapes.
attrvalue: attrvalue_simple
      | attrvalue_simple TOK_NUMBER
{
  //This may come as, e.g., size=12,12
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_ATTRVALUE);
    if ($2[0]=='+' || $2[0]=='-')
        $$.CombineThemToMe($1, $2);
    else
        $$.CombineThemToMe($1, '+', $2);
  #else
    if ($2[0]=='+' || $2[0]=='-')
        $$.CombineThemToMe($1, @2.start().Print(), $2);
    else
        $$.CombineThemToMe($1, '+', @2.start().Print(), $2);
  #endif
}
      | alignment_attrvalue
      | coord;

attrvalue_simple: symbol_string { $$.set($1); }
      | reserved_word_string { $$.set($1); }
      | reserved_word_string multi_string_continuation { $$.CombineThemToMe($1, $2); };

alignment_attrvalue:  blocknames_plus_number_multi
      | blocknames_plus_number_multi TOK_ATSYMBOL
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_ATTRVALUE);
    csh.AddCSH_ErrorAfter(@2, "Missing token or number here.");
    if (csh.CheckHintAfter(@2, EHintSourceType::ATTR_VALUE))
        csh.AddEntityNamesAtTheEnd("AddAfter@"); //to be filled later
  #endif
    $$.CombineThemToMe($1, '@');
}
      | blocknames_plus_number_multi TOK_ATSYMBOL edgepos
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2+@3, COLOR_ATTRVALUE);
    if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE))
        csh.AddEntityNamesAtTheEnd("AddAfter@"); //to be filled later
  #endif
    $$.CombineThemToMe($1, '@', $3);
}
      | TOK_NUMBER TOK_PERCENT
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_ATTRVALUE);
  #endif
    $$.CombineThemToMe($1, '%');
}
      | TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_ATTRVALUE);
  #endif
    $$.set($1);
}
      | TOK_NUMBER TOK_PERCENT TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_ATTRVALUE);
  #endif
    if ($3[0]=='+' || $3[0]=='-')
        $$.CombineThemToMe($1, '%', $3);
    else
        $$.CombineThemToMe($1, "%+", $3);
}
      | entity_string TOK_PERCENT
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_ATTRVALUE);
  #endif
    $$.CombineThemToMe($1, '%');
}
      | entity_string TOK_PERCENT TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_ATTRVALUE);
  #endif
    if ($3[0]=='+' || $3[0]=='-')
        $$.CombineThemToMe($1, '%', $3);
    else
        $$.CombineThemToMe($1, "%+", $3);
};


edgepos: alpha_string_or_percent
      | alpha_string_or_percent TOK_NUMBER
{
    if ($2[0]=='+' || $2[0]=='-')
        $$.CombineThemToMe($1, $2);
    else
        $$.CombineThemToMe($1, '+', $2);
};



alpha_string_or_percent: alpha_string
      | alpha_string TOK_PERCENT //Allow it since "@v33%" parses as: TOK_AT alpha_string TOK_PERCENT
      | TOK_NUMBER { $$.set($1); }
      | TOK_NUMBER TOK_PERCENT { $$.set($1); };

 //This will leave an "AddEntities" in csh.addEntityNamesAtEnd, when we need to hint an entity
blocknames_plus_number_multi: blocknames_plus_number
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_NameList($1, @1, COLOR_ENTITYNAME, COLOR_COMMA, COLOR_ATTRVALUE, COLOR_ENTITYNAME);
    if ($1->empty())
        $$.init();
    else if ($1->size()==1 && $1->front().name.length())
        $$.set($1->front().name);
    else
  #endif
    {
        std::string acc;
        for (auto &s: *$1)
            if (s.name.empty()) {
                $$.set_error();
                goto finish;
            } else {
                //avoid double signs
                if (acc.length() && s.name.length() && (s.name.front()=='-' || s.name.front()=='+'))
                    acc.pop_back();
  #ifdef C_S_H_IS_COMPILED
                acc.append(s.name).push_back('+');
  #else
                acc.append(s.file_pos.start.Print()).append(s.name).push_back('+');
  #endif
            }
        if (acc.length()) acc.pop_back();
        if (acc.length())
            $$.set_owning(std::move(acc));
        else
            $$.init();
    }
finish:
    delete $1;
};

blocknames_plus_number_or_number: blocknames_plus_number
      | TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    $$ = new CshStringViewWithPosList;
    $$->push_back({$1, @1});
  #else
    $$ = new StringWithPosList;
    $$->push_back({$1, @1});
  #endif
    (void)$1;
};


 //This will leave an "AddEntities" in csh.addEntityNamesAtEnd, when we need to hint an entity
blocknames_plus_number: entity_string
{
  #ifdef C_S_H_IS_COMPILED
    $$ = new CshStringViewWithPosList;
    if (!$1.had_error) {
        $$->push_back({$1, @1});
    } else {
        $$->push_back({"", @1});
        csh.AddCSH_Error(@1, "Expecting a block name or number here.");
    }
    if (csh.CheckHintAt(@1, EHintSourceType::ENTITY))
        csh.AddEntityNamesAtTheEnd("AddEntities+num"); //to be filled later
  #else
    $$ = new StringWithPosList;
    if (!$1.had_error) {
        $$->push_back({$1, @1});
    } else {
        chart.Error.Error(@1, "Expecting a block name or number here.");
        $$->push_back({"", @1});
    }
  #endif
    $1.destroy();
}
      | entity_string TOK_PLUS
{
  #ifdef C_S_H_IS_COMPILED
    $$ = new CshStringViewWithPosList;
    if (!$1.had_error) {
        $$->push_back({$1, @1});
    } else {
        $$->push_back({"", @1});
        csh.AddCSH_Error(@1, "Expecting a block name here.");
    }
    csh.AddCSH_ErrorAfter(@2, "Missing a box name here.");
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_VALUE))
        csh.AddEntityNamesAtTheEnd("AddEntities"); //to be filled later
    else if (csh.CheckHintAfter(@2, EHintSourceType::ATTR_VALUE))
        csh.AddEntityNamesAtTheEnd("AddEntities+num"); //to be filled later
  #else
    chart.Error.Error(@$.after(), "Missing a box name here.");
    $$ = new StringWithPosList;
    if (!$1.had_error) {
        $$->push_back({$1, @1});
    } else {
        chart.Error.Error(@1, "Expecting a block name here.");
        $$->push_back({"", @1});
    }
  #endif
    $1.destroy();
}
      | entity_string TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    $$ = new CshStringViewWithPosList;
    if (!$1.had_error) {
        $$->push_back({$1, @1});
    } else {
        csh.AddCSH_Error(@1, "Expecting a block name here.");
        $$->push_back({"", @1});
    }
    $$->push_back({$2.view().substr($2[0]=='+'), @2});
    //Do the checking in two separate items, so that hintedStringPos
    //covers only one of them (for correct replacement)
    if (csh.CheckHintAt(@1, EHintSourceType::ENTITY))
        csh.AddEntityNamesAtTheEnd("AddEntities"); //to be filled later
    else if (csh.CheckHintAt(@2, EHintSourceType::ENTITY))
        csh.AddEntityNamesAtTheEnd("AddEntities+num"); //to be filled later
  #else
    $$ = new StringWithPosList;
    if (!$1.had_error) {
        $$->push_back({$1, @1});
    } else {
        chart.Error.Error(@1, "Expecting a block name here.");
        $$->push_back({"", @1});
    }
    $$->push_back({$2, @2});
  #endif
    $1.destroy();
    (void)$2;
}
      | entity_string TOK_PLUS blocknames_plus_number
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintBetween(@2, @3, EHintSourceType::ENTITY) ||
        csh.CheckHintAt(@1, EHintSourceType::ENTITY))
        csh.AddEntityNamesAtTheEnd("AddEntities"); //to be filled later
    if ($3) {
       if (!$1.had_error)
            $3->insert($3->begin(), {$1, @1});
       else
            $3->insert($3->begin(), {"", @1});
    }
  #else
    if ($3) {
        if (!$1.had_error)
            $3->insert($3->begin(), {$1, @1});
        else
            $3->insert($3->begin(), {"", @1});
    }
  #endif
    $$ = $3;
    $1.destroy();
};


 //This will leave an "AddEntities" in csh.addEntityNamesAtEnd, when we need to hint an entity
 //This will be invoked for around, join and alignment attributes, but not for aligment modifiers
blocknames_plus: entity_string
{
  #ifdef C_S_H_IS_COMPILED
    $$ = new CshStringViewWithPosList;
    if (!$1.had_error) {
        $$->push_back({$1, @1});
    } else {
        csh.AddCSH_Error(@1, "Expecting a block name or number here.");
        $$->push_back({"", @1});
    }
    if (csh.CheckHintAt(@1, EHintSourceType::ENTITY))
        csh.AddEntityNamesAtTheEnd("AddEntities"); //to be filled later
  #else
    $$ = new StringWithPosList;
    if (!$1.had_error) {
        $$->push_back({$1, @1});
    } else {
        chart.Error.Error(@1, "Expecting a block name or number here.");
        $$->push_back({"", @1});
    }
  #endif
    $1.destroy();
}
      | entity_string TOK_PLUS
{
  #ifdef C_S_H_IS_COMPILED
    $$ = new CshStringViewWithPosList;
    if (!$1.had_error) {
        $$->push_back({$1, @1});
    } else {
        csh.AddCSH_Error(@1, "Expecting a block name here.");
        $$->push_back({"", @1});
    }
    csh.AddCSH_ErrorAfter(@2, "Missing a box name here.");
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_VALUE))
        csh.AddEntityNamesAtTheEnd("AddEntities"); //to be filled later
    else if (csh.CheckHintAfter(@2, EHintSourceType::ATTR_VALUE))
        csh.AddEntityNamesAtTheEnd("AddEntities"); //to be filled later
  #else
    chart.Error.Error(@$.after(), "Missing a box name here.");
    $$ = new StringWithPosList;
    if (!$1.had_error) {
        $$->push_back({$1, @1});
    } else {
        chart.Error.Error(@1, "Expecting a block name here.");
        $$->push_back({"", @1});
    }
  #endif
    $1.destroy();
}
      | entity_string TOK_PLUS blocknames_plus_number
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintBetween(@2, @3, EHintSourceType::ENTITY))
        csh.AddEntityNamesAtTheEnd("AddEntities"); //to be filled later
    if ($3) {
       if (!$1.had_error)
            $3->insert($3->begin(), {$1, @1});
       else
            $3->insert($3->begin(), {"", @1});
    }
  #else
    if ($3) {
        if (!$1.had_error)
            $3->insert($3->begin(), {$1, @1});
        else
            $3->insert($3->begin(), {"", @1});
    }
  #endif
    $$ = $3;
    $1.destroy();
};


coord_hinted: coord
{
  #ifdef C_S_H_IS_COMPILED
    //We dont just check if the hint was located in @$ as the cursor may be
	//*after* @$ in case of a missing parenthesis, for example.
    if (csh.CheckHintLocated(@1) || csh.CheckHintLocated(EHintSourceType::ATTR_VALUE))
        csh.HandleBlockNamePlus("generic_coord");
  #endif
    $$ = $1;
};


coord: TOK_OPARENTHESIS alignment_attrvalue TOK_COMMA alignment_attrvalue TOK_CPARENTHESIS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PARENTHESIS);
    csh.AddCSH(@3, COLOR_COMMA);
    csh.AddCSH(@5, COLOR_PARENTHESIS);
    if (csh.addEntityNamesAtEnd=="AddAfter@") {
        if (csh.CursorIn(@2)>CURSOR_AFTER)
            csh.AddEntityNamesAtTheEnd("AddAfter@X");
        else if (csh.CursorIn(@4)>CURSOR_AFTER)
            csh.AddEntityNamesAtTheEnd("AddAfter@Y");
    }
  #endif
    $$.CombineThemToMe('(', $2, ',' , $4, ')');
}
      | TOK_OPARENTHESIS alignment_attrvalue TOK_COMMA TOK_CPARENTHESIS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PARENTHESIS);
    csh.AddCSH(@3, COLOR_COMMA);
    csh.AddCSH(@4, COLOR_PARENTHESIS);
    if (csh.addEntityNamesAtEnd=="AddAfter@") {
        if (csh.CursorIn(@2)>CURSOR_AFTER)
            csh.AddEntityNamesAtTheEnd("AddAfter@X");
    } else if (csh.CheckHintBetween(@3, @4, EHintSourceType::ATTR_VALUE)) {
        csh.AddEntityNamesAtTheEnd("AddEntities"); //to be filled later
    }
  #endif
    $$.CombineThemToMe('(', $2, ",)");
}
      | TOK_OPARENTHESIS TOK_COMMA alignment_attrvalue TOK_CPARENTHESIS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PARENTHESIS);
    csh.AddCSH(@2, COLOR_COMMA);
    csh.AddCSH(@4, COLOR_PARENTHESIS);
    if (csh.addEntityNamesAtEnd=="AddAfter@") {
        if (csh.CursorIn(@3)>CURSOR_AFTER)
            csh.AddEntityNamesAtTheEnd("AddAfter@Y");
    } else if (csh.CheckHintBetween(@1, @2, EHintSourceType::ATTR_VALUE)) {
        csh.AddEntityNamesAtTheEnd("AddEntities"); //to be filled later
    }
  #endif
    $$.CombineThemToMe("(," , $3, ')');
}
      | TOK_OPARENTHESIS TOK_COMMA TOK_CPARENTHESIS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PARENTHESIS);
    csh.AddCSH(@2, COLOR_COMMA);
    csh.AddCSH(@3, COLOR_PARENTHESIS);
    if (csh.CheckHintBetween(@1, @3, EHintSourceType::ATTR_VALUE)) {
        csh.AddEntityNamesAtTheEnd("AddEntities"); //to be filled later
    }
  #endif
    $$.set(str_view{"(,)"});
}
      | TOK_OPARENTHESIS TOK_CPARENTHESIS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PARENTHESIS);
    csh.AddCSH(@2, COLOR_PARENTHESIS);
    if (csh.CheckHintBetween(@1, @2, EHintSourceType::ATTR_VALUE)) {
        csh.AddEntityNamesAtTheEnd("AddEntities"); //to be filled later
    }
    csh.AddCSH_ErrorAfter(@1, "Missing an X coordinate staring with a blockname.");
  #else
    chart.Error.Error(@1.after(), "Missing an X coordinate staring with a blockname. Ignoring this.");
  #endif
    $$.set(str_view{"("});
}
      | TOK_OPARENTHESIS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PARENTHESIS);
    if (csh.CheckHintAfter(@1, EHintSourceType::ATTR_VALUE)) {
        csh.AddEntityNamesAtTheEnd("AddEntities"); //to be filled later
    }
    csh.AddCSH_ErrorAfter(@$, "Missing an X coordinate staring with a blockname.");
  #else
    chart.Error.Error(@$.after(), "Missing an X coordinate staring with a blockname. Ignoring this.");
  #endif
    $$.set(str_view{"("});
}
      | TOK_OPARENTHESIS alignment_attrvalue
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PARENTHESIS);
    if (csh.addEntityNamesAtEnd=="AddAfter@") {
        if (csh.CursorIn(@2)>CURSOR_AFTER)
            csh.AddEntityNamesAtTheEnd("AddAfter@X");
    }
    csh.AddCSH_ErrorAfter(@$, "Missing comma and an Y coordinate after.");
  #else
    chart.Error.Error(@$.after(), "Missing comma and an Y coordinate after. Ignoring this.");
  #endif
    $2.destroy();
    $$.set(str_view{"("});
}
      | TOK_OPARENTHESIS alignment_attrvalue TOK_COMMA
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PARENTHESIS);
    csh.AddCSH(@3, COLOR_COMMA);
    if (csh.addEntityNamesAtEnd=="AddAfter@") {
        if (csh.CursorIn(@2)>CURSOR_AFTER)
            csh.AddEntityNamesAtTheEnd("AddAfter@X");
    } else if (csh.CheckHintAfter(@$, EHintSourceType::ATTR_VALUE)) {
        csh.AddEntityNamesAtTheEnd("AddEntities"); //to be filled later
    }
    csh.AddCSH_ErrorAfter(@$, "Missing an Y coordinate starting with a block name.");
  #else
    chart.Error.Error(@$.after(), "Missing an Y coordinate starting with a block name. Ignoring this.");
  #endif
    $2.destroy();
    $$.set(str_view{"("});
}
      | TOK_OPARENTHESIS TOK_COMMA
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PARENTHESIS);
    csh.AddCSH(@2, COLOR_COMMA);
    if (csh.addEntityNamesAtEnd=="AddAfter@") {
        if (csh.CursorIn(@2)>CURSOR_AFTER)
            csh.AddEntityNamesAtTheEnd("AddAfter@X");
    }
    csh.AddCSH_ErrorAfter(@$, "Missing an Y coordinate starting with a block name.");
  #else
    chart.Error.Error(@$.after(), "Missing an Y coordinate starting with a block name. Ignoring this.");
  #endif
    $$.set(str_view{"("});
}
      | TOK_OPARENTHESIS alignment_attrvalue TOK_COMMA alignment_attrvalue
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PARENTHESIS);
    csh.AddCSH(@3, COLOR_COMMA);
    if (csh.addEntityNamesAtEnd=="AddAfter@") {
        if (csh.CursorIn(@2)>CURSOR_AFTER)
            csh.AddEntityNamesAtTheEnd("AddAfter@X");
        else if (csh.CursorIn(@4)>CURSOR_AFTER)
            csh.AddEntityNamesAtTheEnd("AddAfter@Y");
    }
    csh.AddCSH_ErrorAfter(@$, "Missing a closing parenthesis ')'.");
  #else
    chart.Error.Error(@$.after(), "Missing a closing parenthesis ')'.");
  #endif
    $$.CombineThemToMe('(', $2, ',' , $4, ')');
}
      | TOK_OPARENTHESIS TOK_COMMA alignment_attrvalue
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_PARENTHESIS);
    csh.AddCSH(@2, COLOR_COMMA);
    if (csh.addEntityNamesAtEnd=="AddAfter@") {
        if (csh.CursorIn(@3)>CURSOR_AFTER)
            csh.AddEntityNamesAtTheEnd("AddAfter@Y");
    }
    csh.AddCSH_ErrorAfter(@$, "Missing a closing parenthesis ')'.");
  #else
    chart.Error.Error(@$.after(), "Missing a closing parenthesis ')'.");
  #endif
    $$.CombineThemToMe("(," , $3, ')');
};



/*************************************************************************
 * Strings
 *************************************************************************/


/* 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_BOX | TOK_BOXCOL | TOK_ROW | TOK_COLUMN | TOK_TEXT | TOK_SHAPE | TOK_CELL
      | TOK_COMMAND_DEFSHAPE | TOK_COMMAND_DEFCOLOR | TOK_COMMAND_DEFSTYLE | TOK_COMMAND_DEFDESIGN
      | TOK_COMMAND_DEFPROC | TOK_COMMAND_REPLAY | TOK_COMMAND_SET
      | TOK_COMMAND_USEDESIGN | TOK_COMMAND_USESTYLE
      | TOK_COMMAND_ARROWS | TOK_COMMAND_BLOCKS | TOK_CLONE_MODIFIER_RECURSIVE
      | TOK_CLONE_MODIFIER_ADD | TOK_CLONE_MODIFIER_MOVE | TOK_CLONE_MODIFIER_DROP
      | TOK_CLONE_MODIFIER_UPDATE | TOK_CLONE_MODIFIER_REPLACE | TOK_CLONE_MODIFIER_BEFORE
      | TOK_IF | TOK_THEN | TOK_ELSE | TOK_BYE
      | TOK_BREAK_COMMAND | TOK_SPACE_COMMAND | TOK_MULTI_COMMAND
      | TOK_AROUND_COMMAND | TOK_JOIN_COMMAND | TOK_COPY_COMMAND | TOK_AS
      | TOK_MARK_COMMAND | TOK_EXTEND_COMMAND | TOK_TEMPLATE_COMMAND
      | TOK_CHART | TOK_ALIGN_MODIFIER 
{
    $$.set($1.dir==EDirection::Below ? ($1.major ? "below" : "bottom") :
           $1.dir==EDirection::Above ? ($1.major ? "above" : "top") :
           $1.dir==EDirection::Right ? ($1.major ? "rightof" : "right") :
                                       ($1.major ? "leftof" : "left"));
};

/* List here all your symbols, such as -> that are not alpahnumeric but have default style names.*/
symbol_string: arrowsymbol
{
    if ($1.start) {
        if ($1.end)
            switch($1.style) {
            default: _ASSERT(0); FALLTHROUGH;
            case EArrowStyle::SOLID: $$.set("<->"); break;
            case EArrowStyle::DOTTED: $$.set("<>"); break;
            case EArrowStyle::DASHED: $$.set("<<>>"); break;
            case EArrowStyle::DOUBLE: $$.set("<=>"); break;
            }
        else
            switch($1.style) {
            default: _ASSERT(0); FALLTHROUGH;
            case EArrowStyle::SOLID: $$.set("<-"); break;
            case EArrowStyle::DOTTED: $$.set("<"); break;
            case EArrowStyle::DASHED: $$.set("<<"); break;
            case EArrowStyle::DOUBLE: $$.set("<="); break;
            }
    } else {
        if ($1.end)
            switch($1.style) {
            default: _ASSERT(0); FALLTHROUGH;
            case EArrowStyle::SOLID: $$.set("->"); break;
            case EArrowStyle::DOTTED: $$.set(">"); break;
            case EArrowStyle::DASHED: $$.set(">>"); break;
            case EArrowStyle::DOUBLE: $$.set("=>"); break;
            }
        else
            switch($1.style) {
            default: _ASSERT(0); FALLTHROUGH;
            case EArrowStyle::SOLID: $$.set("--"); break;
            case EArrowStyle::DOTTED: $$.set(".."); break;
            case EArrowStyle::DASHED: $$.set("++"); break;
            case EArrowStyle::DOUBLE: $$.set("=="); break;
            }
    }
};

//will not be a reserved word, symbol or style name
entity_string_single: 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}); };

alpha_string_single: reserved_word_string;

string_single: symbol_string;

tok_param_name_as_multi: TOK_PARAM_NAME
{
  #ifdef C_S_H_IS_COMPILED
    if ($1.empty() || $1[0]!='$' || $1.len<2) {
        csh.AddCSH_Error(@1, "Need name after the '$' sign.");
        $$.set_error();
    } else {
        $$.set($1);  //later functions rely this not being empty if valid
        $$.had_param = true;
        csh.StoreMulti($$, @$);
    }
  #else
    if ($1.empty() || $1[0]!='$' || $1.len<2) {
        chart.Error.Error(@1, "Need name after the '$' sign.");
        $$.set_error();
    } else if (!chart.SkipContent()) {
        //When parsing a procedure we we accept all params
        //as they may be variables defined later
        auto p = chart.GetParameter($1);
        if (p==nullptr) {
            chart.Error.Error(@1, "Undefined parameter or variable name.");
            $$.set_error();
        } else {
            $$.set_owning(StringFormat::PushPosEscapes(p->value.c_str(), @1));
            $$.had_param = true;
        }
    } else {
        $$.init();
        $$.had_param = true;
    }
  #endif
};



multi_string_continuation: TOK_TILDE
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_ErrorAfter(@1, "Missing string to concatenate after '~'.");
    $$.init(); //for CSH keep this valid to be colored
  #else
    chart.Error.Error(@1.after(), "Missing string to concatenate after '~'.");
    $$.set_error();
  #endif
}
      | TOK_TILDE string
{
  #ifdef C_S_H_IS_COMPILED
    csh.StoreMulti($2, @2);
  #endif
    $$ = $2;
};

entity_string_single_or_param: entity_string_single { $$.set($1); }
      | tok_param_name_as_multi;

entity_string: entity_string_single_or_param
      | entity_string_single_or_param multi_string_continuation
{
  #ifdef C_S_H_IS_COMPILED
    csh.StoreMulti($1, @1);
  #endif
    $$.CombineThemToMe($1, $2);
};

alpha_string: entity_string
      | alpha_string_single { $$.set($1); }
      | alpha_string_single multi_string_continuation
{
  #ifdef C_S_H_IS_COMPILED
    csh.StoreMulti($1, @1);
  #endif
    $$.CombineThemToMe($1, $2);
};

string: alpha_string
      | string_single { $$.set($1); }
      | string_single multi_string_continuation
{
  #ifdef C_S_H_IS_COMPILED
    csh.StoreMulti($1, @1);
  #endif
    $$.CombineThemToMe($1, $2);
};


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
    if (proc_helper.open_context_mode == EScopeOpenMode::PROC_REPLAY) {
        //Open a scope to replay a procedure. The text of the procedure has
        //already been placed to the lex buffer stack (in fact the '{' already comes
        //from there), and the parameters are in YYEXTRA.
        //Copy current parent to the newly opened scope - it shall be the same during procedure replay as outside the proc
        Parent parent = chart.MyCurrentContext().parent;
        proc_helper.open_context_mode = EScopeOpenMode::NORMAL;
        chart.PushContext(@1, EContextParse::REPARSING);
        chart.MyCurrentContext().starts_procedure = true;
        chart.MyCurrentContext().parameters = std::move(proc_helper.last_procedure_params);
        chart.MyCurrentContext().export_colors = proc_helper.last_procedure->export_colors;
        chart.MyCurrentContext().export_styles = proc_helper.last_procedure->export_styles;
        proc_helper.last_procedure = nullptr;
        chart.MyCurrentContext().parent = std::move(parent);
    } else {
        //Just open a regular scope
        chart.PushContext(@1);
        //Apply the saved alignment attributes to running style
        chart.MyCurrentContext().running_style_blocks += chart.parent_style;
        chart.MyCurrentContext().parent = chart.current_parent;
    }
  #endif
    (void) $1; //suppress
};

scope_close: TOK_CCBRACKET
{
  #ifdef C_S_H_IS_COMPILED
    $$ = nullptr;
    csh.PopContext();
    csh.AddCSH(@1, COLOR_BRACE);
    if (csh.CheckLineStartHintAfter(@1)) {
       csh.AddLineBeginToHints();
       csh.hintStatus = HINT_READY;
    }
  #else
    $$ = chart.PopContext().release();
  #endif
    (void) $1; //suppress
};


%%


/* END OF FILE */
