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 +4 -4
- data/README.md +29 -20
- data/lib/fmt/parsers/embed_parser.rb +5 -12
- data/lib/fmt/parsers/macro_parser.rb +2 -11
- data/lib/fmt/parsers/template_parser.rb +83 -45
- data/lib/fmt/renderer.rb +39 -49
- data/lib/fmt/version.rb +1 -1
- data/sig/generated/fmt/parsers/embed_parser.rbs +1 -7
- data/sig/generated/fmt/parsers/macro_parser.rbs +0 -5
- data/sig/generated/fmt/parsers/template_parser.rbs +22 -10
- data/sig/generated/fmt/renderer.rbs +12 -19
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed2f5a4d3984f777c398dfe2efeba95883d1474165b0831cea8b045c9d1bc3d3
|
4
|
+
data.tar.gz: 3ae71397daf558b9d32b5377d29f40a2b307a650fc53d08f4faa3cc69759eedb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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-
|
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
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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-
|
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
|
-
}}
|
190
|
+
}}|>bold
|
191
|
+
}}
|
183
192
|
T
|
184
193
|
Fmt(template, one: "Red", two: "Blue", three: "Green")
|
185
|
-
#=> "Multiline:\n\e[31mRed\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(
|
42
|
+
template = TemplateParser.new(source).parse
|
42
43
|
children = [key, placeholder, template].reject(&:empty?)
|
43
|
-
Node.new(:embed, children, urtext: urtext, source:
|
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
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
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
|
35
|
+
# 1) extract embeds first and update the source
|
36
|
+
embeds = extract_embeds(source)
|
40
37
|
embeds.each do |embed|
|
41
|
-
source =
|
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
|
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
|
-
|
95
|
+
def extract_embeds(source)
|
96
|
+
scanner = StringScanner.new(source)
|
72
97
|
|
73
|
-
|
74
|
-
|
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
|
-
|
77
|
-
|
105
|
+
# runs the generator and returns the resulting array
|
106
|
+
embeds = generator.to_a
|
78
107
|
|
79
|
-
|
80
|
-
|
81
|
-
|
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
|
-
|
84
|
-
|
85
|
-
|
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
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
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
|
-
|
96
|
-
|
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
|
-
|
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
|
-
|
117
|
-
|
118
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
47
|
-
def render_embeds(
|
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
|
58
|
-
def render_pipelines(
|
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
|
-
|
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 =
|
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
|
-
|
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
@@ -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: (
|
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
|
-
|
6
|
+
EMBED_PEEK: ::Regexp
|
7
7
|
|
8
|
-
|
8
|
+
PIPELINE_PEEK: ::Regexp
|
9
9
|
|
10
|
-
|
10
|
+
PERCENT_LITERAL: ::Regexp
|
11
11
|
|
12
|
-
|
12
|
+
WHITESPACE: ::Regexp
|
13
13
|
|
14
14
|
# Constructor
|
15
15
|
# @rbs urtext: String -- original source code
|
16
|
-
|
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
|
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
|
33
|
-
def render_embeds: (
|
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
|
40
|
-
def render_pipelines: (
|
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.
|
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-
|
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.
|
304
|
+
rubygems_version: 3.5.21
|
305
305
|
signing_key:
|
306
306
|
specification_version: 4
|
307
307
|
summary: CLI Templating System and String Formatter
|