Created
May 28, 2020 14:19
-
-
Save rgatti/7c3dd1cc2758891a3af32a6a2609bdd9 to your computer and use it in GitHub Desktop.
Groovy Combinator example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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, "") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Interesting
Output for those curious