1
0

Add script to generate files for new language

This commit is contained in:
Nilay Majorwar 2022-01-30 20:32:50 +05:30
parent 3889b97df5
commit 0bf7c0de3a
9 changed files with 184 additions and 2 deletions

@ -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",

@ -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 <language-id> <language-name>
yarn run add-new-language <language-id> <language-name>`
);
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}'`);

@ -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",
};

@ -0,0 +1,5 @@
// @ts-nocheck
import { setupWorker } from "../setup-worker";
import XYZLanguageEngine from "./runtime";
setupWorker(new XYZLanguageEngine());

@ -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 (
<>
<Head>
<title>{LANG_NAME} | Esolang Park</title>
</Head>
<div style={{ height: "100%", display: "flex", flexDirection: "column" }}>
<Header langName={LANG_NAME} />
<div style={{ flexGrow: 1 }}>
<Mainframe langName={LANG_ID} provider={LangProvider} />
</div>
</div>
</>
);
};
export default IDE;

@ -0,0 +1,12 @@
// @ts-nocheck
import { Renderer } from "./renderer";
import { LanguageProvider } from "../types";
import { RS, sampleProgram, editorTokensProvider } from "./common";
const provider: LanguageProvider<RS> = {
Renderer,
sampleProgram,
editorTokensProvider,
};
export default provider;

@ -0,0 +1,7 @@
// @ts-nocheck
import { RendererProps } from "../types";
import { RS } from "./common";
export const Renderer = ({ state }: RendererProps<RS>) => {
return state == null ? null : <p>state.value</p>;
};

@ -0,0 +1,22 @@
// @ts-nocheck
import { LanguageEngine, StepExecutionResult } from "../types";
import { RS } from "./common";
export default class XYZLanguageEngine implements LanguageEngine<RS> {
resetState() {
// TODO: Unimplemented
}
validateCode(code: string) {
// TODO: Unimplemented
}
prepare(code: string, input: string) {
// TODO: Unimplemented
}
executeStep(): StepExecutionResult<RS> {
// TODO: Unimplemented
return { rendererState: { value: 0 }, nextStepLocation: { line: 0 } };
}
}

@ -16,5 +16,5 @@
"incremental": true
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
"exclude": ["node_modules", "scripts/*/*.ts", "scripts/*/*.tsx"]
}