123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- const AST = require("./ast");
-
- module.exports = class Resolver {
- constructor() {
- this.tree = [];
- this.context = {};
-
- this.stdlib = {
- if: (predicate, left, right) => {
- const resolvedPredicate = this.resolveNode(predicate);
-
- this.typecheck(resolvedPredicate, AST.Boolean);
-
- if (resolvedPredicate && resolvedPredicate.value === true) {
- return this.resolveNode(left);
- } else if (resolvedPredicate && resolvedPredicate.value === false) {
- return this.resolveNode(right);
- }
- },
- list: args => {
- let elements = [];
-
- args.forEach(arg => {
- elements.push(this.resolveNode(arg));
- });
-
- return new AST.List({
- elements: elements
- });
- },
- quote: value => {
- return new AST.Symbol({ value: value });
- },
- htmlElement: node => {
- let resolvedArgs = [];
-
- node.args.forEach(arg => {
- resolvedArgs.push(this.resolveNode(arg));
- });
-
- return new AST.Application({
- functionName: node.functionName,
- args: resolvedArgs
- });
- },
- "=": (left, right) => {
- this.typecheck(right, left.constructor);
-
- if (left.constructor.name === "List") {
- if (left.elements.length !== right.elements.length) {
- return new AST.Boolean({ value: false });
- }
-
- let equal = true;
-
- left.elements.forEach((el, i) => {
- this.typecheck(el.value, right.elements[i].constructor);
- if (el.value !== right.elements[i].value) {
- equal = false;
- }
- });
-
- return new AST.Boolean({ value: equal });
- }
-
- return new AST.Boolean({ value: left.value === right.value });
- },
- ">": (left, right) => {
- this.typecheck(left, AST.Number) && this.typecheck(right, AST.Number);
- return new AST.Boolean({ value: left.value > right.value });
- },
- "<": (left, right) => {
- this.typecheck(left, AST.Number) && this.typecheck(right, AST.Number);
- return new AST.Boolean({ value: left.value < right.value });
- },
- ">=": (left, right) => {
- this.typecheck(left, AST.Number) && this.typecheck(right, AST.Number);
- return new AST.Boolean({ value: left.value >= right.value });
- },
- "<=": (left, right) => {
- this.typecheck(left, AST.Number) && this.typecheck(right, AST.Number);
- return new AST.Boolean({ value: left.value <= right.value });
- },
- and: args => {
- let and = true;
-
- args.forEach(arg => {
- this.typecheck(arg, AST.Boolean);
- if (arg.value === false) {
- and = false;
- }
- });
-
- return new AST.Boolean({ value: and });
- },
- or: args => {
- let or = false;
-
- args.forEach(arg => {
- this.typecheck(arg, AST.Boolean);
- if (arg.value === true) {
- or = true;
- }
- });
-
- return new AST.Boolean({ value: or });
- },
- lambda: function(parameter, body) {
- console.log(parameter, body);
- }
- };
- }
-
- resolve(tree, context) {
- this.context = context;
-
- tree.forEach(node => {
- this.tree.push(this.resolveNode(node));
- });
-
- return this.tree;
- }
-
- resolveNode(node, context) {
- if (!context) {
- context = this.context;
- }
-
- switch (node.constructor.name) {
- case "Boolean":
- case "Number":
- case "String":
- return node;
- case "Attribute":
- return new AST.Attribute({
- name: node.name,
- value: this.resolveNode(node.value)
- });
- case "Identifier":
- return this.lookup(node.name);
- case "Application":
- if (this.stdlib.hasOwnProperty(node.functionName.name)) {
- const f = this.stdlib[node.functionName.name];
-
- if (["list", "and", "or"].includes(node.functionName.name)) {
- return f(node.args);
- }
-
- let resolvedArgs = [];
-
- if (node.functionName.name === "quote") {
- resolvedArgs = node.args;
- } else {
- node.args.forEach(arg => {
- resolvedArgs.push(this.resolveNode(arg));
- });
- }
-
- return f(...resolvedArgs);
- }
-
- return this.stdlib.htmlElement(node);
- }
- }
-
- lookup(name) {
- console.log(name);
- const result = this.context[name];
- return this.wrap(result);
- }
-
- wrap(value) {
- switch (value.constructor.name) {
- case "String":
- return new AST.String({ value: value });
- case "Number":
- return new AST.Number({ value: value });
- case "Array":
- return new AST.List({ elements: value.map(this.wrap) });
- }
- }
-
- typecheck(value, type) {
- console.log(value);
- console.log(type);
- if (value.constructor.name !== type.name) {
- throw `Type error: expected a ${type.name} but got a ${
- value.constructor.name
- }`;
- }
- }
- };
|