From de4417981dbdc4d751b2b3202536ba5075430a56 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Fri, 20 Dec 2019 09:50:23 +0800 Subject: [PATCH 1/5] docs: add proposal --- README.md | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index bdd96c99..b250068b 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,11 @@ More sophisticated examples, such as the Sieve of Eratosthenes, Quicksort, Mande ### The Compiler -Clone the repo, (OR simply download `./build/wenyan.js` and set its executable bit using the terminal command `chmod +x wenyan.js`). Then run `./build/wenyan.js` to compile your wenyan souce code to target language. Calling the compiler without arguments prints the help message, reproduced below: +```bash +npm install -g @wenyan-lang/cli +``` + +Calling the compiler without arguments prints the help message, reproduced below: ``` Usage: wenyan [options] [input files] @@ -76,21 +80,13 @@ Options: --output -o : Output file (default: `/dev/stdout') --roman -r : Romanize identifiers (default: `true') ``` + Try building the included examples first, e.g.: ``` -./build/wenyan.js examples/helloworld.wy -o helloworld.js +wenyan examples/helloworld.wy -o helloworld.js ``` -#### Building platform-specific binaries - -- Clone the repo -- `npm install` -- `npm run make_cmdline` - -The macOS, Windows and Linux binaries will be in the `./build` folder. - - ### [The online IDE](http://wenyan-lang.lingdong.works/ide.html) ![](screenshots/screenshot02.png) From 870b0b22517691e423aec4447a5bd833af448209 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Fri, 20 Dec 2019 13:54:07 +0800 Subject: [PATCH 2/5] chore: webpack and npm publish --- .github/workflows/publish.yml | 31 +++ .github/workflows/{nodejs.yml => test.yml} | 0 .gitignore | 3 +- package.json | 20 +- src/cli.js | 275 +++++++++++++++++++++ webpack.config.js | 23 ++ 6 files changed, 349 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/publish.yml rename .github/workflows/{nodejs.yml => test.yml} (100%) create mode 100644 src/cli.js create mode 100644 webpack.config.js diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..bc00d172 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,31 @@ +name: Publish Package + +on: + release: + types: [created] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: actions/setup-node@v1 + with: + node-version: 10 + - run: npm ci + - run: npm test + - run: npm run build + + publish-npm: + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: actions/setup-node@v1 + with: + node-version: 10 + registry-url: https://registry.npmjs.org/ + - run: npm install + - run: npm publish + env: + NODE_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}} \ No newline at end of file diff --git a/.github/workflows/nodejs.yml b/.github/workflows/test.yml similarity index 100% rename from .github/workflows/nodejs.yml rename to .github/workflows/test.yml diff --git a/.gitignore b/.gitignore index 980c1656..50a06c10 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ node_modules/ build/wenyan-linux build/wenyan-macos build/wenyan-win.exe -temp \ No newline at end of file +temp +dist \ No newline at end of file diff --git a/package.json b/package.json index 82ce4e7c..8aa61f41 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,23 @@ { "name": "wenyan-lang", + "version": "0.0.1", + "author": "LingDong", "repository": { "type": "git", "url": "https://github.com/LingDong-/wenyan-lang.git" }, + "license": "MIT", + "bin": { + "wenyan": "./dist/cli.js" + }, + "main": "./dist/core.js", + "files": [ + "dist" + ], "scripts": { + "build": "npm run clear && webpack --mode production", + "dev": "npm run clear && webpack --mode development --watch", + "clear": "rimraf ./dist", "make_cmdline": "node ./tools/make_cmdline.js && pkg ./build/wenyan.js --out-path ./build", "make_ide": "node ./tools/make_ide.js", "make_site": "node ./tools/make_site.js", @@ -26,7 +39,6 @@ "git add" ] }, - "license": "MIT", "devDependencies": { "chai": "^4.2.0", "enzyme": "^3.10.0", @@ -40,6 +52,10 @@ "mocha": "^6.2.2", "mocha-snapshots": "^4.2.0", "pkg": "^4.4.2", - "prettier": "^1.19.1" + "prettier": "^1.19.1", + "rimraf": "^3.0.0", + "webpack": "^4.41.4", + "webpack-cli": "^3.3.10", + "webpack-shell-plugin": "^0.5.0" } } diff --git a/src/cli.js b/src/cli.js new file mode 100644 index 00000000..4aca7a52 --- /dev/null +++ b/src/cli.js @@ -0,0 +1,275 @@ +const fs = require("fs"); +const package = require("../package.json"); +const buildDate = `${new Date().toLocaleDateString("en-US")}`; +const { compile } = require("./parser"); + +function cmdlinecode() { + var ARGS = [ + ["--version", "-v", String, "", "Current version"], + ["--lang", "-l", String, "js", "Language: js/py"], + ["--roman", "-r", String, "none", "Romanize identifiers"], + ["--output", "-o", String, "/dev/stdout", "Output file"], + ["--exec", "-x", Boolean, false, "Execute output"], + ["--eval", "-e", String, "", "Give a string instead of a file"], + ["--log", null, String, "/dev/null", "Log file"], + ["--inspect", "-i", Boolean, false, "Interactive REPL"], + ["--render", null, String, "none", "Render input under given title"] + ]; + var ART = ` ,_ ,_\n \\/ ==\n /\\ []`; + function printhelp() { + ARGS.sort(); + console.log(ART); + console.log( + `\nWENYAN LANG 文言 Compiler v${package.version} (${buildDate})` + ); + console.log("\nUsage: wenyan [options] [input files]"); + console.log("\nOptions:"); + var ret = ""; + for (var i = 0; i < ARGS.length; i++) { + ret += ARGS[i][0].padEnd(9) + " "; + if (ARGS[i][1]) { + ret += ARGS[i][1] + " "; + } else { + ret += " "; + } + ret += ("<" + typeof ARGS[i][2]() + ">").padEnd(9); + ret += " : "; + var s = ARGS[i][4] + " (default: `" + ARGS[i][3] + "`)"; + var n = 50; + if (s.length < n) { + ret += s; + } else { + var l = s.split(" "); + var m = Math.floor(l.length / 2); + var s0 = l.slice(0, m); + var s1 = l.slice(m); + ret += s0 + "\n" + " ".repeat(16) + s1; + } + ret += "\n"; + } + console.log(ret); + } + + function argparse() { + if (process.argv.length <= 2) { + printhelp(); + process.exit(); + } + var args = {}; + for (var i = 0; i < ARGS.length; i++) { + args[ARGS[i][0]] = ARGS[i][3]; + } + var files = []; + var i = 2; + while (i < process.argv.length) { + var a = process.argv[i]; + var b = process.argv[i + 1]; + var found = false; + for (var j = 0; j < ARGS.length; j++) { + if (a && (ARGS[j][0] == a || ARGS[j][1] == a)) { + args[ARGS[j][0]] = ARGS[j][2](b); + i += 2; + found = true; + break; + } + } + if (!found) { + if (a[0] == "-" && !a.includes(".")) { + console.log("Unrecognized argument: ", a); + process.exit(); + } else { + files.push(a); + i += 1; + } + } + } + + if (args["--version"]) return package.version; + + return { files, args }; + } + function replscope() { + function generate(depth) { + var s0 = "global.__scope=new function(){\n"; + var s1 = "\n}"; + for (var i = 0; i < depth; i++) { + var istr = "__" + ("" + i).padStart(8, "0"); + s0 += `this.evil=function(${istr}){global.__scope=this;var __r=eval(${istr});\n`; + s1 = `;return __r}` + s1; + } + return eval(s0 + s1); + } + var stackCallSize = 1000; + for (var i = stackCallSize; i > 0; i -= 200) { + try { + generate(i); + stackCallSize = i; + break; + } catch (e) { + //console.log(i+ " exceeds max stack size"); + } + } + // console.log("final stack size "+stackCallSize); + } + + function repl(args) { + const readline = require("readline"); + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); + global.haserr = false; + rl.question("> ", inp => { + var out = compile("js", inp, { + romanizeIdentifiers: args["--roman"], + logCallback: writer("/dev/null", "a"), + errorCallback: function(x) { + console.error(x); + global.haserr = true; + } + }); + if (global.haserr) { + // console.log("Not evaulated.") + } else { + console.log("\x1b[2m" + out + "\x1b[0m"); + try { + global.__scope.evil(out); + } catch (e) { + console.log(e); + } + } + rl.close(); + repl(args); + }); + } + + function writer(f, mode) { + if (f == "/dev/null") { + return () => 0; + } else if (f == "/dev/stdout") { + return x => + typeof x == "string" + ? console.log(x) + : console.dir(x, { depth: null, maxArrayLength: null }); + } else if (f == "/dev/stderr") { + return console.error; + } else if (mode == "a") { + return function(x) { + fs.appendFileSync( + f, + (typeof x == "object" ? JSON.stringify(x) : x.toString()) + "\n" + ); + }; + } else if (mode == "w") { + return function(x) { + fs.writeFileSync( + f, + (typeof x == "object" ? JSON.stringify(x) : x.toString()) + "\n" + ); + }; + } + } + + function main() { + var { files, args } = argparse(); + var scope_generated = false; + var src = + files + .map(x => + x.endsWith(".svg") + ? unrender([fs.readFileSync(x).toString()]) + : fs.readFileSync(x).toString() + ) + .join("\n") + + "\n" + + args["--eval"]; + var out = compile(args["--lang"], src, { + romanizeIdentifiers: args["--roman"], + logCallback: writer(args["--log"], "a"), + errorCallback: function(x) { + console.error(x); + process.exit(); + } + }); + if (args["--output"] == ".") { + if (files.length == 0) { + console.log( + "Cannot infer output filename because no input file was found." + ); + process.exit(); + } else { + var basename = files[0] + .split(".") + .slice(0, -1) + .join("."); + if (args["--render"] == "none") { + args["--output"] = basename + "." + args["--lang"]; + } else { + args["--output"] = basename; + } + } + } + + if (args["--render"] !== "none") { + var dispname = args["--render"]; + if (dispname == ".") { + var p = args["--output"].split(/[/\\]/); + dispname = p[p.length - 1]; + } + var svgs = render(dispname, src, { plotResult: false }); + + var outputEndsWithSvg = args["--output"].toLowerCase().endsWith(".svg"); + + // only one page rendered + if (svgs.length === 1) { + if (!outputEndsWithSvg) args["--output"] += ".svg"; + fs.writeFileSync(args["--output"], svgs[0]); + console.log(args["--output"]); // Outputs generated filename + } + // multiple pages rendered, output file as `filename.001.svg` etc + else { + if (outputEndsWithSvg) args["--output"] = args["--output"].slice(0, -4); // remove .svg suffix + + for (var i = 0; i < svgs.length; i++) { + var filename = + args["--output"] + "." + i.toString().padStart(3, "0") + ".svg"; + fs.writeFileSync(filename, svgs[i]); + console.log(filename); // Outputs generated filename + } + } + } else if (args["--exec"]) { + if (args["--lang"] == "js") { + if (!args["--inspect"]) { + eval(out); + } else { + replscope(); + scope_generated = true; + global.__scope.evil(out); + } + } else if (args["--lang"] == "py") { + var execSync = require("child_process").execSync; + fs.writeFileSync("tmp.py", out); + var ret = execSync( + "which python3; if [ $? == 0 ]; then python3 tmp.py; else python tmp.py; fi; rm tmp.py", + { encoding: "utf-8" } + ); + console.log(ret); + } + } else if (args["--inspect"]) { + if (!scope_generated) { + replscope(); + } + repl(args); + } else { + // none of "--render", "--exec", "--inspect" are specified + // going to compile mode + writer(args["--output"], "w")(out); + } + + return 0; + } + + main(); +} + +cmdlinecode(); diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 00000000..fd81f8ae --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,23 @@ +const path = require('path') + +module.exports = { + target: 'node', + entry: { + cli: './src/cli.js', + core: './src/parser.js' + }, + devtool: 'source-map', + output: { + path: path.resolve(__dirname, 'dist'), + filename: '[name].js' + }, + resolve: { + extensions: ['.ts', '.js'], + }, + plugins: [ + new webpack.BannerPlugin({ + banner: '#!/usr/bin/env node', + raw: true, + }), + ], +}; \ No newline at end of file From 74e0fabee6fd5228a784bdbb020c3fdd1b395a9f Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Fri, 20 Dec 2019 14:32:27 +0800 Subject: [PATCH 3/5] chore: fix build script --- package.json | 2 +- webpack.config.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 8aa61f41..39aa417e 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "dist" ], "scripts": { - "build": "npm run clear && webpack --mode production", + "build": "npm run clear && webpack --mode production && chmod -x ./dist/cli.js", "dev": "npm run clear && webpack --mode development --watch", "clear": "rimraf ./dist", "make_cmdline": "node ./tools/make_cmdline.js && pkg ./build/wenyan.js --out-path ./build", diff --git a/webpack.config.js b/webpack.config.js index fd81f8ae..5fe6b488 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,4 +1,5 @@ const path = require('path') +const webpack = require('webpack') module.exports = { target: 'node', From 855f3c46d6a86f5261441fd1d9b155d24ff2df67 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Fri, 20 Dec 2019 14:46:13 +0800 Subject: [PATCH 4/5] chore: add version bumper fix --- .github/workflows/publish.yml | 2 +- package.json | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index bc00d172..58d6c524 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -28,4 +28,4 @@ jobs: - run: npm install - run: npm publish env: - NODE_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}} \ No newline at end of file + NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} \ No newline at end of file diff --git a/package.json b/package.json index 39aa417e..50351a59 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "build": "npm run clear && webpack --mode production && chmod -x ./dist/cli.js", "dev": "npm run clear && webpack --mode development --watch", "clear": "rimraf ./dist", + "release": "bump --commit --tag && git push --follow-tags", "make_cmdline": "node ./tools/make_cmdline.js && pkg ./build/wenyan.js --out-path ./build", "make_ide": "node ./tools/make_ide.js", "make_site": "node ./tools/make_site.js", @@ -54,6 +55,7 @@ "pkg": "^4.4.2", "prettier": "^1.19.1", "rimraf": "^3.0.0", + "version-bump-prompt": "^5.0.6", "webpack": "^4.41.4", "webpack-cli": "^3.3.10", "webpack-shell-plugin": "^0.5.0" From e12545e0cb24cb1d6b3d392199db4f2cb0a3dbfd Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Fri, 20 Dec 2019 14:52:41 +0800 Subject: [PATCH 5/5] docs: update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b250068b..69cfba9f 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ More sophisticated examples, such as the Sieve of Eratosthenes, Quicksort, Mande ### The Compiler ```bash -npm install -g @wenyan-lang/cli +npm install -g wenyan-lang ``` Calling the compiler without arguments prints the help message, reproduced below: