aboutsummaryrefslogtreecommitdiff
path: root/src/parcom.cr
diff options
context:
space:
mode:
Diffstat (limited to 'src/parcom.cr')
-rw-r--r--src/parcom.cr108
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
+