Browse Source

Remove resolver and compiler, add define to parser

master
Dylan Baker 5 years ago
parent
commit
4b3ce29206
13 changed files with 124 additions and 556 deletions
  1. 7
    0
      src/ast/functionDefinition.js
  2. 2
    0
      src/ast/index.js
  3. 0
    58
      src/compiler.js
  4. 1
    16
      src/index.js
  5. 7
    0
      src/lexer.js
  6. 46
    9
      src/parser.js
  7. 0
    192
      src/resolver.js
  8. 12
    11
      src/tokenTypes.js
  9. 0
    28
      test/compilerTest.js
  10. 1
    21
      test/helpers.js
  11. 48
    32
      test/parserTest.js
  12. 0
    48
      test/resolverTest.js
  13. 0
    141
      test/stdlibTest.js

+ 7
- 0
src/ast/functionDefinition.js View File

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

+ 2
- 0
src/ast/index.js View File

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

+ 0
- 58
src/compiler.js View File

@@ -1,58 +0,0 @@
1
-const selfClosingTags = require("./util/selfClosingTags");
2
-
3
-module.exports = class Compiler {
4
-  constructor(tree, context) {
5
-    this.tree = tree;
6
-    this.context = context;
7
-    this.pos = 0;
8
-    this.result = "";
9
-  }
10
-
11
-  compile() {
12
-    this.tree.forEach(node => {
13
-      switch (node.constructor.name) {
14
-        case "Application":
15
-          this.result += this.application(node);
16
-          break;
17
-        case "Number":
18
-        case "String":
19
-          this.result += node.value;
20
-          break;
21
-      }
22
-    });
23
-
24
-    return this.result;
25
-  }
26
-
27
-  application(node) {
28
-    let result = `<${node.functionName.name}`;
29
-
30
-    node.args
31
-      .filter(arg => arg.constructor.name === "Attribute")
32
-      .forEach(arg => {
33
-        result += ` ${arg.name}`;
34
-
35
-        let compiler = new Compiler([arg.value], this.context);
36
-        let attrValue = compiler.compile();
37
-
38
-        if (attrValue) {
39
-          result += `="${attrValue}"`;
40
-        }
41
-      });
42
-
43
-    result += ">";
44
-
45
-    node.args
46
-      .filter(arg => arg.constructor.name !== "Attribute")
47
-      .forEach(arg => {
48
-        let compiler = new Compiler([arg], this.context);
49
-        result += compiler.compile();
50
-      });
51
-
52
-    if (!selfClosingTags.includes(node.functionName.name)) {
53
-      result += `</${node.functionName.name}>`;
54
-    }
55
-
56
-    return result;
57
-  }
58
-};

+ 1
- 16
src/index.js View File

@@ -1,7 +1,5 @@
1 1
 const Lexer = require("./lexer");
2 2
 const Parser = require("./parser");
3
-const Resolver = require("./resolver");
4
-const Compiler = require("./compiler");
5 3
 
6 4
 module.exports = function oslo(source, context) {
7 5
   const lexer = new Lexer();
@@ -9,18 +7,5 @@ module.exports = function oslo(source, context) {
9 7
   const parser = new Parser(tokens);
10 8
   const tree = parser.parse();
11 9
 
12
-  if (tree.error) {
13
-    return tree;
14
-  }
15
-
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();
10
+  return tree;
26 11
 };

+ 7
- 0
src/lexer.js View File

@@ -44,6 +44,13 @@ module.exports = class Lexer {
44 44
           value: value
45 45
         });
46 46
         pos += value.length + 1; // the +1 is to account for the apostrophe
47
+      } else if (source[pos].match(/\,/)) {
48
+        tokenStream.tokens.push({
49
+          type: tokenTypes.COMMA,
50
+          line: line,
51
+          value: value
52
+        });
53
+        pos += 1;
47 54
       } else if (source[pos].match(/\d/)) {
48 55
         let number = "";
49 56
         while (source[pos] && source[pos].match(/\d/)) {

+ 46
- 9
src/parser.js View File

@@ -1,7 +1,5 @@
1
-const Error = require("./Error");
2
-
3 1
 const AST = require("./ast");
4
-
2
+const Error = require("./Error");
5 3
 const tokenTypes = require("./tokenTypes");
6 4
 
7 5
 module.exports = class Parser {
@@ -12,7 +10,8 @@ module.exports = class Parser {
12 10
   parse() {
13 11
     let tree = [];
14 12
     while (this.tokenStream.peek().type !== tokenTypes.EOF) {
15
-      tree.push(this.expr());
13
+      let expr = this.expr();
14
+      tree.push(expr);
16 15
       if (this.tokenStream.error) {
17 16
         return {
18 17
           error: this.tokenStream.error
@@ -50,13 +49,21 @@ module.exports = class Parser {
50 49
   form() {
51 50
     this.tokenStream.eat(tokenTypes.OPAREN);
52 51
 
53
-    let node = new AST.Application();
52
+    let node;
54 53
 
55
-    node.functionName = this.identifier();
56
-    node.args = [];
54
+    if (this.tokenStream.peek().value === "define") {
55
+      this.tokenStream.eat(tokenTypes.IDENTIFIER);
57 56
 
58
-    while (this.tokenStream.peek().type !== tokenTypes.CPAREN) {
59
-      node.args.push(this.expr());
57
+      node = this.functionDefinition();
58
+    } else {
59
+      node = new AST.Application();
60
+
61
+      node.functionName = this.identifier();
62
+      node.args = [];
63
+
64
+      while (this.tokenStream.peek().type !== tokenTypes.CPAREN) {
65
+        node.args.push(this.expr());
66
+      }
60 67
     }
61 68
 
62 69
     this.tokenStream.eat(tokenTypes.CPAREN);
@@ -77,6 +84,36 @@ module.exports = class Parser {
77 84
     });
78 85
   }
79 86
 
87
+  functionDefinition() {
88
+    let name = new AST.Identifier({
89
+      name: this.tokenStream.eat(tokenTypes.IDENTIFIER).value
90
+    });
91
+
92
+    let parameters = [];
93
+
94
+    this.tokenStream.eat(tokenTypes.OPAREN);
95
+
96
+    while (this.tokenStream.peek().type !== tokenTypes.CPAREN) {
97
+      parameters.push(
98
+        new AST.Identifier({
99
+          name: this.tokenStream.eat(tokenTypes.IDENTIFIER).value
100
+        })
101
+      );
102
+
103
+      if (this.tokenStream.peek().type === tokenTypes.COMMA) {
104
+        this.tokenStream.eat(tokenTypes.COMMA);
105
+      }
106
+    }
107
+
108
+    this.tokenStream.eat(tokenTypes.CPAREN);
109
+
110
+    return new AST.FunctionDefinition({
111
+      name: name,
112
+      parameters: parameters,
113
+      body: this.form()
114
+    });
115
+  }
116
+
80 117
   identifier() {
81 118
     return new AST.Identifier({
82 119
       name: this.tokenStream.eat(tokenTypes.IDENTIFIER).value

+ 0
- 192
src/resolver.js View File

@@ -1,192 +0,0 @@
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
-};

+ 12
- 11
src/tokenTypes.js View File

@@ -1,13 +1,14 @@
1 1
 module.exports = {
2
-  OPAREN: '(',
3
-  CPAREN: ')',
4
-  KEYWORD: 'keyword',
2
+  ATTRIBUTE: "attribute",
3
+  BOOLEAN: "boolean",
4
+  COMMA: ",",
5
+  CPAREN: ")",
6
+  EOF: "EOF",
7
+  IDENTIFIER: "identifier",
8
+  KEYWORD: "keyword",
9
+  LITERAL: "literal",
10
+  NUMBER: "number",
11
+  OPAREN: "(",
5 12
   QUOTE: '"',
6
-  LITERAL: 'literal',
7
-  ATTRIBUTE: 'attribute',
8
-  SYMBOL: 'symbol',
9
-  NUMBER: 'number',
10
-  BOOLEAN: 'boolean',
11
-  IDENTIFIER: 'identifier',
12
-  EOF: 'EOF',
13
-}
13
+  SYMBOL: "symbol"
14
+};

+ 0
- 28
test/compilerTest.js View File

@@ -1,28 +0,0 @@
1
-const test = require("tape");
2
-const helpers = require("./helpers");
3
-
4
-const tt = require("../src/tokenTypes");
5
-
6
-test("compiles a simple template", t => {
7
-  t.plan(1);
8
-  const result = helpers.compile(`
9
-    (div :class "foobar"
10
-      (p :class "bazquux" "Lorem ipsum dolor sit amet."))
11
-  `);
12
-  t.deepEqual(
13
-    result.replace(/\n/g, "").replace(/  +/g, ""),
14
-    '<div class="foobar"><p class="bazquux">Lorem ipsum dolor sit amet.</p></div>'
15
-  );
16
-});
17
-
18
-test("self closing tags are respected", function(t) {
19
-  t.plan(1);
20
-  const result = helpers.compile(`
21
-    (meta :charset "UTF-8")
22
-    (img :src "test.png")
23
-  `);
24
-  t.deepEqual(
25
-    result.replace(/\n/g, "").replace(/  +/g, ""),
26
-    '<meta charset="UTF-8"><img src="test.png">'
27
-  );
28
-});

+ 1
- 21
test/helpers.js View File

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

+ 48
- 32
test/parserTest.js View File

@@ -1,53 +1,69 @@
1
-const test = require('tape')
2
-const helpers = require('./helpers')
1
+const test = require("tape");
2
+const helpers = require("./helpers");
3 3
 
4
-const AST = require('../src/ast/index')
5
-const tt = require('../src/tokenTypes')
4
+const AST = require("../src/ast/index");
5
+const tt = require("../src/tokenTypes");
6 6
 
7
-test('parses token stream into a tree', t => {
8
-  t.plan(1)
7
+test("parses token stream into a tree", t => {
8
+  t.plan(1);
9 9
   const tree = helpers.parse(`
10 10
     (div :class "foobar"
11 11
       (p :class (cond #t "primary" "secondary")))
12
-  `)
12
+  `);
13 13
 
14 14
   t.deepEqual(tree, [
15 15
     new AST.Application({
16
-      functionName: new AST.Identifier({ name: 'div' }),
16
+      functionName: new AST.Identifier({ name: "div" }),
17 17
       args: [
18 18
         new AST.Attribute({
19
-          name: 'class',
20
-          value: new AST.String({ value: 'foobar' }),
19
+          name: "class",
20
+          value: new AST.String({ value: "foobar" })
21 21
         }),
22 22
         new AST.Application({
23
-          functionName: new AST.Identifier({ name: 'p' }),
23
+          functionName: new AST.Identifier({ name: "p" }),
24 24
           args: [
25 25
             new AST.Attribute({
26
-              name: 'class',
26
+              name: "class",
27 27
               value: new AST.Application({
28
-                functionName: new AST.Identifier({ name: 'cond' }),
28
+                functionName: new AST.Identifier({ name: "cond" }),
29 29
                 args: [
30 30
                   new AST.Boolean({ value: true }),
31
-                  new AST.String({ value: 'primary' }),
32
-                  new AST.String({ value: 'secondary' }),
33
-                ],
34
-              }),
35
-            }),
36
-          ],
37
-        }),
38
-      ],
39
-    }),
40
-  ])
41
-})
31
+                  new AST.String({ value: "primary" }),
32
+                  new AST.String({ value: "secondary" })
33
+                ]
34
+              })
35
+            })
36
+          ]
37
+        })
38
+      ]
39
+    })
40
+  ]);
41
+});
42 42
 
43
-test('allow empty strings', t => {
44
-  t.plan(1)
45
-  const tree = helpers.parse('(p "")')
43
+test("allow empty strings", t => {
44
+  t.plan(1);
45
+  const tree = helpers.parse('(p "")');
46 46
 
47 47
   t.deepEqual(tree, [
48 48
     new AST.Application({
49
-      functionName: new AST.Identifier({ name: 'p' }),
50
-      args: [new AST.String({ value: '' })],
51
-    }),
52
-  ])
53
-})
49
+      functionName: new AST.Identifier({ name: "p" }),
50
+      args: [new AST.String({ value: "" })]
51
+    })
52
+  ]);
53
+});
54
+
55
+test("parses calls to define into a function definition", t => {
56
+  t.plan(1);
57
+  const tree = helpers.parse("(define plusOne (n) (+ n 1))");
58
+
59
+  t.deepEqual(tree, [
60
+    new AST.FunctionDefinition({
61
+      name: new AST.Identifier({ name: "plusOne" }),
62
+      parameters: [new AST.Identifier({ name: "n" })],
63
+      body: new AST.Application({
64
+        functionName: new AST.Identifier({ name: "+" }),
65
+        args: [new AST.Identifier({ name: "n" }), new AST.Number({ value: 1 })]
66
+      })
67
+    })
68
+  ]);
69
+});

+ 0
- 48
test/resolverTest.js View File

@@ -1,48 +0,0 @@
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
-});

+ 0
- 141
test/stdlibTest.js View File

@@ -1,141 +0,0 @@
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
-});

Loading…
Cancel
Save