UNPKG

noshift.js

Version:
641 lines (638 loc) 18.3 kB
// src/convert.ts var noShiftMap = { // "^@^@^@": "```", // マルチバックチック "^4^[": "${", // テンプレート式展開開始 "^0": "^", "^1": "!", "^2": '"', "^3": "#", "^4": "$", "^5": "%", "^7": "'", "^8": "(", "^9": ")", "^-": "=", "^^": "~", "^\\": "_", "^@": "`", "^[": "{", "^]": "}", "^;": "+", "^:": "*", "^,": "<", "^.": ">", "^/": "?" }; var symbolToNoshift = Object.fromEntries( Object.entries(noShiftMap).filter(([key, value]) => key.length === 2 && value.length === 1).map(([key, value]) => [value, key]) ); var keywordReplacements = [ ["@or^-", "|="], ["@and^-", "&="], ["or^-", "||="], ["and^-", "&&="], ["@or", "|"], ["@and", "&"] ]; var wordKeywordReplacements = [ ["or", "||"], ["and", "&&"] ]; function convertNsjsToJs(nsjsCode, options = {}) { const capitalizeInStrings = options.capitalizeInStrings !== false; let jsCode = ""; let i = 0; const len_ns = nsjsCode.length; const STATE = { NORMAL: "NORMAL", // 普通のコード IN_DQ_STRING: "IN_DQ_STRING", // " … " の中 IN_SQ_STRING: "IN_SQ_STRING", // ' … ' の中 IN_BT_SINGLE_STRING: "IN_BT_SINGLE_STRING", // ` … ` の中 IN_BT_MULTI_STRING: "IN_BT_MULTI_STRING", // ``` … ``` の中 IN_TEMPLATE_EXPRESSION: "IN_TEMPLATE_EXPRESSION", // ${ … } の中 RAW_DQ_IN_EXPR: "RAW_DQ_IN_EXPR", // テンプレート式内の " … " の中 (NoShift 変換なし) RAW_SQ_IN_EXPR: "RAW_SQ_IN_EXPR", // テンプレート式内の ' … ' の中 (NoShift 変換なし) IN_LINE_COMMENT: "IN_LINE_COMMENT", // // … の中 IN_BLOCK_COMMENT: "IN_BLOCK_COMMENT" // /^: … ^:/ の中 }; let currentState = STATE.NORMAL; const stateStack = []; const sortedNsKeys = Object.keys(noShiftMap).sort( (a, b) => b.length - a.length ); function tryConsumeNsjsSequence() { let allowGeneral = false; if (currentState === STATE.NORMAL || currentState === STATE.IN_TEMPLATE_EXPRESSION) { allowGeneral = true; } for (const nsKey of sortedNsKeys) { if (!nsjsCode.startsWith(nsKey, i)) continue; if ((nsKey === "^4^[" || nsjsCode.startsWith("^4[", i)) && (currentState === STATE.IN_BT_SINGLE_STRING || currentState === STATE.IN_BT_MULTI_STRING)) { jsCode += "${"; i += nsKey === "^4^[" ? nsKey.length : 3; stateStack.push(currentState); currentState = STATE.IN_TEMPLATE_EXPRESSION; return true; } if (nsKey === "^]" && currentState === STATE.IN_TEMPLATE_EXPRESSION) { jsCode += "}"; i += nsKey.length; currentState = stateStack.pop(); return true; } if (nsKey === "^2" && currentState === STATE.NORMAL) { jsCode += '"'; i += nsKey.length; stateStack.push(currentState); currentState = STATE.IN_DQ_STRING; return true; } if (nsKey === "^2" && currentState === STATE.IN_DQ_STRING) { jsCode += '"'; i += nsKey.length; currentState = stateStack.pop(); return true; } if (nsKey === "^7" && currentState === STATE.NORMAL) { jsCode += "'"; i += nsKey.length; stateStack.push(currentState); currentState = STATE.IN_SQ_STRING; return true; } if (nsKey === "^7" && currentState === STATE.IN_SQ_STRING) { jsCode += "'"; i += nsKey.length; currentState = stateStack.pop(); return true; } if (nsKey === "^@" && (currentState === STATE.NORMAL || currentState === STATE.IN_TEMPLATE_EXPRESSION)) { jsCode += "`"; i += nsKey.length; stateStack.push(currentState); currentState = STATE.IN_BT_SINGLE_STRING; return true; } if (nsKey === "^@" && currentState === STATE.IN_BT_SINGLE_STRING) { jsCode += "`"; i += nsKey.length; currentState = stateStack.pop(); return true; } if (nsKey === "^2" && currentState === STATE.IN_TEMPLATE_EXPRESSION) { jsCode += '"'; i += nsKey.length; stateStack.push(currentState); currentState = STATE.RAW_DQ_IN_EXPR; return true; } if (nsKey === "^7" && currentState === STATE.IN_TEMPLATE_EXPRESSION) { jsCode += "'"; i += nsKey.length; stateStack.push(currentState); currentState = STATE.RAW_SQ_IN_EXPR; return true; } if (nsKey === "^2" && currentState === STATE.RAW_DQ_IN_EXPR) { jsCode += '"'; i += nsKey.length; currentState = stateStack.pop(); return true; } if (nsKey === "^7" && currentState === STATE.RAW_SQ_IN_EXPR) { jsCode += "'"; i += nsKey.length; currentState = stateStack.pop(); return true; } if (allowGeneral) { jsCode += noShiftMap[nsKey]; i += nsKey.length; return true; } } return false; } while (i < len_ns) { let consumed = false; if (currentState === STATE.IN_DQ_STRING) { if (nsjsCode.startsWith("\\^6", i)) { jsCode += "^6"; i += 3; consumed = true; } else if (nsjsCode.startsWith("\\^2", i)) { jsCode += "^2"; i += 3; consumed = true; } else if (nsjsCode.startsWith("\\\\", i)) { jsCode += "\\\\\\\\"; i += 2; consumed = true; } } else if (currentState === STATE.IN_SQ_STRING) { if (nsjsCode.startsWith("\\^6", i)) { jsCode += "^6"; i += 3; consumed = true; } else if (nsjsCode.startsWith("\\^7", i)) { jsCode += "^7"; i += 3; consumed = true; } else if (nsjsCode.startsWith("\\\\", i)) { jsCode += "\\\\\\\\"; i += 2; consumed = true; } } else if (currentState === STATE.RAW_DQ_IN_EXPR) { if (nsjsCode.startsWith("\\^6", i)) { jsCode += "^6"; i += 3; consumed = true; } else if (nsjsCode.startsWith("\\^2", i)) { jsCode += "^2"; i += 3; consumed = true; } else if (nsjsCode.startsWith("\\\\", i)) { jsCode += "\\\\\\\\"; i += 2; consumed = true; } else if (nsjsCode.startsWith("^2", i)) { jsCode += '"'; i += 2; currentState = stateStack.pop(); consumed = true; } if (consumed) { continue; } else { jsCode += nsjsCode[i]; i += 1; continue; } } else if (currentState === STATE.RAW_SQ_IN_EXPR) { if (nsjsCode.startsWith("\\^6", i)) { jsCode += "^6"; i += 3; consumed = true; } else if (nsjsCode.startsWith("\\^7", i)) { jsCode += "^7"; i += 3; consumed = true; } else if (nsjsCode.startsWith("\\\\", i)) { jsCode += "\\\\\\\\"; i += 2; consumed = true; } else if (nsjsCode.startsWith("^7", i)) { jsCode += "'"; i += 2; currentState = stateStack.pop(); consumed = true; } if (consumed) { continue; } else { jsCode += nsjsCode[i]; i += 1; continue; } } else if (currentState === STATE.IN_BT_SINGLE_STRING) { if (nsjsCode.startsWith("\\^6", i)) { jsCode += "^6"; i += 3; consumed = true; } else if (nsjsCode.startsWith("\\^@", i)) { jsCode += "^@"; i += 3; consumed = true; } else if (nsjsCode.startsWith("\\\\", i)) { jsCode += "\\\\\\\\"; i += 2; consumed = true; } } else if (currentState === STATE.IN_BT_MULTI_STRING) { if (nsjsCode.startsWith("\\\\", i)) { jsCode += "\\\\\\\\"; i += 2; consumed = true; } } if (!consumed && currentState !== STATE.IN_LINE_COMMENT && currentState !== STATE.IN_BLOCK_COMMENT) { if (nsjsCode.startsWith("^6", i)) { const inString = currentState === STATE.IN_DQ_STRING || currentState === STATE.IN_SQ_STRING || currentState === STATE.IN_BT_SINGLE_STRING || currentState === STATE.IN_BT_MULTI_STRING; if (!inString || capitalizeInStrings) { i += 2; if (i < len_ns) { jsCode += nsjsCode[i].toUpperCase(); i += 1; } consumed = true; } } } if (!consumed) { if (currentState === STATE.NORMAL && nsjsCode.startsWith("//", i)) { jsCode += "//"; i += 2; stateStack.push(currentState); currentState = STATE.IN_LINE_COMMENT; consumed = true; } else if (currentState === STATE.IN_LINE_COMMENT) { if (nsjsCode[i] === "\n") { jsCode += "\n"; i += 1; currentState = stateStack.pop(); } else { jsCode += nsjsCode[i]; i += 1; } consumed = true; } else if (currentState === STATE.NORMAL && nsjsCode.startsWith("/^:", i)) { jsCode += "/*"; i += 3; stateStack.push(currentState); currentState = STATE.IN_BLOCK_COMMENT; consumed = true; } else if (currentState === STATE.IN_BLOCK_COMMENT && nsjsCode.startsWith("^:/", i)) { jsCode += "*/"; i += 3; currentState = stateStack.pop(); consumed = true; } else if (currentState === STATE.IN_BLOCK_COMMENT) { jsCode += nsjsCode[i]; i += 1; consumed = true; } } if (!consumed && (currentState === STATE.NORMAL || currentState === STATE.IN_TEMPLATE_EXPRESSION)) { for (const [kw, replacement] of keywordReplacements) { if (nsjsCode.startsWith(kw, i)) { jsCode += replacement; i += kw.length; consumed = true; break; } } if (!consumed) { for (const [kw, replacement] of wordKeywordReplacements) { if (nsjsCode.startsWith(kw, i)) { const prevChar = i > 0 ? nsjsCode[i - 1] : " "; const prevIsBoundary = !/[a-zA-Z0-9_$]/.test(prevChar); const afterPos = i + kw.length; const nextChar = afterPos < len_ns ? nsjsCode[afterPos] : " "; const nextIsBoundary = !/[a-zA-Z0-9_$]/.test(nextChar); if (prevIsBoundary && nextIsBoundary) { jsCode += replacement; i += kw.length; consumed = true; break; } } } } } if (!consumed) { consumed = tryConsumeNsjsSequence(); } if (!consumed) { jsCode += nsjsCode[i]; i += 1; } } if (stateStack.length > 0) { if (!options._silent) { console.warn( `Warning: Unmatched literal/templating states. Final state: ${currentState}, Remaining stack: ${stateStack.join( ", " )}` ); } } return jsCode; } var validCaretKeys = new Set(Object.keys(noShiftMap).map((k) => k[1])); validCaretKeys.add("6"); function diagnose(nsjsCode) { const errors = []; const lines = nsjsCode.split("\n"); let state = "NORMAL"; const stateStack = []; const openPositions = []; for (let lineNum = 0; lineNum < lines.length; lineNum++) { const line = lines[lineNum]; if (state === "LINE_COMMENT") { state = stateStack.pop() || "NORMAL"; } for (let col = 0; col < line.length; col++) { const ch = line[col]; const next = col + 1 < line.length ? line[col + 1] : void 0; const next2 = col + 2 < line.length ? line[col + 2] : void 0; if (ch === "\\" && next === "^") { col += 2; continue; } if (ch === "\\" && next === "\\") { col += 1; continue; } if (state === "BLOCK_COMMENT") { if (ch === "^" && next === ":" && next2 === "/") { state = stateStack.pop() || "NORMAL"; openPositions.pop(); col += 2; } continue; } if (state === "LINE_COMMENT") { continue; } if (state === "DQ") { if (ch === "^" && next === "2") { state = stateStack.pop() || "NORMAL"; openPositions.pop(); col += 1; } continue; } if (state === "SQ") { if (ch === "^" && next === "7") { state = stateStack.pop() || "NORMAL"; openPositions.pop(); col += 1; } continue; } if (state === "BT") { if (ch === "^" && next === "4" && (next2 === "^" || next2 === "[")) { if (next2 === "^" && col + 3 < line.length && line[col + 3] === "[") { stateStack.push(state); openPositions.push({ line: lineNum + 1, column: col + 1, type: "TEMPLATE_EXPR" }); state = "TEMPLATE_EXPR"; col += 3; } else if (next2 === "[") { stateStack.push(state); openPositions.push({ line: lineNum + 1, column: col + 1, type: "TEMPLATE_EXPR" }); state = "TEMPLATE_EXPR"; col += 2; } continue; } if (ch === "^" && next === "@") { state = stateStack.pop() || "NORMAL"; openPositions.pop(); col += 1; } continue; } if (state === "TEMPLATE_EXPR") { if (ch === "^" && next === "]") { state = stateStack.pop() || "NORMAL"; openPositions.pop(); col += 1; continue; } } if (ch === "/" && next === "/") { stateStack.push(state); state = "LINE_COMMENT"; break; } if (ch === "/" && next === "^" && next2 === ":") { stateStack.push(state); openPositions.push({ line: lineNum + 1, column: col + 1, type: "BLOCK_COMMENT" }); state = "BLOCK_COMMENT"; col += 2; continue; } if (ch === "^" && next === "2") { stateStack.push(state); openPositions.push({ line: lineNum + 1, column: col + 1, type: "DQ" }); state = "DQ"; col += 1; continue; } if (ch === "^" && next === "7") { stateStack.push(state); openPositions.push({ line: lineNum + 1, column: col + 1, type: "SQ" }); state = "SQ"; col += 1; continue; } if (ch === "^" && next === "@") { stateStack.push(state); openPositions.push({ line: lineNum + 1, column: col + 1, type: "BT" }); state = "BT"; col += 1; continue; } if (ch === "^" && next === "6") { if (col + 2 >= line.length && lineNum === lines.length - 1) { errors.push({ line: lineNum + 1, column: col + 1, message: "^6 at end of file with no following character to capitalize." }); } col += 2; continue; } if (ch === "^" && next !== void 0) { if (!validCaretKeys.has(next)) { errors.push({ line: lineNum + 1, column: col + 1, message: `Unknown sequence '^${next}'.` }); } col += 1; continue; } if (ch === "^" && next === void 0 && lineNum === lines.length - 1) { errors.push({ line: lineNum + 1, column: col + 1, message: "Lone '^' at end of file." }); } } } while (openPositions.length > 0) { const pos = openPositions.pop(); const labels = { DQ: "string literal (^2...^2)", SQ: "string literal (^7...^7)", BT: "template literal (^@...^@)", BLOCK_COMMENT: "block comment (/^:...^:/)", TEMPLATE_EXPR: "template expression (^4^[...^])" }; errors.push({ line: pos.line, column: pos.column, message: `Unclosed ${labels[pos.type] || pos.type} opened here.` }); } return errors; } // package.json var package_default = { name: "noshift.js", version: "0.15.1", description: "Joke language.", bin: { nsc: "./dist/cli.cjs" }, main: "./dist/index.cjs", module: "./dist/index.mjs", types: "./dist/index.d.ts", exports: { ".": { import: { types: "./dist/index.d.mts", default: "./dist/index.mjs" }, require: { types: "./dist/index.d.cts", default: "./dist/index.cjs" } } }, type: "module", scripts: { build: "tsup", test: "vitest run", "test:watch": "vitest", typecheck: "tsc --noEmit", format: "prettier --write ./src/" }, repository: { type: "git", url: "git+https://github.com/otoneko1102/NoShift.js.git" }, keywords: [ "noshift", "nsjs", "joke", "language", "lang" ], author: "otoneko.", license: "MIT", bugs: { url: "https://github.com/otoneko1102/NoShift.js/issues" }, homepage: "https://noshift.js.org", dependencies: { commander: "^14.0.3" }, devDependencies: { "@types/node": "^25.3.0", prettier: "^3.8.1", tsup: "^8.5.1", tsx: "^4.21.0", typescript: "^5.9.3", vitest: "^4.0.18" }, engines: { node: ">=22.12.0" } }; // src/header.ts function getVersion() { return package_default.version; } function addHeader(js, version) { const ver = version ?? getVersion(); const header = `// Generated by NoShift.js ${ver}`; if (js.startsWith("#!")) { const newlineIndex = js.indexOf("\n"); if (newlineIndex === -1) { return js + "\n" + header + "\n"; } const shebang = js.slice(0, newlineIndex + 1); const rest = js.slice(newlineIndex + 1); return shebang + header + "\n" + rest; } return header + "\n" + js; } // src/index.ts function compile(source, options = {}) { let outputText = convertNsjsToJs(source, { capitalizeInStrings: options.capitalizeInStrings !== false }); if (!options.noHeader && outputText.length > 0) { outputText = addHeader(outputText); } return { outputText }; } function diagnose2(source) { return diagnose(source); } var index_default = compile; export { compile, index_default as default, diagnose2 as diagnose }; //# sourceMappingURL=index.mjs.map