VivJson is extension of JSON.
The following 2 blocks are equivalent and valid statements in
VivJson.
The base concept is that the block {...}
is function. After
evaluating it, it will be key-value pairs (a.k.a. map, dict, hashes,
associative arrays) of host language. Finally, it can be exported as
JSON's object.
{
"a": 100,
"b": 15,
"data": 115
}
{
a = 100
b = 15
data = 115
}
Codes that are written with VivJson is suitable to pack together with
data.
In the following sample, codes can be evaluated after extracting from
data.
{
"name": "foo",
"number": 3,
"code": "max = 10, if (number > max) {number = max}"
}
On the other hand, in the following sample, it is assumed that itself
is evaluated as VivJson. And it is assumed that _max
is
given from caller. The result becomes
{"name": "foo", "number": 2}
if _max
is
2
.
{
"name": "foo",
"number": 3,
if (number > _max) {
number = _max
}
}
Print "Hello".
$ python3 -m vivjson 'print("Hello")'
Hello
{}
However "{}" is redundant. It can be reduced with
return("")
.
$ python3 -m vivjson 'print("Hello"), return("")'
Hello
Otherwise, return
is used instead of
print
.
$ python3 -m vivjson 'return("Hello")'
Hello
Arguments can be given before statement.
$ python3 -m vivjson "a:3,b:7,return(a+b)"
10
$ python3 -m vivjson "{a:3,b:7}" "return(a+b)"
10
$ python3 -m vivjson "x=" + "{a:3,b:7}" "return(x.a+x.b)"
10
$ python3 -m vivjson "3,7" "return(_[0]+_[1])"
10
$ python3 -m vivjson 3 7 "return(_[0]+_[1])"
10
$ python3 -m vivjson 10 "return(_)"
10
File can be given instead of text.
File extension must be ".viv" or ".json".
$ python3 -m vivjson test.viv
$ python3 -m vivjson data.json calc.viv
$ python3 -m vivjson "{a:3,b:7}" calc.viv
PIPE can be used with "-i" option.
$ echo "return(3*2)" | python3 -m vivjson -i
$ echo "a=3" | python3 -m vivjson -i "return(a*2)"
$ echo '{"a":3}' | python3 -m vivjson -i "return(a*2)"
$ cat test.viv | python3 -m vivjson -i
$ cat data.json | python3 -m vivjson -i "return(a*b)"
$ echo '[{"name": "dog", "number": 2}, {"name": "cat", "number": 3}]' | python3 -m vivjson -i=values "result = {}, for(value in values){result[value.name] = value.number}, return(result)"
{"dog": 2, "cat": 3}
Parse and execution are done as below.
run
function accepts multi arguments. File paths are also
accepted.
The returned value is Python's native value.
There is no exception. When error occurs, error message is stored into
the returned value.
from vivjson.viv import Viv
= Viv.run('a: 3, b: 2, return(a / b)')
value, error_message print(value) # 1.5
from vivjson.viv import Viv
= Viv.run('a: 3, b: 0, return(a / b)')
value, error_message print(error_message) # [Viv] Error: Cannot evaluate "(a / b)" in (line: 1, column: 20)
Representation of several value is different between Python and
VivJson.
However it is converted automatically.
The returned value of run
is represented as Python's
value.
Python | VivJson |
---|---|
True | true |
False | false |
None | null |
tuple | (N/A) |
Although key of Python's dict can be number, VivJson can't treat it.
Parse and execution are done as below.
Each function accepts multi arguments. File paths are also
accepted.
The returned value is Java's object or primitive value.
There is two types based on managing error.
1st type does not throw exception. Instead of it, the returned value
indicates error if error occurs.
2nd type throw exception if error occurs. And the returned value has
only the actual returned value.
The suffix of 2nd type's method name is "Ex".
import com.benesult.vivjson.Viv;
Float value = Viv.getFloat("a: 3, b: 2, return(a / b)");
System.out.println(value); // 1.5
import com.benesult.vivjson.Viv;
String code = "a: 3, b: 0, return(a / b)";
Float value1 = Viv.getFloat(code); // null
try {
float value2 = Viv.getFloatEx(code);
} catch (VivException e) {
System.err.println(e.getMessage()); // [Viv] Error: Cannot evaluate "(a / b)" in (line: 1, column: 20)
}
JSON offers flexible data structure. Its manipulation is so easy in
dynamically typed language. On the other hand, in statically typed
language, such as Java, it is so difficult.
Thus, this embedded script empowers to manipulate JSON in Java.
print("Hello")
print("1", 2, true)
There are 3 styles.
#
. Finish at the end of the line.//
. Finish at the end of the line./*
. Finish with */
. It is used
to multiple lines and inline comment.year = 2024 # The past
month = 1 // It is valid as month.
day = 30 /* It is valid except
* February.
*/
set_person(/* name */ "tom", /* age */ 20)
There is no declaration like "let" and "var".
a = 100
b = "xyz"
c = true
d = null
e = [1, 2, 3]
f = {"name": "foo", "number": 25}
:
can be used instead of =
.
Their difference is scope.
:
creates the local variable.
Even if the outer block's variable is modified, it is not contained
as the inner block's member. For example, the following code makes that
the value of x
is {"a": 300, "c": {"b": 300}}
.
In other words, the created variable is contained as its enveloped
block's member.
x = {
a = 100
c = {
a = a + 200
b = a
}
}
When c
needs a
as member, it is possible
with :
instead of =
. :
creates
the local variable.
In the following sample, a: a + 200
is the outer block's
a
+ 200
because the creation of a
is done after calculating a + 200
. Then, in
b = a
, a
of the local variable is used. As a
result, the value of x
is
{"a": 100, "c": {"a": 300, "b": 300}}
.
x = {
a = 100
c = {
a: a + 200
b = a
}
}
The variable of the left-hand side can be enclosed with quotation
marks. So {"a": 100}
and {a = 100}
is
equivalent.
"x": {"a": 100}
y = {a = 100}
if (x == y) {print("same")} # same
By the way, the variable is removed with remove
.
a = 100
remove(a)
print(a) # null
When the undefined variable is read, its value is
null
.
Its existence is obtained with in
operator.
x = {a: 10, b: 20}
print("a" in x) # true
print(x) # {"a": 10, "b": 20}
print(x.a) # 10
remove(x.a)
print("a" in x) # false
print(x)' # {"b": 20}
print(x.a) # null
It is allowed only decimal.
There are two types.
2.
is invalid.)Integer and Floating-point number is converted automatically.
For example, 1
, 1.5
, -10
,
3.7e+5
Arithmetic operation and Comparison are possible.
a = 3 + 2 # 5
a = 3 - 2 # 1
a = 3 * 2 # 6
a = 3 / 2 # 1.5
a = 3 % 2 # 1 (Modulo)
a = 3 == 2 # false
a = 3 != 2 # true
a = 3 < 2 # false
a = 3 <= 2 # false
a = 3 > 2 # true
a = 3 >= 2 # true
Increment and decrement is realized with assignment operator:
i += 1
, i -= 1
"int" and "float" are converted with int
function and
float
function, such as a = int(1.5) # 1
and
a = float(1) # 1.0
.
It can be converted into string with string
function,
such as a = string(3) # "3"
.
type
function returns "int" or "float".
a = type(1) # "int"
a = type(1.5) # "float"
String is allowed only UTF-8.
The quotation mark is either of '
or "
. For
example, 'This is text.'
, "Sample"
The length is given with len
function. For example,
len("Sample")
Concatenation is done with +
or *
operator.
a = "Large " + "dog" # "Large dog"
a = ["x", "y"] * "|" # "x|y"
or
a = "|" * ["x", "y"] # "x|y"
Removing word is done with -
operator:
a = "large-dog&small-dog&2cat" - "dog" # "large-&small-&2cat"
Splitting is done with /
operator:
a = "x,y,z" / "," # ["x", "y", "z"]
==
and !=
are used to judge whether
left-hand side string and right-hand side one are same or not.
a = "xyz"
b = a == "xyz" # true
c = a != "xyz" # false
in
is used to judge whether left-hand side string is
contained in right-hand side array/string or not.
a = "dog" in ["cat", "dog", "bird"] # true
a = "pig" in ["cat", "dog", "bird"] # false
a = "dog" in "cat&dog&bird" # true
String's number is converted into pure number with int
or float
function, such as a = int("2")
,
a = float("3.5")
.
type
function returns "string". For example,
a = type("xyz") # "string"
There are two values: true
, false
Logical operation are possible.
a = 100
print(a == 100) # true
b = "xyz"
print(b != "xyz") # false
print(not (b != "xyz")) # true
if (a == 100 and b == "xyz") {
print("Both are satisfied.")
}
if (type(a) == "string" or type(b) == "string") {
print("Either type is string.")
}
In logical operation, null
, 0
,
0.0
is treated as false
. The other is treated
as true
.
and
and or
make Short-circuit evaluation
(minimal evaluation). For example, x = false and foo()
and
x = true or foo()
don't execute foo()
.
type
function returns "boolean". For example,
a = type(false) # "boolean"
When the undefined variable is read, its value is
null
.
a = [1, 2, 3]
b = [a[2], a[3]] # [3, null]
type
function returns "null". For example,
a = type(null) # "null"
Array is initialized with assignment.
a = []
a = [10, 20]
a = [100] * 5 # [100, 100, 100, 100, 100]
It can contain various type's element together.
a = [1, true, {"a": null}, "x"]
Its specific element is accessed with index.
The index starts from 0
.
The size (length) is given with len
function.
Backward access is possible with the negative index.
The redundant last delimiter is permitted.
a = [10, 20, 30, 40, 50]
b = a[0] # 10
c = len(a) # 5
d = a[-2] # 40
e = [1, 2, 3, ] # [1, 2, 3]
a[0] = -100 # a = [-100, 20, 30, 40, 50]
Syntax sugar is existed. a.2
is equivalent to
a[2]
. Similarly, a.-1
is equivalent to
a[-1]
.
a = [10, 20, 30, 40, 50]
b = a.2 # 30
c = a.-1 # 50
a.0 = -100 # a = [-100, 20, 30, 40, 50]
,
, ;
, white-space, and line-break (new line
code) can be used as delimiter.
a = [1, 2, 3]
b = [1; 2; 3]
c = [1 2 3]
d = [
1
2
3
]
e = [1
2
3]
Appending element is done with +
operator.
x = [3, "a"] + 100 # [3, "a", 100]
x = true + [3, "a"] # [true, 3, "a"]
x = [3, "a"] + {"a": 3} # [3, "a", {"a": 3}]
x = [3, "a"] + [true, false] # [3, "a", [true, false]]
: In Array + Array, right-hand side is appended into left-hand
side.Removing element is done with -
operator or
remove
.
x = [3, "a", "a"] - "a" # [3]
x = [3, [100, true]] - [100, true] # [3]
x = [3, {"a": null}] - {"a": null} # [3]
x = [10, 20, 30], remove(x[1]) # [10, 30]
x = [10, 20, 30], remove(x[3]) # Error because out of range
There is two roles about in
operator.
if ("a" in [3, "a", "b"]) {print("exist")} # exist
sum = 0; for (i in [1, 10, 100]) {sum+=i} # sum = 111
type
function returns "array". For example,
x = type([3, "a", "b"]) # "array"
The role of Block:
The group of statements
The following example, do
has one block.
(To tell the truth, each if
also has one block.)
x = 1000
y = null
do {
y = "1xx"
if (x >= 100 and x < 1000) {
break
}
y += "x"
if (x >= 1000 and x < 10000) {
break
}
y = "other"
}
print(y) # "1xxx"
Key-value pairs
It is so-called map, dict, hashes, associative arrays.
x = {
"num": 100,
"str": "text"
}
y = {
num = 100
str = "text"
}
z = x == y # true
Anonymous function
Immediately invoked function expression (IIFE)
For example, after executing code, the value of x
is
{"a": 3, "b": 5}
and 15
is printed.
x = {
a = 3
b = a + 2
print(a * b)
}
Argument of the block
function run(function worker) { # <-- The modifier of
worker() # parameter "worker"
} # is "function".
run({
print("test")
})
A block of the last argument can be moved after parentheses
()
as below.
run() {
print("test")
}
When other argument is nothing, parentheses ()
can be
omitted as below.
run {
print("test")
}
Main code
In command-line
For example, {print(10)}
in
python3 -m vivjson '{print(10)}'
is main code.
Since the outermost bracket can be omitted as syntax-sugar,
print(10)
in python3 -m vivjson 'print(10)'
is
also main code.
In file
The following codes are main code.
The outermost bracket can be omitted as syntax-sugar.
{
a = 100
b = 5
b *= 3
function add(x, y) {
return(x + y)
}
data = add(a, b)
}
a = 100
b = 5
b *= 3
function add(x, y) {
return(x + y)
}
data = add(a, b)
a = 100, b = 5, b *= 3, function add(x, y) {return(x + y)}, data = add(a, b)
Any block is equivalent to the function.
So its result can be assigned into a variable of left-hand side. In
other words, assignment of the block is immediately invoked function
expression (IIFE).
Basically, it becomes key-value pairs (a.k.a. map, dict, hashes,
associative arrays).
When you want to return other value, it is possible with
return
or :=
statement. However
return 10
is not allowed. return
statement
needs parentheses ()
for the returned value though it isn't
needed in most of the language.
When there are return
and :=
together and
return
has value, return
's value is
returned.
function sample() {
a = 3
b = 2
}
x = sample() # {"a": 3, "b": 2}
function sample() {
a = 3
b = 2
return(10)
}
x = sample() # 10
function sample() {
a = 3
return
b = 2
}
x = sample() # {"a": 3}
function sample() {
a = 3
:= 10
b = 2
print("Not interrupt")
}
x = sample() # 10
x = if (true) {a = 3} else {a = 0} # {"a": 3}
x = if (true) { := 3} else { := 0} # 3
Note that same name's function can be defined again.
The member is accessed with its name.
The size (length) is given with len
function.
The redundant last delimiter is permitted.
w = {"a": 3, "b": 100}
x = w["b"] # 100
y = len(w) # 2
z = {"a": 3, "b": 100,} # {"a": 3, "b": 100}
w["b"] = 20 # w = {"a": 3, "b": 20}
Syntax sugar is existed. w.b
is equivalent to
w["b"]
.
w = {"a": 3, "b": 100}
x = w.b # 100
w.b = 20 # w = {"a": 3, "b": 20}
When member's name is number (of string), its quotation can be omitted as syntax sugar.
w = {"a": 3, "b": 100, "5": 7}
x = w.5 # 7
y = w[5] # 7
w.5 = 1000 # w = {"a": 3, "b": 100, "5": 1000}
w.-1 = true # w = {"a": 3, "b": 100, "5": 1000, "-1": true}
Arithmetic operation is possible for each member.
x = {"a": 3, "b": 100} + {"0": "c"} # {"a": 3, "b": 100, "0": "c"}
x = {"a": 3, "b": 100} + {"a": -5} # {"a": -2, "b": 100}
x = {"a": 10, "b": 20, "c": 30} - {"b": 5, "c": 10} # {"a": 10, "b": 15, "c": 20}
x = {"a": 10, "b": 20} - {"b": 5, "c": 10} # {"a": 10, "b": 15, "c": -10}
x = {"a": 2} * {"a": 10, "b": 3} # {"a": 20, "b": null}
x = {"a": 2} * {"b": 3} # {"a": null, "b": null}
x = {"a": 2} / {"a": 10, "b": 3} # {"a": 0.2, "b": null}
x = {"a": 2} / {"b": 3} # ERROR because a = 2 / null happen.
x = {"a": 20} % {"a": 6, "b": 3} # {"a": 2, "b": null}
x = {"a": 2} % {"b": 3} # ERROR because a = 2 % null happen.
Removing element is done with -
operator or
remove
.
x = {"a": 10, "b": 20, "c": 30} - ["b", "c"] # {"a": 10}
x = {"a": 10, "b": 20, "c": 30, "3": 40} - ["b", "c", 3] # ERROR because there is number in array. Key must be string.
x = {"a": 10, "b": 20, "c": 30} - "b" # {"a": 10, "c": 30}
x = {"a": 10, "b": 20, "c": 30} - "d" # {"a": 10, "b": 20, "c": 30}
: Missing "d" is ignored.x = {"a": 10, "b": 20, "c": 30}, remove(x.b) # {"a": 10, "c": 30}
x = {"a": 10, "b": 20, "c": 30}, remove(x["d"]) # {"a": 10, "b": 20, "c": 30}
: Missing "d" is ignored.There is two roles about in
operator.
if ({"a": 2, "b": false} in [1, {"b": false, "a": 2}, "c"]) {print("exist")} # exist
animals = {"dog": 2, "cat": 2}, result = [], for (animal in animals) {result += animal[0] + " is " + animal[1]}, return(result) # ["dog is 2", "cat is 2"]
The right hand sided .
of in
represents
this block.
foo = 3, bar = 2, return("foo" in .)
returns
true
.foo = 3, bar = 2, _result = '', for (pair in .) {_result += pair[0] + ':' + pair[1] + ','}, return(_result)
returns foo:3,bar:2,
.type
function returns "block". For example,
x = type({"dog": 2, "cat": 2}) # "block"
Function's definition begins from modifier "function".
Function call is Call by value.
function x2(k) {
k *= 2
print(k)
}
a = 3
x2(a) # 6 is printed.
print(a) # 3 is printed.
function x2(k) {
k[0] *= 2
k[1] *= 2
print(k)
}
a = [3, 5]
x2(a) # [6, 10] is printed.
print(a) # [3, 5] is printed.
Note that same name's function can be defined again.
Array and block (key-value pairs) can be Call by reference using "reference".
function x2(reference k) {
k[0] *= 2
k[1] *= 2
print(k)
}
a = [3, 5]
x2(a) # [6, 10] is printed.
print(a) # [6, 10] is printed.
Even if number of argument and number of parameter is different,
operation is done.
In other words, error is not happen for this difference.
function add(a, b) {
return(a + b)
}
print(add(3, 2, 1)) # 5 --- 3rd argument is ignored in function "add".
The explicit statement terminator and delimiter are not needed.
Therefore, the following code is valid.
sum = 0 for (item in [1 2 3 4 5]) {sum += item} return(sum) # 15
,
, ;
, white-space, and line-break (new line
code) can be used as statement terminator and delimiter.
So the following code is valid too.
sum = 0, for (item in [1, 2, 3, 4, 5]) {sum += item}; return(sum) # 15
It is impossible that value (number, string, boolean, null), array, block, Arithmetic operation, Logical operation, and Comparison are placed directly.
100 # Error
"invalid" # Error
true # Error
null # Error
[1, 2, 3] # Error
{"x": 1, "y": 2} # Error
15 / 3 # Error
false and true # Error
10 < 100 # Error
They are used as operand and argument.
foo = 3
function test() {print("test")}
test()
for (i = 1; i < 10; i += 1) {foo += 1 continue foo -= 1}
while (foo > 0) {foo -= 1 break}
do {foo = 100}
if (foo < 0) {print("-")} elseif (foo == 0) {print("0")} else {print("+")}
_temp
.___temp___
.b = {
_temp = 3
a = _temp + 2
}
print(b) # {"a": 5}
When you need number as key (name of member), it is realized as below. However it must be string.
a = {"0": 100}
return(a["0"]) # 100
return(a[0]) # 100 -- a[0] is syntax sugar of a["0"].
return(a.0) # 100 -- a.0 is syntax sugar of a["0"]
a = {}
a[0] = 100 # a[0] is syntax sugar of a["0"]
a.1 = 200 # a.1 is syntax sugar of a["1"]
return(a) # {"0": 100, "1": 200}
In the same way, the letter that is not allowed as name can be used as key.
a = {}
a["!."] = "test"
return(a) # {"!.": "test"}
There are 3 types as Loop: for
, while
, and
do
They accept break
/continue
.
Loop times are limited with configuration. The default maximum loop times are 1000.
for
for (i = 0; i < 10; i += 1) { # 10 times
:
:
}
for (;;) { # Infinite. However loop times are limited with configuration.
: # for () { ... } is also infinite loop.
:
}
sum = 0
for (i in [1, 2, 3]) { # Iterator
sum += i
}
print(sum) # 6
animals = {"dog": 2, "cat": 2}
result = []
for (animal in animals) { # Iterator
result += animal[0] + " is " + animal[1]
}
print(result) # ["dog is 2", "cat is 2"]
while
i = 0
while (i < 10) {
:
:
i += 1
}
do
This is not do { ... } while ( condition )
.
This block is executed only one time. However it can be executed more
times with continue
.
x = 1000
y = null
do {
y = "1xx"
if (x >= 100 and x < 1000) {
break
}
y += "x"
if (x >= 1000 and x < 10000) {
break
}
y = "other"
}
print(y) # "1xxx"