jsonnet 0.1.1 → 0.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/.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
|