Hi there! I’m writing a plugin for TI-BASIC, where expressions can have implied multiplication, i.e. 2A is valid to write instead of 2*A. The rules are that implied multiplication is allowed after a trailing unary operator (for examle, factorial) or after an operand, like a number or variable. How can I achieve that in the expression parser from Grammar-Kit? My current expression setup looks like this, and can be found here:
expr ::= or_group
| and_group
| comparison_group
| addition_group
| multiplication_group
| combinations_group
| negation_group
| exponential_group
| modifiers_group
| primary_group
// Private rules to define operators with the same priority
private or_group ::= or_expr | xor_expr
private and_group ::= and_expr
private comparison_group ::= eq_expr | ne_expr | gt_expr | ge_expr | lt_expr | le_expr
private addition_group ::= plus_expr | minus_expr
private multiplication_group ::= mul_expr | div_expr | implied_mul_expr
private combinations_group ::= npr_expr | ncr_expr
private negation_group ::= negation_expr
private exponential_group ::= pow_expr | xroot_expr
private modifiers_group ::= radian_expr | degree_expr | inverse_expr | pow2_expr | transpose_expr | pow3_expr | factorial_expr
private primary_group ::= literal_expr | func_expr | func_optional_expr | paren_expr
// Public rules for each expression
// Precedence 9
or_expr ::= expr OR expr
xor_expr ::= expr XOR expr
// Precedence 8
and_expr ::= expr AND expr
// Precedence 7
eq_expr ::= expr EQ expr
ne_expr ::= expr NE expr
gt_expr ::= expr GT expr
ge_expr ::= expr GE expr
lt_expr ::= expr LT expr
le_expr ::= expr LE expr
// Precedence 6
plus_expr ::= expr PLUS expr
minus_expr ::= expr MINUS expr
// Precedence 5
mul_expr ::= expr TIMES expr
div_expr ::= expr DIVIDE expr
implied_mul_expr ::= implied_mul_arg implied_mul_arg+
private implied_mul_arg ::= NUMBER | MATH_VARIABLE | EXPR_FUNCTIONS_NO_ARGS | ANS_VARIABLE | LIST_VARIABLE | custom_list_with_l | EQUATION_VARIABLE | SIMPLE_VARIABLE | WINDOW_VARIABLE | COLOR_VARIABLE | paren_expr | func_optional_expr | func_expr
// Precedence 4
npr_expr ::= expr NPR expr
ncr_expr ::= expr NCR expr
// Precedence 3.5
negation_expr ::= NEG expr
// Precedence 3
pow_expr ::= expr POW expr { rightAssociative=true }
xroot_expr ::= expr XROOT expr
// Precedence 2
radian_expr ::= expr TO_RADIAN
degree_expr ::= expr TO_DEGREE
inverse_expr ::= expr INVERSE
pow2_expr ::= expr POW2
transpose_expr ::= expr TRANSPOSE
pow3_expr ::= expr POW3
factorial_expr ::= expr FACTORIAL
// Precedence 1
paren_expr ::= LPAREN expr optional_rparen
func_expr ::= (EXPR_FUNCTIONS_WITH_ARGS | DIM) LPAREN <<list expr>> optional_rparen { pin=1 }
func_optional_expr ::= EXPR_FUNCTIONS_OPTIONAL_ARGS [LPAREN <<list expr>> optional_rparen] { pin=1 }
// Base literal
literal_expr ::= list_index | matrix_index | EXPR_FUNCTIONS_NO_ARGS | ANS_VARIABLE | LIST_VARIABLE | custom_list_with_l | EQUATION_VARIABLE | STRING_VARIABLE | SIMPLE_VARIABLE | WINDOW_VARIABLE | MATRIX_VARIABLE | COLOR_VARIABLE | MATH_VARIABLE | NUMBER | STRING | anonymous_list | anonymous_matrix
list_index ::= (LIST_VARIABLE | ANS_VARIABLE | custom_list_with_l) LPAREN expr optional_rparen { pin=2 }
matrix_index ::= (MATRIX_VARIABLE | ANS_VARIABLE) LPAREN expr COMMA expr optional_rparen { pin=2 }
anonymous_list ::= LCURLY <<list expr>> [RCURLY] { pin=1 }
anonymous_matrix ::= LBRACKET <<list anonymous_matrix_row>> [RBRACKET] { pin=1 }
anonymous_matrix_row ::= LBRACKET <<list expr>> [RBRACKET] { pin=1 }
The problem is within the implied_mul_expr rule, which now only allows hardcoded operands. But constructs like A!5 (which is considered valid syntax for TI-BASIC) is not matched with this rule. I tried to experiment with things like implied_mul_expr ::= (modifiers_group | primary_group) (modifiers_group | primary_group) et alia, but that freezes the IDE and so far I couldn’t get it working sadly. Is it possible to achieve implied multiplication with the correct rules?
Besides, now it treats the implied_mul_expr as an atom, so in the tree of operators it gets matched before other atoms:
// Expression root: expr
// Operator priority table:
// 0: BINARY(or_expr) BINARY(xor_expr)
// 1: BINARY(and_expr)
// 2: BINARY(eq_expr) BINARY(ne_expr) BINARY(gt_expr) BINARY(ge_expr)
// BINARY(lt_expr) BINARY(le_expr)
// 3: BINARY(plus_expr) BINARY(minus_expr)
// 4: BINARY(mul_expr) BINARY(div_expr) ATOM(implied_mul_expr) <<----- SEE THIS
// 5: BINARY(npr_expr) BINARY(ncr_expr)
// 6: PREFIX(negation_expr)
// 7: BINARY(pow_expr) BINARY(xroot_expr)
// 8: POSTFIX(radian_expr) POSTFIX(degree_expr) POSTFIX(inverse_expr) POSTFIX(pow2_expr)
// POSTFIX(transpose_expr) POSTFIX(pow3_expr) POSTFIX(factorial_expr)
// 9: ATOM(literal_expr) ATOM(func_expr) ATOM(func_optional_expr) PREFIX(paren_expr)
Which means that implied multiplication matches before, say, a list index, like L1(3 becomes implied multiplication rather than a list index.
Any help is greatly appreciated ![]()