diff options
| author | Matthew Hall <hallmatthew314@gmail.com> | 2023-03-16 20:36:03 +1300 |
|---|---|---|
| committer | Matthew Hall <hallmatthew314@gmail.com> | 2023-03-16 20:36:03 +1300 |
| commit | b274828831fec26cd8b3089ffef14cb96ce2de2f (patch) | |
| tree | ff5927b85a59b4d85c9e4aa269a475a7a37a54a0 /src | |
| parent | 77c370d27be174e0b036b33d1469e84e67a7153a (diff) | |
Second rewrite attempt, this one should work, monkaS
Diffstat (limited to 'src')
| -rw-r--r-- | src/__OLD_parcom.cr | 84 | ||||
| -rw-r--r-- | src/__OLD_parcom/alt.cr (renamed from src/parcom/alt.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/any_token.cr (renamed from src/parcom/any_token.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/assert.cr (renamed from src/parcom/assert.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/at_least.cr (renamed from src/parcom/at_least.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/at_most.cr (renamed from src/parcom/at_most.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/between.cr (renamed from src/parcom/between.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/eof.cr (renamed from src/parcom/eof.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/exactly.cr (renamed from src/parcom/exactly.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/first_of.cr (renamed from src/parcom/first_of.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/flunk.cr (renamed from src/parcom/flunk.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/left.cr (renamed from src/parcom/left.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/many.cr (renamed from src/parcom/many.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/map.cr (renamed from src/parcom/map.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/optional.cr (renamed from src/parcom/optional.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/parser.cr (renamed from src/parcom/parser.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/peek.cr (renamed from src/parcom/peek.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/phrase.cr (renamed from src/parcom/phrase.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/plus.cr (renamed from src/parcom/plus.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/recover.cr (renamed from src/parcom/recover.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/right.cr (renamed from src/parcom/right.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/satisfy.cr (renamed from src/parcom/satisfy.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/sep_by.cr (renamed from src/parcom/sep_by.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/sequence.cr (renamed from src/parcom/sequence.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/some.cr (renamed from src/parcom/some.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/token.cr (renamed from src/parcom/token.cr) | 0 | ||||
| -rw-r--r-- | src/__OLD_parcom/token_seq.cr (renamed from src/parcom/token_seq.cr) | 0 | ||||
| -rw-r--r-- | src/parcom.cr | 56 | ||||
| -rw-r--r-- | src/parcom/basic.cr | 10 |
29 files changed, 137 insertions, 13 deletions
diff --git a/src/__OLD_parcom.cr b/src/__OLD_parcom.cr new file mode 100644 index 0000000..ddb2e50 --- /dev/null +++ b/src/__OLD_parcom.cr @@ -0,0 +1,84 @@ +require "./parcom/*" + +module Parcom + VERSION = "0.1.0" + + # A ParserFail exception should be raised by `Parser#parse` when + # a parse attempt is unsuccessful. + # Raising this exception in the `#parse` method of a Parser "Foo" + # usually follows this pattern to allow for error tracing: + # + # ``` + # class Foo(T, V) < Parser(T, V) + # def parse(tokens : Tokens(T)) : Result(T, V) + # helper.parse(tokens) + # rescue ex : ParserFail + # raise ParserFail.new("Foo: #{ex.message}") + # end + # ``` + class ParserFail < Exception + end + + # `Tokens` is an `Array` wrapper struct to store the input + # stream of one or more `Parser` objects. + # A `Tokens` can be created from any `Iterable`, along with + # `String` objects using a special constructor. + struct Tokens(T) + getter tokens + + # Constructs a `Tokens(Char)` from a `String`. + def self.from_string(s : String) : Tokens(Char) + Tokens.new(s.chars) + end + + # Constructs a `Tokens` from an `Iterable`. + def initialize(ts : Iterable(T)) + if ts.responds_to?(:to_a) + @tokens = ts.to_a + else + @tokens = [] of T + ts.each { |t| @tokens << t } + end + end + + # Exposes `Array#[](Int)`. + def [](index : Int) : T + @tokens[index] + end + + # Exposes `Array#[](Int, Int)`, but wraps the returned array in a new `Tokens`. + def [](start : Int, count : Int) : Tokens(T) + Tokens.new(@tokens[start, count]) + end + + # Exposes `Array#[](Range)`, but wraps the returned array in a new `Tokens`. + def [](range : Range) : Tokens(T) + Tokens.new(@tokens[range]) + end + + # Like `#[]`, but returns `nil` instead of raising an `IndexError`. + def []?(*args) + self.[](*args) + rescue IndexError + nil + end + + # Exposes `Array#empty?`. + def empty? : Bool + @tokens.empty? + end + end + + # A `Result` stores a `Tokens` object and a parsed value, + # and is effectively used to store the state of a parser chain. + # This is used instead of a `Tuple` or `NamedTuple` because: + # 1. This is more idiomatic than a `Tuple`. + # 2. Crystal does not support generic named tuples. + struct Result(T, V) + getter tokens, value + + def initialize(@tokens : Tokens(T), @value : V) + end + end +end + diff --git a/src/parcom/alt.cr b/src/__OLD_parcom/alt.cr index dedd41d..dedd41d 100644 --- a/src/parcom/alt.cr +++ b/src/__OLD_parcom/alt.cr diff --git a/src/parcom/any_token.cr b/src/__OLD_parcom/any_token.cr index 1f65bfc..1f65bfc 100644 --- a/src/parcom/any_token.cr +++ b/src/__OLD_parcom/any_token.cr diff --git a/src/parcom/assert.cr b/src/__OLD_parcom/assert.cr index 5a3e621..5a3e621 100644 --- a/src/parcom/assert.cr +++ b/src/__OLD_parcom/assert.cr diff --git a/src/parcom/at_least.cr b/src/__OLD_parcom/at_least.cr index 2fb8dcf..2fb8dcf 100644 --- a/src/parcom/at_least.cr +++ b/src/__OLD_parcom/at_least.cr diff --git a/src/parcom/at_most.cr b/src/__OLD_parcom/at_most.cr index b70164f..b70164f 100644 --- a/src/parcom/at_most.cr +++ b/src/__OLD_parcom/at_most.cr diff --git a/src/parcom/between.cr b/src/__OLD_parcom/between.cr index 05519e4..05519e4 100644 --- a/src/parcom/between.cr +++ b/src/__OLD_parcom/between.cr diff --git a/src/parcom/eof.cr b/src/__OLD_parcom/eof.cr index 650da56..650da56 100644 --- a/src/parcom/eof.cr +++ b/src/__OLD_parcom/eof.cr diff --git a/src/parcom/exactly.cr b/src/__OLD_parcom/exactly.cr index a34fd0d..a34fd0d 100644 --- a/src/parcom/exactly.cr +++ b/src/__OLD_parcom/exactly.cr diff --git a/src/parcom/first_of.cr b/src/__OLD_parcom/first_of.cr index c4077eb..c4077eb 100644 --- a/src/parcom/first_of.cr +++ b/src/__OLD_parcom/first_of.cr diff --git a/src/parcom/flunk.cr b/src/__OLD_parcom/flunk.cr index 00a0bd6..00a0bd6 100644 --- a/src/parcom/flunk.cr +++ b/src/__OLD_parcom/flunk.cr diff --git a/src/parcom/left.cr b/src/__OLD_parcom/left.cr index 201f497..201f497 100644 --- a/src/parcom/left.cr +++ b/src/__OLD_parcom/left.cr diff --git a/src/parcom/many.cr b/src/__OLD_parcom/many.cr index a734c63..a734c63 100644 --- a/src/parcom/many.cr +++ b/src/__OLD_parcom/many.cr diff --git a/src/parcom/map.cr b/src/__OLD_parcom/map.cr index 34961b5..34961b5 100644 --- a/src/parcom/map.cr +++ b/src/__OLD_parcom/map.cr diff --git a/src/parcom/optional.cr b/src/__OLD_parcom/optional.cr index b368d4e..b368d4e 100644 --- a/src/parcom/optional.cr +++ b/src/__OLD_parcom/optional.cr diff --git a/src/parcom/parser.cr b/src/__OLD_parcom/parser.cr index ca350df..ca350df 100644 --- a/src/parcom/parser.cr +++ b/src/__OLD_parcom/parser.cr diff --git a/src/parcom/peek.cr b/src/__OLD_parcom/peek.cr index 2b6f657..2b6f657 100644 --- a/src/parcom/peek.cr +++ b/src/__OLD_parcom/peek.cr diff --git a/src/parcom/phrase.cr b/src/__OLD_parcom/phrase.cr index 1996fd4..1996fd4 100644 --- a/src/parcom/phrase.cr +++ b/src/__OLD_parcom/phrase.cr diff --git a/src/parcom/plus.cr b/src/__OLD_parcom/plus.cr index 53c9b4f..53c9b4f 100644 --- a/src/parcom/plus.cr +++ b/src/__OLD_parcom/plus.cr diff --git a/src/parcom/recover.cr b/src/__OLD_parcom/recover.cr index 378f8d6..378f8d6 100644 --- a/src/parcom/recover.cr +++ b/src/__OLD_parcom/recover.cr diff --git a/src/parcom/right.cr b/src/__OLD_parcom/right.cr index a0489b1..a0489b1 100644 --- a/src/parcom/right.cr +++ b/src/__OLD_parcom/right.cr diff --git a/src/parcom/satisfy.cr b/src/__OLD_parcom/satisfy.cr index 9734635..9734635 100644 --- a/src/parcom/satisfy.cr +++ b/src/__OLD_parcom/satisfy.cr diff --git a/src/parcom/sep_by.cr b/src/__OLD_parcom/sep_by.cr index fa19027..fa19027 100644 --- a/src/parcom/sep_by.cr +++ b/src/__OLD_parcom/sep_by.cr diff --git a/src/parcom/sequence.cr b/src/__OLD_parcom/sequence.cr index 6a05cde..6a05cde 100644 --- a/src/parcom/sequence.cr +++ b/src/__OLD_parcom/sequence.cr diff --git a/src/parcom/some.cr b/src/__OLD_parcom/some.cr index a2e3563..a2e3563 100644 --- a/src/parcom/some.cr +++ b/src/__OLD_parcom/some.cr diff --git a/src/parcom/token.cr b/src/__OLD_parcom/token.cr index b4e1fef..b4e1fef 100644 --- a/src/parcom/token.cr +++ b/src/__OLD_parcom/token.cr diff --git a/src/parcom/token_seq.cr b/src/__OLD_parcom/token_seq.cr index 45900f9..45900f9 100644 --- a/src/parcom/token_seq.cr +++ b/src/__OLD_parcom/token_seq.cr diff --git a/src/parcom.cr b/src/parcom.cr index ddb2e50..d12b011 100644 --- a/src/parcom.cr +++ b/src/parcom.cr @@ -5,17 +5,6 @@ module Parcom # A ParserFail exception should be raised by `Parser#parse` when # a parse attempt is unsuccessful. - # Raising this exception in the `#parse` method of a Parser "Foo" - # usually follows this pattern to allow for error tracing: - # - # ``` - # class Foo(T, V) < Parser(T, V) - # def parse(tokens : Tokens(T)) : Result(T, V) - # helper.parse(tokens) - # rescue ex : ParserFail - # raise ParserFail.new("Foo: #{ex.message}") - # end - # ``` class ParserFail < Exception end @@ -74,10 +63,51 @@ module Parcom # This is used instead of a `Tuple` or `NamedTuple` because: # 1. This is more idiomatic than a `Tuple`. # 2. Crystal does not support generic named tuples. - struct Result(T, V) + struct Result(T, U) getter tokens, value - def initialize(@tokens : Tokens(T), @value : V) + def initialize(@tokens : Tokens(T), @value : U) + end + end + + class Parser(T, U) + getter name + + def initialize(@name : String, @f : Tokens(T) -> Result(T, U)) + end + + def initialize(@name : String, &block : Tokens(T) -> Result(T, U)) + @f = block + end + + def named(name : String) : self + @name = name + self + end + + def parse(tokens : Tokens(T)) : Result(T, U) + @f.call(tokens) + end + + def parse(tokens : Tokens(T)) : Result(T, U)? + parse(tokens) + rescue + nil + end + + def assert(f : U -> Bool) : Parser(T, U) + p = self + 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}") + end + result + end + end + + def assert(&block : U -> Bool) : Parser(T, U) + assert(block) end end end diff --git a/src/parcom/basic.cr b/src/parcom/basic.cr new file mode 100644 index 0000000..6594555 --- /dev/null +++ b/src/parcom/basic.cr @@ -0,0 +1,10 @@ +require "../parcom.cr" + +module Parcom + module Basic + def pure(value : U) : Parser(T, U) forall T, U + Parser.new("Pure #{value}") { |tokens| Result.new(tokens, value) } + end + end +end + |
