l43_rmap 0.1.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.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/LiCenSE +235 -0
  3. data/README.md +141 -0
  4. data/bin/rmap +12 -0
  5. data/lib/enumerable.rb +28 -0
  6. data/lib/l43_rmap/ast.rb +58 -0
  7. data/lib/l43_rmap/chunk.rb +8 -0
  8. data/lib/l43_rmap/cli/color.rb +29 -0
  9. data/lib/l43_rmap/cli/help.rb +89 -0
  10. data/lib/l43_rmap/cli.rb +108 -0
  11. data/lib/l43_rmap/compiler.rb +68 -0
  12. data/lib/l43_rmap/evaluator/evaluations.rb +23 -0
  13. data/lib/l43_rmap/evaluator.rb +28 -0
  14. data/lib/l43_rmap/function.rb +20 -0
  15. data/lib/l43_rmap/functions/predefined/shell.rb +21 -0
  16. data/lib/l43_rmap/functions/predefined.rb +55 -0
  17. data/lib/l43_rmap/functions.rb +67 -0
  18. data/lib/l43_rmap/parsing/chunk_parser.rb +171 -0
  19. data/lib/l43_rmap/parsing/input.rb +26 -0
  20. data/lib/l43_rmap/parsing/parse_state.rb +18 -0
  21. data/lib/l43_rmap/parsing/rgx_parser.rb +51 -0
  22. data/lib/l43_rmap/parsing/rgx_parsers.rb +71 -0
  23. data/lib/l43_rmap/predefined_patterns.rb +14 -0
  24. data/lib/l43_rmap/runtime/line_time.rb +58 -0
  25. data/lib/l43_rmap/runtime.rb +78 -0
  26. data/lib/l43_rmap/version.rb +6 -0
  27. data/lib/l43_rmap.rb +32 -0
  28. data/lib/peg.backup/all.rb +12 -0
  29. data/lib/peg.backup/combinators/implementation.rb +144 -0
  30. data/lib/peg.backup/combinators.rb +82 -0
  31. data/lib/peg.backup/parser/input.rb +36 -0
  32. data/lib/peg.backup/parser.rb +46 -0
  33. data/lib/peg.backup/parsers/advanced_parsers.rb +26 -0
  34. data/lib/peg.backup/parsers/base_parsers.rb +124 -0
  35. data/lib/peg.backup/parsers/common_parsers.rb +60 -0
  36. data/lib/peg.backup/parsers/true_set.rb +10 -0
  37. data/lib/peg.backup/parsers.rb +14 -0
  38. data/lib/peg.backup/result.rb +78 -0
  39. metadata +135 -0
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'rgx_parser'
4
+ module L43Rmap
5
+ module Parsing
6
+ module RgxParsers
7
+ include Ast
8
+
9
+ BAREWORD = /\A[^\s\)]+/
10
+ BarewordParser = RgxParser.new(BAREWORD) { Bareword.new(it) }
11
+
12
+ DQUOTE = /\A"/
13
+ DquoteParser = RgxParser.new(DQUOTE)
14
+
15
+ ESC_DQUOTE = /\A"(")/
16
+ EscDquoteParser = RgxParser.new(ESC_DQUOTE)
17
+
18
+ ESC_SQUOTE = /\A'(')/
19
+ EscSquoteParser = RgxParser.new(ESC_SQUOTE)
20
+
21
+ ESCAPE = /\A%([%(])/
22
+ EscapeParser = RgxParser.new(ESCAPE) { Verb.new(it) }
23
+
24
+ INDEXED_FIELD = /\A%([-+]?\d+) ?/
25
+ IndexedFieldParser = RgxParser.new(INDEXED_FIELD) { IndexedField.new(it.to_i) }
26
+
27
+ NAMED_FIELD = /\A%([[:alpha:]][_[:alnum:]]*) ?/
28
+ NamedFieldParser = RgxParser.new(NAMED_FIELD) { NamedField.new(it) }
29
+
30
+ NAME = /\A[[:alpha:]][_[:alnum:]]*[\!\?]?/
31
+ NameParser = RgxParser.new(NAME) { Name.new(it) }
32
+
33
+ INT = /\A[-+]?\d+/
34
+ IntParser = RgxParser.new(INT) { Int.new(it.to_i) }
35
+
36
+ LPAR = /\A\(/
37
+ LparParser = RgxParser.new(LPAR) { :ignore }
38
+
39
+ NO_DQUOTE = /\A[^"]+/
40
+ NoDquoteParser = RgxParser.new(NO_DQUOTE)
41
+
42
+ NO_META = /\A[^%(]+/
43
+ NoMetaParser = RgxParser.new(NO_META) { Verb.new(it) }
44
+
45
+ NO_SQUOTE = /\A[^']+/
46
+ NoSquoteParser = RgxParser.new(NO_SQUOTE)
47
+
48
+ OP = /\A([-+*\/%])\s*/
49
+ OpParser = RgxParser.new(OP) { Name.new(it) }
50
+
51
+ RPAR = /\A\)/
52
+ RparParser = RgxParser.new(RPAR)
53
+
54
+ SQUOTE = /\A'/
55
+ SquoteParser = RgxParser.new(SQUOTE)
56
+
57
+ SYMBOL = /\A:([[:alpha:]][_[:alnum:]]*[\!\?]?)/
58
+ SymbolParser = RgxParser.new(SYMBOL) { Sym.new(it) }
59
+
60
+ WS = /\A\s+/
61
+ WsParser = RgxParser.new(WS)
62
+
63
+ ZEROFIELD = /\A%(?: |\z)/
64
+ ZerofieldParser = RgxParser.new(ZEROFIELD) { IndexedField.new(0) }
65
+
66
+ ZEROFIELD_INSIDE = /\A(%)(?: |\))/
67
+ ZerofieldInsideParser = RgxParser.new(ZEROFIELD_INSIDE, only_group: true) { IndexedField.new(0) }
68
+ end
69
+ end
70
+ end
71
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module L43Rmap
4
+ module PredefinedPatterns extend self
5
+
6
+ Patterns = {
7
+ mv_to_ms: 'mv % (lpad %n 0 4).(ext)',
8
+ mv_to_mse: 'mv (se) (lpad %n 0 4).(ext)',
9
+ }
10
+
11
+ def get_pattern(key) = Patterns.fetch(key.to_sym)
12
+ end
13
+ end
14
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+ require_relative '../chunk'
3
+ require_relative '../evaluator'
4
+ require_relative '../function'
5
+ require "securerandom"
6
+ module L43Rmap
7
+ class Runtime
8
+ class LineTime
9
+ Ast = L43Rmap::Ast
10
+ Chunk = L43Rmap::Chunk
11
+ Function = L43Rmap::Function
12
+
13
+ attr_reader :line, :lnb, :rand, :runtime, :terminated, :time
14
+
15
+ def run
16
+ chunks = runtime.chunks
17
+ # p(chunks: chunks.map(&:show))
18
+ loop do
19
+ case chunks
20
+ in []
21
+ return outstack.compact
22
+ in [chunk, *chunks]
23
+ result = chunk.(self)
24
+ # p(result:)
25
+ case result
26
+ when Symbol
27
+ return result
28
+ else
29
+ outstack.push(result)
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ def fields = @__fields__ ||= line.split
36
+
37
+ def outstack = @__outstack__ ||= []
38
+
39
+ def terminate! = @terminated=true
40
+
41
+ private
42
+ def initialize(line:, lnb:, runtime:)
43
+ @chunks = runtime.chunks
44
+ @line = line
45
+ @lnb = lnb
46
+ @runtime = runtime
47
+ @time = Time.new
48
+ end
49
+
50
+ def terminate_runtime
51
+ runtime.terminate
52
+ return [:terminate]
53
+ end
54
+
55
+ end
56
+ end
57
+ end
58
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require_relative 'runtime/line_time'
5
+
6
+ module L43Rmap
7
+ class Runtime
8
+ attr_reader :chunks, :current, :input_stream, :output_stream, :rand, :terminated, :time
9
+
10
+ DefaultDecRandomMax = 10_000
11
+
12
+ def run(input, output)
13
+ @output_stream = make_output_stream(output)
14
+ @input_stream = make_input_stream(input)
15
+ # @rand = SecureRandom.random_number
16
+ @time = Time.now
17
+ # p(time1: time.to_f)
18
+
19
+ execute!
20
+ rescue EOFError
21
+ end
22
+
23
+ def random_hex = @__randomhex__ ||= SecureRandom.hex
24
+
25
+ def random_rd = @__randomrd__ ||= (format "%04d", SecureRandom.random_number(DefaultDecRandomMax))
26
+
27
+ def image = chunks.map(&:image).join("\n")
28
+
29
+ def terminate! = @terminated = true
30
+
31
+ def timestamp(base, mult: 1_000, now: nil)
32
+ # p(time2: time.to_f)
33
+ now ||= time
34
+ (now.to_f * mult).to_i.to_s(base)
35
+ end
36
+
37
+
38
+ private
39
+ def execute!
40
+ lnb = 0
41
+ loop do
42
+ line = input_stream.readline chomp: true
43
+ lnb = lnb + 1
44
+ @current = LineTime.new(line:, lnb:, runtime: self)
45
+ result = current.run
46
+ # p(transformed: result)
47
+ case result
48
+ in []
49
+ next
50
+ in :ignore
51
+ next
52
+ in :terminate | :term
53
+ break
54
+ in Array
55
+ output_stream << result.join
56
+ output_stream << "\n"
57
+ else
58
+ raise "BAD RETURN FROM LineTime: #{result.inspect}"
59
+ end
60
+ end
61
+ end
62
+
63
+ def initialize(chunks)
64
+ @chunks = chunks
65
+ end
66
+
67
+ def make_input_stream(stream)
68
+ return stream unless String === stream
69
+ File.open(stream)
70
+ end
71
+
72
+ def make_output_stream(stream)
73
+ return stream unless String === stream
74
+ File.open(stream, 'w')
75
+ end
76
+ end
77
+ end
78
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module L43Rmap
4
+ VERSION = '0.1.0'
5
+ end
6
+ # SPDX-License-Identifier: AGPL-3.0-or-later
data/lib/l43_rmap.rb ADDED
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ # Global Namespace for the Rmap language implementation
5
+ require_relative 'l43_rmap/parsing/chunk_parser'
6
+ require_relative 'l43_rmap/compiler'
7
+ require_relative 'l43_rmap/runtime'
8
+
9
+ module L43Rmap
10
+ PREDEFINED_FIELDS = [
11
+ "n",
12
+ "t",
13
+ "x"
14
+ ]
15
+
16
+ TAGS = [
17
+ :field,
18
+ :line,
19
+ :sexp,
20
+ :verb
21
+ ]
22
+
23
+ class << self
24
+ def compile(pattern)
25
+ ast = Parsing::ChunkParser.new.parse(pattern)
26
+ chunks = Compiler.new.compile(ast)
27
+ Runtime.new(chunks)
28
+ end
29
+ end
30
+ end
31
+
32
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,12 @@
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
@@ -0,0 +1,144 @@
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
@@ -0,0 +1,82 @@
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
@@ -0,0 +1,36 @@
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
@@ -0,0 +1,46 @@
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
@@ -0,0 +1,26 @@
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