fmt 0.3.1 → 0.3.3

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: 67c0fef310f41def1104325bdcf64e0e74c00be1c491bde56e8dd18f93121711
4
- data.tar.gz: fa7af34e9c94c03ebbd28d4843a0c268fe63304cd95bfde0c0b7cbca7d550916
3
+ metadata.gz: ed2f5a4d3984f777c398dfe2efeba95883d1474165b0831cea8b045c9d1bc3d3
4
+ data.tar.gz: 3ae71397daf558b9d32b5377d29f40a2b307a650fc53d08f4faa3cc69759eedb
5
5
  SHA512:
6
- metadata.gz: b04858bb8cab7de025a11ecf941e175a698559b231c1878bbdf9f5762dcb3eb05cd822ee50b11a0739aa21ee5077ea9a448ef75a0e790e0587c39403e33ca303
7
- data.tar.gz: eb55994dbd819d698b626ea170b56dec4177c02742f8b50da3e1d182704c7ff78043e31aece819ec47762cf6ab4297236d0698e2d80c6b5a974e19632817bccd
6
+ metadata.gz: e87abb12eb2ff4373ec7f13ecd1ac236b01914296a9d2b854f8bb60dd1aef5ee1e834ba45601a9997fe53168260c5ac33f70c78c694c45c1bc49f5c958cb8e61
7
+ data.tar.gz: d26316457a56992b65d8b9c2d2478b36fc9cdd378a454748a2016dfb2c388de91098f73a5cdce23d3d9dc6d8f0d4d47ac8774c66038c39fdcca10adfedec83bb
data/README.md CHANGED
@@ -1,6 +1,15 @@
1
1
  <p align="center">
2
2
  <a href="http://blog.codinghorror.com/the-best-code-is-no-code-at-all/">
3
- <img alt="Lines of Code" src="https://img.shields.io/badge/loc-1042-47d299.svg" />
3
+ <img alt="Lines of Code" src="https://img.shields.io/badge/loc-1050-47d299.svg" />
4
+ </a>
5
+ <a href="https://rubygems.org/gems/fmt">
6
+ <img alt="GEM Version" src="https://img.shields.io/gem/v/fmt">
7
+ </a>
8
+ <a href="https://rubygems.org/gems/fmt">
9
+ <img alt="GEM Downloads" src="https://img.shields.io/gem/dt/fmt">
10
+ </a>
11
+ <a href="https://github.com/hopsoft/fmt/actions">
12
+ <img alt="Tests" src="https://github.com/hopsoft/fmt/actions/workflows/tests.yml/badge.svg" />
4
13
  </a>
5
14
  <a href="https://github.com/testdouble/standard">
6
15
  <img alt="Ruby Style" src="https://img.shields.io/badge/style-standard-168AFE?logo=ruby&logoColor=FE1616" />
@@ -21,21 +30,21 @@
21
30
 
22
31
  ## Table of Contents
23
32
 
24
- - [Getting Started](#getting-started)
25
- - [Usage](#usage)
26
- - [Macros](#macros)
27
- - [Pipelines](#pipelines)
28
- - [Supported Methods](#supported-methods)
29
- - [Rainbow GEM](#rainbow-gem)
30
- - [Composition](#composition)
31
- - [Embedded Templates](#embedded-templates)
32
- - [Customizing Fmt](#customizing-fmt)
33
- - [Kernel Refinement](#kernel-refinement)
34
- - [`fmt(object, *pipeline)`](#fmtobject-pipeline)
35
- - [`fmt_print(object, *pipeline)`](#fmt_printobject-pipeline)
36
- - [`fmt_puts(object, *pipeline)`](#fmt_putsobject-pipeline)
37
- - [Performance](#performance)
38
- - [Sponsors](#sponsors)
33
+ - [Getting Started](#getting-started)
34
+ - [Usage](#usage)
35
+ - [Macros](#macros)
36
+ - [Pipelines](#pipelines)
37
+ - [Supported Methods](#supported-methods)
38
+ - [Rainbow GEM](#rainbow-gem)
39
+ - [Composition](#composition)
40
+ - [Embedded Templates](#embedded-templates)
41
+ - [Customizing Fmt](#customizing-fmt)
42
+ - [Kernel Refinement](#kernel-refinement)
43
+ - [`fmt(object, *pipeline)`](#fmtobject-pipeline)
44
+ - [`fmt_print(object, *pipeline)`](#fmt_printobject-pipeline)
45
+ - [`fmt_puts(object, *pipeline)`](#fmt_putsobject-pipeline)
46
+ - [Performance](#performance)
47
+ - [Sponsors](#sponsors)
39
48
 
40
49
  <!-- Tocer[finish]: Auto-generated, don't remove. -->
41
50
 
@@ -133,7 +142,7 @@ Templates can include multiple format strings with distinct pipelines:
133
142
  ```ruby
134
143
  template = "Date: %<date>.10s|>magenta -- %{msg}|>titleize|>bold"
135
144
  Fmt(template, date: Time.now, msg: "this is cool")
136
- #=> "Date: \e[35m2024-09-20\e[0m \e[1mThis Is Cool\e[0m"
145
+ #=> "Date: \e[35m2024-09-21\e[0m -- \e[1mThis Is Cool\e[0m"
137
146
  ```
138
147
 
139
148
  #### Embedded Templates
@@ -178,11 +187,11 @@ template = <<~T
178
187
  %{one}|>red {{
179
188
  %{two}|>blue {{
180
189
  %{three}|>green
181
- }}
182
- }}|>bold
190
+ }}|>bold
191
+ }}
183
192
  T
184
193
  Fmt(template, one: "Red", two: "Blue", three: "Green")
185
- #=> "Multiline:\n\e[31mRed\e[0m \e[1m\n \e[34mBlue\e[0m \n \e[32mGreen\e[0m"
194
+ #=> "Multiline:\n\e[31mRed\e[0m \n \e[34mBlue\e[0m \e[1m\n \e[32mGreen\e[0m\n \e[0m\n\n"
186
195
  ```
187
196
 
188
197
  ### Customizing Fmt
@@ -30,25 +30,18 @@ module Fmt
30
30
  # Extracts components for building the AST (Abstract Syntax Tree)
31
31
  # @rbs return: Hash[Symbol, Object] -- extracted components
32
32
  def extract
33
- {}
33
+ source = urtext.delete_prefix(Sigils::EMBED_PREFIX).delete_suffix(Sigils::EMBED_SUFFIX)
34
+ {source: source}
34
35
  end
35
36
 
36
37
  # Transforms extracted components into an AST (Abstract Syntax Tree)
37
38
  # @rbs return: Node -- AST (Abstract Syntax Tree)
38
- def transform(**)
39
+ def transform(source:)
39
40
  key = Node.new(:key, [self.key])
40
41
  placeholder = Node.new(:placeholder, [self.placeholder])
41
- template = TemplateParser.new(template_urtext).parse
42
+ template = TemplateParser.new(source).parse
42
43
  children = [key, placeholder, template].reject(&:empty?)
43
- Node.new(:embed, children, urtext: urtext, source: urtext)
44
- end
45
-
46
- private
47
-
48
- # Returns the template urtext
49
- # @rbs return: String
50
- def template_urtext
51
- urtext.delete_prefix(Sigils::EMBED_PREFIX).delete_suffix(Sigils::EMBED_SUFFIX)
44
+ Node.new(:embed, children, urtext: urtext, source: source)
52
45
  end
53
46
  end
54
47
  end
@@ -24,9 +24,7 @@ module Fmt
24
24
  # Extracts components for building the AST (Abstract Syntax Tree)
25
25
  # @rbs return: Hash[Symbol, Object] -- extracted components
26
26
  def extract
27
- code = urtext
28
- code = "#{Sigils::FORMAT_METHOD}('#{urtext}')" if native_format_string?(urtext)
29
-
27
+ code = urtext.delete_prefix(Sigils::FORMAT_PREFIX)
30
28
  tokens = tokenize(code)
31
29
  method = tokens.find(&:method_name?)&.value&.to_sym
32
30
 
@@ -46,7 +44,7 @@ module Fmt
46
44
  # @rbs arguments_tokens: Array[Token] -- arguments tokens
47
45
  # @rbs return: Node -- AST (Abstract Syntax Tree)
48
46
  def transform(method:, arguments_tokens:)
49
- method = Node.new(:name, [method], urtext: urtext, source: method)
47
+ method = Node.new(:name, [method], urtext: urtext, source: method.to_s)
50
48
  arguments = ArgumentsParser.new(arguments_tokens).parse
51
49
  source = "#{method.source}#{arguments.source}"
52
50
  children = [method, arguments].reject(&:empty?)
@@ -102,12 +100,5 @@ module Fmt
102
100
  return false if arguments_started?(tokens) && !arguments_finished?(tokens)
103
101
  true
104
102
  end
105
-
106
- # Indicates if a value is a Ruby native format string
107
- # @rbs value: String -- value to check
108
- # @rbs return: bool
109
- def native_format_string?(value)
110
- value.start_with? Sigils::FORMAT_PREFIX
111
- end
112
103
  end
113
104
  end
@@ -5,22 +5,18 @@
5
5
  module Fmt
6
6
  # Parses a template from a string and builds an AST (Abstract Syntax Tree)
7
7
  class TemplateParser < Parser
8
- PIPELINE_HEAD = %r{(?=(?!#{esc Sigils::PIPE_OPERATOR})#{Sigils::FORMAT_PREFIX})}o # : Regexp -- detects a native Ruby format string
9
- PIPELINE_TAIL = %r{(?=(\s+#{Sigils::FORMAT_PREFIX})|\z)}o # : Regexp -- detects a pipeline suffix
10
-
11
- EMBED_HEAD = %r{(?=#{esc Sigils::EMBED_PREFIX})}o # : Regexp -- detects an embed prefix
12
- EMBED_TAIL = %r{#{esc Sigils::EMBED_SUFFIX}}o # : Regexp -- detects an embed suffix
8
+ EMBED_PEEK = %r{(?=#{esc Sigils::EMBED_PREFIX})}o # : Regexp -- detects start of an embed prefix (look ahead)
9
+ PIPELINE_PEEK = %r{(?=[#{Sigils::FORMAT_PREFIX}][^#{Sigils::FORMAT_PREFIX}])}o # : Regexp -- detects start of a pipeline (look ahead)
10
+ PERCENT_LITERAL = %r{[#{Sigils::FORMAT_PREFIX}]{2}}o # : Regexp -- detects a percent literal
11
+ WHITESPACE = %r{\s}o # : Regexp -- detects whitespace
13
12
 
14
13
  # Constructor
15
14
  # @rbs urtext: String -- original source code
16
- # @rbs scanner: StringScanner?
17
- def initialize(urtext = "", scanner: nil)
15
+ def initialize(urtext = "")
18
16
  @urtext = urtext.to_s
19
- @scanner = scanner || StringScanner.new(@urtext)
20
17
  end
21
18
 
22
19
  attr_reader :urtext # : String -- original source code
23
- attr_reader :scanner # : StringScanner?
24
20
 
25
21
  # Parses the urtext (original source code)
26
22
  # @rbs return: Node -- AST (Abstract Syntax Tree)
@@ -36,11 +32,13 @@ module Fmt
36
32
  def extract
37
33
  source = urtext
38
34
 
39
- embeds = extract_embeds
35
+ # 1) extract embeds first and update the source
36
+ embeds = extract_embeds(source)
40
37
  embeds.each do |embed|
41
- source = "#{source[0...embed[:index]]}#{embed[:placeholder]}#{source[embed[:rindex]..]}"
38
+ source = source.sub(embed[:urtext], embed[:placeholder])
42
39
  end
43
40
 
41
+ # 2) extract pipelines
44
42
  pipelines = extract_pipelines(source)
45
43
 
46
44
  {embeds: embeds, pipelines: pipelines, source: source}
@@ -65,61 +63,101 @@ module Fmt
65
63
 
66
64
  private
67
65
 
68
- # Extracts embed metadata from the urtext
66
+ # Extracts the next embed with the scanner
67
+ # @rbs scanner: StringScanner -- scanner to extract from
68
+ # @rbs return: String? -- extracted embed
69
+ def extract_next_embed(scanner)
70
+ return nil unless scanner.skip_until(EMBED_PEEK)
71
+
72
+ head = Sigils::EMBED_PREFIX[0]
73
+ tail = Sigils::EMBED_SUFFIX[0]
74
+ index = scanner.pos
75
+ stack = 0
76
+
77
+ until scanner.eos?
78
+ case scanner.getch
79
+ in ^head then stack += 1
80
+ in ^tail then stack -= 1
81
+ in nil then break
82
+ else # noop
83
+ end
84
+
85
+ break if stack.zero?
86
+ end
87
+
88
+ return nil unless stack.zero?
89
+
90
+ scanner.string[index...scanner.pos]
91
+ end
92
+
93
+ # Extracts embed metadata from the source
69
94
  # @rbs return: Array[Hash] -- extracted embeds
70
- def extract_embeds
71
- embeds = []
95
+ def extract_embeds(source)
96
+ scanner = StringScanner.new(source)
72
97
 
73
- index = nil
74
- embed = ""
98
+ # will iterate until extract_next_embed returns nil... when run
99
+ generator = Enumerator.new do |yielder|
100
+ while (embed = extract_next_embed(scanner))
101
+ yielder << embed
102
+ end
103
+ end
75
104
 
76
- scanner = StringScanner.new(urtext)
77
- scanner.skip_until(EMBED_HEAD)
105
+ # runs the generator and returns the resulting array
106
+ embeds = generator.to_a
78
107
 
79
- while scanner.matched?
80
- index ||= scanner.charpos
81
- embed = "#{embed}#{scanner.scan_until(EMBED_TAIL)}"
108
+ embeds.map.with_index do |embed, index|
109
+ key = :"embed_#{index}"
110
+ placeholder = "#{Sigils::FORMAT_PREFIX}#{Sigils::KEY_PREFIXES[-1]}#{key}#{Sigils::KEY_SUFFIXES[-1]}"
111
+ {key: key, placeholder: placeholder, urtext: embed}
112
+ end
113
+ end
82
114
 
83
- if embed.scan(EMBED_HEAD).size == embed.scan(EMBED_TAIL).size
84
- rindex = scanner.charpos
85
- key = :"embed_#{index}_#{rindex}"
115
+ # Extracts the next pipeline with the scanner
116
+ # @rbs scanner: StringScanner -- scanner to extract from
117
+ # @rbs return: String? -- extracted pipeline
118
+ def extract_next_pipeline(scanner)
119
+ return nil unless scanner.skip_until(PIPELINE_PEEK)
86
120
 
87
- embeds << {
88
- index: index,
89
- rindex: rindex,
90
- key: key,
91
- placeholder: "#{Sigils::FORMAT_PREFIX}#{Sigils::KEY_PREFIXES[-1]}#{key}#{Sigils::KEY_SUFFIXES[-1]}",
92
- urtext: embed
93
- }
121
+ index = scanner.pos
122
+
123
+ until scanner.eos?
124
+ if scanner.peek(2).match?(PERCENT_LITERAL)
125
+ scanner.pos += 2
126
+ next
127
+ end
94
128
 
95
- index = nil
96
- embed = scanner.skip_until(EMBED_HEAD)
129
+ case [index, scanner.pos, scanner.peek(1)]
130
+ in [i, pos, Sigils::FORMAT_PREFIX] if i == pos then scanner.pos += 1
131
+ in [i, pos, Sigils::FORMAT_PREFIX] if i != pos then break
132
+ in [i, pos, WHITESPACE] if arguments_balanced?(scanner.string[i...pos]) then break
133
+ else scanner.pos += 1
97
134
  end
98
135
  end
99
136
 
100
- embeds
137
+ scanner.string[index...scanner.pos]
101
138
  end
102
139
 
103
140
  # Extracts pipelines from the source
104
141
  # @rbs source: String -- source code to extract pipelines from
105
142
  # @rbs return: Array[String] -- extracted pipelines
106
143
  def extract_pipelines(source)
107
- pipelines = []
108
- pipeline = ""
109
-
110
144
  scanner = StringScanner.new(source)
111
- scanner.skip_until(PIPELINE_HEAD)
112
-
113
- while scanner.matched?
114
- pipeline = scanner.scan_until(PIPELINE_TAIL)
115
145
 
116
- if scanner.matched?
117
- pipelines << pipeline
118
- scanner.skip_until(PIPELINE_HEAD)
146
+ generator = Enumerator.new do |yielder|
147
+ while (pipeline = extract_next_pipeline(scanner))
148
+ yielder << pipeline
119
149
  end
120
150
  end
121
151
 
122
- pipelines
152
+ generator.to_a
153
+ end
154
+
155
+ # Indicates if arguments are balances in the given list of chars
156
+ # @rbs value: String -- value to check
157
+ # @rbs return: bool
158
+ def arguments_balanced?(value)
159
+ return true if value.nil? || value.empty?
160
+ value.count(Sigils::ARGS_PREFIX) == value.count(Sigils::ARGS_SUFFIX)
123
161
  end
124
162
  end
125
163
  end
data/lib/fmt/renderer.rb CHANGED
@@ -23,45 +23,37 @@ module Fmt
23
23
  def render(*args, **kwargs)
24
24
  raise Error, "positional and keyword arguments are mutually exclusive" if args.any? && kwargs.any?
25
25
 
26
- context = template.source
27
-
28
- render_embeds(context, *args, **kwargs) do |embed, result|
26
+ render_embeds(*args, **kwargs) do |embed, result|
29
27
  kwargs[embed.key] = result
30
28
  end
31
29
 
32
- render_pipelines(context, *args, **kwargs)
30
+ rendered = template.source
31
+ render_pipelines(*args, **kwargs) do |pipeline, result|
32
+ rendered = rendered.sub(pipeline.urtext, result.to_s)
33
+ end
34
+ rendered
33
35
  end
34
36
 
35
37
  private
36
38
 
37
- # Escapes a string for use in a regular expression
38
- # @rbs value: String -- string to escape
39
- # @rbs return: String -- escaped string
40
- def esc(value) = Regexp.escape(value.to_s)
41
-
42
39
  # Renders all template embeds
43
- # @rbs context: String -- starting context
44
40
  # @rbs args: Array[Object] -- positional arguments (user provided)
45
41
  # @rbs kwargs: Hash[Symbol, Object] -- keyword arguments (user provided)
46
- # @rbs &block: Proc -- block to execute after rendering embeds (signature: Proc(String, *args, **kwargs))
47
- def render_embeds(context, *args, **kwargs)
42
+ # @rbs &block: Proc -- block executed for each embed (signature: Proc(Embed, String))
43
+ def render_embeds(*args, **kwargs)
48
44
  template.embeds.each do |embed|
49
45
  yield embed, Renderer.new(embed.template).render(*args, **kwargs)
50
46
  end
51
47
  end
52
48
 
53
49
  # Renders all template pipelines
54
- # @rbs context: String -- starting context
55
50
  # @rbs args: Array[Object] -- positional arguments (user provided)
56
51
  # @rbs kwargs: Hash[Symbol, Object] -- keyword arguments (user provided)
57
- # @rbs return: String
58
- def render_pipelines(context, *args, **kwargs)
52
+ # @rbs block: Proc -- block executed for each pipeline (signature: Proc(Pipeline, String))
53
+ def render_pipelines(*args, **kwargs)
59
54
  template.pipelines.each_with_index do |pipeline, index|
60
- result = render_pipeline(pipeline, *args[index..], **kwargs)
61
- context = context.sub(pipeline.urtext, result)
55
+ yield pipeline, render_pipeline(pipeline, *args[index..], **kwargs)
62
56
  end
63
-
64
- context
65
57
  end
66
58
 
67
59
  # Renders a single pipeline
@@ -70,55 +62,53 @@ module Fmt
70
62
  # @rbs kwargs: Hash[Symbol, Object] -- keyword arguments (user provided)
71
63
  # @rbs return: String
72
64
  def render_pipeline(pipeline, *args, **kwargs)
73
- result = ""
65
+ result = nil
74
66
 
75
67
  pipeline.macros.each do |macro|
76
- result = case macro
77
- in name: Sigils::FORMAT_METHOD
78
- case [args, kwargs]
79
- in [], {} then invoke_formatter(macro)
80
- in [], {**} => kwargs then invoke_formatter(macro, **kwargs)
81
- in [*], {} then invoke_formatter(macro, *args)
82
- in [*], {**} => kwargs then invoke_formatter(macro, *args, **kwargs)
83
- end
84
- else invoke_macro(result, macro)
85
- end
68
+ result = invoke_macro(result, macro, *args, **kwargs)
86
69
  end
87
70
 
88
71
  result
89
72
  end
90
73
 
91
- # Invokes native Ruby string formatting
92
- # @rbs macro: Macro -- macro to use (source, arguments, etc.)
93
- # @rbs args: Array[Object] -- positional arguments (user provided)
94
- # @rbs kwargs: Hash[Symbol, Object] -- keyword arguments (user provided)
95
- # @rbs return: String
96
- def invoke_formatter(macro, *args, **kwargs)
97
- callable = Fmt.registry[[Kernel, macro.name]]
98
- context = macro.arguments.args[0]
99
- context.instance_exec(*args, **kwargs, &callable)
100
- rescue => error
101
- raise_format_error(macro, *args, cause: error, **kwargs)
102
- end
103
-
104
74
  # Invokes a macro
105
75
  # @rbs context: Object -- self in callable (Proc)
106
76
  # @rbs macro: Macro -- macro to use (source, arguments, etc.)
77
+ # @rbs args: Array[Object] -- positional arguments (user provided)
78
+ # @rbs kwargs: Hash[Symbol, Object] -- keyword arguments (user provided)
107
79
  # @rbs return: Object -- result
108
- def invoke_macro(context, macro)
80
+ def invoke_macro(context, macro, *args, **kwargs)
109
81
  callable = Fmt.registry[[context.class, macro.name]] || Fmt.registry[[Object, macro.name]]
110
- raise Error, "[#{context.class.name} | Object, #{macro.name}] is not a registered formatter!" unless callable
111
-
112
- args = macro.arguments.args
113
- kwargs = macro.arguments.kwargs
114
82
 
115
- context.instance_exec(*args, **kwargs, &callable)
83
+ case callable
84
+ in nil
85
+ if kwargs.key? macro.name
86
+ kwargs[macro.name]
87
+ else
88
+ quietly do
89
+ context.instance_exec { sprintf(macro.urtext, *args, **kwargs) }
90
+ end
91
+ end
92
+ else
93
+ context.instance_exec(*macro.arguments.args, **macro.arguments.kwargs, &callable)
94
+ end
116
95
  rescue => error
117
96
  args ||= []
118
97
  kwargs ||= {}
119
98
  raise_format_error(macro, *args, cause: error, **kwargs)
120
99
  end
121
100
 
101
+ # Suppresses verbose output for the duration of the block
102
+ # @rbs block: Proc -- block to execute
103
+ # @rbs return: void
104
+ def quietly
105
+ verbose = $VERBOSE
106
+ $VERBOSE = nil
107
+ yield
108
+ ensure
109
+ $VERBOSE = verbose
110
+ end
111
+
122
112
  # Raises an invocation error if/when Proc invocations fail
123
113
  # @rbs macro: Macro -- macro that failed
124
114
  # @rbs args: Array[Object] -- positional arguments (user provided)
data/lib/fmt/version.rb CHANGED
@@ -3,5 +3,5 @@
3
3
  # rbs_inline: enabled
4
4
 
5
5
  module Fmt
6
- VERSION = "0.3.1"
6
+ VERSION = "0.3.3"
7
7
  end
@@ -25,12 +25,6 @@ module Fmt
25
25
 
26
26
  # Transforms extracted components into an AST (Abstract Syntax Tree)
27
27
  # @rbs return: Node -- AST (Abstract Syntax Tree)
28
- def transform: (**untyped) -> Node
29
-
30
- private
31
-
32
- # Returns the template urtext
33
- # @rbs return: String
34
- def template_urtext: () -> String
28
+ def transform: (source: untyped) -> Node
35
29
  end
36
30
  end
@@ -51,10 +51,5 @@ module Fmt
51
51
  # @rbs tokens: Array[Token] -- tokens to check
52
52
  # @rbs return: bool
53
53
  def finished?: (Array[Token] tokens) -> bool
54
-
55
- # Indicates if a value is a Ruby native format string
56
- # @rbs value: String -- value to check
57
- # @rbs return: bool
58
- def native_format_string?: (String value) -> bool
59
54
  end
60
55
  end
@@ -3,23 +3,20 @@
3
3
  module Fmt
4
4
  # Parses a template from a string and builds an AST (Abstract Syntax Tree)
5
5
  class TemplateParser < Parser
6
- PIPELINE_HEAD: ::Regexp
6
+ EMBED_PEEK: ::Regexp
7
7
 
8
- PIPELINE_TAIL: ::Regexp
8
+ PIPELINE_PEEK: ::Regexp
9
9
 
10
- EMBED_HEAD: ::Regexp
10
+ PERCENT_LITERAL: ::Regexp
11
11
 
12
- EMBED_TAIL: ::Regexp
12
+ WHITESPACE: ::Regexp
13
13
 
14
14
  # Constructor
15
15
  # @rbs urtext: String -- original source code
16
- # @rbs scanner: StringScanner?
17
- def initialize: (?String urtext, ?scanner: StringScanner?) -> untyped
16
+ def initialize: (?String urtext) -> untyped
18
17
 
19
18
  attr_reader urtext: untyped
20
19
 
21
- attr_reader scanner: untyped
22
-
23
20
  # Parses the urtext (original source code)
24
21
  # @rbs return: Node -- AST (Abstract Syntax Tree)
25
22
  def parse: () -> Node
@@ -38,13 +35,28 @@ module Fmt
38
35
 
39
36
  private
40
37
 
41
- # Extracts embed metadata from the urtext
38
+ # Extracts the next embed with the scanner
39
+ # @rbs scanner: StringScanner -- scanner to extract from
40
+ # @rbs return: String? -- extracted embed
41
+ def extract_next_embed: (StringScanner scanner) -> String?
42
+
43
+ # Extracts embed metadata from the source
42
44
  # @rbs return: Array[Hash] -- extracted embeds
43
- def extract_embeds: () -> Array[Hash]
45
+ def extract_embeds: (untyped source) -> Array[Hash]
46
+
47
+ # Extracts the next pipeline with the scanner
48
+ # @rbs scanner: StringScanner -- scanner to extract from
49
+ # @rbs return: String? -- extracted pipeline
50
+ def extract_next_pipeline: (StringScanner scanner) -> String?
44
51
 
45
52
  # Extracts pipelines from the source
46
53
  # @rbs source: String -- source code to extract pipelines from
47
54
  # @rbs return: Array[String] -- extracted pipelines
48
55
  def extract_pipelines: (String source) -> Array[String]
56
+
57
+ # Indicates if arguments are balances in the given list of chars
58
+ # @rbs value: String -- value to check
59
+ # @rbs return: bool
60
+ def arguments_balanced?: (String value) -> bool
49
61
  end
50
62
  end
@@ -20,24 +20,17 @@ module Fmt
20
20
 
21
21
  private
22
22
 
23
- # Escapes a string for use in a regular expression
24
- # @rbs value: String -- string to escape
25
- # @rbs return: String -- escaped string
26
- def esc: (String value) -> String
27
-
28
23
  # Renders all template embeds
29
- # @rbs context: String -- starting context
30
24
  # @rbs args: Array[Object] -- positional arguments (user provided)
31
25
  # @rbs kwargs: Hash[Symbol, Object] -- keyword arguments (user provided)
32
- # @rbs &block: Proc -- block to execute after rendering embeds (signature: Proc(String, *args, **kwargs))
33
- def render_embeds: (String context, *untyped args, **untyped kwargs) -> untyped
26
+ # @rbs &block: Proc -- block executed for each embed (signature: Proc(Embed, String))
27
+ def render_embeds: (*untyped args, **untyped kwargs) -> untyped
34
28
 
35
29
  # Renders all template pipelines
36
- # @rbs context: String -- starting context
37
30
  # @rbs args: Array[Object] -- positional arguments (user provided)
38
31
  # @rbs kwargs: Hash[Symbol, Object] -- keyword arguments (user provided)
39
- # @rbs return: String
40
- def render_pipelines: (String context, *untyped args, **untyped kwargs) -> String
32
+ # @rbs block: Proc -- block executed for each pipeline (signature: Proc(Pipeline, String))
33
+ def render_pipelines: (*untyped args, **untyped kwargs) -> untyped
41
34
 
42
35
  # Renders a single pipeline
43
36
  # @rbs pipeline: Pipeline -- pipeline to render
@@ -46,18 +39,18 @@ module Fmt
46
39
  # @rbs return: String
47
40
  def render_pipeline: (Pipeline pipeline, *untyped args, **untyped kwargs) -> String
48
41
 
49
- # Invokes native Ruby string formatting
50
- # @rbs macro: Macro -- macro to use (source, arguments, etc.)
51
- # @rbs args: Array[Object] -- positional arguments (user provided)
52
- # @rbs kwargs: Hash[Symbol, Object] -- keyword arguments (user provided)
53
- # @rbs return: String
54
- def invoke_formatter: (Macro macro, *untyped args, **untyped kwargs) -> String
55
-
56
42
  # Invokes a macro
57
43
  # @rbs context: Object -- self in callable (Proc)
58
44
  # @rbs macro: Macro -- macro to use (source, arguments, etc.)
45
+ # @rbs args: Array[Object] -- positional arguments (user provided)
46
+ # @rbs kwargs: Hash[Symbol, Object] -- keyword arguments (user provided)
59
47
  # @rbs return: Object -- result
60
- def invoke_macro: (Object context, Macro macro) -> Object
48
+ def invoke_macro: (Object context, Macro macro, *untyped args, **untyped kwargs) -> Object
49
+
50
+ # Suppresses verbose output for the duration of the block
51
+ # @rbs block: Proc -- block to execute
52
+ # @rbs return: void
53
+ def quietly: () -> void
61
54
 
62
55
  # Raises an invocation error if/when Proc invocations fail
63
56
  # @rbs macro: Macro -- macro that failed
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fmt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nate Hopkins (hopsoft)
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-22 00:00:00.000000000 Z
11
+ date: 2024-10-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ast
@@ -301,7 +301,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
301
301
  - !ruby/object:Gem::Version
302
302
  version: '0'
303
303
  requirements: []
304
- rubygems_version: 3.5.16
304
+ rubygems_version: 3.5.21
305
305
  signing_key:
306
306
  specification_version: 4
307
307
  summary: CLI Templating System and String Formatter