grammar PS;

options {
    language=Python3;
}

WS: [ \t\r\n]+ -> skip;
DOLLAR_SIGN: '\\$' -> skip;

ADD: '+';
SUB: '-';
MUL: '*';
DIV: '/' | '\\over';

L_PAREN: '(';
R_PAREN: ')';
L_GROUP: '\\lgroup';
R_GROUP: '\\rgroup';
L_BRACE: '{';
R_BRACE: '}';
L_BRACE_VISUAL: '\\{';
R_BRACE_VISUAL: '\\}';
L_BRACE_CMD: '\\lbrace';
R_BRACE_CMD: '\\rbrace';
L_BRACKET: '[';
R_BRACKET: ']';
L_BRACK: '\\lbrack';
R_BRACK: '\\rbrack';

BAR: '|';
L_VERT: '\\lvert';
R_VERT: '\\rvert';
VERT: '\\vert';

NORM: '\\|';

L_FLOOR: '\\lfloor';
R_FLOOR: '\\rfloor';
LL_CORNER: '\\llcorner';
LR_CORNER: '\\lrcorner';

L_CEIL: '\\lceil';
R_CEIL: '\\rceil';
UL_CORNER: '\\ulcorner';
UR_CORNER: '\\urcorner';

L_LEFT: '\\left';
R_RIGHT: '\\right';
ML_LEFT: '\\mleft';
MR_RIGHT: '\\mright';

//functions
FUNC_LIM:  '\\lim';
LIM_APPROACH_SYM: '\\to' | '\\rightarrow' | '\\Rightarrow' | '\\longrightarrow' | '\\Longrightarrow';
FUNC_INT:  '\\int';
FUNC_SUM:  '\\sum';
FUNC_PROD: '\\prod';

FUNC_LOG:  '\\log';
FUNC_LN:   '\\ln';
FUNC_EXP: '\\exp';
FUNC_SIN:  '\\sin';
FUNC_COS:  '\\cos';
FUNC_TAN:  '\\tan';
FUNC_CSC:  '\\csc';
FUNC_SEC:  '\\sec';
FUNC_COT:  '\\cot';

FUNC_ARCSIN: '\\arcsin';
FUNC_ARCCOS: '\\arccos';
FUNC_ARCTAN: '\\arctan';
FUNC_ARCCSC: '\\arccsc';
FUNC_ARCSEC: '\\arcsec';
FUNC_ARCCOT: '\\arccot';

FUNC_SINH: '\\sinh';
FUNC_COSH: '\\cosh';
FUNC_TANH: '\\tanh';
FUNC_ARSINH: '\\arsinh';
FUNC_ARCOSH: '\\arcosh';
FUNC_ARTANH: '\\artanh';
FUNC_ARCSINH: '\\arcsinh';
FUNC_ARCCOSH: '\\arccosh';
FUNC_ARCTANH: '\\arctanh';

FUNC_ARSINH_NAME: 'arsinh';
FUNC_ARCSINH_NAME: 'arcsinh';
FUNC_ARCOSH_NAME: 'arcosh';
FUNC_ARCCOSH_NAME: 'arccosh';
FUNC_ARTANH_NAME: 'artanh';
FUNC_ARCTANH_NAME: 'arctanh';
FUNC_GCD_NAME: 'gcd';
FUNC_LCM_NAME: 'lcm';
FUNC_FLOOR_NAME: 'floor';
FUNC_CEIL_NAME: 'ceil';

FUNC_SQRT: '\\sqrt';
FUNC_GCD: '\\gcd';
FUNC_LCM: '\\lcm';
FUNC_FLOOR: '\\floor';
FUNC_CEIL: '\\ceil';
FUNC_MAX: '\\max';
FUNC_MIN: '\\min';

FUNC_DET: '\\det';

FUNC_EYE_NAME: 'eye';
FUNC_ZEROS_NAME: 'zeros';
FUNC_ONES_NAME: 'ones';
FUNC_COLS_NAME: 'cols';
FUNC_ROWS_NAME: 'rows';
FUNC_DIAG_NAME: 'diag';
FUNC_NORM_NAME: 'norm';
FUNC_RANK_NAME: 'rank';
FUNC_TRACE_NAME: 'trace' | 'tr';
FUNC_RREF_NAME: 'rref';
FUNC_HSTACK_NAME: 'hstack';
FUNC_VSTACK_NAME: 'vstack';
FUNC_ORTHOGONALIZE_NAME: 'orth' | 'ortho' | 'orthogonal' | 'orthogonalize';
FUNC_NULLSPACE_NAME: 'nullspace';
FUNC_DIAGONALIZE_NAME: 'eig' | 'eigen' | 'diagonalize';
FUNC_EIGENVALS_NAME: 'eigenvals' | 'eigenvalues';
FUNC_EIGENVECTORS_NAME: 'eigenvects' | 'eigenvectors';
FUNC_SVD_NAME: 'svd' | 'SVD';

//commands
CMD_TIMES: '\\times';
CMD_CDOT:  '\\cdot';
CMD_DIV:   '\\div';
CMD_FRAC:  '\\frac';
CMD_BINOM: '\\binom' | '\\tbinom' | '\\dbinom';
CMD_CHOOSE: '\\choose';
CMD_MOD: '\\mod';

CMD_MATHIT: '\\mathit';

CMD_OPERATORNAME: '\\operatorname';

//matrix test
MATRIX_TYPE_MATRIX: 'matrix';
MATRIX_TYPE_PMATRIX: 'pmatrix';
MATRIX_TYPE_BMATRIX: 'bmatrix';
MATRIX_TYPE_DET: 'vmatrix';
MATRIX_TYPES: MATRIX_TYPE_MATRIX | MATRIX_TYPE_PMATRIX | MATRIX_TYPE_BMATRIX;
CMD_MATRIX_START: '\\begin' L_BRACE MATRIX_TYPES R_BRACE;
CMD_MATRIX_END: '\\end' L_BRACE MATRIX_TYPES R_BRACE;
CMD_DET_START: '\\begin' L_BRACE MATRIX_TYPE_DET R_BRACE;
CMD_DET_END: '\\end' L_BRACE MATRIX_TYPE_DET R_BRACE;
MATRIX_DEL_COL: '&';
MATRIX_DEL_ROW: '\\\\';

UNDERSCORE: '_';
CARET: '^';
COLON: ':';
SEMICOLON: ';';
COMMA: ',';
PERIOD: '.';

fragment WS_CHAR: [ \t\r\n];
DIFFERENTIAL: 'd' WS_CHAR*? ([a-zA-Z] | '\\' [a-zA-Z]+) (UNDERSCORE ( DIGIT | L_BRACE DIGIT+ R_BRACE))?;

EXP_E: 'e' | '\\exponentialE';
E_NOTATION_E: 'E';
LETTER_NO_E: [a-df-zA-DF-Z]; // exclude e for exponential function and e notation
fragment LETTER: [a-zA-Z];

DIGIT: [0-9];

MATRIX_XRIGHTARROW: '\\xrightarrow' | '\\xRightarrow';
TRANSFORM_EXCHANGE: '<->' | '<=>' | '\\leftrightarrow' | '\\Leftrightarrow';

number: DIGIT+ (',' DIGIT DIGIT DIGIT)* (PERIOD DIGIT+)? | PERIOD DIGIT+;
//    DIGIT+ (COMMA DIGIT DIGIT DIGIT)*
//    | DIGIT* (COMMA DIGIT DIGIT DIGIT)* PERIOD DIGIT+;

e_notation: number E_NOTATION_E (SUB | ADD)? DIGIT+;

IN: '\\in';
ASSIGNMENT: '=';
EQUAL: '==' | '\\equiv';
LT: '<';
LTE: '\\leq' | '\\le' | '\\leqslant';
GT: '>';
GTE: '\\geq' | '\\ge' | '\\geqslant';
UNEQUAL: '!=' | '!==' | '\\ne' | '\\neq' | '\\not\\equiv';

BANG: '!';

PERCENT_SIGN: '\\%';
percent_number: number PERCENT_SIGN;

//Excludes some letters for use as e.g. constants in SYMBOL
fragment GREEK_LETTER:
    '\\char"000391' | //Alpha
    '\\alpha' |
    '\\char"000392' | //Beta
    '\\beta' |
    '\\Gamma' |
    '\\gamma' |
    '\\Delta' |
    '\\delta' |
    '\\char"000190' | //Epsilon
    '\\epsilon' |
    '\\varepsilon' |
    '\\char"000396' | //Zeta
    '\\zeta' |
    '\\char"000397' | //Eta
    '\\eta' |
    '\\Theta' |
    '\\theta' |
    '\\vartheta' |
    '\\char"000399' | //Iota
    '\\iota' |
    '\\char"00039A' | //Kappa
    '\\kappa' |
    '\\Lambda' |
    '\\lambda' |
    '\\char"00039C' | //Mu
    '\\mu' |
    '\\char"00039D' | //Nu
    '\\nu' |
    '\\Xi' |
    '\\xi' |
    '\\char"00039F' | //Omicron
    '\\omicron' |
    '\\Pi' |
    '\\varpi' |
    '\\char"0003A1' | //Rho
    '\\rho' |
    '\\varrho' |
    '\\Sigma' |
    '\\sigma' |
    '\\varsigma' |
    '\\char"0003A4' | //Tau
    '\\tau' |
    '\\Upsilon' |
    '\\upsilon' |
    '\\Phi' |
    '\\phi' |
    '\\varphi' |
    '\\char"0003A7' | //Chi
    '\\chi' |
    '\\Psi' |
    '\\psi' |
    '\\Omega' |
    '\\omega';

GREEK_CMD: GREEK_LETTER [ ]?;

fragment OTHER_SYMBOL:
    '\\Bbbk'  |
    '\\wp'  |
    '\\nabla'  |
    '\\bigstar'  |
    '\\angle'  |
    '\\nexists'  |
    '\\diagdown'  |
    '\\measuredangle'  |
    '\\eth'  |
    '\\diagup'  |
    '\\sphericalangle'  |
    '\\clubsuit'  |
    '\\varnothing'  |
    '\\Diamond'  |
    '\\complement'  |
    '\\diamondsuit'  |
    '\\imath'  |
    '\\Finv'  |
    '\\triangledown'  |
    '\\heartsuit'  |
    '\\jmath'  |
    '\\Game'  |
    '\\triangle'  |
    '\\spadesuit'  |
    '\\ell'  |
    '\\hbar'  |
    '\\vartriangle'  |
    '\\hslash'  |
    '\\blacklozenge'  |
    '\\lozenge'  |
    '\\blacksquare'  |
    '\\mho'  |
    '\\blacktriangle'  |
    '\\sharp'  |
    '\\prime'  |
    '\\Im'  |
    '\\flat'  |
    '\\square'  |
    '\\backprime'  |
    '\\Re'  |
    '\\natural'  |
    '\\surd'  |
    '\\circledS';
OTHER_SYMBOL_CMD: OTHER_SYMBOL [ ]?;

fragment PI: '\\pi';
fragment INFTY_CMD: '\\infty';
fragment PARTIAL_CMD: '\\partial';
fragment INFTY: INFTY_CMD | DOLLAR_SIGN INFTY_CMD | INFTY_CMD PERCENT_SIGN;
fragment EMPTYSET: '\\emptyset';
SYMBOL: PI | PARTIAL_CMD | INFTY | EMPTYSET;

fragment VARIABLE_CMD: '\\variable';
fragment VARIABLE_SYMBOL: (GREEK_CMD | OTHER_SYMBOL_CMD | LETTER | DIGIT)+ (UNDERSCORE ((L_BRACE (GREEK_CMD | OTHER_SYMBOL_CMD | LETTER | DIGIT | COMMA)+ R_BRACE) | (GREEK_CMD | OTHER_SYMBOL_CMD | LETTER | DIGIT)))?;
VARIABLE: VARIABLE_CMD L_BRACE VARIABLE_SYMBOL R_BRACE PERCENT_SIGN?;

//collection of accents
accent_symbol:
    '\\acute'  |
    '\\bar'  |
    '\\overline'  |
    '\\breve'  |
    '\\check'  |
    '\\widecheck'  |
    '\\dot'  |
    '\\ddot'  |
    '\\grave'  |
    '\\hat'  |
    '\\tilde'  |
    '\\widetilde'  |
    '\\vec'  |
    '\\overrightarrow'  |
    '\\bm'  |
    '\\boldsymbol'  |
    '\\text'  |
    '\\textit'  |
    '\\mathbb'  |
    '\\mathbin'  |
    '\\mathbf'  |
    '\\mathcal'  |
    '\\mathclap'  |
    '\\mathclose'  |
    '\\mathellipsis'  |
    '\\mathfrak'  |
    '\\mathinner'  |
    '\\mathit'  |
    '\\mathnormal'  |
    '\\mathop'  |
    '\\mathopen'  |
    '\\mathord'  |
    '\\mathpunct'  |
    '\\mathrel'  |
    '\\mathring'  |
    '\\mathrlap'  |
    '\\mathrm'  |
    '\\mathscr'  |
    '\\mathsf'  |
    '\\mathsterling'  |
    '\\mathtt';

math: relation | relation_list;

transpose: '^T' | '^{T}' | '^\\top' | '^{\\top}' | '\'';

transform_atom: LETTER_NO_E UNDERSCORE (number | L_BRACE number R_BRACE);
transform_scale: (expr | group | ADD | SUB) transform_atom;
transform_swap: transform_atom TRANSFORM_EXCHANGE transform_atom;
transform_assignment: transform_atom transform_scale;
elementary_transform: transform_assignment | transform_scale | transform_swap;
elementary_transforms: elementary_transform (COMMA elementary_transform)*;

matrix:
    CMD_MATRIX_START
    matrix_row (MATRIX_DEL_ROW matrix_row)* MATRIX_DEL_ROW?
    CMD_MATRIX_END
    (MATRIX_XRIGHTARROW (L_BRACKET elementary_transforms R_BRACKET)? L_BRACE elementary_transforms R_BRACE)?;

det:
    CMD_DET_START
    matrix_row (MATRIX_DEL_ROW matrix_row)* MATRIX_DEL_ROW?
    CMD_DET_END;

matrix_row:
    expr (MATRIX_DEL_COL expr)*;

relation:
    relation (IN | ASSIGNMENT | EQUAL | LT | LTE | GT | GTE | UNEQUAL) relation
    | expr;

relation_list:
    relation_list_content
    | L_BRACKET relation_list_content R_BRACKET
    | L_BRACE relation_list_content R_BRACE
    | L_BRACE_VISUAL relation_list_content R_BRACE_VISUAL
    | L_LEFT L_BRACKET relation_list_content R_RIGHT R_BRACKET
    | L_LEFT L_BRACE_VISUAL relation_list_content R_RIGHT R_BRACE_VISUAL
    | ML_LEFT L_BRACKET relation_list_content MR_RIGHT R_BRACKET
    | ML_LEFT L_BRACE_VISUAL relation_list_content MR_RIGHT R_BRACE_VISUAL;

relation_list_content:
    relation COMMA relation (COMMA relation)*
    | relation SEMICOLON relation (SEMICOLON relation)*;

equality:
    expr (EQUAL | ASSIGNMENT) expr;

expr: additive;

additive:
    additive (ADD | SUB) additive
    | mp;

// mult part
mp:
    mp (MUL | CMD_TIMES | CMD_CDOT | DIV | CMD_DIV | COLON | CMD_MOD) mp
    | unary;

mp_nofunc:
    mp_nofunc (MUL | CMD_TIMES | CMD_CDOT | DIV | CMD_DIV | COLON | CMD_MOD) mp_nofunc
    | unary_nofunc;

unary:
    (ADD | SUB) unary
    | postfix+;

unary_nofunc:
    (ADD | SUB) unary_nofunc
    | postfix postfix_nofunc*;

postfix: exp postfix_op*;
postfix_nofunc: exp_nofunc postfix_op*;
postfix_op: BANG | eval_at | transpose;

eval_at:
    BAR (eval_at_sup | eval_at_sub | eval_at_sup eval_at_sub);

eval_at_sub:
    UNDERSCORE L_BRACE
    (expr | equality)
    R_BRACE;

eval_at_sup:
    CARET L_BRACE
    (expr | equality)
    R_BRACE;

exp:
    exp CARET (atom | L_BRACE expr R_BRACE) subexpr?
    | comp;

exp_nofunc:
    exp_nofunc CARET (atom | L_BRACE expr R_BRACE) subexpr?
    | comp_nofunc;

comp:
    group
    | norm_group
    | abs_group
    | floor_group
    | ceil_group
    | func
    | atom
    | binom
    | matrix
    | det;

comp_nofunc:
    group
    | norm_group
    | abs_group
    | floor_group
    | ceil_group
    | atom
    | binom
    | matrix
    | det;

group:
    L_PAREN expr R_PAREN
    | L_GROUP expr R_GROUP
    | L_BRACE expr R_BRACE
    | L_BRACE_VISUAL expr R_BRACE_VISUAL
    | L_BRACE_CMD expr R_BRACE_CMD
    | L_BRACKET expr R_BRACKET
    | L_BRACK expr R_BRACK
    | L_LEFT L_PAREN expr R_RIGHT R_PAREN
    | L_LEFT L_GROUP expr R_RIGHT R_GROUP
    | L_LEFT L_BRACE expr R_RIGHT R_BRACE
    | L_LEFT L_BRACE_VISUAL expr R_RIGHT R_BRACE_VISUAL
    | L_LEFT L_BRACE_CMD expr R_RIGHT R_BRACE_CMD
    | L_LEFT L_BRACKET expr R_RIGHT R_BRACKET
    | L_LEFT L_BRACK expr R_RIGHT R_BRACK
    | ML_LEFT L_PAREN expr MR_RIGHT R_PAREN
    | ML_LEFT L_GROUP expr MR_RIGHT R_GROUP
    | ML_LEFT L_BRACE expr MR_RIGHT R_BRACE
    | ML_LEFT L_BRACE_VISUAL expr MR_RIGHT R_BRACE_VISUAL
    | ML_LEFT L_BRACE_CMD expr MR_RIGHT R_BRACE_CMD
    | ML_LEFT L_BRACKET expr MR_RIGHT R_BRACKET
    | ML_LEFT L_BRACK expr MR_RIGHT R_BRACK;


norm_group:
    NORM expr NORM
    | L_LEFT NORM expr R_RIGHT NORM
    | ML_LEFT NORM expr MR_RIGHT NORM;


abs_group:
    BAR expr BAR
    | L_VERT expr R_VERT
    | VERT expr VERT
    | L_LEFT BAR expr R_RIGHT BAR
    | L_LEFT L_VERT expr R_RIGHT R_VERT
    | L_LEFT VERT expr R_RIGHT VERT
    | ML_LEFT BAR expr MR_RIGHT BAR
    | ML_LEFT L_VERT expr MR_RIGHT R_VERT
    | ML_LEFT VERT expr MR_RIGHT VERT;


floor_group:
    L_FLOOR expr R_FLOOR
    | LL_CORNER expr LR_CORNER
    | L_LEFT L_FLOOR expr R_RIGHT R_FLOOR
    | L_LEFT LL_CORNER expr R_RIGHT LR_CORNER
    | ML_LEFT L_FLOOR expr MR_RIGHT R_FLOOR
    | ML_LEFT LL_CORNER expr MR_RIGHT LR_CORNER;


ceil_group:
    L_CEIL expr R_CEIL
    | UL_CORNER expr UR_CORNER
    | L_LEFT L_CEIL expr R_RIGHT R_CEIL
    | L_LEFT UL_CORNER expr R_RIGHT UR_CORNER
    | ML_LEFT L_CEIL expr MR_RIGHT R_CEIL
    | ML_LEFT UL_CORNER expr MR_RIGHT UR_CORNER;


//indicate an accent
accent:
    accent_symbol
    L_BRACE base=expr R_BRACE;

atom_expr_no_supexpr: (LETTER_NO_E | GREEK_CMD | OTHER_SYMBOL_CMD | accent) subexpr?;
atom_expr: (LETTER_NO_E | GREEK_CMD | OTHER_SYMBOL_CMD | accent) (supexpr subexpr | subexpr supexpr | subexpr | supexpr)?;
atom: atom_expr | SYMBOL | e_notation | percent_number | number | DIFFERENTIAL | mathit | frac | VARIABLE;

mathit: CMD_MATHIT L_BRACE mathit_text R_BRACE;
mathit_text: (LETTER_NO_E | E_NOTATION_E | EXP_E)+;

/*
frac:
    CMD_FRAC L_BRACE
    upper=expr
    R_BRACE L_BRACE
    lower=expr
    R_BRACE;
*/

frac: CMD_FRAC (upperd = DIGIT | L_BRACE upper = expr R_BRACE)
    (lowerd = DIGIT | L_BRACE lower = expr R_BRACE);


//a binomial expression
binom:
    L_BRACE upper=expr CMD_CHOOSE lower=expr R_BRACE
    | CMD_BINOM L_BRACE upper=expr R_BRACE L_BRACE lower=expr R_BRACE;

func_normal_functions_single_arg:
    FUNC_LOG | FUNC_LN | FUNC_EXP
    | FUNC_SIN | FUNC_COS | FUNC_TAN
    | FUNC_CSC | FUNC_SEC | FUNC_COT
    | FUNC_ARCSIN | FUNC_ARCCOS | FUNC_ARCTAN
    | FUNC_ARCCSC | FUNC_ARCSEC | FUNC_ARCCOT
    | FUNC_SINH | FUNC_COSH | FUNC_TANH
    | FUNC_ARSINH | FUNC_ARCOSH | FUNC_ARTANH
    | FUNC_ARCSINH | FUNC_ARCCOSH | FUNC_ARCTANH
    | FUNC_FLOOR | FUNC_CEIL | FUNC_DET;

func_normal_functions_multi_arg:
    FUNC_GCD | FUNC_LCM | FUNC_MAX | FUNC_MIN;

func_operator_names_single_arg:
    FUNC_ARSINH_NAME | FUNC_ARCOSH_NAME | FUNC_ARTANH_NAME
    | FUNC_ARCSINH_NAME | FUNC_ARCCOSH_NAME | FUNC_ARCTANH_NAME
    | FUNC_FLOOR_NAME | FUNC_CEIL_NAME | FUNC_EYE_NAME | FUNC_RANK_NAME | FUNC_TRACE_NAME
    | FUNC_RREF_NAME | FUNC_NULLSPACE_NAME | FUNC_DIAGONALIZE_NAME | FUNC_NORM_NAME
    | FUNC_EIGENVALS_NAME | FUNC_EIGENVECTORS_NAME | FUNC_SVD_NAME | FUNC_COLS_NAME | FUNC_ROWS_NAME;

func_operator_names_multi_arg:
    FUNC_GCD_NAME | FUNC_LCM_NAME | FUNC_ZEROS_NAME | FUNC_ORTHOGONALIZE_NAME
    | FUNC_ONES_NAME | FUNC_DIAG_NAME | FUNC_HSTACK_NAME | FUNC_VSTACK_NAME;

func_normal_single_arg:
    (func_normal_functions_single_arg)
    |
    (CMD_OPERATORNAME L_BRACE func_operator_name=func_operator_names_single_arg R_BRACE);

func_normal_multi_arg:
    (func_normal_functions_multi_arg)
    |
    (CMD_OPERATORNAME L_BRACE func_operator_name=func_operator_names_multi_arg R_BRACE);

func:
    func_normal_single_arg
    (subexpr? supexpr? | supexpr? subexpr?)
    (L_LEFT? L_PAREN func_single_arg R_RIGHT? R_PAREN | ML_LEFT? L_PAREN func_single_arg MR_RIGHT? R_PAREN | func_single_arg_noparens)
    
    | func_normal_multi_arg
    (subexpr? supexpr? | supexpr? subexpr?)
    (L_LEFT? L_PAREN func_multi_arg R_RIGHT? R_PAREN | ML_LEFT? L_PAREN func_multi_arg MR_RIGHT? R_PAREN | func_multi_arg_noparens)

    | atom_expr_no_supexpr supexpr?
    L_LEFT? (L_PAREN | L_BRACKET) func_common_args (R_PAREN | R_BRACKET) R_RIGHT?
    | atom_expr_no_supexpr supexpr?
    L_BRACE L_LEFT? (L_PAREN | L_BRACKET) func_common_args (R_PAREN | R_BRACKET) R_RIGHT? R_BRACE

    | FUNC_INT
    (subexpr supexpr | supexpr subexpr | (UNDERSCORE L_BRACE R_BRACE) (CARET L_BRACE R_BRACE) | (CARET L_BRACE R_BRACE) (UNDERSCORE L_BRACE R_BRACE) )?
    (additive? DIFFERENTIAL | frac | additive)

    | FUNC_SQRT
    (L_BRACKET root=expr R_BRACKET)?
    (base_d=DIGIT | L_BRACE base=expr R_BRACE)

    | (FUNC_SUM | FUNC_PROD)
    (subeq supexpr | supexpr subeq)
    mp
    | FUNC_LIM limit_sub mp
    | EXP_E supexpr?; //Exponential function e^x

args: (expr ',' args) | expr;

func_common_args: atom | (expr ',') | (expr ',' args);

limit_sub:
    UNDERSCORE L_BRACE
    (LETTER_NO_E | GREEK_CMD | OTHER_SYMBOL_CMD)
    LIM_APPROACH_SYM
    expr (CARET L_BRACE (ADD | SUB) R_BRACE)?
    R_BRACE;

func_single_arg: expr;
func_single_arg_noparens: mp_nofunc;

func_multi_arg: expr | (expr ',' func_multi_arg);
func_multi_arg_noparens: mp_nofunc;

subexpr: UNDERSCORE (atom | L_BRACE (expr | args) R_BRACE);
supexpr: CARET (atom | L_BRACE expr R_BRACE);

subeq: UNDERSCORE L_BRACE equality R_BRACE;
supeq: UNDERSCORE L_BRACE equality R_BRACE;

