l43_simple_peg 0.1.0.pre.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 23eaccca5eeb68df26e134c6f18f27b1d5507a629bc710c1a1a21ba22e2958ab
4
+ data.tar.gz: 0f3c03a9c45fbd0eb55df8fd8448106cb5ce26a215b8031352fdb7d069e21629
5
+ SHA512:
6
+ metadata.gz: 0ce27fab43a70fea75c7e257418e600690f182a0e903104beeb35397097b1c2d633059b2548608bf7d23acf0d11211c666d20142e6a044df38f89a46ae195b49
7
+ data.tar.gz: 9dc045b2b19bfc1265e6576554c5d44574405d9666a39729f5d8da1374f732a2b4e0a8d0d1ecf72ed3062094d739db9cf3a4ebfae08f5204a5a0ec077764af92
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'combinators'
4
+ class Peg
5
+ module ClassMethods
6
+
7
+ PredefinedSymbols = {
8
+ dot: "\\.",
9
+ ws: ["\\s", :ignore],
10
+ wss: ["\\s*", :ignore],
11
+ }
12
+
13
+ def choice(*pegs, name: nil, &blk) = Combinators::Choice.new(*pegs, name:, &blk)
14
+
15
+ def ignore(peg, name: nil)
16
+ parser = Peg(peg)
17
+ name ||= "ignored(#{parser.name})"
18
+ parser.ignore
19
+ end
20
+
21
+ def list(ele_peg, sep_peg, min: 1, name: nil, &blk) = Combinators::List.new(ele_peg, sep_peg, min:, name:, &blk)
22
+
23
+ def from_sym(sym, name: nil, &blk)
24
+ regex = PredefinedSymbols.fetch(sym) do
25
+ return posix(sym, name:, &blk)
26
+ end
27
+
28
+ predefined_name = name || "predefined(#{sym})"
29
+ case regex
30
+ in rgx, :ignore
31
+ rgx(rgx, name: predefined_name, &blk).ignore
32
+ else
33
+ rgx(regex, name: predefined_name, &blk)
34
+ end
35
+ end
36
+
37
+ def posix(desc, name: nil, &blk)
38
+ name ||= "posix(#{desc.inspect})"
39
+ peg = if desc.to_s.end_with?("s")
40
+ rgx("[[:#{desc.to_s[0...-1]}:]]+", name:)
41
+ else
42
+ rgx("[[:#{desc.to_s}:]]", name:)
43
+ end
44
+ return peg unless blk
45
+ peg.map(&blk)
46
+ end
47
+
48
+ def rgx(source, name: nil)
49
+ name ||= "rgx(#{source.inspect})"
50
+ regexp = Regexp.compile("\\A#{source}")
51
+ new(name:) do |input|
52
+ if regexp.match(input) in MatchData => md
53
+ [md.match(0), input[md.match_length(0)..]]
54
+ end
55
+ end
56
+ end
57
+
58
+ def seq(*parsers, name: nil) = Combinators::Seq.new(*parsers, name:)
59
+
60
+ def set(chars, name: nil)
61
+ name ||= "Set(#{chars})"
62
+ rgx("[#{chars}]", name:)
63
+ end
64
+ end
65
+ extend ClassMethods
66
+
67
+ end
68
+ # SPDX-License-Identifier: AGPL-3.0-or-later
69
+
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Peg
4
+ module Combinators
5
+ class Choice < Peg
6
+ attr_reader :name, :fn, :pegs
7
+
8
+ private
9
+ def initialize(*pegs, name: nil)
10
+ @pegs = pegs.map { Peg(it) }
11
+ @name = name || pegs.map(&:name).join(" & ")
12
+ @fn = ->(input) { _parse(input) }
13
+ end
14
+
15
+ def _parse(input)
16
+ pegs.each do |peg|
17
+ if peg.(input) in [_, _] => result
18
+ return result
19
+ end
20
+ end
21
+ nil
22
+ end
23
+ end
24
+ end
25
+ end
26
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Peg
4
+ module Combinators
5
+ class List < Peg
6
+ attr_reader :ele_peg, :fn, :min, :name, :sep_peg
7
+
8
+ private
9
+ def initialize(ele_peg, sep_peg, name: nil, min: 1, &blk)
10
+ @ele_peg = Peg(ele_peg, &blk)
11
+ @sep_peg = Peg(sep_peg)
12
+ @min = min
13
+
14
+ @name = name || "list(#{@ele_peg.name}, #{@sep_peg.name})"
15
+ @fn = ->(input) { _parse(input) }
16
+ end
17
+
18
+ def _parse(input)
19
+ ast = []
20
+ count = min
21
+
22
+ if ele_peg.(input) in ele, input
23
+ ast << ele
24
+ count -= 1
25
+ else
26
+ return nil
27
+ end
28
+
29
+ _parse_tail(ast, count: min - 1, input:)
30
+ end
31
+
32
+ def _parse_tail(ast, count:, input:)
33
+ loop do
34
+ if sep_peg.(input) in _, rest
35
+ if ele_peg.(rest) in ele, input
36
+ count -= 1
37
+ ast << ele
38
+ else
39
+ if count.positive?
40
+ return nil
41
+ else
42
+ return [ast, input]
43
+ end
44
+ end
45
+ else
46
+ if count.positive?
47
+ return nil
48
+ else
49
+ return [ast, input]
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Peg
4
+ module Combinators
5
+ class Many < Peg
6
+ attr_reader :fn, :min, :name, :peg
7
+
8
+ private
9
+ def initialize(peg, name: nil, min: 1)
10
+ @peg = peg
11
+ @min = min
12
+ @name = name || "many(#{peg.name}, min: #{min})"
13
+ @fn = ->(input) { _parse(input) }
14
+ end
15
+
16
+ def _parse(input)
17
+ count = min
18
+ ast = []
19
+ loop do
20
+ case peg.(input)
21
+ in partial_ast, input
22
+ count -= 1
23
+ ast << partial_ast
24
+ else
25
+ return if count.positive?
26
+ return [ast, input]
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Peg
4
+ module Combinators
5
+ class Seq < Peg
6
+ attr_reader :name, :fn, :pegs
7
+
8
+ private
9
+ def initialize(*pegs, name: nil)
10
+ @pegs = pegs.map { Peg(it) }
11
+ @name = name || @pegs.map(&:name).join(" & ")
12
+ @fn = ->(input) { _parse(input) }
13
+ end
14
+
15
+ def _parse(input)
16
+ pegs.inject([[], input]) do |(ast, inp), peg|
17
+ # p(inp:, peg: peg.name, ast:)
18
+ if peg.(inp) in [partial, inp]
19
+ # p(partial:, inp:)
20
+ peg.ignore? ? [ast, inp] : [[*ast, partial], inp]
21
+ else
22
+ return nil
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'combinators/choice'
4
+ require_relative 'combinators/list'
5
+ require_relative 'combinators/many'
6
+ require_relative 'combinators/seq'
7
+ class Peg
8
+ module Combinators
9
+
10
+ end
11
+ end
12
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'class_methods'
4
+ class Peg
5
+ module Helpers extend self
6
+
7
+ end
8
+ end
9
+
10
+ def Peg(peg, name: nil)
11
+ name ||= "Peg(#{peg.inspect})"
12
+ case peg
13
+ when String
14
+ Peg.rgx(peg, name:)
15
+ when Symbol
16
+ Peg::Helpers.from_symbol
17
+ end
18
+ end
19
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Time
4
+ def deconstruct(*)
5
+ [year, month, day, hour, min, seconds]
6
+ end
7
+
8
+ def deconstruct_keys(*)
9
+ {
10
+ year:,
11
+ month:,
12
+ day:,
13
+ hour:,
14
+ min:,
15
+ seconds:,
16
+ }
17
+ end
18
+
19
+ def seconds
20
+ s = to_f
21
+ s = sec + (s - s.floor)
22
+ end
23
+ end
24
+
25
+ class Peg
26
+ module Parsers
27
+ module TimeParser
28
+ module TimeHelper extend self
29
+ Units = {
30
+ "s" => 1,
31
+ "m" => 60,
32
+ "h" => 3600,
33
+ "d" => 86400,
34
+ "w" => 604800,
35
+ }
36
+ def add_units(units)
37
+ units => sign, [*pairs]
38
+ sign = sign == "-" ? -1 : 1
39
+ sign * _add_pairs(pairs)
40
+ end
41
+
42
+ def complete_datetime(parts)
43
+ Time.now => {**} => now
44
+ case parts
45
+ in :date, date
46
+ _complete_date(date, now)
47
+ in :time, time
48
+ _complete_time(time, now)
49
+ in [:date, date], [[:time, time]]
50
+ _complete_datetime(date, time, now)
51
+ end
52
+ end
53
+
54
+ private
55
+ def _add_pairs(pairs)
56
+ pairs.inject(0) do |result, (amount, unit)|
57
+ result + amount.to_i * Units.fetch(unit)
58
+ end
59
+ end
60
+
61
+ def _complete_date(date, now)
62
+ case date
63
+ in 0, d
64
+ [now.year, now.month, d, now.hour, now.min, now.seconds]
65
+ in m, d
66
+ [now.year, m, d, now.hour, now.min, now.seconds]
67
+ in y, m, d
68
+ [now.year/100 * 100 + y, m, d, now.hour, now.min, now.seconds]
69
+ else
70
+ raise ArgumentError, "need a pair or triplet, got: #{date.inspect}"
71
+ end
72
+ end
73
+
74
+ def _complete_datetime(date, time, now)
75
+ date_triplet = _complete_date(date, now)[0..2]
76
+ time_triplet = _complete_time(time, now)[3..]
77
+ date_triplet + time_triplet
78
+ end
79
+
80
+ def _complete_time(time, now)
81
+ case time
82
+ in 0, s
83
+ [now.year, now.month, now.day, now.hour, now.min, s]
84
+ in m, s
85
+ [now.year, now.month, now.day, now.hour, m, s]
86
+ in h, m, s
87
+ [now.year, now.month, now.day, h, m, s]
88
+ else
89
+ raise ArgumentError, "need a pair or triplet, got: #{time.inspect}"
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'peg'
4
+ require_relative 'time_parser/time_helper'
5
+ class Peg
6
+ module Parsers
7
+ module TimeParser extend self
8
+
9
+ def datetime_parser = @__datetime_parser__ ||= _datetime_parser
10
+
11
+ def time_parser = @__time_parser__ ||= _time_parser
12
+
13
+ def timestamp_parser = @__timestamp_parser__ ||= _timestamp_parser
14
+ def timestamp_float_parser = @__timestamp_float_parser__ ||= _timestamp_float_parser
15
+ def timestamp_int_parser = @__timestamp_int_parser__ ||= _timestamp_int_parser
16
+
17
+ def offset_parser = @__offset_parser__ ||= _offset_parser
18
+ def offset_in_seconds_parser = @__offset_in_seconds_parser__ ||= _offset_in_seconds_parser
19
+ def offset_in_float_seconds_parser = @__offset_in_float_seconds_parser__ ||= _offset_in_float_seconds_parser
20
+ def offset_in_int_seconds_parser = @__offset_in_int_seconds_parser__ ||= _offset_in_int_seconds_parser
21
+ def offset_with_units_parser = @__offset_with_units_parser__ ||= _offset_with_units_parser
22
+
23
+ def only_date_parser = @__only_date_parser__ ||= _only_date_parser
24
+ def only_time_parser = @__only_time_parser__ ||= _only_time_parser
25
+
26
+ private
27
+
28
+ def _datetime_parser
29
+ Peg.choice(
30
+ Peg.seq(only_date_parser, Peg.seq(Peg.ignore("T"), only_time_parser)),
31
+ only_date_parser,
32
+ only_time_parser,
33
+ ).map{ [:datetime, TimeHelper.complete_datetime(it)] }
34
+ end
35
+
36
+ def _offset_in_float_seconds_parser
37
+ Peg.seq(Peg.set("+-"), :digits, "\\.", :digits, name: "offset in float seconds parser")
38
+ .joined.map(&:to_f)
39
+ end
40
+
41
+ def _offset_in_int_seconds_parser
42
+ Peg.seq(Peg.set("+-"), :digits, name: "offset in int seconds parser")
43
+ .joined.map(&:to_i)
44
+ end
45
+
46
+ def _offset_in_seconds_parser
47
+ offset_in_float_seconds_parser.or(offset_in_int_seconds_parser)
48
+ end
49
+
50
+ def _offset_parser
51
+ Peg.choice(
52
+ offset_with_units_parser,
53
+ offset_in_seconds_parser,
54
+ name: "offset parser"
55
+ ).map { [:offset, it] }
56
+ end
57
+
58
+ def _offset_with_units_parser
59
+ Peg.seq(Peg.set("-+").optional, _time_unit_parser).map { TimeHelper.add_units(it) }
60
+ end
61
+
62
+ def _only_date_parser
63
+ Peg.list(:digits, "-", min: 2, &:to_i).map { [:date, it] }
64
+ end
65
+
66
+ def _only_time_parser
67
+ Peg.list(:digits, ":", min: 2, &:to_i).map { [:time, it] }
68
+ end
69
+
70
+ def _time_parser
71
+ Peg.choice(
72
+ offset_parser,
73
+ datetime_parser,
74
+ timestamp_parser
75
+ )
76
+ end
77
+
78
+ def _time_unit_parser
79
+ unit_parser = Peg.seq(:digits, Peg.set(TimeHelper::Units.keys.join))
80
+ unit_parser.many
81
+ end
82
+
83
+ def _timestamp_parser
84
+ Peg.choice(
85
+ timestamp_float_parser,
86
+ timestamp_int_parser
87
+ ).map { [:timestamp, it] }
88
+ end
89
+
90
+ def _timestamp_float_parser
91
+ Peg.seq(:digits, :dot, :digits).joined.map(&:to_f)
92
+ end
93
+
94
+ def _timestamp_int_parser
95
+ Peg.seq(:digits).joined.map(&:to_i)
96
+ end
97
+ end
98
+ end
99
+ end
100
+ # SPDX-License-Identifier: AGPL-3.0-or-later
data/lib/peg/result.rb ADDED
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Peg
4
+ class Result
5
+ attr_reader :ast, :name, :rest
6
+
7
+ def deconstruct(*) = [ast, rest]
8
+
9
+ def deconstruct_keys(*) = {
10
+ ast:,
11
+ name:,
12
+ rest:,
13
+ }
14
+
15
+ def ==(other)
16
+ self.class === other && ast == other.ast && rest == other.rest
17
+ end
18
+
19
+ def len = match.length
20
+
21
+ def ignore? = @ignore
22
+
23
+ private
24
+ def initialize(ast, rest, name: nil)
25
+ @ast = ast
26
+ @rest = rest
27
+ @name = name
28
+ end
29
+
30
+ end
31
+ end
32
+ # SPDX-License-Identifier: AGPL-3.0-or-later
data/lib/peg/set.rb ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Peg
4
+ class Set < Peg
5
+ private
6
+ end
7
+ end
8
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Peg
4
+ VERSION = '0.1.0-pre'
5
+ end
6
+ # SPDX-License-Identifier: AGPL-3.0-or-later
7
+
data/lib/peg.rb ADDED
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'peg/class_methods'
4
+ require_relative 'peg/result'
5
+ class Peg
6
+ attr_reader :fn, :name
7
+
8
+ def call(input)
9
+ fn.(input).tap do |result|
10
+ # $stderr.puts(input) if $debug # :nocov:
11
+ # $stderr.puts({name:, result:}.inspect) if $debug # :nocov:
12
+ end
13
+ end
14
+
15
+ def ignore
16
+ @ignore = true
17
+ self
18
+ end
19
+
20
+ def ignore? = @ignore
21
+
22
+ def joined(joiner: "", name: nil)
23
+ name ||= "joined(#{self.name})"
24
+ Peg.new(name:) do |input|
25
+ case fn.(input)
26
+ in ast, rest
27
+ [ast.join(joiner), rest]
28
+ else
29
+ nil
30
+ end
31
+ end
32
+ end
33
+
34
+ def many(name: nil, min: 1)
35
+ name ||= "many(#{self.name}, min: #{min})"
36
+ Combinators::Many.new(self, name:, min:)
37
+ end
38
+
39
+ def map(name: nil, &mapper)
40
+ name ||= "map(#{self.name})"
41
+ Peg.new(name:) do |input|
42
+ case fn.(input)
43
+ in ast, rest
44
+ [mapper.(ast), rest]
45
+ else
46
+ nil
47
+ end
48
+ end
49
+ end
50
+
51
+ def or(peg, name: nil, &blk) = Combinators::Choice.new(self, peg, name:, &blk)
52
+
53
+ def parse(input)
54
+ case self.(input)
55
+ in ast, rest
56
+ Result.new(ast, rest, name:)
57
+ else
58
+ nil
59
+ end
60
+ end
61
+
62
+ def optional(name: nil)
63
+ name ||= "optional(#{self.name})"
64
+ self.class.new(name:) do |input|
65
+ if fn.(input) in [_, _] => result
66
+ result
67
+ else
68
+ [nil, input]
69
+ end
70
+ end
71
+ end
72
+
73
+ private
74
+ def initialize(name: nil, &fn)
75
+ @name = name
76
+ @fn = fn
77
+ @ignore = false
78
+ end
79
+ end
80
+
81
+ def Peg(peg, name: nil, &blk)
82
+ name ||= "Peg(#{peg.inspect})"
83
+ case peg
84
+ when String
85
+ Peg.rgx(peg, name:, &blk)
86
+ when Symbol
87
+ Peg.from_sym(peg, name:, &blk)
88
+ when Peg
89
+ peg
90
+ else
91
+ raise ArgumentError, "unexpected arg: #{peg.inspect}, need a String, Symbol or Peg instance"
92
+ end
93
+ end
94
+ # SPDX-License-Identifier: AGPL-3.0-or-later
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: l43_simple_peg
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.pre.pre
5
+ platform: ruby
6
+ authors:
7
+ - Robert Dober
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2026-09-28 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: ostruct
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: 0.6.3
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: 0.6.3
26
+ description: 'peg
27
+
28
+ '
29
+ email: robert.dober@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - lib/peg.rb
35
+ - lib/peg/class_methods.rb
36
+ - lib/peg/combinators.rb
37
+ - lib/peg/combinators/choice.rb
38
+ - lib/peg/combinators/list.rb
39
+ - lib/peg/combinators/many.rb
40
+ - lib/peg/combinators/seq.rb
41
+ - lib/peg/helpers.rb
42
+ - lib/peg/parsers/time_parser.rb
43
+ - lib/peg/parsers/time_parser/time_helper.rb
44
+ - lib/peg/result.rb
45
+ - lib/peg/set.rb
46
+ - lib/peg/version.rb
47
+ licenses:
48
+ - AGPL-3.0-or-later
49
+ metadata: {}
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: 4.0.2
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubygems_version: 4.0.19
65
+ specification_version: 4
66
+ summary: peg
67
+ test_files: []