From 30adc89e884103f20bf3d828fd37a7e9a1bbb329 Mon Sep 17 00:00:00 2001 From: milesartemius Date: Sun, 6 Jun 2021 18:17:34 +0300 Subject: [PATCH 1/2] python added --- .github/workflows/test.yml | 5 ++ core/langs.ts | 4 +- langs/python.ts | 133 +++++++++++++++++++++++++++++++++++++ test/langs/python.test.ts | 38 +++++++++++ 4 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 langs/python.ts create mode 100644 test/langs/python.test.ts diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 087aed7..9445f6a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,6 +14,11 @@ jobs: - name: Checkout 🛎️ uses: actions/checkout@v2.3.4 + - name: Collect Python 🐍 + uses: actions/setup-python@v2 + with: + python-version: '3.8' + - name: Install 🔧 run: | npm install diff --git a/core/langs.ts b/core/langs.ts index 9cf7145..3aa289e 100644 --- a/core/langs.ts +++ b/core/langs.ts @@ -3,6 +3,7 @@ import { getPostfix, getPrefix, multiplePrefix } from "./constants"; import typescript from "../langs/typescript"; import javascript from "../langs/javascript"; +import python from "../langs/python"; @@ -49,7 +50,8 @@ type Constructor = { act: (str: InEntry[][], set: Settings) => Generic, arg: str */ export const LANGUAGES = { "TypeScript (Node.js)": typescript as Constructor, - "JavaScript (DOM)": javascript as Constructor + "JavaScript (DOM)": javascript as Constructor, + "Python3": python as Constructor } /** diff --git a/langs/python.ts b/langs/python.ts new file mode 100644 index 0000000..c987816 --- /dev/null +++ b/langs/python.ts @@ -0,0 +1,133 @@ +import { TYPES, Settings, toast, Generic, varify } from "../core/langs"; +import { convert, ESCAPE_END, ESCAPE_SEPARATOR, ESCAPE_START, ESCAPE_TERMINATE, InEntry, OutEntry } from "../core/converter"; +import { CLASS_CODES } from "../core/constants"; + + + +// Settings variable. +let readable = true; + +// Function to generate a special function, used for generation more easily-read code. +function gen_read () { + const start = `${ESCAPE_START}{'${ESCAPE_SEPARATOR}'.join([str(code) for code in list(codes)])}${ESCAPE_END}`; + const end = `${ESCAPE_START}${ESCAPE_TERMINATE}${ESCAPE_END}`; + return 'def _style(string: str, *codes: int)-> str:\n' + + '\t"""\n' + + '\tFunction styling string with given codes.\n' + + '\tstring -- string to be styled.\n' + + '\t*codes -- ASCII code to be applied to string.\n' + + '\treturns styled string.\n' + + '\t"""\n' + + `\treturn f"${start}{string}${end}"`; +} + +// Main function, constructing the code. +function construct (str: InEntry[][], set: Settings): Generic { + readable = set.readable; + if (set.args.length > 0) toast('Settings unexpected!'); + const codes = str.map((current: InEntry[], index: number) => { + return create_function_for_line(current, index); + }); + const read = readable ? Object.keys(constants).map((value: string): string => { + return `${varify(value)} = ${constants[value]}` + }).join('\n') + `\n\n\n${gen_read()}\n\n\n` : ""; + return { code: `${read}${codes.join("\n\n")}`, formatting: CODE_STYLE }; +} + + + +// Implementation of common types for typescript. +function type (type: string): string { + switch (type) { + case TYPES.int: + return 'int'; + case TYPES.float: + return 'float'; + case TYPES.char: + case TYPES.string: + return 'str'; + case TYPES.int_array: + return 'list[int]'; + case TYPES.string_array: + return 'list[str]'; + } +} + +// Use "\s" instead of whitespaces and tabs. +const CODE_STYLE = [ + { format: /def|return/g, css: 'color: GoldenRod' }, + { format: /(?<=:\s?|->\s?)(?:int|float|str|list\[int]|list\[str])/g, css: 'color: GoldenRod' }, + { format: /[0-9]*/g, css: 'color: CornflowerBlue' }, + + { format: /(?<=def).*(?=\([\s\S]*\))/g, css: 'color: Gold' }, + { format: /(?<=\.).*?(?=\([\s\S]*\))/g, css: 'color: Gold' }, + + { format: /f"(?=.*")|(?<=f".*)"/g, css: 'color: SeaGreen' }, + { format: /(?<=\{).*?(?=})/g, css: 'font-weight: 700' }, + { format: /\\u[0-9]+b\[.*?m/g, css: 'color: Khaki; font-weight: 700' }, + + { format: /#.*/g, css: 'color: Silver' }, + { format: /"""[\s\S]*?"""/g, css: 'color: SeaGreen' } +]; + + + +// Function to escape quotes in string and wrap string itself in quotes if needed. +function escape (str: string, qot: string): string { + let res = str.replace(/"/g, '\\"').replace(/'/g, "\\'"); + res = !!qot ? (res.replace(/\{/g, "{{").replace(/}/g, "}}")) : res; + res = !!qot ? (qot + res + qot) : res; + return res; +} + +// Variables for human-readable code. +const constants = {}; + +// Code and comment are generated at the same time. +function create_function_for_line (entries: InEntry[], iter: number): string { + if (readable) entries.forEach((entry: InEntry) => { + entry.classes.forEach((value: string) => { + constants[value] = CLASS_CODES[value]; + }); + }); + + const declaration = entries.map((value: InEntry): string => { + let currentVar = ""; + if (!!value.var_name) { + let currentVarType = type(value.var_type); + currentVar += value.var_name + ((!!currentVarType) ? (': ' + currentVarType) : ""); + } + return currentVar; + }).filter((value: string): boolean => { + return !!value; + }); + + const sample: string[] = []; + const code: string[] = []; + convert(entries, true).forEach((value: OutEntry) => { + const prefix = (value.prefix.length > 0) ? `${ESCAPE_START}${value.prefix.join(ESCAPE_SEPARATOR)}${ESCAPE_END}` : ""; + const postfix = (value.prefix.length > 0) ? (ESCAPE_START + ESCAPE_TERMINATE + ESCAPE_END) : ""; + const prefixes = readable ? value.prefix.map((num: number): string => { + return varify(Object.keys(constants).find((value: string): boolean => { + return constants[value] == num; + })); + }) : []; + if (value.is_var) { + if (readable && (value.prefix.length > 0)) code.push(`{_style(${value.value}, ${prefixes.join(', ')})}`); + else code.push(`${prefix}{${value.value}}${postfix}`); + sample.push(`[${value.value}]`); + } else { + if (readable && (value.prefix.length > 0)) code.push(`{_style(${escape(value.value, '"')}, ${prefixes.join(', ')})}`); + else code.push(`${prefix}${escape(value.value, "")}${postfix}`); + sample.push(value.value); + } + }); + + return `def print${iter}thLine(${declaration.join(", ")}):\n` + + `\t""" Function writing "${sample.join("")}" to console. """\n` + + `\tprint(f"${code.join("")}")\n`; +} + + + +export default { act: construct, arg: null }; diff --git a/test/langs/python.test.ts b/test/langs/python.test.ts new file mode 100644 index 0000000..80e42c0 --- /dev/null +++ b/test/langs/python.test.ts @@ -0,0 +1,38 @@ +import assert from "assert"; +import * as converter from "../../core/converter"; +import * as langs from "../../core/langs"; +import { check_code, check_default } from "../langs.test"; + + + +describe("Python3 test", () => { + const entry_list: converter.InEntry[] = [ + { classes: ["for-blue", "sty-bold"], value: "Test" }, + { classes: [], value: " " }, + { classes: ["cross", "back-white"], value: "string" } + ]; + const def = check_default(entry_list); + const lang = "Python3"; + const lib_file = "lib.py", code_file = "code.py"; + const code_code = "from lib import print0thLine\nprint0thLine()\n"; + + describe("Python user-readable should match default language output", () => { + process.env["readable-check"] = JSON.stringify(true); + process.env["code-args-input"] = JSON.stringify(""); + const lib_code = langs.construct(lang, [entry_list]).code; + const output = check_code(lib_file, lib_code, code_file, code_code, "python3 ./code.py"); + it(`${def} == ${output}`, function () { + assert.strictEqual(def == output, true); + }); + }); + + describe("Python non-user-readable should match default language output", () => { + process.env["readable-check"] = JSON.stringify(false); + process.env["code-args-input"] = JSON.stringify(""); + const lib_code = langs.construct(lang, [entry_list]).code; + const output = check_code(lib_file, lib_code, code_file, code_code, "python3 ./code.py"); + it(`${def} == ${output}`, function () { + assert.strictEqual(def == output, true); + }); + }); +}); From ab0053833da478fbf90c9cc6a80515fd6afe8725 Mon Sep 17 00:00:00 2001 From: milesartemius Date: Sun, 6 Jun 2021 18:20:19 +0300 Subject: [PATCH 2/2] quote fixed --- langs/python.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/langs/python.ts b/langs/python.ts index c987816..dbb262b 100644 --- a/langs/python.ts +++ b/langs/python.ts @@ -117,7 +117,7 @@ function create_function_for_line (entries: InEntry[], iter: number): string { else code.push(`${prefix}{${value.value}}${postfix}`); sample.push(`[${value.value}]`); } else { - if (readable && (value.prefix.length > 0)) code.push(`{_style(${escape(value.value, '"')}, ${prefixes.join(', ')})}`); + if (readable && (value.prefix.length > 0)) code.push(`{_style(${escape(value.value, "'")}, ${prefixes.join(', ')})}`); else code.push(`${prefix}${escape(value.value, "")}${postfix}`); sample.push(value.value); }