flor 0.0.1 → 0.9.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.
- data/CHANGELOG.md +13 -0
- data/LICENSE.txt +1 -1
- data/Makefile +66 -0
- data/README.md +57 -0
- data/fail.txt +7 -0
- data/flor.gemspec +12 -9
- data/intercepted.txt +123 -0
- data/lib/flor/colours.rb +140 -0
- data/lib/flor/conf.rb +88 -0
- data/lib/flor/core/executor.rb +473 -0
- data/lib/flor/core/node.rb +397 -0
- data/lib/flor/core/procedure.rb +600 -0
- data/lib/flor/core/texecutor.rb +209 -0
- data/lib/flor/core.rb +93 -0
- data/lib/flor/dollar.rb +248 -0
- data/lib/flor/errors.rb +36 -0
- data/lib/flor/flor.rb +556 -0
- data/lib/flor/log.rb +336 -0
- data/lib/flor/migrations/0001_tables.rb +122 -0
- data/lib/flor/parser.rb +414 -0
- data/lib/flor/pcore/_arr.rb +49 -0
- data/lib/flor/pcore/_atom.rb +43 -0
- data/lib/flor/pcore/_att.rb +160 -0
- data/lib/flor/pcore/_dump.rb +60 -0
- data/lib/flor/pcore/_err.rb +30 -0
- data/lib/flor/pcore/_happly.rb +73 -0
- data/lib/flor/pcore/_obj.rb +65 -0
- data/lib/flor/pcore/_skip.rb +63 -0
- data/lib/flor/pcore/apply.rb +60 -0
- data/lib/flor/pcore/arith.rb +46 -0
- data/lib/flor/pcore/break.rb +71 -0
- data/lib/flor/pcore/cmp.rb +72 -0
- data/lib/flor/pcore/cond.rb +57 -0
- data/lib/flor/pcore/cursor.rb +223 -0
- data/lib/flor/pcore/define.rb +96 -0
- data/lib/flor/pcore/fail.rb +45 -0
- data/lib/flor/pcore/ife.rb +56 -0
- data/lib/flor/pcore/loop.rb +53 -0
- data/lib/flor/pcore/map.rb +75 -0
- data/lib/flor/pcore/match.rb +70 -0
- data/lib/flor/pcore/move.rb +65 -0
- data/lib/flor/pcore/noeval.rb +46 -0
- data/lib/flor/pcore/noret.rb +47 -0
- data/lib/flor/pcore/push.rb +69 -0
- data/lib/flor/pcore/sequence.rb +39 -0
- data/lib/flor/pcore/set.rb +76 -0
- data/lib/flor/pcore/stall.rb +35 -0
- data/lib/flor/pcore/until.rb +122 -0
- data/lib/flor/pcore/val.rb +40 -0
- data/lib/flor/punit/cancel.rb +69 -0
- data/lib/flor/punit/cmap.rb +76 -0
- data/lib/flor/punit/concurrence.rb +149 -0
- data/lib/flor/punit/every.rb +46 -0
- data/lib/flor/punit/on.rb +81 -0
- data/lib/flor/punit/schedule.rb +68 -0
- data/lib/flor/punit/signal.rb +47 -0
- data/lib/flor/punit/sleep.rb +53 -0
- data/lib/flor/punit/task.rb +109 -0
- data/lib/flor/punit/trace.rb +51 -0
- data/lib/flor/punit/trap.rb +100 -0
- data/lib/flor/to_string.rb +81 -0
- data/lib/flor/tools/env.rb +103 -0
- data/lib/flor/tools/repl.rb +231 -0
- data/lib/flor/unit/executor.rb +260 -0
- data/lib/flor/unit/hooker.rb +186 -0
- data/lib/flor/unit/journal.rb +52 -0
- data/lib/flor/unit/loader.rb +181 -0
- data/lib/flor/unit/logger.rb +181 -0
- data/lib/flor/unit/models/execution.rb +105 -0
- data/lib/flor/unit/models/pointer.rb +31 -0
- data/lib/flor/unit/models/timer.rb +52 -0
- data/lib/flor/unit/models/trace.rb +31 -0
- data/lib/flor/unit/models/trap.rb +130 -0
- data/lib/flor/unit/models.rb +106 -0
- data/lib/flor/unit/scheduler.rb +419 -0
- data/lib/flor/unit/storage.rb +633 -0
- data/lib/flor/unit/tasker.rb +191 -0
- data/lib/flor/unit/waiter.rb +146 -0
- data/lib/flor/unit/wlist.rb +77 -0
- data/lib/flor/unit.rb +50 -0
- data/lib/flor.rb +40 -3
- metadata +152 -22
- checksums.yaml +0 -7
- data/Rakefile +0 -52
@@ -0,0 +1,209 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2015-2017, John Mettraux, jmettraux+flor@gmail.com
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
#
|
22
|
+
# Made in Japan.
|
23
|
+
#++
|
24
|
+
|
25
|
+
|
26
|
+
module Flor
|
27
|
+
|
28
|
+
class TransientExecutor < Executor
|
29
|
+
|
30
|
+
class TransientTasker
|
31
|
+
|
32
|
+
def has_tasker?(exid, tname); false; end
|
33
|
+
end
|
34
|
+
|
35
|
+
class TransientUnit
|
36
|
+
|
37
|
+
attr_accessor :conf, :opts
|
38
|
+
attr_reader :journal, :tasker, :loader
|
39
|
+
attr_accessor :archive
|
40
|
+
|
41
|
+
def initialize(conf)
|
42
|
+
|
43
|
+
@conf = conf
|
44
|
+
@opts = {}
|
45
|
+
@journal = []
|
46
|
+
@tasker = TransientTasker.new
|
47
|
+
@archive = nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def notify(executor, o)
|
51
|
+
|
52
|
+
return [] if o['consumed']
|
53
|
+
|
54
|
+
Flor.log_message(executor, o) \
|
55
|
+
if @conf['log_msg']
|
56
|
+
|
57
|
+
@journal << o
|
58
|
+
|
59
|
+
[]
|
60
|
+
end
|
61
|
+
|
62
|
+
def remove_node(exid, n)
|
63
|
+
|
64
|
+
(@archive[exid] ||= {})[n['nid']] = Flor.dup(n) if @archive
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def initialize(conf={})
|
69
|
+
|
70
|
+
conf.merge!(Flor::Conf.read_env) unless conf['conf'] == true
|
71
|
+
# don't read FLOR_DEBUG if this executor is only meant to read the conf
|
72
|
+
|
73
|
+
super(
|
74
|
+
TransientUnit.new(conf),
|
75
|
+
[], # no traps
|
76
|
+
{
|
77
|
+
'exid' => Flor.generate_exid('eval', 'u0'),
|
78
|
+
'nodes' => {}, 'errors' => [], 'counters' => {},
|
79
|
+
#'ashes' => {},
|
80
|
+
'start' => Flor.tstamp
|
81
|
+
})
|
82
|
+
end
|
83
|
+
|
84
|
+
def journal; @unit.journal; end
|
85
|
+
def archive; @unit.archive[exid]; end
|
86
|
+
|
87
|
+
def launch(tree, opts={})
|
88
|
+
|
89
|
+
@unit.opts = opts
|
90
|
+
@unit.archive = {} if opts[:archive]
|
91
|
+
|
92
|
+
Flor.print_src(tree, opts) if conf['log_src']
|
93
|
+
|
94
|
+
messages = [ Flor.make_launch_msg(@execution['exid'], tree, opts) ]
|
95
|
+
|
96
|
+
Flor.print_tree(messages.first['tree']) if conf['log_tree']
|
97
|
+
|
98
|
+
walk(messages, opts)
|
99
|
+
end
|
100
|
+
|
101
|
+
def walk(messages, opts={})
|
102
|
+
|
103
|
+
loop do
|
104
|
+
|
105
|
+
message = messages.shift
|
106
|
+
return nil unless message
|
107
|
+
|
108
|
+
if message['point'] == 'terminated' && messages.any?
|
109
|
+
#
|
110
|
+
# try to handle 'terminated' last
|
111
|
+
#
|
112
|
+
messages << message
|
113
|
+
message = messages.shift
|
114
|
+
end
|
115
|
+
|
116
|
+
msgs = process(message)
|
117
|
+
|
118
|
+
messages.concat(msgs)
|
119
|
+
|
120
|
+
return messages if message_match?(message, opts[:until_after])
|
121
|
+
return messages if message_match?(messages, opts[:until])
|
122
|
+
|
123
|
+
return message \
|
124
|
+
if message['point'] == 'terminated'
|
125
|
+
return message \
|
126
|
+
if message['point'] == 'failed' && message['on_error'] == nil
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def step(message)
|
131
|
+
|
132
|
+
process(message)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Used in specs when testing multiple message arrival order on
|
136
|
+
# a "suite" of transient executors
|
137
|
+
#
|
138
|
+
def clone
|
139
|
+
|
140
|
+
c = TransientExecutor.allocate
|
141
|
+
|
142
|
+
c.instance_variable_set(:@unit, @unit)
|
143
|
+
c.instance_variable_set(:@traps, []) # not useful for a TransientEx clone
|
144
|
+
c.instance_variable_set(:@execution, Flor.dup(@execution))
|
145
|
+
|
146
|
+
c
|
147
|
+
end
|
148
|
+
|
149
|
+
protected
|
150
|
+
|
151
|
+
# TODO eventually merge with Waiter.parse_serie
|
152
|
+
#
|
153
|
+
def message_match?(msg_s, ountil)
|
154
|
+
|
155
|
+
return false unless ountil
|
156
|
+
|
157
|
+
ms = msg_s; ms = [ ms ] if ms.is_a?(Hash)
|
158
|
+
|
159
|
+
nid, point = ountil.split(' ')
|
160
|
+
|
161
|
+
ms.find { |m| m['nid'] == nid && m['point'] == point }
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
class ConfExecutor < TransientExecutor
|
166
|
+
|
167
|
+
def self.interpret(path)
|
168
|
+
|
169
|
+
s = path
|
170
|
+
|
171
|
+
s = File.read(s).strip unless s.match(/[\r\n]/)
|
172
|
+
s = "{\n#{s}\n}"
|
173
|
+
|
174
|
+
vs = Hash.new { |h, k| k }
|
175
|
+
class << vs
|
176
|
+
def has_key?(k); ! Flor::Procedure[k]; end
|
177
|
+
end
|
178
|
+
|
179
|
+
vs['root'] = determine_root(path)
|
180
|
+
|
181
|
+
vs['ruby_version'] = RUBY_VERSION
|
182
|
+
vs['ruby_platform'] = RUBY_PLATFORM
|
183
|
+
|
184
|
+
c = (ENV['FLOR_DEBUG'] || '').match(/conf/) ? false : true
|
185
|
+
r = (self.new('conf' => c)).launch(s, vars: vs)
|
186
|
+
|
187
|
+
unless r['point'] == 'terminated'
|
188
|
+
ae = ArgumentError.new("error while reading conf: #{r['error']['msg']}")
|
189
|
+
ae.set_backtrace(r['error']['trc'])
|
190
|
+
fail ae
|
191
|
+
end
|
192
|
+
|
193
|
+
h = Flor.dup(r['payload']['ret'])
|
194
|
+
|
195
|
+
h.merge!('_path' => path) unless path.match(/[\r\n]/)
|
196
|
+
|
197
|
+
h
|
198
|
+
end
|
199
|
+
|
200
|
+
def self.determine_root(path)
|
201
|
+
|
202
|
+
dir = File.absolute_path(File.dirname(path))
|
203
|
+
ps = dir.split(File::SEPARATOR)
|
204
|
+
|
205
|
+
ps.last == 'etc' ? File.absolute_path(File.join(dir, '..')) : dir
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
data/lib/flor/core.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2015-2017, John Mettraux, jmettraux+flor@gmail.com
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
#
|
22
|
+
# Made in Japan.
|
23
|
+
#++
|
24
|
+
|
25
|
+
module Flor
|
26
|
+
|
27
|
+
def self.generate_exid(domain, unit)
|
28
|
+
|
29
|
+
@exid_counter ||= 0
|
30
|
+
@exid_mutex ||= Mutex.new
|
31
|
+
|
32
|
+
t = Time.now.utc
|
33
|
+
|
34
|
+
sus =
|
35
|
+
@exid_mutex.synchronize do
|
36
|
+
|
37
|
+
sus = t.sec * 100000000 + t.usec * 100 + @exid_counter
|
38
|
+
|
39
|
+
@exid_counter = @exid_counter + 1
|
40
|
+
@exid_counter = 0 if @exid_counter > 99
|
41
|
+
|
42
|
+
Munemo.to_s(sus)
|
43
|
+
end
|
44
|
+
|
45
|
+
t = t.strftime('%Y%m%d.%H%M')
|
46
|
+
|
47
|
+
"#{domain}-#{unit}-#{t}.#{sus}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.make_launch_msg(exid, tree, opts)
|
51
|
+
|
52
|
+
t =
|
53
|
+
tree.is_a?(String) ?
|
54
|
+
Flor::Lang.parse(tree, opts[:fname], opts) :
|
55
|
+
tree
|
56
|
+
|
57
|
+
unless t
|
58
|
+
#h = opts.merge(prune: false, rewrite: false)
|
59
|
+
#p Flor::Lang.parse(tree, h[:fname], h)
|
60
|
+
# TODO re-parse and indicate what went wrong...
|
61
|
+
fail ArgumentError.new('flor parse failure')
|
62
|
+
end
|
63
|
+
|
64
|
+
pl = opts[:payload] || opts[:fields] || {}
|
65
|
+
vs = opts[:variables] || opts[:vars] || {}
|
66
|
+
|
67
|
+
msg =
|
68
|
+
{ 'point' => 'execute',
|
69
|
+
'exid' => exid,
|
70
|
+
'nid' => '0',
|
71
|
+
'tree' => t,
|
72
|
+
'payload' => pl,
|
73
|
+
'vars' => vs }
|
74
|
+
|
75
|
+
msg['vdomain'] = opts[:vdomain] \
|
76
|
+
if opts.has_key?(:vdomain)
|
77
|
+
|
78
|
+
msg
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.load_procedures(dir)
|
82
|
+
|
83
|
+
dirpath =
|
84
|
+
if dir.match(/\A[.\/]/)
|
85
|
+
File.join(dir, '*.rb')
|
86
|
+
else
|
87
|
+
File.join(File.dirname(__FILE__), dir, '*.rb')
|
88
|
+
end
|
89
|
+
|
90
|
+
Dir[dirpath].each { |path| require(path) }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
data/lib/flor/dollar.rb
ADDED
@@ -0,0 +1,248 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2015-2017, John Mettraux, jmettraux+flor@gmail.com
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
#
|
22
|
+
# Made in Japan.
|
23
|
+
#++
|
24
|
+
|
25
|
+
|
26
|
+
module Flor
|
27
|
+
|
28
|
+
class Dollar
|
29
|
+
|
30
|
+
module Parser include Raabro
|
31
|
+
|
32
|
+
#static fabr_tree *_str(fabr_input *i)
|
33
|
+
#{
|
34
|
+
# return fabr_rex("s", i,
|
35
|
+
# "("
|
36
|
+
# "\\\\\\)" "|"
|
37
|
+
# "[^\\$\\)]" "|"
|
38
|
+
# "\\$[^\\(]"
|
39
|
+
# ")+");
|
40
|
+
#}
|
41
|
+
def istr(i)
|
42
|
+
rex(:str, i, %r{
|
43
|
+
( \\\) | [^\$)] | \$[^(] )+
|
44
|
+
}x)
|
45
|
+
end
|
46
|
+
|
47
|
+
#static fabr_tree *_outerstr(fabr_input *i)
|
48
|
+
#{
|
49
|
+
# return fabr_rex("s", i,
|
50
|
+
# "("
|
51
|
+
# "[^\\$]" "|" // doesn't mind ")"
|
52
|
+
# "\\$[^\\(]"
|
53
|
+
# ")+");
|
54
|
+
#}
|
55
|
+
def ostr(i)
|
56
|
+
rex(:str, i, %r{
|
57
|
+
( [^\$] | \$(?!\() )+
|
58
|
+
}x)
|
59
|
+
end
|
60
|
+
#
|
61
|
+
# ( [^\$] | \$(?!\() )+
|
62
|
+
# one or more of (not a dollar or a dollar followed by sthing else
|
63
|
+
# than a parenthesis opening)
|
64
|
+
|
65
|
+
def pe(i); str(nil, i, ')'); end
|
66
|
+
def dois(i); alt(nil, i, :dollar, :istr); end
|
67
|
+
def span(i); rep(:span, i, :dois, 0); end
|
68
|
+
def dps(i); str(nil, i, '$('); end
|
69
|
+
def dollar(i); seq(:dollar, i, :dps, :span, :pe); end
|
70
|
+
def doos(i); alt(nil, i, :dollar, :ostr); end
|
71
|
+
def outer(i); rep(:span, i, :doos, 0); end
|
72
|
+
|
73
|
+
def rewrite_str(t)
|
74
|
+
t.string
|
75
|
+
end
|
76
|
+
def rewrite_dollar(t)
|
77
|
+
cn = rewrite(t.children[1])
|
78
|
+
c = cn.first
|
79
|
+
if cn.size == 1 && c.is_a?(String)
|
80
|
+
[ :dol, c ]
|
81
|
+
else
|
82
|
+
[ :dol, cn ]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
def rewrite_span(t)
|
86
|
+
t.children.collect { |c| rewrite(c) }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
module PipeParser include Raabro
|
91
|
+
|
92
|
+
def elt(i); rex(:elt, i, /[^|]+/); end
|
93
|
+
def pipe(i); rex(:pipe, i, /\|\|?/); end
|
94
|
+
def elts(i); jseq(:elts, i, :elt, :pipe); end
|
95
|
+
|
96
|
+
def rewrite_elt(t); t.string; end
|
97
|
+
def rewrite_pipe(t); t.string == '|' ? :pipe : :dpipe; end
|
98
|
+
def rewrite_elts(t); t.children.collect { |e| rewrite(e) }; end
|
99
|
+
end
|
100
|
+
|
101
|
+
#def lookup(s)
|
102
|
+
# # ...
|
103
|
+
#end
|
104
|
+
#
|
105
|
+
# the signature
|
106
|
+
|
107
|
+
# Called when joining multiple results in a string. Easily overwritable.
|
108
|
+
#
|
109
|
+
def stringify(v)
|
110
|
+
|
111
|
+
case v
|
112
|
+
when Array, Hash then JSON.dump(v)
|
113
|
+
else v.to_s
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def quote(s, force)
|
118
|
+
|
119
|
+
return s if force == false && s[0, 1] == '"' && s[-1, 1] == '"'
|
120
|
+
|
121
|
+
JSON.dump([ s ])[1..-2]
|
122
|
+
end
|
123
|
+
|
124
|
+
def match(rex, s)
|
125
|
+
|
126
|
+
s.match(rex) ? s : false
|
127
|
+
end
|
128
|
+
|
129
|
+
def substitute(pat, rpl, gix, s)
|
130
|
+
|
131
|
+
ops =
|
132
|
+
(gix.index('i') ? Regexp::IGNORECASE : 0) |
|
133
|
+
(gix.index('x') ? Regexp::EXTENDED : 0)
|
134
|
+
|
135
|
+
rex = Regexp.new(pat, ops)
|
136
|
+
|
137
|
+
gix.index('g') ? s.gsub(rex, rpl) : s.sub(rex, rpl)
|
138
|
+
end
|
139
|
+
|
140
|
+
def lfilter(s, cmp, len)
|
141
|
+
|
142
|
+
l = s.length
|
143
|
+
|
144
|
+
case cmp
|
145
|
+
when '>' then l > len
|
146
|
+
when '>=' then l >= len
|
147
|
+
when '<' then l < len
|
148
|
+
when '<=' then l <= len
|
149
|
+
when '=', '==' then l == len
|
150
|
+
when '!=', '<>' then l != len
|
151
|
+
else false
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def to_json(o)
|
156
|
+
|
157
|
+
case o
|
158
|
+
when Array, Hash then JSON.dump(o)
|
159
|
+
else JSON.dump([ o ])[1..-2]
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def call(fun, o)
|
164
|
+
|
165
|
+
# NB: yes, $1..$9 are thread-safe (and local, not global)
|
166
|
+
|
167
|
+
case fun
|
168
|
+
|
169
|
+
when 'u' then o.to_s.upcase
|
170
|
+
when 'd' then o.to_s.downcase
|
171
|
+
when 'r' then o.reverse
|
172
|
+
when 'c' then o.to_s.capitalize.gsub(/\s[a-z]/) { |c| c.upcase }
|
173
|
+
when 'q' then quote(o, false)
|
174
|
+
when 'Q' then quote(o, true)
|
175
|
+
|
176
|
+
when 'json' then to_json(o)
|
177
|
+
|
178
|
+
when /^j(.+)/
|
179
|
+
o.respond_to?(:join) ? o.join($1) : o
|
180
|
+
|
181
|
+
when /\Am\/(.+)\/\z/
|
182
|
+
match($1, o.to_s)
|
183
|
+
when /\As\/(.*[^\\]\/)(.+)\/([gix]*)\z/
|
184
|
+
substitute($1.chop, $2, $3, o.to_s)
|
185
|
+
|
186
|
+
when /\A-?\d+\z/ then o.to_s[fun.to_i]
|
187
|
+
when /\A(-?\d+), *(-?\d+)\z/ then o.to_s[$1.to_i, $2.to_i]
|
188
|
+
when /\A(-?\d+)\.\.(-?\d+)\z/ then o.to_s[$1.to_i..$2.to_i]
|
189
|
+
|
190
|
+
when /\A\s*l\s*([><=!]=?|<>)\s*(\d+)\z/
|
191
|
+
lfilter(o.to_s, $1, $2.to_i) ? o.to_s : nil
|
192
|
+
|
193
|
+
else o
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def unescape(s)
|
198
|
+
|
199
|
+
s.gsub(/\\[\$)]/) { |m| m[1, 1] }
|
200
|
+
end
|
201
|
+
|
202
|
+
def do_eval(t)
|
203
|
+
|
204
|
+
#return t if t.is_a?(String)
|
205
|
+
return unescape(t) if t.is_a?(String)
|
206
|
+
|
207
|
+
return t.collect { |c| stringify(do_eval(c)) }.join if t[0] != :dol
|
208
|
+
|
209
|
+
k = do_eval(t[1])
|
210
|
+
ks = PipeParser.parse(k)
|
211
|
+
|
212
|
+
result = nil
|
213
|
+
mode = :lookup # vs :call
|
214
|
+
|
215
|
+
ks.each do |k|
|
216
|
+
|
217
|
+
if k == :pipe then mode = :call; next; end
|
218
|
+
if k == :dpipe && result then break; end
|
219
|
+
if k == :dpipe then mode = :lookup; next; end
|
220
|
+
|
221
|
+
result =
|
222
|
+
if mode == :lookup
|
223
|
+
#k[0, 1] == "'" ? k[1..-1] : do_lookup(k)
|
224
|
+
k[0, 1] == "'" ? k[1..-1] : lookup(k)
|
225
|
+
else # :call
|
226
|
+
call(k, result)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
result
|
231
|
+
end
|
232
|
+
|
233
|
+
def expand(s)
|
234
|
+
|
235
|
+
return s unless s.index('$')
|
236
|
+
|
237
|
+
#Raabro.pp(Parser.parse(s, debug: 2))
|
238
|
+
t = Parser.parse(s)
|
239
|
+
|
240
|
+
return s unless t
|
241
|
+
|
242
|
+
t = t.first if t.size == 1
|
243
|
+
|
244
|
+
do_eval(t)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
data/lib/flor/errors.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2015-2017, John Mettraux, jmettraux+flor@gmail.com
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
#
|
22
|
+
# Made in Japan.
|
23
|
+
#++
|
24
|
+
|
25
|
+
|
26
|
+
class Flor::FlorError < StandardError
|
27
|
+
|
28
|
+
attr_reader :node
|
29
|
+
|
30
|
+
def initialize(message, node=nil)
|
31
|
+
|
32
|
+
super(message)
|
33
|
+
@node = node
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|