-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathinterface.ts
More file actions
193 lines (187 loc) · 6.88 KB
/
interface.ts
File metadata and controls
193 lines (187 loc) · 6.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
import { MutexInterface } from "async-mutex";
import { z } from "zod";
/**
* 各言語の実行環境のインタフェース
*
* ここでは、runtimeを利用する側の注意点を @remarks
* runtimeを実装する側の注意点を @privateRemarks
* で表記します。
*
* @privateRemarks
* このインタフェースのプロパティのうち関数やオブジェクトはすべてuseCallbackやuseMemoなどを用いレンダリングごとに同じインスタンスを返すように実装してください。
*
*/
export interface RuntimeContext {
/**
* 実行環境を初期化します。
*
* @remarks
* useRuntime() 内のuseEffectで呼び出されます。
* useRuntime()でコンテキストを取得する場合はinitを呼び出す必要はないです。
*
* @privateRemarks
* 初期化にコストがかかるものは、init()で初期化フラグがトリガーされたときだけ初期化するようにします。
* useRuntime() が複数回使われた場合はinitも複数回呼ばれるため、
* init()はフラグを立てるだけにし、完了する前にreturnしてよいです。
* 初期化とcleanupはuseEffect()で非同期に行うのがよいです。
*
*/
init?: () => void;
/**
* ランタイムの初期化が完了したか、不要である場合true
*/
ready: boolean;
/**
* 実行環境を排他制御するmutex
* 排他制御が不要な場合はundefined
*
* @privateRemarks
* 実行環境に排他制御が必要な場合、MutexInterfaceのインスタンスを返してください。
*/
mutex?: MutexInterface;
/**
* 実行中のコマンドを中断
*
* @remarks
* 呼び出し側でmutexのロックはしません。
* interrupt()を呼ぶ際にはrunCommand()やrunFiles()が実行中であるためmutexはすでにロックされているはずです。
*
* @privateRemarks
* interrupt関数自体は同期関数で、mutexのロック状態に関わらず即座にreturnしてください。
* 実行環境を中断したあと再起動する際の処理に必要であればmutexをロックして非同期に処理を続行することも可能です。
*
*/
interrupt?: () => void;
/**
* コマンドを実行します。
*
* @param command - 実行するコマンド
* @param onOutput - 実行結果を返すコールバック
* @returns 実行が完了した際に解決するPromise
* ただし、onOutputコールバックは実行完了後に呼ばれる可能性もあります(実行したコマンドが非同期処理を含む場合)。
*
* @remarks
* runCommandを呼び出す際には呼び出し側でmutexをロックします。
* 複数のコマンドを連続実行したい場合があるからです。
* @privateRemarks
* runCommandの中ではmutexはロックしないでください。
* mutexがロックされていなかったらthrowするチェックを入れても良い
*/
runCommand?: (
command: string,
onOutput: (output: ReplOutput | UpdatedFile) => void
) => Promise<void>;
/**
* コードの構文チェックを行います。
* REPLでEnterを押した際の動作に影響します。
*
* @param code - チェックするコード
* @returns 行がコマンドとして完結していれば`complete`、次の行に続く場合は`incomplete`
*
* @remarks
* 呼び出し側でmutexのロックはしません。
* @privateRemarks
* 必要であればcheckSyntax()内でmutexをロックしても良い。
*/
checkSyntax?: (code: string) => Promise<SyntaxStatus>;
/**
* markdown内に記述されているREPLのサンプルコードをパースします。
*
* @example
* ```
* >>> if True:
* ... print("Hello")
* Hello
* ```
* をsplitReplExamplesに通すと
* ```
* [
* {
* command: 'if True:\n print("Hello")',
* output: {
* type: 'output',
* content: 'Hello'
* }
* }
* ]
* ```
* が返されるようにします。
*/
splitReplExamples?: (content: string) => ReplCommand[];
/**
* 指定されたファイルを実行します。
*
* filenames パラメーターの意味は言語の実装により異なります。
* 例えばRustではメインの.rsファイル1つのみを指定し、
* C++ではコンパイルする.cppファイルをすべて指定します。
*
* @param filenames - 実行するファイル名
* @param files - 実行環境に渡すファイル(実行するものと無関係のものを含んでも良い)
* @param onOutput - 実行結果を返すコールバック
* @returns 実行が完了した際に解決するPromise
* ただし、onOutputコールバックは実行完了後に呼ばれる可能性もあります(実行したコマンドが非同期処理を含む場合)。
*
* @remarks
* 呼び出し側でmutexのロックはしません。
* @privateRemarks
* 必要であればrunFiles()内でmutexをロックしても良い。
*/
runFiles: (
filenames: string[],
files: Readonly<Record<string, string>>,
onOutput: (output: ReplOutput | UpdatedFile) => void
) => Promise<void>;
/**
* 指定されたファイルを実行するためのコマンドライン引数文字列を返します。
* 表示用です。
*/
getCommandlineStr?: (filenames: string[]) => string;
/**
* 実行環境の名前、バージョン番号などの情報を返します。
*/
runtimeInfo?: RuntimeInfo;
}
export interface RuntimeInfo {
prettyLangName: string;
version?: string;
}
export const ReplOutputTypeSchema = z.enum([
"stdout",
"stderr",
"error",
"return",
"trace",
"system",
]);
export type ReplOutputType = z.output<typeof ReplOutputTypeSchema>;
export const ReplOutputSchema = z.object({
type: ReplOutputTypeSchema, // 出力の種類
message: z.string(), // 出力メッセージ
});
export type ReplOutput = z.output<typeof ReplOutputSchema>;
export const UpdatedFileSchema = z.object({
type: z.literal("file"),
filename: z.string(),
content: z.string(),
});
export type UpdatedFile = z.output<typeof UpdatedFileSchema>;
export const ReplCommandSchema = z.object({
command: z.string(),
output: z.array(ReplOutputSchema),
commandId: z.string().optional(), // Optional for backward compatibility
});
export type ReplCommand = z.output<typeof ReplCommandSchema>;
export type SyntaxStatus = "complete" | "incomplete" | "invalid"; // 構文チェックの結果
export const emptyMutex: MutexInterface = {
async runExclusive<T>(fn: () => Promise<T> | T) {
const result = fn();
return result instanceof Promise ? result : Promise.resolve(result);
},
acquire: async () => {
return () => {}; // Release function (no-op)
},
waitForUnlock: async () => {},
isLocked: () => false,
cancel: () => {},
release: () => {},
};