ソースを参照

Saving state

master
Dylan Baker 5年前
コミット
91730231df
10個のファイルの変更496行の追加114行の削除
  1. 61
    61
      package-lock.json
  2. 12
    9
      src/ast/index.js
  3. 7
    0
      src/ast/list.js
  4. 1
    13
      src/compiler.js
  5. 20
    11
      src/index.js
  6. 192
    0
      src/resolver.js
  7. 0
    19
      test/compilerTest.js
  8. 14
    1
      test/helpers.js
  9. 48
    0
      test/resolverTest.js
  10. 141
    0
      test/stdlibTest.js

+ 61
- 61
package-lock.json ファイルの表示

@@ -16,7 +16,7 @@
16 16
       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
17 17
       "dev": true,
18 18
       "requires": {
19
-        "balanced-match": "^1.0.0",
19
+        "balanced-match": "1.0.0",
20 20
         "concat-map": "0.0.1"
21 21
       }
22 22
     },
@@ -44,8 +44,8 @@
44 44
       "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=",
45 45
       "dev": true,
46 46
       "requires": {
47
-        "foreach": "^2.0.5",
48
-        "object-keys": "^1.0.8"
47
+        "foreach": "2.0.5",
48
+        "object-keys": "1.0.11"
49 49
       }
50 50
     },
51 51
     "defined": {
@@ -66,11 +66,11 @@
66 66
       "integrity": "sha512-ZnQrE/lXTTQ39ulXZ+J1DTFazV9qBy61x2bY071B+qGco8Z8q1QddsLdt/EF8Ai9hcWH72dWS0kFqXLxOxqslA==",
67 67
       "dev": true,
68 68
       "requires": {
69
-        "es-to-primitive": "^1.1.1",
70
-        "function-bind": "^1.1.1",
71
-        "has": "^1.0.1",
72
-        "is-callable": "^1.1.3",
73
-        "is-regex": "^1.0.4"
69
+        "es-to-primitive": "1.1.1",
70
+        "function-bind": "1.1.1",
71
+        "has": "1.0.1",
72
+        "is-callable": "1.1.3",
73
+        "is-regex": "1.0.4"
74 74
       }
75 75
     },
76 76
     "es-to-primitive": {
@@ -79,9 +79,9 @@
79 79
       "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=",
80 80
       "dev": true,
81 81
       "requires": {
82
-        "is-callable": "^1.1.1",
83
-        "is-date-object": "^1.0.1",
84
-        "is-symbol": "^1.0.1"
82
+        "is-callable": "1.1.3",
83
+        "is-date-object": "1.0.1",
84
+        "is-symbol": "1.0.1"
85 85
       }
86 86
     },
87 87
     "faucet": {
@@ -91,12 +91,12 @@
91 91
       "dev": true,
92 92
       "requires": {
93 93
         "defined": "0.0.0",
94
-        "duplexer": "~0.1.1",
94
+        "duplexer": "0.1.1",
95 95
         "minimist": "0.0.5",
96
-        "sprintf": "~0.1.3",
97
-        "tap-parser": "~0.4.0",
98
-        "tape": "~2.3.2",
99
-        "through2": "~0.2.3"
96
+        "sprintf": "0.1.5",
97
+        "tap-parser": "0.4.3",
98
+        "tape": "2.3.3",
99
+        "through2": "0.2.3"
100 100
       },
101 101
       "dependencies": {
102 102
         "deep-equal": {
@@ -123,12 +123,12 @@
123 123
           "integrity": "sha1-Lnzgox3wn41oUWZKcYQuDKUFevc=",
124 124
           "dev": true,
125 125
           "requires": {
126
-            "deep-equal": "~0.1.0",
127
-            "defined": "~0.0.0",
128
-            "inherits": "~2.0.1",
129
-            "jsonify": "~0.0.0",
130
-            "resumer": "~0.0.0",
131
-            "through": "~2.3.4"
126
+            "deep-equal": "0.1.2",
127
+            "defined": "0.0.0",
128
+            "inherits": "2.0.3",
129
+            "jsonify": "0.0.0",
130
+            "resumer": "0.0.0",
131
+            "through": "2.3.8"
132 132
           }
133 133
         }
134 134
       }
@@ -139,7 +139,7 @@
139 139
       "integrity": "sha1-LEBFC5NI6X8oEyJZO6lnBLmr1NQ=",
140 140
       "dev": true,
141 141
       "requires": {
142
-        "is-function": "~1.0.0"
142
+        "is-function": "1.0.1"
143 143
       }
144 144
     },
145 145
     "foreach": {
@@ -166,12 +166,12 @@
166 166
       "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
167 167
       "dev": true,
168 168
       "requires": {
169
-        "fs.realpath": "^1.0.0",
170
-        "inflight": "^1.0.4",
171
-        "inherits": "2",
172
-        "minimatch": "^3.0.4",
173
-        "once": "^1.3.0",
174
-        "path-is-absolute": "^1.0.0"
169
+        "fs.realpath": "1.0.0",
170
+        "inflight": "1.0.6",
171
+        "inherits": "2.0.3",
172
+        "minimatch": "3.0.4",
173
+        "once": "1.4.0",
174
+        "path-is-absolute": "1.0.1"
175 175
       }
176 176
     },
177 177
     "has": {
@@ -180,7 +180,7 @@
180 180
       "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=",
181 181
       "dev": true,
182 182
       "requires": {
183
-        "function-bind": "^1.0.2"
183
+        "function-bind": "1.1.1"
184 184
       }
185 185
     },
186 186
     "inflight": {
@@ -189,8 +189,8 @@
189 189
       "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
190 190
       "dev": true,
191 191
       "requires": {
192
-        "once": "^1.3.0",
193
-        "wrappy": "1"
192
+        "once": "1.4.0",
193
+        "wrappy": "1.0.2"
194 194
       }
195 195
     },
196 196
     "inherits": {
@@ -223,7 +223,7 @@
223 223
       "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
224 224
       "dev": true,
225 225
       "requires": {
226
-        "has": "^1.0.1"
226
+        "has": "1.0.1"
227 227
       }
228 228
     },
229 229
     "is-symbol": {
@@ -250,7 +250,7 @@
250 250
       "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
251 251
       "dev": true,
252 252
       "requires": {
253
-        "brace-expansion": "^1.1.7"
253
+        "brace-expansion": "1.1.11"
254 254
       }
255 255
     },
256 256
     "minimist": {
@@ -276,7 +276,7 @@
276 276
       "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
277 277
       "dev": true,
278 278
       "requires": {
279
-        "wrappy": "1"
279
+        "wrappy": "1.0.2"
280 280
       }
281 281
     },
282 282
     "path-is-absolute": {
@@ -303,10 +303,10 @@
303 303
       "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
304 304
       "dev": true,
305 305
       "requires": {
306
-        "core-util-is": "~1.0.0",
307
-        "inherits": "~2.0.1",
306
+        "core-util-is": "1.0.2",
307
+        "inherits": "2.0.3",
308 308
         "isarray": "0.0.1",
309
-        "string_decoder": "~0.10.x"
309
+        "string_decoder": "0.10.31"
310 310
       }
311 311
     },
312 312
     "resolve": {
@@ -315,7 +315,7 @@
315 315
       "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==",
316 316
       "dev": true,
317 317
       "requires": {
318
-        "path-parse": "^1.0.5"
318
+        "path-parse": "1.0.5"
319 319
       }
320 320
     },
321 321
     "resumer": {
@@ -324,7 +324,7 @@
324 324
       "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=",
325 325
       "dev": true,
326 326
       "requires": {
327
-        "through": "~2.3.4"
327
+        "through": "2.3.8"
328 328
       }
329 329
     },
330 330
     "sprintf": {
@@ -339,9 +339,9 @@
339 339
       "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=",
340 340
       "dev": true,
341 341
       "requires": {
342
-        "define-properties": "^1.1.2",
343
-        "es-abstract": "^1.5.0",
344
-        "function-bind": "^1.0.2"
342
+        "define-properties": "1.1.2",
343
+        "es-abstract": "1.11.0",
344
+        "function-bind": "1.1.1"
345 345
       }
346 346
     },
347 347
     "string_decoder": {
@@ -356,8 +356,8 @@
356 356
       "integrity": "sha1-pOrhkMENdsehEZIf84u+TVjwnuo=",
357 357
       "dev": true,
358 358
       "requires": {
359
-        "inherits": "~2.0.1",
360
-        "readable-stream": "~1.1.11"
359
+        "inherits": "2.0.3",
360
+        "readable-stream": "1.1.14"
361 361
       }
362 362
     },
363 363
     "tape": {
@@ -366,19 +366,19 @@
366 366
       "integrity": "sha512-j0jO9BiScfqtPBb9QmPLL0qvxXMz98xjkMb7x8lKipFlJZwNJkqkWPou+NU4V6T9RnVh1kuSthLE8gLrN8bBfw==",
367 367
       "dev": true,
368 368
       "requires": {
369
-        "deep-equal": "~1.0.1",
370
-        "defined": "~1.0.0",
371
-        "for-each": "~0.3.2",
372
-        "function-bind": "~1.1.1",
373
-        "glob": "~7.1.2",
374
-        "has": "~1.0.1",
375
-        "inherits": "~2.0.3",
376
-        "minimist": "~1.2.0",
377
-        "object-inspect": "~1.5.0",
378
-        "resolve": "~1.5.0",
379
-        "resumer": "~0.0.0",
380
-        "string.prototype.trim": "~1.1.2",
381
-        "through": "~2.3.8"
369
+        "deep-equal": "1.0.1",
370
+        "defined": "1.0.0",
371
+        "for-each": "0.3.2",
372
+        "function-bind": "1.1.1",
373
+        "glob": "7.1.2",
374
+        "has": "1.0.1",
375
+        "inherits": "2.0.3",
376
+        "minimist": "1.2.0",
377
+        "object-inspect": "1.5.0",
378
+        "resolve": "1.5.0",
379
+        "resumer": "0.0.0",
380
+        "string.prototype.trim": "1.1.2",
381
+        "through": "2.3.8"
382 382
       }
383 383
     },
384 384
     "through": {
@@ -393,8 +393,8 @@
393 393
       "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=",
394 394
       "dev": true,
395 395
       "requires": {
396
-        "readable-stream": "~1.1.9",
397
-        "xtend": "~2.1.1"
396
+        "readable-stream": "1.1.14",
397
+        "xtend": "2.1.2"
398 398
       }
399 399
     },
400 400
     "wrappy": {
@@ -409,7 +409,7 @@
409 409
       "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=",
410 410
       "dev": true,
411 411
       "requires": {
412
-        "object-keys": "~0.4.0"
412
+        "object-keys": "0.4.0"
413 413
       },
414 414
       "dependencies": {
415 415
         "object-keys": {

+ 12
- 9
src/ast/index.js ファイルの表示

@@ -1,17 +1,20 @@
1
-const Application = require('./application')
2
-const Attribute = require('./attribute')
3
-const Boolean = require('./boolean')
4
-const Identifier = require('./identifier')
5
-const Number = require('./number')
6
-const String = require('./string')
7
-const Symbol = require('./symbol')
1
+const Application = require("./application");
2
+const Attribute = require("./attribute");
3
+const Boolean = require("./boolean");
4
+const Identifier = require("./identifier");
5
+const List = require("./list");
6
+const Number = require("./number");
7
+const String = require("./string");
8
+const Symbol = require("./symbol");
8 9
 
9 10
 module.exports = {
10 11
   Application: Application,
11 12
   Attribute: Attribute,
12 13
   Boolean: Boolean,
13 14
   Identifier: Identifier,
15
+  List: List,
14 16
   Number: Number,
15 17
   String: String,
16
-  Symbol, Symbol,
17
-}
18
+  Symbol,
19
+  Symbol
20
+};

+ 7
- 0
src/ast/list.js ファイルの表示

@@ -0,0 +1,7 @@
1
+const Node = require("./node");
2
+
3
+module.exports = class List extends Node {
4
+  constructor(opts) {
5
+    super(opts);
6
+  }
7
+};

+ 1
- 13
src/compiler.js ファイルの表示

@@ -14,9 +14,7 @@ module.exports = class Compiler {
14 14
         case "Application":
15 15
           this.result += this.application(node);
16 16
           break;
17
-        case "Identifier":
18
-          this.result += this.lookup(node);
19
-          break;
17
+        case "Number":
20 18
         case "String":
21 19
           this.result += node.value;
22 20
           break;
@@ -57,14 +55,4 @@ module.exports = class Compiler {
57 55
 
58 56
     return result;
59 57
   }
60
-
61
-  lookup(identifier) {
62
-    let result = this.context[identifier.name];
63
-
64
-    if (!result) {
65
-      throw `Undefined variable '${identifier.name}'`;
66
-    }
67
-
68
-    return result;
69
-  }
70 58
 };

+ 20
- 11
src/index.js ファイルの表示

@@ -1,17 +1,26 @@
1
-const Lexer = require('./lexer')
2
-const Parser = require('./parser')
3
-const Compiler = require('./compiler')
1
+const Lexer = require("./lexer");
2
+const Parser = require("./parser");
3
+const Resolver = require("./resolver");
4
+const Compiler = require("./compiler");
4 5
 
5 6
 module.exports = function oslo(source, context) {
6
-  const lexer = new Lexer()
7
-  const tokens = lexer.scan(source)
8
-  const parser = new Parser(tokens)
9
-  const tree = parser.parse()
7
+  const lexer = new Lexer();
8
+  const tokens = lexer.scan(source);
9
+  const parser = new Parser(tokens);
10
+  const tree = parser.parse();
10 11
 
11 12
   if (tree.error) {
12
-    return tree
13
+    return tree;
13 14
   }
14 15
 
15
-  const compiler = new Compiler(tree, context)
16
-  return compiler.compile()
17
-}
16
+  const resolver = new Resolver();
17
+  const resolvedTree = resolver.resolve(tree, context);
18
+
19
+  const util = require("util");
20
+
21
+  console.log(util.inspect(resolvedTree, { depth: null }));
22
+  process.exit();
23
+
24
+  const compiler = new Compiler(tree);
25
+  return compiler.compile();
26
+};

+ 192
- 0
src/resolver.js ファイルの表示

@@ -0,0 +1,192 @@
1
+const AST = require("./ast");
2
+
3
+module.exports = class Resolver {
4
+  constructor() {
5
+    this.tree = [];
6
+    this.context = {};
7
+
8
+    this.stdlib = {
9
+      if: (predicate, left, right) => {
10
+        const resolvedPredicate = this.resolveNode(predicate);
11
+
12
+        this.typecheck(resolvedPredicate, AST.Boolean);
13
+
14
+        if (resolvedPredicate && resolvedPredicate.value === true) {
15
+          return this.resolveNode(left);
16
+        } else if (resolvedPredicate && resolvedPredicate.value === false) {
17
+          return this.resolveNode(right);
18
+        }
19
+      },
20
+      list: args => {
21
+        let elements = [];
22
+
23
+        args.forEach(arg => {
24
+          elements.push(this.resolveNode(arg));
25
+        });
26
+
27
+        return new AST.List({
28
+          elements: elements
29
+        });
30
+      },
31
+      quote: value => {
32
+        return new AST.Symbol({ value: value });
33
+      },
34
+      htmlElement: node => {
35
+        let resolvedArgs = [];
36
+
37
+        node.args.forEach(arg => {
38
+          resolvedArgs.push(this.resolveNode(arg));
39
+        });
40
+
41
+        return new AST.Application({
42
+          functionName: node.functionName,
43
+          args: resolvedArgs
44
+        });
45
+      },
46
+      "=": (left, right) => {
47
+        this.typecheck(right, left.constructor);
48
+
49
+        if (left.constructor.name === "List") {
50
+          if (left.elements.length !== right.elements.length) {
51
+            return new AST.Boolean({ value: false });
52
+          }
53
+
54
+          let equal = true;
55
+
56
+          left.elements.forEach((el, i) => {
57
+            this.typecheck(el.value, right.elements[i].constructor);
58
+            if (el.value !== right.elements[i].value) {
59
+              equal = false;
60
+            }
61
+          });
62
+
63
+          return new AST.Boolean({ value: equal });
64
+        }
65
+
66
+        return new AST.Boolean({ value: left.value === right.value });
67
+      },
68
+      ">": (left, right) => {
69
+        this.typecheck(left, AST.Number) && this.typecheck(right, AST.Number);
70
+        return new AST.Boolean({ value: left.value > right.value });
71
+      },
72
+      "<": (left, right) => {
73
+        this.typecheck(left, AST.Number) && this.typecheck(right, AST.Number);
74
+        return new AST.Boolean({ value: left.value < right.value });
75
+      },
76
+      ">=": (left, right) => {
77
+        this.typecheck(left, AST.Number) && this.typecheck(right, AST.Number);
78
+        return new AST.Boolean({ value: left.value >= right.value });
79
+      },
80
+      "<=": (left, right) => {
81
+        this.typecheck(left, AST.Number) && this.typecheck(right, AST.Number);
82
+        return new AST.Boolean({ value: left.value <= right.value });
83
+      },
84
+      and: args => {
85
+        let and = true;
86
+
87
+        args.forEach(arg => {
88
+          this.typecheck(arg, AST.Boolean);
89
+          if (arg.value === false) {
90
+            and = false;
91
+          }
92
+        });
93
+
94
+        return new AST.Boolean({ value: and });
95
+      },
96
+      or: args => {
97
+        let or = false;
98
+
99
+        args.forEach(arg => {
100
+          this.typecheck(arg, AST.Boolean);
101
+          if (arg.value === true) {
102
+            or = true;
103
+          }
104
+        });
105
+
106
+        return new AST.Boolean({ value: or });
107
+      },
108
+      lambda: function(parameter, body) {
109
+        console.log(parameter, body);
110
+      }
111
+    };
112
+  }
113
+
114
+  resolve(tree, context) {
115
+    this.context = context;
116
+
117
+    tree.forEach(node => {
118
+      this.tree.push(this.resolveNode(node));
119
+    });
120
+
121
+    return this.tree;
122
+  }
123
+
124
+  resolveNode(node, context) {
125
+    if (!context) {
126
+      context = this.context;
127
+    }
128
+
129
+    switch (node.constructor.name) {
130
+      case "Boolean":
131
+      case "Number":
132
+      case "String":
133
+        return node;
134
+      case "Attribute":
135
+        return new AST.Attribute({
136
+          name: node.name,
137
+          value: this.resolveNode(node.value)
138
+        });
139
+      case "Identifier":
140
+        return this.lookup(node.name);
141
+      case "Application":
142
+        if (this.stdlib.hasOwnProperty(node.functionName.name)) {
143
+          const f = this.stdlib[node.functionName.name];
144
+
145
+          if (["list", "and", "or"].includes(node.functionName.name)) {
146
+            return f(node.args);
147
+          }
148
+
149
+          let resolvedArgs = [];
150
+
151
+          if (node.functionName.name === "quote") {
152
+            resolvedArgs = node.args;
153
+          } else {
154
+            node.args.forEach(arg => {
155
+              resolvedArgs.push(this.resolveNode(arg));
156
+            });
157
+          }
158
+
159
+          return f(...resolvedArgs);
160
+        }
161
+
162
+        return this.stdlib.htmlElement(node);
163
+    }
164
+  }
165
+
166
+  lookup(name) {
167
+    console.log(name);
168
+    const result = this.context[name];
169
+    return this.wrap(result);
170
+  }
171
+
172
+  wrap(value) {
173
+    switch (value.constructor.name) {
174
+      case "String":
175
+        return new AST.String({ value: value });
176
+      case "Number":
177
+        return new AST.Number({ value: value });
178
+      case "Array":
179
+        return new AST.List({ elements: value.map(this.wrap) });
180
+    }
181
+  }
182
+
183
+  typecheck(value, type) {
184
+    console.log(value);
185
+    console.log(type);
186
+    if (value.constructor.name !== type.name) {
187
+      throw `Type error: expected a ${type.name} but got a ${
188
+        value.constructor.name
189
+      }`;
190
+    }
191
+  }
192
+};

+ 0
- 19
test/compilerTest.js ファイルの表示

@@ -15,25 +15,6 @@ test("compiles a simple template", t => {
15 15
   );
16 16
 });
17 17
 
18
-test("renders variables according to passed-in context", t => {
19
-  t.plan(1);
20
-  const result = helpers.compile(
21
-    `
22
-      (div :class classOne
23
-        (p :class classTwo bodyText))
24
-    `,
25
-    {
26
-      classOne: "foobar",
27
-      classTwo: "bazquux",
28
-      bodyText: "Lorem ipsum dolor sit amet."
29
-    }
30
-  );
31
-  t.deepEqual(
32
-    result.replace(/\n/g, "").replace(/  +/g, ""),
33
-    '<div class="foobar"><p class="bazquux">Lorem ipsum dolor sit amet.</p></div>'
34
-  );
35
-});
36
-
37 18
 test("self closing tags are respected", function(t) {
38 19
   t.plan(1);
39 20
   const result = helpers.compile(`

+ 14
- 1
test/helpers.js ファイルの表示

@@ -1,6 +1,7 @@
1 1
 const Compiler = require("../src/compiler");
2 2
 const Lexer = require("../src/lexer");
3 3
 const Parser = require("../src/parser");
4
+const Resolver = require("../src/resolver");
4 5
 
5 6
 const scan = source => {
6 7
   const lexer = new Lexer();
@@ -12,6 +13,11 @@ const parse = source => {
12 13
   return parser.parse();
13 14
 };
14 15
 
16
+const resolve = (source, context) => {
17
+  const resolver = new Resolver();
18
+  return resolver.resolve(parse(source), context);
19
+};
20
+
15 21
 const compile = (source, context) => {
16 22
   const compiler = new Compiler(parse(source), context);
17 23
   return compiler.compile();
@@ -20,5 +26,12 @@ const compile = (source, context) => {
20 26
 module.exports = {
21 27
   scan: scan,
22 28
   parse: parse,
23
-  compile: compile
29
+  compile: compile,
30
+  resolve: resolve
24 31
 };
32
+
33
+console.log(
34
+  require("util").inspect(resolve("(p :class pClass)", { pClass: "lorem" }), {
35
+    depth: null
36
+  })
37
+);

+ 48
- 0
test/resolverTest.js ファイルの表示

@@ -0,0 +1,48 @@
1
+const test = require("tape");
2
+const helpers = require("./helpers");
3
+
4
+const AST = require("../src/ast");
5
+
6
+test("resolves an identifier in the content position", t => {
7
+  t.plan(1);
8
+
9
+  t.deepEqual(
10
+    helpers.resolve("(p text)", { text: "Lorem ipsum dolor sit amet" }),
11
+    [
12
+      new AST.Application({
13
+        functionName: new AST.Identifier({ name: "p" }),
14
+        args: [new AST.String({ value: "Lorem ipsum dolor sit amet" })]
15
+      })
16
+    ]
17
+  );
18
+});
19
+
20
+test("resolves an identifier as attribute value", t => {
21
+  t.plan(1);
22
+
23
+  t.deepEqual(helpers.resolve("(p :class pClass)", { pClass: "testClass" }), [
24
+    new AST.Application({
25
+      functionName: new AST.Identifier({ name: "p" }),
26
+      args: [
27
+        new AST.Attribute({
28
+          name: "class",
29
+          value: new AST.String({ value: "testClass" })
30
+        })
31
+      ]
32
+    })
33
+  ]);
34
+});
35
+
36
+test("resolves an identifier that points to a list", t => {
37
+  t.plan(1);
38
+
39
+  t.deepEqual(helpers.resolve("list", { list: [1, 2, 3] }), [
40
+    new AST.List({
41
+      elements: [
42
+        new AST.Number({ value: 1 }),
43
+        new AST.Number({ value: 2 }),
44
+        new AST.Number({ value: 3 })
45
+      ]
46
+    })
47
+  ]);
48
+});

+ 141
- 0
test/stdlibTest.js ファイルの表示

@@ -0,0 +1,141 @@
1
+const test = require("tape");
2
+const helpers = require("./helpers");
3
+
4
+const AST = require("../src/ast/index");
5
+
6
+test("quote", t => {
7
+  t.plan(3);
8
+
9
+  t.deepEqual(helpers.resolve("(quote 5)"), [
10
+    new AST.Symbol({
11
+      value: new AST.Number({
12
+        value: 5
13
+      })
14
+    })
15
+  ]);
16
+
17
+  t.deepEqual(helpers.resolve('(quote "hello")'), [
18
+    new AST.Symbol({
19
+      value: new AST.String({
20
+        value: "hello"
21
+      })
22
+    })
23
+  ]);
24
+
25
+  t.deepEqual(helpers.resolve("(quote (list 1 2 3))"), [
26
+    new AST.Symbol({
27
+      value: new AST.Application({
28
+        functionName: new AST.Identifier({ name: "list" }),
29
+        args: [
30
+          new AST.Number({ value: 1 }),
31
+          new AST.Number({ value: 2 }),
32
+          new AST.Number({ value: 3 })
33
+        ]
34
+      })
35
+    })
36
+  ]);
37
+});
38
+
39
+test("list", t => {
40
+  t.plan(2);
41
+
42
+  t.deepEqual(helpers.resolve("(list 1 2 3 4 5)"), [
43
+    new AST.List({
44
+      elements: [
45
+        new AST.Number({ value: 1 }),
46
+        new AST.Number({ value: 2 }),
47
+        new AST.Number({ value: 3 }),
48
+        new AST.Number({ value: 4 }),
49
+        new AST.Number({ value: 5 })
50
+      ]
51
+    })
52
+  ]);
53
+
54
+  t.deepEqual(helpers.resolve("(list (list 1 2) (list 3 4))"), [
55
+    new AST.List({
56
+      elements: [
57
+        new AST.List({
58
+          elements: [new AST.Number({ value: 1 }), new AST.Number({ value: 2 })]
59
+        }),
60
+        new AST.List({
61
+          elements: [new AST.Number({ value: 3 }), new AST.Number({ value: 4 })]
62
+        })
63
+      ]
64
+    })
65
+  ]);
66
+});
67
+
68
+test("if", t => {
69
+  t.plan(3);
70
+
71
+  t.deepEqual(helpers.resolve("(if #t 1 0)"), [new AST.Number({ value: 1 })]);
72
+
73
+  t.deepEqual(helpers.resolve("(if #f 1 0)"), [new AST.Number({ value: 0 })]);
74
+
75
+  t.deepEqual(helpers.resolve("(if (if #t #t #f) 1 0)"), [
76
+    new AST.Number({ value: 1 })
77
+  ]);
78
+});
79
+
80
+test("=", t => {
81
+  t.plan(6);
82
+
83
+  t.equal(helpers.resolve("(= 5 5)")[0].value, true);
84
+  t.equal(helpers.resolve("(= 5 6)")[0].value, false);
85
+  t.equal(helpers.resolve('(= "hello" "hello")')[0].value, true);
86
+  t.equal(helpers.resolve('(= "hello" "world")')[0].value, false);
87
+  t.equal(
88
+    helpers.resolve("(= (list #t #t #t) (list (> 5 4) (< 3 7) (= 9 9)))")[0]
89
+      .value,
90
+    true
91
+  );
92
+  t.equal(
93
+    helpers.resolve("(= (list #t #t #t) (list (> 5 4) (< 3 7) (= 9 10)))")[0]
94
+      .value,
95
+    false
96
+  );
97
+});
98
+
99
+test(">", t => {
100
+  t.plan(2);
101
+
102
+  t.deepEqual(helpers.resolve("(> 5 4)"), [new AST.Boolean({ value: true })]);
103
+  t.deepEqual(helpers.resolve("(> 4 5)"), [new AST.Boolean({ value: false })]);
104
+});
105
+
106
+test("<", t => {
107
+  t.plan(2);
108
+
109
+  t.deepEqual(helpers.resolve("(< 5 4)"), [new AST.Boolean({ value: false })]);
110
+  t.deepEqual(helpers.resolve("(< 4 5)"), [new AST.Boolean({ value: true })]);
111
+});
112
+
113
+test(">=", t => {
114
+  t.plan(3);
115
+
116
+  t.deepEqual(helpers.resolve("(>= 5 5)"), [new AST.Boolean({ value: true })]);
117
+  t.deepEqual(helpers.resolve("(>= 6 5)"), [new AST.Boolean({ value: true })]);
118
+  t.deepEqual(helpers.resolve("(>= 5 6)"), [new AST.Boolean({ value: false })]);
119
+});
120
+
121
+test("<=", t => {
122
+  t.plan(3);
123
+
124
+  t.deepEqual(helpers.resolve("(<= 5 5)"), [new AST.Boolean({ value: true })]);
125
+  t.deepEqual(helpers.resolve("(<= 5 6)"), [new AST.Boolean({ value: true })]);
126
+  t.deepEqual(helpers.resolve("(<= 6 5)"), [new AST.Boolean({ value: false })]);
127
+});
128
+
129
+test("and", t => {
130
+  t.plan(2);
131
+
132
+  t.equal(helpers.resolve("(and #t #t #t)")[0].value, true);
133
+  t.equal(helpers.resolve("(and #t #t #f)")[0].value, false);
134
+});
135
+
136
+test("or", t => {
137
+  t.plan(2);
138
+
139
+  t.equal(helpers.resolve("(or #f #f #f)")[0].value, false);
140
+  t.equal(helpers.resolve("(or #t #f #f)")[0].value, true);
141
+});

読み込み中…
キャンセル
保存