l43_rmap 0.1.4 → 0.1.5

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: d55c3692c807fd46a368f265ddfb492640c44e134cfcd56660d831482612191e
4
- data.tar.gz: 25c329e6544381cb52b5b386e7fe73d62c00c14d9e7179de9f324a952846f4a2
3
+ metadata.gz: 27b3cc1e893f020ac4ddac1a4e58ed9bf6f839d3bbd688f0afc9f5b284f518e1
4
+ data.tar.gz: 1ba30b1149bb306e419f380a55d0b801d01926b6513e828f7e50469b3bb10d13
5
5
  SHA512:
6
- metadata.gz: 99ba2278ccd9f935472b12b4c983147255e3daeebfc3084000e6229a86db1fd7c8b71fb544b04707d171517ee7bb46e364955a711613e13e3793099d7c513319
7
- data.tar.gz: 966bc037f9c52c256debfc74682e8b05ce2bf90bc9381afc54a808dff5d912e10caef3628b6fb03410b825433c3e42ab123dce78cc3924329ff71599e62cf36f
6
+ metadata.gz: 29dff91cc35c9f721c5dd46c4b0ad526df91a403843f96153fb567bc3113df74a45cf0e32edee5da92935aa068ef093c92d969f08efce5f1aa3da517e24714b8
7
+ data.tar.gz: 3c04d57c6b251897fabf78ffcb91249ebb166bc6c74523cdb8976203faafe85cd9675238a9d9ca99851ec5ffa0681b5b9f074bd380dc91e521e1fbb13a327be4
data/lib/l43_rmap/ast.rb CHANGED
@@ -13,6 +13,13 @@ module L43Rmap
13
13
  super(value:, type: :bareword)
14
14
  end
15
15
  end
16
+ class FieldRange < Node
17
+ def initialize(first, last)
18
+ first = first.pred if first > 0
19
+ last = last.pred if last > 0
20
+ super(value: first..last, type: :field_range)
21
+ end
22
+ end
16
23
  class IndexedField < Node
17
24
  def initialize(value)
18
25
  super(value:, type: :indexed_field)
@@ -65,6 +65,8 @@ module L43Rmap
65
65
  putcol
66
66
  putcol(:bold, :blue, "predefined patterns", :white, " are the following:")
67
67
  putcol
68
+ putcol(4, :bold, :magenta, "date_grep", :white, ":", :blue, "<date-range>", :white, " same as ", arg1("(if (fin % <date-range>) % :ignore)"))
69
+ putcol(4, :bold, :magenta, "date_grep", :white, ":", :blue, "<subject>,<date-range>", :white, " same as ", arg1("(if (fin <subject> <date-range>) <subject> :ignore)"))
68
70
  putcol(4, :bold, :magenta, "ignore_ws", :white, " same as ", arg1("(unless (m '\\A\\s*\\z') %)"))
69
71
  putcol(4, :bold, :magenta, "mv_to_ms", :white, " same as ", :bold, :blue, "mv % %m-(lpad %n 0 4).(ext)")
70
72
  putcol(4, :bold, :magenta, "mv_to_mse", :white, " same as ", :bold, :blue, "mv (se) %m-(lpad %n 0 4).(ext)")
data/lib/l43_rmap/cli.rb CHANGED
@@ -40,10 +40,13 @@ module L43Rmap
40
40
  .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")])
41
41
  .flag(:version, :v, desc: ["Just print current version ",dimmed(version_string)], stop: true)
42
42
  .section("Options")
43
- .keyword(:input, :i, desc: ["file to read from, defaults to ", :bold, :cyan, '$stdin'], default: $stdin)
43
+ .keyword(:glob, :g, desc: ["do not read from ", :bold, :cyan, "<input>", :reset, " but reads the names of the expanded ", :bold, :blue, "<glob>", :reset, :dim,
44
+ ". Conflicts with ", :bold, :blue, "input: "], default: nil)
45
+ .keyword(:input, :i, desc: ["file to read from, defaults to ", :bold, :cyan, '$stdin'], default: nil)
44
46
  .keyword(:limit, :l, desc:["if specified, limits output to the given value of lines, it is applied, ", :bold, "after", :reset, " randomization (if applicable)"], default: nil, &:to_i)
45
47
  .keyword(:output, :o, desc: ["file to write to, defaults to ", :bold, :cyan, '$stdout'], default: $stdout)
46
48
  .keyword(:pattern, desc: ["use a predefined pattern instead of positional argument. ", :bold, "Conflicts with ", :blue, "pattern"], default: nil)
49
+ .post_check('must not provide input: and glob:') { !(it.glob && it.input) }
47
50
  end
48
51
 
49
52
  def _compile
@@ -51,23 +54,23 @@ module L43Rmap
51
54
  end
52
55
 
53
56
  def _run
54
- runtime.run(kwds.input, kwds.output, randomize: kwds.randomize)
57
+ runtime.run(input, kwds.output, randomize: kwds.randomize)
55
58
  :ok
56
59
  end
57
60
 
58
61
  def check_args
59
62
  args in [pattern]
60
- @pattern = pattern
61
- return check_pattern if pattern
62
- return true if kwds.pattern
63
- puterr("need exactly one positional arg as ", :blue, "pattern", :reset, nil, "but got: ", :yellow, :bold, args.inspect)
64
- puthint
63
+ if pattern && kwds.pattern
64
+ puterr("must not have positonal arg as ", :blue, :bold, "pattern", :white, " and", kwd("pattern"))
65
+ puthint
66
+ false
67
+ else
68
+ @pattern = pattern || kwds.pattern || "%"
69
+ end
65
70
  end
66
71
 
67
72
  def check_pattern
68
- return true unless kwds.pattern
69
- puterr("must not have positonal arg as ", :blue, :bold, "pattern", :white, " and", kwd("pattern"))
70
- puthint
73
+ return :ok unless kwds.pattern
71
74
  end
72
75
 
73
76
  def compiled
@@ -102,6 +105,13 @@ module L43Rmap
102
105
  putcol(:blue, :bold, {args:}.inspect)
103
106
  :ok
104
107
  end
108
+
109
+ def input
110
+ return Dir.glob(kwds.glob).lazy.filter { File.file?(it) } if kwds.glob
111
+
112
+ kwds.input || $stdin
113
+ end
114
+
105
115
  def replace_pattern_if_predefined
106
116
  return unless kwds.pattern
107
117
  @pattern = PredefinedPatterns.get_pattern(kwds.pattern)
@@ -21,6 +21,8 @@ module L43Rmap
21
21
  compile_function(value)
22
22
  in {type: :indexed_field, value:}
23
23
  compile_field(value)
24
+ in {type: :field_range, value:}
25
+ compile_field_range(value)
24
26
  in {type: :sexp, value:}
25
27
  compile_sexp(value)
26
28
  in {type: :str, value:}
@@ -36,6 +38,10 @@ module L43Rmap
36
38
  compile_subfield value.pred
37
39
  end
38
40
 
41
+ def compile_field_range(range)
42
+ Function.new("field range #{range}") { |rt| rt.fields[range]&.join(" ") || "" }
43
+ end
44
+
39
45
  def compile_function(name)
40
46
  Functions.get_field(name)
41
47
  # TODO: Fallbacks
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+
5
+ require_relative '../../tools/time'
6
+ module L43Rmap
7
+ module Functions
8
+ module Predefined
9
+ module Files extend self
10
+ def bn(rt, *args)
11
+ case args
12
+ in []
13
+ File.basename(rt.line)
14
+ in [fn]
15
+ File.basename(fn.to_s)
16
+ else
17
+ raise ArgumentError, "bn supports at most 1 argument #{args.inspect}"
18
+ end
19
+ end
20
+
21
+ def file_extension(rt, *args)
22
+ case args
23
+ in []
24
+ File.extname(rt.line)[1..]
25
+ in [fn]
26
+ File.extname(fn.to_s)[1..]
27
+ else
28
+ raise ArgumentError, "ext supports at most 1 argument #{args.inspect}"
29
+ end
30
+ end
31
+
32
+ def file_in(rt, *args)
33
+ case args
34
+ in []
35
+ raise ArgumentError, "fin needs at least 1 argument"
36
+ in [time]
37
+ _file_in(rt.line, time)
38
+ in [file, time]
39
+ _file_in(file, time)
40
+ else
41
+ raise ArgumentError, "fin supports at most 2 arguments #{args.inspect}"
42
+ end
43
+ end
44
+
45
+ private
46
+ def _file_in(file, time)
47
+ mtime = File.lstat(file).mtime.utc
48
+ time_range = Tools::Time.range_from_string(time.to_s)
49
+ result = Tools::Time.in_range(mtime, time_range)
50
+ # p(file: File.basename(file), mtime: mtime.iso8601, time:, start: time_range.start.iso8601, result:)
51
+ result
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -5,6 +5,7 @@ module L43Rmap
5
5
  module Predefined
6
6
  module Shell extend self
7
7
  def se(rt, *args)
8
+ require 'shellwords'
8
9
  case args
9
10
  in []
10
11
  rt.line.shellescape
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../evaluator/evaluations'
4
+ require_relative 'predefined/files'
4
5
  require_relative 'predefined/shell'
5
6
  require_relative 'predefined/str'
6
7
 
@@ -28,17 +29,6 @@ module L43Rmap
28
29
  end
29
30
  end
30
31
 
31
- def file_extension(rt, *args)
32
- case args
33
- in []
34
- File.extname(rt.line)[1..]
35
- in [fn]
36
- File.extname(fn.to_s)[1..]
37
- else
38
- raise ArgumentError, "ext needs at most 1 argument"
39
- end
40
- end
41
-
42
32
  def unless_fn(_rt, cond, false_branch, true_branch=nil)
43
33
  if cond
44
34
  true_branch
@@ -40,7 +40,9 @@ module L43Rmap
40
40
  "unless" => Function.new("unless") { |*args| Predefined.unless_fn(*args) },
41
41
 
42
42
  # File
43
- "ext" => Function.new("ext") { |*args| Predefined.file_extension(*args) },
43
+ "bn" => Function.new("bn") { |*args| Predefined::Files.bn(*args) },
44
+ "ext" => Function.new("ext") { |*args| Predefined::Files.file_extension(*args) },
45
+ "fin" => Function.new("ext") { |*args| Predefined::Files.file_in(*args) },
44
46
 
45
47
  # Int
46
48
  "inc" => Function.new("inc") { |*args| Predefined.inc(*args) },
@@ -40,6 +40,8 @@ module L43Rmap
40
40
  # p(input:)
41
41
  if chunk = ZerofieldParser.parse(input)
42
42
  chunk
43
+ elsif chunk = FieldRangeParser.parse(input)
44
+ chunk
43
45
  elsif chunk = IndexedFieldParser.parse(input)
44
46
  # dbg("FieldParser", chunk:)
45
47
  chunk
@@ -24,6 +24,8 @@ module L43Rmap
24
24
  ParseState.new(ast: postprocess(m), input: advance(input, m, m))
25
25
  in [m, a]
26
26
  ParseState.new(ast: postprocess(a), input: advance(input, a, m))
27
+ in [m, a, b]
28
+ ParseState.new(ast: postprocess2(a, b), input: advance(input, a, m))
27
29
  in [_, *rest]
28
30
  raise BadRgx, "too many captures; max = 1, had #{rest.count}"
29
31
  end
@@ -45,6 +47,7 @@ module L43Rmap
45
47
  end
46
48
 
47
49
  def postprocess(a) = blk ? blk.(a) : a
50
+ def postprocess2(a, b) = blk ? blk.(a, b) : [a, b]
48
51
  end
49
52
  end
50
53
  end
@@ -21,6 +21,9 @@ module L43Rmap
21
21
  ESCAPE = /\A%([%(])/
22
22
  EscapeParser = RgxParser.new(ESCAPE) { Verb.new(it) }
23
23
 
24
+ FIELD_RANGE = /\A%(-?\d+)-(-?\d+)/
25
+ FieldRangeParser = RgxParser.new(FIELD_RANGE) { |a, b| FieldRange.new(a.to_i, b.to_i) }
26
+
24
27
  INDEXED_FIELD = /\A%([-+]?\d+) ?/
25
28
  IndexedFieldParser = RgxParser.new(INDEXED_FIELD) { IndexedField.new(it.to_i) }
26
29
 
@@ -4,12 +4,32 @@ module L43Rmap
4
4
  module PredefinedPatterns extend self
5
5
 
6
6
  Patterns = {
7
+ date_grep: '(if (fin $2 "$1") $2 :ignore)',
7
8
  ignore_ws: "(unless (m '\A\s*\z') %)",
8
9
  mv_to_ms: 'mv % (lpad %n 0 4).(ext)',
9
10
  mv_to_mse: 'mv (se) (lpad %n 0 4).(ext)',
11
+
10
12
  }
11
13
 
12
- def get_pattern(key) = Patterns.fetch(key.to_sym)
14
+ def get_pattern(key)
15
+ key.split(":", 2) => key, *args
16
+ pattern = Patterns.fetch(key.to_sym)
17
+ case args
18
+ in []
19
+ pattern
20
+ else
21
+ pattern_with_arg(pattern, args.first.split(","))
22
+ end
23
+ end
24
+
25
+ private
26
+ def pattern_with_arg(pattern, args)
27
+ args << "%"
28
+ args.take(2).each_with_index.inject(pattern) do |pattern, (arg, idx)|
29
+ pat = /(?<!\$)\$#{idx.succ}(?!\d)/
30
+ pattern.gsub(pat, arg)
31
+ end
32
+ end
13
33
  end
14
34
  end
15
35
  # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -24,7 +24,7 @@ module L43Rmap
24
24
  def random_hex = @__randomhex__ ||= SecureRandom.hex
25
25
 
26
26
  def random_rd = @__randomrd__ ||= (format "%04d", SecureRandom.random_number(DefaultDecRandomMax))
27
-
27
+
28
28
  def image = chunks.map(&:image).join("\n")
29
29
 
30
30
  def terminate! = @terminated = true
@@ -38,10 +38,9 @@ module L43Rmap
38
38
 
39
39
  private
40
40
  def execute!
41
- lnb = 0
42
41
  loop do
43
- line = input_stream.readline chomp: true
44
- lnb = lnb + 1
42
+ input_stream.next => line, idx
43
+ lnb = idx.succ
45
44
  @current = LineTime.new(line:, lnb:, runtime: self)
46
45
  result = current.run
47
46
  # p(transformed: result)
@@ -58,18 +57,37 @@ module L43Rmap
58
57
  raise "BAD RETURN FROM LineTime: #{result.inspect}"
59
58
  end
60
59
  end
60
+ maybe_output_buffer
61
61
  end
62
62
 
63
63
  def initialize(chunks, limit: nil)
64
64
  @chunks = chunks
65
65
  @limit = limit || -1
66
66
  end
67
-
67
+
68
68
  def make_input_stream(stream)
69
- return stream unless String === stream
70
- File.open(stream)
69
+ case stream
70
+ when Array
71
+ stream.lazy.with_index
72
+ when String
73
+ File.foreach(stream, chomp: true).with_index
74
+ when Enumerator::Lazy
75
+ stream.with_index
76
+ else
77
+ make_generic_input_stream(stream)
78
+ end
79
+ end
80
+
81
+ def make_generic_input_stream(stream)
82
+ Enumerator.new do |y|
83
+ loop do
84
+ y << stream.readline(chomp: true)
85
+ end
86
+ rescue EOFError
87
+ raise StopIteration, "EOF of #{stream}"
88
+ end.with_index
71
89
  end
72
-
90
+
73
91
  def make_output_stream(stream)
74
92
  return stream unless String === stream
75
93
  File.open(stream, 'w')
@@ -81,11 +99,21 @@ module L43Rmap
81
99
  sorted = output_buffer
82
100
  .sort_by { rand }
83
101
  # p(sorted:)
84
- sorted.each do
85
- raise EOFError, "limit reached" if limit.zero?
86
- @limit -= 1
87
- output_stream << it
88
- output_stream << "\n"
102
+ sorted.each do
103
+ raise EOFError, "limit reached" if limit.zero?
104
+ @limit -= 1
105
+ output_stream << it
106
+ output_stream << "\n"
107
+ end
108
+ end
109
+
110
+ def next_line
111
+ @__reader__ ||=
112
+ case input
113
+ when Enumerator
114
+ -> { input.next }
115
+ else
116
+ -> { input.readline chomp: true }
89
117
  end
90
118
  end
91
119
 
@@ -97,6 +125,7 @@ module L43Rmap
97
125
  else
98
126
  raise EOFError, "limit reached" if limit.zero?
99
127
  @limit -= 1
128
+ # p(line:)
100
129
  output_stream << line
101
130
  output_stream << "\n"
102
131
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module L43Rmap
4
+ module Tools
5
+ module Time
6
+ module Rgx
7
+ def ndigits(n)
8
+ n = n - 4
9
+ rgx = [ "\\A",
10
+ "(\\d{2})",
11
+ "(\\d{2})",
12
+ *(n/2).times.map { "(\\d{2})?" },
13
+ "\\z"
14
+ ].join
15
+ Regexp.compile(rgx)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'l43/open_object'
4
+
5
+ require 'date'
6
+ require_relative 'time/rgx'
7
+ module L43Rmap
8
+ module Tools
9
+ module Time extend self
10
+ extend Rgx
11
+ Range = L43::OpenObject.def_class(:start, :stop)
12
+ BadTimeFormat = Class.new(RuntimeError)
13
+
14
+ def in_range(time, range)
15
+ time = time.to_time
16
+ left = (time >= range.start)
17
+ # p(time: time.iso8601, start: range.start.iso8601, left:)
18
+ left && (time <= range.stop)
19
+ end
20
+
21
+ def range_from_string(str)
22
+ case str.gsub(/[_T:]/, '').split(/(?:-|\.\.)/)
23
+ in [start]
24
+ _range_from_strings(start)
25
+ in [start, stop]
26
+ _range_from_strings(start, stop)
27
+ else
28
+ raise ArgumentError, "too many ranges in #{str}"
29
+ end
30
+ end
31
+
32
+ def time_from_string(str) = _mk_time(str)
33
+
34
+ private
35
+
36
+ YearMonthRgx = ndigits(12)
37
+
38
+ def _mk_time(str)
39
+ m = YearMonthRgx.match(str)
40
+ raise BadTimeFormat, "not a 4 to 12 digit even length str #{str.inspect}" unless m
41
+
42
+ m.captures.compact.map(&:to_i) => y, m, *rest
43
+
44
+ data = [2000 + y, m, *rest.take(4)]
45
+ # require "debug"; binding.break
46
+ DateTime.new(*data)
47
+ end
48
+
49
+ def _range_from_strings(startstr, stop=nil)
50
+ start = _mk_time(startstr)
51
+ stop = _complete_to_end(stop, start, startstr.length)
52
+ Range.new(
53
+ start: start.to_time,
54
+ stop: stop.to_time
55
+ )
56
+ end
57
+
58
+ def _complete_to_end(stop, start, len)
59
+
60
+ if stop
61
+ len = stop.length
62
+ start = _mk_time(stop) if stop
63
+ end
64
+
65
+ case len
66
+ when 4
67
+ start.next_month
68
+ when 6
69
+ start.next_day
70
+ when 8
71
+ start + 1r/24
72
+ when 10
73
+ start + 1r/1440
74
+ when 12
75
+ start + 1r/86400
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ # 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.4'
4
+ VERSION = '0.1.5'
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.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Dober
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-06-06 00:00:00.000000000 Z
10
+ date: 2026-06-08 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: ostruct
@@ -24,47 +24,61 @@ dependencies:
24
24
  - !ruby/object:Gem::Version
25
25
  version: 0.6.3
26
26
  - !ruby/object:Gem::Dependency
27
- name: l43_open_object
27
+ name: shellwords
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: 0.3.0
32
+ version: 0.2.2
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.3.0
39
+ version: 0.2.2
40
40
  - !ruby/object:Gem::Dependency
41
- name: l43_simple_color
41
+ name: l43_opt_parser
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: 0.2.3
46
+ version: 0.3.3
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: 0.2.3
53
+ version: 0.3.3
54
54
  - !ruby/object:Gem::Dependency
55
- name: l43_peg
55
+ name: l43_open_object
56
56
  requirement: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: 0.3.1
60
+ version: 0.3.0
61
61
  type: :runtime
62
62
  prerelease: false
63
63
  version_requirements: !ruby/object:Gem::Requirement
64
64
  requirements:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: 0.3.1
67
+ version: 0.3.0
68
+ - !ruby/object:Gem::Dependency
69
+ name: l43_simple_color
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: 0.2.3
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: 0.2.3
68
82
  description: 'A hybrid between awk and grep
69
83
 
70
84
  '
@@ -90,6 +104,7 @@ files:
90
104
  - lib/l43_rmap/function.rb
91
105
  - lib/l43_rmap/functions.rb
92
106
  - lib/l43_rmap/functions/predefined.rb
107
+ - lib/l43_rmap/functions/predefined/files.rb
93
108
  - lib/l43_rmap/functions/predefined/shell.rb
94
109
  - lib/l43_rmap/functions/predefined/str.rb
95
110
  - lib/l43_rmap/parsing/chunk_parser.rb
@@ -100,6 +115,8 @@ files:
100
115
  - lib/l43_rmap/predefined_patterns.rb
101
116
  - lib/l43_rmap/runtime.rb
102
117
  - lib/l43_rmap/runtime/line_time.rb
118
+ - lib/l43_rmap/tools/time.rb
119
+ - lib/l43_rmap/tools/time/rgx.rb
103
120
  - lib/l43_rmap/version.rb
104
121
  homepage: https://codeberg.org/lab419/rmap
105
122
  licenses: