├── .eslintignore ├── .yarnrc ├── .prettierignore ├── jest.config.mjs ├── .gitignore ├── packages ├── hacklang-loader │ ├── test │ │ ├── fixture │ │ │ ├── src │ │ │ │ └── index.js │ │ │ └── dist │ │ │ │ └── main.js │ │ ├── webpack.config.js │ │ └── index.test.js │ ├── tsconfig.json │ ├── lib │ │ ├── options.json │ │ └── index.js │ └── package.json └── hacklang │ ├── src │ ├── test │ │ ├── README.md │ │ └── test.ts │ ├── grammer │ │ ├── Language.ts │ │ ├── Token.ts │ │ ├── TokenType.ts │ │ ├── Keyword.ts │ │ └── Punctuator.ts │ ├── index.ts │ ├── ErrorHandler.ts │ ├── api.ts │ ├── Parser.ts │ └── Tokenizer.ts │ ├── tsconfig.json │ └── package.json ├── .prettierrc.json ├── lerna.json ├── example └── helloworld.hc ├── .github ├── CONTRIBUTING.md ├── PULL_REQUEST_TEMPLATE.md └── ISSUE_TEMPLATE.md ├── OPENKEYWORDS.md ├── test ├── test-test.ts └── api.test.ts ├── tsconfig.base.json ├── CONTRIBUTING.md ├── .eslintrc ├── patches ├── @lerna+package+3.16.0.patch ├── @lerna+package-graph+3.18.5.patch └── @lerna+npm-publish+3.18.5.patch ├── LICENSE ├── package.json └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | workspaces-experimental true 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore artifacts: 2 | build 3 | -------------------------------------------------------------------------------- /jest.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | preset: "ts-jest", 3 | }; 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | /.idea/ 3 | /.vscode/ 4 | build/ 5 | *.log 6 | -------------------------------------------------------------------------------- /packages/hacklang-loader/test/fixture/src/index.js: -------------------------------------------------------------------------------- 1 | let variable = "value"; 2 | -------------------------------------------------------------------------------- /packages/hacklang/src/test/README.md: -------------------------------------------------------------------------------- 1 | This doesn't have real tests yet. I know. Don't complain. 2 | -------------------------------------------------------------------------------- /packages/hacklang/src/grammer/Language.ts: -------------------------------------------------------------------------------- 1 | export enum Language { 2 | JAVASCRIPT, 3 | HACKLANG, 4 | } 5 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "singleQuote": false, 4 | "trailingComma": "es5" 5 | } 6 | -------------------------------------------------------------------------------- /packages/hacklang/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Parser"; 2 | export * from "./Tokenizer"; 3 | export * from "./api"; 4 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "npmClient": "yarn", 3 | "packages": ["packages/*"], 4 | "version": "0.0.0", 5 | "useWorkspaces": true 6 | } 7 | -------------------------------------------------------------------------------- /example/helloworld.hc: -------------------------------------------------------------------------------- 1 | sam main(){ 2 | winston(5 gleichgleichgleich 5){ 3 | vincent "Hello World"; 4 | } 5 | } 6 | console.log(main()); 7 | -------------------------------------------------------------------------------- /packages/hacklang/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "include": ["src"], 4 | "compilerOptions": { 5 | "outDir": "build" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/hacklang-loader/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "include": ["src"], 4 | "compilerOptions": { 5 | "outDir": "build" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/hacklang-loader/lib/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "dummy": { 5 | "description": "This is description of dummy", 6 | "type": "string" 7 | } 8 | }, 9 | "additionalProperties": false 10 | } 11 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Example Contributing Guidelines 2 | 3 | This is an example of GitHub's contributing guidelines file. Check out GitHub's [CONTRIBUTING.md help center article](https://help.github.com/articles/setting-guidelines-for-repository-contributors/) for more information. 4 | -------------------------------------------------------------------------------- /OPENKEYWORDS.md: -------------------------------------------------------------------------------- 1 | # Open Keywords For Hacklang 2 | 3 | | **Open keywords and/or operators** 4 | | ------------------------------------------------------------------ | 5 | | `delete` | 6 | | `do` | 7 | | `extends` | 8 | | `finally` | 9 | | `in` | 10 | | `instanceof`| 11 | | `switch` | 12 | | `typeof` | 13 | | `with` | 14 | -------------------------------------------------------------------------------- /packages/hacklang/src/grammer/Token.ts: -------------------------------------------------------------------------------- 1 | import { TokenType } from "./TokenType"; 2 | 3 | export interface Location { 4 | line: number; 5 | column: number; 6 | } 7 | 8 | export interface Token { 9 | type: TokenType; 10 | value: string; 11 | loc?: { 12 | start: Location; 13 | end: Location; 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | * **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...) 2 | 3 | 4 | 5 | * **What is the current behavior?** (You can also link to an open issue here) 6 | 7 | 8 | 9 | * **What is the new behavior (if this is a feature change)?** 10 | 11 | 12 | 13 | * **Other information**: 14 | -------------------------------------------------------------------------------- /test/test-test.ts: -------------------------------------------------------------------------------- 1 | import { compile } from "../api"; 2 | 3 | const testString = ` 4 | function main(){ 5 | if(5 === 5){ 6 | return "Hello World"; 7 | } 8 | } 9 | console.log(main()); 10 | const x = { 11 | test: "test" 12 | }; 13 | `; 14 | 15 | const result = compile(testString); 16 | console.info(result.ast); 17 | console.info(result.code); 18 | -------------------------------------------------------------------------------- /packages/hacklang/src/test/test.ts: -------------------------------------------------------------------------------- 1 | import { compile } from "../api"; 2 | 3 | const testString = ` 4 | function main(){ 5 | if(5 === 5){ 6 | return "Hello World"; 7 | } 8 | } 9 | console.log(main()); 10 | const x = { 11 | test: "test" 12 | }; 13 | `; 14 | 15 | const result = compile(testString); 16 | console.info(result.ast); 17 | console.info(result.code); 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | * **I'm submitting a ...** 2 | [ ] bug report 3 | [ ] feature request 4 | [ ] question about the decisions made in the repository 5 | [ ] question about how to use this project 6 | 7 | * **Summary** 8 | 9 | 10 | 11 | * **Other information** (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.) 12 | -------------------------------------------------------------------------------- /packages/hacklang/src/grammer/TokenType.ts: -------------------------------------------------------------------------------- 1 | export enum TokenType { 2 | EOF = "EOF", 3 | BooleanLiteral = "BooleanLiteral", 4 | Identifier = "Identifier", 5 | Keyword = "Keyword", 6 | NullLiteral = "NullLiteral", 7 | NumericLiteral = "NumericLiteral", 8 | Punctuator = "Punctuator", 9 | StringLiteral = "StringLiteral", 10 | RegularExpression = "RegularExpression", 11 | Template = "Template", 12 | 13 | WHITESPACE = "Whitespace", 14 | } 15 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "sourceMap": true, 6 | "outDir": "build", 7 | "downlevelIteration": true, 8 | "strict": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "isolatedModules": true, 11 | "noImplicitAny": true, 12 | "skipLibCheck": true, 13 | "declaration": true, 14 | "allowJs": true, 15 | "checkJs": true 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribute To HackLang 2 | 3 | # Want To Contribute To HackLang? 4 | 5 | ## What You Can Do 6 | 7 | - Apply To Have Your Name Become A HackLang Keyword 8 | - Contribute to creating the Javascript-based language 9 | - Add to the list of keywords so we can continue to add names 10 | 11 | ## Apply To Have Your Name Become A HackLang Keyword 12 | 13 | We still need names for the many, many keywords that have yet to be claimed. Claim yours right now, by applying [here](https://airtable.com/shr69zRPjSpmrLdmh) 14 | -------------------------------------------------------------------------------- /packages/hacklang-loader/test/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | /** @type import("@types/webpack").Configuration */ 4 | module.exports = { 5 | mode: "development", 6 | context: path.join(__dirname, "fixture"), 7 | entry: path.join(__dirname, "fixture/src"), 8 | output: { 9 | path: path.join(__dirname, "fixture/dist"), 10 | }, 11 | module: { 12 | rules: [ 13 | { 14 | test: /.js$/, 15 | loader: require.resolve(path.join(__dirname, "..")), 16 | options: {}, 17 | }, 18 | ], 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /packages/hacklang/src/ErrorHandler.ts: -------------------------------------------------------------------------------- 1 | import { Location } from "./grammer/Token"; 2 | 3 | export class ErrorHandler { 4 | source: string; 5 | lines: string[]; 6 | constructor(source: string) { 7 | this.source = source; 8 | this.lines = this.source.split("\n"); 9 | } 10 | 11 | createError(message: string, location: Location): void { 12 | const padding = " ".repeat(10); 13 | console.error(padding + this.lines[location.line]); 14 | console.error(padding + " ".repeat(location.column) + "^"); 15 | console.error("Error: " + message); 16 | process.exitCode = 1 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": [ 5 | "@typescript-eslint" 6 | ], 7 | "env": { 8 | "node": true 9 | }, 10 | "extends": [ 11 | "eslint:recommended", 12 | "plugin:@typescript-eslint/eslint-recommended", 13 | "plugin:@typescript-eslint/recommended" 14 | ], 15 | "overrides": [ 16 | { 17 | "files": ["*.js"], 18 | "rules": { 19 | "@typescript-eslint/no-var-requires": "off" 20 | } 21 | }, 22 | { 23 | "files": ["*.test.js"], 24 | "env": { 25 | "jest": true 26 | } 27 | } 28 | ], 29 | "rules": { 30 | "@typescript-eslint/no-explicit-any": 0 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/hacklang/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hacklang", 3 | "version": "1.0.0", 4 | "main": "build", 5 | "description": "Hackahackalangolango", 6 | "license": "MIT", 7 | "keywords": [], 8 | "scripts": { 9 | "prettier": "prettier -c --write \"**/*.{js,jsx,ts,tsx}\"", 10 | "lint": "eslint . --ext .ts", 11 | "build": "tsc", 12 | "test": "ts-node ./src/test/test.ts" 13 | }, 14 | "devDependencies": { 15 | "@types/node": "^14.6.1", 16 | "@typescript-eslint/eslint-plugin": "^3.10.1", 17 | "@typescript-eslint/parser": "^3.10.1", 18 | "eslint": "^7.7.0", 19 | "prettier": "^2.1.0", 20 | "ts-node": "^9.0.0" 21 | }, 22 | "dependencies": { 23 | "typescript": "^4.0.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/hacklang-loader/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hacklang-loader", 3 | "version": "0.1.0", 4 | "description": "", 5 | "main": "lib", 6 | "directories": { 7 | "lib": "lib" 8 | }, 9 | "scripts": { 10 | "test": "jest", 11 | "build": ":" 12 | }, 13 | "license": "MIT", 14 | "dependencies": { 15 | "loader-utils": "^2.0.0", 16 | "schema-utils": "^2.7.0" 17 | }, 18 | "peerDependencies": { 19 | "webpack": "^4.0.0 || ^5.0.0" 20 | }, 21 | "devDependencies": { 22 | "@types/jest": "^26.0.10", 23 | "@types/webpack": "^4.41.21", 24 | "jest": "^26.4.2", 25 | "memfs": "^3.2.0", 26 | "patch-package": "^6.2.2", 27 | "unionfs": "^4.4.0", 28 | "webpack": "^4.44.1" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/api.test.ts: -------------------------------------------------------------------------------- 1 | import { compile } from "../src"; 2 | 3 | describe("compiler api works", () => { 4 | test("default works", () => { 5 | const { code } = compile("const wuto = 'foxtrot';"); 6 | 7 | expect(code).toBe("yuto wuto gleich 'foxtrot';\n"); 8 | }); 9 | 10 | test("toHacklang", () => { 11 | const { code } = compile("yuto wuto gleich 'foxtrot';\n", { 12 | outputLang: "hacklang", 13 | }); 14 | 15 | expect(code).toBe("const wuto = 'foxtrot';\n\n"); 16 | }); 17 | 18 | test("toJavaScript works", () => { 19 | const { code } = compile("const wuto = 'foxtrot';", { 20 | outputLang: "javascript", 21 | }); 22 | 23 | expect(code).toBe("yuto wuto gleich 'foxtrot';\n"); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /patches/@lerna+package+3.16.0.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/@lerna/package/index.js b/node_modules/@lerna/package/index.js 2 | index 67b9970..6e2912e 100644 3 | --- a/node_modules/@lerna/package/index.js 4 | +++ b/node_modules/@lerna/package/index.js 5 | @@ -209,6 +209,11 @@ class Package { 6 | if (resolved.registry || resolved.type === "directory") { 7 | // a version (1.2.3) OR range (^1.2.3) OR directory (file:../foo-pkg) 8 | depCollection[depName] = `${savePrefix}${depVersion}`; 9 | + 10 | + // https://github.com/lerna/lerna/pull/2450 11 | + if (resolved.explicitWorkspace) { 12 | + depCollection[depName] = `workspace:${depCollection[depName]}`; 13 | + } 14 | } else if (resolved.gitCommittish) { 15 | // a git url with matching committish (#v1.2.3 or #1.2.3) 16 | const [tagPrefix] = /^\D*/.exec(resolved.gitCommittish); 17 | -------------------------------------------------------------------------------- /packages/hacklang-loader/lib/index.js: -------------------------------------------------------------------------------- 1 | const loaderUtils = require("loader-utils"); 2 | const validateOptions = require("schema-utils"); 3 | const options = require("./options.json"); 4 | const hacklang = require("hacklang"); 5 | 6 | /** 7 | * @param {string} source 8 | * @this {import("webpack").loader.LoaderContext} 9 | */ 10 | module.exports = function loader(source) { 11 | /** @type {import("@types/webpack").Logger} */ 12 | const logger = this.getLogger("hacklang-loader"); 13 | const schema = loaderUtils.getOptions(this); 14 | 15 | validateOptions(schema, options, { 16 | name: "HackLang Loader", 17 | baseDataPath: "options", 18 | }); 19 | 20 | /** @type {string | null} */ 21 | let result = null; 22 | try { 23 | result = hacklang.compile(source); 24 | logger.info(`Compiled ${this.resourcePath}`); 25 | } catch (err) { 26 | return this.callback(err); 27 | } 28 | 29 | this.callback(null, result.code); 30 | }; 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 HackClub 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/hacklang-loader/test/index.test.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const fs = require("fs"); 3 | const webpack = require("webpack"); 4 | 5 | const webpackConfig = require("./webpack.config"); 6 | 7 | test("webpack works", (/** @type {object} */ cb) => { 8 | const compiler = webpack(webpackConfig); 9 | 10 | compiler.run((err, stats) => { 11 | handleError(err, stats, cb); 12 | 13 | const output = fs.readFileSync(path.join(__dirname, "fixture/dist/main.js")); 14 | expect(output).not.toMatch(/throw new Error/); 15 | expect(output).toMatch(/luke variable gleich/); 16 | 17 | cb(); 18 | }); 19 | }); 20 | 21 | /** 22 | * @param {Error} err 23 | * @param {webpack.Stats} stats 24 | * @param {object} cb 25 | */ 26 | function handleError(err, stats, cb) { 27 | if (err) { 28 | console.error(err); 29 | if (err.details) { 30 | console.error(err.details); 31 | } 32 | cb.fail(new Error(err || err.details)); 33 | } 34 | 35 | const info = stats.toJson(); 36 | if (stats.hasErrors()) { 37 | console.error(info.errors); 38 | cb.fail(new Error(info.errors)); 39 | } 40 | 41 | if (stats.hasWarnings()) { 42 | console.warn(info.warnings); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hacklang-meta", 3 | "private": true, 4 | "version": "0.1.0", 5 | "description": "A programming language created for and by hackclubbers. Basically javascript but the keywords are replaced with hackclubbers' names.", 6 | "main": "index.js", 7 | "scripts": { 8 | "prettier": "prettier -c --write \"**/*.{js,jsx,ts,tsx}\"", 9 | "lint": "eslint . --ext .ts", 10 | "build": "yarn lerna exec -- yarn build", 11 | "test": "yarn lerna exec -- yarn test", 12 | "postinstall": "patch-package" 13 | }, 14 | "workspaces": [ 15 | "packages/*" 16 | ], 17 | "directories": { 18 | "example": "example" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/hackclub/hacklang.git" 23 | }, 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/hackclub/hacklang/issues" 27 | }, 28 | "homepage": "https://github.com/hackclub/hacklang#readme", 29 | "devDependencies": { 30 | "@types/node": "^14.6.1", 31 | "@typescript-eslint/eslint-plugin": "^3.10.1", 32 | "@typescript-eslint/parser": "^3.10.1", 33 | "eslint": "^7.7.0", 34 | "lerna": "^3.22.1", 35 | "prettier": "^2.1.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /patches/@lerna+package-graph+3.18.5.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/@lerna/package-graph/index.js b/node_modules/@lerna/package-graph/index.js 2 | index f860af4..8fa64c1 100644 3 | --- a/node_modules/@lerna/package-graph/index.js 4 | +++ b/node_modules/@lerna/package-graph/index.js 5 | @@ -57,8 +57,18 @@ class PackageGraph extends Map { 6 | // Yarn decided to ignore https://github.com/npm/npm/pull/15900 and implemented "link:" 7 | // As they apparently have no intention of being compatible, we have to do it for them. 8 | // @see https://github.com/yarnpkg/yarn/issues/4212 9 | - const spec = graphDependencies[depName].replace(/^link:/, "file:"); 10 | + let spec = graphDependencies[depName].replace(/^link:/, "file:"); 11 | + 12 | + // npa doesn't support the explicit workspace: protocol, supported by 13 | + // pnpm and Yarn. 14 | + // https://github.com/lerna/lerna/pull/2450 15 | + const explicitWorkspace = /^workspace:/.test(spec); 16 | + if (explicitWorkspace) { 17 | + spec = spec.replace(/^workspace:/, ""); 18 | + } 19 | + 20 | const resolved = npa.resolve(depName, spec, currentNode.location); 21 | + resolved.explicitWorkspace = explicitWorkspace 22 | 23 | if (!depNode) { 24 | // it's an external dependency, store the resolution and bail 25 | -------------------------------------------------------------------------------- /patches/@lerna+npm-publish+3.18.5.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/@lerna/npm-publish/npm-publish.js b/node_modules/@lerna/npm-publish/npm-publish.js 2 | index ee6ad13..6a31d17 100644 3 | --- a/node_modules/@lerna/npm-publish/npm-publish.js 4 | +++ b/node_modules/@lerna/npm-publish/npm-publish.js 5 | @@ -32,6 +32,15 @@ const PublishConfig = figgyPudding( 6 | } 7 | ); 8 | 9 | +function stripWorkspaceProtocolFromDeps(deps) { 10 | + if (!deps) return; 11 | + for (const [name, version] of Object.entries(deps)) { 12 | + if (version.startsWith("workspace:")) { 13 | + deps[name] = version.slice(10); 14 | + } 15 | + } 16 | +} 17 | + 18 | function npmPublish(pkg, tarFilePath, _opts, otpCache) { 19 | const { scope } = npa(pkg.name); 20 | // pass only the package scope to libnpmpublish 21 | @@ -67,6 +76,10 @@ function npmPublish(pkg, tarFilePath, _opts, otpCache) { 22 | manifest.publishConfig.tag = opts.tag; 23 | } 24 | 25 | + stripWorkspaceProtocolFromDeps(manifest.dependencies); 26 | + stripWorkspaceProtocolFromDeps(manifest.peerDependencies); 27 | + stripWorkspaceProtocolFromDeps(manifest.devDependencies); 28 | + 29 | return otplease(innerOpts => publish(manifest, tarData, innerOpts), opts, otpCache).catch(err => { 30 | opts.log.silly("", err); 31 | opts.log.error(err.code, (err.body && err.body.error) || err.message); 32 | -------------------------------------------------------------------------------- /packages/hacklang/src/api.ts: -------------------------------------------------------------------------------- 1 | import { ErrorHandler } from "./ErrorHandler"; 2 | import { Language } from "./grammer/Language"; 3 | import { Parser } from "./Parser"; 4 | import { Tokenizer } from "./Tokenizer"; 5 | 6 | interface HacklangResult { 7 | code: string | null; 8 | } 9 | 10 | interface CompileOptions { 11 | outputLang?: "hacklang" | "javascript"; 12 | } 13 | 14 | export function compile(input: string, options: CompileOptions = {}): HacklangResult { 15 | const toHacklang = (input: string): Parser => { 16 | const tokey = new Tokenizer(input, new ErrorHandler(input), Language.HACKLANG); 17 | tokey.tokenize(); 18 | 19 | const parsey = new Parser(tokey.bag, Language.HACKLANG); 20 | parsey.parse(); 21 | 22 | return parsey; 23 | }; 24 | const toJavaScript = (input: string): Parser => { 25 | const tokey = new Tokenizer(input, new ErrorHandler(input), Language.JAVASCRIPT); 26 | tokey.tokenize(); 27 | 28 | const parsey = new Parser(tokey.bag, Language.JAVASCRIPT); 29 | parsey.parse(); 30 | 31 | return parsey; 32 | }; 33 | 34 | let parser: Parser | Record = {}; 35 | if (options.outputLang === void 0) { 36 | parser = toJavaScript(input); 37 | } else if (options.outputLang === "hacklang") { 38 | parser = toHacklang(input); 39 | } else if (options.outputLang === "javascript") { 40 | parser = toJavaScript(input); 41 | } 42 | 43 | if (parser && parser.output === undefined) { 44 | parser.output = null; 45 | } 46 | 47 | return { 48 | code: parser.output, 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /packages/hacklang/src/Parser.ts: -------------------------------------------------------------------------------- 1 | import { HackLangKeyword, JavaScriptKeyword, Keyword } from "./grammer/Keyword"; 2 | import { Language } from "./grammer/Language"; 3 | import { HackLangPunctuator, JavaScriptPunctuator, Punctuator } from "./grammer/Punctuator"; 4 | import { Token } from "./grammer/Token"; 5 | import { TokenType } from "./grammer/TokenType"; 6 | 7 | export class Parser { 8 | bag: Token[]; 9 | output: string | null = null; 10 | punctuator: { 11 | from: Punctuator; 12 | to: Punctuator; 13 | }; 14 | keyword: { 15 | from: Keyword; 16 | to: Keyword; 17 | }; 18 | 19 | constructor(bag: Token[], language: Language) { 20 | this.bag = bag; 21 | this.punctuator = { 22 | from: language === Language.JAVASCRIPT ? JavaScriptPunctuator : HackLangPunctuator, 23 | to: language === Language.JAVASCRIPT ? HackLangPunctuator : JavaScriptPunctuator, 24 | }; 25 | this.keyword = { 26 | from: language === Language.JAVASCRIPT ? JavaScriptKeyword : HackLangKeyword, 27 | to: language === Language.JAVASCRIPT ? HackLangKeyword : JavaScriptKeyword, 28 | }; 29 | } 30 | 31 | parse(): void { 32 | this.output = this.bag.reduce( 33 | (previousValue, currentValue) => (previousValue += this.getValue(currentValue)), 34 | "" 35 | ); 36 | } 37 | 38 | getValue(token: Token): string { 39 | const getKeyFromValue = (obj: any, value: string) => { 40 | return Object.keys(obj).find((key) => obj[key] === value); 41 | }; 42 | 43 | switch (token.type) { 44 | case TokenType.Keyword: 45 | return this.keyword.to[getKeyFromValue(this.keyword.from, token.value) as never]; 46 | case TokenType.Punctuator: 47 | return this.punctuator.to[getKeyFromValue(this.punctuator.from, token.value) as never]; 48 | default: 49 | return token.value; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/hacklang/src/grammer/Keyword.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Most langs will hard code their keywords but I wanted to make it easy to change the keywords so I did it like this. The keys represent the JavaScript semantic equivalent. 3 | */ 4 | 5 | export type Keyword = typeof JavaScriptKeyword; 6 | 7 | export const JavaScriptKeyword = { 8 | AWAIT: "await", 9 | BREAK: "break", 10 | CASE: "case", 11 | CATCH: "catch", 12 | CLASS: "class", 13 | CONST: "const", 14 | CONTINUE: "continue", 15 | DEBUGGER: "debugger", 16 | DEFAULT: "default", 17 | DELETE: "delete", 18 | DO: "do", 19 | ELSE: "else", 20 | ENUM: "enum", 21 | EXPORT: "export", 22 | EXTENDS: "extends", 23 | FALSE: "false", 24 | FINALLY: "finally", 25 | FOR: "for", 26 | FUNCTION: "function", 27 | IF: "if", 28 | IMPLEMENTS: "implements", 29 | IMPORT: "import", 30 | IN: "in", 31 | INSTANCEOF: "instanceof", 32 | INTERFACE: "interface", 33 | LET: "let", 34 | NEW: "new", 35 | NULL: "null", 36 | PACKAGE: "package", 37 | PRIVATE: "private", 38 | PROTECTED: "protected", 39 | PUBLIC: "public", 40 | RETURN: "return", 41 | SUPER: "super", 42 | SWITCH: "switch", 43 | STATIC: "static", 44 | THIS: "this", 45 | THROW: "throw", 46 | TRY: "try", 47 | TRUE: "true", 48 | TYPEOF: "typeof", 49 | VAR: "var", 50 | VOID: "void", 51 | WHILE: "while", 52 | WITH: "with", 53 | YIELD: "yield", 54 | 55 | // This isn't technically a keyword but ok... 56 | CONSTRUCTOR: "constructor", 57 | }; 58 | 59 | export const HackLangKeyword: typeof JavaScriptKeyword = { 60 | ...JavaScriptKeyword, 61 | VAR: "rice", 62 | CONST: "yuto", 63 | CLASS: "aditya", 64 | THROW: "aditya", 65 | THIS: "ken", 66 | IF: "winston", 67 | WHILE: "theo", 68 | CONSTRUCTOR: "zrl", 69 | IMPORT: "josh", 70 | LET: "luke", // I am your father 71 | /* if found please */ RETURN: "vincent", 72 | NEW: "caleb", 73 | FUNCTION: "sam", 74 | }; 75 | -------------------------------------------------------------------------------- /packages/hacklang/src/grammer/Punctuator.ts: -------------------------------------------------------------------------------- 1 | export type Punctuator = typeof JavaScriptPunctuator; 2 | 3 | export const JavaScriptPunctuator = { 4 | // Other 5 | COLON: ":", 6 | COMMA: ",", 7 | PERIOD: ".", 8 | SEMI_COLON: ";", 9 | 10 | // Grouping 11 | OPEN_BRAC: "[", 12 | CLOSE_BRAC: "]", 13 | OPEN_BRACE: "{", 14 | CLOSE_BRACE: "}", 15 | OPEN_PAREN: "(", 16 | CLOSE_PAREN: ")", 17 | 18 | // Increment 19 | INCREMENT: "++", 20 | DECREMENT: "--", 21 | 22 | // Arithmetic 23 | UNARY_PLUS: "+", 24 | UNARY_MINUS: "-", 25 | DIVISION: "/", 26 | MULTIPLICATION: "*", 27 | REMAINDER: "%", 28 | EXPONENTIAL: "**", 29 | 30 | // Relational 31 | LESS_THAN: "<", 32 | GREATER_THAN: ">", 33 | GREATER_EQUAL_THAN: ">=", 34 | LESS_EQUAL_THAN: "<=", 35 | 36 | // Equality 37 | INEQUALITY: "!=", 38 | EQUAL: "==", 39 | NON_IDENTITY: "!==", 40 | STRICT_EQUAL: "===", 41 | 42 | // Bitwise 43 | BITWISE_NOT: "~", 44 | BITWISE_LEFT: "<<", 45 | BITWISE_RIGHT: ">>", 46 | BITWISE_UNSIGNED_RIGHT: ">>>", 47 | BITWISE_AND: "&", 48 | BITWISE_OR: "|", 49 | BITWISE_XOR: "^", 50 | 51 | // Logic 52 | LOGICAL_NOT: "!", 53 | LOGICAL_AND: "&&", 54 | LOGICAL_OR: "||", 55 | TERNARY: "?", 56 | 57 | // Assignment 58 | ASSIGNMENT: "=", 59 | MULTIPLICATION_ASSIGNMENT: "*=", 60 | ADDITION_ASSIGNMENT: "+=", 61 | DIVISION_ASSIGNMENT: "/=", 62 | SUBTRACTION_ASSIGNMENT: "-=", 63 | LEFT_SHIFT_ASSIGNMENT: "<<=", 64 | RIGHT_SHIFT_ASSIGNMENT: ">>=", 65 | UNSIGNED_RIGHT_SHIFT_ASSIGNMENT: ">>>=", 66 | BITWISE_AND_ASSIGNMENT: "&=", 67 | BITWISE_OR_ASSIGNMENT: "|=", 68 | BITWISE_XOR_ASSIGNMENT: "^=", 69 | LOGICAL_OR_ASSIGNMENT: "||=", 70 | LOGICAL_AND_ASSIGNMENT: "&&=", 71 | LOGICAL_NULLISH_ASSIGNMENT: "??=", 72 | }; 73 | 74 | export const HackLangPunctuator: typeof JavaScriptPunctuator = { 75 | ...JavaScriptPunctuator, 76 | ASSIGNMENT: "gleich", 77 | EQUAL: "gleichgleich", 78 | STRICT_EQUAL: "gleichgleichgleich", 79 | }; 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hacklang 2 | 3 | A programming language created for and by hackclubbers. Basically javascript but the keywords are replaced with hackclubbers' names. 4 | 5 | To contribute, please check out [CONTRIBUTING.md](https://github.com/hackclub/hacklang/blob/master/CONTRIBUTING.md) 6 | Want a keyword? Apply [here](https://airtable.com/shr69zRPjSpmrLdmh) 7 | 8 | | **hacklang keyword or operator** | **JavaScript Equivalent** | 9 | | ------------------------------------------------------------------ | ------------------------- | 10 | | `rice` ([Rishi Kothari](https://github.com/rishiosaur)) | `var` | 11 | | `yuto` ([Yuto N.](https://github.com/starptr)) | `const` | 12 | | `aditya` ([Aditya Rawat](https://github.com/aditya1rawat)) | `class` | 13 | | `c a r o t` ([Kevin Dai](https://github.com/TheOneKevin/)) | `throw` | 14 | | `gleich` ([Matt Gleich](https://github.com/Matt-Gleich)) | `=` | 15 | | `ken` ([Ken Bro Mueller](https://github.com/kenmueller)) | `this` | 16 | | `winston` ([Winston Iskandar](https://github.com/winstoniskandar)) | `if` | 17 | | `theo` ([Theo Bleier](https://github.com/tmb)) | `while` | 18 | | `zrl` ([Zach Latta](https://github.com/zachlatta)) | `constructor` | 19 | | `josh` ([Josh Brown](https://github.com/jbis9051)) | `import` | 20 | | `luke` ([Luke Carapezza](https://github.com/lukec11)) | `let` | 21 | | `vincent` ([Vincent Dörig](https://github.com/vincentdoerig)) | `return` | 22 | | `caleb` ([Caleb Denio](https://github.com/cjdenio)) | `new` | 23 | | `kayley` ([Kayley Seow](https://github.com/kayleyseow)) | `else` | 24 | | `breadduck` (Kate) | `await` | 25 | | `lachlanjc` ([Lachlan Campbell](https://github.com/lachlanjc)) | `async` | 26 | | `chalk` ([Isac Portillo](https://github.com/ChalkHuman)) | `export` | 27 | | `safin` ([Safin Singh](https://github.com/safinsingh)) | `case` | 28 | | `eleeza` ([Eleeza A.](https://github.com/E-Lee-Za)) | `try` | 29 | | `claire` ([Claire Wang](https://github.com/clairebookworm)) | `continue` | 30 | | `neel` ([Neel Redkar](https://github.com/neelr)) | `default` | 31 | | `taylor` ([Taylor Lee](https://github.com/taylorylee)) | `yield` | 32 | | `ongzz` ([Ong Zhi Zheng](https://github.com/Fogeinator)) | `void` | 33 | | `sam` ([Sam Poder](https://github.com/sampoder)) | `function` | 34 | | `anthony` ([Anthony Kung](https://github.com/Anthonykung)) | `break` | 35 | | `sarthak` ([Sarthak](https://github.com/sarthaktexas)) | `catch` | 36 | | `srsh` ([Saharsh Yeruva](https://github.com/saharshy29)) | `public` | 37 | | `amogh` ([Amogh Chaubey](https://github.com/qmogh)) | `private` | 38 | | `steggy` ([Steggy](https://github.com/rfblock)) | `debugger` | 39 | | `jacob` ([Jacob Haap](https://github.com/jacobhaap)) | `static` | 40 | | `kofler` ([Edwin Kofler](https://github.com/eankeen)) | `from` | 41 | | `fayd` ([Faisal Sayed](https://github.com/faisalsayed10)) | `as` | 42 | | `aaryan` ([Aaryan Porwal](https://github.com/aaryanporwal)) | `super` | 43 | -------------------------------------------------------------------------------- /packages/hacklang-loader/test/fixture/dist/main.js: -------------------------------------------------------------------------------- 1 | /******/ (function(modules) { // webpackBootstrap 2 | /******/ // The module cache 3 | /******/ var installedModules = {}; 4 | /******/ 5 | /******/ // The require function 6 | /******/ function __webpack_require__(moduleId) { 7 | /******/ 8 | /******/ // Check if module is in cache 9 | /******/ if(installedModules[moduleId]) { 10 | /******/ return installedModules[moduleId].exports; 11 | /******/ } 12 | /******/ // Create a new module (and put it into the cache) 13 | /******/ var module = installedModules[moduleId] = { 14 | /******/ i: moduleId, 15 | /******/ l: false, 16 | /******/ exports: {} 17 | /******/ }; 18 | /******/ 19 | /******/ // Execute the module function 20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 21 | /******/ 22 | /******/ // Flag the module as loaded 23 | /******/ module.l = true; 24 | /******/ 25 | /******/ // Return the exports of the module 26 | /******/ return module.exports; 27 | /******/ } 28 | /******/ 29 | /******/ 30 | /******/ // expose the modules object (__webpack_modules__) 31 | /******/ __webpack_require__.m = modules; 32 | /******/ 33 | /******/ // expose the module cache 34 | /******/ __webpack_require__.c = installedModules; 35 | /******/ 36 | /******/ // define getter function for harmony exports 37 | /******/ __webpack_require__.d = function(exports, name, getter) { 38 | /******/ if(!__webpack_require__.o(exports, name)) { 39 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); 40 | /******/ } 41 | /******/ }; 42 | /******/ 43 | /******/ // define __esModule on exports 44 | /******/ __webpack_require__.r = function(exports) { 45 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 46 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 47 | /******/ } 48 | /******/ Object.defineProperty(exports, '__esModule', { value: true }); 49 | /******/ }; 50 | /******/ 51 | /******/ // create a fake namespace object 52 | /******/ // mode & 1: value is a module id, require it 53 | /******/ // mode & 2: merge all properties of value into the ns 54 | /******/ // mode & 4: return value when already ns object 55 | /******/ // mode & 8|1: behave like require 56 | /******/ __webpack_require__.t = function(value, mode) { 57 | /******/ if(mode & 1) value = __webpack_require__(value); 58 | /******/ if(mode & 8) return value; 59 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; 60 | /******/ var ns = Object.create(null); 61 | /******/ __webpack_require__.r(ns); 62 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); 63 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); 64 | /******/ return ns; 65 | /******/ }; 66 | /******/ 67 | /******/ // getDefaultExport function for compatibility with non-harmony modules 68 | /******/ __webpack_require__.n = function(module) { 69 | /******/ var getter = module && module.__esModule ? 70 | /******/ function getDefault() { return module['default']; } : 71 | /******/ function getModuleExports() { return module; }; 72 | /******/ __webpack_require__.d(getter, 'a', getter); 73 | /******/ return getter; 74 | /******/ }; 75 | /******/ 76 | /******/ // Object.prototype.hasOwnProperty.call 77 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 78 | /******/ 79 | /******/ // __webpack_public_path__ 80 | /******/ __webpack_require__.p = ""; 81 | /******/ 82 | /******/ 83 | /******/ // Load entry module and return exports 84 | /******/ return __webpack_require__(__webpack_require__.s = "./src/index.js"); 85 | /******/ }) 86 | /************************************************************************/ 87 | /******/ ({ 88 | 89 | /***/ "./src/index.js": 90 | /*!**********************!*\ 91 | !*** ./src/index.js ***! 92 | \**********************/ 93 | /*! no static exports found */ 94 | /***/ (function(module, exports) { 95 | 96 | eval("throw new Error(\"Module parse failed: Unexpected token (1:5)\\nFile was processed with these loaders:\\n * ../../lib/index.js\\nYou may need an additional loader to handle the result of these loaders.\\n> luke variable gleich \\\"value\\\";\\n| \\n| \");\n\n//# sourceURL=webpack:///./src/index.js?"); 97 | 98 | /***/ }) 99 | 100 | /******/ }); -------------------------------------------------------------------------------- /packages/hacklang/src/Tokenizer.ts: -------------------------------------------------------------------------------- 1 | import { ErrorHandler } from "./ErrorHandler"; 2 | import { HackLangKeyword, JavaScriptKeyword, Keyword } from "./grammer/Keyword"; 3 | import { Language } from "./grammer/Language"; 4 | import { HackLangPunctuator, JavaScriptPunctuator, Punctuator } from "./grammer/Punctuator"; 5 | import { Location, Token } from "./grammer/Token"; 6 | import { TokenType } from "./grammer/TokenType"; 7 | 8 | export class Tokenizer { 9 | static specialChars = ["/", '"', "'", "`"]; 10 | bag: Token[] = []; 11 | line = 0; 12 | char = 0; 13 | codeChar = 0; 14 | code: string; 15 | errorHandler: ErrorHandler; 16 | punctuator: { 17 | from: Punctuator; 18 | to: Punctuator; 19 | }; 20 | keyword: { 21 | from: Keyword; 22 | to: Keyword; 23 | }; 24 | 25 | constructor(code: string, errorHandler: ErrorHandler, language: Language) { 26 | this.code = code; 27 | this.errorHandler = errorHandler; 28 | this.punctuator = { 29 | from: language === Language.JAVASCRIPT ? JavaScriptPunctuator : HackLangPunctuator, 30 | to: language === Language.JAVASCRIPT ? HackLangPunctuator : JavaScriptPunctuator, 31 | }; 32 | this.keyword = { 33 | from: language === Language.JAVASCRIPT ? JavaScriptKeyword : HackLangKeyword, 34 | to: language === Language.JAVASCRIPT ? HackLangKeyword : JavaScriptKeyword, 35 | }; 36 | } 37 | 38 | tokenize(): void { 39 | const lines = this.code.split("\n"); 40 | const punctuatorValues = Object.values(this.punctuator.from); 41 | while (this.line < lines.length) { 42 | const line = lines[this.line]; 43 | let toilet: string[] = []; // (buffer) 44 | this.char = 0; 45 | const startLoc: Location = { 46 | line: this.line, 47 | column: this.char, 48 | }; 49 | 50 | while (this.char < line.length) { 51 | const char = line[this.char]; 52 | this.char++; 53 | this.codeChar++; 54 | 55 | const nextChar = line[this.char]; 56 | const currentString = toilet.join(""); 57 | 58 | if (char.match(/\s/)) { 59 | if (currentString.length === 0 || currentString.match(/^\s+$/)) { 60 | toilet.push(char); 61 | } 62 | if (!nextChar.match(/\s/)) { 63 | this.flush(toilet, startLoc); 64 | if (!currentString.match(/^\s+$/)) { 65 | toilet = [char]; 66 | } else { 67 | toilet = []; 68 | } 69 | } 70 | continue; 71 | } 72 | 73 | if (currentString.match(/^\s+$/)) { 74 | this.flush(toilet, startLoc); 75 | toilet = []; 76 | } 77 | 78 | if ( 79 | nextChar && 80 | !nextChar.match(/\d|\w/) && 81 | punctuatorValues.some((punc) => punc.startsWith(nextChar)) 82 | ) { 83 | this.flush([...toilet, char], startLoc); 84 | toilet = []; 85 | continue; 86 | } 87 | 88 | if ( 89 | punctuatorValues.some((punc) => punc === currentString) && 90 | !punctuatorValues.some((punc) => punc.startsWith(currentString + char)) 91 | ) { 92 | this.flush(toilet, startLoc); 93 | toilet = [char]; 94 | continue; 95 | } 96 | 97 | if (Tokenizer.specialChars.includes(char)) { 98 | this.flush(toilet, startLoc); 99 | toilet = []; 100 | this.char--; 101 | this.codeChar--; 102 | this.handleSpecialChar(); 103 | this.char++; 104 | this.codeChar++; 105 | continue; 106 | } 107 | 108 | toilet.push(char); 109 | } 110 | if (toilet.length > 0) { 111 | this.flush(toilet, startLoc); 112 | toilet = []; 113 | } 114 | this.bag.push({ 115 | type: TokenType.WHITESPACE, 116 | value: "\n", 117 | loc: { 118 | start: { ...startLoc }, 119 | end: { 120 | line: this.line, 121 | column: this.char + 1, 122 | }, 123 | }, 124 | }); 125 | this.line++; 126 | this.codeChar++; 127 | } 128 | } 129 | 130 | flush(buffer: string[], startLocation: Location): void { 131 | const punctuatorValues = Object.values(this.punctuator.from); 132 | const bagPush = (type: TokenType) => { 133 | this.bag.push({ 134 | type, 135 | value: string, 136 | loc: { 137 | start: { ...startLocation }, 138 | end: { 139 | line: this.line, 140 | column: this.char, 141 | }, 142 | }, 143 | }); 144 | startLocation.column = this.char; 145 | }; 146 | 147 | const string = buffer.join(""); 148 | if (string.length === 0) { 149 | return; 150 | } 151 | if (string.match(/^\s+$/)) { 152 | // ^^ all whitespace 153 | bagPush(TokenType.WHITESPACE); 154 | return; 155 | } 156 | if (punctuatorValues.some((punc) => punc === string)) { 157 | bagPush(TokenType.Punctuator); 158 | return; 159 | } 160 | if ([this.keyword.from.TRUE, this.keyword.from.FALSE].includes(string)) { 161 | bagPush(TokenType.BooleanLiteral); 162 | return; 163 | } 164 | if (string === this.keyword.from.NULL) { 165 | bagPush(TokenType.NullLiteral); 166 | return; 167 | } 168 | if (Object.values(this.keyword.from).includes(string)) { 169 | bagPush(TokenType.Keyword); 170 | return; 171 | } 172 | if (string.match(/^\d+$/)) { 173 | bagPush(TokenType.NumericLiteral); 174 | return; 175 | } 176 | if (!string.match(/^[A-Za-z]+\w*$/)) { 177 | this.errorHandler.createError("Invalid Identifier: " + string, startLocation); 178 | } 179 | bagPush(TokenType.Identifier); 180 | } 181 | 182 | handleSpecialChar(): void { 183 | const lines = this.code.split("\n"); 184 | const startChar = lines[this.line][this.char]; 185 | const startLocation: Location = { 186 | line: this.line, 187 | column: this.char, 188 | }; 189 | const buffer: string[] = []; 190 | buffer.push(startChar); 191 | this.char++; 192 | 193 | // eslint-disable-next-line no-constant-condition 194 | while (true) { 195 | const line = lines[this.line]; 196 | const char = line[this.char]; 197 | buffer.push(char); 198 | if (char === startChar && line[this.char - 1] !== "\\") { 199 | break; 200 | } 201 | this.char++; 202 | this.codeChar++; 203 | if (this.char > line.length) { 204 | if (startChar !== "`") { 205 | this.errorHandler.createError("Unexpected end of line", startLocation); 206 | } 207 | if (this.line + 1 > lines.length) { 208 | this.errorHandler.createError("Unexpected end of file", startLocation); 209 | } 210 | this.char = 0; 211 | this.line++; 212 | this.codeChar++; 213 | } 214 | } 215 | const bagPush = (type: TokenType) => { 216 | this.bag.push({ 217 | type, 218 | value: buffer.join(""), 219 | loc: { 220 | start: { ...startLocation }, 221 | end: { 222 | line: this.line, 223 | column: this.char, 224 | }, 225 | }, 226 | }); 227 | startLocation.column = this.char; 228 | }; 229 | 230 | switch (startChar) { 231 | case '"': 232 | case "'": 233 | bagPush(TokenType.StringLiteral); 234 | break; 235 | case "/": // TODO support comments 236 | bagPush(TokenType.RegularExpression); 237 | break; 238 | case "`": 239 | bagPush(TokenType.Template); 240 | break; 241 | default: 242 | this.errorHandler.createError("Unexpected character: " + startChar, startLocation); 243 | } 244 | } 245 | } 246 | --------------------------------------------------------------------------------