module Parcom VERSION = "0.1.0" class ParserException < Exception end struct 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) @p1.parse(tokens) rescue ParserException @p2.parse(tokens) end end end