Class Parser
The grammar, in the order of increasing binding strength:
expr := term { ('+' | '-') term }
term := factor { ('*' | '/') factor }
factor := ('+' | '-') factor | primary
primary := ( NUMBER | '(' expr ')' | call ) [ unit ]
call := ('min' | 'max') '(' expr ',' expr { ',' expr } ')'
| 'clamp' '(' expr ',' expr ',' expr ')'
Precedence of the unit suffix falls out for free. A postfix suffix binds tighter than any binary operator, and because scaling IS multiplication, 2*50% and (2*50)% are arithmetically identical. The only observable case is additive: 5+5% is 5+(5*s) while (5+5)% is (5+5)*s, which is exactly what parentheses are for.
Rules that have to be written down. One suffix per primary, so 10%w px is a syntax error; a bare number is px; a suffix may follow a parenthesised group, as in (5+5)%; and px is the identity scale so the emitter emits no opcode for it.
Variadic in the grammar, PAIRWISE in the opcodes. min(a,b,c,d) emits a b MIN2 c MIN2 d MIN2, and clamp(lo,val,hi) emits val hi MIN2 lo MAX2. This is what keeps the evaluation stack bounded by nesting rather than by argument count, which is what lets the client run on a fixed stack it never grows. There is deliberately no variadic opcode with an arity operand: that would destroy the bound.
Errors. Exactly one error is reported, the first, with the offset and length of the offending span in the ORIGINAL source string. There is no error recovery and no multiple-error reporting. An unclosed parenthesis reports the offset of the OPENING parenthesis rather than end-of-input, which is why the parser keeps a stack of open parenthesis offsets. The wording of the previous implementation's messages is preserved wherever the same condition exists, so that the Designer's messages do not change gratuitously.
- Author:
- Christopher Mindus
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final intThe maximum number of simultaneously open parentheses.Constructor Summary
ConstructorsMethod Summary
Field Details
MAX_NEST
public static final int MAX_NESTThe maximum number of simultaneously open parentheses. A function call's opening parenthesis counts toward the same limit as a grouping one.This is a human-comprehensibility limit and is the one that must not move. Ten open parentheses is already past what anyone can read; beyond it the author is told to rewrite, which is a better outcome than a formula nobody can follow.
Formula.MAX_STACK_DEPTHis DERIVED from this value rather than chosen independently, so that the two cannot drift apart. They did once: the stack was hardcoded at 20 against a believed worst case ofnest+2, when the true cost is TWO slots per nesting level, and a formula obeying this rule could then be refused for exceeding a limit this rule says nothing about. Change this constant and the stack follows automatically.- See Also:
Constructor Details
Parser
Creates a parser for a formula source string.- Parameters:
src- The formula source, exactly as the author wrote it.- Throws:
FormulaException- If the source cannot even be tokenised.
Method Details
parse
Parses the whole source and returns the compiled formula.- Returns:
- The compiled formula.
- Throws:
FormulaException- On any syntax or semantic error, carrying the offset and length of the offending span in the original source.