diff --git a/.gitignore b/.gitignore index 4560abe..31241da 100644 --- a/.gitignore +++ b/.gitignore @@ -59,5 +59,5 @@ typings/ # next.js build output .next - +dist/ .cache \ No newline at end of file diff --git a/browser.js b/browser.js new file mode 100644 index 0000000..383b442 --- /dev/null +++ b/browser.js @@ -0,0 +1,11 @@ +import { run, Environment } from './index' + +const button = document.getElementById('run') +button.onclick = () => { + const code = document.getElementById('code').value + const browserEnv = new Environment() + browserEnv.setBuiltin('alert', (_, arg) => alert(arg[0])) + browserEnv.setBuiltin('confirm', (_, elem) => confirm(elem[0])) + browserEnv.setBuiltin('prompt', (_, p) => prompt(p[0], p.length > 1 ? p[1] : null)) + run(code, browserEnv) +} diff --git a/cli.js b/cli.js new file mode 100755 index 0000000..985b425 --- /dev/null +++ b/cli.js @@ -0,0 +1,78 @@ +#! /usr/bin/env node +const fs = require('fs') +const readline = require('readline') +const Environment = require('./environment') +const { run } = require('./index') + +let options = { + debug: false, + history: 30, + prompt: '>' +} + +const runPrompt = () => { + const prompt = options.prompt + ' ' + process.stdout.write(prompt) + const lineReader = readline.createInterface({ + input: process.stdin, + output: process.stdout, + terminal: true, + prompt: prompt, + historySize: +options.history + }) + const env = new Environment() + env.setBuiltin('readFile', (_vars, args) => fs.readFileSync(args[0], 'utf8')) + + lineReader.on('line', line => { + let code = line + if (!line.endsWith(';') && !line.endsWith('}')) code += ';' + // TODO: Support multi-line block statements + const lastLine = run(code, env, options.debug) + console.log(JSON.stringify(lastLine)) + process.stdout.write(prompt) + }) +} + +const runFile = filename => { + try { + const file = fs.readFileSync(filename, 'utf8') + run(file, undefined, options.debug) + } catch (e) { + console.error(`YALI could not read the file ${filename}`) + console.error(e) + } +} + +const optionRegex = /--(\w+)(?:=(.+))?/ +const processOptions = args => + args + .map(arg => { + const match = optionRegex.exec(arg) + if (match) { + const [_, option, value] = match + if (!value) { + options[option] = !options[option] + } else { + options[option] = value + } + } else { + return arg + } + }) + .filter(Boolean) + +const main = argv => { + process.title = 'YALI' + const args = processOptions(argv.slice(2)) + if (options.debug) console.log(options) + if (args.length > 1) { + console.error('Usage: jlox [script]') + return 64 + } else if (args.length === 1) { + runFile(args[0]) + } else { + runPrompt() + } +} + +return main(process.argv) diff --git a/index.html b/index.html new file mode 100644 index 0000000..616e3d7 --- /dev/null +++ b/index.html @@ -0,0 +1,27 @@ + + + + + + YALI.js + + + + +
+ + + + \ No newline at end of file diff --git a/index.js b/index.js index 95786a5..39d9ca6 100755 --- a/index.js +++ b/index.js @@ -1,27 +1,18 @@ -#! /usr/bin/env node -const fs = require('fs') const chalk = require('chalk') -const readline = require('readline') const Tokenizer = require('./tokenizer') const Parser = require('./parser') const { LoxError } = require('./errors') const Interpreter = require('./interpreter') const Environment = require('./environment') -let options = { - debug: false, - history: 30, - prompt: '>' -} - -const run = (code, environment) => { +const run = (code, environment, debug = false) => { try { const tokenizer = new Tokenizer(code) const tokens = tokenizer.scanTokens() - if (options.debug) console.log(tokens) + if (debug) console.log(tokens) const parser = new Parser(tokens) const statements = parser.parse() - if (options.debug) console.log(statements) + if (debug) console.log(statements) const interpreter = new Interpreter(environment) let lastStatement for (let statement of statements) { @@ -59,69 +50,7 @@ const run = (code, environment) => { } } -const runPrompt = () => { - const prompt = options.prompt + ' ' - process.stdout.write(prompt) - const lineReader = readline.createInterface({ - input: process.stdin, - output: process.stdout, - terminal: true, - prompt: prompt, - historySize: +options.history - }) - const env = new Environment() - env.setBuiltin('readFile', (_vars, args) => fs.readFileSync(args[0], 'utf8')) - - lineReader.on('line', line => { - let code = line - if (!line.endsWith(';') && !line.endsWith('}')) code += ';' - // TODO: Support multi-line block statements - const lastLine = run(code, env) - console.log(JSON.stringify(lastLine)) - process.stdout.write(prompt) - }) -} - -const runFile = filename => { - try { - const file = fs.readFileSync(filename, 'utf8') - run(file) - } catch (e) { - console.error(`YALI could not read the file ${filename}`) - console.error(e) - } -} - -const optionRegex = /--(\w+)(?:=(.+))?/ -const processOptions = args => - args - .map(arg => { - const match = optionRegex.exec(arg) - if (match) { - const [_, option, value] = match - if (!value) { - options[option] = !options[option] - } else { - options[option] = value - } - } else { - return arg - } - }) - .filter(Boolean) - -const main = argv => { - process.title = 'YALI' - const args = processOptions(argv.slice(2)) - if (options.debug) console.log(options) - if (args.length > 1) { - console.error('Usage: jlox [script]') - return 64 - } else if (args.length === 1) { - runFile(args[0]) - } else { - runPrompt() - } -} - -return main(process.argv) +module.exports = { + run, + Environment +} \ No newline at end of file diff --git a/package.json b/package.json index aa6da64..e06f657 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "name": "yali", - "version": "1.0.0", + "version": "0.10.2", "description": "Yet Another Lox Interpreter. Javascript Implementation of the Lox Programming Language", "main": "index.js", - "bin": "./index.js", + "bin": "./cli.js", "scripts": { "test": "jest", "format": "prettier --write **/*.js",