flor 0.0.1 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|