opencode-leetcode-realworld 0.2.3 → 0.4.0
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/README.md +58 -2
- package/package.json +1 -1
- package/src/index.ts +24 -1
- package/src/languages.ts +473 -119
- package/src/patterns.ts +2 -1
- package/src/prompt.ts +73 -49
- package/src/scaffold.ts +157 -4
- package/src/schema.ts +226 -0
- package/src/types.ts +22 -1
package/src/languages.ts
CHANGED
|
@@ -1,9 +1,25 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildTypes,
|
|
3
|
+
isFullyTyped,
|
|
4
|
+
isIdentifier,
|
|
5
|
+
isReserved,
|
|
6
|
+
pascal,
|
|
7
|
+
schemaSignature,
|
|
8
|
+
snake,
|
|
9
|
+
type SchemaNode,
|
|
10
|
+
type TypeDecl,
|
|
11
|
+
type TypeRef,
|
|
12
|
+
} from "./schema";
|
|
1
13
|
import type { FileEntry, LanguageId } from "./types";
|
|
2
14
|
|
|
3
15
|
export interface LanguageContext {
|
|
4
16
|
projectName: string;
|
|
5
17
|
functionName: string;
|
|
6
18
|
title: string;
|
|
19
|
+
input: SchemaNode;
|
|
20
|
+
output: SchemaNode;
|
|
21
|
+
inputExample: unknown;
|
|
22
|
+
outputExample: unknown;
|
|
7
23
|
}
|
|
8
24
|
|
|
9
25
|
export interface LanguageAdapter {
|
|
@@ -11,17 +27,32 @@ export interface LanguageAdapter {
|
|
|
11
27
|
name: string;
|
|
12
28
|
aliases: string[];
|
|
13
29
|
extension: string;
|
|
14
|
-
/** Human hint shown in the generated README. */
|
|
15
30
|
runtime: string;
|
|
31
|
+
/** True when the starter exposes static types for the payload/result. */
|
|
32
|
+
typed: boolean;
|
|
16
33
|
starter(ctx: LanguageContext): string;
|
|
17
|
-
/** Extra project files (build manifests etc). */
|
|
18
34
|
files(ctx: LanguageContext): FileEntry[];
|
|
19
|
-
/** Command run once before the cases, or null when not needed. */
|
|
20
35
|
build(ctx: LanguageContext): string | null;
|
|
21
|
-
/** Command that runs the solution once, reading one JSON value on stdin. */
|
|
22
36
|
run(ctx: LanguageContext): string;
|
|
23
37
|
}
|
|
24
38
|
|
|
39
|
+
const INPUT_ROOT = "Payload";
|
|
40
|
+
const OUTPUT_ROOT = "Output";
|
|
41
|
+
|
|
42
|
+
function example(value: unknown): string {
|
|
43
|
+
return JSON.stringify(value);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Format comment for languages without static types (and as a fallback). */
|
|
47
|
+
function formatComment(prefix: string, ctx: LanguageContext): string {
|
|
48
|
+
return [
|
|
49
|
+
`${prefix} Input format: ${schemaSignature(ctx.input)}`,
|
|
50
|
+
`${prefix} Input example: ${example(ctx.inputExample)}`,
|
|
51
|
+
`${prefix} Output format: ${schemaSignature(ctx.output)}`,
|
|
52
|
+
`${prefix} Output example: ${example(ctx.outputExample)}`,
|
|
53
|
+
].join("\n");
|
|
54
|
+
}
|
|
55
|
+
|
|
25
56
|
function packageJson(projectName: string, script: string): FileEntry {
|
|
26
57
|
return {
|
|
27
58
|
relative: "package.json",
|
|
@@ -40,6 +71,401 @@ function packageJson(projectName: string, script: string): FileEntry {
|
|
|
40
71
|
};
|
|
41
72
|
}
|
|
42
73
|
|
|
74
|
+
/* ------------------------------------------------------------------ */
|
|
75
|
+
/* TypeScript: named interfaces -> dot completion on `payload.` */
|
|
76
|
+
/* ------------------------------------------------------------------ */
|
|
77
|
+
|
|
78
|
+
function tsRef(ref: TypeRef): string {
|
|
79
|
+
switch (ref.t) {
|
|
80
|
+
case "string":
|
|
81
|
+
return "string";
|
|
82
|
+
case "int":
|
|
83
|
+
case "double":
|
|
84
|
+
return "number";
|
|
85
|
+
case "bool":
|
|
86
|
+
return "boolean";
|
|
87
|
+
case "any":
|
|
88
|
+
return "unknown";
|
|
89
|
+
case "null":
|
|
90
|
+
return "null";
|
|
91
|
+
case "array": {
|
|
92
|
+
const inner = tsRef(ref.of);
|
|
93
|
+
return inner.includes(" ") ? `(${inner})[]` : `${inner}[]`;
|
|
94
|
+
}
|
|
95
|
+
case "named":
|
|
96
|
+
return ref.name;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function tsProp(name: string): string {
|
|
101
|
+
return isIdentifier(name) && !isReserved(name) ? name : JSON.stringify(name);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function tsDecl(decl: TypeDecl): string {
|
|
105
|
+
const fields = decl.fields
|
|
106
|
+
.map((field) => ` ${tsProp(field.name)}${field.optional ? "?" : ""}: ${tsRef(field.ref)};`)
|
|
107
|
+
.join("\n");
|
|
108
|
+
return `interface ${decl.name} {\n${fields}\n}`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function typescriptStarter(ctx: LanguageContext): string {
|
|
112
|
+
const input = buildTypes(INPUT_ROOT, ctx.input);
|
|
113
|
+
const output = buildTypes(OUTPUT_ROOT, ctx.output);
|
|
114
|
+
const decls = [...input.decls, ...output.decls].map(tsDecl).join("\n\n");
|
|
115
|
+
const inputType = tsRef(input.root);
|
|
116
|
+
const outputType = tsRef(output.root);
|
|
117
|
+
const header = decls ? `${decls}\n\n` : "";
|
|
118
|
+
return `import { readFileSync } from "node:fs";
|
|
119
|
+
|
|
120
|
+
${header}function ${ctx.functionName}(payload: ${inputType}): ${outputType} {
|
|
121
|
+
throw new Error("Not implemented: ${ctx.functionName}");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const input = readFileSync(0, "utf8");
|
|
125
|
+
const payload = JSON.parse(input.trim() === "" ? "null" : input) as ${inputType};
|
|
126
|
+
process.stdout.write(JSON.stringify(${ctx.functionName}(payload) ?? null));
|
|
127
|
+
`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* ------------------------------------------------------------------ */
|
|
131
|
+
/* Go: structs with json tags */
|
|
132
|
+
/* ------------------------------------------------------------------ */
|
|
133
|
+
|
|
134
|
+
function goRef(ref: TypeRef): string {
|
|
135
|
+
switch (ref.t) {
|
|
136
|
+
case "string":
|
|
137
|
+
return "string";
|
|
138
|
+
case "int":
|
|
139
|
+
return "int";
|
|
140
|
+
case "double":
|
|
141
|
+
return "float64";
|
|
142
|
+
case "bool":
|
|
143
|
+
return "bool";
|
|
144
|
+
case "any":
|
|
145
|
+
case "null":
|
|
146
|
+
return "any";
|
|
147
|
+
case "array":
|
|
148
|
+
return "[]" + goRef(ref.of);
|
|
149
|
+
case "named":
|
|
150
|
+
return ref.name;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function goFieldName(name: string): string {
|
|
155
|
+
const value = pascal(name);
|
|
156
|
+
return value.length > 0 ? value : "Field";
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function goDecl(decl: TypeDecl): string {
|
|
160
|
+
const fields = decl.fields
|
|
161
|
+
.map((field) => {
|
|
162
|
+
const tag = `json:"${field.name}${field.optional ? ",omitempty" : ""}"`;
|
|
163
|
+
return "\t" + goFieldName(field.name) + " " + goRef(field.ref) + " `" + tag + "`";
|
|
164
|
+
})
|
|
165
|
+
.join("\n");
|
|
166
|
+
return `type ${decl.name} struct {\n${fields}\n}`;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function goAliasOrDecls(
|
|
170
|
+
root: TypeRef,
|
|
171
|
+
decls: TypeDecl[],
|
|
172
|
+
rootName: string,
|
|
173
|
+
): string {
|
|
174
|
+
const pieces = decls.map(goDecl);
|
|
175
|
+
if (root.t !== "named") pieces.unshift(`type ${rootName} = ${goRef(root)}`);
|
|
176
|
+
return pieces.join("\n\n");
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function goStarter(ctx: LanguageContext): string {
|
|
180
|
+
const input = buildTypes(INPUT_ROOT, ctx.input);
|
|
181
|
+
const output = buildTypes(OUTPUT_ROOT, ctx.output);
|
|
182
|
+
const types = [
|
|
183
|
+
goAliasOrDecls(input.root, input.decls, INPUT_ROOT),
|
|
184
|
+
goAliasOrDecls(output.root, output.decls, OUTPUT_ROOT),
|
|
185
|
+
].join("\n\n");
|
|
186
|
+
return `package main
|
|
187
|
+
|
|
188
|
+
import (
|
|
189
|
+
"encoding/json"
|
|
190
|
+
"fmt"
|
|
191
|
+
"io"
|
|
192
|
+
"os"
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
${types}
|
|
196
|
+
|
|
197
|
+
func ${ctx.functionName}(payload ${INPUT_ROOT}) ${OUTPUT_ROOT} {
|
|
198
|
+
panic("Not implemented: ${ctx.functionName}")
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
func main() {
|
|
202
|
+
raw, _ := io.ReadAll(os.Stdin)
|
|
203
|
+
var payload ${INPUT_ROOT}
|
|
204
|
+
if len(raw) > 0 {
|
|
205
|
+
if err := json.Unmarshal(raw, &payload); err != nil {
|
|
206
|
+
panic(err)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
out, err := json.Marshal(${ctx.functionName}(payload))
|
|
210
|
+
if err != nil {
|
|
211
|
+
panic(err)
|
|
212
|
+
}
|
|
213
|
+
fmt.Print(string(out))
|
|
214
|
+
}
|
|
215
|
+
`;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/* ------------------------------------------------------------------ */
|
|
219
|
+
/* Rust: serde structs */
|
|
220
|
+
/* ------------------------------------------------------------------ */
|
|
221
|
+
|
|
222
|
+
function rustRef(ref: TypeRef): string {
|
|
223
|
+
switch (ref.t) {
|
|
224
|
+
case "string":
|
|
225
|
+
return "String";
|
|
226
|
+
case "int":
|
|
227
|
+
return "i64";
|
|
228
|
+
case "double":
|
|
229
|
+
return "f64";
|
|
230
|
+
case "bool":
|
|
231
|
+
return "bool";
|
|
232
|
+
case "any":
|
|
233
|
+
case "null":
|
|
234
|
+
return "serde_json::Value";
|
|
235
|
+
case "array":
|
|
236
|
+
return `Vec<${rustRef(ref.of)}>`;
|
|
237
|
+
case "named":
|
|
238
|
+
return ref.name;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function rustField(name: string): string {
|
|
243
|
+
let value = snake(name);
|
|
244
|
+
if (isReserved(value)) value = `${value}_`;
|
|
245
|
+
return value;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function rustDecl(decl: TypeDecl): string {
|
|
249
|
+
const fields = decl.fields
|
|
250
|
+
.map((field) => {
|
|
251
|
+
const ident = rustField(field.name);
|
|
252
|
+
const base = rustRef(field.ref);
|
|
253
|
+
const ty = field.optional ? `Option<${base}>` : base;
|
|
254
|
+
const attrs: string[] = [];
|
|
255
|
+
if (ident !== field.name) attrs.push(` #[serde(rename = "${field.name}")]`);
|
|
256
|
+
if (field.optional) {
|
|
257
|
+
attrs.push(' #[serde(default, skip_serializing_if = "Option::is_none")]');
|
|
258
|
+
}
|
|
259
|
+
const attrText = attrs.length > 0 ? attrs.join("\n") + "\n" : "";
|
|
260
|
+
return `${attrText} ${ident}: ${ty},`;
|
|
261
|
+
})
|
|
262
|
+
.join("\n");
|
|
263
|
+
return `#[derive(Serialize, Deserialize)]\nstruct ${decl.name} {\n${fields}\n}`;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function rustAliasOrDecls(root: TypeRef, decls: TypeDecl[], rootName: string): string {
|
|
267
|
+
const pieces = decls.map(rustDecl);
|
|
268
|
+
if (root.t !== "named") pieces.unshift(`type ${rootName} = ${rustRef(root)};`);
|
|
269
|
+
return pieces.join("\n\n");
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function rustStarter(ctx: LanguageContext): string {
|
|
273
|
+
const input = buildTypes(INPUT_ROOT, ctx.input);
|
|
274
|
+
const output = buildTypes(OUTPUT_ROOT, ctx.output);
|
|
275
|
+
const types = [
|
|
276
|
+
rustAliasOrDecls(input.root, input.decls, INPUT_ROOT),
|
|
277
|
+
rustAliasOrDecls(output.root, output.decls, OUTPUT_ROOT),
|
|
278
|
+
].join("\n\n");
|
|
279
|
+
return `use serde::{Deserialize, Serialize};
|
|
280
|
+
use std::io::Read;
|
|
281
|
+
|
|
282
|
+
${types}
|
|
283
|
+
|
|
284
|
+
fn ${ctx.functionName}(payload: ${INPUT_ROOT}) -> ${OUTPUT_ROOT} {
|
|
285
|
+
let _ = &payload;
|
|
286
|
+
panic!("Not implemented: ${ctx.functionName}");
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
fn main() {
|
|
290
|
+
let mut raw = String::new();
|
|
291
|
+
std::io::stdin().read_to_string(&mut raw).expect("read stdin");
|
|
292
|
+
let trimmed = raw.trim();
|
|
293
|
+
let source = if trimmed.is_empty() { "null" } else { trimmed };
|
|
294
|
+
let payload: ${INPUT_ROOT} = serde_json::from_str(source).expect("parse json");
|
|
295
|
+
let result = ${ctx.functionName}(payload);
|
|
296
|
+
println!("{}", serde_json::to_string(&result).expect("serialize json"));
|
|
297
|
+
}
|
|
298
|
+
`;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/* ------------------------------------------------------------------ */
|
|
302
|
+
/* C#: classes with JsonPropertyName */
|
|
303
|
+
/* ------------------------------------------------------------------ */
|
|
304
|
+
|
|
305
|
+
function csRef(ref: TypeRef): string {
|
|
306
|
+
switch (ref.t) {
|
|
307
|
+
case "string":
|
|
308
|
+
return "string";
|
|
309
|
+
case "int":
|
|
310
|
+
return "long";
|
|
311
|
+
case "double":
|
|
312
|
+
return "double";
|
|
313
|
+
case "bool":
|
|
314
|
+
return "bool";
|
|
315
|
+
case "any":
|
|
316
|
+
case "null":
|
|
317
|
+
return "JsonNode";
|
|
318
|
+
case "array":
|
|
319
|
+
return `List<${csRef(ref.of)}>`;
|
|
320
|
+
case "named":
|
|
321
|
+
return ref.name;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function csFieldName(name: string): string {
|
|
326
|
+
const value = pascal(name);
|
|
327
|
+
return value.length > 0 ? value : "Field";
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function csDecl(decl: TypeDecl): string {
|
|
331
|
+
const props = decl.fields
|
|
332
|
+
.map((field) => {
|
|
333
|
+
return ` [JsonPropertyName("${field.name}")]\n public ${csRef(field.ref)} ${csFieldName(
|
|
334
|
+
field.name,
|
|
335
|
+
)} { get; set; }`;
|
|
336
|
+
})
|
|
337
|
+
.join("\n\n");
|
|
338
|
+
return `class ${decl.name}\n{\n${props}\n}`;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function csStarter(ctx: LanguageContext): string {
|
|
342
|
+
const input = buildTypes(INPUT_ROOT, ctx.input);
|
|
343
|
+
const output = buildTypes(OUTPUT_ROOT, ctx.output);
|
|
344
|
+
const decls = [...input.decls, ...output.decls].map(csDecl).join("\n\n");
|
|
345
|
+
const inputType = csRef(input.root);
|
|
346
|
+
const outputType = csRef(output.root);
|
|
347
|
+
return `using System;
|
|
348
|
+
using System.Collections.Generic;
|
|
349
|
+
using System.Text.Json;
|
|
350
|
+
using System.Text.Json.Nodes;
|
|
351
|
+
using System.Text.Json.Serialization;
|
|
352
|
+
|
|
353
|
+
${decls}
|
|
354
|
+
|
|
355
|
+
class Program
|
|
356
|
+
{
|
|
357
|
+
static ${outputType} ${ctx.functionName}(${inputType} payload)
|
|
358
|
+
{
|
|
359
|
+
throw new NotImplementedException("Not implemented: ${ctx.functionName}");
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
static void Main()
|
|
363
|
+
{
|
|
364
|
+
var raw = Console.In.ReadToEnd().Trim();
|
|
365
|
+
var payload = JsonSerializer.Deserialize<${inputType}>(raw);
|
|
366
|
+
Console.Write(JsonSerializer.Serialize(${ctx.functionName}(payload)));
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
`;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/* ------------------------------------------------------------------ */
|
|
373
|
+
/* Swift: Codable structs (only when the schema is fully typed) */
|
|
374
|
+
/* ------------------------------------------------------------------ */
|
|
375
|
+
|
|
376
|
+
function swiftRef(ref: TypeRef): string {
|
|
377
|
+
switch (ref.t) {
|
|
378
|
+
case "string":
|
|
379
|
+
return "String";
|
|
380
|
+
case "int":
|
|
381
|
+
return "Int";
|
|
382
|
+
case "double":
|
|
383
|
+
return "Double";
|
|
384
|
+
case "bool":
|
|
385
|
+
return "Bool";
|
|
386
|
+
case "array":
|
|
387
|
+
return `[${swiftRef(ref.of)}]`;
|
|
388
|
+
case "named":
|
|
389
|
+
return ref.name;
|
|
390
|
+
default:
|
|
391
|
+
return "Any";
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function swiftFieldName(name: string): string {
|
|
396
|
+
if (isIdentifier(name) && !isReserved(name)) return name;
|
|
397
|
+
let value = snake(name);
|
|
398
|
+
if (isReserved(value)) value = `${value}_`;
|
|
399
|
+
return isIdentifier(value) ? value : "field";
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function swiftDecl(decl: TypeDecl): string {
|
|
403
|
+
const fields: string[] = [];
|
|
404
|
+
const codingKeys: string[] = [];
|
|
405
|
+
let needsKeys = false;
|
|
406
|
+
for (const field of decl.fields) {
|
|
407
|
+
const prop = swiftFieldName(field.name);
|
|
408
|
+
const base = swiftRef(field.ref);
|
|
409
|
+
const ty = field.optional ? `${base}?` : base;
|
|
410
|
+
fields.push(` let ${prop}: ${ty}`);
|
|
411
|
+
if (prop !== field.name) {
|
|
412
|
+
needsKeys = true;
|
|
413
|
+
codingKeys.push(` case ${prop} = "${field.name}"`);
|
|
414
|
+
} else {
|
|
415
|
+
codingKeys.push(` case ${prop}`);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
const keys = needsKeys ? `\n\n enum CodingKeys: String, CodingKey {\n${codingKeys.join("\n")}\n }` : "";
|
|
419
|
+
return `struct ${decl.name}: Codable {\n${fields.join("\n")}${keys}\n}`;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function swiftAliasOrDecls(root: TypeRef, decls: TypeDecl[], rootName: string): string {
|
|
423
|
+
const pieces = decls.map(swiftDecl);
|
|
424
|
+
if (root.t !== "named") pieces.unshift(`typealias ${rootName} = ${swiftRef(root)}`);
|
|
425
|
+
return pieces.join("\n\n");
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function swiftStarter(ctx: LanguageContext): string {
|
|
429
|
+
const input = buildTypes(INPUT_ROOT, ctx.input);
|
|
430
|
+
const output = buildTypes(OUTPUT_ROOT, ctx.output);
|
|
431
|
+
const types = [
|
|
432
|
+
swiftAliasOrDecls(input.root, input.decls, INPUT_ROOT),
|
|
433
|
+
swiftAliasOrDecls(output.root, output.decls, OUTPUT_ROOT),
|
|
434
|
+
].join("\n\n");
|
|
435
|
+
return `import Foundation
|
|
436
|
+
|
|
437
|
+
${types}
|
|
438
|
+
|
|
439
|
+
func ${ctx.functionName}(_ payload: ${INPUT_ROOT}) -> ${OUTPUT_ROOT} {
|
|
440
|
+
fatalError("Not implemented: ${ctx.functionName}")
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
let raw = String(data: FileHandle.standardInput.readDataToEndOfFile(), encoding: .utf8) ?? ""
|
|
444
|
+
let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
445
|
+
let data = trimmed.isEmpty ? Data("null".utf8) : Data(raw.utf8)
|
|
446
|
+
let payload = try! JSONDecoder().decode(${INPUT_ROOT}.self, from: data)
|
|
447
|
+
let out = try! JSONEncoder().encode(${ctx.functionName}(payload))
|
|
448
|
+
FileHandle.standardOutput.write(out)
|
|
449
|
+
`;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
function swiftFallbackStarter(ctx: LanguageContext): string {
|
|
453
|
+
return `import Foundation
|
|
454
|
+
|
|
455
|
+
${formatComment("//", ctx)}
|
|
456
|
+
|
|
457
|
+
func ${ctx.functionName}(_ payload: Any) -> Any {
|
|
458
|
+
fatalError("Not implemented: ${ctx.functionName}")
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
let raw = String(data: FileHandle.standardInput.readDataToEndOfFile(), encoding: .utf8) ?? ""
|
|
462
|
+
let data = raw.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? Data("null".utf8) : Data(raw.utf8)
|
|
463
|
+
let payload = try! JSONSerialization.jsonObject(with: data, options: [.fragmentsAllowed])
|
|
464
|
+
let out = try! JSONSerialization.data(withJSONObject: ${ctx.functionName}(payload), options: [.fragmentsAllowed])
|
|
465
|
+
FileHandle.standardOutput.write(out)
|
|
466
|
+
`;
|
|
467
|
+
}
|
|
468
|
+
|
|
43
469
|
const adapters: Record<LanguageId, LanguageAdapter> = {
|
|
44
470
|
typescript: {
|
|
45
471
|
id: "typescript",
|
|
@@ -47,16 +473,8 @@ const adapters: Record<LanguageId, LanguageAdapter> = {
|
|
|
47
473
|
aliases: ["ts"],
|
|
48
474
|
extension: "ts",
|
|
49
475
|
runtime: "Node.js 22.6+ (uses `--experimental-strip-types`)",
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
function ${functionName}(payload: unknown): unknown {
|
|
53
|
-
throw new Error("Not implemented: ${functionName}");
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const input = readFileSync(0, "utf8");
|
|
57
|
-
const payload = JSON.parse(input.trim() === "" ? "null" : input);
|
|
58
|
-
process.stdout.write(JSON.stringify(${functionName}(payload) ?? null));
|
|
59
|
-
`,
|
|
476
|
+
typed: true,
|
|
477
|
+
starter: typescriptStarter,
|
|
60
478
|
files: (ctx) => [
|
|
61
479
|
packageJson(ctx.projectName, "node tests/runner.mjs"),
|
|
62
480
|
{
|
|
@@ -81,7 +499,7 @@ process.stdout.write(JSON.stringify(${functionName}(payload) ?? null));
|
|
|
81
499
|
},
|
|
82
500
|
],
|
|
83
501
|
build: () => null,
|
|
84
|
-
run: (
|
|
502
|
+
run: () => "node --experimental-strip-types src/solution.ts",
|
|
85
503
|
},
|
|
86
504
|
|
|
87
505
|
javascript: {
|
|
@@ -90,15 +508,18 @@ process.stdout.write(JSON.stringify(${functionName}(payload) ?? null));
|
|
|
90
508
|
aliases: ["js", "node"],
|
|
91
509
|
extension: "mjs",
|
|
92
510
|
runtime: "Node.js 18+",
|
|
93
|
-
|
|
94
|
-
|
|
511
|
+
typed: false,
|
|
512
|
+
starter: (ctx) => `${formatComment("//", ctx)}
|
|
513
|
+
|
|
514
|
+
function ${ctx.functionName}(payload) {
|
|
515
|
+
throw new Error("Not implemented: ${ctx.functionName}");
|
|
95
516
|
}
|
|
96
517
|
|
|
97
518
|
const chunks = [];
|
|
98
519
|
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
99
520
|
const input = Buffer.concat(chunks).toString("utf8");
|
|
100
521
|
const payload = JSON.parse(input.trim() === "" ? "null" : input);
|
|
101
|
-
process.stdout.write(JSON.stringify(${functionName}(payload) ?? null));
|
|
522
|
+
process.stdout.write(JSON.stringify(${ctx.functionName}(payload) ?? null));
|
|
102
523
|
`,
|
|
103
524
|
files: (ctx) => [packageJson(ctx.projectName, "node tests/runner.mjs")],
|
|
104
525
|
build: () => null,
|
|
@@ -111,18 +532,21 @@ process.stdout.write(JSON.stringify(${functionName}(payload) ?? null));
|
|
|
111
532
|
aliases: ["py", "python3"],
|
|
112
533
|
extension: "py",
|
|
113
534
|
runtime: "Python 3.8+",
|
|
114
|
-
|
|
535
|
+
typed: false,
|
|
536
|
+
starter: (ctx) => `${formatComment("#", ctx)}
|
|
537
|
+
|
|
538
|
+
import json
|
|
115
539
|
import sys
|
|
116
540
|
|
|
117
541
|
|
|
118
|
-
def ${functionName}(payload):
|
|
119
|
-
raise NotImplementedError("Not implemented: ${functionName}")
|
|
542
|
+
def ${ctx.functionName}(payload):
|
|
543
|
+
raise NotImplementedError("Not implemented: ${ctx.functionName}")
|
|
120
544
|
|
|
121
545
|
|
|
122
546
|
def main():
|
|
123
547
|
raw = sys.stdin.read().strip()
|
|
124
548
|
payload = json.loads(raw) if raw else None
|
|
125
|
-
print(json.dumps(${functionName}(payload)))
|
|
549
|
+
print(json.dumps(${ctx.functionName}(payload)))
|
|
126
550
|
|
|
127
551
|
|
|
128
552
|
if __name__ == "__main__":
|
|
@@ -139,15 +563,18 @@ if __name__ == "__main__":
|
|
|
139
563
|
aliases: ["rb"],
|
|
140
564
|
extension: "rb",
|
|
141
565
|
runtime: "Ruby 2.6+ (json is in the standard library)",
|
|
142
|
-
|
|
566
|
+
typed: false,
|
|
567
|
+
starter: (ctx) => `${formatComment("#", ctx)}
|
|
568
|
+
|
|
569
|
+
require "json"
|
|
143
570
|
|
|
144
|
-
def ${functionName}(payload)
|
|
145
|
-
raise NotImplementedError, "Not implemented: ${functionName}"
|
|
571
|
+
def ${ctx.functionName}(payload)
|
|
572
|
+
raise NotImplementedError, "Not implemented: ${ctx.functionName}"
|
|
146
573
|
end
|
|
147
574
|
|
|
148
575
|
raw = $stdin.read.strip
|
|
149
576
|
payload = raw.empty? ? nil : JSON.parse(raw)
|
|
150
|
-
puts JSON.generate(${functionName}(payload))
|
|
577
|
+
puts JSON.generate(${ctx.functionName}(payload))
|
|
151
578
|
`,
|
|
152
579
|
files: () => [],
|
|
153
580
|
build: () => null,
|
|
@@ -160,15 +587,17 @@ puts JSON.generate(${functionName}(payload))
|
|
|
160
587
|
aliases: ["php8"],
|
|
161
588
|
extension: "php",
|
|
162
589
|
runtime: "PHP 7.4+",
|
|
163
|
-
|
|
590
|
+
typed: false,
|
|
591
|
+
starter: (ctx) => `<?php
|
|
592
|
+
${formatComment("//", ctx)}
|
|
164
593
|
|
|
165
|
-
function ${functionName}($payload) {
|
|
166
|
-
throw new Exception("Not implemented: ${functionName}");
|
|
594
|
+
function ${ctx.functionName}($payload) {
|
|
595
|
+
throw new Exception("Not implemented: ${ctx.functionName}");
|
|
167
596
|
}
|
|
168
597
|
|
|
169
598
|
$raw = trim(stream_get_contents(STDIN));
|
|
170
599
|
$payload = $raw === "" ? null : json_decode($raw, true);
|
|
171
|
-
echo json_encode(${functionName}($payload));
|
|
600
|
+
echo json_encode(${ctx.functionName}($payload));
|
|
172
601
|
`,
|
|
173
602
|
files: () => [],
|
|
174
603
|
build: () => null,
|
|
@@ -180,38 +609,10 @@ echo json_encode(${functionName}($payload));
|
|
|
180
609
|
name: "Go",
|
|
181
610
|
aliases: ["golang"],
|
|
182
611
|
extension: "go",
|
|
183
|
-
runtime: "Go 1.18+ (
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
"encoding/json"
|
|
188
|
-
"fmt"
|
|
189
|
-
"io"
|
|
190
|
-
"os"
|
|
191
|
-
)
|
|
192
|
-
|
|
193
|
-
func ${functionName}(payload any) any {
|
|
194
|
-
panic("Not implemented: ${functionName}")
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
func main() {
|
|
198
|
-
raw, _ := io.ReadAll(os.Stdin)
|
|
199
|
-
var payload any
|
|
200
|
-
if len(raw) > 0 {
|
|
201
|
-
if err := json.Unmarshal(raw, &payload); err != nil {
|
|
202
|
-
panic(err)
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
out, err := json.Marshal(${functionName}(payload))
|
|
206
|
-
if err != nil {
|
|
207
|
-
panic(err)
|
|
208
|
-
}
|
|
209
|
-
fmt.Print(string(out))
|
|
210
|
-
}
|
|
211
|
-
`,
|
|
212
|
-
files: () => [
|
|
213
|
-
{ relative: "go.mod", contents: "module practice\n\ngo 1.21\n" },
|
|
214
|
-
],
|
|
612
|
+
runtime: "Go 1.18+ (`any`)",
|
|
613
|
+
typed: true,
|
|
614
|
+
starter: goStarter,
|
|
615
|
+
files: () => [{ relative: "go.mod", contents: "module practice\n\ngo 1.21\n" }],
|
|
215
616
|
build: () => "mkdir -p out && go build -o out/solution src/solution.go",
|
|
216
617
|
run: () => "./out/solution",
|
|
217
618
|
},
|
|
@@ -221,28 +622,9 @@ func main() {
|
|
|
221
622
|
name: "Rust",
|
|
222
623
|
aliases: ["rs", "cargo"],
|
|
223
624
|
extension: "rs",
|
|
224
|
-
runtime: "Rust 1.70+ (
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
fn ${functionName}(payload: Value) -> Value {
|
|
229
|
-
let _ = payload;
|
|
230
|
-
panic!("Not implemented: ${functionName}");
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
fn main() {
|
|
234
|
-
let mut raw = String::new();
|
|
235
|
-
std::io::stdin().read_to_string(&mut raw).expect("read stdin");
|
|
236
|
-
let trimmed = raw.trim();
|
|
237
|
-
let payload: Value = if trimmed.is_empty() {
|
|
238
|
-
Value::Null
|
|
239
|
-
} else {
|
|
240
|
-
serde_json::from_str(trimmed).expect("parse json")
|
|
241
|
-
};
|
|
242
|
-
let result = ${functionName}(payload);
|
|
243
|
-
println!("{}", serde_json::to_string(&result).expect("serialize json"));
|
|
244
|
-
}
|
|
245
|
-
`,
|
|
625
|
+
runtime: "Rust 1.70+ (serde + serde_json)",
|
|
626
|
+
typed: true,
|
|
627
|
+
starter: rustStarter,
|
|
246
628
|
files: (ctx) => [
|
|
247
629
|
{
|
|
248
630
|
relative: "Cargo.toml",
|
|
@@ -252,6 +634,7 @@ version = "0.1.0"
|
|
|
252
634
|
edition = "2021"
|
|
253
635
|
|
|
254
636
|
[dependencies]
|
|
637
|
+
serde = { version = "1", features = ["derive"] }
|
|
255
638
|
serde_json = "1"
|
|
256
639
|
|
|
257
640
|
[[bin]]
|
|
@@ -270,24 +653,8 @@ path = "src/solution.rs"
|
|
|
270
653
|
aliases: ["cs", "dotnet"],
|
|
271
654
|
extension: "cs",
|
|
272
655
|
runtime: ".NET 8 SDK",
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
class Program
|
|
277
|
-
{
|
|
278
|
-
static JsonNode? ${functionName}(JsonNode? payload)
|
|
279
|
-
{
|
|
280
|
-
throw new NotImplementedException("Not implemented: ${functionName}");
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
static void Main()
|
|
284
|
-
{
|
|
285
|
-
var raw = Console.In.ReadToEnd().Trim();
|
|
286
|
-
var payload = raw.Length == 0 ? null : JsonNode.Parse(raw);
|
|
287
|
-
Console.Write(${functionName}(payload)?.ToJsonString() ?? "null");
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
`,
|
|
656
|
+
typed: true,
|
|
657
|
+
starter: csStarter,
|
|
291
658
|
files: (ctx) => [
|
|
292
659
|
{
|
|
293
660
|
relative: `${ctx.projectName}.csproj`,
|
|
@@ -296,7 +663,7 @@ class Program
|
|
|
296
663
|
<OutputType>Exe</OutputType>
|
|
297
664
|
<TargetFramework>net8.0</TargetFramework>
|
|
298
665
|
<ImplicitUsings>enable</ImplicitUsings>
|
|
299
|
-
<Nullable>
|
|
666
|
+
<Nullable>disable</Nullable>
|
|
300
667
|
<AssemblyName>solution</AssemblyName>
|
|
301
668
|
</PropertyGroup>
|
|
302
669
|
</Project>
|
|
@@ -312,22 +679,9 @@ class Program
|
|
|
312
679
|
name: "Swift",
|
|
313
680
|
aliases: [],
|
|
314
681
|
extension: "swift",
|
|
315
|
-
runtime: "Swift 5.5+
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
func ${functionName}(_ payload: Any) -> Any {
|
|
319
|
-
fatalError("Not implemented: ${functionName}")
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
let raw = String(data: FileHandle.standardInput.readDataToEndOfFile(), encoding: .utf8) ?? ""
|
|
323
|
-
let data = raw.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
|
|
324
|
-
? Data("null".utf8)
|
|
325
|
-
: Data(raw.utf8)
|
|
326
|
-
let payload = try! JSONSerialization.jsonObject(with: data, options: [.fragmentsAllowed])
|
|
327
|
-
let result = ${functionName}(payload)
|
|
328
|
-
let out = try! JSONSerialization.data(withJSONObject: result, options: [.fragmentsAllowed])
|
|
329
|
-
FileHandle.standardOutput.write(out)
|
|
330
|
-
`,
|
|
682
|
+
runtime: "Swift 5.5+",
|
|
683
|
+
typed: true,
|
|
684
|
+
starter: (ctx) => (isFullyTyped(ctx.input) && isFullyTyped(ctx.output) ? swiftStarter(ctx) : swiftFallbackStarter(ctx)),
|
|
331
685
|
files: () => [],
|
|
332
686
|
build: () => "mkdir -p out && swiftc -O src/solution.swift -o out/solution",
|
|
333
687
|
run: () => "./out/solution",
|