jsonnet 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.clang-format +11 -0
- data/ext/jsonnet/callbacks.c +241 -0
- data/ext/jsonnet/helpers.c +74 -0
- data/ext/jsonnet/jsonnet.c +4 -348
- data/ext/jsonnet/jsonnet_values.c +206 -0
- data/ext/jsonnet/ruby_jsonnet.h +39 -0
- data/ext/jsonnet/vm.c +351 -0
- data/lib/jsonnet/version.rb +1 -1
- data/lib/jsonnet/vm.rb +36 -1
- data/test/fixtures/jpath.libsonnet +3 -0
- data/test/test_vm.rb +218 -1
- metadata +10 -2
data/lib/jsonnet/version.rb
CHANGED
data/lib/jsonnet/vm.rb
CHANGED
@@ -95,10 +95,45 @@ module Jsonnet
|
|
95
95
|
# @yieldreturn [Array<String>] a pair of the content of the imported file and
|
96
96
|
# its path.
|
97
97
|
def handle_import
|
98
|
-
self.import_callback = Proc.new
|
98
|
+
self.import_callback = to_method(Proc.new)
|
99
99
|
nil
|
100
100
|
end
|
101
101
|
|
102
|
+
##
|
103
|
+
# Define a function (native extension) in the VM and let the given block
|
104
|
+
# handle the invocation of the function.
|
105
|
+
#
|
106
|
+
# @param name [Symbol|String] name of the function.
|
107
|
+
# Must be a valid identifier in Jsonnet.
|
108
|
+
# @param body [#to_proc] body of the function.
|
109
|
+
# @yield calls the given block instead of `body` if `body` is `nil`
|
110
|
+
#
|
111
|
+
# @note Currently it cannot define keyword or optional paramters in Jsonnet.
|
112
|
+
# Also all the positional optional parameters of the body are interpreted
|
113
|
+
# as required parameters. And the body cannot have keyword, rest or
|
114
|
+
# keyword rest paramters.
|
115
|
+
def define_function(name, body = nil)
|
116
|
+
body = body ? body.to_proc : Proc.new
|
117
|
+
params = body.parameters.map.with_index do |(type, name), i|
|
118
|
+
raise ArgumentError, "rest or keyword parameters are not allowed: #{type}" \
|
119
|
+
unless [:req, :opt].include? type
|
120
|
+
|
121
|
+
name || "p#{i}"
|
122
|
+
end
|
123
|
+
|
124
|
+
register_native_callback(name.to_sym, to_method(body), params);
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
# Wraps the function body with a method so that `break` and `return`
|
129
|
+
# behave like `return` as they do in a body of Module#define_method.
|
130
|
+
def to_method(body)
|
131
|
+
mod = Module.new {
|
132
|
+
define_method(:dummy, body)
|
133
|
+
}
|
134
|
+
mod.instance_method(:dummy).bind(body.binding.receiver)
|
135
|
+
end
|
136
|
+
|
102
137
|
class UnsupportedOptionError < RuntimeError; end
|
103
138
|
end
|
104
139
|
end
|
data/test/test_vm.rb
CHANGED
@@ -110,13 +110,56 @@ class TestVM < Test::Unit::TestCase
|
|
110
110
|
end
|
111
111
|
end
|
112
112
|
|
113
|
-
test "Jsonnet::VM#ext_var binds a variable" do
|
113
|
+
test "Jsonnet::VM#ext_var binds a variable to a string value" do
|
114
114
|
vm = Jsonnet::VM.new
|
115
115
|
vm.ext_var("var1", "foo")
|
116
116
|
result = vm.evaluate('[std.extVar("var1")]')
|
117
117
|
assert_equal JSON.parse('["foo"]'), JSON.parse(result)
|
118
118
|
end
|
119
119
|
|
120
|
+
test "Jsonnet::VM#ext_code binds a variable to a code fragment" do
|
121
|
+
vm = Jsonnet::VM.new
|
122
|
+
vm.ext_code("var1", "{a:1}")
|
123
|
+
result = vm.evaluate('[std.extVar("var1")]')
|
124
|
+
assert_equal JSON.parse(<<-EOS), JSON.parse(result)
|
125
|
+
[
|
126
|
+
{
|
127
|
+
"a": 1
|
128
|
+
}
|
129
|
+
]
|
130
|
+
EOS
|
131
|
+
end
|
132
|
+
|
133
|
+
test "Jsonnet::VM#tla_var binds a top-level variable to a string value" do
|
134
|
+
vm = Jsonnet::VM.new
|
135
|
+
vm.tla_var("var1", "foo")
|
136
|
+
result = vm.evaluate('function(var1) [var1, var1]')
|
137
|
+
assert_equal JSON.parse('["foo", "foo"]'), JSON.parse(result)
|
138
|
+
end
|
139
|
+
|
140
|
+
test "Jsonnet::VM#tla_var binds a top-level argument to a string value" do
|
141
|
+
vm = Jsonnet::VM.new
|
142
|
+
vm.tla_var("var1", "foo")
|
143
|
+
result = vm.evaluate('function(var1) [var1, var1]')
|
144
|
+
assert_equal JSON.parse('["foo", "foo"]'), JSON.parse(result)
|
145
|
+
end
|
146
|
+
|
147
|
+
test "Jsonnet::VM#tla_code binds a top-level argument to a code fragment" do
|
148
|
+
vm = Jsonnet::VM.new
|
149
|
+
vm.tla_code("var1", "{a:1}")
|
150
|
+
result = vm.evaluate('function(var1) [var1, var1]')
|
151
|
+
assert_equal JSON.parse(<<-EOS), JSON.parse(result)
|
152
|
+
[
|
153
|
+
{
|
154
|
+
"a": 1
|
155
|
+
},
|
156
|
+
{
|
157
|
+
"a": 1
|
158
|
+
}
|
159
|
+
]
|
160
|
+
EOS
|
161
|
+
end
|
162
|
+
|
120
163
|
test 'Jsonnet::VM#evaluate returns a JSON per filename on multi mode' do
|
121
164
|
vm = Jsonnet::VM.new
|
122
165
|
[
|
@@ -257,6 +300,180 @@ class TestVM < Test::Unit::TestCase
|
|
257
300
|
assert_true called
|
258
301
|
end
|
259
302
|
|
303
|
+
test "Jsonnet::VM#handle_import treats global escapes as define_method does" do
|
304
|
+
num_eval = 0
|
305
|
+
begin
|
306
|
+
bodies = [
|
307
|
+
proc {|rel, base| return 'null', '/x.libsonnet' },
|
308
|
+
lambda {|rel, base| return 'null', '/x.libsonnet' },
|
309
|
+
proc {|rel, base| next 'null', '/x.libsonnet' },
|
310
|
+
lambda {|rel, base| next 'null', '/x.libsonnet' },
|
311
|
+
proc {|rel, base| break 'null', '/x.libsonnet' },
|
312
|
+
lambda {|rel, base| break 'null', '/x.libsonnet' },
|
313
|
+
]
|
314
|
+
bodies.each do |prc|
|
315
|
+
vm = Jsonnet::VM.new
|
316
|
+
vm.handle_import(&prc)
|
317
|
+
|
318
|
+
result = vm.evaluate('import "a.jsonnet"')
|
319
|
+
assert_nil JSON.load(result)
|
320
|
+
|
321
|
+
num_eval += 1
|
322
|
+
end
|
323
|
+
ensure
|
324
|
+
assert_equal bodies.size, num_eval
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
test "Jsonnet::VM#handle_import is safe on throw" do
|
329
|
+
[
|
330
|
+
proc {|rel, base| throw :dummy },
|
331
|
+
lambda {|rel, base| throw :dummy },
|
332
|
+
].each do |prc|
|
333
|
+
vm = Jsonnet::VM.new
|
334
|
+
vm.handle_import(&prc)
|
335
|
+
|
336
|
+
catch(:dummy) {
|
337
|
+
vm.evaluate('import "a.jsonnet"')
|
338
|
+
flunk "never reach here"
|
339
|
+
}
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
test "Jsonnet::VM#jpath_add adds a library search path" do
|
344
|
+
vm = Jsonnet::VM.new
|
345
|
+
snippet = "(import 'jpath.libsonnet') {b: 2}"
|
346
|
+
assert_raise(Jsonnet::EvaluationError) {
|
347
|
+
vm.evaluate(snippet)
|
348
|
+
}
|
349
|
+
|
350
|
+
vm.jpath_add(File.join(__dir__, 'fixtures'))
|
351
|
+
result = vm.evaluate(snippet)
|
352
|
+
assert_equal JSON.parse(<<-EOS), JSON.parse(result)
|
353
|
+
{
|
354
|
+
"a": 1,
|
355
|
+
"b": 2
|
356
|
+
}
|
357
|
+
EOS
|
358
|
+
end
|
359
|
+
|
360
|
+
test "Jsonnet::VM#define_function adds a new native extension" do
|
361
|
+
vm = Jsonnet::VM.new
|
362
|
+
called = false
|
363
|
+
|
364
|
+
vm.define_function("myPow") do |x, y|
|
365
|
+
called = true
|
366
|
+
x ** y
|
367
|
+
end
|
368
|
+
|
369
|
+
result = vm.evaluate("std.native('myPow')(3, 4)")
|
370
|
+
assert_equal 3**4, JSON.load(result)
|
371
|
+
assert_true called
|
372
|
+
end
|
373
|
+
|
374
|
+
test "Jsonnet::VM#define_function passes various types of arguments" do
|
375
|
+
[
|
376
|
+
[%q(null), nil],
|
377
|
+
[%q("abc"), "abc"],
|
378
|
+
[%q(1), 1.0],
|
379
|
+
[%q(1.25), 1.25],
|
380
|
+
[%q(true), true],
|
381
|
+
[%q(false), false],
|
382
|
+
].each do |expr, value|
|
383
|
+
vm = Jsonnet::VM.new
|
384
|
+
vm.define_function("myFunc") do |x|
|
385
|
+
assert_equal value, x
|
386
|
+
next nil
|
387
|
+
end
|
388
|
+
vm.evaluate("std.native('myFunc')(#{expr})")
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
test "Jsonnet::VM#define_function returns various types of values" do
|
393
|
+
[
|
394
|
+
[nil, nil],
|
395
|
+
["abc", "abc"],
|
396
|
+
[1, 1.0],
|
397
|
+
[1.25, 1.25],
|
398
|
+
[true, true],
|
399
|
+
[false, false],
|
400
|
+
].each do |retval, expected|
|
401
|
+
vm = Jsonnet::VM.new
|
402
|
+
vm.define_function("myFunc") { retval }
|
403
|
+
|
404
|
+
result = vm.evaluate("std.native('myFunc')()")
|
405
|
+
assert_equal expected, JSON.load(result)
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
test "Jsonnet::VM#define_function translates an exception in a native function into an error" do
|
410
|
+
vm = Jsonnet::VM.new
|
411
|
+
vm.define_function("myFunc") do |x|
|
412
|
+
raise "something wrong"
|
413
|
+
end
|
414
|
+
assert_raise(Jsonnet::EvaluationError) {
|
415
|
+
vm.evaluate("std.native('myFunc')(1)")
|
416
|
+
}
|
417
|
+
end
|
418
|
+
|
419
|
+
test "Jsonnet::VM#define_function let the function return a compound object" do
|
420
|
+
vm = Jsonnet::VM.new
|
421
|
+
vm.define_function("myCompound") do |x, y|
|
422
|
+
{
|
423
|
+
x => y,
|
424
|
+
y => [x, y, y, x],
|
425
|
+
}
|
426
|
+
end
|
427
|
+
|
428
|
+
result = vm.evaluate("std.native('myCompound')('abc', 'def')")
|
429
|
+
assert_equal JSON.parse(<<-EOS), JSON.parse(result)
|
430
|
+
{
|
431
|
+
"abc": "def",
|
432
|
+
"def": ["abc", "def", "def", "abc"]
|
433
|
+
}
|
434
|
+
EOS
|
435
|
+
end
|
436
|
+
|
437
|
+
test "Jsonnet::VM#define_function treats global escapes as define_method does" do
|
438
|
+
num_eval = 0
|
439
|
+
begin
|
440
|
+
bodies = [
|
441
|
+
proc {|x| return x },
|
442
|
+
lambda {|x| return x },
|
443
|
+
proc {|x| next x },
|
444
|
+
lambda {|x| next x },
|
445
|
+
proc {|x| break x },
|
446
|
+
lambda {|x| break x },
|
447
|
+
]
|
448
|
+
bodies.each do |prc|
|
449
|
+
vm = Jsonnet::VM.new
|
450
|
+
vm.define_function(:myFunc, prc)
|
451
|
+
|
452
|
+
result = vm.evaluate('std.native("myFunc")(1.25) + 0.25')
|
453
|
+
assert_equal 1.25 + 0.25, JSON.load(result)
|
454
|
+
|
455
|
+
num_eval += 1
|
456
|
+
end
|
457
|
+
ensure
|
458
|
+
assert_equal bodies.size, num_eval
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
test "Jsonnet::VM#define_function is safe on throw" do
|
463
|
+
[
|
464
|
+
proc {|x| throw :dummy },
|
465
|
+
lambda {|x| throw :dummy },
|
466
|
+
].each do |prc|
|
467
|
+
vm = Jsonnet::VM.new
|
468
|
+
vm.define_function(:myFunc, prc)
|
469
|
+
|
470
|
+
catch(:dummy) {
|
471
|
+
vm.evaluate('std.native("myFunc")(1.234)')
|
472
|
+
flunk "never reach here"
|
473
|
+
}
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
260
477
|
private
|
261
478
|
def with_example_file(content)
|
262
479
|
Tempfile.open("example.jsonnet") {|f|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jsonnet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yuki Yugui Sonoda
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-07-
|
11
|
+
date: 2017-07-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mini_portile2
|
@@ -88,6 +88,7 @@ extensions:
|
|
88
88
|
- ext/jsonnet/extconf.rb
|
89
89
|
extra_rdoc_files: []
|
90
90
|
files:
|
91
|
+
- ".clang-format"
|
91
92
|
- ".gitignore"
|
92
93
|
- ".travis.yml"
|
93
94
|
- Gemfile
|
@@ -95,12 +96,18 @@ files:
|
|
95
96
|
- README.md
|
96
97
|
- Rakefile
|
97
98
|
- ext/jsonnet/.gitignore
|
99
|
+
- ext/jsonnet/callbacks.c
|
98
100
|
- ext/jsonnet/extconf.rb
|
101
|
+
- ext/jsonnet/helpers.c
|
99
102
|
- ext/jsonnet/jsonnet.c
|
103
|
+
- ext/jsonnet/jsonnet_values.c
|
104
|
+
- ext/jsonnet/ruby_jsonnet.h
|
105
|
+
- ext/jsonnet/vm.c
|
100
106
|
- jsonnet.gemspec
|
101
107
|
- lib/jsonnet.rb
|
102
108
|
- lib/jsonnet/version.rb
|
103
109
|
- lib/jsonnet/vm.rb
|
110
|
+
- test/fixtures/jpath.libsonnet
|
104
111
|
- test/test_jsonnet.rb
|
105
112
|
- test/test_vm.rb
|
106
113
|
homepage: ''
|
@@ -128,5 +135,6 @@ signing_key:
|
|
128
135
|
specification_version: 4
|
129
136
|
summary: Jsonnet library
|
130
137
|
test_files:
|
138
|
+
- test/fixtures/jpath.libsonnet
|
131
139
|
- test/test_jsonnet.rb
|
132
140
|
- test/test_vm.rb
|