l43_rmap 0.1.1 → 0.1.2

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: b8ba2487a434243ab2888e9b24af15fd55a85c63aa15d6d3d5bb67aa52c4bc59
4
- data.tar.gz: 32ebaa0d8ceb99d9f07361269dd9665ad03afcb437dda0aaabf2bcd31e1b04ed
3
+ metadata.gz: b95afae624f3dd5ce13c2761678d547694c5a9e11a80ea7ca23fb8cf94604b53
4
+ data.tar.gz: d813c9e6dc4186232cdacf3412d14c869cc40ec055a9f240e9a0774bed781fd8
5
5
  SHA512:
6
- metadata.gz: 5ff67dc0c2829b61ba98bd427308b7bd5b8de03adfb8c12729412a8d68fb49036f4a76410c6fcff487af07bd8e78d73cd9d7f06e8b670dfb1663503637968691
7
- data.tar.gz: 2d62c95dedcd2a9e7cb09feb8b7a654a9ce005cc0641bb90b188216410f34c9c55671c0cf3765340aa3f750db2c8aebf60e86256e38fec1f9fe7d6c3dcacaad6
6
+ metadata.gz: a7d2d9ac1512236a1a3d8f393284392e43bebaf58423842d61cff0f9ccc5b32b59457531d4791c6af26f0868abf02669e590812300f632c99be028852eb8cb73
7
+ data.tar.gz: 03d07b9bf563eb0ddb6a4cf2545cc66aba27a0d478745761756190d15f338275f62ac3b1533429092e2b8461855b6627a107525dcd89077857d727d5889d2627
@@ -4,10 +4,12 @@ module L43Rmap
4
4
  class Cli
5
5
  module Color
6
6
  def arg(name) = [:bold, :blue, "<#{name}>", :reset]
7
+ def arg1(name) = [:bold, :blue, name, :reset]
7
8
 
8
9
  def kwd(name) = [" kwd ", :bold, :cyan, name, ":", :reset]
9
10
 
10
11
  def named(field) = [2, :bold, :blue, "%", field, :reset]
12
+
11
13
  def puterr(*chunks)
12
14
  putcol(:red, :bold, "ERROR: ", :white, *chunks)
13
15
  false
@@ -65,6 +65,7 @@ module L43Rmap
65
65
  putcol
66
66
  putcol(:bold, :blue, "predefined patterns", :white, " are the following:")
67
67
  putcol
68
+ putcol(4, :bold, :magenta, "ignore_ws", :white, " same as ", arg1("(unless (m '\\A\\s*\\z') %)"))
68
69
  putcol(4, :bold, :magenta, "mv_to_ms", :white, " same as ", :bold, :blue, "mv % %m-(lpad %n 0 4).(ext)")
69
70
  putcol(4, :bold, :magenta, "mv_to_mse", :white, " same as ", :bold, :blue, "mv (se) %m-(lpad %n 0 4).(ext)")
70
71
  :helped
data/lib/l43_rmap/cli.rb CHANGED
@@ -50,6 +50,7 @@ module L43Rmap
50
50
  .flag(:help_pattern, desc: ["Describe ", :bold, :blue, "pattern"], stop: true)
51
51
  .flag(:help_predefined, desc: ["Describe ", :bold, :blue, "predefined patterns"], stop: true)
52
52
  .flag(:help_sexp, desc: ["Describe ", :bold, :blue, "s-expressions"], stop: true)
53
+ .flag(:randomize, :r, desc: ["Does not write to the ", arg1("output stream"), " immideately but into a buffer, the buffer is then randomized before being printed to ", arg1("output stream")])
53
54
  .section("Options")
54
55
  .keyword(:input, :i, desc: ["file to read from, defaults to ", :bold, :cyan, '$stdin'], default: $stdin)
55
56
  .keyword(:output, :o, desc: ["file to write to, defaults to ", :bold, :cyan, '$stdout'], default: $stdout)
@@ -62,7 +63,7 @@ module L43Rmap
62
63
  end
63
64
 
64
65
  def _run
65
- runtime.run(kwds.input, kwds.output)
66
+ runtime.run(kwds.input, kwds.output, randomize: kwds.randomize)
66
67
  :ok
67
68
  end
68
69
 
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module L43Rmap
4
+ module Functions
5
+ module Predefined
6
+ module Str extend self
7
+
8
+ def gsub(rt, subject, pattern, replacement=nil)
9
+ return _gsub(subject, pattern, replacement) if replacement
10
+
11
+ _gsub(rt.line, subject, pattern)
12
+ end
13
+
14
+ def lpad(_rt, subject, filler, length)
15
+ subject.to_s.rjust(length.to_i, filler.to_s)
16
+ end
17
+
18
+ def match(rt, rgx, subject=nil)
19
+ subject ||= rt.line
20
+ r = Regexp.compile(rgx)
21
+ r.match(subject)
22
+ end
23
+
24
+ def sub(rt, subject, pattern, replacement=nil)
25
+ return _sub(subject, pattern, replacement) if replacement
26
+
27
+ _sub(rt.line, subject, pattern)
28
+ end
29
+
30
+ private
31
+
32
+ def _gsub(subject, pattern, replacement)
33
+ rgx = _mk_rgx(pattern)
34
+ subject&.gsub(rgx, replacement)
35
+ end
36
+
37
+ def _mk_posix(pattern)
38
+ case pattern
39
+ in *klass, "*"
40
+ Regexp.compile("[[:#{klass.join}:]]*")
41
+ in *klass, "+"
42
+ Regexp.compile("[[:#{klass.join}:]]+")
43
+ in *klass, "?"
44
+ Regexp.compile("[[:#{klass.join}:]]?")
45
+ else
46
+ Regexp.compile("[[:#{pattern.join}:]]")
47
+ end
48
+ end
49
+
50
+ def _mk_rgx(pattern)
51
+ case pattern
52
+ when Symbol
53
+ _mk_posix(pattern.to_s.grapheme_clusters)
54
+ when String
55
+ Regexp.compile(pattern)
56
+ else
57
+ raise ArgumentError, "pattern must be a String or Symbol, but was #{pattern.inspect}"
58
+ end
59
+ end
60
+
61
+ def _sub(subject, pattern, replacement)
62
+ rgx = _mk_rgx(pattern)
63
+ subject&.sub(rgx, replacement)
64
+ end
65
+
66
+ end
67
+ end
68
+ end
69
+ end
70
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative '../evaluator/evaluations'
4
4
  require_relative 'predefined/shell'
5
+ require_relative 'predefined/str'
5
6
 
6
7
  module L43Rmap
7
8
  module Functions
@@ -38,14 +39,12 @@ module L43Rmap
38
39
  end
39
40
  end
40
41
 
41
- def lpad(_rt, subject, filler, length)
42
- subject.to_s.rjust(length.to_i, filler.to_s)
43
- end
44
-
45
- def match(rt, rgx, subject=nil)
46
- subject ||= rt.line
47
- r = Regexp.compile(rgx)
48
- r.match(subject)
42
+ def unless_fn(_rt, cond, false_branch, true_branch=nil)
43
+ if cond
44
+ true_branch
45
+ else
46
+ false_branch
47
+ end
49
48
  end
50
49
  end
51
50
 
@@ -35,11 +35,23 @@ module L43Rmap
35
35
 
36
36
  # Names only available inside of S-Expressions
37
37
  DefinedFunctions = {
38
+ # Logic
39
+ "if" => Function.new("if") { |*args| Predefined.if_fn(*args) },
40
+ "unless" => Function.new("unless") { |*args| Predefined.unless_fn(*args) },
41
+
42
+ # File
38
43
  "ext" => Function.new("ext") { |*args| Predefined.file_extension(*args) },
44
+
45
+ # Int
39
46
  "inc" => Function.new("inc") { |*args| Predefined.inc(*args) },
40
- "if" => Function.new("if") { |*args| Predefined.if_fn(*args) },
41
- "lpad" => Function.new("lpad") { |*args| Predefined.lpad(*args) },
42
- "m" => Function.new("match") { |*args| Predefined.match(*args) },
47
+
48
+ # Str
49
+ "lpad" => Function.new("lpad") { |*args| Predefined::Str.lpad(*args) },
50
+ "gsub" => Function.new("gsub") { |*args| Predefined::Str.gsub(*args) },
51
+ "m" => Function.new("match") { |*args| Predefined::Str.match(*args) },
52
+ "sub" => Function.new("sub") { |*args| Predefined::Str.sub(*args) },
53
+
54
+ # Shell
43
55
  "se" => Function.new("se") { |*args| Predefined::Shell.se(*args) },
44
56
  }
45
57
 
@@ -57,10 +69,16 @@ module L43Rmap
57
69
  Function.new("fallback to #{name}") do |rt, rcv, *args|
58
70
  rcv.send(name, *args)
59
71
  rescue NoMethodError
60
- rcv.to_i.send(name, *args)
72
+ try_sending(name, rcv, *args)
61
73
  end
62
74
  end
63
75
  end
76
+
77
+ def try_sending(name, rcv, *args)
78
+ rcv.to_i.send(name, *args)
79
+ rescue NoMethodError
80
+ rcv.to_s.send(name, *args)
81
+ end
64
82
  end
65
83
  end
66
84
  end
@@ -54,7 +54,7 @@ module L43Rmap
54
54
  SQUOTE = /\A'/
55
55
  SquoteParser = RgxParser.new(SQUOTE)
56
56
 
57
- SYMBOL = /\A:([[:alpha:]][_[:alnum:]]*[\!\?]?)/
57
+ SYMBOL = /\A:([^\s\)]+)/
58
58
  SymbolParser = RgxParser.new(SYMBOL) { Sym.new(it) }
59
59
 
60
60
  WS = /\A\s+/
@@ -4,6 +4,7 @@ module L43Rmap
4
4
  module PredefinedPatterns extend self
5
5
 
6
6
  Patterns = {
7
+ ignore_ws: "(unless (m '\A\s*\z') %)",
7
8
  mv_to_ms: 'mv % (lpad %n 0 4).(ext)',
8
9
  mv_to_mse: 'mv (se) (lpad %n 0 4).(ext)',
9
10
  }
@@ -5,19 +5,20 @@ require_relative 'runtime/line_time'
5
5
 
6
6
  module L43Rmap
7
7
  class Runtime
8
- attr_reader :chunks, :current, :input_stream, :output_stream, :rand, :terminated, :time
8
+ # attr_reader :chunks, :current, :input_stream, :output_stream, :rand, :randomize, :terminated, :time
9
+ attr_reader :chunks, :current, :input_stream, :output_stream, :randomize, :terminated, :time
9
10
 
10
11
  DefaultDecRandomMax = 10_000
11
12
 
12
- def run(input, output)
13
+ def run(input, output, randomize: false)
13
14
  @output_stream = make_output_stream(output)
14
15
  @input_stream = make_input_stream(input)
15
- # @rand = SecureRandom.random_number
16
16
  @time = Time.now
17
- # p(time1: time.to_f)
17
+ @randomize = randomize
18
18
 
19
19
  execute!
20
20
  rescue EOFError
21
+ maybe_output_buffer
21
22
  end
22
23
 
23
24
  def random_hex = @__randomhex__ ||= SecureRandom.hex
@@ -52,8 +53,7 @@ module L43Rmap
52
53
  in :terminate | :term
53
54
  break
54
55
  in Array
55
- output_stream << result.join
56
- output_stream << "\n"
56
+ push_line(result.join)
57
57
  else
58
58
  raise "BAD RETURN FROM LineTime: #{result.inspect}"
59
59
  end
@@ -73,6 +73,28 @@ module L43Rmap
73
73
  return stream unless String === stream
74
74
  File.open(stream, 'w')
75
75
  end
76
+
77
+ def maybe_output_buffer
78
+ return unless randomize
79
+
80
+ output_buffer
81
+ .sort_by { rand }
82
+ .each do
83
+ output_stream << it
84
+ output_stream << "\n"
85
+ end
86
+ end
87
+
88
+ def output_buffer = @__output_buffer__ ||= []
89
+
90
+ def push_line(line)
91
+ if randomize
92
+ output_buffer << line
93
+ else
94
+ output_stream << line
95
+ output_stream << "\n"
96
+ end
97
+ end
76
98
  end
77
99
  end
78
100
  # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module L43Rmap
4
- VERSION = '0.1.1'
4
+ VERSION = '0.1.2'
5
5
  end
6
6
  # SPDX-License-Identifier: AGPL-3.0-or-later
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: l43_rmap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Dober
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-06-03 00:00:00.000000000 Z
10
+ date: 2026-06-06 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: ostruct
@@ -91,6 +91,7 @@ files:
91
91
  - lib/l43_rmap/functions.rb
92
92
  - lib/l43_rmap/functions/predefined.rb
93
93
  - lib/l43_rmap/functions/predefined/shell.rb
94
+ - lib/l43_rmap/functions/predefined/str.rb
94
95
  - lib/l43_rmap/parsing/chunk_parser.rb
95
96
  - lib/l43_rmap/parsing/input.rb
96
97
  - lib/l43_rmap/parsing/parse_state.rb
@@ -100,17 +101,6 @@ files:
100
101
  - lib/l43_rmap/runtime.rb
101
102
  - lib/l43_rmap/runtime/line_time.rb
102
103
  - lib/l43_rmap/version.rb
103
- - lib/peg.backup/all.rb
104
- - lib/peg.backup/combinators.rb
105
- - lib/peg.backup/combinators/implementation.rb
106
- - lib/peg.backup/parser.rb
107
- - lib/peg.backup/parser/input.rb
108
- - lib/peg.backup/parsers.rb
109
- - lib/peg.backup/parsers/advanced_parsers.rb
110
- - lib/peg.backup/parsers/base_parsers.rb
111
- - lib/peg.backup/parsers/common_parsers.rb
112
- - lib/peg.backup/parsers/true_set.rb
113
- - lib/peg.backup/result.rb
114
104
  homepage: https://codeberg.org/lab419/rmap
115
105
  licenses:
116
106
  - AGPL-3.0-or-later
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'combinators'
4
- require_relative 'parsers'
5
- require_relative 'parsers'
6
- module Peg
7
- module All
8
- include Combinators
9
- include Parsers
10
- end
11
- end
12
- # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,144 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../../enumerable'
4
- module Peg
5
- class InfiniteLoop < Exception; end
6
-
7
- module Combinators
8
- module Implementation
9
-
10
- def _debug(parser, name: nil)
11
- Parser.new(parser.name) do |input, _name|
12
- puts "debugging #{name || parser.name}: #{input.inspect}"
13
- result = Parser.parse(parser, input)
14
- puts "debugging #{name || parser.name}: #{result}"
15
- result
16
- end
17
- end
18
-
19
- def _lookahead(parsers, name:)
20
- parser = _select(parsers)
21
- Parser.new(name || "lookahead(#{parser.name})") do |input, name|
22
- case Parser.parse(parser, input)
23
- in {ok: true}
24
- {ok: true, ast: nil, input:}
25
- in error
26
- error
27
- end
28
- end
29
- end
30
-
31
- # def _map(parser, name, mapper)
32
- # name ||= "map(#{parser.name})"
33
- # Parser.new(name) do |input, _name|
34
- # case Parser.parse(parser, input)
35
- # in {ok: true, ast: ast, input: new_input}
36
- # {ok: true, ast: mapper.(ast), input: new_input}
37
- # in error
38
- # error
39
- # end
40
- # end
41
- # end
42
-
43
- def _map_result(parser, name, mapper)
44
- Parser.new(name) do |input, name|
45
- result = Parser.parse(parser, input)
46
- # require "debug"; binding.break
47
- mapper.(result)
48
- end
49
- end
50
-
51
- def _many(parser, max:, min:, name:)
52
- Parser.new(name) do |input, _name|
53
- total_ast = []
54
- original_input = input
55
- current_input = input
56
- match_count = 0
57
- loop do
58
- if current_input.empty?
59
- break Result.ok(ast: total_ast, input:) if match_count >= min
60
- break Result.nok(error: "many #{name} did not succeed the required #{min} times, but only #{match_count}", input: original_input, name:)
61
- end
62
-
63
- case Parser.parse(parser, current_input)
64
- in {ok: true, ast:, input:}
65
- raise InfiniteLoop, "must not parse zero width inside many in parser: #{parser.name}" if input.pos == current_input.pos
66
- current_input = input
67
- total_ast = [*total_ast, ast]
68
- match_count += 1
69
- break Result.ok(ast: total_ast, input:) if max && match_count >= max
70
- else
71
- break Result.ok(ast: total_ast, input:) if match_count >= min
72
- break Result.nok(error: "many #{name} did not succeed the required #{min} times, but only #{match_count}", input: original_input, name:)
73
- end
74
- end
75
- end
76
- end
77
-
78
- def _satisfy(parser, name:, &satisfier)
79
- Parser.new(name || "satisfy(#{parser.name})") do |input, name|
80
- original_input = input
81
- case Parser.parse(parser, input)
82
- in {ok: false} => error
83
- error
84
- in {ok: true, ast:, input:} => result
85
- ok = satisfier.(ast)
86
- if ok == true
87
- result
88
- elsif ok
89
- {ok: true, ast: ok, input:}
90
- else
91
- {ok: false, input: original_input, error: "satisfier #{name} failed", name: name}
92
- end
93
- end
94
- end
95
- end
96
-
97
- def _select(*parsers, name: nil)
98
- parsers = parsers.flatten
99
- raise ArgumentError, "all parsers must be instances of Parser" unless parsers.all? { Parser === it }
100
- Parser.new(name || "select #{parsers.map(&:name).join(", ")}") do |input, name|
101
- result = {ok: false, error: "No parser matched in select named #{name}", input:}
102
- parsers.each do |parser|
103
- # case p(Parser.parse(parser, input))
104
- this_result = Parser.parse(parser, input)
105
- # require "debug"; binding.break
106
- case this_result
107
- in {ok: true} => result
108
- break result
109
- in _
110
- nil
111
- end
112
- end
113
- result
114
- end
115
- end
116
-
117
- def _sequence(parsers, name)
118
- name ||= "seq(#{parsers.map(&:name).join(", ")})"
119
- Parser.new(name) do |input, _name|
120
- original_input = input
121
- result = parsers.reduce_while [input, []] do |(input, ast), parser|
122
- # require "debug"; binding.break
123
- parsed = Parser.parse(parser, input)
124
- # p parsed
125
- case parsed
126
- in {ok: true, ast: ast_node, input:}
127
- cont_reduce([input, [*ast, ast_node]])
128
- in {ok: false, error:}
129
- halt_reduce(Result.nok(input: original_input, error:, name: name))
130
- end
131
- end
132
-
133
- case result
134
- in {ok: false} => error
135
- error
136
- in [input, ast]
137
- Result.ok(ast:, input:)
138
- end
139
- end
140
- end
141
- end
142
- end
143
- end
144
- # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,82 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'parser'
4
- require_relative 'combinators/implementation'
5
- module Peg
6
- module Combinators extend self
7
- include Implementation
8
-
9
- def lookahead(*parsers, name: nil)
10
- parsers = make_parsers(*parsers)
11
- _lookahead(parsers, name:)
12
- end
13
-
14
- def many(*parsers, max: nil, min: 0, name: nil)
15
- name ||= "many(#{parsers.map(&:name).join(", ")})"
16
- case parsers
17
- in []
18
- raise ArgumentError, "missing parser"
19
- in [parser]
20
- _many(parser, max:, min:, name:)
21
- in _
22
- many(select(*parsers), max:, min:, name:)
23
- end
24
- end
25
-
26
- def map(parser, name: nil, &mapper)
27
- raise ArgumentError, "missing mapper function" unless mapper
28
- raise ArgumentError, "mapper function must have arity 1" unless mapper.arity == 1
29
- name ||= "map(#{parser.name})"
30
- Parser.new(name) do |input|
31
- Parser.parse(parser, input).map(&mapper)
32
- end
33
- end
34
- alias_method :_map, :map
35
-
36
- def map_result(parser, name: nil, &mapper)
37
- raise ArgumentError, "missing mapper function" unless mapper
38
- raise ArgumentError, "mapper function must have arity 1" unless mapper.arity == 1
39
- _map_result(parser, name || "map_result(#{parser.name})", mapper)
40
- end
41
-
42
- def maybe(parser, name: nil)
43
- Parser.new(name || "maybe(#{parser.name})") do |input, name|
44
- case Parser.parse(parser, input)
45
- in {ok: false}
46
- {ok: true, ast: nil, input:}
47
- in success
48
- success
49
- end
50
- end
51
- end
52
-
53
- def satisfy(parser, name: nil, &satisfier)
54
- raise ArgumentError, "missing satisfier block" unless satisfier
55
- parser = make_parser(parser)
56
- _satisfy(parser, name, satisfier)
57
- end
58
-
59
- def select(*parsers, name: nil)
60
- case parsers
61
- in []
62
- raise ArgumentError, "missing parser in select"
63
- in [parser]
64
- raise ArgumentError, "one parser in a selection is a NOP, remove the select call"
65
- in _
66
- _select(*make_parsers(parsers), name)
67
- end
68
- end
69
-
70
- def sequence(*parsers, name: nil)
71
- case parsers
72
- in []
73
- raise ArgumentError, "missing parser"
74
- in [parser]
75
- raise ArgumentError, "one parser in a sequence is a NOP, remove the sequence call"
76
- in _
77
- _sequence(make_parsers(parsers), name)
78
- end
79
- end
80
- end
81
- end
82
- # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Peg
4
- class Parser
5
- class Input
6
- attr_reader :content, :pos
7
-
8
- def advance(by=1)
9
- self.class.new(content.drop(by), pos+by)
10
- end
11
-
12
- def empty? = content.empty?
13
-
14
- def show(count)
15
- raise ArgumentError, "count must be an integer > 5" unless Integer === count && count > 5
16
- return content.join("") if content.length < count
17
-
18
- "#{content.take(count-3).join}..."
19
- end
20
-
21
- def ==(other)
22
- self.class === other &&
23
- other.content == content &&
24
- other.pos == pos
25
- end
26
-
27
- private
28
- def initialize(content, pos=1)
29
- @content = content
30
- @pos = pos
31
- end
32
-
33
- end
34
- end
35
- end
36
- # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'combinators/implementation'
4
- require_relative 'parser/input'
5
- require_relative 'parsers'
6
- require_relative 'result'
7
-
8
- module Peg
9
- class Parser
10
- include Peg::Combinators::Implementation
11
-
12
- attr_reader :parse_fn, :name
13
-
14
- def self.parse(parser, input)
15
- raise ArgumentError, "parser must be an instance of #{self}" unless self === parser
16
- case input
17
- when String
18
- parser.parse_fn.(Peg::Parser::Input.new(input.grapheme_clusters))
19
- when Peg::Parser::Input
20
- parser.parse_fn.(input)
21
- else
22
- raise ArgumentError, "input must be a string or instance of Input" unless self.class === parser
23
- end
24
- end
25
-
26
- def and(*parsers, name: nil) = _sequence([self, *parsers], name)
27
-
28
- def debug(name: nil) = _debug(self, name:)
29
-
30
- def many(name: nil, max: nil, min: 0) = _many(self, name:, max:, min:)
31
- def map(name: nil, &blk) = Peg::Combinators.map(self, name:, &blk)
32
-
33
- def or(*parsers, name: nil) = _select(self, *parsers, name:)
34
-
35
- def satisfy(name: nil, &satisfier) = _satisfy(self, name:, &satisfier)
36
-
37
- private
38
- def initialize(name, &blk)
39
- raise ArgumentError, "blk must be provided as a parse function" unless blk
40
- @name = name
41
- @parse_fn = blk
42
- end
43
-
44
- end
45
- end
46
- # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../combinators'
4
- require_relative '../parsers'
5
- module Peg
6
- module Parsers
7
- module AdvancedParsers
8
- include Peg::Combinators
9
- include Peg::Parsers
10
-
11
- def list_parser(element_parser:, seperator_parser:, name: "list parser")
12
- sequence(
13
- element_parser,
14
- many(
15
- sequence(
16
- seperator_parser, element_parser
17
- )
18
- .map { it[1] }
19
- )
20
- )
21
- .map { it.flatten.compact }
22
- end
23
- end
24
- end
25
- end
26
- # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,124 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'true_set'
4
- module Peg
5
- module Parsers
6
- module BaseParsers
7
-
8
- # Parses a character which is a member of any of the `char_classes`
9
- def char_class_parser(*char_classes, name: nil)
10
- case char_classes
11
- in [char_class]
12
- _1_char_class_parser(char_class, name:)
13
- else
14
- _char_classes_parser(*char_classes, name:)
15
- end
16
- end
17
-
18
- def char_parser(set=nil, name: nil, negate: false)
19
- set = mk_set(set)
20
- name ||= "char_parser(#{set.inspect})"
21
- Parser.new(name) do |input|
22
- case input.content
23
- in []
24
- Result.nok(error: "unexpected end of input", input:, name:)
25
- in [h, *]
26
- if set.member?(h) && !negate || !set.member?(h) && negate
27
- Result.ok(ast: h, input: input.advance)
28
- else
29
- Result.nok(input:, error: "#{h} is not member of the required set #{set}", name:)
30
- end
31
- end
32
- end
33
- end
34
-
35
- def end_parser(name: nil)
36
- name ||= "end_parser"
37
- Parser.new(name) do |input|
38
- case input.content
39
- in []
40
- Result.ok(ast: nil, input:)
41
- in _
42
- Result.nok(input: input, error: "not at end of input", name:)
43
- end
44
- end
45
- end
46
-
47
- def make_parser(parser)
48
- case parser
49
- when String
50
- char_parser(parser)
51
- else
52
- parser
53
- end
54
- end
55
-
56
- def make_parsers(*parsers)
57
- parsers
58
- .flatten
59
- .map { make_parser it }
60
- end
61
-
62
- private
63
-
64
- def _1_char_class_parser(char_class, name:)
65
- rgx = Regexp.compile("[[:#{char_class}:]]")
66
- name ||= "char_class_parser(:#{char_class})"
67
- Parser.new(name) do |input|
68
- case input.content
69
- in []
70
- Result.nok(error: "unexpected end of input", input:, name:)
71
- in [h, *]
72
- if rgx.match?(h)
73
- Result.ok(ast: h, input: input.advance)
74
- else
75
- Result.nok(input:, name:, error: "#{h} does not match the char class: :#{char_class}")
76
- end
77
- end
78
- end
79
- end
80
-
81
- def _char_classes_parser(*char_classes, name:)
82
- rgx = Regexp.compile("[#{_compile_char_classes(char_classes)}]")
83
- name ||= "char_class_parser(#{char_classes.inspect})"
84
- Parser.new(name) do |input|
85
- case input.content
86
- in []
87
- Result.nok(error: "unexpected end of input", input:, name:)
88
- in [h, *]
89
- if rgx.match?(h)
90
- Result.ok(ast: h, input: input.advance)
91
- else
92
- Result.nok(input:, name:, error: "#{h} does not match the char class: :#{char_classes}")
93
- end
94
- end
95
- end
96
- end
97
-
98
- def _compile_char_class(char_class)
99
- case char_class
100
- when Symbol
101
- "[:#{char_class}:]"
102
- when String
103
- "[#{char_class}]"
104
- end
105
- end
106
-
107
- def _compile_char_classes(char_classes)
108
- "[" +
109
- char_classes
110
- .map { _compile_char_class it }
111
- .join + "]"
112
- end
113
-
114
- def mk_set(set)
115
- if set
116
- Set.new(set.grapheme_clusters)
117
- else
118
- TrueSet
119
- end
120
- end
121
- end
122
- end
123
- end
124
- # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,60 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../combinators'
4
- module Peg
5
- module Parsers
6
- module CommonParsers
7
- include Peg::Combinators
8
-
9
- def id_parser(name: nil, lead_class: :alpha, inner_class: [:alnum, "_"] )
10
- sequence(char_class_parser(*Array(lead_class)),
11
- many(char_class_parser(*Array(inner_class)), name:),
12
- name:
13
- ).map {
14
- it.flatten.join.to_sym
15
- }
16
- end
17
-
18
-
19
- # Just parses any string starting with either a `+` or `-` sign followed by at least one
20
- # _decimal digit_.
21
- #
22
- # **N.B.** that leading zeroes are parsed (and therefore ignored) and will not parse
23
- # it as a hexadecimal or octal number
24
- def int_parser(name=nil)
25
- parser = sequence(
26
- maybe(char_parser("+-")),
27
- many(char_class_parser(:digit), min: 1),
28
- name: name || "int_parser")
29
- map_result(parser) { _make_int(it) }
30
- end
31
-
32
- def literal_set(set, name: nil, lead_class: :alpha, inner_class: [:alnum, "_"])
33
- id_parser(name:, lead_class:, inner_class:)
34
- .satisfy { set.member? it }
35
- end
36
-
37
- def ws_parser(name=nil)
38
- map(
39
- many(
40
- char_class_parser(:space),
41
- min: 1,
42
- name: "ws_parser"
43
- )
44
- ) {|_| nil}
45
- end
46
-
47
- private
48
-
49
- def _make_int(result)
50
- case result
51
- in {ok: true, ast:}
52
- result.merge(ast: ast.join.to_i)
53
- in {input:}
54
- result.merge(error: "Not an integer at #{input.show(10).inspect} in int_parser")
55
- end
56
- end
57
- end
58
- end
59
- end
60
- # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Peg
4
- module Parsers
5
- module TrueSet extend self
6
- def member?(_) = true
7
- end
8
- end
9
- end
10
- # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "parsers/base_parsers"
4
- require_relative "parsers/common_parsers"
5
- module Peg
6
- module Parsers
7
- extend BaseParsers
8
- include BaseParsers
9
-
10
- extend CommonParsers
11
- include CommonParsers
12
- end
13
- end
14
- # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,78 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Peg
4
- class Result
5
- class IllegalState < RuntimeError
6
- end
7
-
8
- attr_reader :ast, :error, :input, :ok
9
-
10
- def self.ok(ast:, input:) = new(ok: true, ast:, input:)
11
-
12
- def self.nok(input:, error:, name: nil)
13
- error = error + " in parser: #{name}" if name
14
- new(ok: false, input:, error:)
15
- end
16
-
17
- def deconstruct_keys(*, **) = to_h
18
-
19
- def map(&blk)
20
- return self unless ok
21
- self.class.ok(ast: blk.(ast), input:)
22
- end
23
-
24
- def merge(ast: nil, error: nil, input: nil)
25
- @ast = ast if ast
26
- @error = error if error
27
- @input = input if input
28
- self
29
- end
30
-
31
- def or(input:, error:, name: nil) = self.class.nok(input:, error:, name:)
32
-
33
- def reduce(values, &blk)
34
- result = self
35
- values.each do |ele|
36
- result = blk.(result, ele)
37
- break result unless result.ok
38
- end
39
- result
40
- end
41
-
42
- def to_h = {ast:, error:, input:, ok:}
43
-
44
- def to_s = inspect
45
-
46
- def update(&updater)
47
- raise IllegalState, "must not update an error result" unless ok
48
- map(&updater)
49
- end
50
-
51
- def update_or(input:, error:, name: nil, &updater)
52
- return map(&updater) if ok
53
-
54
- self.class.nok(input:, error:, name:)
55
- end
56
-
57
- def while(input: nil, error: nil, name: nil, &blk)
58
- result = self
59
- count = 0
60
- loop do
61
- return input ? self.class.nok(input:, error:, name:) : result unless result.ok
62
- result = blk.(self, count)
63
- count += 1
64
- end
65
- result
66
- end
67
-
68
- private
69
- def initialize(ok:, input:, ast: nil, error: nil)
70
- @ast = ast
71
- @error = error
72
- @input = input
73
- @ok = ok
74
- end
75
-
76
- end
77
- end
78
- # SPDX-License-Identifier: AGPL-3.0-or-later