l43_rgx 0.1.1 → 0.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 01a532b87cd5cfd3a0f06d773bbd556168b9548bd081b7ae22df35f6f2adcdba
4
- data.tar.gz: a7784b4825e708af44f2703d643b0f08bd46d84f6b78d452e02ceacd8668bbd6
3
+ metadata.gz: e701126bd544a239a2fdb2c5afa73a405b6cffa998fb1fffae151b6d0d6c93fc
4
+ data.tar.gz: f2c7b9e91c0c21af9dbb2b541b096a7cf46bb516635e93451412ad5a420cb7db
5
5
  SHA512:
6
- metadata.gz: 7d1f476ba49438a5db986e2cc607308aaa85857e31f779ea136395edd4b65800ae67d36885dac3e5e7e1e24d16f510a7e9c5252940c6b9f90d32c2f9129074b2
7
- data.tar.gz: 0e7c86b2ca3c2da91acc7b97c2e0315897c146888532280709cfbdf8cd76f911325ed0f0deaf044588fff2c4cb0a5d31c7c4956dc0370bf3680143da9bac6d85
6
+ metadata.gz: 1c9c95914e349bf0caa5bee8c36def1d3c38dd31a18a2d6fb8d2be694decaa4735deadd2e346e7fa7b0207ed7e8def8c1522f7801f97819f03342a3c0a718d55
7
+ data.tar.gz: d777ce962ad1a77a3277f92b3176a20b1baa70e3086138d6a41eda8cc4fddf5869f4a634351d07e0d39674e6a8c7274c712cd7cbf6ec03ebeff102b1a939ee0e
data/lib/l43/rgx/all.rb CHANGED
@@ -3,5 +3,6 @@
3
3
  require_relative '../rgx'
4
4
  def Rgx(*parts, **options, &callback) = L43::Rgx.new(*parts, **options, &callback)
5
5
  Rgx = L43::Rgx
6
+ R = Rgx::R
6
7
  Result = Rgx::Result
7
8
  # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -4,6 +4,15 @@ module L43
4
4
  class Rgx
5
5
  module Compile
6
6
 
7
+ PredefinedSymbols = {
8
+ any: ".",
9
+ end: "\\z",
10
+ star: "*",
11
+ plus: "+",
12
+ option: "?",
13
+ ws: "[[:blank:]]*",
14
+ }.freeze
15
+
7
16
  private
8
17
  def _a_anchor
9
18
  "\\A" if [:begin, :both].member?(anchored)
@@ -26,20 +35,17 @@ module L43
26
35
  def _compile_part(part)
27
36
  case part
28
37
  in String
29
- part
38
+ escape_strings ? Regexp.escape(part) : part
30
39
  in Symbol
31
- repr = part.to_s
32
- if repr.end_with?("s")
33
- "[[:#{repr[0...-1]}:]]+"
34
- else
35
- "[[:#{repr}:]]"
36
- end
40
+ _compile_symbol(part)
37
41
  in Integer
38
42
  "{#{part}}"
39
43
  in Range
40
44
  "{#{part.first},#{part.last}}"
41
45
  in Array
42
46
  "(#{part.map { _compile_part(it) }.join})"
47
+ in Regexp
48
+ part
43
49
  else
44
50
  if self.class === part
45
51
  raise CompilationError, "part is anchored, only use unanchored instances of #{self.class}" if part.anchored?
@@ -54,6 +60,17 @@ module L43
54
60
  parts.map { _compile_part it }.join
55
61
  end
56
62
 
63
+ def _compile_symbol(part)
64
+ PredefinedSymbols.fetch(part) do
65
+ repr = part.to_s
66
+ if repr.end_with?("s")
67
+ "[[:#{repr[0...-1]}:]]+"
68
+ else
69
+ "[[:#{repr}:]]"
70
+ end
71
+ end
72
+ end
73
+
57
74
  def _look_ahead
58
75
  if followed_by
59
76
  followed = self.class.new(*Array(followed_by)).source
@@ -3,6 +3,7 @@
3
3
  module L43
4
4
  class Rgx
5
5
  CompilationError = Class.new(RuntimeError)
6
+ InternalError = Class.new(RuntimeError)
6
7
  end
7
8
  end
8
9
  # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module L43
4
+ class Rgx
5
+ class R
6
+ class And < R
7
+ attr_reader :parsers
8
+
9
+ def name
10
+ @name || ["(", parsers.map(&:name).join(" && "), ")"].join(" ")
11
+ end
12
+
13
+ def _parse(input)
14
+ mapped_ast = @parsers.map do |parser|
15
+ result = parser.parse(input)
16
+ case result
17
+ in ast, input
18
+ parser.ignore ? nil : ast
19
+ else
20
+ return nil
21
+ end
22
+ rescue NoMethodError => nme
23
+ # require "debug"; binding.break
24
+ raise
25
+ end
26
+ [_apply_callbacks(mapped_ast.compact), input]
27
+ end
28
+
29
+ private
30
+ def initialize(*parsers, name: nil, &blk)
31
+ @parsers = parsers
32
+ callbacks << [blk, :all] if blk
33
+ @name = name
34
+ end
35
+
36
+ end
37
+ end
38
+ end
39
+ end
40
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'l43/open_object'
4
+ module L43
5
+ class Rgx
6
+ class R
7
+ Ast = OpenObject.def_class :value do
8
+ def deconstruct(*) = Array(value)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'l43/core/enum/hash'
4
+ module L43
5
+ class Rgx
6
+ class R
7
+ module ClassMethods
8
+ define_method :end do |&blk|
9
+ R.new(:end, capture_rest: false, &blk)
10
+ end
11
+
12
+ def fail
13
+ R::Fail.new
14
+ end
15
+
16
+ def from_regexp(part, name: nil, &blk)
17
+ name ||= part.inspect
18
+ R.new(part, name:, escape_strings: false, &blk)
19
+ end
20
+
21
+ def literals(*literals, name: nil, &blk)
22
+ name ||= "or(#{literals.join(", ")})"
23
+ self.or(*literals.map { R.new(it) }, name:, &blk)
24
+ end
25
+
26
+ def make(*parts, **options, &blk)
27
+ case parts
28
+ in [R => r]
29
+ raise ArgumentError, "must not provide a blk with an R instance" if blk
30
+ r
31
+ in [R => _, _, *]
32
+ raise ArgumentError, "must only use one instance of R as or argument"
33
+ in []
34
+ raise ArgumentError, "need at least one argument"
35
+ else
36
+ _make_from_parts(parts, options, blk)
37
+ end
38
+ end
39
+
40
+ def many(*parts, min: 1, name: nil, **options, &blk) =
41
+ R::Many.new(*parts, min: 1, name: nil, **options, &blk)
42
+
43
+ def reduce(*parts, value:, min: 1, name: nil, **options, &blk) =
44
+ R::Reduce.new(*parts, min: 1, name: nil, **options, &blk)
45
+
46
+ def and(*rs, name: nil, &blk) = R::And.new(*rs, name:, &blk)
47
+ def or(*rs, name: nil, &blk) = R::Or.new(*rs, name:, &blk)
48
+
49
+ private
50
+ def _make_from_parts(parts, options, blk)
51
+ if options[:capture_all]
52
+ R.new(*parts.flatten.map { [it] }, **options.without(:capture_all), &blk)
53
+ else
54
+ R.new(*parts, **options.without(:capture_all), &blk)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module L43
4
+ class Rgx
5
+ class R
6
+ class Fail < R
7
+
8
+ def and(*parts, name: nil, ignore: false, &blk)
9
+ self
10
+ end
11
+
12
+ def or(*parts, name: nil, &blk)
13
+ case parts
14
+ in [R => r]
15
+ r
16
+ in [R => _, _, *]
17
+ raise ArgumentError, "must only use one instance of R as or argument"
18
+ in []
19
+ raise ArgumentError, "need at least one argument"
20
+ else
21
+ R.new(*parts, name:, ignore:, &blk)
22
+ end
23
+ end
24
+
25
+ def name = "Fail"
26
+
27
+ def parse(_input) = nil
28
+
29
+ private
30
+ def initialize(*, **) = nil
31
+
32
+ end
33
+ end
34
+ end
35
+ end
36
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module L43
4
+ class Rgx
5
+ class R
6
+ class Many < R
7
+ attr_reader :min, :name, :parser, :rgx
8
+
9
+ private
10
+ def initialize(*parts, min: 1, name: nil, **options, &callback)
11
+ @min = min
12
+ @name = name || "many(#{parts.inspect}, min: #{min})"
13
+ case parts.first
14
+ when R
15
+ @parser = parts.first
16
+ else
17
+ @parser = R.new(*parts, name:, **options)
18
+ end
19
+ _map_by_arity(callback) if callback
20
+ end
21
+
22
+ def _parse(input)
23
+ needed = min
24
+ ast = []
25
+ loop do
26
+ case parser.parse(input)
27
+ in nil
28
+ if needed.positive?
29
+ return nil
30
+ else
31
+ return [_apply_callbacks(ast), input]
32
+ end
33
+ in captured, input
34
+ needed -= 1
35
+ ast = ast << captured
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ module L43
4
+ class Rgx
5
+ class R
6
+ module InstanceMethods
7
+ def and(*parts, name: nil, **options, &blk)
8
+ R.and(self, R.make(*parts, **options, &blk), name:)
9
+ end
10
+
11
+ def flattened(&callback)
12
+ callbacks << [-> { it.flatten }, :all]
13
+ _map_by_arity(callback) if callback
14
+ self
15
+ end
16
+
17
+ def joined(by='', name: nil, &blk)
18
+ @name ||= "joined(#{@name}"
19
+ callbacks << [->{ it.join(by) }, :all]
20
+ callbacks << [blk, :all] if blk
21
+ self
22
+ end
23
+
24
+ def many(name: nil, min: 1, **options, &blk)
25
+ R.many(self, name:, min:, **options, &blk)
26
+ end
27
+
28
+ def reduce(value, name: nil, min: 1, **options, &blk)
29
+ Reducer.new(self, value:, name:, min:, **options, &blk)
30
+ end
31
+
32
+ def map(*args, name: nil, &blk)
33
+ raise ArgumentError, "must not provide arguments and a blk" if blk && !args.empty?
34
+
35
+ @name = name || "map(#{@name})"
36
+ args = [:itself] if args.empty? && blk.nil?
37
+
38
+ return _map_by_arity(blk) if args.empty?
39
+
40
+ callback = -> result do
41
+ result.send(*args)
42
+ end
43
+ callbacks << [callback, :all]
44
+ self
45
+ end
46
+
47
+ def map_capture(n, *args, name: nil, &blk)
48
+ raise ArgumentError, "#{n} is not an Integer" unless Integer === n
49
+ raise ArgumentError, "must not provide arguments and a blk" if blk && !args.empty?
50
+
51
+ @name = name || "map_capture(#{n})(#{@name})"
52
+ args = [:itself] if args.empty? && blk.nil?
53
+
54
+ if args.empty?
55
+ callbacks << [blk, n]
56
+ else
57
+ callback = -> result do
58
+ result.send(*args)
59
+ end
60
+ callbacks << [callback, n]
61
+ end
62
+ self
63
+ end
64
+
65
+ def map_first(*args, name: nil, &blk)
66
+ name ||= "map_first(#{name})"
67
+ map_capture(0, *args, name:, &blk)
68
+ end
69
+
70
+ def or(*parts, name: nil, **options, &blk)
71
+ R.or(self, R.make(*parts, **options, &blk), name:)
72
+ end
73
+
74
+ private
75
+ def _apply_callbacks(matches)
76
+ # p(matches:) if $debug_r
77
+ callbacks.inject(matches) do |result, callback|
78
+ # p(result:) if $debug_r
79
+ case callback
80
+ in blk, :all
81
+ blk.(result)
82
+ in blk, :splat
83
+ blk.(*result)
84
+ in blk, :first
85
+ blk.(result.first)
86
+ in blk, Integer => n
87
+ blk.(result[n])
88
+ else
89
+ raise InternalError, "undefined action type #{callback}"
90
+ end
91
+ end
92
+ end
93
+
94
+ def _map_by_arity(blk)
95
+ calling_type = blk.arity < 0 ? :splat : :all
96
+ callbacks << [blk, calling_type]
97
+ self
98
+ end
99
+
100
+ def mkrgx(*parts, **options)
101
+ case parts
102
+ in [Rgx => rgx, *]
103
+ rgx
104
+ in [Regexp => regexp, *]
105
+ rgx
106
+ in [Array, *]
107
+ Rgx(*parts, **{anchored: :begin, capture_rest: true, escape_strings: true}.merge(options))
108
+ in [head, *rest]
109
+ Rgx([head], *rest, **{anchored: :begin, capture_rest: true, escape_strings: true}.merge(options))
110
+ end
111
+ end
112
+ end
113
+
114
+ # module UniversalMethods
115
+ # end
116
+ end
117
+ end
118
+ end
119
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module L43
4
+ class Rgx
5
+ class R
6
+ class Or < R
7
+ attr_reader :alternatives
8
+
9
+ def name
10
+ @name || ["(", alternatives.map(&:name).join(" || "), ")"].join(" ")
11
+ end
12
+
13
+ private
14
+ def initialize(*alternatives, name: nil, &blk)
15
+ @alternatives = alternatives
16
+ @name = name
17
+ _map_by_arity(blk) if blk
18
+ end
19
+
20
+ def _parse(input)
21
+ alternatives.find_value do |alternative|
22
+ case alternative.parse(input)
23
+ in nil
24
+ nil
25
+ in ast, rest
26
+ [_apply_callbacks(ast), rest]
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module L43
4
+ class Rgx
5
+ class R
6
+ class Reducer < R
7
+
8
+ attr_reader :name
9
+
10
+ private
11
+ attr_reader :min, :parser, :reducer
12
+
13
+ def initialize(*parts, value:, min: 1, name: nil, **options, &reducer)
14
+ @min = min
15
+ @name = name || "reducer(#{parts.inspect}, min: #{min})"
16
+ case parts.first
17
+ when R
18
+ @parser = parts.first
19
+ else
20
+ @parser = R.new(*parts, name:, **options)
21
+ end
22
+ @reducer = reducer
23
+ @value = value
24
+ end
25
+
26
+ def _parse(input)
27
+ needed = min
28
+ value = @value
29
+ loop do
30
+ case parser.parse(input)
31
+ in nil
32
+ if needed.positive?
33
+ return nil
34
+ else
35
+ return [_apply_callbacks(value), input]
36
+ end
37
+ in captured, input
38
+ needed -= 1
39
+ value = reducer.(value, *captured)
40
+ end
41
+ end
42
+ end
43
+
44
+ end
45
+ end
46
+ end
47
+ end
48
+ # SPDX-License-Identifier: AGPL-3.0-or-later
data/lib/l43/rgx/r.rb ADDED
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'l43/core/enum/enumerable'
4
+
5
+ require_relative 'r/ast'
6
+ require_relative 'r/and'
7
+ require_relative 'r/fail'
8
+ require_relative 'r/many'
9
+ require_relative 'r/or'
10
+ require_relative 'r/reducer'
11
+ require_relative 'r/class_methods'
12
+ require_relative 'r/methods'
13
+ module L43
14
+ class Rgx
15
+ class R
16
+ include InstanceMethods
17
+ # include UniversalMethods
18
+ extend ClassMethods
19
+ # extend UniversalMethods
20
+
21
+ attr_reader :ignore, :name, :parts, :rgx
22
+
23
+ def ignore!
24
+ @ignore = true
25
+ self
26
+ end
27
+
28
+ def parse(input)
29
+ # p(enter: name) if $debug_r
30
+ result = _parse(input)
31
+ # p(name:) if $debug_r
32
+ # p(result: result) if $debug_r
33
+ result
34
+ end
35
+
36
+ def _parse(input)
37
+ case rgx.regexp.match(input)
38
+ in nil
39
+ nil
40
+ in mdata
41
+ result = [_apply_callbacks(mdata.captures[0...-1]), mdata.captures.last]
42
+ # p(result_after_apply_cb: result)
43
+ result
44
+ end
45
+ end
46
+
47
+ private
48
+ def initialize(*parts, name: nil, ignore: false, **options, &callback)
49
+ @options = options
50
+ @ignore = ignore
51
+ @name = name || parts.inspect
52
+ @parts = parts
53
+ @rgx = mkrgx(*parts, **options)
54
+ _map_by_arity(callback) if callback
55
+ end
56
+
57
+ def callbacks = @__callbacks__ ||= []
58
+ end
59
+ end
60
+ end
61
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -2,7 +2,7 @@
2
2
 
3
3
  module L43
4
4
  class Rgx
5
- VERSION = "0.1.1"
5
+ VERSION = "0.2.0"
6
6
  end
7
7
  end
8
8
  # SPDX-License-Identifier: AGPL-3.0-or-later
data/lib/l43/rgx.rb CHANGED
@@ -4,6 +4,7 @@ require_relative 'rgx/errors'
4
4
  require_relative 'rgx/compile'
5
5
  require_relative 'rgx/methods'
6
6
  require_relative 'rgx/result'
7
+ require_relative 'rgx/r'
7
8
  require 'l43/core/forwarder'
8
9
 
9
10
  module L43
@@ -15,7 +16,7 @@ module L43
15
16
 
16
17
  LegalAnchors = [nil, :begin, :both, :end].freeze
17
18
 
18
- attr_reader :regexp, :source
19
+ attr_reader :escape_strings, :regexp, :source
19
20
  forward :match, to: :regexp
20
21
 
21
22
  def anchored? = anchored || capture_rest
@@ -48,6 +49,7 @@ module L43
48
49
  anchored: nil,
49
50
  capture: false,
50
51
  capture_rest: false,
52
+ escape_strings: false,
51
53
  followed_by: nil,
52
54
  not_followed_by: nil,
53
55
  not_preceded_by: nil,
@@ -74,6 +76,7 @@ module L43
74
76
  @callback = callback
75
77
  @capture = capture
76
78
  @capture_rest = capture_rest
79
+ @escape_strings = escape_strings
77
80
  @followed_by = followed_by
78
81
  @not_followed_by = not_followed_by
79
82
  @not_preceded_by = not_preceded_by
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: l43_rgx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Dober
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-09-10 00:00:00.000000000 Z
10
+ date: 2026-09-23 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: ostruct
@@ -29,14 +29,28 @@ dependencies:
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: 0.2.9
32
+ version: 0.2.11
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: 0.2.9
39
+ version: 0.2.11
40
+ - !ruby/object:Gem::Dependency
41
+ name: l43_open_object
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: 0.3.0
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: 0.3.0
40
54
  description: 'Human Readable Regexp Wrapper and Tools
41
55
 
42
56
  '
@@ -50,6 +64,15 @@ files:
50
64
  - lib/l43/rgx/compile.rb
51
65
  - lib/l43/rgx/errors.rb
52
66
  - lib/l43/rgx/methods.rb
67
+ - lib/l43/rgx/r.rb
68
+ - lib/l43/rgx/r/and.rb
69
+ - lib/l43/rgx/r/ast.rb
70
+ - lib/l43/rgx/r/class_methods.rb
71
+ - lib/l43/rgx/r/fail.rb
72
+ - lib/l43/rgx/r/many.rb
73
+ - lib/l43/rgx/r/methods.rb
74
+ - lib/l43/rgx/r/or.rb
75
+ - lib/l43/rgx/r/reducer.rb
53
76
  - lib/l43/rgx/result.rb
54
77
  - lib/l43/rgx/version.rb
55
78
  homepage: https://codeberg.org/lab419/l43_rgx