gloo 6.1.0 → 6.2.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.
- checksums.yaml +4 -4
- data/CLAUDE.md +1 -1
- data/README.md +1 -0
- data/docs/application.md +24 -4
- data/docs/getting_started.md +2 -4
- data/docs/language_objects.md +1 -1
- data/docs/language_syntax.md +29 -2
- data/docs/plugins.md +325 -3
- data/docs/web_app.md +211 -0
- data/lib/VERSION +1 -1
- data/lib/VERSION_NOTES +6 -0
- data/lib/gloo/app/engine.rb +17 -2
- data/lib/gloo/app/log.rb +5 -4
- data/lib/gloo/app/platform.rb +5 -0
- data/lib/gloo/app/prompt.rb +2 -1
- data/lib/gloo/app/settings.rb +25 -15
- data/lib/gloo/app/table.rb +7 -3
- data/lib/gloo/app/theme.rb +144 -0
- data/lib/gloo/core/invoker.rb +102 -0
- data/lib/gloo/core/parser.rb +48 -7
- data/lib/gloo/core/tokens.rb +81 -1
- data/lib/gloo/docs/help_shell.rb +67 -20
- data/lib/gloo/docs/markdown_renderer.rb +7 -7
- data/lib/gloo/expr/call.rb +54 -0
- data/lib/gloo/expr/expression.rb +3 -0
- data/lib/gloo/objs/basic/string.rb +5 -25
- data/lib/gloo/objs/basic/string_msgs.rb +192 -2
- data/lib/gloo/objs/basic/text.rb +5 -26
- data/lib/gloo/objs/ctrl/function.rb +17 -4
- data/lib/gloo/verbs/help.rb +8 -5
- data/lib/gloo/verbs/invoke.rb +39 -46
- data/lib/gloo/verbs/list.rb +5 -4
- data/test.gloo/objs/string.test.gloo +54 -0
- data/test.gloo/verbs/invoke.test.gloo +73 -0
- metadata +5 -3
- data/.DS_Store +0 -0
- data/.travis.yml +0 -5
|
@@ -21,8 +21,46 @@ module Gloo
|
|
|
21
21
|
.map { |m| m.to_s.sub( /\Amsg_/, '' ) }
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
#
|
|
25
|
+
# Documentation for each message this mixin implements, for use
|
|
26
|
+
# in String and Text's doc_data (see help_shell). Kept here as
|
|
27
|
+
# the single source, since String and Text share the exact
|
|
28
|
+
# same message set and would otherwise have to keep two
|
|
29
|
+
# hand-written copies of this list in sync.
|
|
30
|
+
#
|
|
31
|
+
def self.message_docs
|
|
32
|
+
return [
|
|
33
|
+
'up — Convert the string to uppercase. This message changes the value of the string.',
|
|
34
|
+
'down — Convert the string to lowercase. This message changes the value of the string.',
|
|
35
|
+
'size — Get the size of the string. It will have the string size.',
|
|
36
|
+
'count_chars — Count the number of characters in the string. It will have the character count.',
|
|
37
|
+
'count_words — Count the number of words in the string. It will have the word count.',
|
|
38
|
+
'count_lines — Count the number of lines in the string. It will have the line count.',
|
|
39
|
+
'starts_with? ({str}) — Check if the string starts with the given string. A parameter is required: the string to look for at the beginning of this string. It will have a boolean.',
|
|
40
|
+
'ends_with? ({str}) — Check if the string ends with the given string. A parameter is required: the string to look for at the end of this string. It will have a boolean.',
|
|
41
|
+
'substring? ({str}) — Check if the string includes the given sub-string. A parameter is required: the string to look for in this string. It will have a boolean.',
|
|
42
|
+
'format_for_html — Format this string for HTML output. Tabs, spaces and returns are converted to HTML elements. The value of the string is changed.',
|
|
43
|
+
'encode64 — Base64 encode the string. This message changes the value of the string. It will have the encoded string.',
|
|
44
|
+
'decode64 — Decode the string from Base64. This message changes the value of the string. It will have the decoded string.',
|
|
45
|
+
'escape — Escape the string to make it URL safe. This message changes the value of the string. It will have the escaped string.',
|
|
46
|
+
'unescape — Unescape the string (from URL safe format). This message changes the value of the string. It will have the unescaped string.',
|
|
47
|
+
'gen_uuid — Set the value of the string to a newly generated, random UUID. This message changes the value of the string.',
|
|
48
|
+
'gen_alphanumeric ({len}) — Set the value of the string to a newly generated, random alphanumeric string. The {len} parameter is optional; the length is 10 if not specified. This message changes the value of the string.',
|
|
49
|
+
'gen_hex ({len}) — Set the value of the string to a newly generated, random hex string. The {len} parameter is optional; the length is 10 if not specified. This message changes the value of the string.',
|
|
50
|
+
'gen_base64 ({len}) — Set the value of the string to a newly generated, random base64 string. The {len} parameter is optional; the length is 12 if not specified. This message changes the value of the string.',
|
|
51
|
+
'trim — Strip whitespace from the beginning and end of the string. This message changes the value of the string. It will have the trimmed string.',
|
|
52
|
+
'sub ({from} {to}) — Substitute the first occurrence of {from} with {to}. Both parameters are required. This message changes the value of the string. It will have the result.',
|
|
53
|
+
'gsub ({from} {to}) — Substitute all occurrences of {from} with {to}. Both parameters are required. This message changes the value of the string. It will have the result.',
|
|
54
|
+
'split ({from} {to}) — Get the substring from index {from} up to (not including) index {to}. Indexes are 0-based; out-of-range indexes are clamped to the start or end of the string. Both parameters are required. Does not change the value of the string. It will have the substring.',
|
|
55
|
+
'splitl ({index}) — Get the substring to the left of index {index} (same as split (0, {index})). A parameter is required. Does not change the value of the string. It will have the substring.',
|
|
56
|
+
'splitr ({index}) — Get the substring from index {index} to the end of the string (same as split ({index}, size)). A parameter is required. Does not change the value of the string. It will have the substring.',
|
|
57
|
+
'split_list ({delim} {dst.path}) — Split the string by {delim} and put the parts into children of the container at {dst.path} (or an alias that points to one), one part per child, in order. Existing children are matched by position and have their values set; extra parts get new (untyped) children, numbered from 1; extra existing children are left alone. Both parameters are required. Does not change the value of the string. It will have the number of parts.',
|
|
58
|
+
'page — Show the value in a pager (less), for viewing long content a screen at a time.'
|
|
59
|
+
]
|
|
60
|
+
end
|
|
24
61
|
|
|
25
|
-
|
|
62
|
+
|
|
63
|
+
#
|
|
26
64
|
# Strip whitespace from the beginning and end of the string.
|
|
27
65
|
#
|
|
28
66
|
def msg_trim
|
|
@@ -120,7 +158,115 @@ module Gloo
|
|
|
120
158
|
end
|
|
121
159
|
end
|
|
122
160
|
|
|
123
|
-
#
|
|
161
|
+
#
|
|
162
|
+
# Get the substring from index {from} up to (not including) index {to}.
|
|
163
|
+
# Indexes are 0-based. Out-of-range indexes are clamped to the
|
|
164
|
+
# beginning or end of the string. Does not change the string's value.
|
|
165
|
+
#
|
|
166
|
+
def msg_split
|
|
167
|
+
return '' unless value
|
|
168
|
+
|
|
169
|
+
if @params&.token_count&.positive?
|
|
170
|
+
expr = Gloo::Expr::Expression.new( @engine, [ @params.tokens.first ] )
|
|
171
|
+
from = expr.evaluate.to_i
|
|
172
|
+
expr = Gloo::Expr::Expression.new( @engine, [ @params.tokens.last ] )
|
|
173
|
+
to = expr.evaluate.to_i
|
|
174
|
+
|
|
175
|
+
result = clamped_substring( from, to )
|
|
176
|
+
@engine.heap.it.set_to result
|
|
177
|
+
return result
|
|
178
|
+
else
|
|
179
|
+
# Error
|
|
180
|
+
@engine.log.error MISSING_PARAM_MSG
|
|
181
|
+
@engine.heap.it.set_to false
|
|
182
|
+
return false
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
#
|
|
187
|
+
# Get the substring to the left of (not including) index {index}.
|
|
188
|
+
# Same as split( 0, index ). Does not change the string's value.
|
|
189
|
+
#
|
|
190
|
+
def msg_splitl
|
|
191
|
+
return '' unless value
|
|
192
|
+
|
|
193
|
+
if @params&.token_count&.positive?
|
|
194
|
+
expr = Gloo::Expr::Expression.new( @engine, @params.tokens )
|
|
195
|
+
index = expr.evaluate.to_i
|
|
196
|
+
|
|
197
|
+
result = clamped_substring( 0, index )
|
|
198
|
+
@engine.heap.it.set_to result
|
|
199
|
+
return result
|
|
200
|
+
else
|
|
201
|
+
# Error
|
|
202
|
+
@engine.log.error MISSING_PARAM_MSG
|
|
203
|
+
@engine.heap.it.set_to false
|
|
204
|
+
return false
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
#
|
|
209
|
+
# Get the substring from index {index} to the end of the string.
|
|
210
|
+
# Same as split( index, size ). Does not change the string's value.
|
|
211
|
+
#
|
|
212
|
+
def msg_splitr
|
|
213
|
+
return '' unless value
|
|
214
|
+
|
|
215
|
+
if @params&.token_count&.positive?
|
|
216
|
+
expr = Gloo::Expr::Expression.new( @engine, @params.tokens )
|
|
217
|
+
index = expr.evaluate.to_i
|
|
218
|
+
|
|
219
|
+
result = clamped_substring( index, value.length )
|
|
220
|
+
@engine.heap.it.set_to result
|
|
221
|
+
return result
|
|
222
|
+
else
|
|
223
|
+
# Error
|
|
224
|
+
@engine.log.error MISSING_PARAM_MSG
|
|
225
|
+
@engine.heap.it.set_to false
|
|
226
|
+
return false
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
#
|
|
231
|
+
# Split the string by the given delimiter and put the parts into
|
|
232
|
+
# children of the target container, one part per child, in order.
|
|
233
|
+
# The target is a path to a container, or to an alias that points
|
|
234
|
+
# to one. Existing children are matched by position (not name) and
|
|
235
|
+
# have their values set; if there are more parts than children,
|
|
236
|
+
# new (untyped) children are created for the extras, numbered
|
|
237
|
+
# from 1. Existing children beyond the part count are left alone.
|
|
238
|
+
# The string's own value is unchanged; the number of parts is put
|
|
239
|
+
# into 'it'.
|
|
240
|
+
#
|
|
241
|
+
def msg_split_list
|
|
242
|
+
return unless value
|
|
243
|
+
|
|
244
|
+
if @params&.token_count.to_i < 2
|
|
245
|
+
@engine.log.error MISSING_PARAM_MSG
|
|
246
|
+
@engine.heap.it.set_to false
|
|
247
|
+
return false
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
expr = Gloo::Expr::Expression.new( @engine, [ @params.tokens.first ] )
|
|
251
|
+
delim = expr.evaluate
|
|
252
|
+
|
|
253
|
+
target = split_list_target( @params.tokens.last )
|
|
254
|
+
return false unless target
|
|
255
|
+
|
|
256
|
+
parts = value.split( delim )
|
|
257
|
+
existing = target.children
|
|
258
|
+
parts.each_with_index do |part, index|
|
|
259
|
+
child = existing[ index ]
|
|
260
|
+
child ||= target.find_add_child( ( index + 1 ).to_s, 'untyped' )
|
|
261
|
+
child.set_value part
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
count = parts.count
|
|
265
|
+
@engine.heap.it.set_to count
|
|
266
|
+
return count
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
#
|
|
124
270
|
# Does the string contain the given string?
|
|
125
271
|
#
|
|
126
272
|
# This was formerly an overload of obj.contains?
|
|
@@ -344,6 +490,50 @@ module Gloo
|
|
|
344
490
|
@engine.platform.page( value )
|
|
345
491
|
end
|
|
346
492
|
|
|
493
|
+
private
|
|
494
|
+
|
|
495
|
+
#
|
|
496
|
+
# Resolve the target container path for split_list. The path may
|
|
497
|
+
# point directly at a container, or at an alias that points to
|
|
498
|
+
# one. Returns nil (and logs an error, setting 'it' to false)
|
|
499
|
+
# if the path doesn't exist or doesn't resolve to a container.
|
|
500
|
+
#
|
|
501
|
+
def split_list_target( token )
|
|
502
|
+
pn = Gloo::Core::Pn.new( @engine, token )
|
|
503
|
+
unless pn&.exists?
|
|
504
|
+
@engine.log.error 'Target container path does not exist!'
|
|
505
|
+
@engine.heap.it.set_to false
|
|
506
|
+
return nil
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
target = pn.resolve
|
|
510
|
+
target = Gloo::Objs::Alias.resolve_alias( @engine, target )
|
|
511
|
+
unless target&.is_container?
|
|
512
|
+
@engine.log.error 'Target for split_list must be a container!'
|
|
513
|
+
@engine.heap.it.set_to false
|
|
514
|
+
return nil
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
return target
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
#
|
|
521
|
+
# Get the substring from index {from} up to (not including) index {to}.
|
|
522
|
+
# Out-of-range indexes are clamped to the string's own bounds (0 and
|
|
523
|
+
# its length), rather than raising an error. A degenerate range
|
|
524
|
+
# (from at or past to) returns an empty string.
|
|
525
|
+
#
|
|
526
|
+
def clamped_substring( from, to )
|
|
527
|
+
len = value.length
|
|
528
|
+
from = 0 if from.negative?
|
|
529
|
+
from = len if from > len
|
|
530
|
+
to = 0 if to.negative?
|
|
531
|
+
to = len if to > len
|
|
532
|
+
return '' if from >= to
|
|
533
|
+
|
|
534
|
+
return value[ from...to ]
|
|
535
|
+
end
|
|
536
|
+
|
|
347
537
|
end
|
|
348
538
|
end
|
|
349
539
|
end
|
data/lib/gloo/objs/basic/text.rb
CHANGED
|
@@ -73,32 +73,11 @@ module Gloo
|
|
|
73
73
|
:name => KEYWORD,
|
|
74
74
|
:shortcut => KEYWORD_SHORT,
|
|
75
75
|
:description => 'A longer, multi-line text string. Use BEGIN ' \
|
|
76
|
-
'and END to mark the text range.'
|
|
77
|
-
|
|
78
|
-
'
|
|
79
|
-
'
|
|
80
|
-
|
|
81
|
-
'size — Get the size of the string. It will have the string size.',
|
|
82
|
-
'count_chars — Count the number of characters in the string. It will have the character count.',
|
|
83
|
-
'count_words — Count the number of words in the string. It will have the word count.',
|
|
84
|
-
'count_lines — Count the number of lines in the string. It will have the line count.',
|
|
85
|
-
'starts_with? ({str}) — Check if the string starts with the given string. A parameter is required: the string to look for at the beginning of this string. It will have a boolean.',
|
|
86
|
-
'ends_with? ({str}) — Check if the string ends with the given string. A parameter is required: the string to look for at the end of this string. It will have a boolean.',
|
|
87
|
-
'substring? ({str}) — Check if the string includes the given sub-string. A parameter is required: the string to look for in this string. It will have a boolean.',
|
|
88
|
-
'format_for_html — Format this string for HTML output. Tabs, spaces and returns are converted to HTML elements. The value of the string is changed.',
|
|
89
|
-
'encode64 — Base64 encode the string. This message changes the value of the string. It will have the encoded string.',
|
|
90
|
-
'decode64 — Decode the string from Base64. This message changes the value of the string. It will have the decoded string.',
|
|
91
|
-
'escape — Escape the string to make it URL safe. This message changes the value of the string. It will have the escaped string.',
|
|
92
|
-
'unescape — Unescape the string (from URL safe format). This message changes the value of the string. It will have the unescaped string.',
|
|
93
|
-
'gen_uuid — Set the value of the string to a newly generated, random UUID. This message changes the value of the string.',
|
|
94
|
-
'gen_alphanumeric ({len}) — Set the value of the string to a newly generated, random alphanumeric string. The {len} parameter is optional; the length is 10 if not specified. This message changes the value of the string.',
|
|
95
|
-
'gen_hex ({len}) — Set the value of the string to a newly generated, random hex string. The {len} parameter is optional; the length is 10 if not specified. This message changes the value of the string.',
|
|
96
|
-
'gen_base64 ({len}) — Set the value of the string to a newly generated, random base64 string. The {len} parameter is optional; the length is 12 if not specified. This message changes the value of the string.',
|
|
97
|
-
'trim — Strip whitespace from the beginning and end of the string. This message changes the value of the string. It will have the trimmed string.',
|
|
98
|
-
'sub ({from}, {to}) — Substitute the first occurrence of {from} with {to}. Both parameters are required. This message changes the value of the string. It will have the result.',
|
|
99
|
-
'gsub ({from}, {to}) — Substitute all occurrences of {from} with {to}. Both parameters are required. This message changes the value of the string. It will have the result.',
|
|
100
|
-
'page — Show the value in a pager (less), for viewing long content a screen at a time.'
|
|
101
|
-
],
|
|
76
|
+
'and END to mark the text range. Shares the same messages ' \
|
|
77
|
+
'as the string object type; the two differ mainly by ' \
|
|
78
|
+
'convention — text for longer, multi-line blocks, string ' \
|
|
79
|
+
'for a single word or line.',
|
|
80
|
+
:messages => StringMsgs.message_docs,
|
|
102
81
|
:examples => <<~EXAMPLES.strip
|
|
103
82
|
t [container] :
|
|
104
83
|
msg [txt] : BEGIN
|
|
@@ -161,23 +161,36 @@ module Gloo
|
|
|
161
161
|
# Messages
|
|
162
162
|
# ---------------------------------------------------------------------
|
|
163
163
|
|
|
164
|
-
#
|
|
164
|
+
#
|
|
165
165
|
# Invoke the function, run the script and return the result.
|
|
166
|
-
#
|
|
166
|
+
#
|
|
167
|
+
# If on_invoke itself hits an error, it is left alone rather
|
|
168
|
+
# than being clobbered with an unreliable result - callers can
|
|
169
|
+
# tell this happened because the engine is left in an error
|
|
170
|
+
# state (@engine.heap.error), same as any other failed verb.
|
|
171
|
+
#
|
|
167
172
|
def invoke args
|
|
168
173
|
@engine.log.debug "Invoking function: #{name}"
|
|
169
174
|
|
|
170
175
|
set_params args if args
|
|
176
|
+
|
|
177
|
+
@engine.heap.error.start_tracking
|
|
171
178
|
run_on_invoke
|
|
179
|
+
failed = @engine.heap.error.error_count.positive?
|
|
180
|
+
|
|
181
|
+
if failed
|
|
182
|
+
run_after_invoke
|
|
183
|
+
return nil
|
|
184
|
+
end
|
|
172
185
|
|
|
173
|
-
if @engine.running_app&.obj&.embedded_renderer
|
|
186
|
+
if @engine.running_app&.obj&.embedded_renderer
|
|
174
187
|
return_value = @engine.running_app.obj.embedded_renderer.render result, params_hash
|
|
175
188
|
else
|
|
176
189
|
return_value = result
|
|
177
190
|
end
|
|
178
191
|
@engine.heap.it.set_to return_value
|
|
179
192
|
run_after_invoke
|
|
180
|
-
|
|
193
|
+
|
|
181
194
|
return return_value
|
|
182
195
|
end
|
|
183
196
|
|
data/lib/gloo/verbs/help.rb
CHANGED
|
@@ -12,7 +12,7 @@ module Gloo
|
|
|
12
12
|
KEYWORD_SHORT = '?'.freeze
|
|
13
13
|
|
|
14
14
|
BANNER = "Entering the gloo help shell. Type 'quit' to exit.\n" \
|
|
15
|
-
"Try: verbs, objects, settings, extensions, libraries, docs, " \
|
|
15
|
+
"Try: verbs, objects, settings, theme, extensions, libraries, docs, " \
|
|
16
16
|
"verb {name}, object {name}, doc {name}, library {name}, extension {name}\n".freeze
|
|
17
17
|
|
|
18
18
|
#
|
|
@@ -50,12 +50,14 @@ module Gloo
|
|
|
50
50
|
:name => KEYWORD,
|
|
51
51
|
:shortcut => KEYWORD_SHORT,
|
|
52
52
|
:description => 'Enter the interactive help shell. From there, ' \
|
|
53
|
-
'look up verbs, object types, settings,
|
|
54
|
-
'and narrative doc pages, or
|
|
55
|
-
'verb, object, doc page,
|
|
53
|
+
'look up verbs, object types, settings, the current color ' \
|
|
54
|
+
'theme, extensions, libraries, and narrative doc pages, or ' \
|
|
55
|
+
'get detailed help for a specific verb, object, doc page, ' \
|
|
56
|
+
'loaded library, or loaded extension.',
|
|
56
57
|
:syntax => [ 'help' ],
|
|
57
58
|
:result => "Enters the help shell, prompt \"help> \". Commands: " \
|
|
58
|
-
'verbs, objects, settings, extensions, libraries, docs
|
|
59
|
+
'verbs, objects, settings, theme, extensions, libraries, docs ' \
|
|
60
|
+
'(lists — theme also shows a color preview of both palettes), ' \
|
|
59
61
|
'verb {name}, object {name}, doc {name}, library {name}, ' \
|
|
60
62
|
'extension {name} (detail for one, tab-completable — library ' \
|
|
61
63
|
'{name} / extension {name} show the README for a loaded core ' \
|
|
@@ -65,6 +67,7 @@ module Gloo
|
|
|
65
67
|
help> verbs
|
|
66
68
|
help> verb put
|
|
67
69
|
help> object container
|
|
70
|
+
help> theme
|
|
68
71
|
help> docs
|
|
69
72
|
help> doc getting_started
|
|
70
73
|
help> library db
|
data/lib/gloo/verbs/invoke.rb
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
#
|
|
4
4
|
# Invoke a function from a script.
|
|
5
5
|
#
|
|
6
|
+
# Resolution, validation and the actual invoke are all handled by
|
|
7
|
+
# Gloo::Core::Invoker, shared with inline calls inside expressions
|
|
8
|
+
# (see Gloo::Expr::Call) so both go through the same error handling.
|
|
9
|
+
#
|
|
6
10
|
|
|
7
11
|
module Gloo
|
|
8
12
|
module Verbs
|
|
@@ -15,24 +19,10 @@ module Gloo
|
|
|
15
19
|
# Run the verb.
|
|
16
20
|
#
|
|
17
21
|
def run
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
pn = Gloo::Core::Pn.new( @engine, @tokens.second )
|
|
23
|
-
func = pn.resolve
|
|
24
|
-
|
|
25
|
-
# Is the object a function?
|
|
26
|
-
if func&.is_function?
|
|
27
|
-
params = get_params_arr
|
|
28
|
-
|
|
29
|
-
@engine.log.debug "invoking function: #{func.pn}"
|
|
30
|
-
result = func.invoke( params )
|
|
31
|
-
@engine.log.debug "function returned: #{result}"
|
|
32
|
-
@engine.heap.it.set_to result
|
|
33
|
-
return result
|
|
34
|
-
end
|
|
35
|
-
end
|
|
22
|
+
target = @tokens.token_count > 1 ? @tokens.second : nil
|
|
23
|
+
arg_tokens = @tokens.token_count > 1 ? @tokens.params[ 1..-1 ] : []
|
|
24
|
+
|
|
25
|
+
return Gloo::Core::Invoker.invoke( @engine, target, arg_tokens )
|
|
36
26
|
end
|
|
37
27
|
|
|
38
28
|
#
|
|
@@ -49,32 +39,6 @@ module Gloo
|
|
|
49
39
|
return KEYWORD_SHORT
|
|
50
40
|
end
|
|
51
41
|
|
|
52
|
-
# ---------------------------------------------------------------------
|
|
53
|
-
# Private functions
|
|
54
|
-
# ---------------------------------------------------------------------
|
|
55
|
-
|
|
56
|
-
private
|
|
57
|
-
|
|
58
|
-
#
|
|
59
|
-
# Get params array.
|
|
60
|
-
#
|
|
61
|
-
def get_params_arr
|
|
62
|
-
@engine.log.debug "token params: #{@tokens.params}"
|
|
63
|
-
params = @tokens.params[1..-1]
|
|
64
|
-
|
|
65
|
-
@engine.log.info "params: #{params}"
|
|
66
|
-
evaluated_params = []
|
|
67
|
-
|
|
68
|
-
params.each do |p|
|
|
69
|
-
expr = Gloo::Expr::Expression.new( @engine, [ p ] )
|
|
70
|
-
evaluated_params << expr.evaluate
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
@engine.log.debug "evaluated_params: #{evaluated_params}"
|
|
74
|
-
|
|
75
|
-
return evaluated_params
|
|
76
|
-
end
|
|
77
|
-
|
|
78
42
|
# ---------------------------------------------------------------------
|
|
79
43
|
# Verb Documentation
|
|
80
44
|
# ---------------------------------------------------------------------
|
|
@@ -87,12 +51,36 @@ module Gloo
|
|
|
87
51
|
:name => KEYWORD,
|
|
88
52
|
:shortcut => KEYWORD_SHORT,
|
|
89
53
|
:description => 'Invoke a function. Set it to the result of the function.',
|
|
90
|
-
:syntax => [
|
|
54
|
+
:syntax => [
|
|
55
|
+
'invoke {path.to.function} {params}',
|
|
56
|
+
'invoke( {path.to.function} {params} ) — inline, usable inside any expression',
|
|
57
|
+
'~>( {path.to.function} {params} ) — inline, shortcut spelling'
|
|
58
|
+
],
|
|
91
59
|
:parameters => [
|
|
92
60
|
'{path.to.function} — The function we want to invoke.',
|
|
93
61
|
'{params} — The list of parameters to the function.'
|
|
94
62
|
],
|
|
95
|
-
:result => 'The result of the function is put into it.'
|
|
63
|
+
:result => 'The result of the function is put into it. The ' \
|
|
64
|
+
'inline forms (invoke(...), ~>(...)) can be used anywhere ' \
|
|
65
|
+
'an expression is evaluated - show, put ... into, if, ' \
|
|
66
|
+
'unless, eval, and more - and evaluate to the result ' \
|
|
67
|
+
'directly rather than going through it.',
|
|
68
|
+
:errors => [
|
|
69
|
+
"#{Gloo::Core::Invoker::NO_TARGET_ERR} — No function reference was given.",
|
|
70
|
+
"#{Gloo::Core::Invoker::NOT_FOUND_ERR}{path.to.function} — The path doesn't resolve to any object.",
|
|
71
|
+
"#{Gloo::Core::Invoker::NOT_FUNCTION_ERR}{path.to.function} — The path resolves to an object that isn't a function.",
|
|
72
|
+
"#{Gloo::Core::Invoker::PARAM_COUNT_ERR}{path.to.function} (expected N, got N) — The number of params given doesn't match what the function declares."
|
|
73
|
+
],
|
|
74
|
+
:notes => 'If the function itself fails during invocation ' \
|
|
75
|
+
'(its on_invoke script hits an error), it is left ' \
|
|
76
|
+
'unchanged rather than being set to an unreliable result. ' \
|
|
77
|
+
'The underlying error is whatever was reported by the ' \
|
|
78
|
+
'failure inside the function. ' \
|
|
79
|
+
'Inline call params are space-separated, same as the ' \
|
|
80
|
+
'standalone verb - each one is evaluated as a single ' \
|
|
81
|
+
'token (a literal or object reference), not a multi-token ' \
|
|
82
|
+
"sub-expression, so invoke( f 3+4 ) won't parse 3+4 as " \
|
|
83
|
+
'one arg.',
|
|
96
84
|
:examples => <<~EXAMPLES.strip
|
|
97
85
|
#
|
|
98
86
|
# Function examples
|
|
@@ -111,6 +99,11 @@ module Gloo
|
|
|
111
99
|
invoke functions.add 3 4
|
|
112
100
|
show it
|
|
113
101
|
|
|
102
|
+
# Inline, anywhere an expression is evaluated:
|
|
103
|
+
show invoke( functions.add 3 4 )
|
|
104
|
+
put ~>( functions.add 3 4 ) into total
|
|
105
|
+
show "Total: " + invoke( functions.add 3 4 )
|
|
106
|
+
|
|
114
107
|
show 'done' (white)
|
|
115
108
|
|
|
116
109
|
#
|
data/lib/gloo/verbs/list.rb
CHANGED
|
@@ -73,16 +73,17 @@ module Gloo
|
|
|
73
73
|
# Show object in standard format.
|
|
74
74
|
#
|
|
75
75
|
def show_obj( obj, indent = ' ' )
|
|
76
|
+
theme = @engine.theme
|
|
76
77
|
if obj.multiline_value? && obj.value_is_array?
|
|
77
|
-
str = "#{indent}#{obj.name}"
|
|
78
|
-
str << " [#{obj.type_display}] : "
|
|
78
|
+
str = theme.emphasis( "#{indent}#{obj.name}" )
|
|
79
|
+
str << theme.accent( " [#{obj.type_display}] : " )
|
|
79
80
|
@engine.log.show str
|
|
80
81
|
obj.value.each do |line|
|
|
81
82
|
@engine.log.show "#{indent} #{line}"
|
|
82
83
|
end
|
|
83
84
|
else
|
|
84
|
-
str = "#{indent}#{obj.name}"
|
|
85
|
-
str << " [#{obj.type_display}] : "
|
|
85
|
+
str = theme.emphasis( "#{indent}#{obj.name}" )
|
|
86
|
+
str << theme.accent( " [#{obj.type_display}] : " )
|
|
86
87
|
str << "#{obj.value}"
|
|
87
88
|
@engine.log.show str
|
|
88
89
|
end
|
|
@@ -7,6 +7,7 @@ tests [can] :
|
|
|
7
7
|
string [can] :
|
|
8
8
|
|
|
9
9
|
s [string] :
|
|
10
|
+
dst [can] :
|
|
10
11
|
|
|
11
12
|
up_down [test] :
|
|
12
13
|
description [string] : Convert string case
|
|
@@ -42,3 +43,56 @@ tests [can] :
|
|
|
42
43
|
tell ^^.s to count_chars
|
|
43
44
|
eval it = 5
|
|
44
45
|
assert "expected 5 characters"
|
|
46
|
+
|
|
47
|
+
split [test] :
|
|
48
|
+
description [string] : Get a substring between two indexes
|
|
49
|
+
on_test [script] :
|
|
50
|
+
put 'one two three' into ^^.s
|
|
51
|
+
tell ^^.s to split (4 7)
|
|
52
|
+
eval it = 'two'
|
|
53
|
+
assert "expected split (4 7) to equal 'two'"
|
|
54
|
+
eval ^^.s = 'one two three'
|
|
55
|
+
assert "expected split to leave the string unchanged"
|
|
56
|
+
|
|
57
|
+
splitl [test] :
|
|
58
|
+
description [string] : Get the substring to the left of an index
|
|
59
|
+
on_test [script] :
|
|
60
|
+
put 'one two three' into ^^.s
|
|
61
|
+
tell ^^.s to splitl (7)
|
|
62
|
+
eval it = 'one two'
|
|
63
|
+
assert "expected splitl (7) to equal 'one two'"
|
|
64
|
+
|
|
65
|
+
splitr [test] :
|
|
66
|
+
description [string] : Get the substring from an index to the end
|
|
67
|
+
on_test [script] :
|
|
68
|
+
put 'one two three' into ^^.s
|
|
69
|
+
tell ^^.s to splitr (4)
|
|
70
|
+
eval it = 'two three'
|
|
71
|
+
assert "expected splitr (4) to equal 'two three'"
|
|
72
|
+
|
|
73
|
+
split_out_of_range [test] :
|
|
74
|
+
description [string] : Out-of-range split indexes clamp to the string's bounds
|
|
75
|
+
on_test [script] :
|
|
76
|
+
put 'one two three' into ^^.s
|
|
77
|
+
tell ^^.s to split (-5 4)
|
|
78
|
+
eval it = 'one '
|
|
79
|
+
assert "expected split (-5 4) to clamp to 'one '"
|
|
80
|
+
tell ^^.s to split (8 100)
|
|
81
|
+
eval it = 'three'
|
|
82
|
+
assert "expected split (8 100) to clamp to 'three'"
|
|
83
|
+
|
|
84
|
+
split_list [test] :
|
|
85
|
+
description [string] : Split a string into children of a target container
|
|
86
|
+
on_test [script] :
|
|
87
|
+
put 'one,two,three' into ^^.s
|
|
88
|
+
tell ^^.s to split_list (',' ^^.dst)
|
|
89
|
+
eval it = 3
|
|
90
|
+
assert "expected split_list to put the part count into it"
|
|
91
|
+
eval ^^.dst.1 = 'one'
|
|
92
|
+
assert "expected dst.1 to equal 'one'"
|
|
93
|
+
eval ^^.dst.2 = 'two'
|
|
94
|
+
assert "expected dst.2 to equal 'two'"
|
|
95
|
+
eval ^^.dst.3 = 'three'
|
|
96
|
+
assert "expected dst.3 to equal 'three'"
|
|
97
|
+
eval ^^.s = 'one,two,three'
|
|
98
|
+
assert "expected split_list to leave the string unchanged"
|
|
@@ -18,3 +18,76 @@ tests [can] :
|
|
|
18
18
|
invoke tests.verbs.invoke.f
|
|
19
19
|
eval it = 42
|
|
20
20
|
assert "expected it to be 42"
|
|
21
|
+
|
|
22
|
+
add [ƒ] :
|
|
23
|
+
on_invoke [script] :
|
|
24
|
+
put ^.params.x + ^.params.y into ^.result
|
|
25
|
+
params [can] :
|
|
26
|
+
x [int] :
|
|
27
|
+
y [int] :
|
|
28
|
+
result [int] :
|
|
29
|
+
|
|
30
|
+
total [int] :
|
|
31
|
+
|
|
32
|
+
inline_invoke_in_show_test [test] :
|
|
33
|
+
description [string] : Inline invoke( ... ) works inside show.
|
|
34
|
+
on_test [script] :
|
|
35
|
+
show invoke( tests.verbs.invoke.add 3 4 )
|
|
36
|
+
eval it = 7
|
|
37
|
+
assert "expected it to be 7"
|
|
38
|
+
|
|
39
|
+
inline_shortcut_in_show_test [test] :
|
|
40
|
+
description [string] : Inline ~>( ... ) works inside show, same as invoke( ... ).
|
|
41
|
+
on_test [script] :
|
|
42
|
+
show ~>( tests.verbs.invoke.add 3 4 )
|
|
43
|
+
eval it = 7
|
|
44
|
+
assert "expected it to be 7"
|
|
45
|
+
|
|
46
|
+
inline_invoke_with_operator_test [test] :
|
|
47
|
+
description [string] : Inline invoke( ... ) composes with an operator.
|
|
48
|
+
on_test [script] :
|
|
49
|
+
show "Total: " + invoke( tests.verbs.invoke.add 3 4 )
|
|
50
|
+
eval it = "Total: 7"
|
|
51
|
+
assert "expected it to be 'Total: 7'"
|
|
52
|
+
|
|
53
|
+
inline_invoke_in_put_test [test] :
|
|
54
|
+
description [string] : Inline invoke( ... ) works inside put ... into.
|
|
55
|
+
on_test [script] :
|
|
56
|
+
put 0 into ^^.total
|
|
57
|
+
put invoke( tests.verbs.invoke.add 3 4 ) into ^^.total
|
|
58
|
+
eval ^^.total = 7
|
|
59
|
+
assert "expected total to be 7"
|
|
60
|
+
|
|
61
|
+
inline_invoke_in_if_test [test] :
|
|
62
|
+
description [string] : Inline invoke( ... ) works inside if.
|
|
63
|
+
on_test [script] :
|
|
64
|
+
eval false
|
|
65
|
+
if invoke( tests.verbs.invoke.add 3 4 ) = 7 then eval true
|
|
66
|
+
assert "expected the if branch to have run"
|
|
67
|
+
|
|
68
|
+
err_fired [bool] : false
|
|
69
|
+
|
|
70
|
+
on_error [script] :
|
|
71
|
+
put true into ^.err_fired
|
|
72
|
+
|
|
73
|
+
inline_invoke_error_test [test] :
|
|
74
|
+
description [string] : Inline invoke( ... ) reports an error for an unresolved target, same as the standalone verb.
|
|
75
|
+
on_test [script] :
|
|
76
|
+
put false into ^^.err_fired
|
|
77
|
+
show invoke( tests.verbs.invoke.no_such_function )
|
|
78
|
+
eval ^^.err_fired = true
|
|
79
|
+
assert "expected on_error to have fired for an unresolved inline call target"
|
|
80
|
+
|
|
81
|
+
greet [ƒ] :
|
|
82
|
+
on_invoke [script] :
|
|
83
|
+
put 'Hi, ' + ^.params.name into ^.result
|
|
84
|
+
params [can] :
|
|
85
|
+
name [string] :
|
|
86
|
+
result [string] :
|
|
87
|
+
|
|
88
|
+
inline_invoke_with_quoted_arg_test [test] :
|
|
89
|
+
description [string] : A quoted string arg inside invoke( ... ) is passed through as one arg, not split on its internal space.
|
|
90
|
+
on_test [script] :
|
|
91
|
+
show invoke( tests.verbs.invoke.greet "Bob Smith" )
|
|
92
|
+
eval it = "Hi, Bob Smith"
|
|
93
|
+
assert "expected it to be 'Hi, Bob Smith'"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gloo
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 6.
|
|
4
|
+
version: 6.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Eric Crane
|
|
@@ -250,12 +250,10 @@ executables:
|
|
|
250
250
|
extensions: []
|
|
251
251
|
extra_rdoc_files: []
|
|
252
252
|
files:
|
|
253
|
-
- ".DS_Store"
|
|
254
253
|
- ".gitignore"
|
|
255
254
|
- ".rubocop.yml"
|
|
256
255
|
- ".ruby-gemset"
|
|
257
256
|
- ".ruby-version"
|
|
258
|
-
- ".travis.yml"
|
|
259
257
|
- CLAUDE.md
|
|
260
258
|
- CODE_OF_CONDUCT.md
|
|
261
259
|
- Gemfile
|
|
@@ -275,6 +273,7 @@ files:
|
|
|
275
273
|
- docs/operators.md
|
|
276
274
|
- docs/plugins.md
|
|
277
275
|
- docs/verbs.md
|
|
276
|
+
- docs/web_app.md
|
|
278
277
|
- exe/gloo
|
|
279
278
|
- exe/o
|
|
280
279
|
- gloo.gemspec
|
|
@@ -293,6 +292,7 @@ files:
|
|
|
293
292
|
- lib/gloo/app/running_app.rb
|
|
294
293
|
- lib/gloo/app/settings.rb
|
|
295
294
|
- lib/gloo/app/table.rb
|
|
295
|
+
- lib/gloo/app/theme.rb
|
|
296
296
|
- lib/gloo/convert/converter.rb
|
|
297
297
|
- lib/gloo/convert/falseclass_to_integer.rb
|
|
298
298
|
- lib/gloo/convert/nilclass_to_date.rb
|
|
@@ -314,6 +314,7 @@ files:
|
|
|
314
314
|
- lib/gloo/core/gloo_system.rb
|
|
315
315
|
- lib/gloo/core/heap.rb
|
|
316
316
|
- lib/gloo/core/here.rb
|
|
317
|
+
- lib/gloo/core/invoker.rb
|
|
317
318
|
- lib/gloo/core/it.rb
|
|
318
319
|
- lib/gloo/core/literal.rb
|
|
319
320
|
- lib/gloo/core/obj.rb
|
|
@@ -332,6 +333,7 @@ files:
|
|
|
332
333
|
- lib/gloo/exec/runner.rb
|
|
333
334
|
- lib/gloo/exec/script.rb
|
|
334
335
|
- lib/gloo/exec/stack.rb
|
|
336
|
+
- lib/gloo/expr/call.rb
|
|
335
337
|
- lib/gloo/expr/expression.rb
|
|
336
338
|
- lib/gloo/expr/l_boolean.rb
|
|
337
339
|
- lib/gloo/expr/l_decimal.rb
|
data/.DS_Store
DELETED
|
Binary file
|