import 'package:meta/meta.dart'; import '../core/parser.dart'; import '../parser/combinator/settable.dart'; import '../reflection/iterable.dart'; import 'group.dart'; import 'utils.dart'; /// A builder that allows the simple definition of expression grammars with /// prefix, postfix, and left- and right-associative infix operators. /// /// The following code creates the empty expression builder producing values of /// type [num]: /// /// ```dart /// final builder = ExpressionBuilder(); /// ``` /// /// Every [ExpressionBuilder] needs to define at least one primitive type to /// parse. In this example these are the literal numbers. The mapping function /// converts the string input into an actual number. /// /// ```dart /// builder.primitive(digit() /// .plus() /// .seq(char('.').seq(digit().plus()).optional()) /// .flatten() /// .trim() /// .map(num.parse)); /// ```dart /// /// Then we define the operator-groups in descending precedence. The highest /// precedence have parentheses. The mapping function receives both the opening /// parenthesis, the value, and the closing parenthesis as arguments: /// /// ```dart /// builder.group().wrapper( /// char('(').trim(), char(')').trim(), (left, value, right) => value); /// ``` /// /// Then come the normal arithmetic operators. We are using /// [cascade notation](https://dart.dev/guides/language/language-tour#cascade-notation) /// to define multiple operators on the same precedence-group. The mapping /// functions receive both, the terms and the parsed operator in the order they /// appear in the parsed input: /// /// ```dart /// // Negation is a prefix operator. /// builder.group().prefix(char('-').trim(), (operator, value) => -value); /// /// // Power is right-associative. /// builder.group().right(char('^').trim(), (left, operator, right) => math.pow(left, right)); /// /// // Multiplication and addition are left-associative, multiplication has /// // higher priority than addition. /// builder.group() /// ..left(char('*').trim(), (left, operator, right) => left * right) /// ..left(char('/').trim(), (left, operator, right) => left / right); /// builder.group() /// ..left(char('+').trim(), (left, operator, right) => left + right) /// ..left(char('-').trim(), (left, operator, right) => left - right); /// ``` /// /// Finally we can build the parser: /// /// ```dart /// final parser = builder.build(); /// ``` /// /// After executing the above code we get an efficient parser that correctly /// evaluates expressions like: /// /// ```dart /// parser.parse('-8'); // -8 /// parser.parse('1+2*3'); // 7 /// parser.parse('1*2+3'); // 5 /// parser.parse('8/4/2'); // 2 /// parser.parse('2^2^3'); // 256 /// ``` class ExpressionBuilder { final List> _primitives = []; final List> _groups = []; final SettableParser _loopback = undefined(); /// The parser for this expression builder. Can be used to loop back to this /// parser. Parser get loopback => _loopback; /// Defines a new primitive, literal, or value [parser]. void primitive(Parser parser) => _primitives.add(parser); /// Creates a new group of operators that share the same priority. @useResult ExpressionGroup group() { final group = ExpressionGroup(_loopback); _groups.add(group); return group; } /// Builds the expression parser. @useResult Parser build() { assert(_primitives.isNotEmpty, 'At least one primitive parser expected'); final parser = _groups.fold>( buildChoice(_primitives), (parser, group) => group.build(parser), ); // Replace all uses of `_loopback` with `parser`. Do not use `resolve()` // because that might try to resolve unrelated parsers outside of the scope // of the `ExpressionBuilder` and cause infinite recursion. for (final parent in allParser(parser)) { parent.replace(_loopback, parser); } // Also update the `_loopback` parser, just in case somebody keeps a reference // to it (not that anybody should do that). _loopback.set(parser); return parser; } }