Tiny language for spending low resource and embedding within App, etc.
Codes that are written with VivJson is suitable to pack together with data.
Case 1. Codes as a part of data
VivJson's codes can be written into single line. So it is easy that
codes are packed together with data. Then codes can be evaluated after
extracting from data.
{
"name": "foo",
"number": 3,
"code": "max = 10, if (number > max) {number = max}"
}
Case 2. Data as a part of codes
VivJson can evaluate JSON value as code.
The result also becomes JSON value after evaluating. Note that only
variables are exported.
In the following sample, 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
}
}
They are equivalent to JSON basically.
Number is allowed only decimal.
There are two types.
2.
is invalid.)Integer and Floating-point number is converted automatically.
Note that String and Number is not converted automatically.
string
, int
, and float
functions
should be called.
For example, 1
, 1.5
, -10
,
3.7e+5
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")
true
false
In logic operation, null
, 0
,
0.0
is treated as false
. The other is treated
as true
.
When the undefined variable is read, its value is
null
.
For example, a value of the following b
is
[3, null]
.
a = [1, 2, 3]
b = [a[2], a[3]]
Array has elements that are surrounded with square brackets. For
example, [1, 2, 3]
Array can contain various type's element together. For example,
[1, true, {"a": null}, "x"]
Its specific element is accessed with index. For example,
a[2]
The size (length) is given with len
function. For example,
len(a)
The index starts from 0
. And the index ends to
(len(a) - 1)
for Array a
.
Backward access is possible with the negative index. For example,
a[-1]
is 3
for
a = [1, 2, 3]
.
Syntax sugar: a.2
is equivalent to a[2]
.
Similarly, a.-1
is equivalent to a[-1]
.
,
, ;
, white-space, and line-break (new line
code) can be used as delimiter.
So the following arrays are equivalent.
a = [1, 2, 3]
b = [1; 2; 3]
c = [1 2 3]
d = [
1
2
3
]
e = [1
2
3]
By the way, the redundant last delimiter is permitted.
So f = [1, 2, 3, ]
is also equivalent to the above.
Block has statements that are surrounded with curly brackets. For
example, {i = 0, print(i)}
.
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. object, map, dict, hashes,
associative arrays).
When you want to return other value, it is possible with
return
or :=
statement.
For example, the following assignment makes a value
{"a": 100, "b": 15}
and it is assigned into variable
x
.
x = {
a = 100
b = 5
b *= 3
}
if
/elseif
/else
statement can
be assigned too. The following if
assigns
{"y": 1}
into variable x
.
x = if (true) { y = 1 } else { y = 2 }
for
/while
/do
loop can be
assigned too. The following for
assigns
{"i": 10, "j": 18}
into variable x
.
x = for (i = 0; i < 10; i += 1) { j = i * 2 }
When you need only j
, i
should be replaced
with _i
. As a result, {"j": 18}
is assigned
into variable x
x = for (_i = 0; _i < 10; _i += 1) { j = _i * 2 }
Furthermore, :=
statement assigns the plain value
18
into variable x
x = for (i = 0; i < 10; i += 1) { := i * 2 }
In pure function or anonymous function (includes IIFE),
return
statement can be used instead of :=
statement.
The following variable x
's value becomes
3
.
function sum(a, b) {
return(a + b)
}
x = sum(1, 2)
When return
is not existed/evaluated, the returned value
is only variables that are created in this block.
Thus, the parameter (argument) is not contained.
So the following variable x
's value becomes
{"c": 3}
.
function sum(a, b) {
c = a + b
}
x = sum(1, 2)
The independent block is not allowed. For example, the following code is invalid.
a = 100
{
b = 200
}
On the other hand, the following code is valid. There are 2 reasons.
Firstly, it is argument. Secondly, line-break (new line code) can be
used instead of ;
and ,
.
function sample(x, y) {
print(x + y.b)
}
# It is valid.
# |
# V
sample(
100
{
b = 200
}
)
The explicit statement terminator is not needed.
However it can specify with ;
, ,
, or
line-break (new line code).
Thus, {a = 100 b = 5}
, {a = 100; b = 5;}
, and
{a = 100, b = 5}
are equivalent.
:
can be used instead of =
for the
assignment.
(Note that the variable of the left-hand side is treated as local
variable when :
is used.)
The variable of the left-hand side can be enclosed with quotation
marks.
As a result, JSON's object is valid statements for VivJson.
x = {"a": 100, "b": 15, "data": 115}
y = {
a = 100
b = 15
data = 115
}
if (x == y) {print('same')}
There are various styles in order to access member.
For example,
x = {"a": 100, "b": [true, {"c": 3, "0": "foo"}]}
:
x["b"][0]
is true
. Similarly,
x["b"].0
, x.b[0]
, x.b.0
,
x.b.-2
, and x.b[-2]
is same value.x.b["0"]
is invalid because the index of array
must be number.)x.b.1.0
is "foo"
. Similarly,
x.b.1["0"]
, x.b.1[0]
, x.b.-1[0]
,
x.b[-1].0
, and x.b.-1.0
is same value.Any block {...}
is equivalent to the function.
There are 6 types as the function.
function test(a) {return(a * 2)}
.for
, while
, and
do
.if
, elseif
, and else
.There are 3 situations.
Assignment of the block is 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 is anonymous function if "function" is given as modifier of the corresponded parameter.
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")
}
On the other hand, when modifier of the corresponded parameter is
omitted, the block is evaluated and its result is given as
argument.
Thus the following code and the above code make same result.
function output(value) {
print(value)
}
output({
return("test")
})
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)
Pure function is well known type, such as
function test(a) {return(a * 2)}
.
Its definition begins from modifier "function".
function sample() {
a = 3
b = 2
}
The above function returns {"a": 3, "b": 2}
.
When you want to return another value, such as literal, it is realized
with return
statement. For example, the following function
returns 10
.
function sample() {
a = 3
b = 2
return(10)
}
return 10
is not allowed. return
statement
needs parentheses ()
for the returned value though it isn't
needed in most of the language.
On the other hand, return
(that excludes parentheses and
value) is allowed too. The following function returns
{"a": 3, "b": 2}
. a
is not 5
because this function is escaped with return
statement
before a = a + b
.
function sample() {
a = 3
b = 2
return
a = a + b
}
:=
also returns the returned value.
However :=
does not interrupt the function. So the
following function executes print
.
function sample() {
a = 3
:= 10
b = 2
print("Not interrupt")
}
x = sample() # 10
When there are return
and :=
together and
return
has value, return
's value is
returned.
function sample() {
a = 3
:= 10
b = 2
return(a + b)
}
x = sample() # 5
return |
:= |
The returned value |
---|---|---|
nothing | nothing | Block's original value |
nothing | := value |
Value of := |
return |
nothing | Block's original value |
return |
:= value |
Value of := |
return(value) |
nothing | Value of return |
return(value) |
:= value |
Value of return |
In VivJson, for
, while
, and do
are implemented as special function.
C-like for
loop
for (i = 0; i < 10; i += 1) {
:
:
}
Note that ,
, white-space, and line-break (new line code)
can be used instead of ;
as statement terminator
(delimiter).
For example, for (i = 0, i < 10, i += 1) { ... }
and
for (i = 0 i < 10 i += 1) { ... }
are valid.
C-like infinite loop
However loop times are limited with configuration. The default maximum
loop times are 1000.
for (;;) {
:
:
}
Note that for () { ... }
is also infinite loop.
for
loop with iterator
In for
loop, in
operator with
array/block/.
makes iterator.
sum = 0
for (i in [1, 2, 3]) {
sum += i
}
print(sum) # 6
When the right-hand side is key-value pairs (block), an array of 2
elements are generated.
1st element has key. 2nd element has value.
for (item in {orange: 2, grape: 3}) {
print("There are " + item[1] + " " + item[0] + "s.")
}
/* There are 2 oranges.
* There are 3 grapes.
*/
The iterator of this block (scope) is obtained
with .
.
foo = 10
bar = 30
baz = 20
max = -1
for (pair in .) {
if (max < pair[1]) {
max = pair[1]
}
}
print(max) # 30
However, such code may generate the unexpected result because
.
has the variable max
.
When _max
is used instead of max
, it is
improved. Because .
does not treat the variable whose
prefix is "_".
_max = -1
for (pair in .) {
if (_max < pair[1]) {
_max = pair[1]
}
}
print(_max) # 30
The returned value of for
loop.
x = for (i = 0; i < 10; i += 1) { # {"i": 10, "j": 18}
j = i * 2
}
x = for (_i = 0; _i < 10; _i += 1) { # {"j": 18}
j = _i * 2
}
:=
is useful in order to return the particular
value.
return
statement is not suitable for this purpose because
the innermost function finishes.
x = for (i = 0; i < 10; i += 1) { # 18
:= i * 2
}
The following while
loop repeats 10 times.
i = 0
while (i < 10) {
:
:
i += 1
}
The following while
loop is infinite.
However loop times are limited with configuration. The default maximum
loop times are 1000.
while (true) {
:
:
}
while
loop can also make the returned value just like
for
loop.
This is not do { ... } while ( condition )
.
This block is executed only one time. However it can be executed more
times with continue
.
This is useful as below.
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"
do
loop can also make the returned value just like
for
loop.
In VivJson, if
, elseif
, and
else
are implemented as special function.
if (...) {
:
:
} elseif (...) {
:
:
} elseif (...) {
:
:
} else (...) {
:
:
}
if
/elseif
/else
can also make
the returned value just like for
loop.
function enclosure(a) {
x = a
function closure(y) {
return(x + y)
}
return(closure)
}
z1 = enclosure(100)
z2 = enclosure(200)
a = z1(5)
b = z2(10)
print(a, b) # 105, 210
When other function is finished, key-value pairs are collected from
its block.
However variables whose prefix is "_" and the definition of function are
removed.
On the other hand, class constructor keeps whole variables/definitions.
To tell the truth, class and block of anonymous function are same structure. When anonymous function is executed as class constructor, its Closure is used as class method.
Main code can be used as class constructor a present.
Any block is equivalent to the function as mentioned above.
It is classified into 6 types. One of the reason is that
break
/continue
/return
works as
the expected behavior.
function test(a) {return(a * 2)}
.for
, while
, and
do
.if
, elseif
, and else
.break
/continue
works for innermost Loop
that includes it.return
works for innermost pure/anonymous function that
includes it.Type | Within Loop | Using break /continue |
Using return |
In the following diagram |
---|---|---|---|---|
Anonymous function | ----- | Disable | Enable for itself | * 1 *, * 2 *, * 7 * |
Pure function | ----- | Disable | Enable for itself | * 6 * |
Loop | ----- | Enable for itself | Enable for innermost Anonymous/Pure | * 4 *, * 9 * |
Limited function | No | Disable | Enable for innermost Anonymous/Pure | * 3 *, * 8 * |
Limited function | Yes | Enable for innermost loop | Enable for innermost Anonymous/Pure | * 5 *, * 10 * |
* 1 *
a = {
* 2 *
}
b = 100
if (b >= 100) {
* 3 *
}
for (i = 0; i < 100; i += 1) {
* 4 *
if (i == 0) {
* 5 *
}
}
function sample(x) {
* 6 *
y = {
* 7 *
}
if (x >= 100) {
* 8 *
}
for (i = 0; i < 100; i += 1) {
* 9 *
if (i == 0) {
* 10 *
}
}
}
return
accepts the returned value that is surrounded
with parentheses ()
. For example,
return(100)
.
The returned value and parentheses ()
can be omitted like
return
. Then the default value is returned.
Although it looks like obsolete C style, VivJson needs this style.
The above default value is the evaluated block. For example,
x = { a = 3, b = 2, b *= 5 }
is evaluated as
x = {"a": 3, "b": 10}
.
When break
/continue
/return
is
executed, the interrupted block(function)'s result is collected until
interrupted location. For example,
function test() {a = 10; return; a = 20}, x = test()
is
evaluated as {"x": {"a": 10}}
.
The letter of name must be alphabet, digit (0-9), or "_" (underscore).
Only the first letter must be alphabet or "_" (underscore).
In other words, digit (0-9) is invalid for the first letter.
When the first letter has "_" (underscore), its variable is not contained in the result.
It is invalid that both of prefix and suffix are "___" (Triple underscores).
Valid sample:
Invalid sample:
The following code is evaluated as b = {"a": 5}
because
the first letter of "_temp" is "_" (underscore).
b = {
_temp = 3
a = _temp + 2
}
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"}
Lexical scope is applied.
The inner block (as function) can use its outer block's variable/function.
{
_factor = 2
function twice(x) {
result = x * _factor # The outer variable "_factor" can be used.
return(result)
}
a = 100
b = {
c = a + 200 # The outer variable "a" can be used.
}
d = twice(b.c) # The following variable "e" can't be used here.
# Because it is not assigned now.
e = 5
}
The evaluated block becomes key-value pairs (a.k.a. map, dict,
hashes, associative arrays). In the above example,
b = {c = a + 200}
makes that the value of b
is
{"c": 300}
.
Carefully, 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
}
}
Getting value of the undefined variable is permitted. Its value is
null
.
On the other hand, setting value may be made error.
a = [1, 2, 3]
print(a[3]) # null though a[3] is out of range.
a[2] = 0 # No problem. It is formal as modification.
a[3] = 4 # <-- Error happens. The cause is out of range.
# a += 4 is correct for this purpose (appending).
print(c) # null though c is undefined.
c = 4 # No problem. It is formal as setting.
Removing the defined variable is possible with remove
function.
c = 4
print(c) # 4
remove(c)
print(c) # null
a = [1, 2, 3]
print(a) # [1, 2, 3]
remove(a[1])
print(a) # [1, 3]
There is implicit variable _
in the function.
Arguments are stored into _
as array.
For example, when add(3, 2)
is called, _[0]
is
3
and _[1]
is 2
.
function add() {
return(_[0] + _[1])
}
print(add(3, 2)) # 5
Note that there is no implicit variable for immediately invoked function expression (IIFE).
The parameter of function is alias of implicit variable.
1st parameter is alias of _[0]
.
2nd parameter is alias of _[1]
.
:
:
For example, the argument 5
is stored into implicit
variable _[0]
. Then a *= 2
makes
_[0] = 10
because the parameter a
is alias of
_[0]
.
function x2(a) {
a *= 2
return(_[0])
}
print(x2(5)) # 10
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.
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".
function add_dirty(a, b) {
return(a + b + _[2]) # Even if 3rd parameter (alias) is not given,
# it can be read via implicit variable.
}
print(add_dirty(3, 2, 1)) # 6
function add_whole() {
sum = 0
for (value in _) {
sum += value
}
return(sum)
}
print(add_whole(-5, 10, 1.5)) # 6.5
function test(a, b) {
print(a, b)
}
test(100) # 100, null --- The missing argument is treated as null.
The explicit statement terminator is not needed.
Thus, line-break (new line code) can be used to separate
parameters/arguments.
For example, in the following code, 300
is printed.
function sample(x, y) {
print(x + y.b)
}
sample(
100
{
b = 200
}
)
Similarly, the following code has also same arguments.
function sample(x, y) {
print(x + y.b)
}
sample(100
{b = 200})
Each statement is evaluated step by step from top to bottom and from
left to right.
However a part of evaluation is from right to left. Refer to the
following table in Order of
operations.
When the definition of certain function is not evaluated yet, calling it makes error.
x = test(1) # Error
function test(a) {
return(a * 100)
}
On the other hand, the definition of same function name can be written many times.
function test(a) {
return(a*100)
}
print(test(1)) # 100
function test(a) {
return(a+10)
}
print(test(1)) # 11
In the following table, order indicates from highest to lowest.
Operators | Associates | Purpose |
---|---|---|
. [] () | Left to Right | Member/Array access, Function call |
+ - not | Right to left | Unary operation ("not" is logical) |
* / % | Left to Right | Arithmetic |
+ - | Left to Right | Arithmetic |
< <= > >= | N/A | Comparison |
== != | N/A | Comparison |
and | Left to Right | Logical AND |
or | Left to Right | Logical OR |
= : += -= *= /= %= := | Right to left | Assignment |
There is no bitwise operation.
and
and or
make Short-circuit evaluation
(minimal evaluation). For example, x = false and foo()
and
x = true or foo()
don't execute foo()
.
Note that VivJson needs not only arithmetic operation but also
assignment or function call.
Thus, it is invalid that 3 + 2
is existed just under main
code. On the other hand, a = 3 + 2
and
test(3 + 2)
are valid.
left \ right | block | array | string | int | float | boolean | null |
---|---|---|---|---|---|---|---|
block | block | array | ERROR | ERROR | ERROR | ERROR | block |
array | array | array | array | array | array | array | array |
string | ERROR | array | string | string | string | string | string |
int | ERROR | array | string | int | float | boolean | int |
float | ERROR | array | string | float | float | boolean | float |
boolean | ERROR | array | string | boolean | boolean | boolean | boolean |
null | block | array | string | int | float | boolean | null |
{"a": 3, "b": 100} + {"0": "x"}
-->
{"a": 3, "b": 100, "0": "x"}
{"a": 3, "b": 100} + {"a": -5}
-->
{"a": -2, "b": 100}
[3, "a"] + {"a": 3}
-->
[3, "a", {"a": 3}]
{"a": 3} + [3, "a"]
-->
[{"a": 3}, 3, "a"]
[3, "a"] + [true, false]
-->
[3, "a", [true, false]]
[3, "a"] + true
--> [3, "a", true]
true + [3, "a"]
--> [true, 3, "a"]
3 + "a"
--> "3a"
3 + false
--> true
: 3 is equivalent to
true. Then true + false --> true or false.3 + 2
--> 5
3 + null
--> 3
left \ right | block | array | string | int | float | boolean | null |
---|---|---|---|---|---|---|---|
block | block | # | block | ERROR | ERROR | ERROR | block |
array | array | array | array | array | array | array | array |
string | ERROR | * | string | ERROR | ERROR | ERROR | string |
int | ERROR | ERROR | ERROR | int | float | ERROR | int |
float | ERROR | ERROR | ERROR | float | float | ERROR | float |
boolean | ERROR | ERROR | ERROR | ERROR | ERROR | ERROR | boolean |
null | ERROR | ERROR | ERROR | ERROR | ERROR | ERROR | null |
# is block or ERROR
* is string or ERROR
{"a": 10, "b": 20, "c": 30} - {"b": 5, "c": 10}
-->
{"a": 10, "b": 15, "c": 20}
{"a": 10, "b": 20} - {"b": 5, "c": 10}
-->
{"a": 10, "b": 15, "c": -10}
{"a": 10, "b": 20, "c": 30} - ["b", "c"]
-->
{"a": 10}
{"a": 10, "b": 20, "c": 30, "3": 40} - ["b", "c", 3]
--> ERROR because there is number in array.{"a": 10, "b": 20, "c": 30} - "b"
-->
{"a": 10, "c": 30}
{"a": 10, "b": 20, "c": 30} - "d"
-->
{"a": 10, "b": 20, "c": 30}
: Missing "d" is ignored.[3, "a", "a"] - "a"
--> [3]
[3, [100, true]] - [100, true]
-->
[3]
[3, {"a": null}] - {"a": null}
-->
[3]
[3, false, "a"] - true
-->
[3, false, "a"]
: Missing "true" is ignored."large-dog&small-dog&2cat" - "dog"
-->
"large-&small-&2cat"
"large-dog&small-dog&2cat" - ["large-", "small-"]
--> "dog&dog&2cat"
"large-dog&small-dog&2cat" - ["large-", 2]
--> ERROR1.5 - 3.1
--> -1.6
{"a": null} - null
--> {"a": null}
100 - null
--> 100
true - null
--> true
null - null
--> null
null - "a"
--> ERRORleft \ right | block | array | string | int | float | boolean | null |
---|---|---|---|---|---|---|---|
block | block | ERROR | ERROR | ERROR | ERROR | ERROR | null |
array | ERROR | ERROR | string | array | array | ERROR | null |
string | ERROR | string | ERROR | string | string | ERROR | null |
int | ERROR | array | string | int | float | ERROR | null |
float | ERROR | array | string | float | float | ERROR | null |
boolean | ERROR | ERROR | ERROR | ERROR | ERROR | ERROR | null |
null | null | null | null | null | null | null | null |
{"a": 2} * {"a": 10, "b": 3}
-->
{"a": 20, "b": null}
{"a": 2} * {"b": 3}
-->
{"a": null, "b": null}
[3, "a"] * 2
--> [3, "a", 3, "a"]
2 * [3, "a"]
--> [3, "a", 3, "a"]
"3a" * 2
--> "3a3a"
2 * "3a"
--> "3a3a"
[3, "a"] * "|"
--> "3|a"
"|" * [3, "a"]
--> "3|a"
3 * 2
--> 6
n * 0
.
100 * null
--> null
left \ right | block | array | string | int | float | boolean | null |
---|---|---|---|---|---|---|---|
block | # | ERROR | ERROR | ERROR | ERROR | ERROR | ERROR |
array | ERROR | ERROR | ERROR | ERROR | ERROR | ERROR | ERROR |
string | ERROR | ERROR | array | ERROR | ERROR | ERROR | ERROR |
int | ERROR | ERROR | ERROR | * | * | ERROR | ERROR |
float | ERROR | ERROR | ERROR | * | * | ERROR | ERROR |
boolean | ERROR | ERROR | ERROR | ERROR | ERROR | ERROR | ERROR |
null | null | null | null | x | x | null | ERROR |
# is block or ERROR
* is int or float or ERROR
x is null or ERROR
{"a": 2} / {"a": 10, "b": 3}
-->
{"a": 0.2, "b": null}
{"a": 2} / {"b": 3}
--> ERROR because a = 2 / null
happen."a,b,c" / ","
--> ["a", "b", "c"]
"a,b,c," / ","
-->
["a", "b", "c", ""]
"a,b,c" / ""
-->
["a", ",", "b", ",", "c"]
3 / 2
--> 1.5
3 / 0
--> ERROR because dividing by zero makes
infinite.3 / null
--> ERRORnull / 3
--> null
null / 0
--> ERROR (Dividing by zero is ERROR.)left \ right | block | array | string | int | float | boolean | null |
---|---|---|---|---|---|---|---|
block | # | ERROR | ERROR | ERROR | ERROR | ERROR | ERROR |
array | ERROR | ERROR | ERROR | ERROR | ERROR | ERROR | ERROR |
string | ERROR | ERROR | ERROR | ERROR | ERROR | ERROR | ERROR |
int | ERROR | ERROR | ERROR | * | * | ERROR | ERROR |
float | ERROR | ERROR | ERROR | * | * | ERROR | ERROR |
boolean | ERROR | ERROR | ERROR | ERROR | ERROR | ERROR | ERROR |
null | null | null | null | x | x | null | ERROR |
# is block or ERROR
* is int or float or ERROR
x is null or ERROR
{"a": 20} % {"a": 6, "b": 3}
-->
{"a": 2, "b": null}
{"a": 2} % {"b": 3}
--> ERROR because a = 2 % null
happen.15 % 6
--> 3
15 % 0
--> ERRORnull % 2
--> null
null / 0
--> ERROR (Modulo by zero is ERROR.)and
: And
false and true
--> false
true and true
--> true
or
: Or
false or false
--> false
true or false
--> true
not
: Invert
not true
--> false
not false
--> 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()
.
Operator | Description | Operand |
---|---|---|
== |
Equal | Any |
!= |
Not equal | Any |
< |
Less than | Number |
<= |
Less than or equal to | Number |
> |
Greater than | Number |
>= |
Greater than or equal to | Number |
in |
Contain | Left-hand side: Any, Right-hand side: block, array, string, or . |
.
of right-hand side represents this block (scope). For example,
foo = 3, bar = 2, return("foo" in .)
returns
true
.
Note that in
of for (* in *) {...}
makes
iterator. It is not comparison. Refer to for
loop.
Operator | Description |
---|---|
= |
Normal assignment |
: |
Local assignment (Creating local variable) |
+= |
Assignment by addition |
-= |
Assignment by difference |
*= |
Assignment by product |
/= |
Assignment by quotient |
%= |
Assignment by remainder |
:= |
Returned value's assignment (Refer to Block and Returned value) |
The explicit statement terminator is not needed.
Therefore, the following code is valid.
sum = 0 for (item in [1, 2, 3]) {sum += item} return(sum)
,
, ;
, white-space, and line-break (new line
code) can be used as statement terminator (delimiter).
So the following code is valid too.
sum = 0, for (item in [1, 2, 3]) {sum += item}; return(sum)
The following statement can be placed just under block (includes Main code).
foo = 3
function test() {...}
test()
for
, while
, and do
if
, elseif
, and else
break
, continue
, and
return
break
and continue
makes
runtime-error if they are not placed in loop.){
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("+")}
}
It is impossible that value (number, string, boolean, null), array, block, Arithmetic operation, Logical operation, and Comparison are placed just under block (includes Main code).
{
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
}
However it is possible that they are placed as below.
bar = 100
test(100)
if (10 != 100) {print('different')}
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)
int
: Convert string/float into int. Decimal fraction
value is truncated. For example, int("20")
,
int(3.5)
float
: Convert string/int into float. For example,
float("20")
, float(10000)
string
: Convert any value into string. For example,
string(3.5)
len
: Count length (size) of array/block/string. For
example, len([1, 2])
, len({"a": 1, "b": 2})
,
len("abc")
insert
: Insert a value into the array.
insert(array, index, value)
. For example,
a = [1, 3, 4], insert(a, len(a), 5), insert(a, 1, 2)
strip
: Remove white-space and new line code from the
head/tail of string. Double-byte space (Full-width space) is also
removed.type
: Get the type of value.type(3)
, type("abc")
print
: Print to stdout. For example,
print(100)
, print("a", 20)
Note that remove
is not belonged to Standard library.
Because it is the statement though it looks like function.