Skip to content

Commit 96c2cf8

Browse files
committed
macro for calculations, readme updated
1 parent 7d95c7e commit 96c2cf8

11 files changed

+470
-60
lines changed

Diff for: LANGUAGE.md

+133-1
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,138 @@ evaluates as: `{ "$exec": "myExec" }` and the executor is not called.
574574
The anonymous function defined above always returns the string `"foo"`. Without `$quote` it would have been the reference to the function with the name `foo`.
575575

576576

577+
## Calculations
578+
579+
Predefined executor `calc` defines methods for arythmetic, comparison and logical operations. For all operations the arguments (`$args`) should be an array and operations are applied to the list:
580+
581+
```json
582+
{ "$+": [ 1, 2, 3 ] }
583+
```
584+
585+
or using the full syntax:
586+
587+
```json
588+
{
589+
"$exec": "calc",
590+
"$method": "add",
591+
"$args": [ 1, 2, 3 ]
592+
}
593+
```
594+
595+
Full syntax can be useful if you need to determine the required operation using some script:
596+
597+
```json
598+
{
599+
"$exec": "calc",
600+
"$method": { "$data": "/method" },
601+
"$args": [ 1, 2, 3 ]
602+
}
603+
```
604+
605+
For arythmetic and comparison operations arguments must be numbers, there is no type coercion.
606+
607+
For boolean operations arguments must be boolean values.
608+
609+
Equality operations can work with any type.
610+
611+
612+
Defined operations:
613+
614+
| method|short syntax|evaluation|
615+
|--------------|:---:|---|
616+
| add |"$+" |add all arguments|
617+
| subtract |"$-" |subtract arguments from the first argument|
618+
| multiply |"$*" |multiply all arguments|
619+
| divide |"$/" |divide the first argument by the rest|
620+
| equal |"$=="|true if all arguments are equal|
621+
| notEqual |"$!="|true if at least one argument is different|
622+
| greater |"$>" |true if arguments are descending|
623+
| greaterEqual |"$>="|true if arguments are not ascending|
624+
| lesser |"$<" |true if arguments are ascending|
625+
| lesserEqual |"$<="|true if arguments are not descending|
626+
| and |"$&&"|true if all arguments are true|
627+
| or |"$||"|true if one or more arguments are true and the rest are false|
628+
| xor |"$^^"|true if exactly one argument is true and others are false|
629+
| not |"$!" |negates boolean value|
630+
631+
632+
## Array iteration
633+
634+
Predefined executor `array` implements methods for array iteration:
635+
636+
```json
637+
{
638+
"$$array.map": {
639+
"data": [
640+
"/resource/1",
641+
"/resource/2",
642+
"/resource/3"
643+
],
644+
"iterator": {
645+
"$func": {
646+
"$$router.get": { "path": { "$data": "/path" } }
647+
},
648+
"$args": ["path"]
649+
}
650+
}
651+
}
652+
```
653+
654+
The example above calls the method `get` of executor `router` for all paths. The result of evaluation of this script will be the array of responses.
655+
656+
Same script using full syntax:
657+
658+
```json
659+
{
660+
"$exec": "array",
661+
"$method": "map",
662+
"$args": {
663+
"data": [
664+
"/resource/1",
665+
"/resource/2",
666+
"/resource/3"
667+
],
668+
"iterator": {
669+
"$func": {
670+
"$exec": "router",
671+
"$method": "get",
672+
"$args": { "path": { "$data": "/path" } }
673+
},
674+
"$args": ["path"]
675+
}
676+
}
677+
}
678+
```
679+
680+
Defined array methods:
681+
682+
| method |evaluation result|
683+
|--------|---|
684+
| map |new array with function call results for each item|
685+
| filter |new array of original items for which function calls return `true`|
686+
| every |`true` if all function calls return `true`|
687+
| some |`true` if at least one function call returns `true`|
688+
689+
690+
This script filters only positive numbers from array:
691+
692+
```
693+
{
694+
"$$array.filter": {
695+
"data": [ -2, -1, 0, 1, 2, 3 ],
696+
"iterator": {
697+
"$func": {
698+
"$>": [ { "$data": "/num" }, 0 ]
699+
},
700+
"$args": ["num"]
701+
}
702+
}
703+
}
704+
```
705+
706+
For all methods iterator function is called with 3 parameters: array item, item index and the array itself.
707+
708+
577709
## Macros
578710

579-
JSONScript defines several core macros to support short syntax for executor and function calls and for calculations. The interpreter may allow to define your own custom macros.
711+
JSONScript defines several core macros to support short syntax for calculations and for calling executors methods and functions. The interpreter may allow to define your own custom macros.

Diff for: macros/calc.json

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "calc",
3+
"description": "short calculations syntax",
4+
"rules": [
5+
{
6+
"description": "calculation",
7+
"pattern": {
8+
"\\$([+\\-*/=!<>&\\|^]{1,2})": 1
9+
},
10+
"script": {
11+
"$exec": "calc",
12+
"$method": { "$1": {
13+
"+": "add",
14+
"-": "subtract",
15+
"*": "multiply",
16+
"/": "divide",
17+
"==": "equal",
18+
"!=": "notEqual",
19+
">": "greater",
20+
">=": "greaterEqual",
21+
"<": "lesser",
22+
"<=": "lesserEqual",
23+
"&&": "and",
24+
"||": "or",
25+
"^^": "xor",
26+
"!": "not"
27+
} },
28+
"$args": 1
29+
}
30+
}
31+
]
32+
}

Diff for: macros/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22

33
module.exports = [
44
require('./exec.json'),
5-
require('./call.json')
5+
require('./call.json'),
6+
require('./calc.json')
67
];

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "jsonscript",
3-
"version": "0.3.0",
3+
"version": "0.4.0",
44
"description": "Platform independent asynchronous and concurrent scripting language using JSON format",
55
"main": "index.js",
66
"scripts": {

Diff for: schema/expand_macros.json

+82-36
Original file line numberDiff line numberDiff line change
@@ -9,43 +9,82 @@
99
"type": "object"
1010
},
1111
"then": {
12-
"anyOf": [
12+
"allOf": [
1313
{
14-
"expandJsMacro": {
15-
"description": "executor call with method",
16-
"pattern": {
17-
"^\\$\\$([^\\.]+)\\.([^\\.]+)$": 1
14+
"anyOf": [
15+
{
16+
"expandJsMacro": {
17+
"description": "executor call with method",
18+
"pattern": {
19+
"^\\$\\$([^\\.]+)\\.([^\\.]+)$": 1
20+
},
21+
"script": {
22+
"$exec": "$1",
23+
"$method": "$2",
24+
"$args": 1
25+
}
26+
}
1827
},
19-
"script": {
20-
"$exec": "$1",
21-
"$method": "$2",
22-
"$args": 1
23-
}
24-
}
25-
},
26-
{
27-
"expandJsMacro": {
28-
"description": "executor call without method",
29-
"pattern": {
30-
"^\\$\\$([^\\.]+)$": 1
28+
{
29+
"expandJsMacro": {
30+
"description": "executor call without method",
31+
"pattern": {
32+
"^\\$\\$([^\\.]+)$": 1
33+
},
34+
"script": {
35+
"$exec": "$1",
36+
"$args": 1
37+
}
38+
}
3139
},
32-
"script": {
33-
"$exec": "$1",
34-
"$args": 1
35-
}
36-
}
37-
},
38-
{
39-
"expandJsMacro": {
40-
"description": "call named function with arguments",
41-
"pattern": {
42-
"^\\$\\#(.+)$": 1
40+
{
41+
"expandJsMacro": {
42+
"description": "call named function with arguments",
43+
"pattern": {
44+
"^\\$\\#(.+)$": 1
45+
},
46+
"script": {
47+
"$call": "$1",
48+
"$args": 1
49+
}
50+
}
4351
},
44-
"script": {
45-
"$call": "$1",
46-
"$args": 1
52+
{
53+
"expandJsMacro": {
54+
"description": "calculation",
55+
"pattern": {
56+
"\\$([+\\-*/=!<>&\\|^]{1,2})": 1
57+
},
58+
"script": {
59+
"$exec": "calc",
60+
"$method": {
61+
"$1": {
62+
"+": "add",
63+
"-": "subtract",
64+
"*": "multiply",
65+
"/": "divide",
66+
"==": "equal",
67+
"!=": "notEqual",
68+
">": "greater",
69+
">=": "greaterEqual",
70+
"<": "lesser",
71+
"<=": "lesserEqual",
72+
"&&": "and",
73+
"||": "or",
74+
"^^": "xor",
75+
"!": "not"
76+
}
77+
},
78+
"$args": 1
79+
}
80+
}
81+
},
82+
{
83+
"additionalProperties": {
84+
"$ref": "#"
85+
}
4786
}
48-
}
87+
]
4988
},
5089
{
5190
"additionalProperties": {
@@ -54,9 +93,16 @@
5493
}
5594
]
5695
}
96+
},
97+
{
98+
"if": {
99+
"type": "array"
100+
},
101+
"then": {
102+
"items": {
103+
"$ref": "#"
104+
}
105+
}
57106
}
58-
],
59-
"items": {
60-
"$ref": "#"
61-
}
107+
]
62108
}

Diff for: schema/expand_macros.json.dot

+23-17
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,28 @@
33
"$schema": "https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-v5.json#",
44
"title": "JSONScript macro expansion schema",
55
"description": "Schema with custom keywords that expands macros in JSON script.",
6-
"switch": [{
7-
"if": { "type": "object" },
8-
"then": {
9-
"anyOf": [
10-
{{~ it.macros:macro }}
11-
{{~ macro.rules:rule }}
12-
{
13-
"expandJsMacro": {{= JSON.stringify(rule) }}
14-
},
15-
{{~}}
16-
{{~}}
17-
{
18-
"additionalProperties": { "$ref": "#" }
19-
}
20-
]
6+
"switch": [
7+
{
8+
"if": { "type": "object" },
9+
"then": {
10+
"allOf": [
11+
{
12+
"anyOf": [
13+
{{~ it.macros:macro }}
14+
{{~ macro.rules:rule }}
15+
{ "expandJsMacro": {{= JSON.stringify(rule) }} },
16+
{{~}}
17+
{{~}}
18+
{ "additionalProperties": { "$ref": "#" } }
19+
]
20+
},
21+
{ "additionalProperties": { "$ref": "#" } }
22+
]
23+
}
24+
},
25+
{
26+
"if": { "type": "array" },
27+
"then": { "items": { "$ref": "#" } }
2128
}
22-
}],
23-
"items": { "$ref": "#" }
29+
]
2430
}

Diff for: schema/macro.json

+18
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,27 @@
4343
"type": "string",
4444
"pattern": "^\\$[1-9]$"
4545
},
46+
{
47+
"description": "object with a single property that refers to the match; the value of the property is a substitution map",
48+
"type": "object",
49+
"minProperties": 1,
50+
"maxProperties": 1,
51+
"additionalProperties": false,
52+
"patternProperties": {
53+
"^\\$[1-9]$": {
54+
"type": "object",
55+
"additionalProperties": {
56+
"type": "string"
57+
}
58+
}
59+
}
60+
},
4661
{
4762
"description": "this number referes to the value in the macro",
4863
"type": "integer"
64+
},
65+
{
66+
"description": "any valid JSONScript"
4967
}
5068
]
5169
}

0 commit comments

Comments
 (0)