diff options
| author | Matthew Hall <hallmatthew314@gmail.com> | 2023-03-16 23:11:23 +1300 |
|---|---|---|
| committer | Matthew Hall <hallmatthew314@gmail.com> | 2023-03-16 23:11:23 +1300 |
| commit | f023ca56dbf9372464afe0060270fcef85271db0 (patch) | |
| tree | b045bebfbb8ac7e6208e5703cb0f53808b14fe89 /src/parcom.cr | |
| parent | b274828831fec26cd8b3089ffef14cb96ce2de2f (diff) | |
I'm too tired for this
Diffstat (limited to 'src/parcom.cr')
| -rw-r--r-- | src/parcom.cr | 56 |
1 files changed, 53 insertions, 3 deletions
diff --git a/src/parcom.cr b/src/parcom.cr index d12b011..ffa5654 100644 --- a/src/parcom.cr +++ b/src/parcom.cr @@ -68,9 +68,43 @@ module Parcom def initialize(@tokens : Tokens(T), @value : U) end + + def map(f : U -> V) : Result(T, V) forall V + Result.new(@tokens, f.call(@value)) + end + + def map(&block : U -> V) : Result(T, V) forall V + map(block) + end end - class Parser(T, U) + # A parser defines a process of extracting some sort of data + # from a stream of tokens. + # `Parser` objects wrap a function/block that accepts a `Tokens` object, + # and either returns a `Result`, or raises a `ParserFail`. + # This struct also defines a handful of methods for modifying a parser's + # behavior. + # + # The function that is wrapped by a parser should: + # 1. Have the type `Tokens(T) -> Result(T, U)` + # 1. Indicate failure by raising a `ParserFail` + # 1. Not introduce side-effects + # + # Instead of inheriting from `Parser`, custom parsers should be + # defined using a method that generates the parser directly: + # ``` + # struct Foo(T, U) < Parser(T, Array(U)) + # def parse(tokens) + # # This will cause headaches + # end + # end + # + # # Do this instead: + # def foo(t : T.class, u : U.class, p : Parser(T, U)) : Parser(T, U) forall T, U + # Parser(T, U).new("Foo") { |tokens| "your code here" } + # end + # ``` + struct Parser(T, U) getter name def initialize(@name : String, @f : Tokens(T) -> Result(T, U)) @@ -80,6 +114,11 @@ module Parcom @f = block end + # Changes the `name` property and returns `self`. + # This should be used to specify a custom name: + # ``` + # a = Basic.token(Char, 'a').named("letter a") + # ``` def named(name : String) : self @name = name self @@ -89,7 +128,7 @@ module Parcom @f.call(tokens) end - def parse(tokens : Tokens(T)) : Result(T, U)? + def parse?(tokens : Tokens(T)) : Result(T, U)? parse(tokens) rescue nil @@ -97,7 +136,7 @@ module Parcom def assert(f : U -> Bool) : Parser(T, U) p = self - Parser.new("#{p.name} assertion") do |tokens| + Parser.new("#{p.name} (assertion)") do |tokens| result = p.parse(tokens) unless f.call(r.value) raise ParserFail.new("Assertion failed for value #{r.value}") @@ -109,6 +148,17 @@ module Parcom def assert(&block : U -> Bool) : Parser(T, U) assert(block) end + + def map(f : U -> T) : Parser(T, V) forall V + p = self + Parser(T, V).new("#{p.name} (mapped)") do |tokens| + p.parse(tokens).map(f) + end + end + + def map(&block : U -> T) : Parser(T, V) forall V + map(block) + end end end |
