1 | 'use strict'
|
2 |
|
3 | const {
|
4 | TokenTypeData,
|
5 | Expr,
|
6 | Token,
|
7 | WrappedPrimitive
|
8 | } = require('./lang');
|
9 | const currentLine = require('./currentLine');
|
10 | const {paths} = require('./cache');
|
11 |
|
12 | function withPathInfo(value, key, currentPath) {
|
13 | const isArray = typeof key === 'number'
|
14 | const newPath = `${currentPath}${isArray ? `[${key}]` : `.${key}`}`
|
15 | if (typeof value === 'undefined') {
|
16 | throw new Error(`Undefined value in carmi expression: ${newPath} at ${currentLine()}`)
|
17 | }
|
18 |
|
19 | if (value && typeof value === 'object') {
|
20 | paths.set(value, newPath)
|
21 | }
|
22 |
|
23 | return value
|
24 | }
|
25 |
|
26 | function convertArrayAndObjectsToExpr(v) {
|
27 | if (typeof v === 'undefined') {
|
28 | throw new Error('Carmi expressions can not contain undefined');
|
29 | }
|
30 |
|
31 | let path
|
32 | if (v && typeof v === 'object') {
|
33 | v = v || 'null'
|
34 | path = paths.get(v)
|
35 | if (!path) {
|
36 | path = '{}'
|
37 | paths.set(v, path)
|
38 | }
|
39 | }
|
40 |
|
41 | if (v === null) {
|
42 | return new Token('null');
|
43 | } else if (v.constructor === Object) {
|
44 | return createExpr(
|
45 | new Token('object', currentLine()),
|
46 | ...Object.keys(v).reduce((acc, key) => {
|
47 | acc.push(key);
|
48 | acc.push(withPathInfo(v[key], key, path));
|
49 | return acc;
|
50 | }, [])
|
51 | );
|
52 | } else if (v.constructor === Array) {
|
53 | return createExpr(new Token('array', currentLine()), ...v.map((entry, index) => withPathInfo(entry, index, path)));
|
54 | } else if (typeof v === 'boolean' || typeof v === 'string' || typeof v === 'number') {
|
55 | return new WrappedPrimitive(v);
|
56 | }
|
57 | return v;
|
58 | }
|
59 |
|
60 | function createExpr(...args) {
|
61 | args = args.map(token => {
|
62 | token = convertArrayAndObjectsToExpr(token);
|
63 | if (token instanceof WrappedPrimitive) {
|
64 | return token.toJSON();
|
65 | }
|
66 | return token;
|
67 | });
|
68 | if (args[0] instanceof Token && TokenTypeData[args[0].$type]) {
|
69 | const len = TokenTypeData[args[0].$type].len;
|
70 | if (len && (args.length < len[0] || args.length > len[1])) {
|
71 | throw new Error(
|
72 | `invalid length for expression ${args[0].$type} length:${args.length} expected:${len[0]}-${len[1]}`
|
73 | );
|
74 | }
|
75 | }
|
76 | return Expr.apply(null, args);
|
77 | }
|
78 |
|
79 | module.exports = {
|
80 | convertArrayAndObjectsToExpr,
|
81 | createExpr
|
82 | }
|