//5 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 564
%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 "msccsh.h"
    #include "commands.h"//For AttrNames and AttrValues
    #include "msc.h" //For AttrNames and AttrValues
    #include "msc_lexer.h"

    //To allow including both msc_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 msc;
}

//Goes to the parser.cpp files
%code {
    #include "msc_tokenizer.h"
    #include "msc_lexer.h"
    #include "msc_parse_tools.h"

    constexpr auto get_classifier(msc::lexing_state current_state) noexcept {
        switch (current_state) {
            default:
            case msc::lexing_state::no_change: _ASSERT(0); FALLTHROUGH;
            case msc::lexing_state::basic: return msc::basic::readandclassify<sv_reader<C_S_H>>;
            case msc::lexing_state::mscgen_compat: return msc::mscgen_compat::readandclassify<sv_reader<C_S_H>>;
            case msc::lexing_state::mscgen_compat_box: return msc::mscgen_compat_box::readandclassify<sv_reader<C_S_H>>;
        }
    }

    constexpr msc::lexing_state update_state(msc::lexing_state current_state, msc::lexing_state new_state, EMscgenCompat mode) noexcept {
        _ASSERT(current_state!=msc::lexing_state::no_change);
        if (new_state == msc::lexing_state::mscgen_compat_box) return msc::lexing_state::mscgen_compat_box;
        switch (mode) {
        case EMscgenCompat::NO_COMPAT: return msc::lexing_state::basic;
        case EMscgenCompat::FORCE_MSCGEN: return msc::lexing_state::mscgen_compat;
        default: _ASSERT(0); FALLTHROUGH;
        case EMscgenCompat::AUTODETECT: return new_state == msc::lexing_state::no_change ? current_state : new_state;
        }
    }

    #ifdef C_S_H_IS_COMPILED
        void msc_csh_error(SourceLocationRange<C_S_H>* pos, Csh &csh, msc::lexing_state&, sv_reader<C_S_H>&, const char *msg) {
            csh.AddCSH_Error(*pos, std::string(msg));
        }
        int msc_csh_lex(MSC_CSH_STYPE *lvalp, SourceLocationRange<C_S_H> *llocp, msc::MscCsh& csh,
                        msc::lexing_state &current_state, sv_reader<C_S_H>& input) {
            auto res = get_token<msc_csh_tokentype>(input, csh, get_classifier(current_state), *lvalp, *llocp);
            current_state = update_state(current_state, res.new_state, csh.mscgen_compat);
            return res.token;
        }
    #else
        void msc_compile_error(SourceLocationRange<C_S_H>* pos, Chart &chart, const procedure_parse_helper &,
                               msc::lexing_state&, sv_reader<C_S_H>& input, const char *msg) {
            auto [err, once] = msc::BeautifySyntaxError(msg, input.get_last_token());
            chart.Error.Error(*pos, err);
        }
        int msc_compile_lex(MSC_COMPILE_STYPE *lvalp, SourceLocationRange<C_S_H> *llocp, MscChart& chart,
                            msc::lexing_state &current_state, sv_reader<C_S_H>& input) {
            auto res = get_token<msc_compile_tokentype>(input, chart, get_classifier(current_state), *lvalp, *llocp);
            current_state = update_state(current_state, res.new_state, chart.mscgen_compat);
            return res.token;
        }
    #endif
}

%token TOK_STRING TOK_QSTRING TOK_MULTILINE_QSTRING TOK_NUMBER TOK_DASH TOK_EQUAL TOK_COMMA TOK_TILDE
       TOK_SEMICOLON TOK_PLUS TOK_PLUS_EQUAL TOK_PIPE_SYMBOL
       TOK_OCBRACKET TOK_CCBRACKET TOK_OSBRACKET TOK_CSBRACKET TOK_OPARENTHESIS TOK_CPARENTHESIS
       TOK_PARAM_NAME TOK_ASTERISK
       TOK_MSC TOK_COLON_STRING TOK_COLON_QUOTED_STRING TOK_STYLE_NAME TOK_COLORDEF TOK_COLORDEF_NAME_NUMBER
       TOK_REL_TO TOK_REL_FROM TOK_REL_BIDIR TOK_REL_X TOK_REL_DASH_X TOK_REL_MSCGEN
       TOK_SPECIAL_ARC TOK_EMPH TOK_EMPH_PLUS_PLUS
       TOK_OINLINE TOK_CINLINE TOK_INLINE TOK_UNMATCHED_INLINE
       TOK_COMMAND_HEADING TOK_COMMAND_NUDGE TOK_COMMAND_NEWPAGE
       TOK_COMMAND_DEFSHAPE TOK_COMMAND_DEFCOLOR TOK_COMMAND_DEFSTYLE TOK_COMMAND_DEFDESIGN
       TOK_COMMAND_DEFPROC TOK_COMMAND_REPLAY TOK_COMMAND_SET TOK_COMMAND_INCLUDE
       TOK_COMMAND_BIG TOK_COMMAND_BOX TOK_COMMAND_PIPE
       TOK_COMMAND_MARK TOK_COMMAND_MARK_SRC TOK_COMMAND_MARK_DST
       TOK_COMMAND_PARALLEL TOK_COMMAND_OVERLAP TOK_COMMAND_INLINE
       TOK_VERTICAL TOK_VERTICAL_SHAPE TOK_AT TOK_LOST TOK_AT_POS
       TOK_SHOW TOK_HIDE TOK_ACTIVATE TOK_DEACTIVATE TOK_BYE
       TOK_START TOK_BEFORE TOK_END TOK_AFTER
       TOK_COMMAND_VSPACE TOK_COMMAND_HSPACE TOK_COMMAND_SYMBOL TOK_COMMAND_NOTE
       TOK_COMMAND_COMMENT TOK_COMMAND_ENDNOTE TOK_COMMAND_FOOTNOTE
       TOK_COMMAND_TITLE TOK_COMMAND_SUBTITLE TOK_COMMAND_TEXT
       TOK_SHAPE_COMMAND TOK_JOIN TOK_IF TOK_THEN TOK_ELSE
       TOK_MSCGEN_RBOX TOK_MSCGEN_ABOX
       TOK_UNRECOGNIZED_CHAR TOK_EOF 0
       TOK_WHITESPACE TOK_NEWLINE TOK_COMMENT
       TOK_SYMBOLPOS_ABOVE TOK_SYMBOLPOS_BELOW TOK_SYMBOLPOS_LEFTOF TOK_SYMBOLPOS_RIGHTOF TOK_SYMBOLPOS_AROUND
%union
{
    str_view                                                   str;
    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(ArcBase)*>                          arcbase;
    gsl::owner<CHAR_IF_CSH(ArcList)*>                          arclist;
    gsl::owner<CHAR_IF_CSH(Arrow)*>                            arcarrow;
    gsl::owner<CHAR_IF_CSH(Vertical)*>                         arcvertarrow;
    gsl::owner<CHAR_IF_CSH(Box)*>                              arcbox;
    gsl::owner<CHAR_IF_CSH(Pipe)*>                             arcpipe;
    gsl::owner<CHAR_IF_CSH(BoxSeries)*>                        arcboxseries;
    gsl::owner<CHAR_IF_CSH(PipeSeries)*>                       arcpipeseries;
    gsl::owner<CHAR_IF_CSH(ParallelBlocks)*>                   arcparallel;
    EArcSymbol                                                 arcsymbol;
    gsl::owner<CHAR_IF_CSH(EntityAppHelper)*>                  entitylist;
    gsl::owner<CHAR_IF_CSH(Attribute)*>                        attrib;
    gsl::owner<CHAR_IF_CSH(AttributeList)*>                    attriblist;
    gsl::owner<CHAR_IF_CSH(VertXPos)*>                         vertxpos;
    gsl::owner<CHAR_IF_CSH(VertRefPos)*>                       vertrefpos;
    gsl::owner<CHAR_IF_CSH(ExtVertXPos)*>                      extvertxpos;
    gsl::owner<CHAR_IF_CSH(NamePair)*>                         namerel;
    gsl::owner<std::vector<std::string>*>                      stringlist;
    CHAR_IF_CSH(ESide)                                         eside;
    gsl::owner<CHAR_IF_CSH(ArrowSegmentData)*>                 arcsegdata;
    Vertical::EVerticalShape                                   vshape;
    gsl::owner<CHAR_IF_CSH(ArcTypePlusDir)*>                   arctypeplusdir;
    ShapeElement::Type                                         shapecommand;
    gsl::owner<CHAR_IF_CSH(Shape)*>                            shape;
    gsl::owner<CHAR_IF_CSH(ShapeElement)*>                     shapeelement;
    gsl::owner<CHAR_IF_CSH(ArrowEnding)*>                      arrowending;
    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 <condition>  condition ifthen_condition else leftofrightof
%type <compare_op> comp
%type <arcbase>    arcrel arc arc_with_parallel arc_with_parallel_semicolon opt scope_close
                   symbol_command symbol_command_no_inline symbol_command_no_attr note comment ifthen
%type <arcvertarrow> vertrel_no_xpos vertrel
%type <arcarrow>   arcrel_to arcrel_from arcrel_bidir arcrel_arrow arrow_with_specifier arrow_with_specifier_incomplete
%type <arcbox>     boxrel mscgen_boxrel
%type <arcpipe>    first_pipe
%type <arcboxseries> first_box box_list mscgen_box
%type <arcpipeseries> pipe_list_no_content pipe_list
%type <arcparallel> parallel
%type <arclist>    top_level_arclist arclist arclist_maybe_no_semicolon braced_arclist optlist mscgen_boxlist
%type <entitylist> entitylist entity first_entity
%type <arcsymbol>  TOK_REL_TO TOK_REL_FROM TOK_REL_BIDIR TOK_REL_MSCGEN
                   relation_to_cont_no_loss relation_from_cont_no_loss relation_bidir_cont_no_loss
                   TOK_EMPH TOK_EMPH_PLUS_PLUS emphrel
                   TOK_SPECIAL_ARC
%type <arcsegdata> relation_to relation_from relation_bidir
                   relation_to_cont relation_from_cont relation_bidir_cont
%type <eside>      comment_command
%type <attrib>     arcattr
%type <vertxpos>   vertxpos
%type <vertrefpos> vertrefpos
%type <extvertxpos> extvertxpos extvertxpos_no_string
%type <namerel>    entityrel markerrel_no_string hspace_location
%type <attriblist> arcattrlist full_arcattrlist full_arcattrlist_with_label
                   full_arcattrlist_with_label_or_number
%type <multi_str>  entity_string string alpha_string
                   entity_string_single_or_param
                   multi_string_continuation tok_param_name_as_multi
                   TOK_COLON_STRING TOK_COLON_QUOTED_STRING TOK_MULTILINE_QSTRING color_string
%type <str>        entity_string_single string_single alpha_string_single
                   reserved_word_string entity_command_prefixes titlecommandtoken
                   overlap_or_parallel mscgen_emphrel color_def_string symbol_type_string symbol_string
                   include vertical_keyword
                   TOK_STRING TOK_QSTRING TOK_COLORDEF TOK_COLORDEF_NAME_NUMBER
                   TOK_STYLE_NAME TOK_MSC TOK_COMMAND_BIG TOK_COMMAND_BOX TOK_COMMAND_PIPE
                   TOK_COMMAND_DEFSHAPE TOK_COMMAND_DEFCOLOR TOK_COMMAND_DEFSTYLE TOK_COMMAND_DEFDESIGN
                   TOK_COMMAND_DEFPROC TOK_COMMAND_REPLAY TOK_COMMAND_SET TOK_COMMAND_INCLUDE
                   TOK_COMMAND_NEWPAGE TOK_COMMAND_HEADING TOK_COMMAND_NUDGE
                   TOK_COMMAND_PARALLEL TOK_COMMAND_OVERLAP TOK_COMMAND_INLINE TOK_BYE
                   TOK_COMMAND_MARK TOK_COMMAND_MARK_SRC TOK_COMMAND_MARK_DST
                   TOK_NUMBER TOK_START TOK_BEFORE TOK_END TOK_AFTER
                   TOK_VERTICAL  TOK_AT TOK_LOST TOK_AT_POS
                   TOK_SHOW TOK_HIDE TOK_ACTIVATE TOK_DEACTIVATE
                   TOK_COMMAND_VSPACE TOK_COMMAND_HSPACE TOK_COMMAND_SYMBOL TOK_COMMAND_NOTE
                   TOK_COMMAND_COMMENT TOK_COMMAND_ENDNOTE TOK_COMMAND_FOOTNOTE
                   TOK_COMMAND_TITLE TOK_COMMAND_SUBTITLE TOK_COMMAND_TEXT TOK_VERTICAL_SHAPE
                   TOK_MSCGEN_RBOX TOK_MSCGEN_ABOX TOK_REL_X TOK_JOIN TOK_PARAM_NAME
                   TOK_IF TOK_THEN TOK_ELSE TOK_INLINE
                   TOK_SYMBOLPOS_ABOVE TOK_SYMBOLPOS_BELOW TOK_SYMBOLPOS_LEFTOF TOK_SYMBOLPOS_RIGHTOF TOK_SYMBOLPOS_AROUND symbol_position_string

%type <input_text_ptr> TOK_OCBRACKET TOK_CCBRACKET scope_open_proc_body scope_close_proc_body
%type <stringlist> stylenamelist
%type <vshape>     vertical_shape
%type<arctypeplusdir> empharcrel_straight
%type <shapecommand> TOK_SHAPE_COMMAND
%type <shapeelement> shapeline
%type <shape> shapedeflist
%type <arrowending> special_ending
%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 {if (!C_S_H) delete $$;} <arcbase> <arclist> <arcarrow> <arcvertarrow>
%destructor {if (!C_S_H) delete $$;} <arcbox> <arcpipe> <arcboxseries> <arcpipeseries> <arcparallel>
%destructor {if (!C_S_H) delete $$;} <entitylist> <attrib> <attriblist> <shape> <shapeelement>
%destructor {if (!C_S_H) delete $$;} <vertxpos> <extvertxpos> <namerel> <arrowending>
%destructor {if (!C_S_H) delete $$;} <procparamdeflist> <procparamdef> <procparaminvoc> <procparaminvoclist> <procdefhelper> <procedure>
%destructor {$$.destroy();} <multi_str>
%destructor {delete $$;} <stringlist>
%destructor {} <cprocedure> <input_text_ptr> <condition> <compare_op> <str>
%destructor {
  #ifdef C_S_H_IS_COMPILED
    csh.PopContext();
  #else
    chart.PopContext();
  #endif
} ifthen_condition else

%%

msc_with_bye: msc eof
{
	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;
}

msc:
{
  #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
}
      | msckey braced_arclist
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckLineStartHintBefore(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @1))
        csh.AddDesignsToHints(true);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @1)) {
        csh.AddOptionsToHints();
        csh.AllowAnything();
    } else if (csh.CheckHintLocated(EHintSourceType::LINE_START, @1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.AddArcs($2);
  #endif
}
      | TOK_MSC error eof
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckLineStartHintBefore(@1) || csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_Error(@2, "Missing an equal sign or a list of elements between braces ('{' and '}').");
  #else
    chart.Error.Error(@2, "Missing an equal sign or a list of elements between braces ('{' and '}').");
  #endif
    (void)$1;
	YYACCEPT; //We should not parse further in msc_with_bye as we may have something beyond BYE (in eof)
}
      | top_level_arclist eof
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckLineStartHintBefore(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
#else
    chart.AddArcs($1);
  #endif
	YYACCEPT; //We should not parse further in msc_with_bye as we may have something beyond BYE (in eof)
}
      | top_level_arclist error eof
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckLineStartHintBefore(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
    CshPos pos = @2;
    if (@1.end_pos >= @2.first_pos)
        pos.first_pos = @1.end_pos;
    csh.AddCSH_Error(pos, "Could not recognize this as a valid line.");
  #else
    chart.AddArcs($1);
    chart.Error.Error(@2, "Could not recognize this as a valid line.");
  #endif
	YYACCEPT; //We should not parse further in msc_with_bye as we may have something beyond BYE (in eof)
};

top_level_arclist: arclist_maybe_no_semicolon
      | arclist_maybe_no_semicolon 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;
}
      | arclist_maybe_no_semicolon TOK_CCBRACKET top_level_arclist
{
  #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;
};


msckey:       TOK_MSC
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.SwitchToMscgenCompatMode();
  #else
    chart.SwitchToMscgenCompatMode();
  #endif
    (void)$1;
}
      | TOK_MSC TOK_EQUAL
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_EQUAL);
    csh.AddCSH_ErrorAfter(@2, "Missing a design name.");
    csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME);
    csh.CheckHintAfter(@2, EHintSourceType::ATTR_VALUE, "msc");
  #else
    chart.Error.Error(@2.after(), "Missing design name.");
  #endif
    (void)$1;
}
      | TOK_MSC TOK_EQUAL string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_EQUAL);
    csh.AddCSH(@3, COLOR_DESIGNNAME);
    csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME);
    csh.CheckHintBetween(@2, @3, EHintSourceType::ATTR_VALUE, "msc");
    csh.CheckHintAt(@3, EHintSourceType::ATTR_VALUE, "msc");
    if (!csh.SkipContent()) {
        std::string msg = csh.SetDesignTo($3, true);
        if (msg.length())
            csh.AddCSH_Error(@3, std::move(msg));
    }
  #else
    if (!$3.had_error && !chart.SkipContent()) {
        ArcBase *dummy = chart.AddAttribute(Attribute("msc", $3, @1, @3));
        if (dummy) delete dummy;
    }
  #endif
    (void)$1;
    $3.destroy();
};

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


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.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.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_ErrorAfter(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_arcattrlist
{
  #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_arcattrlist procedure_body
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @1))
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRNAME) + "export",
            "Set if styles and colors defined in the procedure remain valid after calling it.",
            EHintType::ATTR_NAME));
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @1))
        csh.AddYesNoToHints();
  #else
    $$ = new ProcDefParseHelper<AttributeList>;
    $$->body = $2;
    $$->linenum_body = @2;
    $$->attrs = $1;
  #endif
}
      | procedure_body
{
  #ifdef C_S_H_IS_COMPILED
  #else
    $$ = new ProcDefParseHelper<AttributeList>;
    $$->body = $1;
    $$->linenum_body = @1;
  #endif
};


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

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

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

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

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

proc_def_param: TOK_PARAM_NAME
{
  #ifdef C_S_H_IS_COMPILED
    if ($1 && $1[0]=='$' && $1[1])
        csh.AddCSH(@1, COLOR_PARAMNAME);
    else
        csh.AddCSH_Error(@1, "Need name after the '$' sign.");
    $$ = nullptr; //no value
  #else
    if ($1 && $1[0]=='$' && $1[1]) {
        $$ = new ProcParamDef($1, @1);
    } else {
        chart.Error.Error(@1, "Need name after the '$' sign.");
        $$ = nullptr;
    }
  #endif
}
      | TOK_PARAM_NAME TOK_EQUAL
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_EQUAL);
    if ($1 && $1[0]=='$' && $1[1])
        csh.AddCSH(@1, COLOR_PARAMNAME);
    else
        csh.AddCSH_Error(@1, "Need name after the '$' sign.");
    $$ = nullptr; //no value
  #else
    if ($1 && $1[0]=='$' && $1[1]) {
        $$ = new ProcParamDef($1, @1);
    } else {
        chart.Error.Error(@1, "Need name after the '$' sign.");
        $$ = nullptr;
    }
  #endif
}
      | 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 arclist_maybe_no_semicolon 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 arclist_maybe_no_semicolon 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 arclist_maybe_no_semicolon 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 arclist_maybe_no_semicolon 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 arclist_maybe_no_semicolon TOK_BYE
{
  #ifdef C_S_H_IS_COMPILED
    csh.BracePairs.push_back(@$);
    csh.AddCSH_Error(@3, "The command 'bye' can only be used at the top level.");
    csh.PopContext();
  #else
    auto tmp = new Procedure;
    tmp->status = EDefProcResult::PROBLEM;
    tmp->file_pos = @$;
    $$ = tmp;
    chart.PopContext();
    chart.Error.Error(@3, "The command 'bye' can not be used between curly braces '{' and '}'.");
    chart.Error.Error(@1, @3, "Here is the opening '{'.");
    if ($2)
        delete $2;
  #endif
  (void)$1;
  (void)$3;
}
      | scope_open_proc_body TOK_BYE
{
  #ifdef C_S_H_IS_COMPILED
    csh.BracePairs.push_back(@$);
    csh.AddCSH_Error(@2, "The command 'bye' can only be used at the top level and not inside curly braces '{' and '}'.");
    csh.PopContext();
  #else
    auto tmp = new Procedure;
    tmp->status = EDefProcResult::PROBLEM;
    tmp->file_pos = @$;
    $$ = tmp;
    chart.PopContext();
    chart.Error.Error(@2, "The command 'bye' can not be used between curly braces '{' and '}'.");
  #endif
  (void)$1;
  (void)$2;
};

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


arclist_maybe_no_semicolon : arclist
      | arclist arc_with_parallel
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstruction(@2);
    csh.AddCSH_ErrorAfter(@2, "Missing a semicolon (';').");
  #else
    if ($2) {
		($1)->Append($2);
		($2)->SetLineEnd(@2);
	}
    $$ = $1;
    chart.Error.Error(@2.after(), "Missing a semicolon ';'.");
    chart.Error.Error(@2, @2.after(), "Here is the beginning of the command as I understood it.");
  #endif
}
      | arc_with_parallel
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstruction(@1);
    csh.AddCSH_ErrorAfter(@1, "Missing a semicolon (';').");
  #else
    if ($1) {
		($1)->SetLineEnd(@1);
        $$ = (new ArcList)->Append($1); /* New list */
    } else
        $$ = new ArcList;
    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
};


arclist:    arc_with_parallel_semicolon
{
  #ifndef C_S_H_IS_COMPILED
    if ($1)
        $$ = (new ArcList)->Append($1); /* New list */
    else
        $$ = new ArcList;
  #endif
}
      | arclist arc_with_parallel_semicolon
{
  #ifndef C_S_H_IS_COMPILED
    if ($2) ($1)->Append($2);     /* Add to existing list */
    $$ = $1;
  #endif
};

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


overlap_or_parallel: TOK_COMMAND_PARALLEL | TOK_COMMAND_OVERLAP | TOK_JOIN;

vertical_keyword: TOK_VERTICAL {
  #ifdef C_S_H_IS_COMPILED
    csh.hint_vertical_shapes = true;
  #endif
    $$ = $1;
};

arc_with_parallel: arc
      | overlap_or_parallel arc
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginAndParallelToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckLineStartHintBetween(@1, @2)) {
        csh.AddLineBeginAndParallelToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    if ($2) {
        if (CaseInsensitiveEqual($1, "parallel"))
            ($2)->SetParallel();
        else if (CaseInsensitiveEqual($1, "overlap"))
            ($2)->SetOverlap();
        else if (CaseInsensitiveEqual($1, "join"))
            ($2)->SetJoin(@1);
        else {
            _ASSERT(0);
        }
    }
    $$ = $2;
  #endif
}
      | overlap_or_parallel
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginAndParallelToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@1, EHintSourceType::LINE_START)) {
        csh.AddLineBeginAndParallelToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = nullptr;
  #endif
    (void)$1;
};

arc:           arcrel
{
  #ifdef C_S_H_IS_COMPILED
  #else
    if ($1)
        ($1)->AddAttributeList(nullptr);
    $$=($1);
  #endif
}
      | arcrel full_arcattrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        Arrow::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        Arrow::AttributeValues(csh.hintAttrName, csh);
  #else
    if ($1)
        ($1)->AddAttributeList($2);
    $$ = $1;
  #endif
}
      | TOK_COMMAND_BIG
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginAndParallelToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckEntityHintAfter(@1))
        csh.AllowAnything();
    csh.AddCSH_ErrorAfter(@1, "Missing an arrow specification.");
  #else
    chart.Error.Error(@1.after(), "Missing an arrow specification.");
  #endif
    (void)$1;
    $$ = nullptr;
}
      |TOK_COMMAND_BIG arcrel
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginAndParallelToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckEntityHintAt(@2) || csh.CheckEntityHintBetween(@1, @2))
        csh.AllowAnything();
  #else
    //Returns nullptr, if BIG is before a self-pointing arrow
    ArcBase *arc = chart.CreateBlockArrow($2);
    if (arc)
        arc->AddAttributeList(nullptr);
    delete $2;
    $$ = arc;
  #endif
    (void)$1;
}
      | TOK_COMMAND_BIG arcrel full_arcattrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginAndParallelToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @3))
        BlockArrow::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @3))
        BlockArrow::AttributeValues(csh.hintAttrName, csh);
    else if (csh.CheckEntityHintAt(@2) || csh.CheckEntityHintBetween(@1, @2))
        csh.AllowAnything();
  #else
    //Returns nullptr, if BIG is before a self-pointing arrow
    BlockArrow *arrow = chart.CreateBlockArrow($2);
    if (arrow) arrow->AddAttributeList($3);
    $$ = arrow;
    delete $2;
  #endif
    (void)$1;
}
      | vertical_keyword
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginAndParallelToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@1, EHintSourceType::KEYWORD)) {
        csh.AddVerticalTypesToHints();
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.AddLeftRightOfToHints();
        csh.addMarkersAtEnd = true;
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH_ErrorAfter(@1, "Missing a marker, 'at', 'leftof' or 'rightof'; one of 'brace', 'bracket', 'range', 'box', 'pointer' or 'block'; an arrow or box symbol, such as '->' or '--'.");
  #else
    chart.Error.Error(@1.after(), "Missing a marker, 'at', 'leftof' or 'rightof'; one of 'brace', 'bracket', 'range', 'box', 'pointer' or 'block'; an arrow or box symbol, such as '->' or '--'.");
  #endif
    (void)$1;
    $$ = nullptr;
}
      | vertical_keyword vertrel
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginAndParallelToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddVerticalTypesToHints();
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.AddLeftRightOfToHints();
        csh.addMarkersAtEnd = true;
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH(@1, COLOR_KEYWORD);
  #else
    if ($2) {
      ($2)->SetVerticalShape(Vertical::ARROW);
      ($2)->AddAttributeList(nullptr);
      $$ = ($2);
    } else $$ = nullptr;
  #endif
    (void)$1;
}
      | vertical_keyword vertical_shape vertrel
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginAndParallelToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAt(@2, EHintSourceType::KEYWORD)) {
        csh.AddVerticalTypesToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    if ($3) {
      ($3)->SetVerticalShape($2);
      ($3)->AddAttributeList(nullptr);
      $$ = ($3);
    } else $$ = nullptr;
  #endif
    (void)$1;
}
      | vertical_keyword vertical_shape
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginAndParallelToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAt(@2, EHintSourceType::KEYWORD)) {
        csh.AddVerticalTypesToHints();
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.addMarkersAtEnd = true;
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@2, EHintSourceType::MARKER)) {
        csh.hintStatus = HINT_FILLING;
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.AddLeftRightOfToHints();
    }
#else
	ArcTypePlusDir typeplusdir;
	typeplusdir.arc.type = $2==Vertical::BOX ? EArcSymbol::BOX_SOLID : EArcSymbol::ARC_SOLID;
	typeplusdir.arc.lost = EArrowLost::NOT;
	typeplusdir.dir = EDirType::RIGHT;
	Vertical *ava = new Vertical(&typeplusdir, MARKER_HERE_STR, MARKER_HERE_STR, &chart);
	VertXPos vxp;
	ava->AddXpos(&vxp);
    ava->SetVerticalShape($2);
    ava->AddAttributeList(nullptr);
	$$ = ava;
  #endif
    (void)$1;
}
      | vertical_keyword vertrel full_arcattrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginAndParallelToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddVerticalTypesToHints();
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.addMarkersAtEnd = true;
        csh.hintStatus = HINT_READY;
    } if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @3))
        Vertical::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @3))
        Vertical::AttributeValues(csh.hintAttrName, csh, Vertical::ARROW);
  #else
    if ($2) {
      ($2)->SetVerticalShape(Vertical::ARROW);
      ($2)->AddAttributeList($3);
      $$ = ($2);
    } else $$ = nullptr;
  #endif
    (void)$1;
}
      | vertical_keyword vertical_shape vertrel full_arcattrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginAndParallelToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAt(@2, EHintSourceType::KEYWORD)) {
        csh.AddVerticalTypesToHints();
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.addMarkersAtEnd = true;
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @4))
        Vertical::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @4))
        Vertical::AttributeValues(csh.hintAttrName, csh, $2);
  #else
    if ($3) {
      ($3)->SetVerticalShape($2);
      ($3)->AddAttributeList($4);
      $$ = ($3);
    } else $$ = nullptr;
  #endif
    (void)$1;
}
      | vertical_keyword vertical_shape full_arcattrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginAndParallelToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAt(@2, EHintSourceType::KEYWORD)) {
        csh.AddVerticalTypesToHints();
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_KEYWORD) + "at", nullptr, EHintType::KEYWORD, true));
        csh.addMarkersAtEnd = true;
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@2, @3, EHintSourceType::MARKER)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.AddLeftRightOfToHints();
        csh.addMarkersAtEnd = true;
        csh.hintStatus = HINT_FILLING;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @3))
        Vertical::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @3))
        Vertical::AttributeValues(csh.hintAttrName, csh, $2);
  #else
	ArcTypePlusDir typeplusdir;
    typeplusdir.arc.type = EArcSymbol::ARC_SOLID;
	typeplusdir.arc.lost = EArrowLost::NOT;
	typeplusdir.dir = EDirType::RIGHT;
	Vertical *ava = new Vertical(&typeplusdir, MARKER_HERE_STR, MARKER_HERE_STR, &chart);
	VertXPos vxp;
	ava->AddXpos(&vxp);
    ava->SetVerticalShape($2);
    ava->AddAttributeList($3);
	$$ = ava;
  #endif
    (void)$1;
}
      | full_arcattrlist
{
    //Here we have no label and may continue as a parallel block
    //->offer parallel attributes, as well...
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @1)) {
        Divider::AttributeNames(csh, false, false);
        ParallelBlocks::AttributeNames(csh, true);
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @1)) {
        Divider::AttributeValues(csh.hintAttrName, csh, false, false);
        ParallelBlocks::AttributeValues(csh.hintAttrName, csh, true);
    }
  #else
    //... but due to the lack of curly brace we are a divider
    $$ = new Divider(EArcSymbol::DIV_VSPACE, &chart);
    ($$)->AddAttributeList($1);
  #endif
}
      | TOK_COLON_STRING
{
    //Here we have a colon label added: this is a divider
  #ifdef C_S_H_IS_COMPILED
  #else
    AttributeList *al = new AttributeList;
    al->Append(std::make_unique<Attribute>("label", $1, @$, @$.IncStartCol()));
    $$ = new Divider(EArcSymbol::DIV_VSPACE, &chart);
    ($$)->AddAttributeList(al);
  #endif
    $1.destroy();
}
      | TOK_COLON_STRING full_arcattrlist
{
    //Here we have a colon label added: this is a divider
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        Divider::AttributeNames(csh, false, false);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        Divider::AttributeValues(csh.hintAttrName, csh, false, false);
  #else
    ($2)->Prepend(std::make_unique<Attribute>("label", $1, @1, @1.IncStartCol()));
    $$ = new Divider(EArcSymbol::DIV_VSPACE, &chart);
    ($$)->AddAttributeList($2);
#endif
    $1.destroy();
}
      | full_arcattrlist TOK_COLON_STRING full_arcattrlist
{
    //Here we have a colon label added: this is a divider
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @1) ||
        csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @3))
        Divider::AttributeNames(csh, false, false);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @1) ||
             csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @3))
        Divider::AttributeValues(csh.hintAttrName, csh, false, false);
  #else
    $1->Append(std::make_unique<Attribute>("label", $2, @2, @2.IncStartCol()));
    //Merge $3 at the end of $1 (after the colon label, so ordering is kept)
    $1->splice($1->end(), *$3);
    delete $3;
    $$ = new Divider(EArcSymbol::DIV_VSPACE, &chart);
    $$->AddAttributeList($1);
  #endif
    $2.destroy();
}
      | full_arcattrlist TOK_COLON_STRING
{
    //Here we have a colon label added: this is a divider
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @1))
        Divider::AttributeNames(csh, false, false);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @1))
        Divider::AttributeValues(csh.hintAttrName, csh, false, false);
  #else
    $1->Append(std::make_unique<Attribute>("label", $2, @2, @2.IncStartCol()));
    $$ = new Divider(EArcSymbol::DIV_VSPACE, &chart);
    $$->AddAttributeList($1);
  #endif
    $2.destroy();
}
      | first_entity
{
  #ifdef C_S_H_IS_COMPILED
  #else
    if ($1) {
        $$ = (new EntityCommand($1, &chart, false));
        $$->AddAttributeList(nullptr);
    } else
        $$ = nullptr;
  #endif
}
      | entity_command_prefixes
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckEntityHintAfter(@1))
        csh.AllowAnything();
  #else
    //Show/Hide/Activate/Deactivate all entities so far.
    //Call constructor specifically created for this case.
    $$ = new EntityCommand(&chart, $1, @$);
	($$)->AddAttributeList(nullptr);
  #endif
}
      | entity_command_prefixes first_entity
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckEntityHintBetween(@1, @2) || csh.CheckEntityHintAt(@1))
        csh.AllowAnything();
  #else
    EntityCommand *ce = new EntityCommand($2, &chart, false);
    ce->AddAttributeList(nullptr);
    $$ = ce->ApplyPrefix($1);
  #endif
}
      | first_entity TOK_COMMA
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    if (csh.CheckEntityHintAfter(@2))
        csh.AllowAnything();
    csh.AddCSH_ErrorAfter(@2, "Missing an entity.");
  #else
    EntityCommand *ce = new EntityCommand($1, &chart, false);
    ce->AddAttributeList(nullptr);
    chart.Error.Error(@2.after(), "Missing an entity.");
    $$ = ce;
  #endif
}
      | first_entity TOK_COMMA entitylist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    if (csh.CheckEntityHintBetween(@2, @3))
        csh.AllowAnything();
  #else
    ($3)->Prepend($1);
    EntityCommand *ce = new EntityCommand($3, &chart, false);
    delete ($1);
    ce->AddAttributeList(nullptr);
    $$ = ce;
  #endif
}
      | entity_command_prefixes first_entity TOK_COMMA
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@3, COLOR_COMMA);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckEntityHintBetween(@1, @2) || csh.CheckEntityHintAt(@1))
        csh.AllowAnything();
    else if (csh.CheckEntityHintAfter(@3))
        csh.AllowAnything();
    csh.AddCSH_ErrorAfter(@3, "Missing an entity.");
  #else
    EntityCommand *ce = new EntityCommand($2, &chart, false);
    ce->AddAttributeList(nullptr);
    $$ = ce->ApplyPrefix($1);
    chart.Error.Error(@3.after(), "Missing an entity.");
  #endif
}
      | entity_command_prefixes first_entity TOK_COMMA entitylist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@3, COLOR_COMMA);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckEntityHintBetween(@3, @4))
        csh.AllowAnything();
#else
    ($4)->Prepend($2);
    EntityCommand *ce = new EntityCommand($4, &chart, false);
    delete ($2);
    ce->AddAttributeList(nullptr);
    $$ = ce->ApplyPrefix($1);
  #endif
}
      | optlist
{
  #ifdef C_S_H_IS_COMPILED
  #else
    /* If there were arcs defined by the options (e.g., background)
     * enclose them in an "ParallelBlocks" element used only for this.
     * This will be an internally defined ParallelBlocks that will
     * get unrolled in MscChart::PostParseArcList()*/
    $$ = ($1) ? new ParallelBlocks(&chart, $1, nullptr, true, true) : nullptr;
  #endif
}
      | mscgen_boxlist
{
  #ifdef C_S_H_IS_COMPILED
  #else
    /* This may be a list of BoxSeries (each with one emptybox), we
     * enclose them in an "ParallelBlocks" element used only for this.
     * This will be an internally defined ParallelBlocks that will
     * get unrolled in MscChart::PostParseArcList()*/
    $$ = ($1) ? new ParallelBlocks(&chart, $1, nullptr, false, true) : nullptr;
  #endif
}
      | box_list
{
  #ifdef C_S_H_IS_COMPILED
  #else
    $$ = $1; //to remove warning for downcast
  #endif
}
      | pipe_list
{
  #ifdef C_S_H_IS_COMPILED
  #else
    $$ = $1; //to remove warning for downcast
  #endif
}
      | parallel
{
    $$ = $1;
}
      | TOK_COMMAND_DEFSHAPE shapedef
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
    if (csh.SkipContent()) {
        csh.AddCSH_Error(@1, "Cannot define shapes inside a procedure.");
    }
  #else
    if (chart.SkipContent()) {
        chart.Error.Error(@1, "Cannot define shapes inside a procedure.");
    }
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_COMMAND_DEFSHAPE
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
    if (csh.SkipContent()) {
        csh.AddCSH_Error(@1, "Cannot define shapes inside a procedure.");
    } else {
        csh.AddCSH(@1, COLOR_KEYWORD);
        csh.AddCSH_ErrorAfter(@$, "Missing shape name and definition.");
    }
#else
    if (chart.SkipContent()) {
        chart.Error.Error(@$, "Cannot define shapes inside a procedure.");
    } else {
        chart.Error.Error(@$.after(), "Missing shape name and definition.");
    }
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_COMMAND_DEFCOLOR colordeflist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@1, @2, EHintSourceType::ATTR_VALUE)) {
        csh.AddColorValuesToHints(true);
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_COMMAND_DEFCOLOR
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@1, EHintSourceType::ATTR_VALUE)) {
        csh.AddColorValuesToHints(true);
        csh.hintStatus = HINT_READY;
	}
	csh.AddCSH_ErrorAfter(@$, "Missing color name to (re-)define.");
  #else
    chart.Error.Error(@$.after(), "Missing a color name to (re-)define.");
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_COMMAND_DEFSTYLE styledeflist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@1, @2, EHintSourceType::ATTR_VALUE)) {
        csh.AddStylesToHints(true, true);
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_COMMAND_DEFSTYLE
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@1, EHintSourceType::ATTR_VALUE)) {
        csh.AddStylesToHints(true, true);
        csh.hintStatus = HINT_READY;
	}
	csh.AddCSH_ErrorAfter(@$, "Missing style name to (re-)define.");
  #else
    chart.Error.Error(@$.after(), "Missing a style name to (re-)define.");
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_COMMAND_DEFDESIGN designdef
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
    if (csh.SkipContent())
        csh.AddCSH_Error(@1, "Cannot define designs inside a procedure.");
  #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.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
    if (csh.SkipContent()) {
        csh.AddCSH_Error(@1, "Cannot define designs inside a procedure.");
    } else {
        csh.AddCSH(@1, COLOR_KEYWORD);
        csh.AddCSH_ErrorAfter(@$, "Missing design name to (re-)define.");
    }
  #else
    if (chart.SkipContent()) {
        chart.Error.Error(@$, "Cannot define designs inside a procedure.");
    } else {
        chart.Error.Error(@$.after(), "Missing a design name to (re-)define.");
    }
    $$ = nullptr;
  #endif
    (void)$1;
}
      | defproc
{
  #ifdef C_S_H_IS_COMPILED
  #else
    $$ = nullptr;
  #endif
}
      | set
{
  #ifdef C_S_H_IS_COMPILED
  #else
    $$ = nullptr;
  #endif
}
      | TOK_COMMAND_HEADING
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = new EntityCommand(&chart, $1, @$);
    ($$)->AddAttributeList(nullptr);
  #endif
    (void)$1;
}
      | TOK_COMMAND_HEADING full_arcattrlist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        EntityCommand::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        EntityCommand::AttributeValues(csh.hintAttrName, csh);
  #else
    $$ = (new EntityCommand(nullptr, &chart, false));
    ($$)->AddAttributeList($2);
  #endif
    (void)$1;
}
      | TOK_COMMAND_NUDGE
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = (new Divider(EArcSymbol::DIV_VSPACE, &chart));
    ($$)->AddAttributeList(nullptr);
  #endif
    (void)$1;
}
      | TOK_COMMAND_NUDGE full_arcattrlist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        Divider::AttributeNames(csh, true, false);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        Divider::AttributeValues(csh.hintAttrName, csh, true, false);
  #else
    $$ = (new Divider(EArcSymbol::DIV_VSPACE, &chart));
    ($$)->AddAttributeList($2);
  #endif
    (void)$1;
}
      | titlecommandtoken full_arcattrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        Divider::AttributeNames(csh, false, true);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        Divider::AttributeValues(csh.hintAttrName, csh, false, true);
  #else
    const EArcSymbol t = CaseInsensitiveEqual("title", $1) ? EArcSymbol::DIV_TITLE :
                         CaseInsensitiveEqual("subtitle", $1) ? EArcSymbol::DIV_SUBTITLE :
                         EArcSymbol::INVALID;
    $$ = (new Divider(t, &chart));
    ($$)->AddAttributeList($2);
  #endif
}
      | titlecommandtoken
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH_ErrorAfter(@$, "Missing label.");
  #else
    chart.Error.Error(@$.after(), "Missing label. Ignoring (sub)title.", "Titles and subtitles must have a label.");
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_COMMAND_TEXT
{
  #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::KEYWORD)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH_ErrorAfter(@1, "Missing 'at' keyword.");
  #else
    $$ = nullptr;
    chart.Error.Error(@1.after(), "Missing 'at' clause.");
#endif
    (void)$1;
}
      | TOK_COMMAND_TEXT full_arcattrlist_with_label
{
  #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::KEYWORD)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        Symbol::AttributeNames(csh, 0);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        Symbol::AttributeValues(csh.hintAttrName, csh, 0);
    csh.AddCSH_ErrorAfter(@1, "Missing 'at' keyword.");
  #else
    $$ = nullptr;
    chart.Error.Error(@1.after(), "Missing 'at' clause. Ignoring this.");
    if ($2)
        delete $2;
  #endif
    (void)$1;
}
      | TOK_COMMAND_TEXT vertxpos full_arcattrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @3))
        Symbol::AttributeNames(csh, 0);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @3))
        Symbol::AttributeValues(csh.hintAttrName, csh, 0);
#else
    if ($2) {
        Symbol* s = new Symbol(&chart, "text", FileLineColRange{}, @$, $2, @2);
        s->AddAttributeList($3);
        $$ = s;
        delete $2;
    } else {
        $$ = nullptr;
    }
  #endif
    (void)$1;
}
      | TOK_COMMAND_TEXT vertxpos
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH_ErrorAfter(@2, "Missing label.");
  #else
    if ($2)
        delete $2;
    chart.Error.Error(@2.after(), "Missing actual text - specify a label. Ignoring this.");
    $$ = nullptr;
#endif
    (void)$1;
}
      | TOK_COMMAND_MARK
{
  #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::KEYWORD)) {
        csh.AddToHints(CshHint(csh.HintPrefixNonSelectable() + "<marker name>",
            "Specify the name of the marker.",
            EHintType::KEYWORD, false));
        csh.hintStatus=HINT_READY;
    }
    csh.AddCSH_ErrorAfter(@1, "Missing marker name.");
  #else
    $$ = nullptr;
    chart.Error.Error(@1.after(), "Missing marker name. Ignoring this.", "You need to supply a name which then can be used to refer to this vertical position you are marking here.");
  #endif
  (void)$1;
}
      | TOK_COMMAND_MARK entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH(@2, COLOR_MARKERNAME);
    if (!csh.SkipContent() && !$2.had_error)
        csh.MarkerNames.insert($2.str());
  #else
    if ($2.had_error) {
        $$ = nullptr;
    } else {
        $$ = new Marker($2, @$, &chart);
        ($$)->AddAttributeList(nullptr);
    }
  #endif
    (void)$1;
    $2.destroy();
}
      | TOK_COMMAND_MARK entity_string full_arcattrlist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_MARKERNAME);
    if (!csh.SkipContent() && !$2.had_error)
        csh.MarkerNames.insert($2.str());
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @3))
        Marker::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @3))
        Marker::AttributeValues(csh.hintAttrName, csh);
  #else
    if ($2.had_error) {
        $$ = nullptr;
        delete $3;
    } else {
        $$ = new Marker($2, @$, &chart);
        ($$)->AddAttributeList($3);
    }
  #endif
    (void)$1;
    $2.destroy();
}
      | TOK_COMMAND_MARK full_arcattrlist
{
  #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::KEYWORD)) {
        csh.AddToHints(CshHint(csh.HintPrefixNonSelectable() + "<marker name>",
            "Specify the name of the marker.",
            EHintType::KEYWORD, false));
        csh.hintStatus=HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        Marker::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        Marker::AttributeValues(csh.hintAttrName, csh);
    csh.AddCSH_ErrorAfter(@1, "Missing marker name.");
  #else
    $$ = nullptr;
    chart.Error.Error(@1.after(), "Missing marker name. Ignoring this.", "You need to supply a name which then can be used to refer to this vertical position you are marking here.");
    if ($2)
        delete $2;
  #endif
    (void)$1;
}
      | TOK_COMMAND_MARK reserved_word_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH_Error(@2, "This is a reserved word and cannot be used as a marker name.");
  #else
    $$ = nullptr;
    chart.Error.Error(@2, "This is a reserved word and cannot be used as a marker name. Ignoring this.");
  #endif
    (void)$1;
    (void)$2;
}
      | TOK_COMMAND_MARK reserved_word_string full_arcattrlist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @3))
        Marker::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @3))
        Marker::AttributeValues(csh.hintAttrName, csh);
    csh.AddCSH_Error(@2, "This is a reserved word and cannot be used as a marker name.");
  #else
    $$ = nullptr;
    chart.Error.Error(@2, "This is a reserved word and cannot be used as a marker name. Ignoring this.");
    if ($3)
        delete $3;
  #endif
    (void)$1;
    (void)$2;
}
      | TOK_COMMAND_NEWPAGE
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
#else
    $$ = new Newpage(&chart, true);
    ($$)->AddAttributeList(nullptr);
  #endif
    (void)$1;
}
      | TOK_COMMAND_NEWPAGE full_arcattrlist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        Newpage::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        Newpage::AttributeValues(csh.hintAttrName, csh);
  #else
    $$ = new Newpage(&chart, true);
    ($$)->AddAttributeList($2);
  #endif
    (void)$1;
}
      | symbol_command
      | note
      | comment
      | TOK_COMMAND_HSPACE hspace_location full_arcattrlist_with_label_or_number
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @3))
        AddHSpace::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @3))
        AddHSpace::AttributeValues(csh.hintAttrName, csh);
  #else
    $$ = new AddHSpace(&chart, $2);
    ($$)->AddAttributeList($3);
  #endif
    (void)$1;
}
      | TOK_COMMAND_HSPACE hspace_location
{
  #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(@2, EHintSourceType::KEYWORD)) {
        csh.AddToHints(CshHint(csh.HintPrefixNonSelectable() + "<number>",
            "Enter a number in pixels to set horizontal spacing.",
            EHintType::KEYWORD, false));
        csh.AddToHints(CshHint(csh.HintPrefixNonSelectable() + "<label>",
            "Enter some text the width of which will be used as horizontal spacing.",
            EHintType::KEYWORD, false));
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH_ErrorAfter(@$, "Missing either a number or a label.");
  #else
    $$ = new AddHSpace(&chart, $2); //Will trigger an error: either label or space attr is needed
  #endif
    (void)$1;
}
      | TOK_COMMAND_HSPACE
{
  #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::ENTITY)) {
        csh.AddEntitiesToHints();
        csh.AddLeftRightHSpaceToHints();
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH_ErrorAfter(@1, "Missing an entity, 'left comment' or 'right comment'.");
  #else
    $$ = nullptr;
    chart.Error.Error(@1.after(), "Missing an entity, 'left comment' or 'right comment'. Ignoring this command.");
  #endif
    (void)$1;
}
      | TOK_COMMAND_HSPACE full_arcattrlist_with_label_or_number
{
  #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::ENTITY)) {
        csh.AddEntitiesToHints();
        csh.AddLeftRightHSpaceToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        AddHSpace::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        AddHSpace::AttributeValues(csh.hintAttrName, csh);
    csh.AddCSH_ErrorAfter(@1, "Missing an entity, 'left comment' or 'right comment'.");
#else
    $$ = nullptr;
    chart.Error.Error(@1.after(), "Missing an entity, 'left comment' or 'right comment'. Ignoring this command.");
    delete $2;
  #endif
    (void)$1;
}
      | TOK_COMMAND_VSPACE full_arcattrlist_with_label_or_number
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        AddVSpace::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        AddVSpace::AttributeValues(csh.hintAttrName, csh);
  #else
    $$ = new AddVSpace(&chart);
    ($$)->AddAttributeList($2);
  #endif
    (void)$1;
}
      | TOK_COMMAND_VSPACE
{
  #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::KEYWORD)) {
        csh.AddToHints(CshHint(csh.HintPrefixNonSelectable() + "<number>",
            "Enter a number in pixels to add that much empty vertical spacing.",
            EHintType::KEYWORD, false));
        csh.AddToHints(CshHint(csh.HintPrefixNonSelectable() + "<label>",
            "Enter some text the height of which will be added as vertical empty space.",
            EHintType::KEYWORD, false));
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH_ErrorAfter(@1, "Missing either a number or a label.");
  #else
    $$ = new AddVSpace(&chart); //will result in an error, since label or space attribute is needed
  #endif
    (void)$1;
}
      | ifthen
{
  #ifdef C_S_H_IS_COMPILED
    csh.IfThenElses.push_back(@$);
  #endif
    $$ = $1;
};


titlecommandtoken: TOK_COMMAND_TITLE | TOK_COMMAND_SUBTITLE;

hspace_location: entityrel
      | TOK_AT_POS TOK_COMMAND_COMMENT
{
  #ifdef C_S_H_IS_COMPILED
    if (CaseInsensitiveEqual($1, "left") || CaseInsensitiveEqual($1, "right"))
        csh.AddCSH(@1, COLOR_KEYWORD);
    else
        csh.AddCSH_Error(@1, "Use either `left comment` or `right comment` to specify which comment column to size.");
    csh.AddCSH(@2, COLOR_KEYWORD);
    if (csh.CheckHintAt(@1, EHintSourceType::ENTITY)) {
        csh.AddEntitiesToHints();
        csh.AddLeftRightHSpaceToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    if (CaseInsensitiveEqual($1, "left"))
        $$ = new NamePair(LNOTE_ENT_STR, @1, {}, @1);
    else if (CaseInsensitiveEqual($1, "right"))
        $$ = new NamePair(RNOTE_ENT_STR, @1, {}, @1);
    else {
        chart.Error.Error(@1, "Use either `left` or `right` to specify which comment column to size. Ignoring command.");
        $$ = nullptr;
    }
  #endif
    (void)$1;
    (void)$2;
}
      | TOK_AT_POS
{
  #ifdef C_S_H_IS_COMPILED
    if (CaseInsensitiveEqual($1, "left") || CaseInsensitiveEqual($1, "right")) {
        csh.AddCSH(@1, COLOR_KEYWORD);
        if (csh.CheckHintAfter(@1, EHintSourceType::KEYWORD)) {
            csh.AddToHints(CshHint(csh.HintPrefix(COLOR_KEYWORD) + "comment", nullptr, EHintType::KEYWORD, true));
            csh.hintStatus = HINT_READY;
        }
    }  else
        csh.AddCSH_Error(@1, "Use either `left comment` or `right comment` to specify which comment column to size.");
    if (csh.CheckHintAt(@1, EHintSourceType::ENTITY)) {
        csh.AddEntitiesToHints();
        csh.AddLeftRightHSpaceToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@1, "Use <entity>-<entity>, `left comment`, `right comment` to specify horizontal spacing. Ignoring command.");
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_COMMAND_COMMENT
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintAt(@1, EHintSourceType::ENTITY)) {
        csh.AddEntitiesToHints();
        csh.AddLeftRightHSpaceToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@1, "Use `left comment` or `right comment` to specify horizontal spacing for comments. Ignoring command.");
    $$ = nullptr;
  #endif
    (void)$1;
};


comp: TOK_EMPH
{
    $$ = $1==EArcSymbol::BOX_DOUBLE ? ECompareOperator::EQUAL : ECompareOperator::INVALID;
}
      | TOK_REL_MSCGEN
{
    $$ = $1==EArcSymbol::ARC_DOUBLE_BIDIR ? ECompareOperator::EQUAL : ECompareOperator::INVALID;
}
      | TOK_REL_TO
{
    $$ = $1==EArcSymbol::ARC_DOUBLE ? ECompareOperator::GREATER_OR_EQUAL : $1==EArcSymbol::ARC_DOTTED ? ECompareOperator::GREATER : ECompareOperator::INVALID;
}
      | TOK_REL_FROM
{
    $$ = $1==EArcSymbol::ARC_DOUBLE ? ECompareOperator::SMALLER_OR_EQUAL : $1==EArcSymbol::ARC_DOTTED ? ECompareOperator::SMALLER : ECompareOperator::INVALID;
}
      | TOK_REL_BIDIR
{
    $$ = $1==EArcSymbol::ARC_DOTTED_BIDIR ? ECompareOperator::NOT_EQUAL : ECompareOperator::INVALID;
};

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; //to suppress
}
      | 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; //to supress warnings
}
      | TOK_IF
{
  #ifdef C_S_H_IS_COMPILED
    $$ = csh.Contexts.back().if_condition = 2;
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@1, "Missing condition.");
    csh.PushContext(true, EContextParse::SKIP_CONTENT);
  #else
    $$ = chart.MyCurrentContext().if_condition = 2;
    chart.PushContext(@1, EContextParse::SKIP_CONTENT);
    chart.Error.Error(@1.after(), "Missing condition after 'if'.");
  #endif
    (void)$1;
}
      | TOK_IF error
{
  #ifdef C_S_H_IS_COMPILED
    $$ = csh.Contexts.back().if_condition = 2;
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_Error(@2, "Missing condition instead of this.");
    csh.PushContext(true, EContextParse::SKIP_CONTENT);
  #else
    $$ = chart.MyCurrentContext().if_condition = 2;
    chart.PushContext(@1, EContextParse::SKIP_CONTENT);
    chart.Error.Error(@2, "Missing condition after 'if'.");
  #endif
    (void)$1;
}
      | TOK_IF error TOK_THEN
{
  #ifdef C_S_H_IS_COMPILED
    $$ = csh.Contexts.back().if_condition = 2;
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_Error(@2, "Missing condition instead of this.");
    csh.PushContext(true, EContextParse::SKIP_CONTENT);
    if (csh.CheckLineStartHintAfter(@3)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = chart.MyCurrentContext().if_condition = 2;
    chart.PushContext(@1, EContextParse::SKIP_CONTENT);
    chart.Error.Error(@2, "Missing condition after 'if'.");
  #endif
    (void)$1;
    (void)$3;
};


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

ifthen: ifthen_condition arc_with_parallel
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstructionIfNotBrace(@2);
    csh.PopContext();
    if (csh.CheckHintAfter(@2, EHintSourceType::KEYWORD)) {
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_KEYWORD) + "else",
            "Continue the 'if/then' statement with 'else'.",
            EHintType::KEYWORD, true));
        csh.hintStatus = HINT_READY;
    }
  #else
    if ($1==1) {
        $$ = $2;
    } else {
        $$ = nullptr;
        delete $2;
    }
    chart.PopContext();
  #endif
}
      | ifthen_condition
{
  #ifdef C_S_H_IS_COMPILED
    if ($1!=2)
        csh.AddCSH_ErrorAfter(@1, "Missing command after 'then'.");
    csh.PopContext();
  #else
    if ($1!=2)
        chart.Error.Error(@1.after(), "Missing a well-formed command after 'then'. Ignoring 'if' clause.");
    chart.PopContext();
    $$ = nullptr;
  #endif
    (void)$1; //suppress
}
      | ifthen_condition error
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_Error(@2, "Missing command after 'then'.");
    csh.PopContext();
  #else
    chart.Error.Error(@2, "Missing a well-formed command. Ignoring 'if' clause.");
    chart.PopContext();
    $$ = nullptr;
  #endif
    (void)$1; //suppress
}
      | ifthen_condition arc_with_parallel else
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstructionIfNotBrace(@2);
    csh.AddCSH_ErrorAfter(@3, "Missing command after 'else'.");
    csh.PopContext();
  #else
    delete $2;
    $$ = nullptr;
    chart.Error.Error(@3.after(), "Missing command after 'else'. Ignoring 'if' clause.");
    chart.PopContext();
  #endif
    (void)$1; (void)$3; //suppress
}
      | ifthen_condition arc_with_parallel error else
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstructionIfNotBrace(@2);
    csh.AddCSH_Error(@3, "I am not sure what is coming here.");
    csh.AddCSH_ErrorAfter(@4, "Missing command after 'else'.");
    csh.PopContext();
  #else
    delete $2;
    $$ = nullptr;
    chart.Error.Error(@3, "I am not sure what is coming here.");
    chart.Error.Error(@4.after(), "Missing command after 'else'. Ignoring 'if' clause.");
    chart.PopContext();
  #endif
    (void)$1; (void)$4; //suppress
}
      | ifthen_condition error else
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_Error(@2, "I am not sure what is coming here.");
    csh.AddCSH_ErrorAfter(@3, "Missing command after 'else'.");
    csh.PopContext();
  #else
    $$ = nullptr;
    chart.Error.Error(@2, "I am not sure what is coming here.");
    chart.Error.Error(@3.after(), "Missing command after 'else'. Ignoring 'if' clause.");
    chart.PopContext();
  #endif
    (void)$1; (void)$3; //suppress
}
      | ifthen_condition arc_with_parallel else arc_with_parallel
{
  #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;
    }
    chart.PopContext();
  #endif
    (void)$3; //suppress
}
      | ifthen_condition arc_with_parallel error else arc_with_parallel
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstructionIfNotBrace(@2);
    csh.AddInstructionIfNotBrace(@5);
    csh.AddCSH_Error(@3, "I am not sure what is coming here.");
    csh.PopContext();
  #else
    $$ = nullptr;
    delete $2;
    delete $5;
    chart.Error.Error(@3, "I am not sure what is coming here. Ignoring 'if' command.");
    chart.PopContext();
  #endif
    (void)$1; (void)$4; //suppress
}
      | ifthen_condition error else arc_with_parallel
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInstructionIfNotBrace(@4);
    csh.AddCSH_Error(@2, "I am not sure what is coming here.");
    csh.PopContext();
  #else
    $$ = nullptr;
    delete $4;
    chart.Error.Error(@2, "I am not sure what is coming here. Ignoring 'if' command.");
    chart.PopContext();
  #endif
    (void)$1; (void)$3; //suppress
};



full_arcattrlist_with_label_or_number: full_arcattrlist_with_label
      | TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_ATTRVALUE);
  #else
    AttributeList *al = new AttributeList;
    al->Append(std::make_unique<Attribute>("space", $1, @1, @1));
    $$ = al;
  #endif
}
      | TOK_NUMBER full_arcattrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_ATTRVALUE);
  #else
    ($2)->Append(std::make_unique<Attribute>("space", $1, @1, @1));
    $$ = $2;
  #endif
};

dash_or_dashdash: TOK_DASH | TOK_EMPH;

entityrel: entity_string dash_or_dashdash
{
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
        csh.AddCSH_EntityName(@1, $1);
    csh.CheckEntityHintAt(@1);
    csh.AddCSH(@2, COLOR_SYMBOL);
    csh.CheckEntityHintAfter(@2);
  #else
    if ($1.had_error)
        $$ = nullptr;
    else
        $$ = new NamePair($1, @1, {}, FileLineColRange());
  #endif
    $1.destroy();
}
      | dash_or_dashdash entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_SYMBOL);
    if (!$2.had_error)
        csh.AddCSH_EntityName(@2, $2);
    csh.CheckEntityHintAt(@2);
  #else
    if ($2.had_error)
        $$ = nullptr;
    else
        $$ = new NamePair({}, FileLineColRange(), $2, @2);
  #endif
    $2.destroy();
}
      | dash_or_dashdash
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_SYMBOL);
    csh.CheckEntityHintAfter(@1);
  #else
    $$ = nullptr;
  #endif
}
      | entity_string dash_or_dashdash entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
        csh.AddCSH_EntityName(@1, $1);
    csh.CheckEntityHintAt(@1);
    csh.AddCSH(@2, COLOR_SYMBOL);
    if (!$3.had_error)
        csh.AddCSH_EntityName(@3, $3);
    csh.CheckEntityHintAt(@3);
  #else
    if ($1.had_error || $3.had_error)
        $$ = nullptr;
    else
        $$ = new NamePair($1, @1, $3, @3);
  #endif
    $1.destroy();
    $3.destroy();
}
      | entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_EntityName(@1, $1);
    csh.CheckEntityHintAt(@1);
  #else
    $$ = new NamePair($1, @1, {}, FileLineColRange());
  #endif
    $1.destroy();
};


markerrel_no_string: entity_string dash_or_dashdash
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_MARKERNAME);
    csh.AddCSH(@2, COLOR_SYMBOL);
    csh.CheckHintAt(@1, EHintSourceType::MARKER);
    csh.CheckHintAfter(@2, EHintSourceType::MARKER);
  #else
    if ($1.had_error)
        $$ = nullptr;
    else
        $$ = new NamePair($1, @1, {}, FileLineColRange());
  #endif
    $1.destroy();
}
      | dash_or_dashdash entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_SYMBOL);
    csh.AddCSH(@2, COLOR_MARKERNAME);
    csh.CheckHintAt(@2, EHintSourceType::MARKER);
  #else
    if ($2.had_error)
        $$ = nullptr;
    else
        $$ = new NamePair({}, FileLineColRange(), $2, @2);
  #endif
    $2.destroy();
}
      | dash_or_dashdash
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_SYMBOL);
    csh.CheckHintAt(@1, EHintSourceType::MARKER);
    csh.CheckHintAfter(@1, EHintSourceType::MARKER);
  #else
    $$ = nullptr;
  #endif
}
      | entity_string dash_or_dashdash entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_MARKERNAME);
    csh.CheckHintAt(@1, EHintSourceType::MARKER);
    csh.AddCSH(@2, COLOR_SYMBOL);
    csh.AddCSH(@3, COLOR_MARKERNAME);
    csh.CheckHintAt(@3, EHintSourceType::MARKER);
  #else
    if ($1.had_error || $3.had_error)
        $$ = nullptr;
    else
        $$ = new NamePair($1, @1, $3, @3);
  #endif
    $1.destroy();
    $3.destroy();
};

entity_command_prefixes: TOK_HIDE | TOK_SHOW | TOK_ACTIVATE | TOK_DEACTIVATE;

optlist:     opt
{
  #ifndef C_S_H_IS_COMPILED
    if ($1) {
        $$ = (new ArcList)->Append($1); /* New list */
        //($1)->MakeMeLastNotable(); Do not make chart options notable
    } else
        $$ = nullptr;
  #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
    if ($3) {
        if ($1)
            $$ = ($1)->Append($3);     /* Add to existing list */
        else
            $$ = (new ArcList)->Append($3); /* New list */
        //($3)->MakeMeLastNotable(); Do not make chart options notable
    } else
        $$ = $1;
  #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;
  #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)) {
        MscChart::AttributeValues($1, csh);
        csh.hintStatus = HINT_READY;
    }
  #else
    if ($1.had_error || ($1.had_param && chart.SkipContent()))
        $$ = nullptr;
    else
        $$ = chart.AddAttribute(Attribute($1, $3, @1, @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_AttrValue_CheckAndAddEscapeHint(@3, $3, $1);
    }
    csh.AddCSH(@2, COLOR_EQUAL);
    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)) {
        MscChart::AttributeValues($1, csh);
        csh.hintStatus = HINT_READY;
    }
  #else
    if ($1.had_error || $3.had_error ||
        (($1.had_param || $3.had_param) && chart.SkipContent()))
        $$ = nullptr;
    else
        $$ = chart.AddAttribute(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);
    csh.AddCSH_ErrorAfter(@2, "Missing option value.");
    if (!$1.had_error && csh.CheckHintAfter(@2, EHintSourceType::ATTR_VALUE, $1)) {
        MscChart::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();
}
      | TOK_MSC TOK_EQUAL string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_EQUAL);
    if (!$3.had_error)
        csh.AddCSH(@3, COLOR_DESIGNNAME);
    if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE, "msc")) {
        csh.AddDesignsToHints(true);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddOptionsToHints();
        csh.hintStatus = HINT_READY;
    }
    if (!$3.had_error && !csh.SkipContent()) {
        std::string msg = csh.SetDesignTo($3, true);
        if (msg.length())
            csh.AddCSH_Error(@3, std::move(msg));
    }
  #else
    if (chart.SkipContent())
        $$ = nullptr;
    else
        $$ = chart.AddAttribute(Attribute("msc", $3, @$, @3));
  #endif
    (void)$1;
    $3.destroy();
}
      | TOK_MSC TOK_EQUAL
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_EQUAL);
    csh.AddCSH_ErrorAfter(@2, "Missing option value.");
    if (csh.CheckHintAfter(@2, EHintSourceType::ATTR_VALUE, "msc")) {
        csh.AddDesignsToHints(true);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddOptionsToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@2.after(), "Missing option value.");
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_MSC TOK_PLUS_EQUAL string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_EQUAL);
    if (!$3.had_error)
        csh.AddCSH(@3, COLOR_DESIGNNAME);
    if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE, "msc+")) {
        csh.AddDesignsToHints(false);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddOptionsToHints();
        csh.hintStatus = HINT_READY;
    }
    if (!$3.had_error) {
        std::string msg = csh.SetDesignTo($3, false);
        if (msg.length())
            csh.AddCSH_Error(@3, std::move(msg));
    }
  #else
    if ($3.had_error)
        $$ = nullptr;
    else
        $$ = chart.AddAttribute(Attribute("msc+", $3, @$, @3));
  #endif
    (void)$1;
    $3.destroy();
}
      | TOK_MSC TOK_PLUS_EQUAL
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_EQUAL);
    csh.AddCSH_ErrorAfter(@2, "Missing option value.");
    if (csh.CheckHintAfter(@2, EHintSourceType::ATTR_VALUE, "msc+")) {
        csh.AddDesignsToHints(false);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddOptionsToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@2.after(), "Missing option value.");
    $$ = nullptr;
  #endif
    (void)$1;
};

entitylist:   entity
{
  #ifndef C_S_H_IS_COMPILED
    $$ = $1;
  #endif
}
      | entitylist TOK_COMMA entity
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    if (csh.CheckEntityHintBetween(@2, @3))
        csh.AllowAnything();
#else
    ($3)->Prepend($1);
    $$ = $3;
    delete ($1);
  #endif
}
      | entitylist TOK_COMMA
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    if (csh.CheckEntityHintAfter(@2))
        csh.AllowAnything();
  #else
    $$ = $1;
    chart.Error.Error(@2.after(), "Expecting an entity here.");
  #endif
};


entity:       entity_string full_arcattrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAt(@1))
        csh.AllowAnything();
    if (!$1.had_error)
        csh.AddCSH_EntityName(@1, $1);
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        EntityApp::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        EntityApp::AttributeValues(csh.hintAttrName, csh);
  #else
    if ($1.had_error) {
        $$ = nullptr;
        delete $2;
    } else {
        EntityApp *ed = new EntityApp($1, &chart);
        ed->SetLineEnd(@$);
        $$ = ed->AddAttributeList($2, nullptr, FileLineCol()).release();
    }
  #endif
    $1.destroy();
}
      | entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAt(@1))
        csh.AllowAnything();
    if (!$1.had_error)
        csh.AddCSH_EntityName(@1, $1);
  #else
    if ($1.had_error)
        $$ = nullptr;
    else {
        EntityApp *ed = new EntityApp($1, &chart);
        ed->SetLineEnd(@$);
        $$ = ed->AddAttributeList(nullptr, nullptr, FileLineCol()).release();
    }
  #endif
    $1.destroy();
}
      | entity_string full_arcattrlist_with_label braced_arclist
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAt(@1))
        csh.AllowAnything();
    if (!$1.had_error)
        csh.AddCSH_EntityName(@1, $1);
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        EntityApp::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        EntityApp::AttributeValues(csh.hintAttrName, csh);
  #else
    if ($1.had_error) {
        $$ = nullptr;
        delete $2;
        delete $3;
    } else {
        EntityApp *ed = new EntityApp($1, &chart);
        ed->SetLineEnd(@1 + @2);
        $$ = ed->AddAttributeList($2, $3, @3).release();
    }
  #endif
    $1.destroy();
}
      | entity_string braced_arclist
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAt(@1))
        csh.AllowAnything();
    if (!$1.had_error)
        csh.AddCSH_EntityName(@1, $1);
  #else
    if ($1.had_error) {
        $$ = nullptr;
        delete $2;
    } else {
       EntityApp *ed = new EntityApp($1, &chart);
       ed->SetLineEnd(@1);
       $$ = ed->AddAttributeList(nullptr, $2, @2).release();
    }
  #endif
    $1.destroy();
};

first_entity:  entity_string full_arcattrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        EntityApp::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        EntityApp::AttributeValues(csh.hintAttrName, csh);
    if (!$1.had_error)
        csh.AddCSH_KeywordOrEntity(@1, $1);  //Do it after AddLineBeginToHints so if this is a newly defined entity it one is not included among the hints
  #else
    if ($1.had_error) {
        $$ = nullptr;
        delete $2;
    } else {
        EntityApp *ed = new EntityApp($1, &chart);
        ed->SetLineEnd(@$);
        $$ = ed->AddAttributeList($2, nullptr, FileLineCol()).release();
    }
  #endif
    $1.destroy();
}
      | entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
    if (!$1.had_error)
        csh.AddCSH_KeywordOrEntity(@1, $1);   //Do it after AddLineBeginToHints so if this is a newly defined entity it one is not included among the hints
  #else
    if ($1.had_error)
        $$ = nullptr;
    else {
        EntityApp *ed = new EntityApp($1, &chart);
        ed->SetLineEnd(@$);
        $$ = ed->AddAttributeList(nullptr, nullptr, FileLineCol()).release();
    }
  #endif
    $1.destroy();
}
      | entity_string full_arcattrlist_with_label braced_arclist
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        EntityApp::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        EntityApp::AttributeValues(csh.hintAttrName, csh);
    if (!$1.had_error)
        csh.AddCSH_KeywordOrEntity(@1, $1);  //Do it after AddLineBeginToHints so this one is not included
  #else
    if ($1.had_error) {
        $$ = nullptr;
        delete $2;
        delete $3;
    } else {
        EntityApp *ed = new EntityApp($1, &chart);
        ed->SetLineEnd(@1 + @2);
        $$ = ed->AddAttributeList($2, $3, @3).release();
    }
  #endif
    $1.destroy();
}
      | entity_string braced_arclist
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
    if (!$1.had_error)
        csh.AddCSH_KeywordOrEntity(@1, $1);   //Do it after AddLineBeginToHints so this one is not included
  #else
    if ($1.had_error) {
        $$ = nullptr;
        delete $2;
    } else {
        EntityApp *ed = new EntityApp($1, &chart);
        ed->SetLineEnd(@1);
        $$ = ed->AddAttributeList(nullptr, $2, @2).release();
    }
  #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_arcattrlist
{
  #ifdef C_S_H_IS_COMPILED
    for (auto &str : *($1))
        if (csh.ForbiddenStyles.find(str) == csh.ForbiddenStyles.end())
            csh.CurrentContext().StyleNames.insert(str);
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        MscStyle().AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        MscStyle().AttributeValues(csh.hintAttrName, csh);
  #else
    if (!chart.SkipContent())
	    chart.AddAttributeListToStyleList($2, $1); //deletes $2, as well
    else
        delete $2;
  #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);
};

/* 'string' does not match "++", so we list it separately */
stylenamelist: string
{
  #ifdef C_S_H_IS_COMPILED
    $$ = new std::vector<string>;
    if (!$1.had_error) {
        csh.AddCSH(@1, COLOR_STYLENAME);
        if ($1.view()=="emphasis")
            ($$)->push_back("box");
        else if ($1.view()=="emptyemphasis")
            ($$)->push_back("emptybox");
        else ($$)->push_back($1.str());
    }
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
	    csh.AddStylesToHints(true, true);
        csh.AllowAnything();
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = new std::vector<string>;
    if (!$1.had_error)
        $$->emplace_back(ConvertEmphasisToBox($1, @1, chart));
  #endif
    $1.destroy();
}
      | TOK_EMPH_PLUS_PLUS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_STYLENAME);
    $$ = new std::vector<string>;
	$$->push_back("++");
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
		csh.AddStylesToHints(true, true);
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = new std::vector<string>;
    $$->emplace_back(ConvertEmphasisToBox("++", @1, chart));
  #endif
}
      | 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.AllowAnything();
        csh.hintStatus = HINT_READY;
    }
	$$ = $1;
  #else
    $$ = $1;
    chart.Error.Error(@$.after(), "Missing a style name to (re-)define.");
  #endif
};
      | stylenamelist TOK_COMMA string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    $$ = $1;
    if (!$3.had_error) {
        csh.AddCSH(@3, COLOR_STYLENAME);
        if ($3.view()=="emphasis")
            ($$)->push_back("box");
        else if ($3.view()=="emptyemphasis")
            ($$)->push_back("emptybox");
        else ($$)->push_back($3.str());
    }
    if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_NAME)) {
        csh.AddStylesToHints(true, true);
        csh.AllowAnything();
        csh.hintStatus = HINT_READY;
    }
  #else
    if (!$3.had_error)
        $1->emplace_back(ConvertEmphasisToBox($3, @3, chart));
    $$ = $1;
  #endif
    $3.destroy();
}
      | stylenamelist TOK_COMMA TOK_EMPH_PLUS_PLUS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    csh.AddCSH(@3, COLOR_STYLENAME);
    $$ = $1;
	($$)->push_back("++");
    if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_NAME)) {
	    csh.AddStylesToHints(true, true);
        csh.AllowAnything();
        csh.hintStatus = HINT_READY;
    }
  #else
    $1->emplace_back(ConvertEmphasisToBox("++", @3, chart));
    $$ = $1;
  #endif
};

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 '"+std::string($1) +"'.").c_str());
  #else
    if (!$1.had_error)
       chart.Error.Error(@$.after(), "Here should come a shape definition beginning with '{'. Ignoring this malformed shape definition for '"+std::string($1) +"'.");
  #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 '"+std::string($1) +"'.").c_str());
  #else
    if (!$1.had_error)
        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 '"+std::string($1) +"'.");
  #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 (!csh.SkipContent())
    	csh.AddShapeName($1);
  #else
    chart.Error.Error(@3.after(), "Missing '}'.");
    chart.Error.Error(@2, @3.after(), "Here is the corresponding '{'.");
    if ($3) {
        if (!chart.SkipContent() && !$1.had_error)
	        chart.Shapes.Add(std::string($1), @1, chart.file_url, chart.file_info, std::move(*$3), chart.Error);
	    delete $3;
    }
  #endif
    $1.destroy();
    (void)$2;
}
		| entity_string TOK_OCBRACKET shapedeflist TOK_CCBRACKET
{
  #ifdef C_S_H_IS_COMPILED
    csh.BracePairs.push_back(@2+@4);
    if (!$1.had_error)
        csh.AddCSH(@1, COLOR_ATTRVALUE);
    csh.AddCSH(@2, COLOR_BRACE);
    csh.AddCSH(@4, COLOR_BRACE);
    if (!csh.SkipContent() && !$1.had_error)
    	csh.AddShapeName($1);
  #else
    if ($3) {
        if (!chart.SkipContent() && !$1.had_error)
            chart.Shapes.Add($1, @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);
    if (!csh.SkipContent() && !$1.had_error)
    	csh.AddShapeName($1);
    csh.AddCSH_Error(@4, "Only numbers can come after shape commands.");
  #else
    if ($3) {
        if (!chart.SkipContent() && !$1.had_error)
            chart.Shapes.Add($1, @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
}
      | shapeline
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_ErrorAfter(@1, "Missing semicolon.");
  #else
    $$ = new Shape;
	if ($1) {
		($$)->Add(std::move(*($1)));
		delete $1;
	}
    chart.Error.Error(@$.after(), "Missing semicolon (';').");
  #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;
	}
    chart.Error.Error(@1, "syntax error.");
  #endif
}
      | error shapeline
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_Error(@1, "I do not understand this.");
    csh.AddCSH_ErrorAfter(@2, "Missing semicolon.");
#else
    $$ = new Shape;
	if ($2) {
		($$)->Add(std::move(*($2)));
		delete $2;
	}
    chart.Error.Error(@1, "syntax error.");
    chart.Error.Error(@$.after(), "Missing semicolon (';').");
  #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 shapeline
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_ErrorAfter(@2, "Missing semicolon.");
  #else
	if ($2) {
		($1)->Add(std::move(*($2)));
		delete $2;
	}
    $$ = $1;
    chart.Error.Error(@$.after(), "Missing semicolon (';').");
  #endif
}
      | shapedeflist error
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_Error(@2, "Only numbers can come after shape commands.");
  #else
    $$ = $1;
    chart.Error.Error(@2, "syntax error.");
  #endif
}
      | shapedeflist TOK_SEMICOLON
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SEMICOLON);
  #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.AllowAnything();
        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.AllowAnything();
        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_def_string: TOK_COLORDEF | TOK_COLORDEF_NAME_NUMBER;

color_string: TOK_COLORDEF { $$.set($1); }
      | TOK_COLORDEF_NAME_NUMBER { $$.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.CurrentContext().Colors.GetColor($3);
        if (color.type!=ColorType::INVALID)
            csh.CurrentContext().Colors[$1.str()] = color;
    }
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddColorValuesToHints(true);
        csh.AllowAnything();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE)) {
        csh.AddColorValuesToHints(false);
        csh.AllowAnything();
        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_EMPH_PLUS_PLUS color_string
{
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
        csh.AddCSH(@1, COLOR_COLORNAME);
    csh.AddCSH(@2, COLOR_EQUAL);
    csh.AddCSH(@3, COLOR_COLORDEF);
    if (!$4.had_error) {
        csh.AddCSH(@4, COLOR_COLORDEF);
        csh.AddCSH(@3, COLOR_COLORDEF);
    }
    if (!$1.had_error && !$4.had_error) {
        ColorType color = csh.CurrentContext().Colors.GetColor("++"+$4.str());
        if (color.type!=ColorType::INVALID)
            csh.CurrentContext().Colors[$1.str()] = color;
    }
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddColorValuesToHints(true);
        csh.AllowAnything();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@2, @4, EHintSourceType::ATTR_VALUE)) {
        csh.AddColorValuesToHints(false);
        csh.AllowAnything();
        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.AllowAnything();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@2, EHintSourceType::ATTR_VALUE)) {
        csh.AddColorValuesToHints(false);
        csh.AllowAnything();
        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.AllowAnything();
        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.SkipContent()) {
        auto &d = csh.CurrentContext().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.SkipContent()) {
        auto &d = csh.CurrentContext().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(false, 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.AllowAnything();
        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.AllowAnything();
        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.AllowAnything();
        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.AllowAnything();
        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)) {
        MscChart::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)) {
        MscChart::AttributeValues($1, csh);
        csh.hintStatus = HINT_READY;
    }
  #else
    if (!$1.had_error && !$3.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)) {
        MscChart::AttributeValues($1, csh);
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@2.after(), "Missing option value. Ignoring this.");
#endif
    $1.destroy();
}
      | TOK_MSC TOK_EQUAL string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_EQUAL);
    if (!$3.had_error)
        csh.AddCSH(@3, COLOR_DESIGNNAME);
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddDesignOptionsToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE, $1)) {
        MscChart::AttributeValues("msc", csh);
        csh.hintStatus = HINT_READY;
    }
    if (!$3.had_error) {
        std::string msg = csh.SetDesignTo($3, true);
        if (msg.length())
           csh.AddCSH_Error(@3, std::move(msg));
    }
  #else
    if (!$3.had_error)
        chart.AddDesignAttribute(Attribute("msc", $3, @$, @3));
  #endif
    (void)$1;
    $3.destroy();
}
      | TOK_MSC TOK_PLUS_EQUAL string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_EQUAL);
    if (!$3.had_error)
        csh.AddCSH(@3, COLOR_DESIGNNAME);
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddDesignOptionsToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE, $1)) {
        MscChart::AttributeValues("msc+", csh);
        csh.hintStatus = HINT_READY;
    }
    if (!$3.had_error) {
        std::string msg = csh.SetDesignTo($3, false);
        if (msg.length())
            csh.AddCSH_Error(@3, std::move(msg));
    }
  #else
    if (!$3.had_error)
        chart.AddDesignAttribute(Attribute("msc+", $3, @$, @3));
  #endif
    (void)$1;
    $3.destroy();
}
      | TOK_MSC TOK_EQUAL
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_AttrName(@1, $1, COLOR_OPTIONNAME);
    csh.AddCSH(@2, COLOR_EQUAL);
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddDesignOptionsToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@2, EHintSourceType::ATTR_VALUE, "msc")) {
        MscChart::AttributeValues($1, csh);
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@2.after(), "Missing design name. Ignoring this.");
#endif
    (void)$1;
}
      | TOK_MSC TOK_PLUS_EQUAL
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_AttrName(@1, $1, COLOR_OPTIONNAME);
    csh.AddCSH(@2, COLOR_EQUAL);
    if (csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME)) {
        csh.AddDesignOptionsToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@2, EHintSourceType::ATTR_VALUE, "msc+")) {
        MscChart::AttributeValues($1, csh);
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@2.after(), "Missing design name. Ignoring this.");
#endif
    (void)$1;
};


parallel:    braced_arclist
{
  #ifndef C_S_H_IS_COMPILED
    if ($1)
        $$ = new ParallelBlocks(&chart, $1, nullptr, false, false);
    else
        $$ = nullptr;
  #endif
}
      | full_arcattrlist braced_arclist
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @1))
        ParallelBlocks::AttributeNames(csh, true);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @1))
        ParallelBlocks::AttributeValues(csh.hintAttrName, csh, true);
  #else
    if ($2) {
        $$ = new ParallelBlocks(&chart, $2, $1, false, false);
    } else {
        $$ = nullptr;
        if ($1) delete $1;
    }
  #endif
}
      | parallel braced_arclist
{
  #ifndef C_S_H_IS_COMPILED
    if ($2==nullptr)
        $$ = $1;
    else if ($1)
        $$ = ($1)->AddArcList($2, nullptr);
    else
        $$ = new ParallelBlocks(&chart, $2, nullptr, false, false);
  #endif
}
      | parallel full_arcattrlist braced_arclist
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        ParallelBlocks::AttributeNames(csh, false);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        ParallelBlocks::AttributeValues(csh.hintAttrName, csh, false);
  #else
    if ($3==nullptr) {
        $$ = $1;
        if ($2) delete $2;
    } else if ($1)
        $$ = ($1)->AddArcList($3, $2);
    else
        $$ = new ParallelBlocks(&chart, $3, $2, false, false);
  #endif
}
      | parallel full_arcattrlist
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        ParallelBlocks::AttributeNames(csh, false);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        ParallelBlocks::AttributeValues(csh.hintAttrName, csh, false);
    csh.AddCSH_ErrorAfter(@2,
        "Need an additional parallel block enclosed between '{' and '}'.");
  #else
    $$ = $1;
    if ($2) delete $2;
    chart.Error.Error(@2.after(),
        "Missing an additional parallel block enclosed between '{' and '}' after the attributes.");
  #endif
};

optional_box_keyword: | TOK_COMMAND_BOX
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
  #else
  #endif
    (void)$1;
}

//This term never returns nullptr. (For a TOK_COMMAND_BOX without a following boxrel
//we assume EArcSymbol::BOX_SOLID.
box_list: first_box
{
  #ifdef C_S_H_IS_COMPILED
    csh.CheckEntityHintAfter(@1);
  #else
    $$ = $1;
  #endif
}
      | TOK_COMMAND_BOX
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckEntityHintAfter(@1))
        csh.AllowAnything();
  #else
    //Allocate a solid box with no entities and no attribute list (the second param to BoxSeries())
    $$ = new BoxSeries(
	          new Box(EArcSymbol::BOX_SOLID, @1, {}, @1, {}, @1, &chart),
			  nullptr);
    ($$)->ExpandFirstLineEnd(@$);
  #endif
    (void)$1;
}
      | TOK_COMMAND_BOX braced_arclist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.CheckEntityHintAfter(@2);
  #else
    FileLineColRange box_file_pos = @2;
    box_file_pos.start.col--; box_file_pos.end.col--;
    Box *temp = new Box(EArcSymbol::BOX_SOLID, box_file_pos, {}, @1, {}, @1, &chart);
    temp->AddArcList($2);
	temp->SetLineEnd(box_file_pos);
    $$ = new BoxSeries(temp, nullptr);
  #endif
    (void)$1;
}
      | TOK_COMMAND_BOX full_arcattrlist_with_label braced_arclist
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        Box::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        Box::AttributeValues(csh.hintAttrName, csh);
    else
        csh.CheckEntityHintAfter(@3);
  #else
    FileLineColRange box_file_pos = @2;
    box_file_pos.start.col--; box_file_pos.end.col--;
    Box *temp = new Box(EArcSymbol::BOX_SOLID, box_file_pos, {}, @1, {}, @1, &chart);
    temp->AddArcList($3)->SetLineEnd(@2 + @2);
    $$ = new BoxSeries(temp, $2);
  #endif
    (void)$1;
}
      | TOK_COMMAND_BOX full_arcattrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        Box::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        Box::AttributeValues(csh.hintAttrName, csh);
    else
        csh.CheckEntityHintAfter(@2);
  #else
    FileLineColRange box_file_pos = @2;
    box_file_pos.start.col--; box_file_pos.end.col--;
    Box *temp = new Box(EArcSymbol::BOX_SOLID, box_file_pos, {}, @1, {}, @1, &chart);
    temp->SetLineEnd(@2 + @2);
    $$ = new BoxSeries(temp, $2);
  #endif
    (void)$1;
}
      | TOK_COMMAND_BOX first_box
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else
        csh.CheckEntityHintAfter(@2);
  #else
    ($2)->ExpandFirstLineEnd(@$);
    $$ = $2;
  #endif
  (void)$1;
}
/* ALWAYS Add Arclist before Attributes. AddArcList changes default attributes!! */
      | box_list optional_box_keyword boxrel
{
  #ifndef C_S_H_IS_COMPILED
    ($3)->SetLineEnd(@2 + @3);
    $$ = ($1)->AddBox($3, nullptr);
  #endif
}
      | box_list optional_box_keyword boxrel full_arcattrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @4))
        Box::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @4))
        Box::AttributeValues(csh.hintAttrName, csh);
    else
        csh.CheckEntityHintAfter(@4);
  #else
    if ($3)
        ($3)->SetLineEnd(@2 + @4);
    $$ = ($1)->AddBox($3, $4);
  #endif
}
      | box_list optional_box_keyword boxrel braced_arclist
{
  #ifdef C_S_H_IS_COMPILED
    csh.CheckEntityHintAfter(@4);
  #else
    if ($3) {
        ($3)->AddArcList($4)->SetLineEnd(@2 + @3);
        $$ = ($1)->AddBox($3, nullptr);
    } else {
        delete $4;
        $$ = $1;
    }
  #endif
}
      | box_list optional_box_keyword boxrel full_arcattrlist_with_label braced_arclist
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @4))
        Box::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @4))
        Box::AttributeValues(csh.hintAttrName, csh);
    else
        csh.CheckEntityHintAfter(@5);
  #else
    if ($3) {
        ($3)->AddArcList($5)->SetLineEnd(@2 + @4);
        $$ = ($1)->AddBox($3, $4);
    } else {
        delete $4;
        delete $5;
        $$ = $1;
    }
  #endif
}
      | box_list braced_arclist
{
  #ifdef C_S_H_IS_COMPILED
    csh.CheckEntityHintAfter(@2);
  #else
    FileLineColRange box_file_pos = @2;
    box_file_pos.start.col--; box_file_pos.end.col--;
    Box *temp = new Box(EArcSymbol::BOX_UNDETERMINED_FOLLOW, box_file_pos, {}, @1, {}, @1, &chart);
    temp->AddArcList($2);
	temp->SetLineEnd(box_file_pos);
    $$ = ($1)->AddBox(temp, nullptr);
  #endif
}
      | box_list full_arcattrlist_with_label braced_arclist
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        Box::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        Box::AttributeValues(csh.hintAttrName, csh);
    else
        csh.CheckEntityHintAfter(@3);
  #else
    FileLineColRange box_file_pos = @2;
    box_file_pos.start.col--; box_file_pos.end.col--;
    Box *temp = new Box(EArcSymbol::BOX_UNDETERMINED_FOLLOW, box_file_pos, {}, @1, {}, @1, &chart);
    temp->AddArcList($3)->SetLineEnd(@2 + @2);
    $$ = ($1)->AddBox(temp, $2);
  #endif
};

mscgen_box: mscgen_boxrel
{
  #ifndef C_S_H_IS_COMPILED
    if ($1) {
        ($1)->SetLineEnd(@$);
        $$ = new BoxSeries($1, nullptr);
    } else
        $$ = nullptr;
  #endif
}
      | mscgen_boxrel full_arcattrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        Box::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        Box::AttributeValues(csh.hintAttrName, csh);
  #else
    if ($1) {
        ($1)->SetLineEnd(@$);
        $$ = new BoxSeries($1, $2);
    } else
        $$ = nullptr;
  #endif
};

mscgen_boxlist: mscgen_box
{
  #ifndef C_S_H_IS_COMPILED
    if ($1) {
        $$ = new ArcList;
        ($$)->Append($1);
    } else
        $$ = nullptr;
  #endif
}
      | mscgen_boxlist TOK_COMMA mscgen_box
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
  #else
    if ($3) {
        if ($1) {
            ($1)->back()->SetParallel();
            $$ = $1;
        } else
            $$ = new ArcList;
        ($$)->Append($3);
    } else
        $$ = $1;
  #endif
};


/* ALWAYS Add Arclist before Attributes. AddArcList changes default attributes!! */
first_box:   boxrel
{
  #ifndef C_S_H_IS_COMPILED
    if ($1) {
        ($1)->SetLineEnd(@$);
        $$ = new BoxSeries($1, nullptr);
    } else
        $$ = nullptr;
  #endif
}
      | boxrel full_arcattrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        Box::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        Box::AttributeValues(csh.hintAttrName, csh);
  #else
    if ($1) {
        ($1)->SetLineEnd(@$);
        $$ = new BoxSeries($1, $2);
    } else {
        delete $2;
        $$ = nullptr;
    }
  #endif
}
      | boxrel braced_arclist
{
  #ifndef C_S_H_IS_COMPILED
    if ($1) {
        ($1)->SetLineEnd(@1);
        ($1)->AddArcList($2);
        $$ = new BoxSeries($1, nullptr);
    } else {
        delete $2;
        $$ = nullptr;
    }
  #endif
}
      | boxrel full_arcattrlist_with_label braced_arclist
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        Box::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        Box::AttributeValues(csh.hintAttrName, csh);
  #else
    if ($1) {
        ($1)->SetLineEnd(@1 + @2);
        ($1)->AddArcList($3);
        $$ = new BoxSeries($1, $2);
    } else {
        delete $2;
        delete $3;
        $$ = nullptr;
    }
  #endif
};


first_pipe: TOK_COMMAND_PIPE boxrel
{
  #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::ENTITY)) {
        csh.AddEntitiesToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    if ($2) {
        $$ = new Pipe($2);
        ($$)->SetLineEnd(@$);
        ($$)->AddAttributeList(nullptr);
    } else
        $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_COMMAND_PIPE error
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::ENTITY)) {
        csh.AddEntitiesToHints();
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH_ErrorAfter(@2, "Missing a box symbol.");
  #else
    chart.Error.Error(@2.after(), "Missing a box symbol. Ignoring pipe.");
    $$ = nullptr;
  #endif
    (void)$1;
}
      | TOK_COMMAND_PIPE
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else
        csh.CheckEntityHintAfter(@1);
  #else
    $$ = nullptr;
    chart.Error.Error(@1.after(), "The keyword '" + string($1) +"' should be followed by an entity, or '--', '..', '++' or '=='.");
  #endif
    (void)$1;
}
      | TOK_COMMAND_PIPE boxrel full_arcattrlist_with_label
{
  #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::ENTITY)) {
        csh.AddEntitiesToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @3))
        Pipe::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @3))
        Pipe::AttributeValues(csh.hintAttrName, csh);
  #else
    if ($2) {
        $$ = new Pipe($2);
        ($$)->SetLineEnd(@$);
        ($$)->AddAttributeList($3);
    } else {
        delete $3;
        $$ = nullptr;
    }
  #endif
    (void)$1;
};

pipe_list_no_content: first_pipe
{
  #ifdef C_S_H_IS_COMPILED
    csh.CheckEntityHintAfter(@$);
  #else
    if ($1)
      $$ = new PipeSeries($1);
    else
      $$ = nullptr;
  #endif
}
      | pipe_list_no_content boxrel
{
  #ifdef C_S_H_IS_COMPILED
    csh.CheckEntityHintAfter(@$);
#else
    if ($2) {
        Pipe *ap = new Pipe($2);
        ap->SetLineEnd(@2);
        if ($1)
          $$ = ($1)->AddFollowWithAttributes(ap, nullptr);
        else {
          ap->AddAttributeList(nullptr);
          $$ = new PipeSeries(ap);
        }
    } else 
        $$ = $1;
  #endif
}
      | pipe_list_no_content boxrel full_arcattrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @3))
        Pipe::AttributeNames(csh);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @3))
        Pipe::AttributeValues(csh.hintAttrName, csh);
    else
        csh.CheckEntityHintAfter(@$);
  #else
    if ($2) {
        Pipe *ap = new Pipe($2);
        ap->SetLineEnd(@2 + @3);
        if ($1)
          $$ = ($1)->AddFollowWithAttributes(ap, $3);
        else {
          ap->AddAttributeList($3);
          $$ = new PipeSeries(ap);
        }
    } else {
        delete $3;
        $$ = $1;
    }
  #endif
};

pipe_list: pipe_list_no_content
      | pipe_list_no_content braced_arclist
{
  #ifdef C_S_H_IS_COMPILED
    csh.CheckEntityHintBetween(@1, @2);
  #else
    $$ = ($1)->AddArcList($2);
  #endif
};

emphrel: TOK_EMPH_PLUS_PLUS | TOK_EMPH;

boxrel:   entity_string emphrel entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.CheckEntityHintAt(@1);
    if (!$1.had_error)
        csh.AddCSH_EntityName(@1, $1);
    csh.AddCSH(@2, COLOR_SYMBOL);
    csh.CheckEntityHintBetweenAndAt(@2, @3);
    if (!$3.had_error)
         csh.AddCSH_EntityName(@3, $3);
  #else
    if ($1.had_error || $3.had_error)
        $$ = nullptr;
    else
        $$ = new Box($2, @2, $1, @1, $3, @3, &chart);
  #endif
    $1.destroy();
    $3.destroy();
}
      | emphrel entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_SYMBOL);
    csh.CheckEntityHintBetweenAndAt(@1, @2);
    if (!$2.had_error)
        csh.AddCSH_EntityName(@2, $2);
  #else
    if ($2.had_error)
        $$ = nullptr;
    else
        $$ = new Box($1, @1, {}, @1, $2, @2, &chart);
  #endif
    $2.destroy();
}
      | entity_string emphrel
{
  #ifdef C_S_H_IS_COMPILED
    csh.CheckEntityHintAt(@1);
    if (!$1.had_error)
        csh.AddCSH_EntityName(@1, $1);
    csh.AddCSH(@2, COLOR_SYMBOL);
    csh.CheckEntityHintAfter(@2);
  #else
    if ($1.had_error)
        $$ = nullptr;
    else
        $$ = new Box($2, @2, $1, @1, {}, @2, &chart);
  #endif
    $1.destroy();
}
      | emphrel
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_SYMBOL);
    csh.CheckEntityHintAfter(@1);
  #else
    $$ = new Box($1, @1, {}, @1, {}, @1, &chart);
  #endif
};

mscgen_emphrel: TOK_MSCGEN_RBOX | TOK_MSCGEN_ABOX | TOK_COMMAND_BOX | TOK_COMMAND_NOTE;

mscgen_boxrel: entity_string mscgen_emphrel entity_string
{
    //mscgen compatibility: a box with no content or
#ifdef C_S_H_IS_COMPILED
    csh.CheckEntityHintAt(@1);
    if (!$1.had_error)
        csh.AddCSH_EntityName(@1, $1);
    if (csh.mscgen_compat == EMscgenCompat::FORCE_MSCGEN)
        csh.AddCSH(@2, COLOR_KEYWORD);
    else
        csh.AddCSH(@2, COLOR_KEYWORD_MSCGEN);
    csh.CheckEntityHintBetweenAndAt(@2, @3);
    if (!$3.had_error)
        csh.AddCSH_EntityName(@3, $3);
#else
    if ($1.had_error || $3.had_error) {
        $$ = nullptr;
    } else {
        Box::Emscgen_compat c = Box::MSCGEN_COMPAT_NONE;
        if (CaseInsensitiveEqual($2, "rbox")) c = Box::MSCGEN_COMPAT_RBOX;
        else if (CaseInsensitiveEqual($2, "abox")) c = Box::MSCGEN_COMPAT_ABOX;
        else if (CaseInsensitiveEqual($2, "box")) c = Box::MSCGEN_COMPAT_BOX;
        else if (CaseInsensitiveEqual($2, "note")) c = Box::MSCGEN_COMPAT_NOTE;
        $$ = new Box(c, @2, $1, @1, $3, @3, &chart);
    }
    if (chart.mscgen_compat != EMscgenCompat::FORCE_MSCGEN)
        chart.Error.WarnMscgen(@2, "Deprecated box symbol.", "Use '--' and attribute 'line.corner' instead.");
#endif
    $1.destroy();
    $3.destroy();
};

vertxpos: TOK_AT entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckEntityHintBetweenAndAt(@1, @2)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(false);
        csh.hintStatus = HINT_READY;
    }
    if (!$2.had_error)
        csh.AddCSH_EntityName(@2, $2);
  #else
    if ($2.had_error)
        $$ = nullptr;
    else
        $$ = new VertXPos(chart, $2, @2);
  #endif
    (void)$1;
    $2.destroy();
}
      | TOK_AT entity_string TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckEntityHintBetweenAndAt(@1, @2)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(false);
        csh.hintStatus = HINT_READY;
    }
    if (!$2.had_error)
        csh.AddCSH_EntityName(@2, $2);
    csh.AddCSH(@3, COLOR_ATTRVALUE);
  #else
    if ($2.had_error)
        $$ = nullptr;
    else
        $$ = new VertXPos(chart, $2, @2, VertXPos::POS_AT, to_double($3));
  #endif
    (void)$1;
    $2.destroy();
}
      | TOK_AT entity_string TOK_DASH
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (!$2.had_error)
        csh.AddCSH_EntityName(@2, $2);
    csh.AddCSH(@3, COLOR_SYMBOL);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckEntityHintBetweenAndAt(@1, @2)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(false);
        csh.hintStatus = HINT_READY;
    } else
        csh.CheckEntityHintAfter(@3);
  #else
    if ($2.had_error)
        $$ = nullptr;
    else
        $$ = new VertXPos(chart, $2, @2, VertXPos::POS_LEFT_SIDE);
  #endif
    (void)$1;
    $2.destroy();
}
      | TOK_AT entity_string TOK_DASH TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (!$2.had_error)
        csh.AddCSH_EntityName(@2, $2);
    csh.AddCSH(@3, COLOR_SYMBOL);
    csh.AddCSH(@4, COLOR_ATTRVALUE);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckEntityHintBetweenAndAt(@1, @2)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(false);
        csh.hintStatus = HINT_READY;
    }
  #else
    if ($2.had_error)
        $$ = nullptr;
    else
        $$ = new VertXPos(chart, $2, @2, VertXPos::POS_LEFT_SIDE, to_double($4));
  #endif
    (void)$1;
    $2.destroy();
}
      | TOK_AT entity_string TOK_PLUS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (!$2.had_error)
        csh.AddCSH_EntityName(@2, $2);
    csh.AddCSH(@3, COLOR_SYMBOL);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckEntityHintBetweenAndAt(@1, @2)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(false);
        csh.hintStatus = HINT_READY;
    }
  #else
    if ($2.had_error)
        $$ = nullptr;
    else
        $$ = new VertXPos(chart, $2, @2, VertXPos::POS_RIGHT_SIDE);
  #endif
    (void)$1;
    $2.destroy();
}
      | TOK_AT entity_string TOK_PLUS TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (!$2.had_error)
        csh.AddCSH_EntityName(@2, $2);
    csh.AddCSH(@3, COLOR_SYMBOL);
    csh.AddCSH(@4, COLOR_ATTRVALUE);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckEntityHintBetweenAndAt(@1, @2)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(false);
        csh.hintStatus = HINT_READY;
    }
#else
    if ($2.had_error)
        $$ = nullptr;
    else
        $$ = new VertXPos(chart, $2, @2, VertXPos::POS_RIGHT_SIDE, to_double($4));
  #endif
    (void)$1;
    $2.destroy();
}
      | TOK_AT entity_string emphrel
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (!$2.had_error)
        csh.AddCSH_EntityName(@2, $2);
    csh.AddCSH(@3, COLOR_SYMBOL);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckEntityHintBetweenAndAt(@1, @2)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(false);
        csh.hintStatus = HINT_READY;
    }
  #else
    if ($2.had_error)
        $$ = nullptr;
    else switch ($3) {
    default:
        _ASSERT(0);
        FALLTHROUGH;
    case EArcSymbol::BOX_SOLID:
        $$ = new VertXPos(chart, $2, @2, VertXPos::POS_LEFT_BY);
        break;
    case EArcSymbol::BOX_DASHED:
        $$ = new VertXPos(chart, $2, @2, VertXPos::POS_RIGHT_BY);
        break;
    case EArcSymbol::BOX_DOTTED:
        chart.Error.Error(@3,
                        "unexpected '..', expected '-', '--', '+' or '++'."
                        " Ignoring vertical.");
        $$ = nullptr;
        break;
    case EArcSymbol::BOX_DOUBLE:
        chart.Error.Error(@3,
                        "unexpected '==', expected '-', '--', '+' or '++'."
                        " Ignoring vertical.");
        $$ = nullptr;
        break;
    }
  #endif
    (void)$1;
    $2.destroy();
}
      | TOK_AT entity_string TOK_DASH entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (!$2.had_error)
        csh.AddCSH_EntityName(@2, $2);
    csh.AddCSH(@3, COLOR_SYMBOL);
    if (!$4.had_error)
        csh.AddCSH_EntityName(@4, $4);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckEntityHintBetweenAndAt(@1, @2)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(false);
        csh.hintStatus = HINT_READY;
    } else
        csh.CheckEntityHintBetweenAndAt(@3, @4);
  #else
    if ($2.had_error || $4.had_error)
        $$ = nullptr;
    else
        $$ = new VertXPos(chart, $2, @2, $4, @4);
  #endif
    (void)$1;
    $2.destroy();
    $4.destroy();
}
      | TOK_AT entity_string TOK_EMPH entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (!$2.had_error)
        csh.AddCSH_EntityName(@2, $2);
    csh.AddCSH(@3, COLOR_SYMBOL);
    if (!$4.had_error)
        csh.AddCSH_EntityName(@4, $4);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckEntityHintBetweenAndAt(@1, @2)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(false);
        csh.hintStatus = HINT_READY;
    } else
        csh.CheckEntityHintBetweenAndAt(@3, @4);
  #else
    if ($2.had_error || $4.had_error)
        $$ = nullptr;
    else
        $$ = new VertXPos(chart, $2, @2, $4, @4);
  #endif
    (void)$1;
    $2.destroy();
    $4.destroy();
}
      | TOK_AT entity_string TOK_DASH entity_string TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (!$2.had_error)
        csh.AddCSH_EntityName(@2, $2);
    csh.AddCSH(@3, COLOR_SYMBOL);
    if (!$4.had_error)
        csh.AddCSH_EntityName(@4, $4);
    csh.AddCSH(@5, COLOR_ATTRVALUE);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckEntityHintBetweenAndAt(@1, @2)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(false);
        csh.hintStatus = HINT_READY;
    } else
        csh.CheckEntityHintBetweenAndAt(@3, @4);
  #else
    if ($2.had_error || $4.had_error)
        $$ = nullptr;
    else
        $$ = new VertXPos(chart, $2, @2, $4, @4, to_double($5));
  #endif
    (void)$1;
    $2.destroy();
    $4.destroy();
}
      | TOK_AT entity_string TOK_EMPH entity_string TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (!$2.had_error)
        csh.AddCSH_EntityName(@2, $2);
    csh.AddCSH(@3, COLOR_SYMBOL);
    if (!$4.had_error)
        csh.AddCSH_EntityName(@4, $4);
    csh.AddCSH(@5, COLOR_ATTRVALUE);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckEntityHintBetweenAndAt(@1, @2)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(false);
        csh.hintStatus = HINT_READY;
    } else
        csh.CheckEntityHintBetweenAndAt(@3, @4);
  #else
    if ($2.had_error || $4.had_error)
        $$ = nullptr;
    else
        $$ = new VertXPos(chart, $2, @2, $4, @4, to_double($5));
  #endif
    (void)$1;
    $2.destroy();
    $4.destroy();
}
      | TOK_AT
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckEntityHintAfter(@1)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(false);
        csh.hintStatus = HINT_READY;
    }
    csh.AddCSH_ErrorAfter(@1, "Missing an entity name.");
  #else
    $$ = nullptr;
    chart.Error.Error(@1.after(), "Missing an entity name.");
  #endif
    (void)$1;
};


empharcrel_straight: emphrel
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_SYMBOL);
	$$ = 0; //dummy to supress warning
  #else
    $$ = new ArcTypePlusDir($1, EDirType::INDETERMINATE);
  #endif
}
      | TOK_ASTERISK emphrel
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_SYMBOL);
    csh.CheckEntityHintBetween(@1, @2);
	$$ = 0; //dummy to supress warning
  #else
    $$ = new ArcTypePlusDir($2, EArrowLost::AT_SRC, EDirType::INDETERMINATE, @1);
  #endif
}
      |  emphrel TOK_ASTERISK
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SYMBOL);
	$$ = 0; //dummy to supress warning
  #else
    $$ = new ArcTypePlusDir($1, EArrowLost::AT_SRC, EDirType::INDETERMINATE, @2);
  #endif
}
      | TOK_ASTERISK emphrel TOK_ASTERISK
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_SYMBOL);
    csh.AddCSH_Error(@3, MULTIPLE_ASTERISK_ERROR_MSG);
    csh.CheckEntityHintBetween(@1, @2);
    $$ = 0; //dummy to supress warning
  #else
    chart.Error.Error(@3, "Only one loss can be specified. Ignoring second asterisk ('*').");
    $$ = new ArcTypePlusDir($2, EArrowLost::AT_SRC, EDirType::INDETERMINATE, @1);
  #endif
}
      | relation_from
{
  #ifdef C_S_H_IS_COMPILED
	$$ = 0; //dummy to supress warning
  #else
    $$ = new ArcTypePlusDir($1, EDirType::LEFT);
  #endif
}
      | relation_to
{
  #ifdef C_S_H_IS_COMPILED
	$$ = 0; //dummy to supress warning
  #else
    $$ = new ArcTypePlusDir($1, EDirType::RIGHT);
  #endif
}
      | relation_bidir
{
  #ifdef C_S_H_IS_COMPILED
	$$ = 0; //dummy to supress warning
  #else
    $$ = new ArcTypePlusDir($1, EDirType::BIDIR);
  #endif
};

vertrel_no_xpos: entity_string empharcrel_straight entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
       csh.AddCSH(@1, COLOR_MARKERNAME);
    if (!$3.had_error)
       csh.AddCSH(@3, COLOR_MARKERNAME);
    if (csh.CheckHintAt(@1, EHintSourceType::MARKER))
        if (csh.hint_vertical_shapes)
            csh.AddVerticalTypesToHints();
    csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::MARKER);
  #else
    if ($1.had_error || $3.had_error)
        $$ = nullptr;
    else
        $$ = new Vertical($2, $1, $3, &chart);
    delete $2;
  #endif
    $1.destroy();
    $3.destroy();
}
      | empharcrel_straight entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (!$2.had_error)
        csh.AddCSH(@2, COLOR_MARKERNAME);
    if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::MARKER))
        if (csh.hint_vertical_shapes)
            csh.AddVerticalTypesToHints();
  #else
    if ($2.had_error)
        $$ = nullptr;
    else
        $$ = new Vertical($1, MARKER_HERE_STR, $2, &chart);
    delete $1;
  #endif
    $2.destroy();
}
      | entity_string empharcrel_straight
{
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
        csh.AddCSH(@1, COLOR_MARKERNAME);
    if (csh.CheckHintAt(@1, EHintSourceType::MARKER))
        if (csh.hint_vertical_shapes)
            csh.AddVerticalTypesToHints();
    csh.CheckHintAfter(@2, EHintSourceType::MARKER);
  #else
    if ($1.had_error)
        $$ = nullptr;
    else
        $$ = new Vertical($2, $1, MARKER_HERE_STR, &chart);
    delete $2;
  #endif
    $1.destroy();
}
      | empharcrel_straight
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintAfter(@1, EHintSourceType::MARKER))
        csh.AddLeftRightCenterToHints();
  #else
    $$ = new Vertical($1, MARKER_HERE_STR, MARKER_HERE_STR, &chart);
    delete $1;
  #endif
}
      | entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
        csh.AddCSH(@1, COLOR_MARKERNAME);
    if (csh.CheckHintAt(@1, EHintSourceType::MARKER))
        if (csh.hint_vertical_shapes)
            csh.AddVerticalTypesToHints();
    csh.AddCSH_ErrorAfter(@1, "Missing a box or arrow symbol.");
  #else
    $$ = nullptr;
    chart.Error.Error(@1.after(), "Missing a box or arrow symbol.");
  #endif
    $1.destroy();
};

leftofrightof: TOK_SYMBOLPOS_RIGHTOF { $$ = 1; (void)$1; }
             | TOK_SYMBOLPOS_LEFTOF { $$ = 0; (void)$1; };

vertrefpos: leftofrightof 
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
  #else
    $$ = new VertRefPos{.side = $1 ? ESide::RIGHT : ESide::LEFT};
  #endif
}
        | leftofrightof entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_ATTRVALUE);
  #else
    if ($2.had_error)
        $$ = nullptr;
    else
        $$ = new VertRefPos{.side = $1 ? ESide::RIGHT : ESide::LEFT, .refname = $2.str(), .refname_loc = @2};
  #endif
};

vertrel: vertrel_no_xpos vertxpos
{
  #ifdef C_S_H_IS_COMPILED
  #else
    if ($1)
        $$ = ($1)->AddXpos($2);
    else
        $$ = nullptr;
    delete $2;
  #endif
}
      | vertrel_no_xpos
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintAfter(@1, EHintSourceType::KEYWORD)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.hintStatus = HINT_READY;
    }
#else
    if ($1) {
        VertXPos vxp;
        $$ = ($1)->AddXpos(&vxp);
    } else
        $$ = nullptr;
  #endif
}
      | vertxpos
{
  #ifdef C_S_H_IS_COMPILED
  #else
    if ($1) {
		ArcTypePlusDir typeplusdir;
		typeplusdir.arc.type = EArcSymbol::ARC_SOLID;
		typeplusdir.arc.lost = EArrowLost::NOT;
		typeplusdir.dir = EDirType::RIGHT;
		Vertical *ava = new Vertical(&typeplusdir, MARKER_HERE_STR, MARKER_HERE_STR, &chart);
		ava->AddXpos($1);
		$$ = ava;
		delete $1;
	} else
	    $$ = nullptr;
  #endif
} 
         | vertrefpos
 {
  #ifdef C_S_H_IS_COMPILED
  #else
    if ($1) {
		ArcTypePlusDir typeplusdir;
		typeplusdir.arc.type = EArcSymbol::ARC_SOLID;
		typeplusdir.arc.lost = EArrowLost::NOT;
		typeplusdir.dir = EDirType::RIGHT;
        $$ = new Vertical(&typeplusdir, MARKER_HERE_STR, MARKER_HERE_STR, &chart);
        $$->AddRefPos($1);
        delete $1;
	} else
	    $$ = nullptr;
  #endif
}
        | empharcrel_straight vertrefpos
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintBetween(@1, @2, EHintSourceType::MARKER)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.AddLeftRightCenterToHints();
    }
  #else
    $$ = new Vertical($1, MARKER_HERE_STR, MARKER_HERE_STR, &chart);
    $$->AddRefPos($2);
    delete $1;
    delete $2;
  #endif
};

arcrel:       TOK_SPECIAL_ARC
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_SYMBOL);
  #else
    $$ = new Divider($1, &chart);
  #endif
}
      | arrow_with_specifier
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintAfter(@1, EHintSourceType::KEYWORD)) {
        csh.AddArrowSpecifiersToHints();
        csh.hintStatus = HINT_READY;
    }
  #endif
    //explicit copy here to suppress bison warning (since the two types are different)
    $$ = $1;
}
      | arrow_with_specifier_incomplete
{
  #ifdef C_S_H_IS_COMPILED
  #endif
    //explicit copy here to suppress bison warning (since the two types are different)
    $$ = $1;
}
      | arrow_with_specifier error
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_Error(@2, "Could not figure out what comes here.");
    if (csh.CheckHintBetween(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddArrowSpecifiersToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@2, "Could not figure out what comes here.");
  #endif
    //explicit copy here to suppress bison warning (since the two types are different)
    $$ = $1;
}
      | arrow_with_specifier_incomplete error
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_Error(@2, "Could not figure out what comes here.");
  #else
    chart.Error.Error(@2, "Could not figure out what comes here.");
  #endif
    //explicit copy here to suppress bison warning (since the two types are different)
    $$ = $1;
};

arrow_with_specifier_incomplete:  arrow_with_specifier entity_string_single
{
  #ifdef C_S_H_IS_COMPILED
    if (CaseInsensitiveBeginsWith("lost", $2) ||
        CaseInsensitiveBeginsWith("start", $2) ||
        CaseInsensitiveBeginsWith("end", $2))
        csh.AddCSH(@2, COLOR_KEYWORD_PARTIAL);
    else
        csh.AddCSH_Error(@2, "Expecting 'lost at', 'start before' or 'end after' clauses, a semicolon ';' or attributes '['.");
    if (csh.CheckHintAt(@2, EHintSourceType::KEYWORD)) {
        csh.AddArrowSpecifiersToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@2.after(), "Expecting 'lost at', 'start before' or 'end after' clauses, a semicolon ';' or attributes '['.");
    $$ = $1;
  #endif
}
      | arrow_with_specifier TOK_LOST
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@2, "Missing 'at' clause.");
    if (csh.CheckHintAfter(@2, EHintSourceType::KEYWORD)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@2.after(), "Missing 'at' clause.");
    $$ = $1;
  #endif
   (void)$2;
}
      | arrow_with_specifier TOK_START
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@2, "Missing 'before' keyword.");
    if (csh.CheckHintAfter(@2, EHintSourceType::KEYWORD)) {
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_KEYWORD) + "before",
            "Use the 'start before' keyword to indicate where the message originates from.",
            EHintType::KEYWORD, true));
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@2.after(), "Missing 'before' keyword.");
    $$ = $1;
  #endif
   (void)$2;
}
      | arrow_with_specifier TOK_START entity_string_single
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_KEYWORD);
    if (CaseInsensitiveBeginsWith("before", $3))
        csh.AddCSH(@3, COLOR_KEYWORD_PARTIAL);
    else
        csh.AddCSH_Error(@3, "Missing 'before' keyword.");
    if (csh.CheckHintAt(@3, EHintSourceType::KEYWORD)) {
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_KEYWORD) + "before",
            "Use the 'start before' keyword to indicate where the message originates from.",
            EHintType::KEYWORD, true));
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@3, "Missing 'before' keyword.");
    $$ = $1;
  #endif
   (void)$2;
}
      | arrow_with_specifier TOK_START TOK_BEFORE
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_KEYWORD);
    csh.AddCSH(@3, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@3, "Missing an entity after 'start before'.");
    if (csh.CheckHintAfter(@3, EHintSourceType::KEYWORD)) {
        csh.AddStartBeforeEndAfterContentToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@3.after(), "Missing entity after the 'start before' clause.");
    $$ = $1;
  #endif
   (void)$2;
   (void)$3;
}
      | arrow_with_specifier TOK_END
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@2, "Missing 'after' keyword.");
    if (csh.CheckHintAfter(@2, EHintSourceType::KEYWORD)) {
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_KEYWORD) + "after",
            "Use the 'end after' keyword to indicate where the message is sent to.",
            EHintType::KEYWORD, true));
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@2.after(), "Missing 'after' keyword.");
    $$ = $1;
  #endif
   (void)$2;
}
      | arrow_with_specifier TOK_END entity_string_single
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_KEYWORD);
    if (CaseInsensitiveBeginsWith("after", $3))
        csh.AddCSH(@3, COLOR_KEYWORD_PARTIAL);
    else
        csh.AddCSH_Error(@3, "Missing 'after' keyword.");
    if (csh.CheckHintAt(@3, EHintSourceType::KEYWORD)) {
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_KEYWORD) + "after",
            "Use the 'end after' keyword to indicate where the message is sent to.",
            EHintType::KEYWORD, true));
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@3, "Missing 'after' keyword.");
    $$ = $1;
  #endif
   (void)$2;
}
      | arrow_with_specifier TOK_END TOK_AFTER
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_KEYWORD);
    csh.AddCSH(@3, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@3, "Missing an entity after 'end after'.");
    if (csh.CheckHintAfter(@3, EHintSourceType::KEYWORD)) {
        csh.AddStartBeforeEndAfterContentToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@3.after(), "Missing entity after the 'end after' clause.");
    $$ = $1;
  #endif
   (void)$2;
   (void)$3;
};


arrow_with_specifier:  arcrel_arrow
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintAfter(@1, EHintSourceType::KEYWORD)) {
        csh.AddArrowSpecifiersToHints();
        csh.hintStatus = HINT_READY;
    }
  #endif
    $$ = $1;
}
      | arrow_with_specifier TOK_LOST vertxpos
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_KEYWORD);
  #else
    if ($1)
        $$ = ($1)->AddLostPos($3, @2 + @3);
    else {
        $$ = nullptr;
        delete $3;
    }
  #endif
   (void)$2;
}
      | arrow_with_specifier TOK_START TOK_BEFORE special_ending
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_KEYWORD);
    csh.AddCSH(@3, COLOR_KEYWORD);
  #else
    $$ = ($1)->AddStartPos($4);
  #endif
   (void)$2;
   (void)$3;
}
      | arrow_with_specifier TOK_END TOK_AFTER special_ending
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_KEYWORD);
    csh.AddCSH(@3, COLOR_KEYWORD);
  #else
    $$ = ($1)->AddEndPos($4);
  #endif
   (void)$2;
   (void)$3;
};

special_ending: entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
        csh.AddCSH(@1, COLOR_ENTITYNAME);
  #else
    if ($1.had_error)
        $$ = nullptr;
    else
        $$ = new ArrowEnding(chart, $1, @1);
  #endif
   $1.destroy();
}
      | entity_string TOK_NUMBER
{
  #ifdef C_S_H_IS_COMPILED
    if (!$1.had_error)
        csh.AddCSH(@1, COLOR_ENTITYNAME);
    csh.AddCSH(@2, COLOR_ATTRVALUE);
  #else
    if ($1.had_error)
        $$ = nullptr;
    else
        $$ = new ArrowEnding(chart, $1, @1, to_double($2));
  #endif
   $1.destroy();
}
      | TOK_COLORDEF_NAME_NUMBER
{
  //We allow this to capture <entity>+-number without space
  //Find the +-sign's offset
  const size_t off = std::min($1.view().size(), $1.view().find_first_of("+-"));
  #ifdef C_S_H_IS_COMPILED
    CshPos pos = @1;
    pos.end_pos.char_index = pos.first_pos.char_index + UTF8len($1.view().substr(0, off));
    pos.end_pos.byte_index = pos.first_pos.byte_index + off;
    csh.AddCSH(pos, COLOR_ENTITYNAME);
    pos.first_pos = pos.end_pos;
    pos.end_pos = @1.end_pos;
    csh.AddCSH(pos, COLOR_ATTRVALUE);
  #else
    double offset = to_double($1.view().substr(off));
    const FileLineCol start = @1;
    $$ = new ArrowEnding(chart, $1.view().substr(0, off),
                         {start, start.AdvanceUTF8($1.view().substr(0, off))}, offset);
  #endif
};


arcrel_arrow: arcrel_to | arcrel_from | arcrel_bidir
      | entity_string TOK_REL_MSCGEN entity_string
{
  #ifdef C_S_H_IS_COMPILED
    //This rule can happen only in mscgen compat mode
    //(or else the lexer does not return TOK_REL_MSCGEN
    _ASSERT(csh.mscgen_compat == EMscgenCompat::FORCE_MSCGEN);
    if (csh.CheckEntityHintAt(@1) || csh.CheckEntityHintAt(@3))
        csh.AllowAnything();
    if (!$1.had_error)
        csh.AddCSH_EntityName(@1, $1);
    csh.AddCSH(@2, COLOR_SYMBOL);
    if (!$3.had_error)
        csh.AddCSH_EntityName(@3, $3);
#else
    //This rule can happen only in mscgen compat mode
    //(or else the lexer does not return TOK_REL_MSCGEN
    _ASSERT(chart.mscgen_compat == EMscgenCompat::FORCE_MSCGEN);
    if ($1.had_error || $3.had_error)
        $$ = nullptr;
    else {
        ArrowSegmentData data($2);
        $$ = chart.CreateArrow(data, $1, @1, $3, true, @3, $1.had_param || $3.had_param);
        ($$)->Indicate_Mscgen_Headless();
    }
  #endif
    $1.destroy();
    $3.destroy();
}
      | entity_string TOK_REL_DASH_X entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAt(@1) || csh.CheckEntityHintAt(@3))
        csh.AllowAnything();
    if (!$1.had_error)
        csh.AddCSH_EntityName(@1, $1);
    csh.AddCSH(@2, COLOR_SYMBOL);
    if (!$3.had_error)
        csh.AddCSH_EntityName(@3, $3);
  #else
    if ($1.had_error || $3.had_error)
        $$ = nullptr;
    else {
        ArrowSegmentData data(EArcSymbol::ARC_SOLID, EArrowLost::AT_DST, @2);
        $$ = chart.CreateArrow(data, $1, @1, $3, true, @3, $1.had_param || $3.had_param);
    }
  #endif
    $1.destroy();
    $3.destroy();
}
      | entity_string TOK_REL_X TOK_DASH entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAt(@1) || csh.CheckEntityHintAt(@4))
        csh.AllowAnything();
    if (!$1.had_error)
    csh.AddCSH_EntityName(@1, $1);
    if (csh.mscgen_compat == EMscgenCompat::FORCE_MSCGEN) {
        if (@2.end_pos == @3.first_pos) {
            csh.AddCSH(@2 + @3, COLOR_SYMBOL);
            if (!$4.had_error)
                csh.AddCSH_EntityName(@4, $4);
        } else {
            csh.AddCSH_Error(@2, "Missing arrow symbol before 'x'. (Perhaps remove space after?)");
        }
    } else {
        if (@2.end_pos == @3.first_pos) {
            csh.AddCSH_Error(@2 + @3, "Unsupported arrow symbol ('x-'), use '* <-' for indicating loss instead.");
        } else {
            csh.AddCSH_Error(@2, "Missing arrow symbol before 'x'.");
        }
    }
  #else
    if (chart.mscgen_compat == EMscgenCompat::FORCE_MSCGEN) {
        if (@2.after() == @3) {
            if ($1.had_error || $4.had_error)
                $$ = nullptr;
            else {
                ArrowSegmentData data(EArcSymbol::ARC_SOLID, EArrowLost::AT_DST, @2);
                $$ = chart.CreateArrow(data, $4, @4, $1, false, @1, $1.had_param || $4.had_param);
            }
        } else {
            chart.Error.Error(@2, "Missing arrow symbol before 'x'.",
                "Perhaps remove the whitespace after?");
            $$ = nullptr;
        }
    } else {
        if (@2.after() == @3) {
            chart.Error.Error(@2, "This arrow symbol ('x-') is supported only in mscgen compatibility mode.",
                "Use an asterisk '*' instead to express a lost message (like '-> *').");
            $$ = nullptr;
        } else {
            chart.Error.Error(@2, "Missing arrow symbol before 'x'.");
            $$ = nullptr;
        }
    }
  #endif
    $1.destroy();
    (void)$2;
    $4.destroy();
}
      | entity_string TOK_REL_DASH_X
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAt(@1))
        csh.AllowAnything();
    if (!$1.had_error)
        csh.AddCSH_EntityName(@1, $1);
    csh.AddCSH(@2, COLOR_SYMBOL);
    csh.AddCSH_ErrorAfter(@2, "Missing an entity name.");
  #else
    chart.Error.Error(@2.after(), "Missing an entity name after the loss arrow symbol.");
    $$ = nullptr;
#endif
    $1.destroy();
}
      | entity_string TOK_REL_X TOK_DASH
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAt(@1))
        csh.AllowAnything();
    if (!$1.had_error)
        csh.AddCSH_EntityName(@1, $1);
    if (csh.mscgen_compat == EMscgenCompat::FORCE_MSCGEN) {
        if (@2.end_pos == @3.first_pos) {
            csh.AddCSH(@2 + @3, COLOR_SYMBOL);
            csh.AddCSH_ErrorAfter(@2, "Missing an entity name.");
        } else {
            csh.AddCSH_Error(@2, "Missing arrow symbol before 'x'. (Perhaps remove space after?)");
        }
    } else {
        if (@2.end_pos == @3.first_pos) {
            csh.AddCSH_Error(@2 + @3, "Unsupported arrow symbol ('x-'), use '* <-' for indicating loss instead.");
        } else {
            csh.AddCSH_Error(@2, "Missing arrow symbol before 'x'.");
        }
    }
  #else
    if (chart.mscgen_compat == EMscgenCompat::FORCE_MSCGEN && @2.after() == @3)
         chart.Error.Error(@3.after(), "Missing an entity name after the loss arrow symbol.");
    else
        chart.Error.Error(@2, "Missing arrow symbol before 'x'.");
    $$ = nullptr;
#endif
    $1.destroy();
    (void)$2;
}
      | entity_string TOK_DASH entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAt(@1) || csh.CheckEntityHintAt(@3))
        csh.AllowAnything();
    if (!$1.had_error)
        csh.AddCSH_EntityName(@1, $1);
    if (!$3.had_error)
        csh.AddCSH_EntityName(@3, $3);
    csh.AddCSH_Error(@2, "Missing an arrow or box symbol.");
  #else
    chart.Error.Error(@2, "Missing an arrow or box symbol. Ignoring this line.");
    $$ = nullptr;
  #endif
    $1.destroy();
    $3.destroy();
}
      | TOK_DASH entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_Error(@1+@2, "Missing an arrow or box symbol.");
  #else
    chart.Error.Error(@1, "Missing an arrow or box symbol. Ignoring this line.");
    $$ = nullptr;
  #endif
    $2.destroy();
}
      | entity_string TOK_DASH
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAt(@1))
        csh.AllowAnything();
    if (!$1.had_error)
        csh.AddCSH_EntityName(@1, $1);
    csh.AddCSH_Error(@2, "Missing an arrow or box symbol.");
  #else
    chart.Error.Error(@2, "Missing an arrow or box symbol. Ignoring this line.");
    $$ = nullptr;
  #endif
    $1.destroy();
};


arcrel_to:    entity_string relation_to entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAt(@1) || csh.CheckEntityHintBetweenAndAt(@2, @3))
        csh.AllowAnything();
    if (!$1.had_error)
        csh.AddCSH_EntityName(@1, $1);
    if (!$3.had_error)
        csh.AddCSH_EntityName(@3, $3);
  #else
    if ($2 && !$1.had_error && !$3.had_error)
        $$ = chart.CreateArrow(*$2, $1, @1, $3, true, @3, $1.had_param || $3.had_param);
    else
        $$ = nullptr;
    delete $2;
  #endif
    $1.destroy();
    $3.destroy();
}
      | relation_to
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAfter(@1)) {
        csh.hintStatus = HINT_FILLING; //to allow adding "lost_at"
        csh.AllowAnything();
    }
  #else
    if ($1)
        $$ = chart.CreateArrow(*$1, LSIDE_ENT_STR, @1, RSIDE_ENT_STR, true, @1, false);
    else
        $$ = nullptr;
    delete $1;
  #endif
}
      | relation_to entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintBetweenAndAt(@1, @2))
        csh.AllowAnything();
    if (!$2.had_error)
        csh.AddCSH_EntityName(@2, $2);
  #else
    if ($1 && !$2.had_error)
        $$ = chart.CreateArrow(*$1, LSIDE_ENT_STR, @1, $2, true, @2, $2.had_param);
    else
        $$ = nullptr;
    delete $1;
  #endif
    $2.destroy();
}
      | TOK_PIPE_SYMBOL relation_to entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_SYMBOL);
    if (csh.CheckEntityHintBetweenAndAt(@2, @3))
        csh.AllowAnything();
    if (!$3.had_error)
       csh.AddCSH_EntityName(@3, $3);
  #else
    if ($2 && !$3.had_error)
        $$ = chart.CreateArrow(*$2, LSIDE_PIPE_ENT_STR, @1, $3, true, @3, $3.had_param);
    else
        $$ = nullptr;
    delete $2;
  #endif
    $3.destroy();
}
      | entity_string relation_to
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAt(@1))
        csh.AllowAnything();
    if (!$1.had_error)
        csh.AddCSH_EntityName(@1, $1);
    if (csh.CheckEntityHintAfter(@2)) {
        csh.hintStatus = HINT_FILLING; //to allow adding "lost_at"
        csh.AllowAnything();
    }
  #else
    if ($2 && !$1.had_error)
        $$ = chart.CreateArrow(*$2, $1, @1, RSIDE_ENT_STR, true, @2, $1.had_param);
    else
        $$ = nullptr;
    delete $2;
  #endif
    $1.destroy();
}
      | entity_string relation_to TOK_PIPE_SYMBOL
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAt(@1))
        csh.AllowAnything();
    if (!$1.had_error)
        csh.AddCSH_EntityName(@1, $1);
    if (csh.CheckEntityHintAfter(@2)) {
        csh.hintStatus = HINT_FILLING; //to allow adding "lost_at"
        csh.AllowAnything();
    }
    csh.AddCSH(@3, COLOR_SYMBOL);
  #else
    if ($2 && !$1.had_error)
        $$ = chart.CreateArrow(*$2, $1, @1, RSIDE_PIPE_ENT_STR, true, @3, $1.had_param);
    else
        $$ = nullptr;
    delete $2;
  #endif
    $1.destroy();
}
      | arcrel_to relation_to_cont entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAt(@3))
        csh.AllowAnything();
    if (!$3.had_error)
        csh.AddCSH_EntityName(@3, $3);
  #else
    if ($2 && !$3.had_error)
        $$ = ($1)->AddSegment(*$2, $3, @3, @2 + @3);
    else
        $$ = $1;
    delete $2;
  #endif
    $3.destroy();
}
      | arcrel_to relation_to_cont
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAfter(@1)) {
        csh.hintStatus = HINT_FILLING; //to allow adding "lost_at"
        csh.AllowAnything();
    }
  #else
    if ($2)
        $$ = ($1)->AddSegment(*$2, {}, @2, @2);
    else
        $$ = $1;
    delete $2;
  #endif
}
      | arcrel_to relation_to_cont TOK_PIPE_SYMBOL
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAfter(@1)) {
        csh.hintStatus = HINT_FILLING; //to allow adding "lost_at"
        csh.AllowAnything();
    }
    csh.AddCSH(@3, COLOR_SYMBOL);
  #else
    if ($2)
        $$ = ($1)->AddSegment(*$2, RSIDE_PIPE_ENT_STR, @2, @2);
    else
        $$ = $1;
    delete $2;
  #endif
};


arcrel_from:    entity_string relation_from entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAt(@1) || csh.CheckEntityHintBetweenAndAt(@2, @3))
        csh.AllowAnything();
    if (!$1.had_error)
        csh.AddCSH_EntityName(@1, $1);
    if (!$3.had_error)
        csh.AddCSH_EntityName(@3, $3);
  #else
    if ($2 && !$1.had_error && !$3.had_error)
        $$ = chart.CreateArrow(*$2, $3, @3, $1, false, @1, $1.had_param || $3.had_param);
    else
        $$ = nullptr;
    delete $2;
  #endif
    $1.destroy();
    $3.destroy();
}
      | relation_from
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAfter(@1)) {
        csh.hintStatus = HINT_FILLING; //to allow adding "lost_at"
        csh.AllowAnything();
    }
  #else
    if ($1)
        $$ = chart.CreateArrow(*$1, RSIDE_ENT_STR, @1, LSIDE_ENT_STR, false, @1, false);
    else
        $$ = nullptr;
    delete $1;
  #endif
}
      | relation_from entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintBetweenAndAt(@1, @2))
        csh.AllowAnything();
    if (!$2.had_error)
        csh.AddCSH_EntityName(@2, $2);
  #else
    if ($1 && !$2.had_error)
        $$ = chart.CreateArrow(*$1, $2, @2, LSIDE_ENT_STR, false, @1, $2.had_param);
    else
        $$ = nullptr;
    delete $1;
  #endif
    $2.destroy();
}
      | TOK_PIPE_SYMBOL relation_from entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_SYMBOL);
    if (csh.CheckEntityHintBetweenAndAt(@2, @3))
        csh.AllowAnything();
    if (!$3.had_error)
        csh.AddCSH_EntityName(@3, $3);
  #else
    if ($2 && !$3.had_error)
        $$ = chart.CreateArrow(*$2, $3, @3, LSIDE_PIPE_ENT_STR, false, @1, $3.had_param);
    else
        $$ = nullptr;
    delete $2;
  #endif
    $3.destroy();
}
      | entity_string relation_from
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAt(@1))
        csh.AllowAnything();
    if (!$1.had_error)
        csh.AddCSH_EntityName(@1, $1);
    if (csh.CheckEntityHintAfter(@2)) {
        csh.hintStatus = HINT_FILLING; //to allow adding "lost_at"
        csh.AllowAnything();
    }
  #else
    if ($2 && !$1.had_error)
        $$ = chart.CreateArrow(*$2, RSIDE_ENT_STR, @2, $1, false, @1, $1.had_param);
    else
        $$ = nullptr;
    delete $2;
  #endif
    $1.destroy();
}
      | entity_string relation_from TOK_PIPE_SYMBOL
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAt(@1))
        csh.AllowAnything();
    if (!$1.had_error)
        csh.AddCSH_EntityName(@1, $1);
    if (csh.CheckEntityHintAfter(@2)) {
        csh.hintStatus = HINT_FILLING; //to allow adding "lost_at"
        csh.AllowAnything();
    }
    csh.AddCSH(@3, COLOR_SYMBOL);
  #else
    if ($2 && !$1.had_error)
        $$ = chart.CreateArrow(*$2, RSIDE_PIPE_ENT_STR, @3, $1, false, @1, $1.had_param);
    else
        $$ = nullptr;
    delete $2;
  #endif
    $1.destroy();
}
      | arcrel_from relation_from_cont entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintBetweenAndAt(@2, @3))
        csh.AllowAnything();
    if (!$3.had_error)
        csh.AddCSH_EntityName(@3, $3);
  #else
    if ($2 && !$3.had_error)
        $$ = ($1)->AddSegment(*$2, $3, @3, @2 + @3);
    else
        $$ = $1;
    delete $2;
  #endif
    $3.destroy();
}
      | arcrel_from relation_from_cont
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAfter(@2)) {
        csh.hintStatus = HINT_FILLING; //to allow adding "lost_at"
        csh.AllowAnything();
    }
  #else
    if ($2)
        $$ = ($1)->AddSegment(*$2, {}, @2, @2);
    else
        $$ = $1;
    delete $2;
  #endif
}
      | arcrel_from relation_from_cont TOK_PIPE_SYMBOL
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAfter(@2)) {
        csh.hintStatus = HINT_FILLING; //to allow adding "lost_at"
        csh.AllowAnything();
    }
    csh.AddCSH(@3, COLOR_SYMBOL);
  #else
    if ($2)
        $$ = ($1)->AddSegment(*$2, RSIDE_PIPE_ENT_STR, @3, @2);
    else
        $$ = $1;
    delete $2;
  #endif
};


arcrel_bidir:    entity_string relation_bidir entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAt(@1) ||csh.CheckEntityHintBetweenAndAt(@2, @3))
        csh.AllowAnything();
    if (!$1.had_error)
        csh.AddCSH_EntityName(@1, $1);
    if (!$3.had_error)
        csh.AddCSH_EntityName(@3, $3);
  #else
    if ($2 && !$1.had_error && !$3.had_error)
        $$ = chart.CreateArrow(*$2, $1, @1, $3, true, @3, $1.had_param || $3.had_param);
    else
        $$ = nullptr;
    delete $2;
  #endif
    $1.destroy();
    $3.destroy();
}
      | relation_bidir entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintBetweenAndAt(@1, @2))
        csh.AllowAnything();
    if (!$2.had_error)
        csh.AddCSH_EntityName(@2, $2);
  #else
    if ($1 && !$2.had_error)
        $$ = chart.CreateArrow(*$1, LSIDE_ENT_STR, @1, $2, true, @2, $2.had_param);
    else
        $$ = nullptr;
    delete $1;
  #endif
    $2.destroy();
}
      | relation_bidir
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAfter(@1)) {
        csh.hintStatus = HINT_FILLING; //to allow adding "lost_at"
        csh.AllowAnything();
    }
  #else
    if ($1)
        $$ = chart.CreateArrow(*$1, LSIDE_ENT_STR, @1, RSIDE_ENT_STR, true, @1, false);
    else
        $$ = nullptr;
    delete $1;
  #endif
}
      | TOK_PIPE_SYMBOL relation_bidir entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_SYMBOL);
    if (csh.CheckEntityHintBetweenAndAt(@2, @3))
        csh.AllowAnything();
    if (!$3.had_error)
        csh.AddCSH_EntityName(@3, $3);
  #else
    if ($2 && !$3.had_error)
        $$ = chart.CreateArrow(*$2, LSIDE_PIPE_ENT_STR, @1, $3, true, @3, $3.had_param);
    else
        $$ = nullptr;
    delete $2;
  #endif
    $3.destroy();
}
      | entity_string relation_bidir
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAt(@1))
        csh.AllowAnything();
    if (!$1.had_error)
        csh.AddCSH_EntityName(@1, $1);
    if (csh.CheckEntityHintAfter(@2))
        csh.hintStatus = HINT_FILLING; //to allow adding "lost_at"
  #else
    if ($2 && !$1.had_error)
        $$ = chart.CreateArrow(*$2, $1, @1, RSIDE_ENT_STR, true, @2, $1.had_param);
    else
        $$ = nullptr;
    delete $2;
  #endif
    $1.destroy();
}
      | entity_string relation_bidir TOK_PIPE_SYMBOL
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAt(@1))
        csh.AllowAnything();
    csh.AddCSH_EntityName(@1, $1);
    if (csh.CheckEntityHintAfter(@2)) {
        csh.hintStatus = HINT_FILLING; //to allow adding "lost_at"
        csh.AllowAnything();
    }
    csh.AddCSH(@3, COLOR_SYMBOL);
  #else
    if ($2 && !$1.had_error)
        $$ = chart.CreateArrow(*$2, $1, @1, RSIDE_PIPE_ENT_STR, true, @3, $1.had_param);
    else
        $$ = nullptr;
    delete $2;
  #endif
    $1.destroy();
}
      | arcrel_bidir relation_bidir_cont entity_string
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintBetweenAndAt(@2, @3))
        csh.AllowAnything();
    if (!$3.had_error)
        csh.AddCSH_EntityName(@3, $3);
  #else
    if ($2 && !$3.had_error)
        $$ = ($1)->AddSegment(*$2, $3, @3, @2 + @3);
    else
        $$ = $1;
    delete $2;
  #endif
    $3.destroy();
}
      | arcrel_bidir relation_bidir_cont
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAfter(@2)) {
        csh.hintStatus = HINT_FILLING; //to allow adding "lost_at"
        csh.AllowAnything();
    }
  #else
    if ($2)
        $$ = ($1)->AddSegment(*$2, {}, @2, @2);
    else
        $$ = $1;
    delete $2;
  #endif
}
      | arcrel_bidir relation_bidir_cont TOK_PIPE_SYMBOL
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckEntityHintAfter(@2)) {
        csh.hintStatus = HINT_FILLING; //to allow adding "lost_at"
        csh.AllowAnything();
    }
    csh.AddCSH(@3, COLOR_SYMBOL);
  #else
    if ($2)
        $$ = ($1)->AddSegment(*$2, RSIDE_PIPE_ENT_STR, @3, @2);
    else
        $$ = $1;
    delete $2;
  #endif
};

relation_to_cont_no_loss: TOK_REL_TO | TOK_DASH {$$=EArcSymbol::ARC_UNDETERMINED_SEGMENT;};
relation_from_cont_no_loss: TOK_REL_FROM | TOK_DASH {$$=EArcSymbol::ARC_UNDETERMINED_SEGMENT;};
relation_bidir_cont_no_loss: TOK_REL_BIDIR | TOK_DASH {$$=EArcSymbol::ARC_UNDETERMINED_SEGMENT;};

relation_to: TOK_REL_TO
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_SYMBOL);
    csh.asteriskNo = 0;
  #else
    $$ = new ArrowSegmentData($1);
  #endif
}
      | TOK_ASTERISK TOK_REL_TO
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_SYMBOL);
    csh.asteriskNo = 1;
  #else
    $$ = new ArrowSegmentData($2, EArrowLost::AT_SRC, @1);
  #endif
}
      | TOK_REL_TO TOK_ASTERISK
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_SYMBOL);
    csh.asteriskNo = 1;
  #else
    $$ = new ArrowSegmentData($1, EArrowLost::AT_DST, @2);
  #endif
}
      | TOK_ASTERISK TOK_REL_TO TOK_ASTERISK
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1 + @2, COLOR_SYMBOL);
    csh.AddCSH_Error(@3, MULTIPLE_ASTERISK_ERROR_MSG);
    csh.asteriskNo = 2;
  #else
    chart.Error.Error(@3, "One arrow may be lost only once. Ignoring subsequent asterisks ('*').");
    $$ = new ArrowSegmentData($2, EArrowLost::AT_SRC, @1);
  #endif
};

relation_from: TOK_REL_FROM
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_SYMBOL);
    csh.asteriskNo = 1;
  #else
    $$ = new ArrowSegmentData($1);
  #endif
}
      | TOK_ASTERISK TOK_REL_FROM
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_SYMBOL);
    csh.asteriskNo = 1;
  #else
    $$ = new ArrowSegmentData($2, EArrowLost::AT_DST, @1);
  #endif
}
      | TOK_REL_FROM TOK_ASTERISK
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_SYMBOL);
    csh.asteriskNo = 1;
  #else
    $$ = new ArrowSegmentData($1, EArrowLost::AT_SRC, @2);
  #endif
}
      | TOK_ASTERISK TOK_REL_FROM TOK_ASTERISK
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1 + @2, COLOR_SYMBOL);
    csh.AddCSH_Error(@3, MULTIPLE_ASTERISK_ERROR_MSG);
    csh.asteriskNo = 2;
  #else
    chart.Error.Error(@3, "One arrow may be lost only once. Ignoring subsequent asterisks ('*').");
    $$ = new ArrowSegmentData($2, EArrowLost::AT_DST, @1);
  #endif
};

relation_bidir: TOK_REL_BIDIR
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_SYMBOL);
    csh.asteriskNo = 0;
  #else
    $$ = new ArrowSegmentData($1);
  #endif
}
      | TOK_ASTERISK TOK_REL_BIDIR
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_SYMBOL);
    csh.asteriskNo = 1;
  #else
    $$ = new ArrowSegmentData($2, EArrowLost::AT_SRC, @1);
  #endif
}
      | TOK_REL_BIDIR TOK_ASTERISK
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_SYMBOL);
    csh.asteriskNo = 1;
  #else
    $$ = new ArrowSegmentData($1, EArrowLost::AT_DST, @2);
  #endif
}
      | TOK_ASTERISK TOK_REL_BIDIR TOK_ASTERISK
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1 + @2, COLOR_SYMBOL);
    csh.AddCSH_Error(@3, MULTIPLE_ASTERISK_ERROR_MSG);
    csh.asteriskNo = 2;
  #else
    chart.Error.Error(@3, "One arrow may be lost only once. Ignoring subsequent asterisks ('*').");
    $$ = new ArrowSegmentData($2, EArrowLost::AT_SRC, @1);
  #endif
};

relation_to_cont: relation_to_cont_no_loss
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_SYMBOL);
  #else
    $$ = new ArrowSegmentData($1);
  #endif
}
      | TOK_ASTERISK relation_to_cont_no_loss
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SYMBOL);
    if (++csh.asteriskNo == 1)
        csh.AddCSH(@1, COLOR_SYMBOL);
    else
        csh.AddCSH_Error(@1, MULTIPLE_ASTERISK_ERROR_MSG);
  #else
    $$ = new ArrowSegmentData($2, EArrowLost::AT_SRC, @1);
  #endif
}
      | relation_to_cont_no_loss TOK_ASTERISK
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_SYMBOL);
    if (++csh.asteriskNo == 1)
        csh.AddCSH(@2, COLOR_SYMBOL);
    else
        csh.AddCSH_Error(@2, MULTIPLE_ASTERISK_ERROR_MSG);
  #else
    $$ = new ArrowSegmentData($1, EArrowLost::AT_DST, @2);
  #endif
}
      | TOK_ASTERISK relation_to_cont_no_loss TOK_ASTERISK
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SYMBOL);
    if (++csh.asteriskNo == 1)
        csh.AddCSH(@1, COLOR_SYMBOL);
    else
        csh.AddCSH_Error(@1, MULTIPLE_ASTERISK_ERROR_MSG);
    csh.AddCSH_Error(@3, MULTIPLE_ASTERISK_ERROR_MSG);
  #else
    chart.Error.Error(@3, "One arrow may be lost only once. Ignoring this asterisk ('*').");
    $$ = new ArrowSegmentData($2, EArrowLost::AT_SRC, @1);
  #endif
};

relation_from_cont: relation_from_cont_no_loss
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_SYMBOL);
  #else
    $$ = new ArrowSegmentData($1);
  #endif
}
      | TOK_ASTERISK relation_from_cont_no_loss
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SYMBOL);
    if (++csh.asteriskNo == 1)
        csh.AddCSH(@1, COLOR_SYMBOL);
    else
        csh.AddCSH_Error(@1, MULTIPLE_ASTERISK_ERROR_MSG);
  #else
    $$ = new ArrowSegmentData($2, EArrowLost::AT_DST, @1);
  #endif
}
      | relation_from_cont_no_loss TOK_ASTERISK
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_SYMBOL);
    if (++csh.asteriskNo == 1)
        csh.AddCSH(@2, COLOR_SYMBOL);
    else
        csh.AddCSH_Error(@2, MULTIPLE_ASTERISK_ERROR_MSG);
  #else
    $$ = new ArrowSegmentData($1, EArrowLost::AT_SRC, @2);
  #endif
}
      | TOK_ASTERISK relation_from_cont_no_loss TOK_ASTERISK
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SYMBOL);
    if (++csh.asteriskNo == 1)
        csh.AddCSH(@1, COLOR_SYMBOL);
    else
        csh.AddCSH_Error(@1, MULTIPLE_ASTERISK_ERROR_MSG);
    csh.AddCSH_Error(@3, MULTIPLE_ASTERISK_ERROR_MSG);
  #else
    chart.Error.Error(@3, "One arrow may be lost only once. Ignoring this asterisk ('*').");
    $$ = new ArrowSegmentData($2, EArrowLost::AT_DST, @1);
  #endif
};

relation_bidir_cont: relation_bidir_cont_no_loss
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@$, COLOR_SYMBOL);
  #else
    $$ = new ArrowSegmentData($1);
  #endif
}
      | TOK_ASTERISK relation_bidir_cont_no_loss
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SYMBOL);
    if (++csh.asteriskNo == 1)
        csh.AddCSH(@1, COLOR_SYMBOL);
    else
        csh.AddCSH_Error(@1, MULTIPLE_ASTERISK_ERROR_MSG);
  #else
    $$ = new ArrowSegmentData($2, EArrowLost::AT_SRC, @1);
  #endif
}
      | relation_bidir_cont_no_loss TOK_ASTERISK
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_SYMBOL);
    if (++csh.asteriskNo == 1)
        csh.AddCSH(@2, COLOR_SYMBOL);
    else
        csh.AddCSH_Error(@2, MULTIPLE_ASTERISK_ERROR_MSG);
  #else
    $$ = new ArrowSegmentData($1, EArrowLost::AT_DST, @2);
  #endif
}
      | TOK_ASTERISK relation_bidir_cont_no_loss TOK_ASTERISK
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_SYMBOL);
    if (++csh.asteriskNo == 1)
        csh.AddCSH(@1, COLOR_SYMBOL);
    else
        csh.AddCSH_Error(@1, MULTIPLE_ASTERISK_ERROR_MSG);
    csh.AddCSH_Error(@3, MULTIPLE_ASTERISK_ERROR_MSG);
  #else
    chart.Error.Error(@3, "One arrow may be lost only once. Ignoring this asterisk ('*').");
    $$ = new ArrowSegmentData($2, EArrowLost::AT_SRC, @1);
  #endif
};

//This is there so that when the user starts typing "left" and is at
//"lef", we match the entity string here.
extvertxpos: extvertxpos_no_string
      | entity_string_single
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_ExtvxposDesignatorName(@1, $1);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddLeftRightCenterToHints();
        csh.hintStatus = HINT_FILLING;
    }
  #else
    $$ = nullptr;
  #endif
    (void)$1;
};

extvertxpos_no_string: TOK_AT_POS vertxpos
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_ExtvxposDesignatorName(@1, $1);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddLeftRightCenterToHints();
        csh.hintStatus = HINT_FILLING;
    } else if (csh.CheckHintBetween(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = new ExtVertXPos($1, @1, $2);
    delete $2;
  #endif
    (void)$1;
}
      | TOK_AT_POS
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_ExtvxposDesignatorName(@1, $1);
    if (csh.CheckHintAt(@1, EHintSourceType::KEYWORD)) {
        csh.AddLeftRightCenterToHints();
        csh.hintStatus = HINT_FILLING;
    } else if (csh.CheckHintAfter(@1, EHintSourceType::KEYWORD)) {
        csh.AddVertXPosSyntaxNonSelectableToHints(true);
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = nullptr;
  #endif
    (void)$1;
};

symbol_position_string: TOK_SYMBOLPOS_ABOVE | TOK_SYMBOLPOS_BELOW | TOK_SYMBOLPOS_LEFTOF | TOK_SYMBOLPOS_RIGHTOF | TOK_SYMBOLPOS_AROUND;

 /** Here we allow entity string for 'arc' and 'rectange', symbol_string for '...'
  * and COMMAND TEXT for 'text'*/
symbol_type_string: entity_string_single | symbol_string | TOK_COMMAND_TEXT | TOK_COMMAND_BIG;

//in CSH mode we return (char*)0 for symbols positioned with markers and entity lines, (char*)1 for symbols positioned relative to other elements ("byref" symbols), (char*)2 if undecided
symbol_command_no_attr: TOK_COMMAND_SYMBOL symbol_type_string markerrel_no_string extvertxpos
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_SymbolName(@2, $2);
    csh.NoteInliningLanguage($2, @2);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddSymbolTypesToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::MARKER)) {
        csh.AddSymbolPosToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@3, @4, EHintSourceType::KEYWORD)) {
        csh.AddLeftRightCenterToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@4, EHintSourceType::KEYWORD)) {
        csh.AddLeftRightCenterToHints();
        csh.hintStatus = HINT_FILLING;
    }
    $$ = (char*)0;
  #else
    $$ = new Symbol(&chart, $2, @2, @$, $3, $4, nullptr);
    delete $3;
    delete $4;
  #endif
    (void)$1;
}
      | TOK_COMMAND_SYMBOL symbol_type_string markerrel_no_string extvertxpos extvertxpos
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_SymbolName(@2, $2);
    csh.NoteInliningLanguage($2, @2);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddSymbolTypesToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::MARKER)) {
        csh.AddSymbolPosToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@3, @4, EHintSourceType::KEYWORD)) {
        csh.AddLeftRightCenterToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@4, @5, EHintSourceType::KEYWORD)) {
        csh.AddLeftRightCenterToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@5, EHintSourceType::KEYWORD)) {
        csh.AddLeftRightCenterToHints();
        csh.hintStatus = HINT_FILLING;
    }
    $$ = (char*)0;
#else
    $$ = new Symbol(&chart, $2, @2, @$, $3, $4, $5);
    delete $3;
    delete $4;
    delete $5;
  #endif
    (void)$1;
}
//This is to cater for when the user has entered multiple extvertpos stuff - we indicate only one is needed
      | TOK_COMMAND_SYMBOL symbol_type_string markerrel_no_string extvertxpos extvertxpos extvertxpos
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_SymbolName(@2, $2);
    csh.NoteInliningLanguage($2, @2);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddSymbolTypesToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::MARKER)) {
        csh.AddSymbolPosToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@3, @4, EHintSourceType::KEYWORD)) {
        csh.AddLeftRightCenterToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@4, @5, EHintSourceType::KEYWORD)) {
        csh.AddLeftRightCenterToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@4, @6, EHintSourceType::KEYWORD)) {
        csh.AddLeftRightCenterToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@6, EHintSourceType::KEYWORD)) {
        csh.AddLeftRightCenterToHints();
        csh.hintStatus = HINT_FILLING;
    }
    csh.AddCSH_Error(@6, "Too many left/center/right specifiers here, at most two can be given.");
    $$ = (char*)0;
#else
    $$ = new Symbol(&chart, $2, @2, @$, $3, $4, $5);
    delete $3;
    delete $4;
    delete $5;
    delete $6;
    chart.Error.Error(@6, "Too many specifiers here, ignoring last one.", "At most two can be given.");
  #endif
    (void)$1;
}
      | TOK_COMMAND_SYMBOL symbol_type_string extvertxpos_no_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_SymbolName(@2, $2);
    csh.NoteInliningLanguage($2, @2);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddSymbolTypesToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@2, @3, EHintSourceType::MARKER)) {
        csh.AddLeftRightCenterToHints(); //markers, plus left/center/right
        csh.AddSymbolPosToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@3, EHintSourceType::KEYWORD)) {
        csh.AddLeftRightCenterToHints();
        csh.hintStatus = HINT_FILLING;
    }
    $$ = (char*)0;
  #else
    $$ = new Symbol(&chart, $2, @2, @$, nullptr, $3, nullptr);
    delete $3;
  #endif
    (void)$1;
}
      | TOK_COMMAND_SYMBOL symbol_type_string extvertxpos_no_string extvertxpos
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_SymbolName(@2, $2);
    csh.NoteInliningLanguage($2, @2);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddSymbolTypesToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@2, @3, EHintSourceType::MARKER)) {
        csh.AddLeftRightCenterToHints(); //markers, plus left/center/right
        csh.AddSymbolPosToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@3, @4, EHintSourceType::KEYWORD)) {
        csh.AddLeftRightCenterToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@4, EHintSourceType::KEYWORD)) {
        csh.AddLeftRightCenterToHints();
        csh.hintStatus = HINT_FILLING;
    }
    $$ = (char*)0;
  #else
    $$ = new Symbol(&chart, $2, @2, @$, nullptr, $3, $4);
    delete $3;
    delete $4;
  #endif
    (void)$1;
}
      | TOK_COMMAND_SYMBOL symbol_type_string vertxpos
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_SymbolName(@2, $2);
    csh.NoteInliningLanguage($2, @2);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddSymbolTypesToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetween(@2, @3, EHintSourceType::MARKER)) {
        csh.AddLeftRightCenterToHints(); //markers, plus left/center/right
        csh.AddSymbolPosToHints();
        csh.hintStatus = HINT_READY;
    }
    $$ = (char*)0;
  #else
    $$ = new Symbol(&chart, $2, @2, @$, $3, @3);
    delete $3;
  #endif
    (void)$1;
}
      | TOK_COMMAND_SYMBOL symbol_type_string markerrel_no_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_SymbolName(@2, $2);
    csh.NoteInliningLanguage($2, @2);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddSymbolTypesToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::MARKER)) {
        csh.AddSymbolPosToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@3, EHintSourceType::KEYWORD)) {
        csh.AddLeftRightCenterToHints();
        csh.hintStatus = HINT_FILLING;
    }
    $$ = (char*)0;
  #else
    $$ = new Symbol(&chart, $2, @2, @$, $3, nullptr, nullptr);
    delete $3;
  #endif
    (void)$1;
}
      | TOK_COMMAND_SYMBOL symbol_type_string entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_SymbolName(@2, $2);
    csh.NoteInliningLanguage($2, @2);
    if (!$3.had_error)
        csh.AddCSH_LeftRightCenterMarker(@3, $3);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddSymbolTypesToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::MARKER)) {
        csh.AddLeftRightCenterToHints();
        csh.AddSymbolPosToHints();
        csh.hintStatus = HINT_READY;
    }
    $$ = (char*)2;
  #else
    if ($3.had_error)
        $$ = nullptr;
    else
        $$ = new Symbol(&chart, $2, @2, @$, nullptr, nullptr, nullptr);
    chart.Error.Error(@3, "Expecting 'left'/'right'/'center', or 'above'/'below'/'leftof'/'rightof'/'around', or a marker name followed by a dash. Ignoring this piece.");
  #endif
    (void)$1;
    $3.destroy();
}
      | TOK_COMMAND_SYMBOL symbol_type_string symbol_position_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_SymbolName(@2, $2);
    csh.NoteInliningLanguage($2, @2);
    csh.AddCSH(@3, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddSymbolTypesToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::MARKER)) {
        csh.AddLeftRightCenterToHints();
        csh.AddSymbolPosToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@3, EHintSourceType::ATTR_VALUE)) {
        csh.addRefNamesAtEnd = true;
        csh.hintStatus = HINT_READY;
    }
    $$ = (char*)1;
  #else
    $$ = new Symbol(&chart, $2, @2, @$, $3, @3);
  #endif
    (void)$1;
}
      | TOK_COMMAND_SYMBOL symbol_type_string symbol_position_string entity_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_SymbolName(@2, $2);
    csh.NoteInliningLanguage($2, @2);
    csh.AddCSH(@3, COLOR_KEYWORD);
    csh.AddCSH(@4, COLOR_ATTRVALUE); //The ref name is like an attribute value
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddSymbolTypesToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::MARKER)) {
        csh.AddLeftRightCenterToHints();
        csh.AddSymbolPosToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@3, @4, EHintSourceType::ATTR_VALUE)) {
        csh.addRefNamesAtEnd = true;
        csh.hintStatus = HINT_READY;
    }
    $$ = (char*)1;
  #else
    $$ = new Symbol(&chart, $2, @2, @$, $3, @3, $4, @4);
  #endif
    (void)$1;
}
      | TOK_COMMAND_SYMBOL symbol_type_string
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_SymbolName(@2, $2);
    csh.NoteInliningLanguage($2, @2);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintBetweenAndAt(@1, @2, EHintSourceType::KEYWORD)) {
        csh.AddSymbolTypesToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@2, EHintSourceType::MARKER)) {
        csh.AddLeftRightCenterToHints();
        csh.AddSymbolPosToHints();
        csh.hintStatus = HINT_READY;
    }
    $$ = (char*)2;
  #else
    $$ = new Symbol(&chart, $2, @2, @$, nullptr, nullptr, nullptr);
  #endif
    (void)$1;
}
      | TOK_COMMAND_SYMBOL
{
  #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::KEYWORD)) {
        csh.AddSymbolTypesToHints();
        csh.hintStatus = HINT_READY;
    }
    $$ = (char*)2;
  #else
    chart.Error.Error(@1.after(), "Missing a symbol type.", "Use 'arc', '...', 'text', 'rectangle', 'cross', 'shape', or the name of a language to inline a full chart.");
    $$ = nullptr;
  #endif
    (void)$1;
};

symbol_command_no_inline: symbol_command_no_attr
{
  #ifndef C_S_H_IS_COMPILED
    if ($1)
        ($1)->AddAttributeList(nullptr);
    $$ = $1;
  #endif
}
      | symbol_command_no_attr full_arcattrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        Symbol::AttributeNames(csh, (size_t)$1);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        Symbol::AttributeValues(csh.hintAttrName, csh, (size_t)$1);
  #else
    if ($1)
        ($1)->AddAttributeList($2);
    $$ = $1;
  #endif
};

symbol_command: symbol_command_no_inline
              | symbol_command_no_inline TOK_INLINE
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddInLine($2, @2, csh.pedantic_at_start);
  #else
    if (auto s = dynamic_cast<Symbol*>($1))
        s->AddInline($2, @2);
    $$ = $1;
  #endif
};

note:            TOK_COMMAND_NOTE TOK_AT string full_arcattrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_KEYWORD);
    if (!$3.had_error)
        csh.AddCSH_EntityOrMarkerName(@3, $3);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @4))
        Note::AttributeNames(csh, true);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @4))
        Note::AttributeValues(csh.hintAttrName, csh, true);
    else if (csh.CheckEntityHintBetweenAndAt(@2, @3))
        csh.addMarkersAtEnd = true; //after 'at' a marker or an entity may come
  #else
    if ($3.had_error) {
        $$ = nullptr;
        delete $4;
    } else {
        $$ = new Note(&chart, $3, @3);
        ($$)->AddAttributeList($4);
    }
  #endif
    (void)$1;
    (void)$2;
    $3.destroy();
}
      | TOK_COMMAND_NOTE full_arcattrlist_with_label
{
  #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::KEYWORD)) {
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_KEYWORD) + "at", nullptr, EHintType::KEYWORD, true)); //XXX Other can come
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        Note::AttributeNames(csh, true);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        Note::AttributeValues(csh.hintAttrName, csh, true);
  #else
    $$ = new Note(&chart);
    ($$)->AddAttributeList($2);
  #endif
    (void)$1;
}
      | TOK_COMMAND_NOTE TOK_AT
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@2, "Missing an entity or marker name.");
    csh.AddCSH_ErrorAfter(@2, "Notes need a label.");
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckEntityHintAfter(@2))
        csh.addMarkersAtEnd = true; //after 'at' a marker or an entity may come
  #else
    $$ = nullptr;
    chart.Error.Error(@2.after(), "Missing an entity or marker name after 'at'.");
    chart.Error.Error(@2.after(), "Missing a label, ignoring note.", "Notes and comments must have text, specify a label.");
#endif
    (void)$1;
    (void)$2;
}
      | TOK_COMMAND_NOTE 
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@1, "Notes need a label. You can also continue with 'at <entity>'.");
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintAfter(@1, EHintSourceType::KEYWORD)) {
        csh.AddToHints(CshHint{csh.HintPrefix(COLOR_KEYWORD) + "at", nullptr, EHintType::KEYWORD, true});
        csh.hintStatus = HINT_READY;
    }
  #else
    $$ = nullptr;
    chart.Error.Error(@1.after(), "Missing a label, ignoring note.", "Notes and comments must have text, specify a label.");
#endif
    (void)$1;
}
      | TOK_COMMAND_NOTE TOK_AT full_arcattrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH(@2, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@2, "Missing an entity or marker name.");
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckEntityHintBetweenAndAt(@2, @3))
        csh.addMarkersAtEnd = true;
    if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @3))
        Note::AttributeNames(csh, true);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @3))
        Note::AttributeValues(csh.hintAttrName, csh, true);
  #else
    chart.Error.Error(@2.after(), "Missing an entity or marker name after 'at'. Ignoring 'at' clause.");
    $$ = new Note(&chart);
    ($$)->AddAttributeList($3);
  #endif
    (void)$1;
    (void)$2;
};

comment_command: TOK_COMMAND_COMMENT
{
  #ifndef C_S_H_IS_COMPILED
    $$= ESide::LEFT;
  #endif
    (void)$1;
}
      | TOK_COMMAND_ENDNOTE
{
  #ifndef C_S_H_IS_COMPILED
    $$= ESide::END;
  #endif
    (void)$1;
}
      | TOK_COMMAND_FOOTNOTE
{
  #ifndef C_S_H_IS_COMPILED
    $$= ESide::END;
  #endif
    (void)$1;
};

comment:            comment_command full_arcattrlist_with_label
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    } else if (csh.CheckHintLocated(EHintSourceType::ATTR_NAME, @2))
        Note::AttributeNames(csh, false);
    else if (csh.CheckHintLocated(EHintSourceType::ATTR_VALUE, @2))
        Note::AttributeValues(csh.hintAttrName, csh, false);
  #else
    $$ = new Note(&chart, $1);
    ($$)->AddAttributeList($2);
  #endif
}
      | comment_command
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@1, COLOR_KEYWORD);
    csh.AddCSH_ErrorAfter(@1, "Comments and notes need a label.");
    if (csh.CheckLineStartHintAt(@1)) {
        csh.AddLineBeginToHints();
        csh.hintStatus = HINT_READY;
    }
  #else
    chart.Error.Error(@1.after(), "Missing a label, ignoring comment/note.", "Notes and comments must have text, specify a label.");
    $$ = nullptr;
  #endif
};

full_arcattrlist_with_label: TOK_COLON_STRING
{
  #ifdef C_S_H_IS_COMPILED
  #else
        $$ = (new AttributeList)->Append(std::make_unique<Attribute>("label", $1, @$, @$.IncStartCol()));
  #endif
    $1.destroy();
}
      | TOK_COLON_STRING full_arcattrlist
{
  #ifdef C_S_H_IS_COMPILED
  #else
        $$ = ($2)->Prepend(std::make_unique<Attribute>("label", $1, @1, @1.IncStartCol()));
  #endif
    $1.destroy();
}
      | full_arcattrlist TOK_COLON_STRING full_arcattrlist
{
  #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
    $2.destroy();

}
      | full_arcattrlist TOK_COLON_STRING
{
  #ifdef C_S_H_IS_COMPILED
  #else
    $$ = $1->Append(std::make_unique<Attribute>("label", $2, @2, @2.IncStartCol()));
  #endif
    $2.destroy();
}
      | full_arcattrlist;


full_arcattrlist: 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 arcattrlist 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 arcattrlist 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 arcattrlist
{
  #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 arcattrlist 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
};

arcattrlist:    arcattr
{
  #ifdef C_S_H_IS_COMPILED
  #else
    $$ = (new AttributeList)->Append($1);
  #endif
}
      | arcattrlist TOK_COMMA arcattr
{
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_COMMA);
    csh.CheckHintBetween(@2, @3, EHintSourceType::ATTR_NAME);
  #else
    $$ = ($1)->Append($3);
  #endif
}
      | arcattrlist 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
};

arcattr:         alpha_string TOK_EQUAL string
{
  //string=string
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_EQUAL);
    if (!$1.had_error) {
        csh.AddCSH_AttrName(@1, $1, COLOR_ATTRNAME);
        if (!$3.had_error) {
            csh.AddCSH_AttrValue_CheckAndAddEscapeHint(@3, $3, $1);
            csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE, $1);
        }
    }
    csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME);
  #else
    if ($1.had_error || $3.had_error ||
        (($1.had_param || $3.had_param) && chart.SkipContent()))
        $$ = nullptr;
    else
        $$ = new Attribute($1, $3, @1, @3);
  #endif
    $1.destroy();
    $3.destroy();
}
      | alpha_string TOK_EQUAL color_def_string
{
  //string=r,g,b,a or string=name,a
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_EQUAL);
    if (!$1.had_error) {
        csh.AddCSH_AttrName(@1, $1, COLOR_ATTRNAME);
        csh.AddCSH_AttrValue_CheckAndAddEscapeHint(@3, $3, $1);
        csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE, $1);
    }
    csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME);
  #else
    if ($1.had_error || ($1.had_param && chart.SkipContent()))
        $$ = nullptr;
    else
        $$ = new Attribute($1, $3, @1, @3);
  #endif
    $1.destroy();
}
      | alpha_string TOK_EQUAL TOK_EMPH_PLUS_PLUS color_string
{
  //string=string
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_EQUAL);
    if (!$1.had_error)
        csh.AddCSH_AttrName(@1, $1, COLOR_ATTRNAME);
    if (!$4.had_error)
        csh.AddCSH_AttrValue_CheckAndAddEscapeHint(@3+@4, "++"+$4.str(), $1);
    csh.CheckHintBetweenAndAt(@2, @3+@4, EHintSourceType::ATTR_VALUE, $1);
    csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME);
  #else
    if (chart.SkipContent() || $1.had_error || $4.had_error)
        $$ = nullptr;
    else
        $$ = new Attribute($1, "++"+$4.str(), @1, @3 + @4);
  #endif
    $1.destroy();
    $4.destroy();
}
      | alpha_string TOK_EQUAL TOK_EMPH_PLUS_PLUS
{
  //string=string
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH(@2, COLOR_EQUAL);
    if (!$1.had_error) {
        csh.AddCSH_AttrName(@1, $1, COLOR_ATTRNAME);
        csh.AddCSH_AttrValue_CheckAndAddEscapeHint(@3, "++", $1);
        csh.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE, $1);
    }
    csh.AddCSH_ErrorAfter(@3, "Continue with a color name or definition.");
    csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME);
  #else
    if ($1.had_error || ($1.had_param && chart.SkipContent()))
        $$ = 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.CheckHintBetweenAndAt(@2, @3, EHintSourceType::ATTR_VALUE, $1);
    }
    csh.AddCSH(@2, COLOR_EQUAL);
    csh.AddCSH(@3, COLOR_ATTRVALUE);
    csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME);
  #else
    if ($1.had_error || ($1.had_param && chart.SkipContent()))
        $$ = nullptr;
    else
        $$ = new Attribute($1, $3, @$, @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.CheckHintAfter(@2, EHintSourceType::ATTR_VALUE, $1);
    }
    csh.AddCSH(@2, COLOR_EQUAL);
    csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME);
  #else
    if ($1.had_error || ($1.had_param && chart.SkipContent()))
        $$ = 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)
        $$ = nullptr;
    else
        $$ = new Attribute($1, @$);
  #endif
    $1.destroy();
}
 /* 'string' does not match "++", so we list it separately */
      | TOK_EMPH_PLUS_PLUS
{
  //here we accept non alpha strings for "->" and similar style names
  #ifdef C_S_H_IS_COMPILED
    csh.AddCSH_StyleOrAttrName(@1, "++");
    csh.CheckHintAt(@1, EHintSourceType::ATTR_NAME);
  #else
    $$ = new Attribute("++", @$);
  #endif
};

vertical_shape: TOK_VERTICAL_SHAPE
{
  #ifdef C_S_H_IS_COMPILED
    csh.hint_vertical_shapes = false;
  #endif
  if (CaseInsensitiveEqual($1, "brace")) $$ = Vertical::BRACE;
  else if (CaseInsensitiveEqual($1, "bracket")) $$ = Vertical::BRACKET;
  else if (CaseInsensitiveEqual($1, "range")) $$ = Vertical::RANGE;
  else if (CaseInsensitiveEqual($1, "pointer")) $$ = Vertical::POINTER;
  else {
      $$ = Vertical::ARROW;
      _ASSERT(0);
  }
  (void)$1;
}
      | TOK_COMMAND_BOX
{
  #ifdef C_S_H_IS_COMPILED
    csh.hint_vertical_shapes = false;
  #endif
  $$ = Vertical::BOX;
  (void)$1;
}
      | TOK_COMMAND_BIG
{
  #ifdef C_S_H_IS_COMPILED
    csh.hint_vertical_shapes = false;
  #endif
  $$ = Vertical::ARROW;
  (void)$1;
};



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

reserved_word_string: TOK_MSC
      | TOK_COMMAND_HEADING | TOK_COMMAND_NUDGE | TOK_COMMAND_NEWPAGE
      | TOK_COMMAND_DEFSHAPE | TOK_COMMAND_DEFCOLOR | TOK_COMMAND_DEFSTYLE | TOK_COMMAND_DEFDESIGN
      | TOK_COMMAND_DEFPROC | TOK_COMMAND_REPLAY | TOK_COMMAND_SET
      | TOK_COMMAND_BIG | TOK_COMMAND_BOX | TOK_COMMAND_PIPE
      | TOK_COMMAND_MARK | TOK_COMMAND_MARK_SRC | TOK_COMMAND_MARK_DST
      | TOK_COMMAND_PARALLEL | TOK_COMMAND_OVERLAP
      | TOK_VERTICAL | TOK_VERTICAL_SHAPE | TOK_AT | TOK_LOST | TOK_AT_POS
      | TOK_SHOW | TOK_HIDE | TOK_ACTIVATE | TOK_DEACTIVATE | TOK_BYE
      | TOK_COMMAND_VSPACE | TOK_COMMAND_HSPACE | TOK_COMMAND_SYMBOL | TOK_COMMAND_TEXT | TOK_COMMAND_NOTE
      | TOK_COMMAND_COMMENT | TOK_COMMAND_ENDNOTE | TOK_COMMAND_FOOTNOTE
      | TOK_COMMAND_TITLE | TOK_COMMAND_SUBTITLE
      | TOK_MSCGEN_RBOX | TOK_MSCGEN_ABOX
      | TOK_START | TOK_BEFORE | TOK_END | TOK_AFTER | TOK_JOIN
      | TOK_IF | TOK_THEN | TOK_ELSE
      | symbol_position_string;

symbol_string: TOK_REL_TO
{
    switch ($1) {
    case EArcSymbol::ARC_DOTTED: $$.set(">"); break;
    case EArcSymbol::ARC_DASHED: $$.set(">>"); break;
    case EArcSymbol::ARC_SOLID:  $$.set("->"); break;
    case EArcSymbol::ARC_DOUBLE: $$.set("=>"); break;
    case EArcSymbol::ARC_DBLDBL: $$.set("=>>"); break;
    case EArcSymbol::ARC_DOUBLE2:
  #ifdef C_S_H_IS_COMPILED
        if (csh.mscgen_compat == EMscgenCompat::FORCE_MSCGEN)
  #else
        if (chart.mscgen_compat == EMscgenCompat::FORCE_MSCGEN)
  #endif
            $$.set(":>");
        else
            $$.set("==>");
        break;
    default: _ASSERT(0);
    }
}
      | TOK_REL_FROM
{
    switch ($1) {
    case EArcSymbol::ARC_DOTTED: $$.set("<"); break;
    case EArcSymbol::ARC_DASHED: $$.set("<<"); break;
    case EArcSymbol::ARC_SOLID:  $$.set("<-"); break;
    case EArcSymbol::ARC_DOUBLE: $$.set("<="); break;
    case EArcSymbol::ARC_DBLDBL: $$.set("=>>"); break;
    case EArcSymbol::ARC_DOUBLE2:
  #ifdef C_S_H_IS_COMPILED
        if (csh.mscgen_compat == EMscgenCompat::FORCE_MSCGEN)
  #else
        if (chart.mscgen_compat == EMscgenCompat::FORCE_MSCGEN)
  #endif
            $$.set("<:");
        else
            $$.set("<==");
        break;
    default: _ASSERT(0);
    }
}

      | TOK_REL_BIDIR
{
    switch ($1) {
    case EArcSymbol::ARC_DOTTED: $$.set("<>"); break;
    case EArcSymbol::ARC_DASHED: $$.set("<<>>"); break;
    case EArcSymbol::ARC_SOLID:  $$.set("<->"); break;
    case EArcSymbol::ARC_DOUBLE: $$.set("<=>"); break;
    case EArcSymbol::ARC_DBLDBL: $$.set("=>>"); break;
    case EArcSymbol::ARC_DOUBLE2:
  #ifdef C_S_H_IS_COMPILED
        if (csh.mscgen_compat == EMscgenCompat::FORCE_MSCGEN)
  #else
        if (chart.mscgen_compat == EMscgenCompat::FORCE_MSCGEN)
  #endif
            $$.set("<:>");
        else
            $$.set("<==>");
        break;
    default: _ASSERT(0);
    }
}
      | TOK_REL_MSCGEN
{
    //This can only come in mscgen mode
    switch ($1) {
    case EArcSymbol::ARC_DOTTED: $$.set(".."); break;
    case EArcSymbol::ARC_SOLID:  $$.set("--"); break;
    case EArcSymbol::ARC_DOUBLE: $$.set("=="); break;
    case EArcSymbol::ARC_DOUBLE2: $$.set("::"); break;
    default: _ASSERT(0);
    }
}
      | TOK_SPECIAL_ARC
{
    switch ($1) {
    case EArcSymbol::DIV_DIVIDER:  $$.set("---"); break;
    case EArcSymbol::DIV_DISCO:    $$.set("..."); break;
    case EArcSymbol::DIV_VSPACE:   $$.set("|||"); break;
    default: _ASSERT(0);
    }
}
      | TOK_EMPH
{
    switch ($1) {
    case EArcSymbol::BOX_SOLID:  $$.set("--"); break;
    case EArcSymbol::BOX_DASHED: $$.set("++"); break; //will likely not happen due to special handling in TOK_COLORDEF
    case EArcSymbol::BOX_DOTTED: $$.set(".."); break;
    case EArcSymbol::BOX_DOUBLE: $$.set("=="); break;
    default: _ASSERT(0);
    }
};

alpha_string_single: reserved_word_string;

string_single: symbol_string | TOK_STYLE_NAME;


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.c_str(), @1));
            $$.had_param = true;
        }
    } else {
        $$.init();
        $$.had_param = true;
    }
  #endif
};



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

entity_string_single_or_param: entity_string_single { $$.set($1); }
      | TOK_MULTILINE_QSTRING
      | 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 input buffer stack (in fact the '{' already comes
        //from there), and the parameters are in proc_helper.last_procedure.
        proc_helper.open_context_mode = EScopeOpenMode::NORMAL;
        chart.PushContext(@1, EContextParse::REPARSING);
        chart.MyCurrentContext().parameters = std::move(proc_helper.last_procedure_params);
        chart.MyCurrentContext().starts_procedure = true;
        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
    OptAttr<double> hscale = chart.MyCurrentContext().hscale;
    $$ = chart.PopContext().release();
    if (hscale)
        chart.MyCurrentContext().hscale = hscale;
  #endif
  (void)$1;
};


%%


/* END OF FILE */
