Skip to content

Instantly share code, notes, and snippets.

@rgatti
Created May 28, 2020 14:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rgatti/7c3dd1cc2758891a3af32a6a2609bdd9 to your computer and use it in GitHub Desktop.
Save rgatti/7c3dd1cc2758891a3af32a6a2609bdd9 to your computer and use it in GitHub Desktop.
Groovy Combinator example
// Abstract parser type
abstract class Parser<T> {
/* Parser implementation ...
*
* A parser only has to implement the base case, ie. how to parse a single
* element.
*/
abstract ParserResult<T> parse(String string)
// Processor
static <T> ParserResult<T> run(Parser<T> parser, String string) {
return parser.parse(string)
}
// Combinators
/* Combine a first parser followed by another parser. The result on success
* is a Set of parsed characters and the remaining String.
*
* parserA.followed(parserB) -> parserAB
*/
Parser<Set> followed(Parser<T> by) {
def self = this
return new Parser<Set>() {
ParserResult<Set> parse(String string) {
def firstResult = self.parse(string)
if(firstResult instanceof FailureResult)
return failure(firstResult.message)
def secondResult = by.parse(firstResult.string)
if(secondResult instanceof FailureResult)
return failure(secondResult.message)
return success([firstResult.value, secondResult.value], secondResult.string)
}
}
}
// Convenience functions
FailureResult<T> failure(String message) {
return new FailureResult<T>(message: message)
}
SuccessResult<T> success(T value, String string) {
return new SuccessResult<T>(value: value, string: string)
}
}
// Result types
interface ParserResult<T> { }
class SuccessResult<T> implements ParserResult<T> {
T value
String string
String toString() { "SuccessResult($value, $string)" }
}
class FailureResult<T> implements ParserResult<T> {
String message
String toString() { "FailureResult($message)" }
}
// Parser declaration
// Create a parser capable of comparing a single character.
Parser<Character> characterParser(String c) {
return new Parser<String>() {
ParserResult<String> parse(String string) {
if(!string) return failure("String is empty")
def firstChar = string[0]
if(firstChar == c) return success(c, string.substring(1))
return failure("$firstChar from $string is not $c")
}
}
}
// Test
def parserA = characterParser 'A'
println Parser.run(parserA, "ABC")
println Parser.run(parserA, "ZBC")
println Parser.run(parserA, "")
def parserB = characterParser 'B'
def parserAB = parserA.followed(parserB)
println Parser.run(parserAB, "ABC")
println Parser.run(parserAB, "ZBC")
println Parser.run(parserAB, "AZC")
println Parser.run(parserAB, "")
@forsythetony
Copy link

Interesting

Output for those curious

SuccessResult(A, BC)
FailureResult(Z from ZBC is not A)
FailureResult(String is empty)
SuccessResult([A, B], C)
FailureResult(Z from ZBC is not A)
FailureResult(Z from ZC is not B)
FailureResult(String is empty)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment