gloo 7.1 → 7.1.2
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/Rakefile +0 -2
- data/docs/application.md +9 -0
- data/docs/development.md +22 -2
- data/docs/iterators.md +25 -1
- data/docs/language_syntax.md +1 -1
- data/docs/plugins.md +1 -1
- data/docs/verbs.md +2 -1
- data/lib/VERSION +1 -1
- data/lib/VERSION_NOTES +16 -0
- data/lib/gloo/app/args.rb +3 -5
- data/lib/gloo/app/log.rb +2 -2
- data/lib/gloo/core/invoker.rb +20 -7
- data/lib/gloo/core/pn.rb +5 -0
- data/lib/gloo/core/verb.rb +14 -3
- data/lib/gloo/exec/exec_env.rb +6 -3
- data/lib/gloo/exec/script.rb +7 -0
- data/lib/gloo/objs/basic/script.rb +9 -0
- data/lib/gloo/objs/ctrl/each.rb +5 -3
- data/lib/gloo/objs/ctrl/each_child.rb +6 -2
- data/lib/gloo/objs/ctrl/each_dir.rb +1 -1
- data/lib/gloo/objs/ctrl/each_file.rb +1 -1
- data/lib/gloo/objs/ctrl/each_line.rb +4 -3
- data/lib/gloo/objs/ctrl/each_word.rb +1 -1
- data/lib/gloo/objs/ctrl/repeat.rb +5 -3
- data/lib/gloo/objs/str_utils/cipher.rb +10 -2
- data/lib/gloo/persist/declaration_ledger.rb +6 -6
- data/lib/gloo/persist/disc_mech.rb +38 -20
- data/lib/gloo/persist/file_loader.rb +1 -1
- data/lib/gloo/persist/persist_man.rb +1 -1
- data/lib/gloo/verbs/break.rb +7 -2
- data/lib/gloo/verbs/create.rb +4 -2
- data/lib/gloo/verbs/eval.rb +5 -4
- data/lib/gloo/verbs/invoke.rb +7 -6
- data/lib/gloo/verbs/load.rb +7 -1
- data/lib/gloo/verbs/log.rb +8 -4
- data/lib/gloo/verbs/show.rb +15 -10
- data/lib/gloo.rb +18 -4
- data/lib/run.rb +3 -9
- data/test.gloo/app/embed.test.gloo +31 -0
- data/test.gloo/app/fixtures/embed_harness.rb +15 -0
- data/test.gloo/ctrl/each.test.gloo +72 -0
- data/test.gloo/lang/exceptions.test.gloo +22 -0
- data/test.gloo/objs/cipher.test.gloo +27 -0
- data/test.gloo/objs/int.test.gloo +6 -3
- data/test.gloo/verbs/break.test.gloo +66 -0
- data/test.gloo/verbs/create.test.gloo +25 -0
- data/test.gloo/verbs/eval.test.gloo +8 -0
- data/test.gloo/verbs/invoke.test.gloo +50 -0
- data/test.gloo/verbs/load.test.gloo +34 -0
- data/test.gloo/verbs/log.test.gloo +16 -0
- data/test.gloo/verbs/show.test.gloo +32 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e3e9f9ec23fd38faee18f95a521fe1d21a8e572b2306c814233c3fc67ae6e5f3
|
|
4
|
+
data.tar.gz: a1f2f961d1860473c14faefb6c5f9ffe0556bae36479cf3755b8333a1b0db68c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d1665486b345b607c64f98519ac7219ac720ccad75d88d135a88225e3ea527871e6305acc95db9b31a89b7ba5373a6016b7fbcb0a354dfc82ecd04a3f5733bc9
|
|
7
|
+
data.tar.gz: 35f9a3c3013d394ed4be04106f682ef92b14328279b12524d345f5a5a04f4ce31d7c8645d38a48484ac699a0d9d8ccc231340004a90cb0b5acdb98f890809b58
|
data/Rakefile
CHANGED
data/docs/application.md
CHANGED
|
@@ -31,6 +31,8 @@ gloo [global option] [file]
|
|
|
31
31
|
|
|
32
32
|
Running gloo with a file specified will run that file. Once that file is done, gloo will quit. However, by specifying `--cli`, once the file has finished, gloo will remain open in CLI mode.
|
|
33
33
|
|
|
34
|
+
Gloo can also be started from another Ruby program; see Development, Running Gloo from Ruby.
|
|
35
|
+
|
|
34
36
|
If a file named on the command line can't be found (a typo, the wrong folder, a missing `.gloo` extension on a full path), gloo prints `File '{name}' was not found.` to stderr and exits with a non-zero status. Any other load or run error also exits non-zero — including syntax errors in a file that otherwise loaded (gloo keeps loading past them, reporting each with its file and line) — so a shell script calling gloo can tell a failed run from a clean one.
|
|
35
37
|
|
|
36
38
|
When specifying a file there are a couple ways to reference the gloo file to open:
|
|
@@ -76,6 +78,11 @@ When specifying a file there are a couple ways to reference the gloo file to ope
|
|
|
76
78
|
- Exits with status 1 if any test failed, or errors or warnings
|
|
77
79
|
were logged that no test expected; 0 otherwise.
|
|
78
80
|
- See Testing.
|
|
81
|
+
--embed - Run in Embedded mode
|
|
82
|
+
- Start the engine and return, for a Ruby program that drives
|
|
83
|
+
gloo itself.
|
|
84
|
+
- This is the default when gloo is created in code with no file
|
|
85
|
+
or mode. See Development, Running Gloo from Ruby.
|
|
79
86
|
--version - Show application version
|
|
80
87
|
--help - Show the help screen
|
|
81
88
|
```
|
|
@@ -158,6 +165,8 @@ Gloo writes to the `gloo.log` and `error.log` files as well as to the console.
|
|
|
158
165
|
|
|
159
166
|
Debug messages are written to the log only, but other messages are also written to the console unless the application is running in quiet mode.
|
|
160
167
|
|
|
168
|
+
On the console, errors and warnings go to stderr, while a script's own output (`show`) goes to stdout. So the two can be redirected separately: `gloo my_script.gloo 2>/dev/null` shows just the script's output.
|
|
169
|
+
|
|
161
170
|
Only error and warning level messages are written to the error log.
|
|
162
171
|
|
|
163
172
|
In test mode (`--test`), errors and warnings go to the log files only, not the console, so the test output is just the test results. An error a test expects (or one from a test file that deliberately contains a problem) is still in `error.log`. The summary at the end of a test run says how many errors and warnings were logged, and by which tests, leaving out those a test expects (`expect_errors`). See Testing.
|
data/docs/development.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Development
|
|
2
2
|
|
|
3
|
-
How the interpreter's source is laid out, how to run its tests,
|
|
3
|
+
How the interpreter's source is laid out, how to run its tests, the conventions to follow when changing it, and how to run gloo from your own Ruby code. This page is for working on gloo itself or driving it from Ruby; for using gloo, start with Getting Started.
|
|
4
4
|
|
|
5
5
|
**Contents**
|
|
6
6
|
|
|
@@ -8,6 +8,7 @@ How the interpreter's source is laid out, how to run its tests, and the conventi
|
|
|
8
8
|
- Tests
|
|
9
9
|
- Documentation
|
|
10
10
|
- Reporting Errors
|
|
11
|
+
- Running Gloo from Ruby
|
|
11
12
|
|
|
12
13
|
## Source Layout
|
|
13
14
|
|
|
@@ -40,7 +41,7 @@ There are two automated suites. Run both when making a change, and add to both:
|
|
|
40
41
|
|
|
41
42
|
### Unit tests — `test/`
|
|
42
43
|
|
|
43
|
-
Standard Ruby minitest tests. The layout mirrors `lib/gloo/`, one test file per class or verb: `test/<area>/<name>_test.rb`, for example `test/verbs/show_test.rb` for `lib/gloo/verbs/show.rb`. Test classes inherit from `
|
|
44
|
+
Standard Ruby minitest tests. The layout mirrors `lib/gloo/`, one test file per class or verb: `test/<area>/<name>_test.rb`, for example `test/verbs/show_test.rb` for `lib/gloo/verbs/show.rb`. Test classes inherit from `BaseTest` (in `test/base_test.rb`), or from `BaseEngineTest` (in `test/base_engine_test.rb`) for a test that needs a started engine in `@engine`.
|
|
44
45
|
|
|
45
46
|
```
|
|
46
47
|
rake test
|
|
@@ -98,3 +99,22 @@ Gloo reports every problem in a way scripts can see (see Language, Syntax, Error
|
|
|
98
99
|
- A question whose answer is "no" is just a result, not an error.
|
|
99
100
|
|
|
100
101
|
Don't use `@engine.log.error` or `@engine.log.warn` for these; they only write to the log, and scripts can't see them. Build "not found" messages with `Gloo::Core::NotFound` (for example `NotFound.object( name )`) so they always read the same way.
|
|
102
|
+
|
|
103
|
+
## Running Gloo from Ruby
|
|
104
|
+
|
|
105
|
+
Gloo can be started from another Ruby program, such as a test harness or an app that runs gloo commands. Create the engine with the params gloo should see — the same words you would type after `gloo` on the command line:
|
|
106
|
+
|
|
107
|
+
```ruby
|
|
108
|
+
require 'gloo'
|
|
109
|
+
|
|
110
|
+
context = Gloo::App::EngineContext.new( [] )
|
|
111
|
+
engine = Gloo::App::Engine.new( context )
|
|
112
|
+
engine.start
|
|
113
|
+
|
|
114
|
+
engine.process_cmd "create greeting as string : 'hello'"
|
|
115
|
+
engine.process_cmd 'show greeting'
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Only those params are used. The Ruby program's own command line (`ARGV`) is never read, so running `ruby harness.rb some_file.gloo` doesn't make gloo load `some_file.gloo`. Pass anything gloo should see explicitly. Only the `gloo` command itself (`Gloo.run`) reads the command line.
|
|
119
|
+
|
|
120
|
+
With no file and no mode switch, the engine runs in Embedded mode (`--embed`): `start` sets the engine up and returns without loading or running anything, and your code drives it from there, for example with `process_cmd`.
|
data/docs/iterators.md
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
- Each Line
|
|
10
10
|
- Each Word
|
|
11
11
|
- Repeat
|
|
12
|
+
- Ending a Loop Early
|
|
12
13
|
|
|
13
14
|
## Each
|
|
14
15
|
|
|
@@ -214,7 +215,7 @@ Iterate for each line in a text block (string).
|
|
|
214
215
|
Children:
|
|
215
216
|
|
|
216
217
|
- `line` (string)
|
|
217
|
-
- The single line of text.
|
|
218
|
+
- The single line of text, without its line ending (`\n` or `\r\n`). A blank line is still visited, as an empty string.
|
|
218
219
|
- `in` (text)
|
|
219
220
|
- The block of text or string.
|
|
220
221
|
- `do` (script)
|
|
@@ -328,3 +329,26 @@ repeat [can] :
|
|
|
328
329
|
```
|
|
329
330
|
|
|
330
331
|
See also: Object Base.
|
|
332
|
+
|
|
333
|
+
## Ending a Loop Early
|
|
334
|
+
|
|
335
|
+
`break` in a loop's `do` script ends the whole loop, not just that pass. This works for `repeat` and every kind of `each`. The script that ran the loop carries on with its next line:
|
|
336
|
+
|
|
337
|
+
```gloo
|
|
338
|
+
early [can] :
|
|
339
|
+
on_load [script] :
|
|
340
|
+
tell early.r to run
|
|
341
|
+
show 'after the loop'
|
|
342
|
+
r [repeat] :
|
|
343
|
+
times [int] : 5
|
|
344
|
+
index [int] :
|
|
345
|
+
do [script] :
|
|
346
|
+
if early.r.index = 2 then break
|
|
347
|
+
show early.r.index
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
This shows `0`, `1`, then `after the loop`.
|
|
351
|
+
|
|
352
|
+
`break` ends the script it's written in. So in nested loops, a `break` in the inner loop's `do` ends only the inner loop, and a `break` in a script the `do` script runs (with `run`, or a function it invokes) ends only that script, not the loop. An `each` with `group_by` still runs `on_group_end` for the group that was open when the loop ended.
|
|
353
|
+
|
|
354
|
+
See also: Break.
|
data/docs/language_syntax.md
CHANGED
|
@@ -75,7 +75,7 @@ Gloo makes a best guess and keeps going when it can, but it never does so silent
|
|
|
75
75
|
- **Warning** — the command was done, but probably not the way it was meant: a value that isn't really of its type, used as a best guess (`'x' is not an integer; using 0.`), or indentation that doesn't line up. A warning is only logged; it isn't an error.
|
|
76
76
|
- **Just a result** — a question whose answer is "no" is not an error at all: `exists?`, `contains?`, `substring?`, and `index_of` (which gives -1) answer in `it`.
|
|
77
77
|
|
|
78
|
-
Errors are logged with where they happened in front of the message: the file and line while a file is loading (`app.gloo:12: Unknown type 'strng'; using untyped.`),
|
|
78
|
+
Errors and warnings are logged with where they happened in front of the message: the file and line while a file is loading (`app.gloo:12: Unknown type 'strng'; using untyped.`), even when a script started the load; otherwise the script and its line (`app.on_load, line 3: Object 'app.nme' was not found.`). On the console both go to stderr, apart from a script's own output. "Not found" errors always read the same way: `Object 'x' was not found.`, `File 'x' was not found.`, `Folder 'x' was not found.`, `Verb 'x' was not found.`
|
|
79
79
|
|
|
80
80
|
What happens after an error:
|
|
81
81
|
|
data/docs/plugins.md
CHANGED
|
@@ -372,7 +372,7 @@ Register it from `stats_ext.rb` with `callback.register_obj( Stats )`.
|
|
|
372
372
|
|
|
373
373
|
### Custom `each` iterators
|
|
374
374
|
|
|
375
|
-
An extension's object can also plug into the built-in `each` verb by providing its own iterator. `extensions/git/src/each_repo.rb` shows the shape: a plain Ruby class (not a `Gloo::Core::Obj`) with `self.use_for?( iterator_obj )` — which returns `true` when the `each` loop's iterator object looks like a match (here, when it has a `repo` child) — and a `run` method that walks whatever it's iterating over, setting the loop's child value and calling `@iterator_obj.run_do` for each item. See the git extension for the full pattern; this hook only makes sense for extensions that also register a matching object type (here, `git_repo`).
|
|
375
|
+
An extension's object can also plug into the built-in `each` verb by providing its own iterator. `extensions/git/src/each_repo.rb` shows the shape: a plain Ruby class (not a `Gloo::Core::Obj`) with `self.use_for?( iterator_obj )` — which returns `true` when the `each` loop's iterator object looks like a match (here, when it has a `repo` child) — and a `run` method that walks whatever it's iterating over, setting the loop's child value and calling `@iterator_obj.run_do` for each item. `run_do` returns true when a `break` in the `do` script ended it, so stop the walk then: `break if @iterator_obj.run_do`. See the git extension for the full pattern; this hook only makes sense for extensions that also register a matching object type (here, `git_repo`).
|
|
376
376
|
|
|
377
377
|
### Documenting the extension
|
|
378
378
|
|
data/docs/verbs.md
CHANGED
|
@@ -86,12 +86,13 @@ If the destination doesn't exist, or the expression can't be worked out (eg. it
|
|
|
86
86
|
|
|
87
87
|
## Load & Save
|
|
88
88
|
|
|
89
|
-
`load` reads a `.gloo` file into the heap and runs its `on_load` script. Give a path relative to the project folder
|
|
89
|
+
`load` reads a `.gloo` file into the heap and runs its `on_load` script. Give a path relative to the project folder, a full path, or one starting with `~` for your home folder; the `.gloo` extension is optional. `*` in place of a file name loads every `.gloo` file in a folder, wherever the folder is. Anything before the `*` is the start of the file names, so `my/app/a*` loads just the files whose names start with `a`.
|
|
90
90
|
|
|
91
91
|
```gloo
|
|
92
92
|
> load my/project/config
|
|
93
93
|
> load my/app/*
|
|
94
94
|
> load ~/.my_app/settings.gloo
|
|
95
|
+
> load ~/.my_app/plugins/*
|
|
95
96
|
```
|
|
96
97
|
|
|
97
98
|
If the name can't be resolved to a file, `load` reports `File '{name}' was not found.` — it fires `on_error` and, when the file was named on the `gloo` command line, prints the message to stderr and exits non-zero. A bad or missing file never fails silently.
|
data/lib/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.1
|
|
1
|
+
7.1.2
|
data/lib/VERSION_NOTES
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
7.1.2 - 2026.10.28
|
|
2
|
+
- Fixes bug with create verb and ^.here reference.
|
|
3
|
+
- Behaviour change: an each over lines gives each line without its line ending.
|
|
4
|
+
- Load with * now works for a full path or a ~ folder, not just a folder in the project.
|
|
5
|
+
- Load no longer needs the .gloo extension for a full or ~ path.
|
|
6
|
+
- Clean run of gloo from a different ruby process (or test).
|
|
7
|
+
- Fixes looking for each child with a group by.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
7.1.1 - 2026.10.26
|
|
11
|
+
- Fixes issues with the value in it after a failed command.
|
|
12
|
+
- Warnings go to stderr, like errors.
|
|
13
|
+
- Break in a loop's do script now ends the whole loop.
|
|
14
|
+
- Cipher doc and test updated.
|
|
15
|
+
|
|
16
|
+
|
|
1
17
|
7.1 - 2026.10.23
|
|
2
18
|
- Loading an empty gloo file is now allowed and only gives a warning.
|
|
3
19
|
- Adds configuration setting for tmp directory.
|
data/lib/gloo/app/args.rb
CHANGED
|
@@ -12,7 +12,6 @@ module Gloo
|
|
|
12
12
|
class Args
|
|
13
13
|
|
|
14
14
|
QUIET = 'quiet'.freeze
|
|
15
|
-
GLOO_ENV = 'GLOO_ENV'.freeze
|
|
16
15
|
|
|
17
16
|
attr_reader :switches, :files, :app_path, :command_tokens
|
|
18
17
|
|
|
@@ -26,8 +25,9 @@ module Gloo
|
|
|
26
25
|
@app_path = nil
|
|
27
26
|
@command_tokens = []
|
|
28
27
|
|
|
28
|
+
# Only the params given; the host process's ARGV is read by
|
|
29
|
+
# Gloo.run, when gloo is run as the command.
|
|
29
30
|
params.each { |o| process_one_arg( o ) }
|
|
30
|
-
ARGV.each { |o| process_one_arg( o ) }
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
#
|
|
@@ -117,9 +117,7 @@ module Gloo
|
|
|
117
117
|
# Then finally use the default: embedded mode.
|
|
118
118
|
#
|
|
119
119
|
def detect_mode
|
|
120
|
-
mode = if
|
|
121
|
-
Mode::TEST
|
|
122
|
-
elsif app?
|
|
120
|
+
mode = if app?
|
|
123
121
|
Mode::APP
|
|
124
122
|
elsif version?
|
|
125
123
|
Mode::VERSION
|
data/lib/gloo/app/log.rb
CHANGED
|
@@ -168,13 +168,13 @@ module Gloo
|
|
|
168
168
|
|
|
169
169
|
#
|
|
170
170
|
# Write a warning message to the log.
|
|
171
|
-
# Also write to the console unless quiet.
|
|
171
|
+
# Also write to the console (on stderr, like errors) unless quiet.
|
|
172
172
|
#
|
|
173
173
|
def warn( msg )
|
|
174
174
|
@warning_count += 1
|
|
175
175
|
@logger.warn msg
|
|
176
176
|
@error.warn msg
|
|
177
|
-
puts @theme.warn( msg ) if errors_to_console?
|
|
177
|
+
$stderr.puts @theme.warn( msg ) if errors_to_console?
|
|
178
178
|
end
|
|
179
179
|
|
|
180
180
|
#
|
data/lib/gloo/core/invoker.rb
CHANGED
|
@@ -18,26 +18,39 @@ module Gloo
|
|
|
18
18
|
#
|
|
19
19
|
# Resolve, validate and invoke the function at the given
|
|
20
20
|
# target path, with the given raw (unevaluated) arg tokens.
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
# unreliable result when that happens).
|
|
21
|
+
# For any failure, reports an error, puts false in `it` and
|
|
22
|
+
# returns nil - before the call (see failed), or inside the
|
|
23
|
+
# function itself (see Function#invoke).
|
|
25
24
|
#
|
|
26
25
|
def self.invoke( engine, target, arg_tokens )
|
|
27
26
|
if target.nil? || target.to_s.strip.empty?
|
|
28
27
|
engine.syntax_err NO_TARGET_ERR
|
|
29
|
-
return
|
|
28
|
+
return failed( engine )
|
|
30
29
|
end
|
|
31
30
|
|
|
32
31
|
func = resolve_function( engine, target )
|
|
33
|
-
return
|
|
32
|
+
return failed( engine ) unless func
|
|
34
33
|
|
|
34
|
+
# An arg that couldn't be worked out was already reported, but
|
|
35
|
+
# the function's own error tracking would clear it if the call
|
|
36
|
+
# went ahead.
|
|
37
|
+
errors_before = engine.heap.error.error_count
|
|
35
38
|
args = evaluate_arg_tokens( engine, arg_tokens )
|
|
36
|
-
return
|
|
39
|
+
return failed( engine ) if engine.heap.error.error_count > errors_before
|
|
40
|
+
return failed( engine ) unless params_count_ok?( engine, func, args )
|
|
37
41
|
|
|
38
42
|
return invoke_function( engine, func, args )
|
|
39
43
|
end
|
|
40
44
|
|
|
45
|
+
#
|
|
46
|
+
# The function couldn't be called (the problem was already
|
|
47
|
+
# reported): put false in `it`, and return nil.
|
|
48
|
+
#
|
|
49
|
+
def self.failed( engine )
|
|
50
|
+
engine.heap.it.set_to false
|
|
51
|
+
return nil
|
|
52
|
+
end
|
|
53
|
+
|
|
41
54
|
#
|
|
42
55
|
# Resolve the function object at the target path. Reports an
|
|
43
56
|
# error and returns nil if the path doesn't resolve to
|
data/lib/gloo/core/pn.rb
CHANGED
|
@@ -172,7 +172,12 @@ module Gloo
|
|
|
172
172
|
# returns nil without reporting an error. A caller that needs the
|
|
173
173
|
# object reports its own error.
|
|
174
174
|
#
|
|
175
|
+
# A here (^) or context (@) reference is expanded first, as resolve
|
|
176
|
+
# does, so a new object's parent can be given the same way as any
|
|
177
|
+
# other path (eg. create ^.x inside a script).
|
|
178
|
+
#
|
|
175
179
|
def get_parent
|
|
180
|
+
expand_refs
|
|
176
181
|
o = @engine.heap.root
|
|
177
182
|
|
|
178
183
|
if self.includes_path?
|
data/lib/gloo/core/verb.rb
CHANGED
|
@@ -94,15 +94,26 @@ module Gloo
|
|
|
94
94
|
def take_evaluated_arg
|
|
95
95
|
return true unless @params
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return false if @engine.heap.error.error_count > errors_before
|
|
97
|
+
value, ok = evaluate_expr( @params.tokens )
|
|
98
|
+
return false unless ok
|
|
100
99
|
|
|
101
100
|
@tokens = @tokens.with_token( value.to_s )
|
|
102
101
|
@params = nil
|
|
103
102
|
return true
|
|
104
103
|
end
|
|
105
104
|
|
|
105
|
+
#
|
|
106
|
+
# Work out an expression from the given tokens. Returns the value
|
|
107
|
+
# and whether it worked: false if the expression reported a
|
|
108
|
+
# problem (already reported), in which case the value is whatever
|
|
109
|
+
# it had worked out so far.
|
|
110
|
+
#
|
|
111
|
+
def evaluate_expr( tokens )
|
|
112
|
+
errors_before = @engine.heap.error.error_count
|
|
113
|
+
value = Gloo::Expr::Expression.new( @engine, tokens ).evaluate
|
|
114
|
+
return value, @engine.heap.error.error_count == errors_before
|
|
115
|
+
end
|
|
116
|
+
|
|
106
117
|
#
|
|
107
118
|
# Run the verb.
|
|
108
119
|
#
|
data/lib/gloo/exec/exec_env.rb
CHANGED
|
@@ -35,13 +35,16 @@ module Gloo
|
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
#
|
|
38
|
-
# Where the line now running is, for error messages: the
|
|
39
|
-
#
|
|
38
|
+
# Where the line now running is, for error messages: the file and
|
|
39
|
+
# line being loaded, or the running script and its line. While a
|
|
40
|
+
# file is being read, a problem is in that file, even when a
|
|
41
|
+
# script started the load.
|
|
40
42
|
#
|
|
41
43
|
def current_location
|
|
44
|
+
return @load_location if @load_location
|
|
42
45
|
return @running_script.location if @running_script
|
|
43
46
|
|
|
44
|
-
return
|
|
47
|
+
return nil
|
|
45
48
|
end
|
|
46
49
|
|
|
47
50
|
#
|
data/lib/gloo/exec/script.rb
CHANGED
|
@@ -115,6 +115,15 @@ module Gloo
|
|
|
115
115
|
def msg_run
|
|
116
116
|
s = Gloo::Exec::Script.new( @engine, self )
|
|
117
117
|
s.run
|
|
118
|
+
@broke_out = s.broke_out?
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
#
|
|
122
|
+
# Did a break end this script's last run? A loop uses this to
|
|
123
|
+
# stop when its do script breaks.
|
|
124
|
+
#
|
|
125
|
+
def broke_out?
|
|
126
|
+
return @broke_out == true
|
|
118
127
|
end
|
|
119
128
|
|
|
120
129
|
# ---------------------------------------------------------------------
|
data/lib/gloo/objs/ctrl/each.rb
CHANGED
|
@@ -43,13 +43,15 @@ module Gloo
|
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
#
|
|
46
|
-
# Run the do script once.
|
|
46
|
+
# Run the do script once. Returns true if a break in it ended the
|
|
47
|
+
# run, so the loop should stop.
|
|
47
48
|
#
|
|
48
49
|
def run_do
|
|
49
50
|
o = find_child DO
|
|
50
|
-
return unless o
|
|
51
|
+
return false unless o
|
|
51
52
|
|
|
52
53
|
Gloo::Exec::Dispatch.message( @engine, 'run', o )
|
|
54
|
+
return o.respond_to?( :broke_out? ) && o.broke_out?
|
|
53
55
|
end
|
|
54
56
|
|
|
55
57
|
|
|
@@ -132,7 +134,7 @@ module Gloo
|
|
|
132
134
|
'One of the following, whose presence determines which kind of loop this is:',
|
|
133
135
|
'child (alias) — iterate a container\'s children.',
|
|
134
136
|
'word (string) — iterate the words in a string.',
|
|
135
|
-
'line (string) — iterate the lines in a block of text.',
|
|
137
|
+
'line (string) — iterate the lines in a block of text. Each line comes without its line ending; a blank line is visited as an empty string.',
|
|
136
138
|
'file (file) — iterate the files in a folder. Optional children: ext (string) limits to files of that extension, ignoring case; recursive (bool) walks the subfolders too; include_dirs (bool) lists folders as well as files.',
|
|
137
139
|
'dir (file) — iterate the directories in a folder. Optional child: recursive (bool) walks the subfolders too.'
|
|
138
140
|
],
|
|
@@ -116,21 +116,25 @@ module Gloo
|
|
|
116
116
|
|
|
117
117
|
o = Gloo::Objs::Alias.resolve_alias( @engine, o )
|
|
118
118
|
o.children.each do |child|
|
|
119
|
+
new_group = false
|
|
119
120
|
if group_mode
|
|
120
121
|
group_value = group_by_value( child )
|
|
121
122
|
if last_group != group_value
|
|
122
123
|
last_group = group_value
|
|
124
|
+
new_group = true
|
|
125
|
+
# The ending group's end runs while child is still its last child.
|
|
123
126
|
if first_time
|
|
124
127
|
first_time = false
|
|
125
128
|
else
|
|
126
129
|
run_on_group_end
|
|
127
130
|
end
|
|
128
|
-
run_on_group_start
|
|
129
131
|
end
|
|
130
132
|
end
|
|
131
133
|
|
|
132
134
|
set_child child
|
|
133
|
-
|
|
135
|
+
# The new group's start runs once child is its first child.
|
|
136
|
+
run_on_group_start if new_group
|
|
137
|
+
break if @iterator_obj.run_do
|
|
134
138
|
end
|
|
135
139
|
|
|
136
140
|
if group_mode && !first_time
|
|
@@ -40,15 +40,16 @@ module Gloo
|
|
|
40
40
|
# ---------------------------------------------------------------------
|
|
41
41
|
|
|
42
42
|
#
|
|
43
|
-
# Run for each line.
|
|
43
|
+
# Run for each line, without its line ending (\n or \r\n). A blank
|
|
44
|
+
# line is still visited, as an empty string.
|
|
44
45
|
#
|
|
45
46
|
def run
|
|
46
47
|
str = @iterator_obj.in_value
|
|
47
48
|
return unless str
|
|
48
49
|
|
|
49
|
-
str.each_line do |line|
|
|
50
|
+
str.each_line( chomp: true ) do |line|
|
|
50
51
|
set_line line
|
|
51
|
-
@iterator_obj.run_do
|
|
52
|
+
break if @iterator_obj.run_do
|
|
52
53
|
end
|
|
53
54
|
end
|
|
54
55
|
|
|
@@ -37,13 +37,15 @@ module Gloo
|
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
#
|
|
40
|
-
# Run the do script once.
|
|
40
|
+
# Run the do script once. Returns true if a break in it ended the
|
|
41
|
+
# run, so the loop should stop.
|
|
41
42
|
#
|
|
42
43
|
def run_do
|
|
43
44
|
o = find_child DO
|
|
44
|
-
return unless o
|
|
45
|
+
return false unless o
|
|
45
46
|
|
|
46
47
|
Gloo::Exec::Dispatch.message( @engine, 'run', o )
|
|
48
|
+
return o.respond_to?( :broke_out? ) && o.broke_out?
|
|
47
49
|
end
|
|
48
50
|
|
|
49
51
|
#
|
|
@@ -98,7 +100,7 @@ module Gloo
|
|
|
98
100
|
def msg_run
|
|
99
101
|
times.times do |index|
|
|
100
102
|
set_index index
|
|
101
|
-
run_do
|
|
103
|
+
break if run_do
|
|
102
104
|
end
|
|
103
105
|
end
|
|
104
106
|
|
|
@@ -209,7 +209,8 @@ module Gloo
|
|
|
209
209
|
{
|
|
210
210
|
:name => KEYWORD,
|
|
211
211
|
:shortcut => KEYWORD_SHORT,
|
|
212
|
-
:description => 'Tool to encrypt and decrypt data
|
|
212
|
+
:description => 'Tool to encrypt and decrypt data, with ' \
|
|
213
|
+
'AES-256-CBC.',
|
|
213
214
|
:children => [
|
|
214
215
|
'key (string) — The encryption key. Base64 encoded.',
|
|
215
216
|
'init_vector (string) — The initialization vector. Base64 encoded.',
|
|
@@ -218,8 +219,15 @@ module Gloo
|
|
|
218
219
|
:messages => [
|
|
219
220
|
'generate_keys — Generate an encryption key and initialization vector. The keys are base64 encoded.',
|
|
220
221
|
'encrypt — Encrypt the data. The data child is changed. It will have the encrypted data.',
|
|
221
|
-
'decrypt — Decrypt the data. The data child is changed. It will have the decrypted data.'
|
|
222
|
+
'decrypt — Decrypt the data. The data child is changed. It will have the decrypted data. With the wrong key or init_vector, decrypt usually reports an error and it is false; but not always (see notes).'
|
|
222
223
|
],
|
|
224
|
+
:notes => 'AES-CBC can\'t always tell that a key is wrong. A ' \
|
|
225
|
+
'wrong key usually leaves the decrypted data with padding ' \
|
|
226
|
+
'that doesn\'t check out, which is reported as an error. But ' \
|
|
227
|
+
'about 1 time in 256 the padding happens to look right: then ' \
|
|
228
|
+
'decrypt reports nothing, and the data is garbage rather than ' \
|
|
229
|
+
'the original. To be sure the key is right, keep a value you ' \
|
|
230
|
+
'can check, such as encrypting a known word alongside the data.',
|
|
223
231
|
:examples => <<~EXAMPLES.strip
|
|
224
232
|
#
|
|
225
233
|
# Encrypt and Decrypt a string.
|
|
@@ -14,11 +14,10 @@ module Gloo
|
|
|
14
14
|
attr_reader :roots
|
|
15
15
|
|
|
16
16
|
#
|
|
17
|
-
# Set up a ledger for
|
|
17
|
+
# Set up a ledger for one file's load.
|
|
18
18
|
#
|
|
19
|
-
def initialize( engine
|
|
19
|
+
def initialize( engine )
|
|
20
20
|
@engine = engine
|
|
21
|
-
@pn = pn
|
|
22
21
|
@roots = []
|
|
23
22
|
@mine = {}.compare_by_identity
|
|
24
23
|
end
|
|
@@ -43,15 +42,16 @@ module Gloo
|
|
|
43
42
|
# `value`. If another file created it and the values differ, the
|
|
44
43
|
# new value is being ignored -- warn so it isn't a surprise. A
|
|
45
44
|
# duplicate name inside one file is left alone (a documented
|
|
46
|
-
# "first one wins").
|
|
45
|
+
# "first one wins"). The warning's location (this file and line)
|
|
46
|
+
# comes from the load.
|
|
47
47
|
#
|
|
48
48
|
def clash( prior, name, value )
|
|
49
49
|
return if @mine.key?( prior )
|
|
50
50
|
return if value.nil? || value.to_s.strip.empty?
|
|
51
51
|
return if prior.value.to_s == value.to_s
|
|
52
52
|
|
|
53
|
-
@engine.
|
|
54
|
-
"
|
|
53
|
+
@engine.warn(
|
|
54
|
+
"'#{name}' is already declared in another loaded file with a " \
|
|
55
55
|
"different value (#{prior.value.inspect} kept, #{value.inspect} ignored)."
|
|
56
56
|
)
|
|
57
57
|
end
|