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
data/lib/gloo/core/parser.rb
CHANGED
|
@@ -39,17 +39,58 @@ module Gloo
|
|
|
39
39
|
# If additional params were provided, split them out
|
|
40
40
|
# from the token list.
|
|
41
41
|
#
|
|
42
|
+
# A trailing (...) is normally the optional-param convention
|
|
43
|
+
# (eg. show "text" (color)) and gets split off here. But a
|
|
44
|
+
# trailing (...) can also be an inline function call that
|
|
45
|
+
# happens to be the last thing in the command (eg.
|
|
46
|
+
# show invoke( functions.add 3 4 )) - that must be left alone
|
|
47
|
+
# so it flows into tokenizing as part of the main command.
|
|
48
|
+
#
|
|
42
49
|
def split_params( cmd )
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
+
i = matching_open_paren_index( cmd )
|
|
51
|
+
return cmd, nil unless i
|
|
52
|
+
return cmd, nil if inline_call_opener?( cmd, i )
|
|
53
|
+
|
|
54
|
+
pstr = cmd[ i + 1..-1 ]
|
|
55
|
+
params = pstr.strip[ 0..-2 ] if pstr
|
|
56
|
+
cmd = cmd[ 0, i ].strip
|
|
50
57
|
return cmd, params
|
|
51
58
|
end
|
|
52
59
|
|
|
60
|
+
#
|
|
61
|
+
# Find the index of the '(' that balances the command's final
|
|
62
|
+
# ')', counting depth from the end so an inline call earlier
|
|
63
|
+
# in the command (or nested parens) doesn't confuse the match.
|
|
64
|
+
# Returns nil if the command doesn't end with ')', or the
|
|
65
|
+
# parens aren't balanced.
|
|
66
|
+
#
|
|
67
|
+
def matching_open_paren_index( cmd )
|
|
68
|
+
return nil unless cmd.strip.end_with?( ')' )
|
|
69
|
+
|
|
70
|
+
depth = 0
|
|
71
|
+
( cmd.length - 1 ).downto( 0 ) do |idx|
|
|
72
|
+
case cmd[ idx ]
|
|
73
|
+
when ')' then depth += 1
|
|
74
|
+
when '('
|
|
75
|
+
depth -= 1
|
|
76
|
+
return idx if depth.zero?
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
return nil
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
#
|
|
83
|
+
# Is the '(' at the given index immediately preceded (no
|
|
84
|
+
# space) by an inline-call keyword, eg. invoke( or ~>( ?
|
|
85
|
+
# Shares its keyword list with Gloo::Core::Tokens, which is
|
|
86
|
+
# where such a call actually gets recognized as one token.
|
|
87
|
+
#
|
|
88
|
+
def inline_call_opener?( cmd, paren_index )
|
|
89
|
+
before = cmd[ 0...paren_index ]
|
|
90
|
+
word = before[ /\S*\z/ ]
|
|
91
|
+
return Gloo::Core::Tokens::CALL_OPENERS.include?( word )
|
|
92
|
+
end
|
|
93
|
+
|
|
53
94
|
#
|
|
54
95
|
# Parse a command and then run it if it parsed correctly.
|
|
55
96
|
#
|
data/lib/gloo/core/tokens.rb
CHANGED
|
@@ -9,6 +9,15 @@ module Gloo
|
|
|
9
9
|
module Core
|
|
10
10
|
class Tokens
|
|
11
11
|
|
|
12
|
+
# Keywords that open an inline function call, eg. invoke( ... )
|
|
13
|
+
# or ~>( ... ). Kept here since Tokens is where they get
|
|
14
|
+
# recognized as a single bounded token; Parser#split_params
|
|
15
|
+
# reuses this same list to avoid mistaking a trailing call for
|
|
16
|
+
# its unrelated trailing-optional-param convention.
|
|
17
|
+
CALL_OPENERS = %w[invoke ~>].freeze
|
|
18
|
+
|
|
19
|
+
QUOTE_CHARS = [ '"', "'" ].freeze
|
|
20
|
+
|
|
12
21
|
attr_reader :cmd, :tokens
|
|
13
22
|
|
|
14
23
|
# ---------------------------------------------------------------------
|
|
@@ -142,8 +151,19 @@ module Gloo
|
|
|
142
151
|
#
|
|
143
152
|
# Create a list of token from the given string.
|
|
144
153
|
#
|
|
154
|
+
# An inline call (invoke( ... ) / ~>( ... )) is checked for
|
|
155
|
+
# first since it needs to be quote-aware in its own right (a
|
|
156
|
+
# call's args can include a quoted string) - see
|
|
157
|
+
# find_call_range. Falls through to the original quote-then-
|
|
158
|
+
# plain-split handling, unchanged, when there's no call.
|
|
159
|
+
#
|
|
145
160
|
def tokenize( str )
|
|
146
|
-
|
|
161
|
+
range = find_call_range( str )
|
|
162
|
+
if range
|
|
163
|
+
tokenize( str[ 0...range.first ] ) if range.first.positive?
|
|
164
|
+
@tokens << str[ range ]
|
|
165
|
+
tokenize( str[ range.last + 1..-1 ] ) if range.last + 1 < str.length
|
|
166
|
+
elsif str.index( '"' )
|
|
147
167
|
i = str.index( '"' )
|
|
148
168
|
j = str.index( '"', i + 1 )
|
|
149
169
|
j ||= str.length
|
|
@@ -164,6 +184,66 @@ module Gloo
|
|
|
164
184
|
end
|
|
165
185
|
end
|
|
166
186
|
|
|
187
|
+
#
|
|
188
|
+
# Find the char range of the first top-level (not inside a
|
|
189
|
+
# quoted string) invoke(...)/~>(...) call in str. Returns nil
|
|
190
|
+
# if there isn't one, or it's never properly closed.
|
|
191
|
+
#
|
|
192
|
+
def find_call_range( str )
|
|
193
|
+
i = 0
|
|
194
|
+
while i < str.length
|
|
195
|
+
ch = str[ i ]
|
|
196
|
+
|
|
197
|
+
if QUOTE_CHARS.include?( ch )
|
|
198
|
+
close = str.index( ch, i + 1 )
|
|
199
|
+
return nil unless close
|
|
200
|
+
|
|
201
|
+
i = close + 1
|
|
202
|
+
next
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
CALL_OPENERS.each do |kw|
|
|
206
|
+
opener = "#{kw}("
|
|
207
|
+
next unless str[ i, opener.length ] == opener
|
|
208
|
+
next unless i.zero? || str[ i - 1 ] =~ /\s/
|
|
209
|
+
|
|
210
|
+
close = find_matching_close_paren( str, i + opener.length - 1 )
|
|
211
|
+
return ( i..close ) if close
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
i += 1
|
|
215
|
+
end
|
|
216
|
+
return nil
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
#
|
|
220
|
+
# Given the index of an open paren, find the index of its
|
|
221
|
+
# balancing close paren - skipping over quoted substrings so
|
|
222
|
+
# a stray paren character inside a quoted arg doesn't throw
|
|
223
|
+
# off the depth count. Returns nil if it's never closed.
|
|
224
|
+
#
|
|
225
|
+
def find_matching_close_paren( str, open_index )
|
|
226
|
+
depth = 0
|
|
227
|
+
i = open_index
|
|
228
|
+
while i < str.length
|
|
229
|
+
ch = str[ i ]
|
|
230
|
+
if QUOTE_CHARS.include?( ch )
|
|
231
|
+
close = str.index( ch, i + 1 )
|
|
232
|
+
return nil unless close
|
|
233
|
+
|
|
234
|
+
i = close + 1
|
|
235
|
+
next
|
|
236
|
+
elsif ch == '('
|
|
237
|
+
depth += 1
|
|
238
|
+
elsif ch == ')'
|
|
239
|
+
depth -= 1
|
|
240
|
+
return i if depth.zero?
|
|
241
|
+
end
|
|
242
|
+
i += 1
|
|
243
|
+
end
|
|
244
|
+
return nil
|
|
245
|
+
end
|
|
246
|
+
|
|
167
247
|
end
|
|
168
248
|
end
|
|
169
249
|
end
|
data/lib/gloo/docs/help_shell.rb
CHANGED
|
@@ -41,11 +41,12 @@ module Gloo
|
|
|
41
41
|
# List all verbs.
|
|
42
42
|
#
|
|
43
43
|
def cmd_show_verbs( _obj, _context )
|
|
44
|
+
theme = @engine.theme
|
|
44
45
|
data = "\n"
|
|
45
|
-
data << " Verbs (shortcut, name)\n"
|
|
46
|
+
data << theme.heading( " Verbs (shortcut, name)\n" )
|
|
46
47
|
@engine.dictionary.get_verbs.sort_by( &:keyword ).each do |v|
|
|
47
|
-
cut = v.keyword_shortcut.ljust( 5, ' ' )
|
|
48
|
-
name = v.keyword.ljust( 20, ' ' )
|
|
48
|
+
cut = theme.accent( v.keyword_shortcut.ljust( 5, ' ' ) )
|
|
49
|
+
name = theme.emphasis( v.keyword.ljust( 20, ' ' ) )
|
|
49
50
|
data << " #{cut} #{name} \n"
|
|
50
51
|
end
|
|
51
52
|
@engine.log.show "#{data}\n"
|
|
@@ -55,14 +56,15 @@ module Gloo
|
|
|
55
56
|
# List all object types.
|
|
56
57
|
#
|
|
57
58
|
def cmd_show_objects( _obj, _context )
|
|
59
|
+
theme = @engine.theme
|
|
58
60
|
data = "\n"
|
|
59
|
-
data << " Objects \n"
|
|
61
|
+
data << theme.heading( " Objects \n" )
|
|
60
62
|
@engine.dictionary.get_obj_types.sort_by( &:typename ).each do |o|
|
|
61
63
|
if o.short_typename != o.typename
|
|
62
|
-
short = "(#{o.short_typename})"
|
|
63
|
-
name = "#{o.typename
|
|
64
|
+
short = theme.accent( "(#{o.short_typename})" )
|
|
65
|
+
name = "#{theme.emphasis( o.typename )} #{short}"
|
|
64
66
|
else
|
|
65
|
-
name = o.typename
|
|
67
|
+
name = theme.emphasis( o.typename )
|
|
66
68
|
end
|
|
67
69
|
data << " #{name.ljust( 30, ' ' )}\n"
|
|
68
70
|
end
|
|
@@ -76,21 +78,44 @@ module Gloo
|
|
|
76
78
|
@engine.settings.show
|
|
77
79
|
end
|
|
78
80
|
|
|
81
|
+
#
|
|
82
|
+
# Show the current theme, how to change it, and a color preview
|
|
83
|
+
# of both palettes - handy for checking how they actually render
|
|
84
|
+
# in whatever terminal you're sitting in.
|
|
85
|
+
#
|
|
86
|
+
def cmd_show_theme( _obj, _context )
|
|
87
|
+
theme = @engine.theme
|
|
88
|
+
settings = @engine.settings
|
|
89
|
+
config_file = File.join( settings.config_path, 'gloo.yml' )
|
|
90
|
+
|
|
91
|
+
data = "\n"
|
|
92
|
+
data << theme.heading( " Theme\n" )
|
|
93
|
+
data << " Current theme: #{theme.emphasis( settings.theme )}\n\n"
|
|
94
|
+
data << theme.muted( " Change it in #{config_file}:\n" )
|
|
95
|
+
data << theme.muted( " theme: dark (or: light)\n\n" )
|
|
96
|
+
data << theme_preview( ' Dark palette', Gloo::App::Theme.new( 'dark' ) )
|
|
97
|
+
data << "\n"
|
|
98
|
+
data << theme_preview( ' Light palette', Gloo::App::Theme.new( 'light' ) )
|
|
99
|
+
@engine.log.show "#{data}\n"
|
|
100
|
+
end
|
|
101
|
+
|
|
79
102
|
#
|
|
80
103
|
# List loaded extensions.
|
|
81
104
|
#
|
|
82
105
|
def cmd_show_extensions( _obj, _context )
|
|
106
|
+
theme = @engine.theme
|
|
83
107
|
data = "\n"
|
|
84
|
-
data << " Extensions\n"
|
|
85
|
-
data <<
|
|
108
|
+
data << theme.heading( " Extensions\n" )
|
|
109
|
+
data << theme.muted(
|
|
110
|
+
" Use `load ext {name}` to load a User Extension, \n" \
|
|
86
111
|
" then `object {name}` / `verb {name}` / `extension {name}` here to see what it adds. \n" \
|
|
87
|
-
" Only loaded extensions are listed below.\n\n"
|
|
112
|
+
" Only loaded extensions are listed below.\n\n" )
|
|
88
113
|
loaded = @engine.ext_manager.loaded_extensions
|
|
89
114
|
if loaded.empty?
|
|
90
|
-
data << " (none loaded)\n"
|
|
115
|
+
data << theme.muted( " (none loaded)\n" )
|
|
91
116
|
else
|
|
92
117
|
loaded.sort.each do |name, _ext|
|
|
93
|
-
data << " #{name
|
|
118
|
+
data << " #{theme.emphasis( name )} \n"
|
|
94
119
|
end
|
|
95
120
|
end
|
|
96
121
|
@engine.log.show "#{data}\n"
|
|
@@ -100,17 +125,19 @@ module Gloo
|
|
|
100
125
|
# List loaded libraries.
|
|
101
126
|
#
|
|
102
127
|
def cmd_show_libraries( _obj, _context )
|
|
128
|
+
theme = @engine.theme
|
|
103
129
|
data = "\n"
|
|
104
|
-
data << " Libraries\n"
|
|
105
|
-
data <<
|
|
130
|
+
data << theme.heading( " Libraries\n" )
|
|
131
|
+
data << theme.muted(
|
|
132
|
+
" Use `load lib {name}` to load a core library, \n" \
|
|
106
133
|
" then `object {name}` / `verb {name}` here to see what it adds. \n" \
|
|
107
|
-
" Only loaded libraries are listed below.\n\n"
|
|
134
|
+
" Only loaded libraries are listed below.\n\n" )
|
|
108
135
|
loaded = @engine.lib_manager.loaded_libraries
|
|
109
136
|
if loaded.empty?
|
|
110
|
-
data << " (none loaded)\n"
|
|
137
|
+
data << theme.muted( " (none loaded)\n" )
|
|
111
138
|
else
|
|
112
139
|
loaded.sort.each do |name, _lib|
|
|
113
|
-
data << " #{name
|
|
140
|
+
data << " #{theme.emphasis( name )} \n"
|
|
114
141
|
end
|
|
115
142
|
end
|
|
116
143
|
@engine.log.show "#{data}\n"
|
|
@@ -120,10 +147,11 @@ module Gloo
|
|
|
120
147
|
# List all narrative doc pages (dev/gloo/docs/*.md).
|
|
121
148
|
#
|
|
122
149
|
def cmd_show_docs( _obj, _context )
|
|
150
|
+
theme = @engine.theme
|
|
123
151
|
data = "\n"
|
|
124
|
-
data << " Docs\n"
|
|
152
|
+
data << theme.heading( " Docs\n" )
|
|
125
153
|
doc_page_names.each do |name|
|
|
126
|
-
data << " #{name
|
|
154
|
+
data << " #{theme.emphasis( name )}\n"
|
|
127
155
|
end
|
|
128
156
|
@engine.log.show "#{data}\n"
|
|
129
157
|
end
|
|
@@ -199,6 +227,22 @@ module Gloo
|
|
|
199
227
|
|
|
200
228
|
private
|
|
201
229
|
|
|
230
|
+
#
|
|
231
|
+
# Build a labeled preview block for one theme's palette: one line
|
|
232
|
+
# per role, colored in that role's own color, labeled with the
|
|
233
|
+
# role's name. Padding is applied to the plain label before it's
|
|
234
|
+
# colorized, so the ANSI codes don't throw off the alignment.
|
|
235
|
+
#
|
|
236
|
+
def theme_preview( title, theme )
|
|
237
|
+
lines = "#{theme.heading( title )}\n"
|
|
238
|
+
Gloo::App::Theme::ROLES.each do |role|
|
|
239
|
+
label = role.to_s.ljust( 12 )
|
|
240
|
+
lines << theme.send( role, " #{label}#{role}" )
|
|
241
|
+
lines << "\n"
|
|
242
|
+
end
|
|
243
|
+
return lines
|
|
244
|
+
end
|
|
245
|
+
|
|
202
246
|
#
|
|
203
247
|
# Colorize markdown and page it, bracketed with a '---' rule above
|
|
204
248
|
# and below so the content stands out from surrounding CLI output.
|
|
@@ -206,7 +250,7 @@ module Gloo
|
|
|
206
250
|
def page_markdown( md )
|
|
207
251
|
rule = '-' * @engine.platform.cols
|
|
208
252
|
bracketed = "#{rule}\n#{md.strip}\n#{rule}\n"
|
|
209
|
-
@engine.platform.page( Gloo::Docs::MarkdownRenderer.colorize( bracketed ) )
|
|
253
|
+
@engine.platform.page( Gloo::Docs::MarkdownRenderer.colorize( bracketed, @engine.theme ) )
|
|
210
254
|
end
|
|
211
255
|
|
|
212
256
|
#
|
|
@@ -257,6 +301,9 @@ module Gloo
|
|
|
257
301
|
name: 'objects', description: 'List all object types', method: 'cmd_show_objects' )
|
|
258
302
|
add_command_node(
|
|
259
303
|
name: 'settings', description: 'Show application settings', method: 'cmd_show_settings' )
|
|
304
|
+
add_command_node(
|
|
305
|
+
name: 'theme', description: 'Show the current theme and preview both palettes',
|
|
306
|
+
method: 'cmd_show_theme' )
|
|
260
307
|
add_command_node(
|
|
261
308
|
name: 'extensions', description: 'List loaded extensions', method: 'cmd_show_extensions' )
|
|
262
309
|
add_command_node(
|
|
@@ -16,18 +16,18 @@ module Gloo
|
|
|
16
16
|
|
|
17
17
|
#
|
|
18
18
|
# Colorize the given markdown text for terminal display.
|
|
19
|
-
def self.colorize( text )
|
|
19
|
+
def self.colorize( text, theme )
|
|
20
20
|
in_code = false
|
|
21
21
|
lines = text.split( "\n" ).map do |line|
|
|
22
22
|
if line.start_with?( '```' )
|
|
23
23
|
in_code = !in_code
|
|
24
|
-
next line
|
|
24
|
+
next theme.muted( line )
|
|
25
25
|
end
|
|
26
|
-
next
|
|
27
|
-
next line.sub( /^#\s*/, '' )
|
|
28
|
-
next line.sub( /^##\s*/, '' )
|
|
29
|
-
next
|
|
30
|
-
next
|
|
26
|
+
next theme.muted( line ) if in_code
|
|
27
|
+
next theme.heading( line.sub( /^#\s*/, '' ) ) if line.start_with?( '# ' )
|
|
28
|
+
next theme.subheading( line.sub( /^##\s*/, '' ) ) if line.start_with?( '## ' )
|
|
29
|
+
next theme.subheading( line ) if line.start_with?( '### ' ) || line.start_with?( '#### ' )
|
|
30
|
+
next theme.muted( line ) if line =~ /\A-{3,}\z/
|
|
31
31
|
|
|
32
32
|
line
|
|
33
33
|
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Author:: Eric Crane (mailto:eric.crane@mac.com)
|
|
2
|
+
# Copyright:: Copyright (c) 2026 Eric Crane. All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# An inline function call inside an expression, eg.
|
|
5
|
+
# invoke( functions.add 3 4 ) or ~>( functions.add 3 4 ).
|
|
6
|
+
#
|
|
7
|
+
# Gloo::Core::Tokens recognizes the whole invoke(...)/~>(...) span
|
|
8
|
+
# as one token; Call takes that single token, splits it back out
|
|
9
|
+
# into a target path and raw arg tokens (reusing Tokens itself, so
|
|
10
|
+
# quoted args and nested calls tokenize the same way they would
|
|
11
|
+
# anywhere else), and hands both to Gloo::Core::Invoker - the same
|
|
12
|
+
# resolve/validate/invoke path the standalone invoke verb uses.
|
|
13
|
+
#
|
|
14
|
+
|
|
15
|
+
module Gloo
|
|
16
|
+
module Expr
|
|
17
|
+
class Call
|
|
18
|
+
|
|
19
|
+
attr_reader :target, :arg_tokens
|
|
20
|
+
|
|
21
|
+
#
|
|
22
|
+
# Does the given token look like an inline call?
|
|
23
|
+
#
|
|
24
|
+
def self.call?( token )
|
|
25
|
+
return false unless token.is_a?( String )
|
|
26
|
+
|
|
27
|
+
return Gloo::Core::Tokens::CALL_OPENERS.any? do |kw|
|
|
28
|
+
token.start_with?( "#{kw}(" ) && token.end_with?( ')' )
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
#
|
|
33
|
+
# Parse the call token into a target path and raw arg tokens.
|
|
34
|
+
#
|
|
35
|
+
def initialize( engine, token )
|
|
36
|
+
@engine = engine
|
|
37
|
+
|
|
38
|
+
inner = token[ token.index( '(' ) + 1..-2 ].to_s
|
|
39
|
+
tokens = Gloo::Core::Tokens.new( inner )
|
|
40
|
+
@target = tokens.first
|
|
41
|
+
@arg_tokens = tokens.params || []
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
#
|
|
45
|
+
# Resolve, validate and invoke - returns the result value, or
|
|
46
|
+
# nil if it failed (an error will already have been reported).
|
|
47
|
+
#
|
|
48
|
+
def value
|
|
49
|
+
return Gloo::Core::Invoker.invoke( @engine, @target, @arg_tokens )
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
data/lib/gloo/expr/expression.rb
CHANGED
|
@@ -47,6 +47,7 @@ module Gloo
|
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
return @left.value if @left.is_a? Gloo::Core::Literal
|
|
50
|
+
return @left.value if @left.is_a? Gloo::Expr::Call
|
|
50
51
|
return resolve_ref @left if @left.is_a? Gloo::Core::Pn
|
|
51
52
|
|
|
52
53
|
return @left
|
|
@@ -75,6 +76,7 @@ module Gloo
|
|
|
75
76
|
#
|
|
76
77
|
def evaluate_sym( sym )
|
|
77
78
|
return sym.value if sym.is_a? Gloo::Core::Literal
|
|
79
|
+
return sym.value if sym.is_a? Gloo::Expr::Call
|
|
78
80
|
return resolve_ref sym if sym.is_a? Gloo::Core::Pn
|
|
79
81
|
|
|
80
82
|
return sym
|
|
@@ -109,6 +111,7 @@ module Gloo
|
|
|
109
111
|
return LInteger.new( token ) if LInteger.integer?( token )
|
|
110
112
|
return LString.new( token ) if LString.string?( token )
|
|
111
113
|
return LDecimal.new( token ) if LDecimal.decimal?( token )
|
|
114
|
+
return Gloo::Expr::Call.new( @engine, token ) if Gloo::Expr::Call.call?( token )
|
|
112
115
|
|
|
113
116
|
# last chance: an Object reference
|
|
114
117
|
return Gloo::Core::Pn.new( @engine, token )
|
|
@@ -59,31 +59,11 @@ module Gloo
|
|
|
59
59
|
:name => KEYWORD,
|
|
60
60
|
:shortcut => KEYWORD_SHORT,
|
|
61
61
|
:description => 'A string value. For string interpolation, ' \
|
|
62
|
-
'see the erb object type.'
|
|
63
|
-
|
|
64
|
-
'
|
|
65
|
-
'
|
|
66
|
-
|
|
67
|
-
'count_chars — Count the number of characters in the string. It will have the character count.',
|
|
68
|
-
'count_words — Count the number of words in the string. It will have the word count.',
|
|
69
|
-
'count_lines — Count the number of lines in the string. It will have the line count.',
|
|
70
|
-
'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.',
|
|
71
|
-
'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.',
|
|
72
|
-
'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.',
|
|
73
|
-
'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.',
|
|
74
|
-
'encode64 — Base64 encode the string. This message changes the value of the string. It will have the encoded string.',
|
|
75
|
-
'decode64 — Decode the string from Base64. This message changes the value of the string. It will have the decoded string.',
|
|
76
|
-
'escape — Escape the string to make it URL safe. This message changes the value of the string. It will have the escaped string.',
|
|
77
|
-
'unescape — Unescape the string (from URL safe format). This message changes the value of the string. It will have the unescaped string.',
|
|
78
|
-
'gen_uuid — Set the value of the string to a newly generated, random UUID. This message changes the value of the string.',
|
|
79
|
-
'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.',
|
|
80
|
-
'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.',
|
|
81
|
-
'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.',
|
|
82
|
-
'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.',
|
|
83
|
-
'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.',
|
|
84
|
-
'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.',
|
|
85
|
-
'page — Show the value in a pager (less), for viewing long content a screen at a time.'
|
|
86
|
-
],
|
|
62
|
+
'see the erb object type. Shares the same messages as the ' \
|
|
63
|
+
'text object type; the two differ mainly by convention — ' \
|
|
64
|
+
'string for a single word or line, text for longer, ' \
|
|
65
|
+
'multi-line blocks.',
|
|
66
|
+
:messages => StringMsgs.message_docs,
|
|
87
67
|
:examples => <<~EXAMPLES.strip
|
|
88
68
|
s [can] :
|
|
89
69
|
msg [string] : Hello World!
|