@jpetit/toolkit 3.1.1 → 3.1.2
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.
- package/lib/ai.ts +200 -0
- package/lib/cleaner.ts +77 -0
- package/lib/compilers/base.ts +159 -0
- package/lib/compilers/clojure.ts +87 -0
- package/lib/compilers/gcc.ts +39 -0
- package/lib/compilers/ghc.ts +39 -0
- package/lib/compilers/gxx.ts +39 -0
- package/lib/compilers/index.ts +81 -0
- package/lib/compilers/java.ts +105 -0
- package/lib/compilers/python3.ts +112 -0
- package/lib/compilers/run-clojure.ts +101 -0
- package/lib/compilers/run-haskell.ts +117 -0
- package/lib/compilers/run-python.ts +103 -0
- package/lib/compilers/rust.ts +39 -0
- package/lib/create-with-jutgeai.ts +407 -0
- package/lib/create-with-template.ts +55 -0
- package/lib/data.ts +25 -0
- package/lib/doctor.ts +238 -0
- package/lib/generate.ts +171 -0
- package/lib/helpers.ts +48 -0
- package/lib/inspector.ts +253 -0
- package/lib/jutge_api_client.ts +4631 -0
- package/lib/maker.ts +613 -0
- package/lib/settings.ts +51 -0
- package/lib/tui.ts +152 -0
- package/lib/types.ts +55 -0
- package/lib/upload.ts +216 -0
- package/lib/utils.ts +201 -0
- package/lib/versions.ts +46 -0
- package/package.json +4 -2
- package/toolkit/about.ts +43 -0
- package/toolkit/ai.ts +56 -0
- package/toolkit/check.ts +16 -0
- package/toolkit/clean.ts +27 -0
- package/toolkit/compilers.ts +29 -0
- package/toolkit/config.ts +91 -0
- package/toolkit/create.ts +37 -0
- package/toolkit/doctor.ts +22 -0
- package/toolkit/generate.ts +213 -0
- package/toolkit/make.ts +82 -0
- package/toolkit/upgrade.ts +9 -0
- package/toolkit/upload.ts +19 -0
package/lib/ai.ts
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { encode } from 'gpt-tokenizer'
|
|
2
|
+
|
|
3
|
+
import { estimateCost } from 'gpt-tokenizer/model/gpt-5'
|
|
4
|
+
import { igniteModel, LlmModel, loadModels, logger, Message } from 'multi-llm-ts'
|
|
5
|
+
import OpenAI from 'openai'
|
|
6
|
+
import tui from './tui'
|
|
7
|
+
import ora from 'ora'
|
|
8
|
+
import { settings } from './settings'
|
|
9
|
+
|
|
10
|
+
// do not log anything from multi-llm-ts
|
|
11
|
+
logger.disable()
|
|
12
|
+
|
|
13
|
+
export async function complete(model: string, systemPrompt: string, userPrompt: string): Promise<string> {
|
|
14
|
+
const parts = model.split('/')
|
|
15
|
+
const providerName = parts[0]!
|
|
16
|
+
const modelName = parts[1]!
|
|
17
|
+
|
|
18
|
+
const config = { apiKey: process.env[keys[providerName]!] || '' }
|
|
19
|
+
|
|
20
|
+
const models = await loadModels(providerName, config)
|
|
21
|
+
const chat = models!.chat.find((m) => m.id === modelName)!
|
|
22
|
+
|
|
23
|
+
const bot = igniteModel(providerName, chat, config)
|
|
24
|
+
const messages = [new Message('system', systemPrompt), new Message('user', userPrompt)]
|
|
25
|
+
if (settings.showPrompts) tui.gray(`[SYSTEM PROMPT] ${systemPrompt}`)
|
|
26
|
+
if (settings.showPrompts) tui.gray(`[USER PROMPT] ${userPrompt}`)
|
|
27
|
+
const spinner = ora(`Generating response with model ${model}`).start()
|
|
28
|
+
const response = await bot.complete(messages)
|
|
29
|
+
spinner.stop()
|
|
30
|
+
const answer = response.content!
|
|
31
|
+
if (settings.showAnswers) tui.gray(`[ANSWER] ${answer}`)
|
|
32
|
+
return answer
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type ModelInfo = Record<string, Record<string, string[]>>
|
|
36
|
+
|
|
37
|
+
export async function listModels(): Promise<ModelInfo> {
|
|
38
|
+
const result: ModelInfo = {}
|
|
39
|
+
const providers = Object.keys(keys).sort()
|
|
40
|
+
for (const providerName of providers) {
|
|
41
|
+
if (providerName === 'ollama') continue // Ollama models are local, skip for now
|
|
42
|
+
const config = { apiKey: process.env[keys[providerName]!] || '' }
|
|
43
|
+
const models = await loadModels(providerName, config)
|
|
44
|
+
if (models === null) continue
|
|
45
|
+
result[providerName] = {}
|
|
46
|
+
for (const modelType in models) {
|
|
47
|
+
result[providerName][modelType] = []
|
|
48
|
+
for (const model of models.chat) {
|
|
49
|
+
result[providerName][modelType].push(model.id)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return result
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export class ChatBot {
|
|
57
|
+
private model: string
|
|
58
|
+
public totalOutputCost: number = 0
|
|
59
|
+
private messages: Message[]
|
|
60
|
+
private bot: LlmModel | null = null
|
|
61
|
+
public totalInputTokens: number = 0
|
|
62
|
+
public totalOutputTokens: number = 0
|
|
63
|
+
public totalInputCost: number = 0
|
|
64
|
+
private systemPrompt: string
|
|
65
|
+
|
|
66
|
+
constructor(model: string, systemPrompt: string) {
|
|
67
|
+
this.model = model
|
|
68
|
+
this.systemPrompt = systemPrompt
|
|
69
|
+
this.messages = [new Message('system', systemPrompt)]
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async init() {
|
|
73
|
+
const parts = this.model.split('/')
|
|
74
|
+
const providerName = parts[0]!
|
|
75
|
+
const modelName = parts[1]!
|
|
76
|
+
const config = { apiKey: process.env[keys[providerName]!] || '' }
|
|
77
|
+
const models = await loadModels(providerName, config)
|
|
78
|
+
const chat = models!.chat.find((m: any) => m.id === modelName)!
|
|
79
|
+
this.bot = igniteModel(providerName, chat, config)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async complete(userPrompt: string): Promise<string> {
|
|
83
|
+
if (!this.bot) {
|
|
84
|
+
throw new Error(`Model '${this.model}' could not be initialized`)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
this.messages.push(new Message('user', userPrompt))
|
|
88
|
+
if (settings.showPrompts) tui.gray(`[SYSTEM PROMPT] ${this.systemPrompt}`)
|
|
89
|
+
if (settings.showPrompts) tui.gray(`[USER PROMPT] ${this.messages[this.messages.length - 1]!.content}`)
|
|
90
|
+
const spinner = ora(`Generating response with model ${this.model}`).start()
|
|
91
|
+
const response = await this.bot.complete(this.messages)
|
|
92
|
+
spinner.stop()
|
|
93
|
+
this.messages.push(new Message('assistant', response.content))
|
|
94
|
+
if (settings.showAnswers) tui.gray(`[ANSWER] ${response.content!}`)
|
|
95
|
+
|
|
96
|
+
const inputTokens = encode(userPrompt).length
|
|
97
|
+
const outputTokens = encode(response.content!).length
|
|
98
|
+
const inputCost = estimateCost(inputTokens)
|
|
99
|
+
const outputCost = estimateCost(outputTokens)
|
|
100
|
+
|
|
101
|
+
this.totalInputTokens += inputTokens
|
|
102
|
+
this.totalOutputTokens += outputTokens
|
|
103
|
+
this.totalInputCost += inputCost.main!.input!
|
|
104
|
+
this.totalOutputCost += outputCost.main!.output!
|
|
105
|
+
|
|
106
|
+
return response.content!
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
forgetLastInteraction() {
|
|
110
|
+
if (this.messages.length > 2) {
|
|
111
|
+
this.messages.pop()
|
|
112
|
+
this.messages.pop()
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
modelInformation(): any {
|
|
117
|
+
if (!this.bot) {
|
|
118
|
+
throw new Error(`Model '${this.model}' has not been initialized`)
|
|
119
|
+
}
|
|
120
|
+
return this.bot.model
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export type PowerEstimation = {
|
|
125
|
+
wattHours: number
|
|
126
|
+
joules: number
|
|
127
|
+
co2Grams: number
|
|
128
|
+
trees: number
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function estimatePowerConsumption(inputTokens: number, outputTokens: number): PowerEstimation {
|
|
132
|
+
// Written by Claude.ai
|
|
133
|
+
|
|
134
|
+
// Very rough estimates based on public data
|
|
135
|
+
// One tree absorbs approximately:
|
|
136
|
+
// - 22 kg (22,000g) of CO2 per year on average
|
|
137
|
+
// - 1 ton (1,000,000g) over its lifetime (~40 years)
|
|
138
|
+
|
|
139
|
+
const wattsPerToken = 0.0003 // ~0.3 milliwatts per token
|
|
140
|
+
const totalTokens = inputTokens + outputTokens
|
|
141
|
+
const co2PerTree = 22000
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
wattHours: (totalTokens * wattsPerToken) / 3600, // Convert to Wh
|
|
145
|
+
joules: totalTokens * wattsPerToken,
|
|
146
|
+
co2Grams: ((totalTokens * wattsPerToken) / 3600) * 0.5, // Rough CO2 estimate
|
|
147
|
+
trees: (((totalTokens * wattsPerToken) / 3600) * 0.5) / co2PerTree,
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const keys: Record<string, string> = {
|
|
152
|
+
google: 'GEMINI_API_KEY',
|
|
153
|
+
openai: 'OPENAI_API_KEY',
|
|
154
|
+
ollama: '',
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function cleanMardownCodeString(s: string): string {
|
|
158
|
+
const pattern = /^\n*```\w*\s*(.*?)\s*```\n*$/s
|
|
159
|
+
const clean = s.replace(pattern, '$1')
|
|
160
|
+
return clean
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Generate image with openai/dall-e-3 or equivalent model
|
|
165
|
+
* Returns png image as a Buffer
|
|
166
|
+
*/
|
|
167
|
+
export async function generateImage(model: string, prompt: string): Promise<Buffer> {
|
|
168
|
+
model = model.split('/')[1]!
|
|
169
|
+
if (settings.showPrompts) tui.gray(`[PROMPT] ${prompt}`)
|
|
170
|
+
|
|
171
|
+
// Initialize OpenAI client
|
|
172
|
+
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })
|
|
173
|
+
|
|
174
|
+
// Generate image
|
|
175
|
+
const spinner = ora(`Generating image with model ${model}`).start()
|
|
176
|
+
const response1 = await openai.images.generate({
|
|
177
|
+
model,
|
|
178
|
+
prompt: prompt,
|
|
179
|
+
n: 1,
|
|
180
|
+
size: '1024x1024',
|
|
181
|
+
quality: 'standard',
|
|
182
|
+
})
|
|
183
|
+
spinner.stop()
|
|
184
|
+
|
|
185
|
+
// Retrieve image URL
|
|
186
|
+
if (!response1.data || !response1.data.length || !response1.data[0] || !response1.data[0].url) {
|
|
187
|
+
throw new Error('No image generated')
|
|
188
|
+
}
|
|
189
|
+
const url = response1.data[0].url
|
|
190
|
+
|
|
191
|
+
// Download image
|
|
192
|
+
spinner.start('Downloading image')
|
|
193
|
+
const response2 = await fetch(url)
|
|
194
|
+
const arrayBuffer = await response2.arrayBuffer()
|
|
195
|
+
const buffer = Buffer.from(arrayBuffer)
|
|
196
|
+
spinner.stop()
|
|
197
|
+
|
|
198
|
+
// Done!
|
|
199
|
+
return buffer
|
|
200
|
+
}
|
package/lib/cleaner.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import tui from './tui'
|
|
3
|
+
import { confirm } from '@inquirer/prompts'
|
|
4
|
+
import { readdir, rm } from 'fs/promises'
|
|
5
|
+
import { toolkitPrefix } from './utils'
|
|
6
|
+
|
|
7
|
+
export async function cleanDirectory(force: boolean, all: boolean, directory: string): Promise<void> {
|
|
8
|
+
const patterns = [
|
|
9
|
+
`^${toolkitPrefix()}-`,
|
|
10
|
+
'\\.exe$',
|
|
11
|
+
'\\.out$',
|
|
12
|
+
'\\.pyc$',
|
|
13
|
+
'\\.class$',
|
|
14
|
+
'\\.o$',
|
|
15
|
+
'\\.hi$',
|
|
16
|
+
'~$',
|
|
17
|
+
'^a\\.out$',
|
|
18
|
+
'^__pycache__$',
|
|
19
|
+
]
|
|
20
|
+
const allPatterns = [
|
|
21
|
+
'\\.cor$',
|
|
22
|
+
'^problem\\.[a-z][a-z]\\.ps$',
|
|
23
|
+
'^problem\\.[a-z][a-z]\\.md$',
|
|
24
|
+
'^problem\\.[a-z][a-z]\\.pdf$',
|
|
25
|
+
'^problem\\.[a-z][a-z]\\.txt$',
|
|
26
|
+
'^problem\\.[a-z][a-z]\\.html$',
|
|
27
|
+
]
|
|
28
|
+
if (all) {
|
|
29
|
+
patterns.push(...allPatterns)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const pattern = new RegExp(patterns.join('|'))
|
|
33
|
+
|
|
34
|
+
const entries = await readdir(directory, { withFileTypes: true })
|
|
35
|
+
|
|
36
|
+
const removalList: string[] = []
|
|
37
|
+
for (const entry of entries) {
|
|
38
|
+
const fullPath = path.join(directory, entry.name)
|
|
39
|
+
if (pattern.test(entry.name)) {
|
|
40
|
+
removalList.push(fullPath)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (removalList.length === 0) {
|
|
45
|
+
tui.success('No entries to remove')
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
tui.warning(`The following ${removalList.length} entries will be removed:`)
|
|
50
|
+
for (const elem of removalList.sort()) {
|
|
51
|
+
tui.print(tui.hyperlink(directory, elem))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!force) {
|
|
55
|
+
console.log()
|
|
56
|
+
const conformation = await confirm({
|
|
57
|
+
message: `Remove ${removalList.length} entries?`,
|
|
58
|
+
default: false,
|
|
59
|
+
})
|
|
60
|
+
if (!conformation) return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let removalCount = 0
|
|
64
|
+
for (const elem of removalList) {
|
|
65
|
+
try {
|
|
66
|
+
await rm(elem, { recursive: true, force: true })
|
|
67
|
+
removalCount++
|
|
68
|
+
} catch (error) {
|
|
69
|
+
tui.error(`Could not remove entry ${elem}`)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
tui.success(`Removed ${removalCount} entries`)
|
|
74
|
+
if (!all) {
|
|
75
|
+
tui.warning(`You can use the --all option to remove generated statement and correct files as well`)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { execa } from 'execa'
|
|
2
|
+
import { exists, rm } from 'fs/promises'
|
|
3
|
+
import { join, sep } from 'path'
|
|
4
|
+
import tui from '../tui'
|
|
5
|
+
import type { Handler } from '../types'
|
|
6
|
+
import { readText, toolkitPrefix, writeText } from '../utils'
|
|
7
|
+
|
|
8
|
+
export type CompilerInfo = {
|
|
9
|
+
compiler_id: string
|
|
10
|
+
name: string
|
|
11
|
+
language: string
|
|
12
|
+
version: string
|
|
13
|
+
flags1: string
|
|
14
|
+
flags2: string
|
|
15
|
+
extension: string
|
|
16
|
+
type: string
|
|
17
|
+
warning: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export abstract class Compiler {
|
|
21
|
+
abstract id(): string
|
|
22
|
+
|
|
23
|
+
abstract name(): string
|
|
24
|
+
|
|
25
|
+
abstract type(): string
|
|
26
|
+
|
|
27
|
+
abstract language(): string
|
|
28
|
+
|
|
29
|
+
abstract version(): Promise<string>
|
|
30
|
+
|
|
31
|
+
abstract flags1(): string
|
|
32
|
+
|
|
33
|
+
abstract flags2(): string
|
|
34
|
+
|
|
35
|
+
abstract tool(): string
|
|
36
|
+
|
|
37
|
+
abstract extension(): string
|
|
38
|
+
|
|
39
|
+
warning(): string {
|
|
40
|
+
return ''
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async available(): Promise<boolean> {
|
|
44
|
+
const version = await this.version()
|
|
45
|
+
return version !== 'not found'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async info(): Promise<CompilerInfo> {
|
|
49
|
+
return {
|
|
50
|
+
compiler_id: this.id(),
|
|
51
|
+
name: this.name(),
|
|
52
|
+
language: this.language(),
|
|
53
|
+
version: await this.version(),
|
|
54
|
+
flags1: this.flags1(),
|
|
55
|
+
flags2: this.flags2(),
|
|
56
|
+
extension: this.extension(),
|
|
57
|
+
type: this.type(),
|
|
58
|
+
warning: this.warning(),
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public async compileNormal(handler: Handler, directory: string, sourcePath: string): Promise<string> {
|
|
63
|
+
const exePath = `${sourcePath}.exe`
|
|
64
|
+
|
|
65
|
+
await this.rmInDir(directory, exePath)
|
|
66
|
+
|
|
67
|
+
tui.command(`${this.tool()} ${this.flags1()} ${sourcePath} -o ${exePath}`)
|
|
68
|
+
await execa({
|
|
69
|
+
reject: false,
|
|
70
|
+
stderr: 'inherit',
|
|
71
|
+
stdout: 'inherit',
|
|
72
|
+
cwd: directory,
|
|
73
|
+
})`${this.tool()} ${this.flags1().split(' ')} ${sourcePath} -o ${exePath}`
|
|
74
|
+
|
|
75
|
+
return exePath
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public async compileWithMain(handler: Handler, directory: string, sourcePath: string): Promise<string> {
|
|
79
|
+
const exePath = `${sourcePath}.exe`
|
|
80
|
+
|
|
81
|
+
tui.command(`add main.${this.extension()} to ${sourcePath}`)
|
|
82
|
+
await this.concatText(directory, [sourcePath, `main.${this.extension()}`], sourcePath)
|
|
83
|
+
|
|
84
|
+
tui.command(`${this.tool()} ${this.flags1()} ${sourcePath} -o ${exePath}`)
|
|
85
|
+
await execa({
|
|
86
|
+
reject: false,
|
|
87
|
+
stderr: 'inherit',
|
|
88
|
+
stdout: 'inherit',
|
|
89
|
+
cwd: directory,
|
|
90
|
+
})`${this.tool()} ${this.flags1().split(' ')} ${sourcePath} -o ${exePath}`
|
|
91
|
+
|
|
92
|
+
return exePath
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Default implementation of execute for compiled languages
|
|
96
|
+
async execute(
|
|
97
|
+
handler: Handler,
|
|
98
|
+
directory: string,
|
|
99
|
+
sourcePath: string,
|
|
100
|
+
inputPath: string,
|
|
101
|
+
outputPath: string,
|
|
102
|
+
): Promise<void> {
|
|
103
|
+
const exePath = `${sourcePath}.exe`
|
|
104
|
+
|
|
105
|
+
// TODO: check in windows
|
|
106
|
+
const relativeExecutablePath = `.${sep}${exePath}` // force prepending ./ to make it work
|
|
107
|
+
await this.rmInDir(directory, outputPath)
|
|
108
|
+
const input = await this.getInput(directory, inputPath)
|
|
109
|
+
|
|
110
|
+
tui.command(`${relativeExecutablePath} < ${inputPath} > ${outputPath}`)
|
|
111
|
+
|
|
112
|
+
const { exitCode } = await execa({
|
|
113
|
+
reject: false,
|
|
114
|
+
input,
|
|
115
|
+
stdout: { file: join(directory, outputPath) },
|
|
116
|
+
stderr: 'inherit',
|
|
117
|
+
cwd: directory,
|
|
118
|
+
})`${relativeExecutablePath}`
|
|
119
|
+
|
|
120
|
+
if (exitCode !== 0) {
|
|
121
|
+
throw new Error(`Execution failed for ${exePath} with exit code ${exitCode}`)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
protected async getVersion(cmd: string, lineIndex: number): Promise<string> {
|
|
126
|
+
try {
|
|
127
|
+
const { stdout } = await execa`${cmd.split(' ')}`
|
|
128
|
+
const lines = stdout.split('\n')
|
|
129
|
+
return lines[lineIndex]?.trim() || 'Unknown version'
|
|
130
|
+
} catch {
|
|
131
|
+
return 'not found'
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
getInput(directory: string, inputPath: string): Promise<string> {
|
|
136
|
+
return readText(join(directory, inputPath))
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
rmInDir(directory: string, path: string): Promise<void> {
|
|
140
|
+
return rm(join(directory, path), { force: true })
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
existsInDir(directory: string, path: string): Promise<boolean> {
|
|
144
|
+
return exists(join(directory, path))
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async concatText(
|
|
148
|
+
directory: string,
|
|
149
|
+
inputPaths: string[],
|
|
150
|
+
outputPath: string,
|
|
151
|
+
separator: string = '\n\n\n',
|
|
152
|
+
): Promise<void> {
|
|
153
|
+
let content = ''
|
|
154
|
+
for (const inputPath of inputPaths) {
|
|
155
|
+
content += (await readText(join(directory, inputPath))) + separator
|
|
156
|
+
}
|
|
157
|
+
await writeText(join(directory, outputPath), content)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import tui from '../tui'
|
|
2
|
+
import { type Handler } from '../types'
|
|
3
|
+
import { Compiler } from './base'
|
|
4
|
+
import { execa } from 'execa'
|
|
5
|
+
import { join } from 'path'
|
|
6
|
+
import { nothing, readText, toolkitPrefix } from '../utils'
|
|
7
|
+
import { rm } from 'fs/promises'
|
|
8
|
+
|
|
9
|
+
export class Clojure_Compiler extends Compiler {
|
|
10
|
+
id(): string {
|
|
11
|
+
return 'Clojure'
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
name(): string {
|
|
15
|
+
return 'Clojure'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type(): string {
|
|
19
|
+
return 'vm'
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
language(): string {
|
|
23
|
+
return 'Clojure'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async version(): Promise<string> {
|
|
27
|
+
return await this.getVersion('clj --version', 0)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
flags1(): string {
|
|
31
|
+
return ''
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
flags2(): string {
|
|
35
|
+
return ''
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
tool(): string {
|
|
39
|
+
return 'clj'
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
extension(): string {
|
|
43
|
+
return 'clj'
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
override async compileNormal(handler: Handler, directory: string, sourcePath: string): Promise<string> {
|
|
47
|
+
await nothing()
|
|
48
|
+
|
|
49
|
+
tui.warning(`No compilation available for Clojure`)
|
|
50
|
+
|
|
51
|
+
return sourcePath
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
override async compileWithMain(handler: Handler, directory: string, sourcePath: string): Promise<string> {
|
|
55
|
+
tui.command(`add main.${this.extension()} to ${sourcePath}`)
|
|
56
|
+
await this.concatText(directory, [sourcePath, `main.${this.extension()}`], sourcePath)
|
|
57
|
+
|
|
58
|
+
tui.warning(`No compilation available for Clojure`)
|
|
59
|
+
|
|
60
|
+
return sourcePath
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
override async execute(
|
|
64
|
+
handler: Handler,
|
|
65
|
+
directory: string,
|
|
66
|
+
sourcePath: string,
|
|
67
|
+
inputPath: string,
|
|
68
|
+
outputPath: string,
|
|
69
|
+
): Promise<void> {
|
|
70
|
+
const exePath = `${toolkitPrefix()}-${sourcePath}`
|
|
71
|
+
|
|
72
|
+
await this.rmInDir(directory, outputPath)
|
|
73
|
+
const input = await this.getInput(directory, inputPath)
|
|
74
|
+
|
|
75
|
+
tui.command(`clj -M ${sourcePath} < ${inputPath} > ${outputPath}`)
|
|
76
|
+
|
|
77
|
+
const { exitCode } = await execa({
|
|
78
|
+
reject: false,
|
|
79
|
+
input,
|
|
80
|
+
stdout: { file: join(directory, outputPath) },
|
|
81
|
+
stderr: 'inherit',
|
|
82
|
+
cwd: directory,
|
|
83
|
+
})`clj -M ${sourcePath}`
|
|
84
|
+
|
|
85
|
+
if (exitCode !== 0) throw new Error(`Execution failed for ${sourcePath}`)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Compiler } from './base'
|
|
2
|
+
|
|
3
|
+
export class GCC_Compiler extends Compiler {
|
|
4
|
+
id(): string {
|
|
5
|
+
return 'GCC'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
name(): string {
|
|
9
|
+
return 'GNU C Compiler'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type(): string {
|
|
13
|
+
return 'compiler'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
language(): string {
|
|
17
|
+
return 'C'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async version(): Promise<string> {
|
|
21
|
+
return await this.getVersion('gcc --version', 0)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
flags1(): string {
|
|
25
|
+
return '-D_JUDGE_ -O2 -DNDEBUG -Wall -Wextra -Wno-sign-compare'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
flags2(): string {
|
|
29
|
+
return ''
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
tool(): string {
|
|
33
|
+
return 'gcc'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
extension(): string {
|
|
37
|
+
return 'c'
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Compiler } from './base'
|
|
2
|
+
|
|
3
|
+
export class GHC_Compiler extends Compiler {
|
|
4
|
+
id(): string {
|
|
5
|
+
return 'GHC'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
name(): string {
|
|
9
|
+
return 'GHC'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type(): string {
|
|
13
|
+
return 'compiler'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
language(): string {
|
|
17
|
+
return 'Haskell'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async version(): Promise<string> {
|
|
21
|
+
return await this.getVersion('ghc --version', 0)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
flags1(): string {
|
|
25
|
+
return '-O3'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
flags2(): string {
|
|
29
|
+
return '-O3'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
tool(): string {
|
|
33
|
+
return 'ghc'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
extension(): string {
|
|
37
|
+
return 'hs'
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Compiler } from './base'
|
|
2
|
+
|
|
3
|
+
export class GXX_Compiler extends Compiler {
|
|
4
|
+
id(): string {
|
|
5
|
+
return 'GXX'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
name(): string {
|
|
9
|
+
return 'GNU C++ Compiler'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type(): string {
|
|
13
|
+
return 'compiler'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
language(): string {
|
|
17
|
+
return 'C++'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async version(): Promise<string> {
|
|
21
|
+
return await this.getVersion('g++ --version', 0)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
flags1(): string {
|
|
25
|
+
return '-std=c++17 -D_JUDGE_ -O2 -DNDEBUG -Wall -Wextra -Wno-sign-compare -Wshadow'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
flags2(): string {
|
|
29
|
+
return '-std=c++17 -D_JUDGE_ -O2 -DNDEBUG -Wall -Wextra -Wno-sign-compare -Wshadow'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
tool(): string {
|
|
33
|
+
return 'g++'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
extension(): string {
|
|
37
|
+
return 'cc'
|
|
38
|
+
}
|
|
39
|
+
}
|