diff options
| author | Matthew Hall <hallmatthew314@gmail.com> | 2023-03-07 01:30:02 +1300 |
|---|---|---|
| committer | Matthew Hall <hallmatthew314@gmail.com> | 2023-03-07 01:30:02 +1300 |
| commit | 14013d04f5023e68b5a804c3735e909241795878 (patch) | |
| tree | d4624f3dd19db4ba5600bfc72d91500ea58f73c0 /src | |
Initial
Diffstat (limited to 'src')
| -rw-r--r-- | src/parcom.cr | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/src/parcom.cr b/src/parcom.cr new file mode 100644 index 0000000..0f907c4 --- /dev/null +++ b/src/parcom.cr @@ -0,0 +1,108 @@ +module Parcom + VERSION = "0.1.0" + + class ParserException < Exception + end + + class Result(T, V) + getter tokens, value + + def initialize(@tokens : Array(T), @value : V) + end + end + + abstract class Parser(T, V) + abstract def parse(tokens : Array(T)) : Result(T, V) + + def assert(f : V -> Bool) + Assert.new(self, f) + end + + def |(other : Parser(T, V)) : Parser(T, V) + Alt.new(self, other) + end + end + + class Flunk(T, V) < Parser(T, V) + def parse(tokens) : Result(T, V) + raise ParserException.new("Flunk: parsing failed") + end + end + + class AnyToken(T) < Parser(T, T) + def parse(tokens : Array(T)) : Result(T, T) + if tokens.empty? + raise ParserException.new("AnyToken: input was empty") + else + Result.new(tokens[1..], tokens[0]) + end + end + end + + class Eof(T) < Parser(T, Nil) + def parse(tokens : Array(T)) : Result(T, Nil) + if tokens.empty? + Result.new(tokens, nil) + else + raise ParserException.new("Eof: input was not empty") + end + end + end + + class Peek(T, V) < Parser(T, V) + def initialize(@p : Parser(T, V)) + end + + def parse(tokens : Array(T)) : Result(T, V) + result = @p.parse(tokens) + Result.new(tokens, result.value) + end + end + + class Assert(T, V) < Parser(T, V) + def initialize(@p : Parser(T, V), @f : V -> Bool) + end + + def parse(tokens : Array(T)) : Result(T, V) + result = @p.parse(tokens) + + unless @f.call(result.value) + raise ParserException.new("Assert: predicate failed") + end + + result + end + end + + class Satisfy(T) < Parser(T, T) + def initialize(@f : T -> Bool) + end + + def parse(tokens : Array(T)) : Result(T, T) + AnyToken(T).new.assert(@f).parse(tokens) + end + end + + class Token(T) < Parser(T, T) + def initialize(@expected : T) + end + + def parse(tokens : Array(T)) : Result(T, T) + Satisfy(T).new(->(x : T) { x == @expected }).parse(tokens) + end + end + + class Alt(T, V) < Parser(T, V) + def initialize(@p1 : Parser(T, V), @p2 : Parser(T, V)) + end + + def parse(tokens : Array(T)) : Result(T, V) + begin + @p1.parse(tokens) + rescue ParserException + @p2.parse(tokens) + end + end + end +end + |
