nadesiko3 3.7.20 → 3.7.22
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/batch/command.txt +483 -445
- package/batch/jsplugin2text.nako3 +1 -1
- package/batch/pickup_command.nako3 +12 -0
- package/core/command/snako.mts +5 -1
- package/core/deno/snako.ts +5 -1
- package/core/package-lock.json +382 -21
- package/core/package.json +4 -4
- package/core/src/README.md +98 -0
- package/core/src/nako3.mts +178 -614
- package/core/src/nako_basic_plugins.mts +39 -0
- package/core/src/nako_core_version.mts +2 -2
- package/core/src/nako_csv.mts +0 -1
- package/core/src/nako_event.mts +49 -0
- package/core/src/nako_gen.mts +291 -202
- package/core/src/nako_indent_inline.mts +2 -2
- package/core/src/nako_lex_rules.mts +1 -1
- package/core/src/nako_parser3.mts +106 -165
- package/core/src/nako_parser_async.mts +97 -0
- package/core/src/nako_parser_base.mts +4 -55
- package/core/src/nako_parser_message.mts +105 -0
- package/core/src/nako_parser_operator.mts +93 -0
- package/core/src/nako_plugin_manager.mts +260 -0
- package/core/src/nako_require.mts +292 -0
- package/core/src/nako_runner.mts +191 -0
- package/core/src/nako_tokenizer.mts +221 -0
- package/core/src/plugin_csv.mts +1 -1
- package/core/src/plugin_system.mts +34 -3259
- package/core/src/plugin_system_array.mts +699 -0
- package/core/src/plugin_system_datetime.mts +368 -0
- package/core/src/plugin_system_debug.mts +403 -0
- package/core/src/plugin_system_dict.mts +85 -0
- package/core/src/plugin_system_json.mts +73 -0
- package/core/src/plugin_system_math.mts +383 -0
- package/core/src/plugin_system_regexp.mts +120 -0
- package/core/src/plugin_system_stdio.mts +86 -0
- package/core/src/plugin_system_string.mts +666 -0
- package/core/src/plugin_system_timer.mts +152 -0
- package/core/src/plugin_system_types.mts +151 -0
- package/core/src/plugin_system_url.mts +193 -0
- package/core/src/plugin_toml.mts +3 -3
- package/core/test/fixtures/README.md +39 -0
- package/core/test/fixtures/make_parser_ast_golden.mjs +31 -0
- package/core/test/fixtures/parser_ast_golden.json +8027 -0
- package/core/test/fixtures/parser_corpus.mjs +120 -0
- package/core/test/indent_test.mjs +6 -0
- package/core/test/nako_basic_plugins_test.mjs +44 -0
- package/core/test/nako_event_test.mjs +85 -0
- package/core/test/nako_gen_perf_test.mjs +178 -0
- package/core/test/nako_parser_async_test.mjs +112 -0
- package/core/test/nako_parser_test.mjs +160 -0
- package/core/test/nako_plugin_manager_test.mjs +185 -0
- package/core/test/nako_require_test.mjs +153 -0
- package/core/test/nako_runner_test.mjs +114 -0
- package/core/test/nako_tokenizer_test.mjs +115 -0
- package/core/test/plugin_system_debug_test.mjs +141 -0
- package/core/test/plugin_system_split_test.mjs +179 -0
- package/core/test/plugin_system_test.mjs +6 -0
- package/core/tsconfig.json +0 -1
- package/package.json +24 -34
- package/release/_hash.txt +40 -40
- package/release/_script-tags.txt +16 -16
- package/release/command.json +1 -1
- package/release/command.json.js +1 -1
- package/release/command_cnako3.json +1 -1
- package/release/command_list.json +1 -1
- package/release/edit_main.js +6 -6
- package/release/edit_main.js.map +3 -3
- package/release/editor.js +6 -6
- package/release/plugin_datetime.js +1 -1
- package/release/plugin_datetime.js.map +3 -3
- package/release/plugin_keigo.js.map +3 -3
- package/release/plugin_markup.js +47 -47
- package/release/plugin_markup.js.map +3 -3
- package/release/plugin_weykturtle3d.js +1 -1
- package/release/plugin_weykturtle3d.js.map +3 -3
- package/release/version.js +2 -2
- package/release/version_main.js +2 -2
- package/release/version_main.js.map +2 -2
- package/release/wnako3.js +187 -214
- package/release/wnako3.js.map +4 -4
- package/release/wnako3webworker.js +172 -199
- package/release/wnako3webworker.js.map +4 -4
- package/src/cnako3mod.mjs +8 -4
- package/src/cnako3mod.mts +8 -4
- package/src/nako_version.mjs +2 -2
- package/src/nako_version.mts +2 -2
- package/src/plugin_browser_ajax.mjs +4 -4
- package/src/plugin_browser_ajax.mts +4 -4
- package/src/plugin_browser_audio.mjs +10 -10
- package/src/plugin_browser_audio.mts +10 -10
- package/src/plugin_browser_camera.mjs +4 -4
- package/src/plugin_browser_camera.mts +4 -4
- package/src/plugin_browser_canvas.mjs +2 -2
- package/src/plugin_browser_canvas.mts +2 -2
- package/src/plugin_browser_crypto.mjs +3 -3
- package/src/plugin_browser_crypto.mts +3 -3
- package/src/plugin_browser_dom_event.mjs +1 -1
- package/src/plugin_browser_dom_event.mts +1 -1
- package/src/plugin_browser_geolocation.mjs +1 -1
- package/src/plugin_browser_geolocation.mts +1 -1
- package/src/plugin_browser_hotkey.mjs +1 -1
- package/src/plugin_browser_hotkey.mts +1 -1
- package/src/plugin_browser_html.mjs +1 -1
- package/src/plugin_browser_html.mts +1 -1
- package/src/plugin_browser_location.mjs +1 -1
- package/src/plugin_browser_location.mts +1 -1
- package/src/plugin_browser_speech.mjs +1 -1
- package/src/plugin_browser_speech.mts +1 -1
- package/src/plugin_browser_storage.mjs +2 -2
- package/src/plugin_browser_storage.mts +2 -2
- package/src/plugin_httpserver.mjs +186 -15
- package/src/plugin_httpserver.mts +178 -12
- package/src/plugin_keigo.mjs +2 -2
- package/src/plugin_keigo.mts +2 -2
- package/src/plugin_node.mjs +48 -49
- package/src/plugin_node.mts +53 -54
- package/src/plugin_weykturtle3d.mjs +56 -56
- package/src/plugin_weykturtle3d.mts +56 -56
- package/src/wnako3.mjs +1 -1
- package/src/wnako3.mts +1 -1
- package/src/wnako3_editor.mjs +5 -5
- package/src/wnako3_editor.mts +5 -5
- package/src/wnako3mod.mjs +5 -2
- package/src/wnako3mod.mts +5 -2
- package/test/common/wnako3mod_test.mjs +48 -0
- package/test/node/package_json_test.mjs +17 -0
- package/test/node/plugin_httpserver_test.mjs +239 -0
- package/tools/nako3server/index.nako3 +4 -2
- package/core/src/nako3.mjs +0 -1021
- package/core/src/nako_ast.mjs +0 -4
- package/core/src/nako_colors.mjs +0 -77
- package/core/src/nako_core_version.mjs +0 -8
- package/core/src/nako_csv.mjs +0 -193
- package/core/src/nako_errors.mjs +0 -166
- package/core/src/nako_from_dncl.mjs +0 -285
- package/core/src/nako_from_dncl2.mjs +0 -347
- package/core/src/nako_gen.mjs +0 -2500
- package/core/src/nako_global.mjs +0 -138
- package/core/src/nako_indent.mjs +0 -442
- package/core/src/nako_indent_chars.mjs +0 -29
- package/core/src/nako_indent_inline.mjs +0 -361
- package/core/src/nako_josi_list.mjs +0 -47
- package/core/src/nako_lex_rules.mjs +0 -319
- package/core/src/nako_lexer.mjs +0 -794
- package/core/src/nako_logger.mjs +0 -221
- package/core/src/nako_parser3.mjs +0 -3250
- package/core/src/nako_parser_base.mjs +0 -403
- package/core/src/nako_parser_const.mjs +0 -37
- package/core/src/nako_prepare.mjs +0 -329
- package/core/src/nako_reserved_words.mjs +0 -42
- package/core/src/nako_source_mapping.mjs +0 -207
- package/core/src/nako_test.mjs +0 -37
- package/core/src/nako_token.mjs +0 -1
- package/core/src/nako_tools.mjs +0 -53
- package/core/src/nako_types.mjs +0 -14
- package/core/src/plugin_api.mjs +0 -4
- package/core/src/plugin_csv.mjs +0 -97
- package/core/src/plugin_math.mjs +0 -352
- package/core/src/plugin_promise.mjs +0 -102
- package/core/src/plugin_system.mjs +0 -3810
- package/core/src/plugin_test.mjs +0 -52
- package/core/src/plugin_toml.mjs +0 -39
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
// deno-lint-ignore-file no-explicit-any
|
|
2
|
+
/**
|
|
3
|
+
* なでしこ3 の「取り込み」文(require)に関する処理
|
|
4
|
+
*
|
|
5
|
+
* NakoCompiler から依存ファイルの読み込み処理を分離したモジュール (#2360)
|
|
6
|
+
* 循環参照を避けるため、このモジュールは nako3.mts を参照せず、
|
|
7
|
+
* 必要な機能は NakoRequireHost インターフェイス経由で受け取る。
|
|
8
|
+
*/
|
|
9
|
+
import { Token } from './nako_types.mjs'
|
|
10
|
+
import { NakoLexer } from './nako_lexer.mjs'
|
|
11
|
+
import { NakoImportError, NakoLexerError } from './nako_errors.mjs'
|
|
12
|
+
import { NakoLogger } from './nako_logger.mjs'
|
|
13
|
+
|
|
14
|
+
const cloneAsJSON = (x: any): any => JSON.parse(JSON.stringify(x))
|
|
15
|
+
|
|
16
|
+
/** インタプリタに「取り込み」文を追加するために用意するオブジェクト */
|
|
17
|
+
export interface LoaderToolTask<T> {
|
|
18
|
+
task: Promise<T>;
|
|
19
|
+
}
|
|
20
|
+
export interface LoaderTool {
|
|
21
|
+
// type: 'nako3' | 'js' | 'invalid' | 'mjs'
|
|
22
|
+
resolvePath: (name: string, token: Token, fromFile: string) => { type: string, filePath: string };
|
|
23
|
+
readNako3: (filePath: string, token: Token) => LoaderToolTask<string>;
|
|
24
|
+
readJs: (filePath: string, token: Token) => LoaderToolTask<any>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface DependenciesItem {
|
|
28
|
+
tokens: Token[];
|
|
29
|
+
alias: Set<string>;
|
|
30
|
+
addPluginFile: () => void;
|
|
31
|
+
funclist: any;
|
|
32
|
+
moduleExport: any;
|
|
33
|
+
}
|
|
34
|
+
export type Dependencies = { [key:string]:DependenciesItem }
|
|
35
|
+
|
|
36
|
+
/** 取り込み文を処理する際に必要となるコード分割機能 */
|
|
37
|
+
export interface NakoRequireScanner {
|
|
38
|
+
rawtokenize (code: string, line: number, filename: string, preCode?: string): Token[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* NakoRequireLoader がホスト(NakoCompiler)に要求する最小限の機能
|
|
43
|
+
*/
|
|
44
|
+
export interface NakoRequireHost extends NakoRequireScanner {
|
|
45
|
+
/** JSプラグインを登録する */
|
|
46
|
+
addPluginFromFile (fpath: string, po: { [key: string]: any }, persistent?: boolean): void;
|
|
47
|
+
getLogger (): NakoLogger;
|
|
48
|
+
/** 名前空間(modList)を汚さずにトークン化するための一時的なコンパイラを作る */
|
|
49
|
+
createScanner (): NakoRequireScanner;
|
|
50
|
+
/** 非同期読み込みに失敗した回数を数える */
|
|
51
|
+
countFailure (): void;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* ファイル内のrequire文の位置を列挙する。出力の配列はstartでソートされている。
|
|
56
|
+
* @param {Token[]} tokens rawtokenizeの出力
|
|
57
|
+
*/
|
|
58
|
+
export function listRequireStatements (tokens: Token[]): Token[] {
|
|
59
|
+
const requireStatements: Token[] = []
|
|
60
|
+
for (let i = 0; i + 2 < tokens.length; i++) {
|
|
61
|
+
// not (string|string_ex) '取り込み'
|
|
62
|
+
if (!(tokens[i].type === 'not' &&
|
|
63
|
+
(tokens[i + 1].type === 'string' || tokens[i + 1].type === 'string_ex') &&
|
|
64
|
+
tokens[i + 2].value === '取込')) {
|
|
65
|
+
continue
|
|
66
|
+
}
|
|
67
|
+
// 取り込むライブラリ
|
|
68
|
+
let filename = String(tokens[i + 1].value) + ''
|
|
69
|
+
// 全角コロン「:」を半角コロン「:」に正規化する(「貯蔵庫:」「拡張プラグイン:」の記法に対応 #2282)
|
|
70
|
+
filename = filename.replace(/^(貯蔵庫|拡張プラグイン):/, '$1:')
|
|
71
|
+
// 『取り込む』文で「拡張プラグイン:」機構を追加する #139
|
|
72
|
+
// (ex) !『貯蔵庫:ojyo-sama.nako3』を取り込む → https://n3s.nadesi.com/plain/ojyo-sama.nako3
|
|
73
|
+
if (filename.startsWith('貯蔵庫:') || filename.startsWith('貯蔵庫:')) {
|
|
74
|
+
filename = `https://n3s.nadesi.com/plain/${filename.substring(4)}`
|
|
75
|
+
}
|
|
76
|
+
// (ex) !『拡張プラグイン:music.js@1.0.2』を取り込む → https://cdn.jsdelivr.net/npm/nadesiko3-music@1.0.2/nadesiko3-music.js
|
|
77
|
+
if (filename.startsWith('拡張プラグイン:') || filename.startsWith('拡張プラグイン:')) {
|
|
78
|
+
const name = filename.substring('拡張プラグイン:'.length)
|
|
79
|
+
const m = name.match(/^([a-zA-Z0-9_-]+)\.(js|mjs|nako3)(@[0-9.]+)?$/)
|
|
80
|
+
if (m) {
|
|
81
|
+
let basename = m[1]
|
|
82
|
+
const ext = m[2]
|
|
83
|
+
const version = m[3] || '@latest'
|
|
84
|
+
if (ext === 'js' || ext === 'mjs') {
|
|
85
|
+
// JSプラグイン
|
|
86
|
+
if (!basename.startsWith('nadesiko3-')) {
|
|
87
|
+
basename = `nadesiko3-${basename}`
|
|
88
|
+
}
|
|
89
|
+
filename = `https://cdn.jsdelivr.net/npm/${basename}${version}/${basename}.${ext}`
|
|
90
|
+
} else {
|
|
91
|
+
// なでしこ3プラグイン
|
|
92
|
+
filename = `https://n3s.nadesi.com/plain/${basename}.${ext}`
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
throw new NakoImportError('『取込』の指定エラー。『拡張プラグイン:(ファイル名).(js|nako3)(@ver)』の書式で指定してください。', tokens[i].file, tokens[i].line)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// push
|
|
99
|
+
requireStatements.push({
|
|
100
|
+
...tokens[i],
|
|
101
|
+
start: i,
|
|
102
|
+
end: i + 3,
|
|
103
|
+
value: filename,
|
|
104
|
+
firstToken: tokens[i],
|
|
105
|
+
lastToken: tokens[i + 2]
|
|
106
|
+
})
|
|
107
|
+
i += 2
|
|
108
|
+
}
|
|
109
|
+
return requireStatements
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* 「取り込み」文で指定された依存ファイルを読み込み、トークン列を置換するクラス
|
|
114
|
+
*/
|
|
115
|
+
export class NakoRequireLoader {
|
|
116
|
+
/**
|
|
117
|
+
* 取り込み文を置換するためのオブジェクト。
|
|
118
|
+
* 正規化されたファイル名がキーになり、取り込み文の引数に指定された正規化されていないファイル名はaliasに入れられる。
|
|
119
|
+
* JavaScriptファイルによるプラグインの場合、contentは空文字列。
|
|
120
|
+
* funclistはシンタックスハイライトの高速化のために事前に取り出した、ファイルが定義する関数名のリスト。
|
|
121
|
+
*/
|
|
122
|
+
dependencies: Dependencies
|
|
123
|
+
private host: NakoRequireHost
|
|
124
|
+
|
|
125
|
+
constructor (host: NakoRequireHost) {
|
|
126
|
+
this.host = host
|
|
127
|
+
this.dependencies = {}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* プログラムが依存するファイルを再帰的に取得する。
|
|
132
|
+
* - 依存するファイルがJavaScriptファイルの場合、そのファイルを実行して評価結果をaddPluginFromFileに渡す。
|
|
133
|
+
* - 依存するファイルがなでしこ言語の場合、ファイルの中身を取得して変数に保存し、再帰する。
|
|
134
|
+
*
|
|
135
|
+
* @param {string} code
|
|
136
|
+
* @param {string} filename
|
|
137
|
+
* @param {string} preCode
|
|
138
|
+
* @param {LoaderTool} tools 実行環境 (ブラウザ or Node.js) によって外部ファイルの取得・実行方法は異なるため、引数でそれらを行う関数を受け取る。
|
|
139
|
+
* - resolvePath は指定した名前をもつファイルを検索し、正規化されたファイル名を返す関数。返されたファイル名はreadNako3かreadJsの引数になる。
|
|
140
|
+
* - readNako3は指定されたファイルの中身を返す関数。
|
|
141
|
+
* - readJsは指定したファイルをJavaScriptのプログラムとして実行し、`export default` でエクスポートされた値を返す関数。
|
|
142
|
+
* @returns {Promise<unknown> | void}
|
|
143
|
+
*/
|
|
144
|
+
load (code: string, filename: string, preCode: string, tools: LoaderTool): Promise<unknown>|void {
|
|
145
|
+
const dependencies: Dependencies = {}
|
|
146
|
+
const host = this.host
|
|
147
|
+
// 名前空間(modList)を汚さないように、取り込み文の検出には別のコンパイラを使う
|
|
148
|
+
const scanner = host.createScanner()
|
|
149
|
+
/**
|
|
150
|
+
* @param {any} item
|
|
151
|
+
* @param {any} tasks
|
|
152
|
+
*/
|
|
153
|
+
const loadJS = (item: any, tasks: any) => {
|
|
154
|
+
// jsならプラグインとして読み込む。(ESMでは必ず動的に読む)
|
|
155
|
+
const obj = tools.readJs(item.filePath, item.firstToken)
|
|
156
|
+
tasks.push(obj.task.then((res: any) => {
|
|
157
|
+
const pluginFuncs = res()
|
|
158
|
+
host.addPluginFromFile(item.filePath, pluginFuncs)
|
|
159
|
+
dependencies[item.filePath].funclist = pluginFuncs
|
|
160
|
+
dependencies[item.filePath].moduleExport = {}
|
|
161
|
+
dependencies[item.filePath].addPluginFile = () => { host.addPluginFromFile(item.filePath, pluginFuncs) }
|
|
162
|
+
}))
|
|
163
|
+
}
|
|
164
|
+
const loadNako3 = (item: any, tasks: any) => {
|
|
165
|
+
// nako3ならファイルを読んでdependenciesに保存する。
|
|
166
|
+
const content = tools.readNako3(item.filePath, item.firstToken)
|
|
167
|
+
const registerFile = (code: string) => {
|
|
168
|
+
// シンタックスハイライトの高速化のために、事前にファイルが定義する関数名のリストを取り出しておく。
|
|
169
|
+
// preDefineFuncはトークン列に変更を加えるため、事前にクローンしておく。
|
|
170
|
+
// 「プラグイン名設定」を行う (#956)
|
|
171
|
+
const modName = NakoLexer.filenameToModName(item.filePath)
|
|
172
|
+
code = `『${modName}』に名前空間設定;『${modName}』にプラグイン名設定;` + code + ';名前空間ポップ;'
|
|
173
|
+
const tokens = host.rawtokenize(code, 0, item.filePath)
|
|
174
|
+
dependencies[item.filePath].tokens = tokens
|
|
175
|
+
const funclist = new Map()
|
|
176
|
+
const moduleexport = new Map()
|
|
177
|
+
NakoLexer.preDefineFunc(cloneAsJSON(tokens), host.getLogger(), funclist, moduleexport)
|
|
178
|
+
dependencies[item.filePath].funclist = funclist
|
|
179
|
+
dependencies[item.filePath].moduleExport = moduleexport
|
|
180
|
+
// 再帰
|
|
181
|
+
return loadRec(code, item.filePath, '')
|
|
182
|
+
}
|
|
183
|
+
// 取り込み構文における問題を減らすため、必ず非同期でプログラムを読み込む仕様とした #1219
|
|
184
|
+
tasks.push(content.task.then((res) => registerFile(res)))
|
|
185
|
+
}
|
|
186
|
+
const loadRec = (code: string, filename: string, preCode: string): Promise<unknown>|void => {
|
|
187
|
+
const tasks: Promise<unknown>[] = []
|
|
188
|
+
// 取り込みが必要な情報一覧を調べる(トークン分割して、取り込みタグを得る)
|
|
189
|
+
const tags = listRequireStatements(scanner.rawtokenize(code, 0, filename, preCode))
|
|
190
|
+
// パスを解決する
|
|
191
|
+
const tagsResolvePath = tags.map((v) => ({ ...v, ...tools.resolvePath(v.value, v.firstToken as Token, filename) }))
|
|
192
|
+
// 取り込み開始
|
|
193
|
+
for (const item of tagsResolvePath) {
|
|
194
|
+
// 2回目以降の読み込み
|
|
195
|
+
// eslint-disable-next-line no-prototype-builtins
|
|
196
|
+
if (dependencies.hasOwnProperty(item.filePath)) {
|
|
197
|
+
dependencies[item.filePath].alias.add(item.value)
|
|
198
|
+
continue
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// 初回の読み込み
|
|
202
|
+
dependencies[item.filePath] = { tokens: [], alias: new Set([item.value]), addPluginFile: ():void => {}, funclist: {}, moduleExport: {} }
|
|
203
|
+
if (item.type === 'js' || item.type === 'mjs') {
|
|
204
|
+
loadJS(item, tasks)
|
|
205
|
+
} else if (item.type === 'nako3') {
|
|
206
|
+
loadNako3(item, tasks)
|
|
207
|
+
} else {
|
|
208
|
+
throw new NakoImportError(`ファイル『${String(item.value)}』を読み込めません。ファイルが存在しないか未対応の拡張子です。`,
|
|
209
|
+
(item.firstToken as Token).file, (item.firstToken as Token).line)
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (tasks.length > 0) {
|
|
213
|
+
return Promise.all(tasks)
|
|
214
|
+
}
|
|
215
|
+
return undefined
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
const result = loadRec(code, filename, preCode)
|
|
220
|
+
|
|
221
|
+
// 非同期な場合のエラーハンドリング
|
|
222
|
+
if (result !== undefined) {
|
|
223
|
+
result.catch((err) => {
|
|
224
|
+
// 読み込みに失敗したら処理を中断する
|
|
225
|
+
host.getLogger().error(err.msg)
|
|
226
|
+
host.countFailure()
|
|
227
|
+
})
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// すべてが終わってからthis.dependenciesに代入する。そうしないと、「実行」ボタンを連打した場合など、
|
|
231
|
+
// load() が並列実行されるときに正しく動作しない。
|
|
232
|
+
this.dependencies = dependencies
|
|
233
|
+
return result
|
|
234
|
+
} catch (err) {
|
|
235
|
+
// 同期処理では素直に例外を投げる
|
|
236
|
+
host.getLogger().error(String(err))
|
|
237
|
+
throw err
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* 再帰的にrequire文を置換する。
|
|
243
|
+
* .jsであれば削除し、.nako3であればそのファイルのトークン列で置換する。
|
|
244
|
+
* @param {Token[]} tokens
|
|
245
|
+
* @param {Set<string>} [includeGuard]
|
|
246
|
+
* @returns {Token[]} 削除された取り込み文のトークン
|
|
247
|
+
*/
|
|
248
|
+
replaceRequireStatements (tokens: Token[], includeGuard: Set<string> = new Set()): Token[] {
|
|
249
|
+
const deletedTokens: Token[] = []
|
|
250
|
+
for (const r of listRequireStatements(tokens).reverse()) {
|
|
251
|
+
const filePath = Object.keys(this.dependencies).find((key) => this.dependencies[key].alias.has(r.value))
|
|
252
|
+
if (filePath === undefined) {
|
|
253
|
+
if (!r.firstToken) { throw new Error(`ファイル『${String(r.value)}』が読み込まれていません。`) }
|
|
254
|
+
throw new NakoLexerError(`ファイル『${String(r.value)}』が読み込まれていません。`,
|
|
255
|
+
(r.firstToken).startOffset || 0,
|
|
256
|
+
(r.firstToken).endOffset || 0,
|
|
257
|
+
(r.firstToken).line, (r.firstToken).file)
|
|
258
|
+
}
|
|
259
|
+
// C言語のinclude guardと同じ仕組みで無限ループを防ぐ。(同一ファイルを別名で取り込むケースがあるため filePath を使う)
|
|
260
|
+
if (includeGuard.has(filePath)) {
|
|
261
|
+
deletedTokens.push(...tokens.splice((r.start || 0), (r.end || 0) - (r.start || 0)))
|
|
262
|
+
continue
|
|
263
|
+
}
|
|
264
|
+
this.dependencies[filePath].addPluginFile()
|
|
265
|
+
const children = cloneAsJSON(this.dependencies[filePath].tokens)
|
|
266
|
+
includeGuard.add(filePath)
|
|
267
|
+
deletedTokens.push(...this.replaceRequireStatements(children, includeGuard))
|
|
268
|
+
deletedTokens.push(...tokens.splice(r.start || 0, (r.end || 0) - (r.start || 0), ...children))
|
|
269
|
+
}
|
|
270
|
+
return deletedTokens
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* replaceRequireStatementsのシンタックスハイライト用の実装。
|
|
275
|
+
* @param {Token[]} tokens
|
|
276
|
+
* @returns {Token[]} 削除された取り込み文のトークン
|
|
277
|
+
*/
|
|
278
|
+
removeRequireStatements (tokens: Token[]): Token[] {
|
|
279
|
+
const deletedTokens: Token[] = []
|
|
280
|
+
for (const r of listRequireStatements(tokens).reverse()) {
|
|
281
|
+
// プラグイン命令のシンタックスハイライトのために、addPluginFileを呼んで関数のリストをthis.dependencies[filePath].funclistに保存させる。
|
|
282
|
+
const filePath = Object.keys(this.dependencies).find((key) => this.dependencies[key].alias.has(r.value))
|
|
283
|
+
if (filePath !== undefined) {
|
|
284
|
+
this.dependencies[filePath].addPluginFile()
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// 全ての取り込み文を削除する。そうしないとトークン化に時間がかかりすぎる。
|
|
288
|
+
deletedTokens.push(...tokens.splice(r.start || 0, (r.end || 0) - (r.start || 0)))
|
|
289
|
+
}
|
|
290
|
+
return deletedTokens
|
|
291
|
+
}
|
|
292
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// deno-lint-ignore-file no-explicit-any
|
|
2
|
+
/**
|
|
3
|
+
* なでしこ3のプログラム実行部
|
|
4
|
+
*
|
|
5
|
+
* NakoCompiler から実行系(evalJS / runSync / runAsync など)を分離したモジュール (#2360)
|
|
6
|
+
* 循環参照を避けるため、このモジュールは nako3.mts を参照せず、
|
|
7
|
+
* 必要な機能は NakoRunnerHost インターフェイス経由で受け取る。
|
|
8
|
+
*/
|
|
9
|
+
import { CompilerOptions, NakoComEventName } from './nako_types.mjs'
|
|
10
|
+
import { NakoLexer } from './nako_lexer.mjs'
|
|
11
|
+
import { NakoLogger } from './nako_logger.mjs'
|
|
12
|
+
import { NakoGlobal } from './nako_global.mjs'
|
|
13
|
+
import { NakoGen, NakoGenResult } from './nako_gen.mjs'
|
|
14
|
+
|
|
15
|
+
/** コンパイラ実行オプションを生成 */
|
|
16
|
+
export function newCompilerOptions (initObj: Partial<CompilerOptions> = {}): CompilerOptions {
|
|
17
|
+
if (initObj === null || typeof initObj !== 'object') { initObj = {} }
|
|
18
|
+
initObj.testOnly = initObj.testOnly || false
|
|
19
|
+
initObj.resetEnv = initObj.resetEnv || false
|
|
20
|
+
initObj.resetAll = initObj.resetAll || false
|
|
21
|
+
initObj.preCode = initObj.preCode || ''
|
|
22
|
+
initObj.nakoGlobal = initObj.nakoGlobal || null
|
|
23
|
+
return initObj as CompilerOptions
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* NakoRunner がホスト(NakoCompiler)に要求する最小限の機能
|
|
28
|
+
*/
|
|
29
|
+
export interface NakoRunnerHost {
|
|
30
|
+
/** なでしこのプログラムをコンパイルする */
|
|
31
|
+
compileFromCode (code: string, filename: string, options?: CompilerOptions): NakoGenResult;
|
|
32
|
+
/** 実行環境(NakoGlobal)を生成する */
|
|
33
|
+
createNakoGlobal (gen: NakoGen, guid: number): NakoGlobal;
|
|
34
|
+
getLogger (): NakoLogger;
|
|
35
|
+
/** コンパイラのイベントを発火する */
|
|
36
|
+
fireEvent (eventName: NakoComEventName, event: any): void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* コンパイル済みのJavaScriptを実行し、実行環境(NakoGlobal)を管理するクラス
|
|
41
|
+
*/
|
|
42
|
+
export class NakoRunner {
|
|
43
|
+
/** 生成した NakoGlobal のインスタンスを保持 */
|
|
44
|
+
globals: NakoGlobal[]
|
|
45
|
+
/** 現在の NakoGlobal オブジェクト */
|
|
46
|
+
currentGlobal: NakoGlobal|null
|
|
47
|
+
private host: NakoRunnerHost
|
|
48
|
+
|
|
49
|
+
constructor (host: NakoRunnerHost) {
|
|
50
|
+
this.host = host
|
|
51
|
+
this.globals = []
|
|
52
|
+
this.currentGlobal = null
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** 各プラグインをリセットする */
|
|
56
|
+
clearPlugins (): void {
|
|
57
|
+
// 他に実行している「なでしこ」があればクリアする
|
|
58
|
+
this.globals.forEach((sys: NakoGlobal) => {
|
|
59
|
+
if (!sys) { return }
|
|
60
|
+
// core #56
|
|
61
|
+
sys.__setSysVar('__forceClose', true)
|
|
62
|
+
sys.reset()
|
|
63
|
+
})
|
|
64
|
+
this.globals = [] // clear
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 環境を指定してJavaScriptのコードを実行する
|
|
69
|
+
* @param code JavaScriptのコード
|
|
70
|
+
* @param nakoGlobal 実行環境
|
|
71
|
+
*/
|
|
72
|
+
evalJS (code: string, nakoGlobal: NakoGlobal): void {
|
|
73
|
+
this.currentGlobal = nakoGlobal // 現在のnakoGlobalを記録
|
|
74
|
+
this.currentGlobal.lastJSCode = code
|
|
75
|
+
// 実行前に環境を初期化するイベントを実行(beforeRun)
|
|
76
|
+
this.host.fireEvent('beforeRun', nakoGlobal)
|
|
77
|
+
try {
|
|
78
|
+
const f = new Function(nakoGlobal.lastJSCode)
|
|
79
|
+
f.apply(nakoGlobal)
|
|
80
|
+
} catch (err: any) {
|
|
81
|
+
// なでしこコードのエラーは抑止してログにのみ記録
|
|
82
|
+
nakoGlobal.numFailures++
|
|
83
|
+
this.host.getLogger().error(err)
|
|
84
|
+
throw err
|
|
85
|
+
}
|
|
86
|
+
// 実行後に終了イベントを実行(finish)
|
|
87
|
+
this.host.fireEvent('finish', nakoGlobal)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* (非推奨) 同期的になでしこのプログラムcodeを実行する
|
|
92
|
+
* @param code なでしこのプログラム
|
|
93
|
+
* @param filename ファイル名
|
|
94
|
+
* @param options オプション
|
|
95
|
+
* @returns 実行に利用したグローバルオブジェクト
|
|
96
|
+
* @deprecated 代わりにrunAsyncメソッドを使ってください。(core #52)
|
|
97
|
+
*/
|
|
98
|
+
runSync (code: string, filename: string, options: CompilerOptions|undefined = undefined): NakoGlobal {
|
|
99
|
+
// コンパイル
|
|
100
|
+
options = newCompilerOptions(options)
|
|
101
|
+
const out = this.host.compileFromCode(code, filename, options)
|
|
102
|
+
// 実行前に環境を生成
|
|
103
|
+
const nakoGlobal = this.getNakoGlobal(options, out.gen, filename)
|
|
104
|
+
// 実行
|
|
105
|
+
this.evalJS(out.runtimeEnv, nakoGlobal)
|
|
106
|
+
return nakoGlobal
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 非同期になでしこのプログラムcodeを実行する
|
|
111
|
+
* @param code なでしこのプログラム
|
|
112
|
+
* @param filename ファイル名
|
|
113
|
+
* @param options オプション
|
|
114
|
+
* @returns 実行に利用したグローバルオブジェクト
|
|
115
|
+
*/
|
|
116
|
+
async runAsync (code: string, filename: string, options: CompilerOptions|undefined = undefined): Promise<NakoGlobal> {
|
|
117
|
+
// コンパイル
|
|
118
|
+
options = newCompilerOptions(options)
|
|
119
|
+
const compiledCode = this.host.compileFromCode(code, filename, options)
|
|
120
|
+
// 実行前に環境を生成
|
|
121
|
+
const nakoGlobal = this.getNakoGlobal(options, compiledCode.gen, filename)
|
|
122
|
+
// 実行
|
|
123
|
+
this.evalJS(compiledCode.runtimeEnv, nakoGlobal)
|
|
124
|
+
return nakoGlobal
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** 実行環境(NakoGlobal)を取得する。指定が無ければ前回の値を再利用する */
|
|
128
|
+
getNakoGlobal (options: CompilerOptions, gen: NakoGen, filename: string): NakoGlobal {
|
|
129
|
+
// オプションを参照
|
|
130
|
+
let g: NakoGlobal|null = options.nakoGlobal
|
|
131
|
+
if (!g) {
|
|
132
|
+
// 空ならば前回の値を参照(リセットするなら新規で作成する)
|
|
133
|
+
if (this.globals.length > 0 && options.resetAll === false && options.resetEnv === false) {
|
|
134
|
+
g = this.globals[this.globals.length - 1]
|
|
135
|
+
} else {
|
|
136
|
+
g = this.host.createNakoGlobal(gen, this.globals.length + 1)
|
|
137
|
+
}
|
|
138
|
+
// 名前空間を設定
|
|
139
|
+
g.__varslist[0].set('名前空間', NakoLexer.filenameToModName(filename))
|
|
140
|
+
}
|
|
141
|
+
if (this.globals.indexOf(g) < 0) { this.globals.push(g) }
|
|
142
|
+
return g
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* (非推奨) 同期的になでしこのプログラムcodeを実行する
|
|
147
|
+
* @deprecated 代わりにrunAsyncメソッドを使ってください。(core #52)
|
|
148
|
+
*/
|
|
149
|
+
runEx (code: string, filename: string, opts: CompilerOptions, preCode = '', nakoGlobal: NakoGlobal|undefined = undefined): NakoGlobal {
|
|
150
|
+
// コンパイル
|
|
151
|
+
opts.preCode = preCode
|
|
152
|
+
if (nakoGlobal) { opts.nakoGlobal = nakoGlobal }
|
|
153
|
+
return this.runSync(code, filename, opts)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* (非推奨) 非同期でなでしこのプログラムを実行する
|
|
158
|
+
* @deprecated 代わりにrunAsyncメソッドを使ってください。(core #52)
|
|
159
|
+
*/
|
|
160
|
+
async run (code: string, fname: string, isReset: boolean, isTest: boolean, preCode = ''): Promise<NakoGlobal> {
|
|
161
|
+
const opts: CompilerOptions = newCompilerOptions({
|
|
162
|
+
resetEnv: isReset,
|
|
163
|
+
resetAll: isReset,
|
|
164
|
+
testOnly: isTest,
|
|
165
|
+
preCode
|
|
166
|
+
})
|
|
167
|
+
return this.runEx(code, fname, opts)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* (非推奨) なでしこのプログラムを実行(他に実行しているインスタンスもリセットする)
|
|
172
|
+
*/
|
|
173
|
+
async runReset (code: string, fname = 'main.nako3', preCode = ''): Promise<NakoGlobal> {
|
|
174
|
+
const opts = newCompilerOptions({ resetAll: true, resetEnv: true, preCode })
|
|
175
|
+
return this.runAsync(code, fname, opts)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* テストを実行する
|
|
180
|
+
* @param code
|
|
181
|
+
* @param fname
|
|
182
|
+
* @param preCode
|
|
183
|
+
* @param testOnly
|
|
184
|
+
*/
|
|
185
|
+
test (code: string, fname: string, preCode = '', testOnly = false): NakoGlobal {
|
|
186
|
+
const options = newCompilerOptions()
|
|
187
|
+
options.preCode = preCode
|
|
188
|
+
options.testOnly = testOnly
|
|
189
|
+
return this.runSync(code, fname, options)
|
|
190
|
+
}
|
|
191
|
+
}
|