From 0bf7c0de3afb99cd2e3bfa1feb6d3a30236b7547 Mon Sep 17 00:00:00 2001 From: Nilay Majorwar Date: Sun, 30 Jan 2022 20:32:50 +0530 Subject: [PATCH] Add script to generate files for new language --- package.json | 3 +- scripts/add-new-language.js | 81 ++++++++++++++++++++++++++ scripts/new-lang-template/common.ts | 27 +++++++++ scripts/new-lang-template/engine.ts | 5 ++ scripts/new-lang-template/ide-page.tsx | 27 +++++++++ scripts/new-lang-template/index.ts | 12 ++++ scripts/new-lang-template/renderer.tsx | 7 +++ scripts/new-lang-template/runtime.ts | 22 +++++++ tsconfig.json | 2 +- 9 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 scripts/add-new-language.js create mode 100644 scripts/new-lang-template/common.ts create mode 100644 scripts/new-lang-template/engine.ts create mode 100644 scripts/new-lang-template/ide-page.tsx create mode 100644 scripts/new-lang-template/index.ts create mode 100644 scripts/new-lang-template/renderer.tsx create mode 100644 scripts/new-lang-template/runtime.ts diff --git a/package.json b/package.json index 4cd373f..a8ae361 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "lint": "next lint", "test": "jest", "dev:worker": "webpack -c worker-pack/webpack.dev.js --watch", - "build:worker": "webpack -c worker-pack/webpack.prod.js" + "build:worker": "webpack -c worker-pack/webpack.prod.js", + "add-new-language": "node scripts/add-new-language.js" }, "dependencies": { "@blueprintjs/core": "^3.51.3", diff --git a/scripts/add-new-language.js b/scripts/add-new-language.js new file mode 100644 index 0000000..48e7d88 --- /dev/null +++ b/scripts/add-new-language.js @@ -0,0 +1,81 @@ +// @ts-check +const path = require("path"); +const fs = require("fs"); +const args = process.argv; + +const langId = args[2]; +const langName = args[3]; +if (!langId || !langName) { + console.log( + `Usage: npm run add-new-language + yarn run add-new-language ` + ); + process.exit(1); +} + +// Check if language provider directory already exists +const dir = path.resolve(__dirname, "../engines/", langId); +if (fs.existsSync(dir)) { + console.log(`Language ID '${langId}' already exists.`); + process.exit(0); +} +fs.mkdirSync(dir); + +/** + * Copy a file from source path to destination. + * Also removes first line from file, which contains the "@ts-nocheck" comment + * @param {string} src Absolute path to source file + * @param {string} dest Absolute path to destination + */ +const copyFile = (src, dest) => { + const rawContents = fs.readFileSync(src).toString(); + const lines = rawContents.split("\n"); + const firstLine = lines.shift(); + if (firstLine !== "// @ts-nocheck") { + console.error(`Template file '${src}' doesn't have @ts-nocheck comment`); + process.exit(1); + } + fs.writeFileSync(dest, lines.join("\n")); +}; + +{ + // Copy language provider template files + ["index.ts", "common.ts", "runtime.ts", "engine.ts", "renderer.tsx"].forEach( + (filename) => { + const srcPath = path.resolve(__dirname, `new-lang-template/${filename}`); + const destPath = path.resolve(dir, filename); + copyFile(srcPath, destPath); + } + ); +} + +{ + // Generate Next.js page + const src = path.resolve(__dirname, "./new-lang-template/ide-page.tsx"); + const dest = path.resolve(__dirname, `../pages/ide/${langId}.tsx`); + const contents = fs.readFileSync(src).toString(); + const finalContents = contents + .replace("$LANG_ID", langId) + .replace("$LANG_NAME", langName); + fs.writeFileSync(dest, finalContents); +} + +{ + // Add entry to `pages/languages.json` + const jsonPath = path.resolve(__dirname, "../pages/languages.json"); + const contents = JSON.parse(fs.readFileSync(jsonPath).toString()); + if (!Array.isArray(contents)) { + console.error("languages.json is malformed, please check its contents"); + process.exit(1); + } + const existingIdx = contents.findIndex((c) => c.id === langId); + if (existingIdx !== -1) { + console.error("languages.json already contains entry."); + process.exit(1); + } + const newContents = [...contents, { display: langName, id: langId }]; + fs.writeFileSync(jsonPath, JSON.stringify(newContents, undefined, 2)); +} + +// Print success message +console.log(`Done! Created files for language '${langId}'`); diff --git a/scripts/new-lang-template/common.ts b/scripts/new-lang-template/common.ts new file mode 100644 index 0000000..8665b19 --- /dev/null +++ b/scripts/new-lang-template/common.ts @@ -0,0 +1,27 @@ +// @ts-nocheck +import { MonacoTokensProvider } from "../types"; + +/** Type of props passed to renderer */ +export type RS = { + value: number; +}; + +/** Sample program */ +export const sampleProgram = [ + "Program line 1", + "Program line 2", + "Program line 3", +].join("\n"); + +/** Syntax highlighting */ +export const editorTokensProvider: MonacoTokensProvider = { + tokenizer: { + root: [ + [/i/, "orange"], + [/d/, "red"], + [/s/, "blue"], + [/o/, "green"], + ], + }, + defaultToken: "comment", +}; diff --git a/scripts/new-lang-template/engine.ts b/scripts/new-lang-template/engine.ts new file mode 100644 index 0000000..cfb80ae --- /dev/null +++ b/scripts/new-lang-template/engine.ts @@ -0,0 +1,5 @@ +// @ts-nocheck +import { setupWorker } from "../setup-worker"; +import XYZLanguageEngine from "./runtime"; + +setupWorker(new XYZLanguageEngine()); diff --git a/scripts/new-lang-template/ide-page.tsx b/scripts/new-lang-template/ide-page.tsx new file mode 100644 index 0000000..2be8394 --- /dev/null +++ b/scripts/new-lang-template/ide-page.tsx @@ -0,0 +1,27 @@ +// @ts-nocheck +import React from "react"; +import { NextPage } from "next"; +import Head from "next/head"; +import { Mainframe } from "../../ui/Mainframe"; +import { Header } from "../../ui/header"; +import LangProvider from "../../engines/$LANG_ID"; +const LANG_ID = "$LANG_ID"; +const LANG_NAME = "$LANG_NAME"; + +const IDE: NextPage = () => { + return ( + <> + + {LANG_NAME} | Esolang Park + +
+
+
+ +
+
+ + ); +}; + +export default IDE; diff --git a/scripts/new-lang-template/index.ts b/scripts/new-lang-template/index.ts new file mode 100644 index 0000000..12dea0b --- /dev/null +++ b/scripts/new-lang-template/index.ts @@ -0,0 +1,12 @@ +// @ts-nocheck +import { Renderer } from "./renderer"; +import { LanguageProvider } from "../types"; +import { RS, sampleProgram, editorTokensProvider } from "./common"; + +const provider: LanguageProvider = { + Renderer, + sampleProgram, + editorTokensProvider, +}; + +export default provider; diff --git a/scripts/new-lang-template/renderer.tsx b/scripts/new-lang-template/renderer.tsx new file mode 100644 index 0000000..793f2df --- /dev/null +++ b/scripts/new-lang-template/renderer.tsx @@ -0,0 +1,7 @@ +// @ts-nocheck +import { RendererProps } from "../types"; +import { RS } from "./common"; + +export const Renderer = ({ state }: RendererProps) => { + return state == null ? null :

state.value

; +}; diff --git a/scripts/new-lang-template/runtime.ts b/scripts/new-lang-template/runtime.ts new file mode 100644 index 0000000..94ff01f --- /dev/null +++ b/scripts/new-lang-template/runtime.ts @@ -0,0 +1,22 @@ +// @ts-nocheck +import { LanguageEngine, StepExecutionResult } from "../types"; +import { RS } from "./common"; + +export default class XYZLanguageEngine implements LanguageEngine { + resetState() { + // TODO: Unimplemented + } + + validateCode(code: string) { + // TODO: Unimplemented + } + + prepare(code: string, input: string) { + // TODO: Unimplemented + } + + executeStep(): StepExecutionResult { + // TODO: Unimplemented + return { rendererState: { value: 0 }, nextStepLocation: { line: 0 } }; + } +} diff --git a/tsconfig.json b/tsconfig.json index 99710e8..34723f1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,5 +16,5 @@ "incremental": true }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], - "exclude": ["node_modules"] + "exclude": ["node_modules", "scripts/*/*.ts", "scripts/*/*.tsx"] }