Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/python-wasm
Path: blob/main/python/pylang/tools/compile.ts
1396 views
1
/*
2
* Copyright (C) 2021 William Stein <[email protected]>
3
* Copyright (C) 2015 Kovid Goyal <kovid at kovidgoyal.net>
4
*
5
* Distributed under terms of the BSD license
6
*/
7
8
import { dirname, join, normalize, resolve } from "path";
9
import { mkdirSync, writeFileSync, readFileSync } from "fs";
10
import { readFile } from "fs/promises";
11
import { runInThisContext } from "vm";
12
import { getImportDirs, once } from "./utils";
13
import createCompiler from "./compiler";
14
15
const PyLang = createCompiler();
16
17
// TODO
18
type Parsed = any;
19
20
// Async because also capable of reading to EOF from stdin.
21
async function readWholeFile(filename?: string): Promise<string> {
22
if (filename) {
23
return (await readFile(filename)).toString();
24
}
25
26
const chunks: string[] = [];
27
process.stdin.setEncoding("utf-8");
28
process.stdin.on("data", (data) => {
29
chunks.push(data.toString());
30
});
31
process.openStdin();
32
await once(process.stdin, "end");
33
return chunks.join("");
34
}
35
36
function process_cache_dir(dir: string): string {
37
dir = resolve(normalize(dir));
38
mkdirSync(dir, { recursive: true });
39
return dir;
40
}
41
42
interface OutputOptions {
43
beautify?: boolean;
44
private_scope?: boolean;
45
omit_baselib?: boolean;
46
keep_docstrings?: boolean;
47
discard_asserts?: boolean;
48
module_cache_dir?: string;
49
comments?: Function | boolean;
50
baselib_plain?: string;
51
sage?: boolean; // sage-style preparsing
52
}
53
54
export default async function Compile({
55
argv,
56
src_path,
57
lib_path,
58
}: {
59
argv: {
60
cache_dir?: string;
61
bare?: boolean;
62
omit_baselib?: boolean;
63
keep_docstrings?: boolean;
64
discard_asserts?: boolean;
65
files: string[];
66
import_path: string;
67
output?: string;
68
execute?: boolean;
69
stats?: boolean;
70
filename_for_stdin?: string;
71
comments?: string;
72
sage?: boolean;
73
};
74
src_path: string;
75
lib_path: string;
76
}): Promise<void> {
77
// configure settings for the output
78
const module_cache_dir = argv.cache_dir
79
? process_cache_dir(argv.cache_dir)
80
: "";
81
const outputOptions = {
82
beautify: true,
83
private_scope: !argv.bare,
84
omit_baselib: argv.omit_baselib,
85
keep_docstrings: argv.keep_docstrings,
86
discard_asserts: argv.discard_asserts,
87
module_cache_dir,
88
} as OutputOptions;
89
90
const files: string[] = argv.files.slice();
91
const stats: { [name: string]: number } = {};
92
const count = files.length || 1;
93
94
function parseFile(code: string, filename: string): Parsed {
95
return PyLang.parse(code, {
96
filename,
97
basedir: filename !== "<stdin>" ? dirname(filename) : undefined,
98
libdir: join(src_path, "lib"),
99
import_dirs: getImportDirs(argv.import_path),
100
discard_asserts: argv.discard_asserts,
101
module_cache_dir,
102
sage: argv.sage,
103
});
104
}
105
106
function writeOutput(output) {
107
if (argv.output) {
108
if (argv.output == "/dev/stdout") {
109
// Node's filesystem module doesn't write directly to /dev/stdout
110
console.log(output);
111
} else if (argv.output == "/dev/stderr") {
112
console.error(output);
113
} else {
114
writeFileSync(argv.output, output, "utf8");
115
}
116
} else if (!argv.execute) {
117
console.log(output);
118
}
119
if (argv.execute) {
120
// @ts-ignore
121
global.require = require;
122
runInThisContext(output);
123
}
124
}
125
126
function timeIt(name: string, f: () => void): void {
127
var t1 = new Date().getTime();
128
f();
129
if (argv.stats) {
130
var spent = new Date().getTime() - t1;
131
if (stats[name]) {
132
stats[name] += spent;
133
} else {
134
stats[name] = spent;
135
}
136
}
137
}
138
139
async function compileSingleFile(code: string): Promise<void> {
140
let topLevel;
141
timeIt("parse", () => {
142
const filename = files[0] || argv.filename_for_stdin || "<stdin>";
143
try {
144
topLevel = parseFile(code, filename);
145
} catch (err) {
146
if (!(err instanceof PyLang.SyntaxError)) {
147
throw err;
148
}
149
console.error(err.toString());
150
process.exit(1);
151
}
152
});
153
154
let output;
155
try {
156
output = new PyLang.OutputStream(outputOptions);
157
} catch (err) {
158
if (err instanceof PyLang.DefaultsError) {
159
console.error(err.message);
160
process.exit(1);
161
}
162
throw err;
163
}
164
165
timeIt("generate", () => {
166
topLevel.print(output);
167
});
168
169
output = output.get();
170
writeOutput(output);
171
}
172
173
if (argv.comments) {
174
if (/^\//.test(argv.comments)) {
175
outputOptions.comments = new Function("return(" + argv.comments + ")")();
176
} else if (argv.comments == "all") {
177
outputOptions.comments = true;
178
} else {
179
outputOptions.comments = (_, comment) => {
180
const { value } = comment;
181
const { type } = comment;
182
if (type == "comment2") {
183
// multiline comment
184
return /@preserve|@license|@cc_on/i.test(value);
185
}
186
};
187
}
188
}
189
190
if (!argv.omit_baselib) {
191
outputOptions.baselib_plain = readFileSync(
192
join(lib_path, "baselib-plain-pretty.js"),
193
"utf-8"
194
);
195
}
196
197
if (files.filter((el) => el == "-").length > 1) {
198
console.error(
199
"ERROR: Can only read a single file from STDIN (two or more dashes specified)"
200
);
201
process.exit(1);
202
}
203
204
if (files.length > 0) {
205
for (const filename of files) {
206
await compileSingleFile(await readWholeFile(filename));
207
}
208
} else {
209
await compileSingleFile(await readWholeFile());
210
}
211
212
if (argv.stats) {
213
console.error(`Timing information (compressed ${count} files):`);
214
for (const name in stats)
215
console.error(`- ${name}: ${(stats[name] / 1000).toFixed(3)}s`);
216
}
217
}
218
219