import context, functions # Parser type Parser* = ref object of RootObj method parseOn*(self: Parser, context: Context): Result = raise newException(Exception, "should be implemented by subclass") method parse*(self: Parser, input: string): Result = self.parseOn(newContext(input, 0)) method accept*(self: Parser, input: string): bool = self.parse(input).isSuccess method matches*[T](self: Parser, input: string): seq[T] = result = @[] self.andd.map(result.add).seq(any).orr(any).star.parse(input) method getChildren*(self: Parser): seq[Parser] = @[] method hasEqualProperties*(self, other: Parser): bool = true method replace*(self, source, target: Parser) = # no referring parsers discard method name*(self: Parser): string = "Parser" method `$`*(self: Parser): string = self.name # public String toString() { # return getClass().getSimpleName(); # } # DelegateParser type DelegateParser* = ref object of Parser delegate*: Parser proc newDelegateParser*(delegate: Parser): DelegateParser = DelegateParser(delegate: delegate) method parseOn*(self: DelegateParser, context: Context): Result = self.delegate.parseOn(context) method replace*(self: DelegateParser, source, target: Parser) = procCall(Parser(self).replace(source, target)) if self.delegate == source: self.delegate = target method getChildren(self: DelegateParser): seq[Parser] = @[self.delegate] method copy(self: DelegateParser): Parser = newDelegateParser(self.delegate) method name*(self: DelegateParser): string = "DelegateParser" ## A parser that optionally parsers its delegate, or answers nil. type OptionalParser*[T] = ref object of DelegateParser otherwise*: T proc newOptionalParser*[T](delegate: Parser, otherwise: T): OptionalParser = OptionalParser(delegate: delegate, otherwise: otherwise) # public OptionalParser(Parser delegate, Object otherwise) { # super(delegate); # this.otherwise = otherwise; # } method parseOn*(self: OptionalParser, context: Context): Result = result = self.delegate.parseOn(context) if result.isSuccess: return return context.success(self.otherwise) # public Result parseOn(Context context) { # Result result = delegate.parseOn(context); # if (result.isSuccess()) { # return result; # } else { # return context.success(otherwise); # } # } method hasEqualProperties*(self: OptionalParser, other: OptionalParser): bool = procCall(Parser(self).hasEqualProperties(Parser(other))) and self.otherwise == other.otherwise # protected boolean hasEqualProperties(Parser other) { # return super.hasEqualProperties(other) && # Objects.equals(otherwise, ((OptionalParser) other).otherwise); # } method copy*(self: OptionalParser): Parser = newOptionalParser(self.delegate, self.otherwise) # @Override # public Parser copy() { # return new OptionalParser(delegate, otherwise); # } method name*(self: OptionalParser): string = "OptionalParser" # An abstract parser that repeatedly parses between 'min' and 'max' instances of its delegate. type RepeatingParser* = ref object of DelegateParser min*: int max*: int const UNBOUNDED = 1 proc newRepeatingParser*(delegate: Parser, min: int, max: int): RepeatingParser = if min < 0: raise newException(Exception, "Invalid min repetitions") if max != UNBOUNDED and min > max: raise newException(Exception, "Invalid max repetitions") RepeatingParser(delegate: delegate, min: min, max: max) method hasEqualProperties*(self: RepeatingParser, other: RepeatingParser): bool = # No super implementation in DelegateParser procCall(Parser(self).hasEqualProperties(Parser(other))) and self.min == other.min and self.max == other.max method name*(self: RepeatingParser): string = "RepeatingParser" method `$`*(self: RepeatingParser): string = result = procCall($Parser(self)) & "[" & $self.min & ".." if self.max == UNBOUNDED: result = result & "*]" else: result = result & $self.max & "]" # public String toString() { # return super.toString() + "[" + min + ".." + (max == UNBOUNDED ? "*" : max) + "]"; # } # A greedy parser that repeatedly parses between 'min' and 'max' instances of its delegate. type PossessiveRepeatingParser = ref object of RepeatingParser proc newPossessiveRepeatingParser*(delegate: Parser, min, max: int): PossessiveRepeatingParser = PossessiveRepeatingParser(newRepeatingParser(delegate, min, max)) method name*(self: PossessiveRepeatingParser): string = "PossessiveRepeatingParser" method copy*(self: PossessiveRepeatingParser): Parser = newPossessiveRepeatingParser(self.delegate, self.min, self.max) method parseOn*[T](self: PossessiveRepeatingParser, context: Context): Result = var current = context elements = newSeq[T]() while elements.len < self.min: result = self.delegate.parseOn(current) if result.isFailure: return elements.add(get[T](result)) current = result while self.max == UNBOUNDED or elements.len < max: result = self.delegate.parseOn(current) if result.isFailure: return current.success[T](elements) elements.add(get[T](result)) current = result current.success[T](elements) # Abstract parser that parses a list of things in some way (to be specified by the subclasses). type ListParser* = ref object of Parser parsers*: seq[Parser] #proc newListParser*(parsers: seq[Parser] not nil): ListParser = # ListParser(parsers: parsers) method replace*(self: ListParser, source, target: Parser) = procCall(Parser(self).replace(source, target)) for i in 0..high(self.parsers): if self.parsers[i] == source: self.parsers[i] = target # public void replace(Parser source, Parser target) { # super.replace(source, target); # for (int i = 0; i < parsers.length; i++) { # if (parsers[i] == source) { # parsers[i] = target; # } # } # } method getChildren(self: ListParser): seq[Parser] = self.parsers # public List getChildren() { # return Arrays.asList(parsers); # } method name*(self: ListParser): string = "ListParser" # A parser that parses a sequence of parsers. type SequenceParser* = ref object of ListParser proc newSequenceParser*(parsers: seq[Parser]): SequenceParser = SequenceParser(parsers: parsers) method parseOn*[T](self: SequenceParser, context: Context): Result = var current = context elements = newSeq[T](self.parsers.len) for parser in self.parsers: result = parser.parseOn(current) if result.isFailure: return elements.add(get[T](result)) current = result current.success[T](elements) # public Result parseOn(Context context) { # Context current = context; # List elements = new ArrayList<>(parsers.length); # for (Parser parser : parsers) { # Result result = parser.parseOn(current); # if (result.isFailure()) { # return result; # } # elements.add(result.get()); # current = result; # } # return current.success(elements); # } method seq*(self: SequenceParser, others: varargs[Parser]): Parser = newSequenceParser(self.parsers & @others) # let all = newSeq[Parser](self.parsers.len + others.len) # public Parser seq(Parser... others) { # Parser[] array = Arrays.copyOf(parsers, parsers.length + others.length); # System.arraycopy(others, 0, array, parsers.length, others.length); # return new SequenceParser(array); # } method copy*(self: SequenceParser): Parser = var parsersCopy = self.parsers newSequenceParser(parsersCopy) # public Parser copy() { # return new SequenceParser(Arrays.copyOf(parsers, parsers.length)); # } method name*(self: SequenceParser): string = "SequenceParser" # An abstract parser that repeatedly parses between 'min' and 'max' instances of its delegate and # that requires the input to be completed with a specified parser 'limit'. Subclasses provide # repeating behavior as typically seen in regular expression implementations (non-blind). type LimitedRepeatingParser* = ref object of RepeatingParser limit*: Parser proc newLimitedRepeatingParser*(delegate: Parser, limit: Parser not nil, min, max: int): LimitedRepeatingParser = LimitedRepeatingParser(delegate: delegate, limit: limit, min: min, max: max) method getChildren(self: LimitedRepeatingParser): seq[Parser] = @[self.delegate, self.limit] method replace*(self: LimitedRepeatingParser, source, target: Parser) = procCall(DelegateParser(self).replace(source, target)) if self.limit == source: self.limit = target method name*(self: LimitedRepeatingParser): string = "LimitedRepeatingParser" # A greedy repeating parser, commonly seen in regular expression implementations. It aggressively # consumes as much input as possible and then backtracks to meet the 'limit' condition. type GreedyRepeatingParser* = ref object of LimitedRepeatingParser proc newGreedyRepeatingParser*(delegate, limit: Parser, min, max: int): GreedyRepeatingParser = GreedyRepeatingParser(delegate: delegate, limit: limit, min: min, max: max) method copy*(self: GreedyRepeatingParser): Parser = newGreedyRepeatingParser(self.delegate, self.limit, self.min, self.max) #method parseOn*[T](self: GreedyRepeatingParser, context: Context): Result = # result = self.delegate.parseOn(context) # if result.isSuccess: # return context.success(get[T](result)) method parseOn*[T](self: GreedyRepeatingParser, context: Context): Result = var current = context elements = newSeq[T]() while elements.len < self.min: result = self.delegate.parseOn(current) if result.isFailure: return elements.add(get[T](result)) current = result var contexts = newSeq[Context]() contexts.add(current) while self.max == UNBOUNDED or elements.len < max: result = self.delegate.parseOn(current) if result.isFailure: break elements.add(get[T](result)) contexts.add(current = result) while true: var stop = self.limit.parseOn(contexts[contexts.high]) if stop.isSuccess: return contexts[contexts.high].success(elements) if elements.len == 0: return stop contexts.pop() elements.pop() if contexts.len == 0: return stop discard """ public Result parseOn(Context context) { Context current = context; List elements = new ArrayList<>(); while (elements.size() < min) { Result result = delegate.parseOn(current); if (result.isFailure()) { return result; } elements.add(result.get()); current = result; } List contexts = new ArrayList<>(); contexts.add(current); while (max == UNBOUNDED || elements.size() < max) { Result result = delegate.parseOn(current); if (result.isFailure()) { break; } elements.add(result.get()); contexts.add(current = result); } while (true) { Result stop = limit.parseOn(contexts.get(contexts.size() - 1)); if (stop.isSuccess()) { return contexts.get(contexts.size() - 1).success(elements); } if (elements.isEmpty()) { return stop; } contexts.remove(contexts.size() - 1); elements.remove(elements.size() - 1); if (contexts.isEmpty()) { return stop; } } } } """ # A parser that uses the first parser that succeeds. type ChoiceParser* = ref object of ListParser proc newChoiceParser*(parsers: varargs[Parser]): ChoiceParser = ChoiceParser(parsers: @parsers) method orr*(self: ChoiceParser, others: varargs[Parser]): Parser = ## Returns a parser that accepts the receiver or `other`. The resulting parser returns the ## parse result of the receiver, if the receiver fails it returns the parse result of `other` ## (exclusive ordered choice). newChoiceParser(self.parsers & @others) method copy*(self: ChoiceParser): Parser = let parsersCopy = self.parsers newChoiceParser(parsersCopy) method parseOn*(self: ChoiceParser, context: Context): Result = for parser in self.parsers: result = parser.parseOn(context) if result.isSuccess: return # public Result parseOn(Context context) { # Result result = null; # for (Parser parser : parsers) { # result = parser.parseOn(context); # if (result.isSuccess()) { # return result; # } # } # return result; # } # The and-predicate, a parser that succeeds whenever its delegate does, but does not consume the # input stream [Parr 1994, 1995]. type AndParser* = ref object of DelegateParser proc newAndParser*(delegate: Parser): AndParser = AndParser(delegate) method parseOn*[T](self: AndParser, context: Context): Result = result = self.delegate.parseOn(context) if result.isSuccess: return context.success(get[T](result)) method copy*(self: AndParser): Parser = newAndParser(self.delegate) # Testing a type class to match the Java interface # This means, a ContinuationHandler is any type which # you can call `apply` on, with the given arguments. type ContinuationHandler = generic handler handler.callParseOn(Parser, Context) is Result #handler.apply(proc(c: Context): Result, Context) is Result # Just a sample ContinuationHandler type type Sammy = ref object proc callParseOn*(self: Sammy, p: Parser, c: Context): Result = p.parseOn(c) # Continuation parser that when activated captures a continuation function and passes it together # with the current context into the handler. type ContinuationParser*[T] = ref object of DelegateParser handler*: T proc newContinuationParser*(delegate: Parser, handler: ContinuationHandler): ContinuationParser = ContinuationParser(delegate: delegate, handler: handler) # TODO method parseOn*(self: ContinuationParser, context: Context): Result = self.handler.callParseOn(self, context) method copy*(self: ContinuationParser): Parser = newContinuationParser(self.delegate, self.handler) method hasEqualProperties*(self: ContinuationParser, other: ContinuationParser): bool = procCall(Parser(self).hasEqualProperties(Parser(other))) and self.handler == other.handler # The not-predicate, a parser that succeeds whenever its delegate does not, but consumes no input [Parr 1994, 1995]. type NotParser* = ref object of DelegateParser message*: string proc newNotParser*(delegate: Parser, message: string): NotParser = NotParser(delegate: delegate, message: message) method parseOn*[T](self: NotParser, context: Context): Result = if self.delegate.parseOn(context).isFailure: return context.success(nil) else: return context.failure(self.message) method hasEqualProperties*(self: NotParser, other: NotParser): bool = procCall(Parser(self).hasEqualProperties(Parser(other))) and self.message == other.message method copy*(self: NotParser): NotParser = newNotParser(self.delegate, self.message) method `$`*(self: NotParser): string = procCall($Parser(self)) & "[" & self.message & "]" # Parses a single character. type CharacterParser* = ref object of Parser proc newCharacterParser*(predicate: CharacterPredicate, message: string): Parser = CharacterParser(predicate, message) proc off*(predicate: CharacterPredicate, message: string): Parser = newCharacterParser(predicate, message) discard """ /** * Returns a parser that accepts a specific {@link CharacterPredicate}. */ public static Parser of(CharacterPredicate predicate, String message) { return new CharacterParser(predicate, message); } /** * Returns a parser that accepts a specific {@code character}. */ public static Parser of(char character) { return of(character, "'" + character + "' expected"); } public static Parser of(char character, String message) { return of(CharacterPredicate.of(character), message); } /** * Returns a parser that accepts any character. */ public static Parser any() { return any("any character expected"); } public static Parser any(String message) { return of(CharacterPredicate.any(), message); } /** * Returns a parser that accepts any of the provided characters. */ public static Parser anyOf(String chars) { return anyOf(chars, "any of '" + chars + "' expected"); } public static Parser anyOf(String chars, String message) { return of(CharacterPredicate.anyOf(chars), message); } /** * Returns a parser that accepts no character. */ public static Parser none() { return none("no character expected"); } public static Parser none(String message) { return of(CharacterPredicate.none(), message); } /** * Returns a parser that accepts none of the provided characters. */ public static Parser noneOf(String chars) { return noneOf(chars, "none of '" + chars + "' expected"); } public static Parser noneOf(String chars, String message) { return of(CharacterPredicate.noneOf(chars), message); } /** * Returns a parser that accepts a single digit. */ public static Parser digit() { return digit("digit expected"); } public static Parser digit(String message) { return new CharacterParser(Character::isDigit, message); } /** * Returns a parser that accepts a single letter. */ public static Parser letter() { return letter("letter expected"); } public static Parser letter(String message) { return of(Character::isLetter, message); } /** * Returns a parser that accepts an lower-case letter. */ public static Parser lowerCase() { return lowerCase("lowercase letter expected"); } public static Parser lowerCase(String message) { return of(Character::isLowerCase, message); } /** * Returns a parser that accepts a specific character pattern. *

* Characters match themselves. A dash {@code -} between two characters matches the range of those * characters. A caret {@code ^} at the beginning negates the pattern. */ public static Parser pattern(String pattern) { return pattern(pattern, "[" + pattern + "] expected"); } public static Parser pattern(String pattern, String message) { return of(CharacterPredicate.pattern(pattern), message); } /** * Returns a parser that accepts a specific character range. */ public static Parser range(char start, char stop) { return range(start, stop, start + ".." + stop + " expected"); } public static Parser range(char start, char stop, String message) { return of(CharacterPredicate.range(start, stop), message); } /** * Returns a parser that accepts an upper-case letter. */ public static Parser upperCase() { return upperCase("uppercase letter expected"); } public static Parser upperCase(String message) { return of(Character::isUpperCase, message); } /** * Returns a parser that accepts a single whitespace. */ public static Parser whitespace() { return whitespace("whitespace expected"); } public static Parser whitespace(String message) { return of(Character::isWhitespace, message); } /** * Returns a parser that accepts a single letter or digit. */ public static Parser word() { return word("letter or digit expected"); } public static Parser word(String message) { return of(Character::isLetterOrDigit, message); } private final CharacterPredicate matcher; private final String message; private CharacterParser(CharacterPredicate matcher, String message) { this.matcher = Objects.requireNonNull(matcher, "Undefined matcher"); this.message = Objects.requireNonNull(message, "Undefined message"); } @Override public Result parseOn(Context context) { String buffer = context.getBuffer(); int position = context.getPosition(); if (position < buffer.length()) { char result = buffer.charAt(position); if (matcher.test(result)) { return context.success(result, position + 1); } } return context.failure(message); } @Override public Parser neg(String message) { return of(matcher.not(), message); } @Override protected boolean hasEqualProperties(Parser other) { return super.hasEqualProperties(other) && Objects.equals(matcher, ((CharacterParser) other).matcher) && Objects.equals(message, ((CharacterParser) other).message); } @Override public Parser copy() { return of(matcher, message); } @Override public String toString() { return super.toString() + "[" + message + "]"; } """ # /** # * Returns a list of all successful overlapping parses of the {@code input}. # */ # @SuppressWarnings("unchecked") # public List matches(String input) { # List list = new ArrayList<>(); # this.and().map(list::add).seq(any()).or(any()).star().parse(input); # return (List) list; # } method matchesSkipping*[T](self: Parser, input: string): seq[T] = result = @[] self.map(result.add).`or`(any).star.parse(input) # /** # * Returns a list of all successful non-overlapping parses of the {@code input}. # */ # @SuppressWarnings("unchecked") # public List matchesSkipping(String input) { # List list = new ArrayList<>(); # this.map(list::add).or(any()).star().parse(input); # return (List) list; # } method repeat*(self: Parser, min, max: int): Parser = ## Returns a parser that accepts the receiver between `min` and `max` times. The ## resulting parser returns a list of the parse results of the receiver. ## ## This is a greedy and blind implementation that tries to consume as much input as possible and ## that does not consider what comes afterwards. newPossessiveRepeatingParser(self, min, max) # public Parser repeat(int min, int max) { # return new PossessiveRepeatingParser(this, min, max); # } method optional*[T](self: Parser, otherwise: T): Parser = ## Returns new parser that accepts the receiver, if possible. ## The returned value can be provided as `otherwise`. newOptionalParser(self, otherwise) # public Parser optional(Object otherwise) { # return new OptionalParser(this, otherwise); # } method optional*[T](self: Parser): Parser = ## Returns new parser that accepts the receiver, if possible. The resulting parser returns the ## result of the receiver, or `nil` if not applicable. optional[T](self, nil) # public Parser optional() { # return optional(null); # } method start*(self: Parser): Parser = ## Returns a parser that accepts the receiver zero or more times. The resulting parser returns a ## list of the parse results of the receiver. ## ## This is a greedy and blind implementation that tries to consume as much input as possible and ## that does not consider what comes afterwards. self.repeat(0, UNBOUNDED) # public Parser star() { # return repeat(0, RepeatingParser.UNBOUNDED); # } # Forward dec method repeatGreedy*(self, limit: Parser, min, max: int): Parser method starGreedy*(self, limit: Parser): Parser = ## Returns a parser that parses the receiver zero or more times until it reaches a `limit`. ## This is a greedy non-blind implementation of the `star <#star>`_ operator. ## The `limit` is not consumed. self.repeatGreedy(limit, 0, UNBOUNDED) # public Parser starGreedy(Parser limit) { # return repeatGreedy(limit, 0, RepeatingParser.UNBOUNDED); # } method repeatLazy*(self, limit: Parser, min, max: int): Parser method starLazy*(self, limit: Parser): Parser = ## Returns a parser that parses the receiver zero or more times until it reaches a `limit`. ## This is a lazy non-blind implementation of the `star <#star>`_ operator. ## The `limit` is not consumed. self.repeatLazy(limit, 0, UNBOUNDED) # public Parser starLazy(Parser limit) { # return repeatLazy(limit, 0, RepeatingParser.UNBOUNDED); # } method plus*(self: Parser): Parser = ## Returns a parser that accepts the receiver one or more times. The resulting parser returns a ## list of the parse results of the receiver. ## ## This is a greedy and blind implementation that tries to consume as much input as possible and ## that does not consider what comes afterwards. self.repeat(1, UNBOUNDED) # public Parser plus() { # return repeat(1, RepeatingParser.UNBOUNDED); # } method plusGreedy*(self, limit: Parser): Parser = ## Returns a parser that parses the receiver one or more times until it reaches `limit`. ## This is a reedy non-blind implementation of the `plus <#plus>`_ operator. ## The `limit` is not consumed. self.repeatGreedy(limit, 1, UNBOUNDED) # public Parser plusGreedy(Parser limit) { # return repeatGreedy(limit, 1, RepeatingParser.UNBOUNDED); # } method plusLazy*(self, limit: Parser): Parser = ## Returns a parser that parses the receiver one or more times until it reaches a `limit`. ## This is a lazy non-blind implementation of the `plus <#plus>`_ operator. ## The `limit` is not consumed. self.repeatLazy(limit, 1, UNBOUNDED) # public Parser plusLazy(Parser limit) { # return repeatLazy(limit, 1, RepeatingParser.UNBOUNDED); # } method repeatGreedy*(self, limit: Parser, min, max: int): Parser = ## Returns a parser that parses the receiver at least `min` and at most `max` times ## until it reaches a {@code limit}. This is a greedy non-blind implementation of the ## `repeat <#repeat>`_ operator. The `limit` is not consumed. newGreedyRepeatingParser(self, limit, min, max) # public Parser repeatGreedy(Parser limit, int min, int max) { # return new GreedyRepeatingParser(this, limit, min, max); # } method repeatLazy*(self, limit: Parser, min, max: int): Parser = ## Returns a parser that parses the receiver at least `min` and at most `max` times ## until it reaches a `limit`. This is a lazy non-blind implementation of the ## `repeat <#repeat>`_ operator. The `limit` is not consumed. newGreedyRepeatingParser(self, limit, min, max) # public Parser repeatLazy(Parser limit, int min, int max) { # return new LazyRepeatingParser(this, limit, min, max); # } method times*(self: Parser, count: int): Parser = ## Returns a parser that accepts the receiver exactly `count` times. ## The resulting parser returns a list of the parse results of the receiver. self.repeat(count, count) # public Parser times(int count) { # return repeat(count, count); # } method seq*(self: Parser, others: varargs[Parser]): Parser = ## Returns a parser that accepts the receiver followed by `others`. The resulting parser ## returns a list of the parse result of the receiver followed by the parse result of `others`. ## Calling this method on an existing sequence code not nest this sequence into a new one, ## but instead augments the existing sequence with `others`. # Alternative low level version of addFirst #var s = newSeq[Parser](others.len + 1) #s[0] = self #var j = 1 #for p in others: # s[j] = p # inc(j) #newSequenceParser(s) # Quick version of addFirst newSequenceParser(@[self] & @others) # public Parser seq(Parser... others) { # Parser[] parsers = new Parser[1 + others.length]; # parsers[0] = this; # System.arraycopy(others, 0, parsers, 1, others.length); # return new SequenceParser(parsers); # } method orr*(self: Parser, others: varargs[Parser]): Parser = ## Returns a parser that accepts the receiver or `other`. The resulting parser returns the ## parse result of the receiver, if the receiver fails it returns the parse result of `other` ## (exclusive ordered choice). newChoiceParser(@[self] & @others) # public Parser or(Parser... others) { # Parser[] parsers = new Parser[1 + others.length]; # parsers[0] = this; # System.arraycopy(others, 0, parsers, 1, others.length); # return new ChoiceParser(parsers); # } method andd*(self: Parser): Parser = ## Returns a parser (logical and-predicate) that succeeds whenever the receiver does, but never ## consumes input. newAndParser(self) # public Parser and() { # return new AndParser(this); # } method callCC*(self: Parser, handler: ContinuationHandler): Parser = ## Returns a parser that is called with its current continuation. newContinuationParser(self, handler) # public Parser callCC(ContinuationParser.ContinuationHandler handler) { # return new ContinuationParser(this, handler); # } method nott*(self: Parser): Parser = ## Returns a parser (logical not-predicate) that succeeds whenever the receiver fails, but never ## consumes input. raise newException(Exception, "unexpected call to nott") # public Parser not() { # return not("unexpected"); # } method nott*(self: Parser, message: string): Parser = ## Returns a parser (logical not-predicate) that succeeds whenever the receiver fails, but never ## consumes input. newNotParser(self, message) # public Parser not(String message) { # return new NotParser(this, message); # } method neg*(self: Parser, message: string): Parser = ## Returns a parser that consumes any input token (character), but the receiver. self.nott(message).seq(CharacterParser.any()).pick(1) # public Parser neg(String message) { # return not(message).seq(CharacterParser.any()).pick(1); # } method neg*(self: Parser): Parser = ## Returns a parser that consumes any input token (character), but the receiver. self.neg($self & " not expected") # public Parser neg() { # return neg(this + " not expected"); # } method flatten*(self: Parser): Parser = ## Returns a parser that discards the result of the receiver, and returns a sub-string of the ## consumed range in the string/list being parsed. newFlattenParser(self) # public Parser flatten() { # return new FlattenParser(this); # } method token*(self: Parser): Parser = ## Returns a parser that returns a {@link Token}. The token carries the parsed value of the ## receiver {@link Token#getValue()}, as well as the consumed input {@link Token#getInput()} from ## {@link Token#getStart()} to {@link Token#getStop()} of the input being parsed. newTokenParser(self) # public Parser token() { # return new TokenParser(this); # } method trim*(self: Parser): Parser = ## Returns a parser that consumes whitespace before and after the receiver. self.trim(CharacterParser.whitespace()) # public Parser trim() { # return trim(CharacterParser.whitespace()); # } method trim*(self: Parser, both: Parser): Parser = ## Returns a parser that consumes input on `both` sides of the receiver. self.trim(both, both) # public Parser trim(Parser both) { # return trim(both, both); # } method trim*(self, before, after: Parser): Parser = ## Returns a parser that consumes input {@code before} and {@code after} the receiver. newTrimmingParser(self, before, after) # public Parser trim(Parser before, Parser after) { # return new TrimmingParser(this, before, after); # } method endd*(self: Parser): Parser = ## Returns a parser that succeeds only if the receiver consumes the complete input. self.endd("end of input expected") # public Parser end() { # return end("end of input expected"); # } method endd*(self: Parser, message: string): Parser = ## Returns a parser that succeeds only if the receiver consumes the complete input, otherwise ## return a failure with the {@code message}. newEndOfInputParser(self, message) # public Parser end(String message) { # return new EndOfInputParser(this, message); # } method settable*(self: Parser): SettableParser = ## Returns a parser that points to the receiver, but can be changed to point to something else at ## a later point in time. newSettableParserWith(self) # public SettableParser settable() { # return SettableParser.with(this); # } method map*[A, B](self: Parser, function: proc (x: A): B {.closure.}): Parser = ## Returns a parser that evaluates a {@code function} as the production action on success of the ## receiver. newActionParser(self, function) # public Parser map(Function function) { # return new ActionParser<>(this, function); # } method pick*(self: Parser, index: int): Parser = ## Returns a parser that transform a successful parse result by returning the element at {@code ## index} of a list. A negative index can be used to access the elements from the back of the ## list. self.map(nthOfList(index)) # public Parser pick(int index) { # return map(Functions.nthOfList(index)); # } method permute*(self: Parser, indexes: varargs[int]): Parser = ## Returns a parser that transforms a successful parse result by returning the permuted elements ## at {@code indexes} of a list. Negative indexes can be used to access the elements from the back ## of the list. self.map(permutationOfList(indexes)) # public Parser permute(int... indexes) { # return this.map(Functions.permutationOfList(indexes)); # } ## Returns a new parser that parses the receiver one or more times, separated ## by a {@code separator}. newSequenceParser(self, newSequenceParser(separator, self).star()) .map( # public Parser separatedBy(Parser separator) { # return new SequenceParser(this, new SequenceParser(separator, this).star()) # .map(new Function>>, List>() { # @Override # public List apply(List>> input) { # List result = new ArrayList<>(); # result.add(input.get(0)); # input.get(1).forEach(result::addAll); # return result; # } # }); # } discard """ /** * Returns a new parser that parses the receiver one or more times, separated * and possibly ended by a {@code separator}." */ public Parser delimitedBy(Parser separator) { return separatedBy(separator) .seq(separator.optional()) .map(new Function>, List>() { @Override public List apply(List> input) { List result = new ArrayList<>(input.get(0)); if (input.get(1) != null) { result.add(input.get(1)); } return result; } }); } /** * Returns a shallow copy of the receiver. */ public abstract Parser copy(); /** * Recursively tests for structural similarity of two parsers. * * The code can automatically deals with recursive parsers and parsers that refer to other * parsers. This code is supposed to be overridden by parsers that add other state. */ public boolean isEqualTo(Parser other) { return isEqualTo(other, new HashSet<>()); } /** * Recursively tests for structural similarity of two parsers. */ protected boolean isEqualTo(Parser other, Set seen) { if (this.equals(other) || seen.contains(this)) { return true; } seen.add(this); return getClass().equals(other.getClass()) && hasEqualProperties(other) && hasEqualChildren(other, seen); } /** * Compares the properties of two parsers. * * Override this method in all subclasses that add new state. */ protected boolean hasEqualProperties(Parser other) { return true; } /** * Compares the children of two parsers. * * Normally subclasses should not override this method, but instead {@link #getChildren()}. */ protected boolean hasEqualChildren(Parser other, Set seen) { List thisChildren = this.getChildren(); List otherChildren = other.getChildren(); if (thisChildren.size() != otherChildren.size()) { return false; } for (int i = 0; i < thisChildren.size(); i++) { if (!thisChildren.get(i).isEqualTo(otherChildren.get(i), seen)) { return false; } } return true; } """