From 149174d0565f0a3e3e94f03fa9ebc6f8291dc3a4 Mon Sep 17 00:00:00 2001 From: namespace Date: Mon, 1 May 2023 14:27:03 +0200 Subject: [PATCH 1/7] WIP --- compiler.js | 181 ++++++++++++++++------------- evaluator.js | 306 ++++++++++++++++++++++++++----------------------- evil.js | 66 +++++++---- primitives.js | 116 +++++++++---------- repl.js | 130 +++++++++------------ run.js | 80 ++++++------- utils/debug.js | 13 +++ 7 files changed, 467 insertions(+), 425 deletions(-) create mode 100644 utils/debug.js diff --git a/compiler.js b/compiler.js index 88566b3..cd70ce7 100644 --- a/compiler.js +++ b/compiler.js @@ -1,112 +1,131 @@ -const { format } = require('./naming') -const { unread } = require('./reader') -const { parse } = require('./parser') -const { safeLookup } = require('./environment') -const { evaluateMacroDefinition, evaluate, expandMacro, isMacro } = require('./evaluator') -const primitives = require('./primitives') +const { format } = require("./naming"); +const { unread } = require("./reader"); +const { parse } = require("./parser"); +const { safeLookup } = require("./environment"); +const { + evaluateMacroDefinition, + evaluate, + expandMacro, + isMacro, +} = require("./evaluator"); +const primitives = require("./primitives"); //@TODO unuglify js //@TODO objects -function compile(node, env){ - switch(node.type){ - case "num": - case "str": - case "bool": return JSON.stringify(node.value, env) - case "quote": return compileQuote(node, env) - case "var": return node.value - case "assignment": return compileAssignment(node, env) - case "definition": return compileDefinition(node, env) - case "if": return compileIf(node, env) - case "let": return compileLet(node, env) - case "lambda": return compileLambda(node, env) - case "progn": return compileProgn(node, env) - case "macrodefinition": return compileMacroDefinition(node, env) - case "macroexpansion": return compileMacroExpansion(node, env) - case "application": return compileApplication(node, env) - } +function compile(node, env) { + switch (node.type) { + case "num": + case "str": + case "bool": + return JSON.stringify(node.value, env); + case "quote": + return compileQuote(node, env); + case "var": + return node.value; + case "assignment": + return compileAssignment(node, env); + case "definition": + return compileDefinition(node, env); + case "if": + return compileIf(node, env); + case "let": + return compileLet(node, env); + case "lambda": + return compileLambda(node, env); + case "progn": + return compileProgn(node, env); + case "macrodefinition": + return compileMacroDefinition(node, env); + case "macroexpansion": + return compileMacroExpansion(node, env); + case "application": + return compileApplication(node, env); + } } -function compileQuote(node){ - return JSON.stringify(node.value)//`"${node.value}"` +function compileQuote(node) { + return JSON.stringify(node.value); //`"${node.value}"` } -function compileDefinition(node, env){ - return `(${node.variable} = ${compile(node.value, env)})` +function compileDefinition(node, env) { + return `(${node.variable} = ${compile(node.value, env)})`; } -function compileAssignment(node, env){ - return `${node.variable} = ${compile(node.value, env)}` +function compileAssignment(node, env) { + return `${node.variable} = ${compile(node.value, env)}`; } -function compileIf(node, env){ - let js = `(${compile(node.condition, env)} ? ` - js += `${compile(node.body, env)} : ` - if(node.elseBody){ - js+= `${compile(node.elseBody, env)})` - } else { - js+= `undefined )` - } - return js +function compileIf(node, env) { + let js = `(${compile(node.condition, env)} ? `; + js += `${compile(node.body, env)} : `; + if (node.elseBody) { + js += `${compile(node.elseBody, env)})`; + } else { + js += `undefined )`; + } + return js; } -function compileLet(node, env){ - const values = node.values.map(n => compile(n, env)) - let js = `(function ` - js += `(${node.vars.join(',')})` - js += `{return ${compile(node.body, env)}})` - js += `(${values.join(',')})` - return js +function compileLet(node, env) { + const values = node.values.map((n) => compile(n, env)); + let js = `(function `; + js += `(${node.vars.join(",")})`; + js += `{return ${compile(node.body, env)}})`; + js += `(${values.join(",")})`; + return js; } -function compileLambda(node, env){ - let js = `(function ${node.name ? node.name : ''}` - js += `(${node.params.join(',')})` - js += `{return ${compile(node.body, env)}})` - return js +function compileLambda(node, env) { + let js = `(function ${node.name ? node.name : ""}`; + js += `(${node.params.join(",")})`; + js += `{return ${compile(node.body, env)}})`; + return js; } -function compileProgn(node, env){ - const sequence = node.nodes.map(n => compile(n, env)) - return `(${sequence.join(', ')})` +function compileProgn(node, env) { + const sequence = node.nodes.map((n) => compile(n, env)); + return `(${sequence.join(", ")})`; } -function compileMacroDefinition(node, env){ - evaluateMacroDefinition(node, env) - return '"[macrodefined]"' +function compileMacroDefinition(node, env) { + evaluateMacroDefinition(node, env); + return '"[macrodefined]"'; } -function compileMacroExpansion(node, env){ - const macro = evaluate(node.operator, env) - const [expanded] = expandMacro(macro, node.args, env) - return `'${unread(expanded)}'` +function compileMacroExpansion(node, env) { + const macro = evaluate(node.operator, env); + const [expanded] = expandMacro(macro, node.args, env); + return `'${unread(expanded)}'`; } -function compileApplication(node, env){ - // @TODO test macro inside macro - const operatorName = compile(node.operator, env) - const operator = safeLookup(operatorName, env) - if(operator && isMacro(operator)){ - const args = node._exp.slice(1) //@TODO abstract - const [expanded, scope] = expandMacro(operator, args, env) - return compile(parse(expanded, scope), scope) - } - let operands = node.operands.map(o => compile(o, env)) - operands = operands.map(o => typeof o === 'object' ? JSON.stringify(o) : o) - return `${format(operatorName)}(${operands.join(',')})` +function compileApplication(node, env) { + // @TODO test macro inside macro + const operatorName = compile(node.operator, env); + const operator = safeLookup(operatorName, env); + if (operator && isMacro(operator)) { + const args = node._exp.slice(1); //@TODO abstract + const [expanded, scope] = expandMacro(operator, args, env); + return compile(parse(expanded, scope), scope); + } + let operands = node.operands.map((o) => compile(o, env)); + operands = operands.map((o) => + typeof o === "object" ? JSON.stringify(o) : o + ); + return `${format(operatorName)}(${operands.join(",")})`; } // ======================================================= -function compilePrimitive(primitive){ - return primitive[1].toString() +function compilePrimitive(primitive) { + return primitive[1].toString(); } -function createCompilationPrefix(){ - let output = '' - for(let primitive of primitives){ - output += compilePrimitive(primitive) + ';\n' - } - return output +function createCompilationPrefix() { + let output = ""; + for (let primitive of primitives) { + output += compilePrimitive(primitive) + ";\n"; + } + return output; } -module.exports = { compile, createCompilationPrefix } +module.exports = { compile, createCompilationPrefix }; diff --git a/evaluator.js b/evaluator.js index e507b0d..930dc59 100644 --- a/evaluator.js +++ b/evaluator.js @@ -1,167 +1,183 @@ -const { isAtom, isUnquoted, isUnquoteSliced, parse } = require('./parser') -const { unread } = require('./reader') -const { lookupVariable, - defineVariable, - defineVariables, - setVariable, - extendEnvironment } = require('./environment') - -function evaluate(node, env){ - switch (node.type){ - case "num": - case "str": - case "bool": return node.value - case "quote": return node.value - case "syntaxquote": return evaluateSyntaxQuote(node, env) - case "var": return evaluateVariable(node, env) - case "assignment": return evaluateAssignment(node, env) - case "definition": return evaluateDefinition(node, env) - case "if": return evaluateIf(node, env) - case "let": return evaluateLet(node, env) - case "lambda": return evaluateLambda(node, env) - case "progn": return evaluateProgn(node, env) - case "macrodefinition": return evaluateMacroDefinition(node, env) - case "macroexpansion": return evaluateMacroExpansion(node, env) - case "application": return evaluateApplication(node, env) - } -} - -function evaluateVariable(node, env){ - return lookupVariable(node.value, env) -} - -function evaluateAssignment(node, env){ - const value = evaluate(node.value, env) - return setVariable(node.variable, value, env) -} - -function evaluateDefinition(node, env){ - const value = evaluate(node.value, env) - return defineVariable(node.variable, value, env) -} - -function evaluateIf(node, env){ - if(evaluate(node.condition, env)){ - return evaluate(node.body, env) - } else { - if(node.elseBody) return evaluate(node.elseBody, env) - } -} - -function evaluateLet(node, env){ - const scope = extendEnvironment(env) - for(let binding of node.bindings){ - evaluate({type: 'definition', variable: binding[0], value: binding[1]}, scope) - } - return evaluate(node.body, scope) -} - -function evaluateProgn(node, env){ - return node.nodes.map(n => evaluate(n, env)).slice(-1)[0] -} - -function evaluateLambda(node, env){ - const body = node.body - const params = node.params - return (...args) => { - const scope = createScope(params, args, env) - const result = evaluate(body, scope) - return result - } -} - -function evaluateApplication(node, env){ - const operator = evaluate(node.operator, env) - if(isMacro(operator)){ - const macro = evaluate(node.operator, env) - const args = node._exp.slice(1) //@TODO abstract - const [expanded, scope] = expandMacro(macro, args, env) - return evaluate(parse(expanded), scope) - } - - const args = node.operands.map(operand => evaluate(operand, env)) - return operator(...args) -} - -function evaluateMacroDefinition(node, env){ - const macro = makeMacro(node.params, node.body) - return defineVariable(node.name, macro, env) -} - -function evaluateMacroExpansion(node, env){ - const macro = evaluate(node.operator, env) - const [expanded] = expandMacro(macro, node.args, env) - return unread(expanded) -} - -function evaluateSyntaxQuote(node, env){ - const result = recursiveSyntaxUnquote(node.value, env) //@TODO if is atom... - return result +const { isAtom, isUnquoted, isUnquoteSliced, parse } = require("./parser"); +const { unread } = require("./reader"); +const { + lookupVariable, + defineVariable, + defineVariables, + setVariable, + extendEnvironment, +} = require("./environment"); + +function evaluate(node, env) { + switch (node.type) { + case "num": + case "str": + case "bool": + case "quote": + return node.value; + case "syntaxquote": + return evaluateSyntaxQuote(node, env); + case "var": + return evaluateVariable(node, env); + case "assignment": + return evaluateAssignment(node, env); + case "definition": + return evaluateDefinition(node, env); + case "if": + return evaluateIf(node, env); + case "let": + return evaluateLet(node, env); + case "lambda": + return evaluateLambda(node, env); + case "progn": + return evaluateProgn(node, env); + case "macrodefinition": + return evaluateMacroDefinition(node, env); + case "macroexpansion": + return evaluateMacroExpansion(node, env); + case "application": + return evaluateApplication(node, env); + } +} + +function evaluateVariable(node, env) { + return lookupVariable(node.value, env); +} + +function evaluateAssignment(node, env) { + const value = evaluate(node.value, env); + return setVariable(node.variable, value, env); +} + +function evaluateDefinition(node, env) { + const value = evaluate(node.value, env); + return defineVariable(node.variable, value, env); +} + +function evaluateIf(node, env) { + if (evaluate(node.condition, env)) { + return evaluate(node.body, env); + } else { + if (node.elseBody) return evaluate(node.elseBody, env); + } +} + +function evaluateLet(node, env) { + const scope = extendEnvironment(env); + for (let binding of node.bindings) { + evaluate( + { type: "definition", variable: binding[0], value: binding[1] }, + scope + ); + } + return evaluate(node.body, scope); +} + +function evaluateProgn(node, env) { + return node.nodes.map((n) => evaluate(n, env)).slice(-1)[0]; +} + +function evaluateLambda(node, env) { + const body = node.body; + const params = node.params; + return (...args) => { + const scope = createScope(params, args, env); + const result = evaluate(body, scope); + return result; + }; +} + +function evaluateApplication(node, env) { + const operator = evaluate(node.operator, env); + if (isMacro(operator)) { + const macro = evaluate(node.operator, env); + const args = node._exp.slice(1); //@TODO abstract + const [expanded, scope] = expandMacro(macro, args, env); + return evaluate(parse(expanded), scope); + } + + const args = node.operands.map((operand) => evaluate(operand, env)); + return operator(...args); +} + +function evaluateMacroDefinition(node, env) { + const macro = makeMacro(node.params, node.body); + return defineVariable(node.name, macro, env); +} + +function evaluateMacroExpansion(node, env) { + const macro = evaluate(node.operator, env); + const [expanded] = expandMacro(macro, node.args, env); + return unread(expanded); +} + +function evaluateSyntaxQuote(node, env) { + const result = recursiveSyntaxUnquote(node.value, env); //@TODO if is atom... + return result; } // Helpers // =============================================================== -function expandMacro(macro, args, env){ - const body = getMacroBody(macro) - const params = getMacroParams(macro) - const scope = createScope(params, args, env) - const expanded = body.map(n => evaluate(n, scope)).slice(-1)[0] - return [expanded, scope] +function expandMacro(macro, args, env) { + const body = getMacroBody(macro); + const params = getMacroParams(macro); + const scope = createScope(params, args, env); + const expanded = body.map((n) => evaluate(n, scope)).slice(-1)[0]; + return [expanded, scope]; } -function makeMacro(parameters, body){ - return [ 'macro', parameters, body] +function makeMacro(parameters, body) { + return ["macro", parameters, body]; } -function getMacroBody(exp){ - return exp[2] +function getMacroBody(exp) { + return exp[2]; } -function getMacroParams(exp){ - return exp[1] +function getMacroParams(exp) { + return exp[1]; } -function isMacro(exp){ - return exp[0] === 'macro' +function isMacro(exp) { + return exp[0] === "macro"; } -function syntaxUnquote(exp, env){ - return evaluate(parse(exp[1]), env) +function syntaxUnquote(exp, env) { + return evaluate(parse(exp[1]), env); } -function recursiveSyntaxUnquote(exps, env){ - let output = [] - for(let exp of exps){ - if(!isAtom(exp)) { - if(isUnquoted(exp)){ - output.push(syntaxUnquote(exp, env)) - } else if(isUnquoteSliced(exp)){ - output.push(...syntaxUnquote(exp, env)) - } else { - output.push(recursiveSyntaxUnquote(exp, env)) - } - } - else output.push(exp) - } - return output +function recursiveSyntaxUnquote(exps, env) { + let output = []; + for (let exp of exps) { + if (!isAtom(exp)) { + if (isUnquoted(exp)) { + output.push(syntaxUnquote(exp, env)); + } else if (isUnquoteSliced(exp)) { + output.push(...syntaxUnquote(exp, env)); + } else { + output.push(recursiveSyntaxUnquote(exp, env)); + } + } else output.push(exp); + } + return output; } -function createScope(params, args, env){ - ;[params, args] = destructureArgs(params, args) - const scope = extendEnvironment(env) - defineVariables(params, args, scope) - return scope +function createScope(params, args, env) { + [params, args] = destructureArgs(params, args); + const scope = extendEnvironment(env); + defineVariables(params, args, scope); + return scope; } -function destructureArgs(params, args){ - const restIndex = params.indexOf('&') - if(restIndex >= 0){ - const restArgs = args.slice(restIndex) - outputParams = [...params.slice(0, restIndex), params[restIndex + 1]] - outputArgs = [...args.slice(0, restIndex), restArgs] - return [outputParams, outputArgs] - } - return [params, args] +function destructureArgs(params, args) { + const restIndex = params.indexOf("&"); + if (restIndex >= 0) { + const restArgs = args.slice(restIndex); + outputParams = [...params.slice(0, restIndex), params[restIndex + 1]]; + outputArgs = [...args.slice(0, restIndex), restArgs]; + return [outputParams, outputArgs]; + } + return [params, args]; } -module.exports = { evaluate, evaluateMacroDefinition, expandMacro, isMacro } +module.exports = { evaluate, evaluateMacroDefinition, expandMacro, isMacro }; diff --git a/evil.js b/evil.js index e98df13..48bea5d 100644 --- a/evil.js +++ b/evil.js @@ -1,27 +1,47 @@ -const { createEnvironment, defineVariable } = require('./environment') -const primitives = require('./primitives') -const { parse } = require('./parser') -const { evaluate } = require('./evaluator') -const { read, InputStream } = require('./reader') -const fs = require('fs') +const { createEnvironment, defineVariable } = require("./environment"); +const primitives = require("./primitives"); +const { parse } = require("./parser"); +const { evaluate } = require("./evaluator"); +const { read, InputStream } = require("./reader"); +const { hideBin } = require("yargs/helpers"); +const yargs = require("yargs/yargs"); +const fs = require("fs"); -function createGlobalEnvironment(){ - const env = createEnvironment() - for(let primitive of primitives){ - defineVariable(primitive[0], primitive[1], env) - } - defineVariable('console', console, env) - return env -} +const argv = yargs(hideBin(process.argv)).argv; + +console.log("args:", argv); + +const sourceFile = argv._[0]; +const debugMode = argv.debug; +const compiledEval = argv.compiledEval; -const path = process.argv[2] -let content = fs.readFileSync(path).toString() -content = content.replace(/;.*/g, "") -content = content.replace(/\r\n/g, " ") -content = content.replace(/\n/g, " ") -const input = `(progn ${content})` +function createGlobalEnvironment() { + const env = createEnvironment(); + for (let primitive of primitives) { + defineVariable(primitive[0], primitive[1], env); + } + defineVariable("console", console, env); + try { + defineVariable("window", window, env); + } catch (err) {} -const env = createGlobalEnvironment() + try { + defineVariable("global", global, env); + } catch (err) {} + return env; +} + +let content = fs.readFileSync(sourceFile).toString(); +content = content.replace(/;.*/g, ""); +content = content.replace(/\r\n/g, " "); +content = content.replace(/\n/g, " "); +const input = `(progn ${content})`; +const env = createGlobalEnvironment(); +const ast = parse(read(InputStream(input)), env); -const ast = parse(read(InputStream(input)), env) -evaluate(ast, env) +if (compiledEval) { + const compilation = createCompilationPrefix() + compile(ast, env); + eval(`(function(){${compilation}}())`); +} else { + evaluate(ast, env); +} diff --git a/primitives.js b/primitives.js index 2a6c147..feb6bbb 100644 --- a/primitives.js +++ b/primitives.js @@ -1,25 +1,26 @@ -const { executeProcedure } = require('./interpreter') +// const { executeProcedure } = require('./interpreter') // @TODO: optimize, hardcode parameters until 5 or 6, if more use reduce -const primitive = (key, func) => ([key, func]) -function add(...args){ - return args.reduce((acc, x) => acc + x, 0) +const primitive = (key, func) => [key, func]; + +function add(...args) { + return args.reduce((acc, x) => acc + x, 0); } -function sub(...args){ - return args.reduce((acc, x) => acc - x, args[0] * 2) +function sub(...args) { + return args.reduce((acc, x) => acc - x, args[0] * 2); } -function lt(a, b){ - return a < b +function lt(a, b) { + return a < b; } -function gt(a, b){ - return a > b +function gt(a, b) { + return a > b; } -function list(...args){ - return args +function list(...args) { + return args; } // const mul = (args) => args.reduce((acc, x) => acc * x, 1) @@ -30,30 +31,30 @@ function list(...args){ // const gte = ([a, b]) => a >= b // const lte = ([a, b]) => a <= b // const not = ([a]) => !a -function not(a){ - return !a +function not(a) { + return !a; } // const eq = (args) => args.reduce((acc, x) => acc === x, args[0]) // const and = (args) => args.reduce((acc, x) => acc && x, true) // const or = (args) => args.reduce((acc, x) => acc || x, false) // const inc = ([a]) => a + 1 -function print (...args) { - return console.log(...args) +function print(...args) { + return console.log(...args); } -function get(seq, accessor){ - return seq[accessor] +function get(seq, accessor) { + return seq[accessor]; } // const pprint = (args) => console.log(JSON.stringify(args)) // const assoc = ([coll, k, v]) => {coll[k] = v; return coll} // const obj = () => ({}) // const list = (args) => args -function dot(key, coll, ...args){ - return coll[key](...args) +function dot(key, coll, ...args) { + return coll[key](...args); } -function map(func, coll){ - return coll.map(func) +function map(func, coll) { + return coll.map(func); } // const filter = ([proc, coll]) => coll.filter(arg => executeProcedure(proc, [arg])) // const reduce = ([proc, coll, initial]) => coll.reduce((acc, x) => executeProcedure(proc, [acc, x]), initial) @@ -64,43 +65,42 @@ function map(func, coll){ // } // } -function partition (coll, size) { - coll = [...coll] - const output = [] - while(coll.length > 0){ - output.push(coll.splice(0, size)) - } - return output +function partition(coll, size) { + coll = [...coll]; + const output = []; + while (coll.length > 0) { + output.push(coll.splice(0, size)); + } + return output; } const primitives = [ - primitive('+', add), - primitive('-', sub), - // primitive('*', mul), - // primitive('/', div), - // primitive('%', mod), - primitive('>', gt), - primitive('<', lt), - // primitive('>=', gte), - // primitive('<=', lte), - // primitive('eq', eq), - primitive('not', not), - // primitive('and', and), - // primitive('or', or), - // primitive('inc', inc), - primitive('print', print), - // primitive('pprint', pprint), - primitive('get', get), - // primitive('assoc', assoc), - // primitive('obj', obj), - primitive('list', list), - primitive('.', dot), - primitive('map', map), - // primitive('filter', filter), - // primitive('reduce', reduce), - // primitive('each', each), - primitive('partition', partition) -] - + primitive("+", add), + primitive("-", sub), + // primitive('*', mul), + // primitive('/', div), + // primitive('%', mod), + primitive(">", gt), + primitive("<", lt), + // primitive('>=', gte), + // primitive('<=', lte), + // primitive('eq', eq), + primitive("not", not), + // primitive('and', and), + // primitive('or', or), + // primitive('inc', inc), + primitive("print", print), + // primitive('pprint', pprint), + primitive("get", get), + // primitive('assoc', assoc), + // primitive('obj', obj), + primitive("list", list), + primitive(".", dot), + primitive("map", map), + // primitive('filter', filter), + // primitive('reduce', reduce), + // primitive('each', each), + primitive("partition", partition), +]; -module.exports = primitives +module.exports = primitives; diff --git a/repl.js b/repl.js index c1e5340..11ce648 100644 --- a/repl.js +++ b/repl.js @@ -1,91 +1,71 @@ -const { createEnvironment, defineVariable } = require('./environment') -const primitives = require('./primitives') -// const { evaluate } = require('./interpreter') -const { format } = require('./naming') -const { evaluate } = require('./evaluator') -const { compile } = require('./compiler') -const { parse } = require('./parser') -const { read, InputStream } = require('./reader') -const readline = require("readline") +const { createEnvironment, defineVariable } = require("./environment"); +const primitives = require("./primitives"); +const { format } = require("./naming"); +const { evaluate } = require("./evaluator"); +const { compile } = require("./compiler"); +const { parse } = require("./parser"); +const { read, InputStream } = require("./reader"); +const readline = require("readline"); -let quit = false +let quit = false; const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout -}) + input: process.stdin, + output: process.stdout, +}); -function pprint(msg){ - console.log(JSON.stringify(msg, null, 2)) +function pprint(msg) { + console.log(JSON.stringify(msg, null, 2)); } -function prompt(question){ - return new Promise((resolve, reject) => { - rl.question(question, input => { - if(input === 'q') quit = true - resolve(input) - }) - }) +function prompt(question) { + return new Promise((resolve, reject) => { + rl.question(question, (input) => { + if (input === "q") quit = true; + resolve(input); + }); + }); } -function createGlobalEnvironment(){ - const env = createEnvironment() - for(let primitive of primitives){ - defineVariable(primitive[0], primitive[1], env) - } - defineVariable('console', console, env) - return env +function createGlobalEnvironment() { + const env = createEnvironment(); + for (let primitive of primitives) { + defineVariable(primitive[0], primitive[1], env); + } + defineVariable("console", console, env); + return env; } -function compilePrimitive(primitive){ - return primitive[1].toString() +function compilePrimitive(primitive) { + return primitive[1].toString(); } -function createCompilationPrefix(){ - let output = '' - for(let primitive of primitives){ - output += compilePrimitive(primitive) + ';\n' - } - return output +function createCompilationPrefix() { + let output = ""; + for (let primitive of primitives) { + output += compilePrimitive(primitive) + ";\n"; + } + return output; } -async function launch(){ - const env = createGlobalEnvironment() - const prefix = createCompilationPrefix() - - while(!quit){ - try{ - const input = await prompt('> ') - if(quit) break - const ast = parse(read(InputStream(input)), env) - console.log("") - console.log("") - console.log("") - console.log("AST") - console.log("================================") - pprint(ast) - - console.log("") - console.log("COMPILATION") - console.log("================================") - const result = prefix + '\n' + compile(ast) - console.log(result) +async function launch(debug = false) { + const env = createGlobalEnvironment(); + const prefix = createCompilationPrefix(); - // const result = evaluate(ast, env) - console.log("") - console.log("EVALUATION") - console.log("================================") - console.log(evaluate(ast, env)) - - console.log("") - console.log("JS EVAL") - console.log("================================") - console.log(eval(result)) - - } catch(err){ - console.log(err) - } + while (!quit) { + try { + const input = await prompt("> "); + if (quit) break; + const ast = parse(read(InputStream(input)), env); + const compilation = prefix + "\n" + compile(ast, env); + // console.log("argv2", argv); + if (argv.debug) { + verboseCompiledEvaluation(ast, compilation, () => eval(compilation)); + } else { + eval(compilation); + } + } catch (err) { + console.log(err); } - rl.close() + } + rl.close(); } - -launch() diff --git a/run.js b/run.js index f086789..496e112 100644 --- a/run.js +++ b/run.js @@ -1,49 +1,43 @@ -const { createEnvironment, defineVariable } = require('./environment') -const primitives = require('./primitives') -const { compile, createCompilationPrefix } = require('./compiler') -const { parse } = require('./parser') -const { read, InputStream } = require('./reader') -const fs = require('fs') +const { createEnvironment, defineVariable } = require("./environment"); +const primitives = require("./primitives"); +const { compile, createCompilationPrefix } = require("./compiler"); +const { parse } = require("./parser"); +const { read, InputStream } = require("./reader"); +const fs = require("fs"); -// function pprint(msg){ -// console.log(JSON.stringify(msg, null, 2)) -// } - -const path = process.argv[2] -let content = fs.readFileSync(path).toString() -content = content.replace(/;.*/g, "") -content = content.replace(/\r\n/g, " ") -content = content.replace(/\n/g, " ") -const input = `(progn ${content})` +const path = process.argv[2]; +let content = fs.readFileSync(path).toString(); +content = content.replace(/;.*/g, ""); +content = content.replace(/\r\n/g, " "); +content = content.replace(/\n/g, " "); +const input = `(progn ${content})`; //@ TODO createCompilationEnvironment instead -function createGlobalEnvironment(){ - const env = createEnvironment() - for(let primitive of primitives){ - defineVariable(primitive[0], primitive[1], env) - } - defineVariable('console', console, env) - return env +function createGlobalEnvironment() { + const env = createEnvironment(); + for (let primitive of primitives) { + defineVariable(primitive[0], primitive[1], env); + } + defineVariable("console", console, env); + return env; } -console.time('read') -const env = createGlobalEnvironment() -const r = read(InputStream(input)) -console.timeEnd('read') -console.time('parse') -const ast = parse(r) -console.timeEnd('parse') -console.time('compile') -const compilation = createCompilationPrefix() + compile(ast, env) -console.timeEnd('compile') -console.log('') -console.log('COMPILATION OUTPUT') -console.log('=================================') -console.log(`(function(){${compilation}}())`) +console.time("read"); +const env = createGlobalEnvironment(); +const r = read(InputStream(input)); +console.timeEnd("read"); +console.time("parse"); +const ast = parse(r); +console.timeEnd("parse"); +console.time("compile"); +const compilation = createCompilationPrefix() + compile(ast, env); +console.timeEnd("compile"); +console.log("\nCOMPILATION OUTPUT"); +console.log("================================="); +console.log(`(function(){${compilation}}())`); -console.log('') -console.log('JS EVAL') -console.log('=================================') -console.time('eval') -eval(`(function(){${compilation}}())`) //@TODO where to put this -console.timeEnd('eval') +console.log("\nJS EVAL"); +console.log("================================="); +console.time("eval"); +eval(`(function(){${compilation}}())`); //@TODO where to put this +console.timeEnd("eval"); diff --git a/utils/debug.js b/utils/debug.js new file mode 100644 index 0000000..3c78982 --- /dev/null +++ b/utils/debug.js @@ -0,0 +1,13 @@ +export function verboseCompiledEvaluation(ast, compilation, evaluationFunc) { + console.log("\n\n\nAST"); + console.log("================================"); + pprint(ast); + + console.log("\nCOMPILATION"); + console.log("================================"); + console.log(compilation); + + console.log("\nJS EVAL"); + console.log("================================"); + console.log(evaluationFunc()); +} From 3b8c99ba99f01f71744c34bdd43987b20a88daaf Mon Sep 17 00:00:00 2001 From: namespace Date: Mon, 1 May 2023 20:42:14 +0200 Subject: [PATCH 2/7] WIP --- .gitignore | 1 + compiler.js | 18 +++++++- evil.js | 57 +++++++++++--------------- package.json | 5 +++ primitives.js | 7 +--- repl.js | 50 +++++++++-------------- utils.js | 8 +++- utils/debug.js | 6 ++- yarn.lock | 109 +++++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 188 insertions(+), 73 deletions(-) create mode 100644 package.json create mode 100644 yarn.lock diff --git a/.gitignore b/.gitignore index 4dbc4d1..9c44e71 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ *.log tmp/ *report* +node_modules diff --git a/compiler.js b/compiler.js index cd70ce7..edde9f5 100644 --- a/compiler.js +++ b/compiler.js @@ -1,7 +1,8 @@ const { format } = require("./naming"); const { unread } = require("./reader"); const { parse } = require("./parser"); -const { safeLookup } = require("./environment"); +const { attempt } = require("./utils"); +const { safeLookup, createEnvironment, defineVariable } = require("./environment"); const { evaluateMacroDefinition, evaluate, @@ -116,6 +117,7 @@ function compileApplication(node, env) { } // ======================================================= + function compilePrimitive(primitive) { return primitive[1].toString(); } @@ -128,4 +130,16 @@ function createCompilationPrefix() { return output; } -module.exports = { compile, createCompilationPrefix }; +function createCompilationEnvironment(){ + + const env = createEnvironment(); + for (let primitive of primitives) { + defineVariable(primitive[0], primitive[1], env); + } + defineVariable("console", console, env); + attempt(() => defineVariable("window", window, env)); + attempt(() => defineVariable("global", global, env)); + return env; +} + +module.exports = { compile, createCompilationPrefix, createCompilationEnvironment }; diff --git a/evil.js b/evil.js index 48bea5d..fb90abd 100644 --- a/evil.js +++ b/evil.js @@ -2,46 +2,37 @@ const { createEnvironment, defineVariable } = require("./environment"); const primitives = require("./primitives"); const { parse } = require("./parser"); const { evaluate } = require("./evaluator"); +const { repl } = require("./repl"); const { read, InputStream } = require("./reader"); const { hideBin } = require("yargs/helpers"); +const { createCompilationEnvironment } = require("./compiler"); const yargs = require("yargs/yargs"); const fs = require("fs"); -const argv = yargs(hideBin(process.argv)).argv; +process.argv = yargs(hideBin(process.argv)).argv; +const sourceFile = process.argv._[0]; +const debugMode = process.argv.debug; +const compiledEval = process.argv.compiledEval; +const env = createCompilationEnvironment(); -console.log("args:", argv); - -const sourceFile = argv._[0]; -const debugMode = argv.debug; -const compiledEval = argv.compiledEval; - -function createGlobalEnvironment() { - const env = createEnvironment(); - for (let primitive of primitives) { - defineVariable(primitive[0], primitive[1], env); - } - defineVariable("console", console, env); - try { - defineVariable("window", window, env); - } catch (err) {} - - try { - defineVariable("global", global, env); - } catch (err) {} - return env; +if(sourceFile) { + compileFile(sourceFile) +} else { + repl(env); } -let content = fs.readFileSync(sourceFile).toString(); -content = content.replace(/;.*/g, ""); -content = content.replace(/\r\n/g, " "); -content = content.replace(/\n/g, " "); -const input = `(progn ${content})`; -const env = createGlobalEnvironment(); -const ast = parse(read(InputStream(input)), env); +function compileFile(sourceFile) { + let content = fs.readFileSync(sourceFile).toString(); + content = content.replace(/;.*/g, ""); + content = content.replace(/\r\n/g, " "); + content = content.replace(/\n/g, " "); + const input = `(progn ${content})`; + const ast = parse(read(InputStream(input)), env); -if (compiledEval) { - const compilation = createCompilationPrefix() + compile(ast, env); - eval(`(function(){${compilation}}())`); -} else { - evaluate(ast, env); + if (compiledEval) { + const compilation = createCompilationPrefix() + compile(ast, env); + eval(`(function(){${compilation}}())`); + } else { + evaluate(ast, env); + } } diff --git a/package.json b/package.json new file mode 100644 index 0000000..4510543 --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "yargs": "^17.7.2" + } +} diff --git a/primitives.js b/primitives.js index feb6bbb..5c253b6 100644 --- a/primitives.js +++ b/primitives.js @@ -1,8 +1,3 @@ -// const { executeProcedure } = require('./interpreter') - -// @TODO: optimize, hardcode parameters until 5 or 6, if more use reduce -const primitive = (key, func) => [key, func]; - function add(...args) { return args.reduce((acc, x) => acc + x, 0); } @@ -74,6 +69,8 @@ function partition(coll, size) { return output; } +const primitive = (key, func) => [key, func]; + const primitives = [ primitive("+", add), primitive("-", sub), diff --git a/repl.js b/repl.js index 11ce648..e76d2f9 100644 --- a/repl.js +++ b/repl.js @@ -2,16 +2,13 @@ const { createEnvironment, defineVariable } = require("./environment"); const primitives = require("./primitives"); const { format } = require("./naming"); const { evaluate } = require("./evaluator"); -const { compile } = require("./compiler"); +const { compile, createCompilationEnvironment, createCompilationPrefix } = require("./compiler"); const { parse } = require("./parser"); const { read, InputStream } = require("./reader"); const readline = require("readline"); let quit = false; -const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, -}); +let rl function pprint(msg) { console.log(JSON.stringify(msg, null, 2)); @@ -26,29 +23,21 @@ function prompt(question) { }); } -function createGlobalEnvironment() { - const env = createEnvironment(); - for (let primitive of primitives) { - defineVariable(primitive[0], primitive[1], env); +function compileAndEval(ast, env, prefix){ + const compilation = prefix + "\n" + compile(ast, env); + if (process.argv.debug) { + return verboseCompiledEvaluation(ast, compilation, () => eval(compilation)); + } else { + return eval(compilation); } - defineVariable("console", console, env); - return env; } -function compilePrimitive(primitive) { - return primitive[1].toString(); -} - -function createCompilationPrefix() { - let output = ""; - for (let primitive of primitives) { - output += compilePrimitive(primitive) + ";\n"; - } - return output; -} +async function repl(env, debug = false) { + rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); -async function launch(debug = false) { - const env = createGlobalEnvironment(); const prefix = createCompilationPrefix(); while (!quit) { @@ -56,16 +45,15 @@ async function launch(debug = false) { const input = await prompt("> "); if (quit) break; const ast = parse(read(InputStream(input)), env); - const compilation = prefix + "\n" + compile(ast, env); - // console.log("argv2", argv); - if (argv.debug) { - verboseCompiledEvaluation(ast, compilation, () => eval(compilation)); - } else { - eval(compilation); - } + const result = compileAndEval(ast, env, prefix) + console.log(result) } catch (err) { console.log(err); } } rl.close(); } + +module.exports = { + repl +} diff --git a/utils.js b/utils.js index 93ad8b7..5e9df75 100644 --- a/utils.js +++ b/utils.js @@ -11,4 +11,10 @@ function recursiveMap(func, array){ return output } -module.exports = { isSurroundedBy, recursiveMap } +function attempt(func){ + try { + func() + } catch (err) {} +} + +module.exports = { isSurroundedBy, recursiveMap, attempt } diff --git a/utils/debug.js b/utils/debug.js index 3c78982..3677957 100644 --- a/utils/debug.js +++ b/utils/debug.js @@ -1,4 +1,4 @@ -export function verboseCompiledEvaluation(ast, compilation, evaluationFunc) { +function verboseCompiledEvaluation(ast, compilation, evaluationFunc) { console.log("\n\n\nAST"); console.log("================================"); pprint(ast); @@ -11,3 +11,7 @@ export function verboseCompiledEvaluation(ast, compilation, evaluationFunc) { console.log("================================"); console.log(evaluationFunc()); } + +module.exports = { + verboseCompiledEvaluation: verboseCompiledEvaluation +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..28f8bd2 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,109 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" From 0b2ad7aa1e9a70130e048bcfc0b830b210438ee5 Mon Sep 17 00:00:00 2001 From: namespace Date: Mon, 1 May 2023 20:46:20 +0200 Subject: [PATCH 3/7] WIP --- run.js | 43 ---------------------------- compiler.js => src/compiler.js | 0 environment.js => src/environment.js | 0 evaluator.js => src/evaluator.js | 0 evil.js => src/evil.js | 0 naming.js => src/naming.js | 0 parser.js => src/parser.js | 0 primitives.js => src/primitives.js | 0 reader.js => src/reader.js | 0 repl.js => src/repl.js | 0 utils.js => src/utils.js | 0 {utils => src/utils}/debug.js | 0 12 files changed, 43 deletions(-) delete mode 100644 run.js rename compiler.js => src/compiler.js (100%) rename environment.js => src/environment.js (100%) rename evaluator.js => src/evaluator.js (100%) rename evil.js => src/evil.js (100%) rename naming.js => src/naming.js (100%) rename parser.js => src/parser.js (100%) rename primitives.js => src/primitives.js (100%) rename reader.js => src/reader.js (100%) rename repl.js => src/repl.js (100%) rename utils.js => src/utils.js (100%) rename {utils => src/utils}/debug.js (100%) diff --git a/run.js b/run.js deleted file mode 100644 index 496e112..0000000 --- a/run.js +++ /dev/null @@ -1,43 +0,0 @@ -const { createEnvironment, defineVariable } = require("./environment"); -const primitives = require("./primitives"); -const { compile, createCompilationPrefix } = require("./compiler"); -const { parse } = require("./parser"); -const { read, InputStream } = require("./reader"); -const fs = require("fs"); - -const path = process.argv[2]; -let content = fs.readFileSync(path).toString(); -content = content.replace(/;.*/g, ""); -content = content.replace(/\r\n/g, " "); -content = content.replace(/\n/g, " "); -const input = `(progn ${content})`; - -//@ TODO createCompilationEnvironment instead -function createGlobalEnvironment() { - const env = createEnvironment(); - for (let primitive of primitives) { - defineVariable(primitive[0], primitive[1], env); - } - defineVariable("console", console, env); - return env; -} - -console.time("read"); -const env = createGlobalEnvironment(); -const r = read(InputStream(input)); -console.timeEnd("read"); -console.time("parse"); -const ast = parse(r); -console.timeEnd("parse"); -console.time("compile"); -const compilation = createCompilationPrefix() + compile(ast, env); -console.timeEnd("compile"); -console.log("\nCOMPILATION OUTPUT"); -console.log("================================="); -console.log(`(function(){${compilation}}())`); - -console.log("\nJS EVAL"); -console.log("================================="); -console.time("eval"); -eval(`(function(){${compilation}}())`); //@TODO where to put this -console.timeEnd("eval"); diff --git a/compiler.js b/src/compiler.js similarity index 100% rename from compiler.js rename to src/compiler.js diff --git a/environment.js b/src/environment.js similarity index 100% rename from environment.js rename to src/environment.js diff --git a/evaluator.js b/src/evaluator.js similarity index 100% rename from evaluator.js rename to src/evaluator.js diff --git a/evil.js b/src/evil.js similarity index 100% rename from evil.js rename to src/evil.js diff --git a/naming.js b/src/naming.js similarity index 100% rename from naming.js rename to src/naming.js diff --git a/parser.js b/src/parser.js similarity index 100% rename from parser.js rename to src/parser.js diff --git a/primitives.js b/src/primitives.js similarity index 100% rename from primitives.js rename to src/primitives.js diff --git a/reader.js b/src/reader.js similarity index 100% rename from reader.js rename to src/reader.js diff --git a/repl.js b/src/repl.js similarity index 100% rename from repl.js rename to src/repl.js diff --git a/utils.js b/src/utils.js similarity index 100% rename from utils.js rename to src/utils.js diff --git a/utils/debug.js b/src/utils/debug.js similarity index 100% rename from utils/debug.js rename to src/utils/debug.js From 17ce5274d54a50ab602e9b19ed64fd7a8b8a788b Mon Sep 17 00:00:00 2001 From: namespace Date: Tue, 9 May 2023 14:56:52 +0200 Subject: [PATCH 4/7] refactor WIP --- src/compiler.js | 2 +- src/evil.js | 23 +++++++++++++++-------- src/parser.js | 2 +- src/reader.js | 2 +- src/{ => utils}/utils.js | 0 test.lisp | 2 +- 6 files changed, 19 insertions(+), 12 deletions(-) rename src/{ => utils}/utils.js (100%) diff --git a/src/compiler.js b/src/compiler.js index edde9f5..a3e8431 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -1,7 +1,7 @@ const { format } = require("./naming"); const { unread } = require("./reader"); const { parse } = require("./parser"); -const { attempt } = require("./utils"); +const { attempt } = require("./utils/utils"); const { safeLookup, createEnvironment, defineVariable } = require("./environment"); const { evaluateMacroDefinition, diff --git a/src/evil.js b/src/evil.js index fb90abd..caef596 100644 --- a/src/evil.js +++ b/src/evil.js @@ -15,18 +15,25 @@ const debugMode = process.argv.debug; const compiledEval = process.argv.compiledEval; const env = createCompilationEnvironment(); -if(sourceFile) { - compileFile(sourceFile) -} else { - repl(env); -} +// if(sourceFile) { +// evalFile(sourceFile) +// } else { +// repl(env); +// } -function compileFile(sourceFile) { - let content = fs.readFileSync(sourceFile).toString(); +function preprocessFile(file){ + let content = fs.readFileSync(file).toString(); content = content.replace(/;.*/g, ""); content = content.replace(/\r\n/g, " "); content = content.replace(/\n/g, " "); - const input = `(progn ${content})`; + return `(progn ${content})`; +} + +preprocessFile("../test.lisp"); + + +function evalFile(sourceFile) { + const input = preprocessFile(sourceFile); const ast = parse(read(InputStream(input)), env); if (compiledEval) { diff --git a/src/parser.js b/src/parser.js index 46b9749..085f078 100644 --- a/src/parser.js +++ b/src/parser.js @@ -1,4 +1,4 @@ -const { isSurroundedBy } = require('./utils') +const { isSurroundedBy } = require('./utils/utils') // DOMAIN NOTES // ================================ diff --git a/src/reader.js b/src/reader.js index a0a1048..85465af 100644 --- a/src/reader.js +++ b/src/reader.js @@ -1,4 +1,4 @@ -const { recursiveMap } = require('./utils') +const { recursiveMap } = require('./utils/utils') let listOverridesPending = 0 diff --git a/src/utils.js b/src/utils/utils.js similarity index 100% rename from src/utils.js rename to src/utils/utils.js diff --git a/test.lisp b/test.lisp index 6b20d59..5e2365f 100644 --- a/test.lisp +++ b/test.lisp @@ -33,6 +33,6 @@ (print "let works" a) (print "for multiple parameters" b)) ;3 - (. :log console "interop works") +(.log console "interop works") (print '1) From c1466b716815d407383b31f01fed82e0cc1fec66 Mon Sep 17 00:00:00 2001 From: namespace Date: Tue, 9 May 2023 14:59:51 +0200 Subject: [PATCH 5/7] refactor WIP --- src/evil.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/evil.js b/src/evil.js index caef596..ed9a449 100644 --- a/src/evil.js +++ b/src/evil.js @@ -15,11 +15,11 @@ const debugMode = process.argv.debug; const compiledEval = process.argv.compiledEval; const env = createCompilationEnvironment(); -// if(sourceFile) { -// evalFile(sourceFile) -// } else { -// repl(env); -// } +if(sourceFile) { + evalFile(sourceFile) +} else { + repl(env); +} function preprocessFile(file){ let content = fs.readFileSync(file).toString(); @@ -29,9 +29,6 @@ function preprocessFile(file){ return `(progn ${content})`; } -preprocessFile("../test.lisp"); - - function evalFile(sourceFile) { const input = preprocessFile(sourceFile); const ast = parse(read(InputStream(input)), env); From 1bb0073fac5e788d4c0e8a17ab5463863b5108ba Mon Sep 17 00:00:00 2001 From: Namespace Date: Mon, 15 May 2023 11:25:03 +0200 Subject: [PATCH 6/7] Create README.md --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..e4fa352 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# evil-lisp + +A lisp intepreter/transpiler built on top of Javascript + +### Features + +* Transpiles to Javascript +* Close interop with Javascript primitives and libraries + * Can directly interact with JS methods: `(.log console "hello")` + * Access to `window` and `global` objects +* Dynamic binding +* Macro system +* REPL From b2744ffb4f970c3b6178d7137f61e38a5a9c00a3 Mon Sep 17 00:00:00 2001 From: namespace Date: Mon, 22 May 2023 16:16:19 +0200 Subject: [PATCH 7/7] admit [] as list characters --- src/reader.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/reader.js b/src/reader.js index 85465af..d9e018f 100644 --- a/src/reader.js +++ b/src/reader.js @@ -14,7 +14,6 @@ const macroCharacters = { let token = readWhile(input, char => !isWhitespace(char) && !isTerminatingMacro(char)) // (. log console "foo") - console.log('token:', token) if(token === ""){ return '.' } @@ -127,6 +126,8 @@ function readList(input){ } function InputStream(input){ + input = input.replace(/\[/g, '('); + input = input.replace(/\]/g, ')'); const delimited = {} // number of open delimited expressions (eg: (), "", []) let pos = 0 const next = () => input.charAt(pos++)