Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion core/langs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";



Expand Down Expand Up @@ -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
}

/**
Expand Down
133 changes: 133 additions & 0 deletions langs/python.ts
Original file line number Diff line number Diff line change
@@ -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 };
38 changes: 38 additions & 0 deletions test/langs/python.test.ts
Original file line number Diff line number Diff line change
@@ -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);
});
});
});