//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 378
%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 "xxxcsh.h"
    #include "xxxchart.h"
    #include "xxx_lexer.h"

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

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

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

    using namespace xxx;
}

//Goes to the parser.cpp files
%code {
    #include "xxx_tokenizer.h"
    #include "xxx_lexer.h"

    #ifdef C_S_H_IS_COMPILED
        void xxx_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 xxx_csh_lex(XXX_CSH_STYPE *lvalp, SourceLocationRange<C_S_H> *llocp, xxx::XxxCsh& csh, sv_reader<C_S_H>& input) {
            return get_token<xxx_csh_tokentype>(input, csh, xxx::readandclassify<sv_reader<C_S_H>>, *lvalp, *llocp);
        }
    #else
        void xxx_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 xxx_compile_lex(XXX_COMPILE_STYPE *lvalp, SourceLocationRange<C_S_H> *llocp, XxxChart& chart, sv_reader<C_S_H>& input) {
            return get_token<xxx_compile_tokentype>(input, chart, xxx::readandclassify<sv_reader<C_S_H>>, *lvalp, *llocp);
        }
    #endif
}

%token TOK_STRING TOK_QSTRING TOK_NUMBER TOK_DASH TOK_EQUAL TOK_COMMA
       TOK_SEMICOLON TOK_PLUS_PLUS
       TOK_OCBRACKET TOK_CCBRACKET TOK_OSBRACKET TOK_CSBRACKET
       TOK_XXX
       TOK_SHAPE_COMMAND TOK_COMP_OP
       TOK_COLON_STRING TOK_COLON_QUOTED_STRING TOK_COLORDEF
       TOK_COMMAND_DEFSHAPE TOK_COMMAND_DEFCOLOR TOK_COMMAND_DEFSTYLE TOK_COMMAND_DEFDESIGN
       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_WHITESPACE TOK_NEWLINE TOK_COMMENT
%union
{
    str_view                                                   str;
    gsl::owner<std::vector<std::string>*>                      stringlist;
    ShapeElement::Type                                         shapecommand;
    gsl::owner<CHAR_IF_CSH(Shape)*>                            shape;
    gsl::owner<CHAR_IF_CSH(ShapeElement)*>                     shapeelement;
    gsl::owner<CHAR_IF_CSH(Attribute)*>                        attribute;
    gsl::owner<CHAR_IF_CSH(AttributeList)*>                    attributelist;
    gsl::owner<CHAR_IF_CSH(XxxInstruction)*>                   instruction;
    gsl::owner<CHAR_IF_CSH(XxxInstrList)*>                     instruction_list;
    const char*                                                input_text_ptr;
    int                                                        condition; //0:false, 1:true, 2:had_error
    ECompareOperator                                           compare_op;
    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;
};

%type <multi_str>  entity_string string alpha_string
                   entity_string_single_or_param
                   multi_string_continuation tok_param_name_as_multi color_string
                   TOK_COLON_QUOTED_STRING TOK_COLON_STRING
%type <str> TOK_STRING TOK_QSTRING TOK_COLORDEF
            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_XXX
            include
            entity_string_single string_single alpha_string_single
            reserved_word_string symbol_string
%type <stringlist> stylenamelist
%type <shapecommand> TOK_SHAPE_COMMAND
%type <shapeelement> shapeline
%type <shape> shapedeflist
%type <attribute> attr
%type <attributelist> full_attrlist_with_label full_attrlist attrlist
%type <instruction> instr instr_with_semicolon opt ifthen
%type <instruction_list>
    instrlist braced_instrlist top_level_instrlist optlist
    several_instructions optlist_with_semicolon scope_close
%type <condition> condition ifthen_condition else
%type <compare_op> comp TOK_COMP_OP
%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

%destructor { }                      <shapecommand> <str>
%destructor {delete $$;}             <stringlist>
%destructor {if (!C_S_H) delete $$;} <*>
%destructor {if (!C_S_H) delete $$;} <procparamdeflist> <procparamdef> <procparaminvoc> <procparaminvoclist> <procdefhelper> <procedure>
%destructor {$$.destroy();} <multi_str>
%destructor {} <cprocedure> <input_text_ptr> <condition> <compare_op>
%destructor {
  #ifdef C_S_H_IS_COMPILED
    csh.PopContext();
  #else
    chart.PopContext();
  #endif
} ifthen_condition else

%%

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
    //TODO: Add the instructionlist to the chart
    (void)$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;
}
      | 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;
};


braced_instrlist: scope_open instrlist scope_close
{
  #ifdef C_S_H_IS_COMPILED
    csh.BracePairs.push_back(@$);
  #else
    if ($3) ($2)->Append($3); //Append any potential CommandNumbering
    $$ = $2;
  #endif
}
      | scope_open scope_close
{
  #ifdef C_S_H_IS_COMPILED
    csh.BracePairs.push_back(@$);
  #else
    $$ = new XxxInstrList;
    //scope_close should not return here with a CommandNumbering
    //but just in case
    if ($2)
        delete($2);
  #endif
}
      | scope_open instrlist error scope_close
{
  #ifdef C_S_H_IS_COMPILED
    csh.BracePairs.push_back(@$);
    csh.AddCSH_Error(@3, "Could not recognize this as a valid line.");
  #else
    if ($4) ($2)->Append($4);
    $$ = $2;
    chart.Error.Error(@3, "Syntax error.");
  #endif
    yyerrok;
}
      | scope_open instrlist error TOK_EOF
{
  #ifdef C_S_H_IS_COMPILED
	csh.AddOpenBracePair(@$);
    csh.AddCSH_Error(@3, "Could not recognize this as a valid line.");
    csh.PopContext();
  #else
    $$ = $2;
    $$->Append(chart.PopContext()); //will be empty list of nodes anyway.
    chart.Error.Error(@3, "Missing '}'.");
    chart.Error.Error(@1, @3, "Here is the corresponding '{'.");
  #endif
}
      | scope_open instrlist TOK_EOF
{
  #ifdef C_S_H_IS_COMPILED
	csh.AddOpenBracePair(@$);
    csh.AddCSH_ErrorAfter(@2, "Missing a closing brace ('}').");
    csh.PopContext();
  #else
    $$ = $2;
    $$->Append(chart.PopContext()); //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
    $$ = std::make_unique<XxxInstrList>(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()); //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
    $$ = std::make_unique<XxxInstrList>(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:    instr_with_semicolon
{
  #ifndef C_S_H_IS_COMPILED
    if ($1)
        $$ = (new XxxInstrList)->Append($1); /* New list */
    else
        $$ = new XxxInstrList;
  #endif
}
      | instrlist instr_with_semicolon
{
  #ifndef C_S_H_IS_COMPILED
    if ($2) ($1)->Append($2);     /* Add to existing list */
    $$ = ($1);
  #endif
}
      | several_instructions
      | instrlist several_instructions
{
  #ifndef C_S_H_IS_COMPILED
    //TODO: Add a nested instructionlist to another instructionslist
    if ($2) ($1)->Append($2);     /* Add to existing list */
    $$ = ($1);
  #endif
};

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


instr_with_semicolon: instr
{
  #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 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 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 (auto proc = $1)
        if (auto ctx = proc->MatchParameters(nullptr, @1.after(), &chart)) {
            input.push(proc->text, EInclusionReason::PROCEDURE, proc->file_pos, @$);
            proc_helper.last_procedure = proc;
            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 (auto proc = $1)
        if (auto ctx = proc->MatchParameters(ProcParamInvocationList{}, @1.after(), &chart)) {
            input.push(proc->text, EInclusionReason::PROCEDURE, proc->file_pos, @$);
            proc_helper.last_procedure = proc;
            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 (auto proc = $1)
        if (auto ctx = proc->MatchParameters(ProcParamInvocationList{}, @1.after(), &chart)) {
            input.push(proc->text, EInclusionReason::PROCEDURE, proc->file_pos, @$);
            proc_helper.last_procedure = proc;
            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 (auto proc = $1; proc && $2) {
        if (auto ctx = proc->MatchParameters($2, @2.end(), &chart)) {
            input.push(proc->text, EInclusionReason::PROCEDURE, proc->file_pos, @$);
            proc_helper.last_procedure = proc;
            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 (auto proc = $1; proc && $2) {
        if (auto ctx = proc->MatchParameters($2, @2.end(), &chart)) {
            input.push(proc->text, EInclusionReason::PROCEDURE, proc->file_pos, @$);
            proc_helper.last_procedure = proc;
            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 (auto proc = $1; proc && $2) {
        if (auto ctx = proc->MatchParameters($2, @2.end(), &chart)) {
            input.push(proc->text, EInclusionReason::PROCEDURE, proc->file_pos, @$);
            proc_helper.last_procedure = proc;
            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
};

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);
  #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;
};

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;
};


optlist_with_semicolon: optlist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstruction(@$);
    csh.AddCSH_ErrorAfter(@1, "Missing semicolon (';').");
  #else
    $$=$1;
    chart.Error.Error(@1.after(), "Missing a semicolon ';'.");
  #endif
}
      | optlist TOK_SEMICOLON
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstruction(@$);
    csh.AddCSH(@2, COLOR_SEMICOLON);
    if (csh.CheckLineStartHintAfter(@2)) {
       csh.AddLineBeginToHints();
       csh.hintStatus = HINT_READY;
    }
  #else
    $$=$1;
  #endif
}
      | optlist error TOK_SEMICOLON
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstruction(@$);
    csh.AddCSH(@3, COLOR_SEMICOLON);
    csh.AddCSH_Error(@2, "I am not sure what is coming here.");
    if (csh.CheckLineStartHintAfter(@3)) {
       csh.AddLineBeginToHints();
       csh.hintStatus = HINT_READY;
    }
  #else
    $$=$1;
    chart.Error.Error(@1.after(), "Missing a semicolon ';' after option(s).");
    chart.Error.Error(@1, @1.after(), "Here is the beginning of the option list as I understood it.");
  #endif
};



instr:         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_DEFCOLOR colordeflist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@1, @2, EHintSourceType::ATTR_VALUE)) {
        csh.AddColorValuesToHints(true);
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_COMMAND_DEFCOLOR
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@1, EHintSourceType::ATTR_VALUE)) {
        csh.AddColorValuesToHints(true);
        csh.hintStatus = HINT_READY;
	}
	csh.AddCSH_ErrorAfter(@$, "Missing color name to (re-)define.");
  #else
    chart.Error.Error(@$.after(), "Missing a color name to (re-)define.");
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_COMMAND_DEFSTYLE styledeflist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@1, @2, EHintSourceType::ATTR_VALUE)) {
        csh.AddStylesToHints(true, true);
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_COMMAND_DEFSTYLE
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@1, EHintSourceType::ATTR_VALUE)) {
        csh.AddStylesToHints(true, true);
        csh.hintStatus = HINT_READY;
	}
	csh.AddCSH_ErrorAfter(@$, "Missing style name to (re-)define.");
  #else
    chart.Error.Error(@$.after(), "Missing a style name to (re-)define.");
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_COMMAND_DEFDESIGN designdef
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.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
}
      | set
{
  #ifdef C_S_H_IS_COMPILED
  #else
    $$ = nullptr;
  #endif
}
      | ifthen
{
  #ifdef C_S_H_IS_COMPILED
    csh.IfThenElses.push_back(@$);
  #endif
    $$ = $1;
}
      | TOK_XXX
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
  #else
    //TODO: Create a new instruction
    //Take care not to make changes to the chart if we are storing a procedure
    if (!chart.SkipContent())
        $$ = std::make_unique<XxxInstruction>(chart).release();
    else
        $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_XXX full_attrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
  #else
    //TODO: Create a new instruction
    //Take care not to make changes to the chart if we are storing a procedure
    if (!chart.SkipContent())
        $$ = std::make_unique<XxxInstruction>(chart).release();
    else
        $$ = nullptr;
    delete $2;
  #endif
    (void)$1;
};


optlist:     opt
{
  #ifndef C_S_H_IS_COMPILED
    if ($1)
        $$ = (new XxxInstrList)->Append($1); /* New list */
    else
        $$ = new XxxInstrList;
  #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 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.AddOptionsToHints();
        csh.hintStatus = HINT_READY;
    } else if (!$1.had_error && csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE, $1)) {
        XxxChart::AttributeValues($1, csh);
        csh.hintStatus = HINT_READY;
    }
  #else
    if ($1.had_error || (chart.SkipContent() && $1.had_param))
        $$ = nullptr;
    else
        $$ = chart.AddAttribute(Attribute($1, $3, @1, @3)).release();
  #endif
    $1.destroy();
}
      | 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 (!$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 (!$1.had_error && csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE, $1)) {
        XxxChart::AttributeValues($1, csh);
        csh.hintStatus = HINT_READY;
    }
  #else
    if ($1.had_error || $3.had_error || (chart.SkipContent() && ($1.had_param || $3.had_param)))
        $$ = nullptr;
    else
        $$ = chart.AddAttribute(Attribute($1, $3, @1, @3)).release();
  #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)) {
        XxxChart::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();
};

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

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

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

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;
}
		| 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);
  #else
    chart.Error.Error(@3.after(), "Missing '}'.");
    chart.Error.Error(@2, @3.after(), "Here is the corresponding '{'.");
    if (!$1.had_error && !$1.empty() && $3) {
        if (!chart.SkipContent())
            chart.Shapes.Add($1.str(), @1, chart.file_url, chart.file_info, std::move(*$3), chart.Error);
	delete $3;
    }
  #endif
    $1.destroy();
    (void)$2;
}
		| 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);
	csh.AddShapeName($1);
  #else
    if (!$1.had_error && !$1.empty() && $3) {
        if (!chart.SkipContent())
            chart.Shapes.Add($1.str(), @1, chart.file_url, chart.file_info, std::move(*$3), chart.Error);
	delete $3;
    }
  #endif
    $1.destroy();
    (void)$2;
    (void)$4;
}
		| 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);
	csh.AddShapeName($1);
    csh.AddCSH_Error(@4, "Only numbers can come after shape commands.");
  #else
    if (!$1.had_error && !$1.empty() && $3) {
        if (!chart.SkipContent())
            chart.Shapes.Add($1.str(), @1, chart.file_url, chart.file_info, std::move(*$3), chart.Error);
	delete $3;
    }
  #endif
    $1.destroy();
    (void)$2;
    (void)$5;
};

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

shapeline: TOK_SHAPE_COMMAND
{
    const int num_args = 0;
	const int should_args = ShapeElement::GetNumArgs($1);
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
	if (should_args != num_args)
		csh.AddCSH_ErrorAfter(@$, ShapeElement::ErrorMsg($1, num_args));
  #else
	$$ = nullptr;
	if (should_args != num_args)
		chart.Error.Error(@$.after(), ShapeElement::ErrorMsg($1, num_args).append(" Ignoring line."));
	else
	    $$ = new ShapeElement($1);
  #endif
}
      | TOK_SHAPE_COMMAND TOK_NUMBER
{
    const int num_args = 1;
	const int should_args = ShapeElement::GetNumArgs($1);
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
	if (should_args != num_args) {
		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.");
  #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
}
      | 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;
		}
	}
  #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
}
      | TOK_SHAPE_COMMAND TOK_NUMBER TOK_NUMBER alpha_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if ($1!=ShapeElement::PORT)
        csh.AddCSH_Error(@4, "You need to specify a number here.");
  #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);
  #endif
  $4.destroy();
}
      | 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)
        csh.AddCSH_Error(@4, "You need to specify a number here.");
  #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));
  #endif
  $4.destroy();
}
      | TOK_SHAPE_COMMAND TOK_NUMBER TOK_NUMBER TOK_NUMBER
{
    const int num_args = 3;
	const int should_args = ShapeElement::GetNumArgs($1);
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
	if (should_args != num_args) {
		std::string msg = ShapeElement::ErrorMsg($1, num_args);
	    switch (ShapeElement::GetNumArgs($1)) {
		case 0:  csh.AddCSH_Error(@2 + @4, std::move(msg)); break;
		case 1:  csh.AddCSH_Error(@3 + @4, std::move(msg)); break;
		case 2:  csh.AddCSH_Error(@4, std::move(msg)); break;
		default: csh.AddCSH_ErrorAfter(@$, std::move(msg)); break;
		}
	} else if ($1==ShapeElement::PORT)
        csh.AddCSH_Error(@4, "You need to specify a port name here starting with a letter.");
  #else
	$$ = nullptr;
	if (should_args > num_args)
		chart.Error.Error(@$.after(), ShapeElement::ErrorMsg($1, num_args).append(" Ignoring line."));
	else if ($1==ShapeElement::PORT)
		chart.Error.Error(@4, "Expecting a port name here. Ignoring line.");
    else
		$$ = new ShapeElement($1, to_double($2), to_double($3), to_double($4));
  #endif
}
      | 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;
		}
	}
  #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
}
      | TOK_SHAPE_COMMAND TOK_NUMBER TOK_NUMBER TOK_NUMBER TOK_NUMBER TOK_NUMBER
{
    const int num_args = 5;
	const int should_args = ShapeElement::GetNumArgs($1);
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
	if (should_args != num_args) {
		std::string msg = ShapeElement::ErrorMsg($1, num_args);
	    switch (ShapeElement::GetNumArgs($1)) {
		case 0:  csh.AddCSH_Error(@2 + @6, std::move(msg)); break;
		case 1:  csh.AddCSH_Error(@3 + @6, std::move(msg)); break;
		case 2:  csh.AddCSH_Error(@4 + @6, std::move(msg)); break;
		case 3:  csh.AddCSH_Error(@5 + @6, std::move(msg)); break;
		case 4:  csh.AddCSH_Error(@6, std::move(msg)); break;
		default: csh.AddCSH_ErrorAfter(@$, std::move(msg)); break;
		}
	}
  #else
	$$ = nullptr;
	if (should_args > num_args)
		chart.Error.Error(@$.after(), ShapeElement::ErrorMsg($1, num_args).append(" Ignoring line."));
	else
		$$ = new ShapeElement($1, to_double($2), to_double($3), to_double($4), to_double($5), to_double($6));
  #endif
}
      | TOK_SHAPE_COMMAND TOK_NUMBER TOK_NUMBER TOK_NUMBER TOK_NUMBER TOK_NUMBER TOK_NUMBER
{
    const int num_args = 6;
	const int should_args = ShapeElement::GetNumArgs($1);
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
	if (should_args != num_args) {
		std::string msg = ShapeElement::ErrorMsg($1, num_args);
	    switch (ShapeElement::GetNumArgs($1)) {
		case 0:  csh.AddCSH_Error(@2 + @7, std::move(msg)); break;
		case 1:  csh.AddCSH_Error(@3 + @7, std::move(msg)); break;
		case 2:  csh.AddCSH_Error(@4 + @7, std::move(msg)); break;
		case 3:  csh.AddCSH_Error(@5 + @7, std::move(msg)); break;
		case 4:  csh.AddCSH_Error(@6 + @7, std::move(msg)); break;
		case 5:  csh.AddCSH_Error(@7, std::move(msg)); break;
		default: csh.AddCSH_ErrorAfter(@$, std::move(msg)); break;
		}
	}
  #else
	$$ = nullptr;
	if (should_args > num_args)
		chart.Error.Error(@$.after(), ShapeElement::ErrorMsg($1, num_args).append(" Ignoring line."));
	else
		$$ = new ShapeElement($1, to_double($2), to_double($3), to_double($4), to_double($5), to_double($6), to_double($7));
  #endif
};

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

color_string: TOK_COLORDEF { $$.set($1); }
      | string;

colordef: alpha_string TOK_EQUAL color_string
{
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
        csh.AddCSH(@1, COLOR_COLORNAME);
    csh.AddCSH(@2, COLOR_EQUAL);
    if (!$3.had_error)
        csh.AddCSH(@3, COLOR_COLORDEF);
    if (!$1.had_error && !$3.had_error) {
        ColorType color = csh.Contexts.back().Colors.GetColor($3);
        if (color.type!=ColorType::INVALID)
            csh.Contexts.back().Colors[$1.str()] = color;
    }
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddColorValuesToHints(true);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE)) {
        csh.AddColorValuesToHints(false);
        csh.hintStatus = HINT_READY;
    }
  #else
    if (!chart.SkipContent() && !$1.had_error && !$3.had_error)
        chart.MyCurrentContext().colors.AddColor($1, $3, chart.Error, @$);
  #endif
    $1.destroy();
    $3.destroy();
}
      |alpha_string TOK_EQUAL TOK_PLUS_PLUS color_string
{
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
        csh.AddCSH(@1, COLOR_COLORNAME);
    csh.AddCSH(@2, COLOR_EQUAL);
    if (!$4.had_error) {
        csh.AddCSH(@3, COLOR_COLORDEF);
        csh.AddCSH(@4, COLOR_COLORDEF);
    }
    if (!$1.had_error && !$4.had_error) {
            ColorType color = csh.Contexts.back().Colors.GetColor("++"+$4.str());
        if (color.type!=ColorType::INVALID)
            csh.Contexts.back().Colors[$1.str()] = color;
    }
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddColorValuesToHints(true);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@2, @4, EHintSourceType::ATTR_VALUE)) {
        csh.AddColorValuesToHints(false);
        csh.hintStatus = HINT_READY;
    }
  #else
    if (!chart.SkipContent() && !$1.had_error && !$4.had_error)
        chart.MyCurrentContext().colors.AddColor($1, "++"+$4.str(), chart.Error, @$);
  #endif
    $1.destroy();
    $4.destroy();
}
      | alpha_string TOK_EQUAL
{
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
        csh.AddCSH(@1, COLOR_COLORNAME);
    csh.AddCSH(@2, COLOR_EQUAL);
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddColorValuesToHints(true);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@2, EHintSourceType::ATTR_VALUE)) {
        csh.AddColorValuesToHints(false);
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH_ErrorAfter(@$, "Missing color definition.");
  #else
    chart.Error.Error(@$.after(), "Missing color definition.");
  #endif
    $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();
};



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;
}
      |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;
};


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;
};

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

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

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

designopt:         entity_string TOK_EQUAL TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    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)) {
        XxxChart::AttributeValues($1, csh);
        csh.hintStatus = HINT_READY;
    }
  #else
    if (!$1.had_error)
        chart.AddDesignAttribute(Attribute($1, $3, @$, @3));
  #endif
    $1.destroy();
}
      | 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)) {
        XxxChart::AttributeValues($1, csh);
        csh.hintStatus = HINT_READY;
    }
  #else
    if (!$1.had_error)
        chart.AddDesignAttribute(Attribute($1, $3, @1, @3));
  #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)) {
        XxxChart::AttributeValues($1, csh);
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@2.after(), "Missing option value. Ignoring this.");
#endif
    $1.destroy();
};


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);
    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
}
      | TOK_PARAM_NAME TOK_EQUAL
{
  #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);
    $$ = 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
}
      | 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
    $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
};


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, @$);
  #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;
};

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.Contexts.back().if_condition = $2;
    const bool cond_true = $2==1;
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@3, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAfter(@3)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
    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;
}
      | 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 instr
{
  #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;
}
      | 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;
}
      | ifthen_condition instr 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;
}
      | ifthen_condition instr 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;
}
      | 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;
}
      | ifthen_condition instr else instr
{
  #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;
}
      | ifthen_condition instr error else instr
{
  #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;
}
      | ifthen_condition error else instr
{
  #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;
};


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


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

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


attr:         alpha_string TOK_EQUAL color_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 && !$3.had_error)
        csh.AddCSH_AttrValue_CheckAndAddEscapeHint(@3, $3, $1);
    csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME);
    if (!$1.had_error)
        csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE, $1);
  #else
    if ($1.had_error || (chart.SkipContent() && $1.had_param))
        $$ = nullptr;
    else
        $$ = new Attribute($1, $3, @1, @3);
  #endif
    $1.destroy();
    $3.destroy();
}
      | alpha_string TOK_EQUAL TOK_PLUS_PLUS color_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);
  #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_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);
  #else
    if ($1.had_error || (chart.SkipContent() && $1.had_param))
        $$ = nullptr;
    else
        $$ = new Attribute($1, "++", @1, @3);
  #endif
    $1.destroy();
}
      | alpha_string TOK_EQUAL TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
        csh.AddCSH_AttrName(@1, $1, COLOR_ATTRNAME);
    csh.AddCSH(@2, COLOR_EQUAL);
    csh.AddCSH(@3, COLOR_ATTRVALUE);
    csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME);
    if (!$1.had_error)
        csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE, $1);
  #else
    if ($1.had_error || (chart.SkipContent() && $1.had_param))
        $$ = nullptr;
    else
        $$ = new Attribute($1, $3, @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);
  #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);
  #else
    if ($1.had_error || (chart.SkipContent() && $1.had_param))
        $$ = nullptr;
    else
        $$ = new Attribute($1, @$);
  #endif
    $1.destroy();
}
 /* 'string' does not match "++", so we list it separately
     UNCOMMENT if you have ++ as a style
      | TOK_PLUS_PLUS
{
  //here we accept non alpha strings for "->" and similar style names
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_StyleOrAttrName(@1, "++");
    csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME);
  #else
    if (chart.SkipContent())
        $$ = nullptr;
    else
        $$ = new Attribute("++", @$);
  #endif
}*/
;



/* 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_XXX
      | 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_BYE;

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

//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
        $$.init();
        $$.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, @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.
        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;
    } else {
        //Just open a regular scope
        chart.PushContext(@1);
    }
  #endif
    (void)$1;
};

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


%%


/* END OF FILE */
