import 'package:meta/meta.dart'; import '../core/parser.dart'; import '../parser/action/map.dart'; import '../parser/combinator/optional.dart'; import '../parser/combinator/sequence.dart'; import '../parser/repeater/possessive.dart'; import '../parser/repeater/separated.dart'; import 'result.dart'; import 'utils.dart'; /// Models a group of operators of the same precedence. class ExpressionGroup { @internal ExpressionGroup(this._loopback); /// Loopback parser used to establish the recursive expressions. final Parser _loopback; /// Defines a new wrapper using [left] and [right] parsers, that are typically /// used for parenthesis. Evaluates the [callback] with the parsed `left` /// delimiter, the `value` and `right` delimiter. void wrapper( Parser left, Parser right, T Function(L left, T value, R right) callback, ) => _wrapper.add((left, _loopback, right).toSequenceParser().map3(callback)); Parser _buildWrapper(Parser inner) => buildChoice([..._wrapper, inner]); final List> _wrapper = []; /// Adds a prefix operator [parser]. Evaluates the [callback] with the parsed /// `operator` and `value`. void prefix(Parser parser, T Function(O operator, T value) callback) => _prefix.add( parser.map( (operator) => ExpressionResultPrefix(operator, callback), ), ); Parser _buildPrefix(Parser inner) => _prefix.isEmpty ? inner : (buildChoice(_prefix).star(), inner).toSequenceParser().map2( (prefix, value) => prefix.reversed.fold(value, (each, result) => result.call(each)), ); final List>> _prefix = []; /// Adds a postfix operator [parser]. Evaluates the [callback] with the parsed /// `value` and `operator`. void postfix(Parser parser, T Function(T value, O operator) callback) => _postfix.add( parser.map( (operator) => ExpressionResultPostfix(operator, callback), ), ); Parser _buildPostfix(Parser inner) => _postfix.isEmpty ? inner : (inner, buildChoice(_postfix).star()).toSequenceParser().map2( (value, postfix) => postfix.fold(value, (each, result) => result.call(each)), ); final List>> _postfix = []; /// Adds a right-associative operator [parser]. Evaluates the [callback] with /// the parsed `left` term, `operator`, and `right` term. void right( Parser parser, T Function(T left, O operator, T right) callback, ) => _right.add( parser.map((operator) => ExpressionResultInfix(operator, callback)), ); Parser _buildRight(Parser inner) => _right.isEmpty ? inner : inner .plusSeparated(buildChoice(_right)) .map( (sequence) => sequence.foldRight( (left, result, right) => result.call(left, right), ), ); final List>> _right = []; /// Adds a left-associative operator [parser]. Evaluates the [callback] with /// the parsed `left` term, `operator`, and `right` term. void left( Parser parser, T Function(T left, O operator, T right) callback, ) => _left.add( parser.map((operator) => ExpressionResultInfix(operator, callback)), ); Parser _buildLeft(Parser inner) => _left.isEmpty ? inner : inner .plusSeparated(buildChoice(_left)) .map( (sequence) => sequence.foldLeft( (left, result, right) => result.call(left, right), ), ); final List>> _left = []; /// Makes the group optional and instead return the provided [value]. void optional(T value) { assert(!_optional, 'At most one optional value expected'); _optionalValue = value; _optional = true; } Parser _buildOptional(Parser inner) => _optional ? inner.optionalWith(_optionalValue) : inner; late T _optionalValue; bool _optional = false; // Internal helper to build the group of parsers. @internal Parser build(Parser inner) => _buildOptional( _buildLeft(_buildRight(_buildPostfix(_buildPrefix(_buildWrapper(inner))))), ); }