@jpetit/toolkit 3.0.19 → 3.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -60,7 +60,13 @@ export abstract class Compiler {
60
60
  abstract compile(handler: Handler, directory: string, sourcePath: string): Promise<void>
61
61
 
62
62
  // Default implementation of execute for compiled languages
63
- async execute(handler: Handler, directory: string, inputPath: string, outputPath: string): Promise<void> {
63
+ async execute(
64
+ handler: Handler,
65
+ directory: string,
66
+ sourcePath: string,
67
+ inputPath: string,
68
+ outputPath: string,
69
+ ): Promise<void> {
64
70
  const executablePath = `solution.${this.extension()}.exe`
65
71
  if (!(await exists(join(directory, executablePath)))) {
66
72
  throw new Error(`Executable file ${executablePath} does not exist in directory ${directory}`)
@@ -1,8 +1,10 @@
1
- // TODO: all!
2
-
1
+ import tui from 'lib/tui'
3
2
  import { type Handler } from '..//types'
4
- import { nothing } from '..//utils'
5
3
  import { Compiler } from './base'
4
+ import { execa } from 'execa'
5
+ import { join } from 'path'
6
+ import { nothing, readText } from 'lib/utils'
7
+ import { rm } from 'fs/promises'
6
8
 
7
9
  export class Clojure_Compiler extends Compiler {
8
10
  id(): string {
@@ -39,6 +41,36 @@ export class Clojure_Compiler extends Compiler {
39
41
 
40
42
  async compile(handler: Handler, directory: string, sourcePath: string): Promise<void> {
41
43
  await nothing()
42
- throw new Error(`Not implemented`)
44
+
45
+ if (handler.source_modifier !== 'none') {
46
+ throw new Error(`source modifier not implemented`)
47
+ }
48
+
49
+ tui.warning(`No compilation available for Clojure`)
50
+ }
51
+
52
+ override async execute(
53
+ handler: Handler,
54
+ directory: string,
55
+ sourcePath: string,
56
+ inputPath: string,
57
+ outputPath: string,
58
+ ): Promise<void> {
59
+ const fullInputPath = join(directory, inputPath)
60
+ const fullOutputPath = join(directory, outputPath)
61
+ await rm(fullOutputPath, { force: true })
62
+ const input = await readText(fullInputPath)
63
+
64
+ tui.command(`clj -M ${sourcePath} < ${inputPath} > ${outputPath}`)
65
+
66
+ const { exitCode } = await execa({
67
+ reject: false,
68
+ input,
69
+ stdout: { file: fullOutputPath },
70
+ stderr: 'inherit',
71
+ cwd: directory,
72
+ })`clj -M ${sourcePath}`
73
+
74
+ if (exitCode !== 0) throw new Error(`Execution failed for ${sourcePath}`)
43
75
  }
44
76
  }
@@ -52,13 +52,13 @@ export class GCC_Compiler extends Compiler {
52
52
  cwd: directory,
53
53
  })`gcc ${this.flags1().split(' ')} ${sourcePath} -o ${exePath}`
54
54
  } else if (handler.source_modifier === 'no_main') {
55
- tui.command(`gcc ${this.flags1()} ${sourcePath} main.cc -o ${exePath}`)
55
+ tui.command(`gcc ${this.flags1()} ${sourcePath} main.c -o ${exePath}`)
56
56
  await execa({
57
57
  reject: false,
58
58
  stderr: 'inherit',
59
59
  stdout: 'inherit',
60
60
  cwd: directory,
61
- })`gcc ${this.flags1().split(' ')} ${sourcePath} main.cc -o ${exePath}`
61
+ })`gcc ${this.flags1().split(' ')} ${sourcePath} main.c -o ${exePath}`
62
62
  } else {
63
63
  throw new Error(`Unknown source modifier: ${handler.source_modifier}`)
64
64
  }
@@ -4,6 +4,9 @@ import { Compiler } from './base'
4
4
  import { rm, exists } from 'fs/promises'
5
5
  import { join } from 'path'
6
6
  import { execa } from 'execa'
7
+ import { read } from 'fs'
8
+ import { concatText, readText, writeText } from 'lib/utils'
9
+ import { dir } from 'console'
7
10
 
8
11
  export class GHC_Compiler extends Compiler {
9
12
  id(): string {
@@ -39,22 +42,36 @@ export class GHC_Compiler extends Compiler {
39
42
  }
40
43
 
41
44
  async compile(handler: Handler, directory: string, sourcePath: string): Promise<void> {
42
- const exePath = sourcePath + '.exe'
43
- const fullExePath = join(directory, exePath)
44
- await rm(fullExePath, { force: true })
45
+ const outputPath = sourcePath + '.exe'
46
+ const fullOutputPath = join(directory, outputPath)
47
+ await rm(fullOutputPath, { force: true })
45
48
 
46
49
  if (handler.source_modifier === 'none') {
47
- tui.command(`ghc ${this.flags1()} ${sourcePath} -o ${exePath}`)
50
+ tui.command(`ghc ${this.flags1()} ${sourcePath} -o ${outputPath}`)
48
51
  await execa({
49
52
  reject: false,
50
53
  stderr: 'inherit',
51
54
  stdout: 'inherit',
52
55
  cwd: directory,
53
- })`ghc ${this.flags1().split(' ')} ${sourcePath} -o ${exePath}`
56
+ })`ghc ${this.flags1().split(' ')} ${sourcePath} -o ${outputPath}`
57
+ } else if (handler.source_modifier === 'no_main') {
58
+ const wrapperPath = `wrapper-${sourcePath}`
59
+
60
+ tui.command(`concat ${sourcePath} and main.hs into ${wrapperPath}`, { italic: true })
61
+ await concatText([join(directory, sourcePath), join(directory, 'main.hs')], join(directory, wrapperPath))
62
+
63
+ tui.command(`ghc ${this.flags1()} ${wrapperPath} -o ${outputPath}`)
64
+
65
+ await execa({
66
+ reject: false,
67
+ stderr: 'inherit',
68
+ stdout: 'inherit',
69
+ cwd: directory,
70
+ })`ghc ${this.flags1().split(' ')} ${wrapperPath} -o ${outputPath}`
54
71
  } else {
55
72
  throw new Error(`Unknown source modifier: ${handler.source_modifier}`)
56
73
  }
57
74
 
58
- if (!(await exists(fullExePath))) throw new Error(`Compilation failed for ${sourcePath}`)
75
+ if (!(await exists(fullOutputPath))) throw new Error(`Compilation failed for ${sourcePath}`)
59
76
  }
60
77
  }
@@ -40,48 +40,57 @@ export class Python3_Compiler extends Compiler {
40
40
  }
41
41
 
42
42
  async compile(handler: Handler, directory: string, sourcePath: string): Promise<void> {
43
- const exePath = sourcePath + '.exe'
43
+ const wrapperPath = `wrapper-${sourcePath}`
44
+ const fullWrapperPath = join(directory, wrapperPath)
45
+ const fullSourcePath = join(directory, sourcePath)
46
+ const source = await readText(fullSourcePath)
44
47
 
48
+ tui.command(`wrap ${sourcePath} to ${wrapperPath}`, { italic: true })
49
+ let wrapperSource: string
45
50
  if (handler.source_modifier === 'none') {
46
- tui.command(`tweak ${sourcePath} to ${exePath}`, { italic: true })
47
- const source = await readText(join(directory, sourcePath))
48
- const sourceModified = source
51
+ wrapperSource = source
49
52
  .replace('import turtle', 'import turtle_pil as turtle')
50
53
  .replace('from turtle import', 'from turtle_pil import')
51
- await writeText(join(directory, exePath), sourceModified)
52
54
  } else if (handler.source_modifier === 'no_main' || handler.source_modifier === 'structs') {
53
- tui.command(`cat ${sourcePath} main.py > ${exePath}`)
54
- const solution = await readText(join(directory, sourcePath))
55
55
  const main = await readText(join(directory, 'main.py'))
56
- const solutionWithMain = solution + '\n\n\n' + main
57
- await writeText(join(directory, exePath), solutionWithMain)
56
+ wrapperSource = source + '\n\n\n' + main
58
57
  } else {
59
58
  throw new Error(`Unknown source modifier: ${handler.source_modifier as string}`)
60
59
  }
60
+ await writeText(fullWrapperPath, wrapperSource)
61
61
 
62
- tui.command(`python3 -m py_compile ${exePath}`)
62
+ tui.command(`python3 -m py_compile ${wrapperPath}`)
63
63
 
64
64
  const { exitCode } = await execa({
65
65
  reject: false,
66
66
  stderr: 'inherit',
67
67
  stdout: 'inherit',
68
68
  cwd: directory,
69
- })`python3 -m py_compile ${exePath}`
69
+ })`python3 -m py_compile ${wrapperPath}`
70
70
 
71
71
  if (exitCode !== 0) throw new Error(`Compilation failed for ${sourcePath}`)
72
72
  }
73
73
 
74
- override async execute(handler: Handler, directory: string, inputPath: string, outputPath: string): Promise<void> {
75
- const exePath = 'solution.py.exe'
76
- if (!(await exists(join(directory, exePath)))) {
77
- throw new Error(`Executable file ${exePath} does not exist in directory ${directory}`)
74
+ override async execute(
75
+ handler: Handler,
76
+ directory: string,
77
+ sourcePath: string,
78
+ inputPath: string,
79
+ outputPath: string,
80
+ ): Promise<void> {
81
+ const wrapperPath = `wrapper-${sourcePath}`
82
+ const fullWrapperPath = join(directory, wrapperPath)
83
+
84
+ if (!(await exists(join(directory, wrapperPath)))) {
85
+ throw new Error(`Executable file ${wrapperPath} does not exist in directory ${directory}`)
78
86
  }
87
+
79
88
  const fullInputPath = join(directory, inputPath)
80
89
  const fullOutputPath = join(directory, outputPath)
81
90
  await rm(fullOutputPath, { force: true })
82
91
  const input = await readText(fullInputPath)
83
92
 
84
- tui.command(`python3 ${exePath} < ${inputPath} > ${outputPath}`)
93
+ tui.command(`python3 ${wrapperPath} < ${inputPath} > ${outputPath}`)
85
94
 
86
95
  const { exitCode } = await execa({
87
96
  reject: false,
@@ -89,8 +98,8 @@ export class Python3_Compiler extends Compiler {
89
98
  stdout: { file: fullOutputPath },
90
99
  stderr: 'inherit',
91
100
  cwd: directory,
92
- })`python3 ${exePath}`
101
+ })`python3 ${wrapperPath}`
93
102
 
94
- if (exitCode !== 0) throw new Error(`Execution failed for ${exePath}`)
103
+ if (exitCode !== 0) throw new Error(`Execution failed for ${wrapperPath}`)
95
104
  }
96
105
  }
@@ -61,7 +61,13 @@ export class RunHaskell_Compiler extends Compiler {
61
61
  if (exitCode !== 0) throw new Error(`Compilation failed for ${sourcePath}`)
62
62
  }
63
63
 
64
- override async execute(handler: Handler, directory: string, inputPath: string, outputPath: string): Promise<void> {
64
+ override async execute(
65
+ handler: Handler,
66
+ directory: string,
67
+ sourcePath: string,
68
+ inputPath: string,
69
+ outputPath: string,
70
+ ): Promise<void> {
65
71
  const mergedPath = `solution-${inputPath}.hs.exe`
66
72
 
67
73
  tui.command(`Merging solution.hs and ${inputPath} into ${mergedPath}`, { italic: true })
@@ -61,7 +61,13 @@ export class RunPython_Compiler extends Compiler {
61
61
  if (exitCode !== 0) throw new Error(`Compilation failed for ${sourcePath}`)
62
62
  }
63
63
 
64
- override async execute(handler: Handler, directory: string, inputPath: string, outputPath: string): Promise<void> {
64
+ override async execute(
65
+ handler: Handler,
66
+ directory: string,
67
+ sourcePath: string,
68
+ inputPath: string,
69
+ outputPath: string,
70
+ ): Promise<void> {
65
71
  const exePath = 'solution.py.exe'
66
72
  if (!(await exists(join(directory, exePath)))) {
67
73
  throw new Error(`Executable file ${exePath} does not exist in directory ${directory}`)
package/lib/maker.ts CHANGED
@@ -293,15 +293,21 @@ export class Maker {
293
293
  }
294
294
  }
295
295
 
296
- async makeCorrect(testcase: string, compiler: Compiler): Promise<ExecutionResult> {
297
- return await this.runTestcase(testcase, `${testcase}.inp`, `${testcase}.cor`, compiler)
296
+ async makeCorrect(testcase: string, compiler: Compiler, sourcePath: string): Promise<ExecutionResult> {
297
+ return await this.runTestcase(testcase, `${testcase}.inp`, `${testcase}.cor`, compiler, sourcePath)
298
298
  }
299
299
 
300
- async runTestcase(testcase: string, input: string, output: string, compiler: Compiler): Promise<ExecutionResult> {
300
+ async runTestcase(
301
+ testcase: string,
302
+ input: string,
303
+ output: string,
304
+ compiler: Compiler,
305
+ sourcePath: string,
306
+ ): Promise<ExecutionResult> {
301
307
  let error = false
302
308
  const start = Date.now()
303
309
  try {
304
- await compiler.execute(this.handler, this.directory, input, output)
310
+ await compiler.execute(this.handler, this.directory, sourcePath, input, output)
305
311
  } catch (e) {
306
312
  tui.error(`Execution failed for testcase '${testcase}'`)
307
313
  error = true
@@ -339,7 +345,7 @@ export class Maker {
339
345
  const results: ExecutionResult[] = []
340
346
 
341
347
  for (const testcase of this.testcases) {
342
- results.push(await this.makeCorrect(testcase, compiler))
348
+ results.push(await this.makeCorrect(testcase, compiler, this.goldenSolution!))
343
349
  }
344
350
 
345
351
  console.log()
@@ -651,7 +657,13 @@ Draft generated with \\textbf{new-jutge-toolkit}.
651
657
  await tui.section('Executing testcases', async () => {
652
658
  for (const testcase of this.testcases) {
653
659
  results.push(
654
- await this.runTestcase(testcase, `${testcase}.inp`, `${testcase}.${extension}.out`, compiler),
660
+ await this.runTestcase(
661
+ testcase,
662
+ `${testcase}.inp`,
663
+ `${testcase}.${extension}.out`,
664
+ compiler,
665
+ program,
666
+ ),
655
667
  )
656
668
  }
657
669
 
package/lib/utils.ts CHANGED
@@ -35,6 +35,18 @@ export async function writeText(path: string, content: string): Promise<void> {
35
35
  await writeFile(path, content)
36
36
  }
37
37
 
38
+ export async function concatText(
39
+ inputPaths: string[],
40
+ outputPath: string,
41
+ separator: string = '\n\n\n',
42
+ ): Promise<void> {
43
+ let content = ''
44
+ for (const inputPath of inputPaths) {
45
+ content += (await readText(inputPath)) + separator
46
+ }
47
+ await writeText(outputPath, content)
48
+ }
49
+
38
50
  export async function writeYaml(path: string, data: any): Promise<void> {
39
51
  const content = YAML.stringify(data, null, 4)
40
52
  await writeText(path, content)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jpetit/toolkit",
3
3
  "description": "Toolkit to prepare problems for Jutge.org",
4
- "version": "3.0.19",
4
+ "version": "3.0.20",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
package/toolkit/make.ts CHANGED
@@ -37,6 +37,7 @@ interface MakeOptions extends MakerOptions {
37
37
  directories: string[]
38
38
  tasks: TaskType[]
39
39
  ignoreErrors: boolean
40
+ onlyErrors: boolean
40
41
  verbose?: boolean
41
42
  }
42
43
 
@@ -44,6 +45,7 @@ export const make = new Command('make')
44
45
  .description('Make problem components')
45
46
  .argument('[directories...]', 'problem directories', ['.'])
46
47
  .option('-i, --ignore-errors', 'ignore errors on a directory and continue processing', false)
48
+ .option('-e, --only-errors', 'only show errors at the final summary', false)
47
49
  .option('-t, --tasks <tasks...>', 'tasks to run: all, inspection, executables, corrects, pdf, text', ['all'])
48
50
  .option('-v, --verbose', 'verbose output (TODO)', false)
49
51
 
@@ -104,10 +106,11 @@ export const make = new Command('make')
104
106
  tui.warning('No problem directories found')
105
107
  }
106
108
  for (const directory of realDirectories) {
107
- tui.directory(directory)
108
109
  if (errors[directory]) {
110
+ tui.directory(directory)
109
111
  tui.error(` ${errors[directory]}`)
110
- } else {
112
+ } else if (!options.onlyErrors) {
113
+ tui.directory(directory)
111
114
  tui.success(` No errors found`)
112
115
  }
113
116
  }