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
package/core/src/nako3.mjs
DELETED
|
@@ -1,1021 +0,0 @@
|
|
|
1
|
-
// parser / lexer
|
|
2
|
-
import { NakoParser } from './nako_parser3.mjs';
|
|
3
|
-
import { NakoLexer } from './nako_lexer.mjs';
|
|
4
|
-
import { NakoPrepare } from './nako_prepare.mjs';
|
|
5
|
-
import { generateJS, NakoGenOptions } from './nako_gen.mjs';
|
|
6
|
-
import { convertInlineIndent, convertIndentSyntax } from './nako_indent_inline.mjs';
|
|
7
|
-
import { convertDNCL } from './nako_from_dncl.mjs';
|
|
8
|
-
import { convertDNCL2 } from './nako_from_dncl2.mjs';
|
|
9
|
-
import { SourceMappingOfTokenization, SourceMappingOfIndentSyntax, OffsetToLineColumn, subtractSourceMapByPreCodeLength } from './nako_source_mapping.mjs';
|
|
10
|
-
import { NakoLexerError, NakoImportError, NakoSyntaxError, InternalLexerError } from './nako_errors.mjs';
|
|
11
|
-
import { NakoLogger } from './nako_logger.mjs';
|
|
12
|
-
import { NakoGlobal } from './nako_global.mjs';
|
|
13
|
-
// version info
|
|
14
|
-
import coreVersion from './nako_core_version.mjs';
|
|
15
|
-
// basic plugins
|
|
16
|
-
import PluginSystem from './plugin_system.mjs';
|
|
17
|
-
import PluginMath from './plugin_math.mjs';
|
|
18
|
-
import PluginCSV from './plugin_csv.mjs';
|
|
19
|
-
import PluginPromise from './plugin_promise.mjs';
|
|
20
|
-
import PluginTOML from './plugin_toml.mjs';
|
|
21
|
-
import PluginTest from './plugin_test.mjs';
|
|
22
|
-
const cloneAsJSON = (x) => JSON.parse(JSON.stringify(x));
|
|
23
|
-
const PLUGIN_MIN_VERSION_INT = 600; // = minor * 100 + patch
|
|
24
|
-
/** コンパイラ実行オプションを生成 */
|
|
25
|
-
export function newCompilerOptions(initObj = {}) {
|
|
26
|
-
if (typeof initObj !== 'object') {
|
|
27
|
-
initObj = {};
|
|
28
|
-
}
|
|
29
|
-
initObj.testOnly = initObj.testOnly || false;
|
|
30
|
-
initObj.resetEnv = initObj.resetEnv || false;
|
|
31
|
-
initObj.resetAll = initObj.resetAll || false;
|
|
32
|
-
initObj.preCode = initObj.preCode || '';
|
|
33
|
-
initObj.nakoGlobal = initObj.nakoGlobal || null;
|
|
34
|
-
return initObj;
|
|
35
|
-
}
|
|
36
|
-
/** なでしこコンパイラ */
|
|
37
|
-
export class NakoCompiler {
|
|
38
|
-
/**
|
|
39
|
-
* @param {undefined | {'useBasicPlugin':true|false}} options
|
|
40
|
-
*/
|
|
41
|
-
constructor(options = undefined) {
|
|
42
|
-
if (options === undefined) {
|
|
43
|
-
options = { useBasicPlugin: true };
|
|
44
|
-
}
|
|
45
|
-
// 環境のリセット
|
|
46
|
-
this.__varslist = [this.newVaiables(), this.newVaiables(), this.newVaiables()]; // このオブジェクトは変更しないこと (this.gen.__varslist と共有する)
|
|
47
|
-
this.__locals = this.newVaiables(); // ローカル変数
|
|
48
|
-
this.__self = this;
|
|
49
|
-
this.__vars = this.__varslist[2]; // alias of __varslist[2]
|
|
50
|
-
this.__v1 = this.__varslist[1]; // alias of __varslist[1]
|
|
51
|
-
this.__v0 = this.__varslist[0]; // alias of __varslist[0]
|
|
52
|
-
// バージョンを設定
|
|
53
|
-
this.version = coreVersion.version;
|
|
54
|
-
this.coreVersion = coreVersion.version;
|
|
55
|
-
this.__globals = []; // 生成した NakoGlobal のインスタンスを保持
|
|
56
|
-
this.__globalObj = null;
|
|
57
|
-
this.__module = {}; // requireなどで取り込んだモジュールの一覧
|
|
58
|
-
this.pluginFunclist = {}; // プラグインで定義された関数
|
|
59
|
-
this.funclist = this.newVaiables(); // プラグインで定義された関数 + ユーザーが定義した関数
|
|
60
|
-
this.moduleExport = this.newVaiables();
|
|
61
|
-
this.pluginfiles = {}; // 取り込んだファイル一覧
|
|
62
|
-
this.commandlist = new Set(); // プラグインで定義された定数・変数・関数の名前
|
|
63
|
-
this.nakoFuncList = this.newVaiables(); // __v1に配置するJavaScriptのコードで定義された関数
|
|
64
|
-
this.eventList = []; // 実行前に環境を変更するためのイベント
|
|
65
|
-
this.codeGenerateor = {}; // コードジェネレータ
|
|
66
|
-
this.debugOption = { useDebug: false, waitTime: 0 };
|
|
67
|
-
this.logger = new NakoLogger();
|
|
68
|
-
this.filename = 'main.nako3';
|
|
69
|
-
/**
|
|
70
|
-
* 取り込み文を置換するためのオブジェクト。
|
|
71
|
-
* 正規化されたファイル名がキーになり、取り込み文の引数に指定された正規化されていないファイル名はaliasに入れられる。
|
|
72
|
-
* JavaScriptファイルによるプラグインの場合、contentは空文字列。
|
|
73
|
-
* funclistはシンタックスハイライトの高速化のために事前に取り出した、ファイルが定義する関数名のリスト。
|
|
74
|
-
*/
|
|
75
|
-
this.dependencies = {};
|
|
76
|
-
this.usedFuncs = new Set();
|
|
77
|
-
this.numFailures = 0;
|
|
78
|
-
if (options.useBasicPlugin) {
|
|
79
|
-
this.addBasicPlugins();
|
|
80
|
-
}
|
|
81
|
-
// 必要なオブジェクトを覚えておく
|
|
82
|
-
this.prepare = NakoPrepare.getInstance();
|
|
83
|
-
this.parser = new NakoParser(this.logger);
|
|
84
|
-
this.lexer = new NakoLexer(this.logger);
|
|
85
|
-
// 関数一覧を設定
|
|
86
|
-
this.lexer.setFuncList(this.funclist);
|
|
87
|
-
this.lexer.setModuleExport(this.moduleExport);
|
|
88
|
-
// link for plysin_system::予約語一覧取得/助詞一覧取得
|
|
89
|
-
this.reservedWords = JSON.parse(JSON.stringify(this.lexer.reservedWords)); // 外部公開用のデータなので複製して保持する
|
|
90
|
-
this.josiList = JSON.parse(JSON.stringify(this.lexer.josiList));
|
|
91
|
-
}
|
|
92
|
-
/** モジュール(名前空間)の一覧を取得する */
|
|
93
|
-
getModList() {
|
|
94
|
-
return this.lexer.modList;
|
|
95
|
-
}
|
|
96
|
-
getLogger() {
|
|
97
|
-
return this.logger;
|
|
98
|
-
}
|
|
99
|
-
getNakoFuncList() {
|
|
100
|
-
return this.nakoFuncList;
|
|
101
|
-
}
|
|
102
|
-
getNakoFunc(name) {
|
|
103
|
-
return this.nakoFuncList.get(name);
|
|
104
|
-
}
|
|
105
|
-
getPluginfiles() {
|
|
106
|
-
return this.pluginfiles;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* 基本的なプラグインを追加する
|
|
110
|
-
*/
|
|
111
|
-
addBasicPlugins() {
|
|
112
|
-
this.addPlugin(PluginSystem);
|
|
113
|
-
this.addPlugin(PluginMath);
|
|
114
|
-
this.addPlugin(PluginPromise);
|
|
115
|
-
this.addPlugin(PluginTest);
|
|
116
|
-
this.addPlugin(PluginCSV);
|
|
117
|
-
this.addPlugin(PluginTOML);
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* loggerを新しいインスタンスで置き換える。
|
|
121
|
-
*/
|
|
122
|
-
replaceLogger() {
|
|
123
|
-
const logger = this.lexer.logger = this.parser.logger = this.logger = new NakoLogger();
|
|
124
|
-
return logger;
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* ファイル内のrequire文の位置を列挙する。出力の配列はstartでソートされている。
|
|
128
|
-
* @param {Token[]} tokens rawtokenizeの出力
|
|
129
|
-
*/
|
|
130
|
-
static listRequireStatements(tokens) {
|
|
131
|
-
const requireStatements = [];
|
|
132
|
-
for (let i = 0; i + 2 < tokens.length; i++) {
|
|
133
|
-
// not (string|string_ex) '取り込み'
|
|
134
|
-
if (!(tokens[i].type === 'not' &&
|
|
135
|
-
(tokens[i + 1].type === 'string' || tokens[i + 1].type === 'string_ex') &&
|
|
136
|
-
tokens[i + 2].value === '取込')) {
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
// 取り込むライブラリ
|
|
140
|
-
let filename = String(tokens[i + 1].value) + '';
|
|
141
|
-
// 全角コロン「:」を半角コロン「:」に正規化する(「貯蔵庫:」「拡張プラグイン:」の記法に対応 #2282)
|
|
142
|
-
filename = filename.replace(/^(貯蔵庫|拡張プラグイン):/, '$1:');
|
|
143
|
-
// 『取り込む』文で「拡張プラグイン:」機構を追加する #139
|
|
144
|
-
// (ex) !『貯蔵庫:ojyo-sama.nako3』を取り込む → https://n3s.nadesi.com/plain/ojyo-sama.nako3
|
|
145
|
-
if (filename.startsWith('貯蔵庫:') || filename.startsWith('貯蔵庫:')) {
|
|
146
|
-
filename = `https://n3s.nadesi.com/plain/${filename.substring(4)}`;
|
|
147
|
-
}
|
|
148
|
-
// (ex) !『拡張プラグイン:music.js@1.0.2』を取り込む → https://cdn.jsdelivr.net/npm/nadesiko3-music@1.0.2/nadesiko3-music.js
|
|
149
|
-
if (filename.startsWith('拡張プラグイン:') || filename.startsWith('拡張プラグイン:')) {
|
|
150
|
-
const name = filename.substring('拡張プラグイン:'.length);
|
|
151
|
-
const m = name.match(/^([a-zA-Z0-9_-]+)\.(js|mjs|nako3)(@[0-9.]+)?$/);
|
|
152
|
-
if (m) {
|
|
153
|
-
let basename = m[1];
|
|
154
|
-
const ext = m[2];
|
|
155
|
-
const version = m[3] || '@latest';
|
|
156
|
-
if (ext === 'js' || ext === 'mjs') {
|
|
157
|
-
// JSプラグイン
|
|
158
|
-
if (!basename.startsWith('nadesiko3-')) {
|
|
159
|
-
basename = `nadesiko3-${basename}`;
|
|
160
|
-
}
|
|
161
|
-
filename = `https://cdn.jsdelivr.net/npm/${basename}${version}/${basename}.${ext}`;
|
|
162
|
-
}
|
|
163
|
-
else {
|
|
164
|
-
// なでしこ3プラグイン
|
|
165
|
-
filename = `https://n3s.nadesi.com/plain/${basename}.${ext}`;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
else {
|
|
169
|
-
throw new NakoImportError('『取込』の指定エラー。『拡張プラグイン:(ファイル名).(js|nako3)(@ver)』の書式で指定してください。', tokens[i].file, tokens[i].line);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
// push
|
|
173
|
-
requireStatements.push({
|
|
174
|
-
...tokens[i],
|
|
175
|
-
start: i,
|
|
176
|
-
end: i + 3,
|
|
177
|
-
value: filename,
|
|
178
|
-
firstToken: tokens[i],
|
|
179
|
-
lastToken: tokens[i + 2]
|
|
180
|
-
});
|
|
181
|
-
i += 2;
|
|
182
|
-
}
|
|
183
|
-
return requireStatements;
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* プログラムが依存するファイルを再帰的に取得する。
|
|
187
|
-
* - 依存するファイルがJavaScriptファイルの場合、そのファイルを実行して評価結果をthis.addPluginFileに渡す。
|
|
188
|
-
* - 依存するファイルがなでしこ言語の場合、ファイルの中身を取得して変数に保存し、再帰する。
|
|
189
|
-
*
|
|
190
|
-
* @param {string} code
|
|
191
|
-
* @param {string} filename
|
|
192
|
-
* @param {string} preCode
|
|
193
|
-
* @param {LoaderTool} tools 実行環境 (ブラウザ or Node.js) によって外部ファイルの取得・実行方法は異なるため、引数でそれらを行う関数を受け取る。
|
|
194
|
-
* - resolvePath は指定した名前をもつファイルを検索し、正規化されたファイル名を返す関数。返されたファイル名はreadNako3かreadJsの引数になる。
|
|
195
|
-
* - readNako3は指定されたファイルの中身を返す関数。
|
|
196
|
-
* - readJsは指定したファイルをJavaScriptのプログラムとして実行し、`export default` でエクスポートされた値を返す関数。
|
|
197
|
-
* @returns {Promise<unknown> | void}
|
|
198
|
-
* @protected
|
|
199
|
-
*/
|
|
200
|
-
_loadDependencies(code, filename, preCode, tools) {
|
|
201
|
-
const dependencies = {};
|
|
202
|
-
const compiler = new NakoCompiler({ useBasicPlugin: true });
|
|
203
|
-
/**
|
|
204
|
-
* @param {any} item
|
|
205
|
-
* @param {any} tasks
|
|
206
|
-
*/
|
|
207
|
-
const loadJS = (item, tasks) => {
|
|
208
|
-
// jsならプラグインとして読み込む。(ESMでは必ず動的に読む)
|
|
209
|
-
const obj = tools.readJs(item.filePath, item.firstToken);
|
|
210
|
-
tasks.push(obj.task.then((res) => {
|
|
211
|
-
const pluginFuncs = res();
|
|
212
|
-
this.addPluginFromFile(item.filePath, pluginFuncs);
|
|
213
|
-
dependencies[item.filePath].funclist = pluginFuncs;
|
|
214
|
-
dependencies[item.filePath].moduleExport = {};
|
|
215
|
-
dependencies[item.filePath].addPluginFile = () => { this.addPluginFromFile(item.filePath, pluginFuncs); };
|
|
216
|
-
}));
|
|
217
|
-
};
|
|
218
|
-
const loadNako3 = (item, tasks) => {
|
|
219
|
-
// nako3ならファイルを読んでdependenciesに保存する。
|
|
220
|
-
const content = tools.readNako3(item.filePath, item.firstToken);
|
|
221
|
-
const registerFile = (code) => {
|
|
222
|
-
// シンタックスハイライトの高速化のために、事前にファイルが定義する関数名のリストを取り出しておく。
|
|
223
|
-
// preDefineFuncはトークン列に変更を加えるため、事前にクローンしておく。
|
|
224
|
-
// 「プラグイン名設定」を行う (#956)
|
|
225
|
-
const modName = NakoLexer.filenameToModName(item.filePath);
|
|
226
|
-
code = `『${modName}』に名前空間設定;『${modName}』にプラグイン名設定;` + code + ';名前空間ポップ;';
|
|
227
|
-
const tokens = this.rawtokenize(code, 0, item.filePath);
|
|
228
|
-
dependencies[item.filePath].tokens = tokens;
|
|
229
|
-
const funclist = new Map();
|
|
230
|
-
const moduleexport = new Map();
|
|
231
|
-
NakoLexer.preDefineFunc(cloneAsJSON(tokens), this.logger, funclist, moduleexport);
|
|
232
|
-
dependencies[item.filePath].funclist = funclist;
|
|
233
|
-
dependencies[item.filePath].moduleExport = moduleexport;
|
|
234
|
-
// 再帰
|
|
235
|
-
return loadRec(code, item.filePath, '');
|
|
236
|
-
};
|
|
237
|
-
// 取り込み構文における問題を減らすため、必ず非同期でプログラムを読み込む仕様とした #1219
|
|
238
|
-
tasks.push(content.task.then((res) => registerFile(res)));
|
|
239
|
-
};
|
|
240
|
-
const loadRec = (code, filename, preCode) => {
|
|
241
|
-
const tasks = [];
|
|
242
|
-
// 取り込みが必要な情報一覧を調べる(トークン分割して、取り込みタグを得る)
|
|
243
|
-
const tags = NakoCompiler.listRequireStatements(compiler.rawtokenize(code, 0, filename, preCode));
|
|
244
|
-
// パスを解決する
|
|
245
|
-
const tagsResolvePath = tags.map((v) => ({ ...v, ...tools.resolvePath(v.value, v.firstToken, filename) }));
|
|
246
|
-
// 取り込み開始
|
|
247
|
-
for (const item of tagsResolvePath) {
|
|
248
|
-
// 2回目以降の読み込み
|
|
249
|
-
// eslint-disable-next-line no-prototype-builtins
|
|
250
|
-
if (dependencies.hasOwnProperty(item.filePath)) {
|
|
251
|
-
dependencies[item.filePath].alias.add(item.value);
|
|
252
|
-
continue;
|
|
253
|
-
}
|
|
254
|
-
// 初回の読み込み
|
|
255
|
-
dependencies[item.filePath] = { tokens: [], alias: new Set([item.value]), addPluginFile: () => { }, funclist: {}, moduleExport: {} };
|
|
256
|
-
if (item.type === 'js' || item.type === 'mjs') {
|
|
257
|
-
loadJS(item, tasks);
|
|
258
|
-
}
|
|
259
|
-
else if (item.type === 'nako3') {
|
|
260
|
-
loadNako3(item, tasks);
|
|
261
|
-
}
|
|
262
|
-
else {
|
|
263
|
-
throw new NakoImportError(`ファイル『${String(item.value)}』を読み込めません。ファイルが存在しないか未対応の拡張子です。`, item.firstToken.file, item.firstToken.line);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
if (tasks.length > 0) {
|
|
267
|
-
return Promise.all(tasks);
|
|
268
|
-
}
|
|
269
|
-
return undefined;
|
|
270
|
-
};
|
|
271
|
-
try {
|
|
272
|
-
const result = loadRec(code, filename, preCode);
|
|
273
|
-
// 非同期な場合のエラーハンドリング
|
|
274
|
-
if (result !== undefined) {
|
|
275
|
-
result.catch((err) => {
|
|
276
|
-
// 読み込みに失敗したら処理を中断する
|
|
277
|
-
this.logger.error(err.msg);
|
|
278
|
-
this.numFailures++;
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
// すべてが終わってからthis.dependenciesに代入する。そうしないと、「実行」ボタンを連打した場合など、
|
|
282
|
-
// loadDependencies() が並列実行されるときに正しく動作しない。
|
|
283
|
-
this.dependencies = dependencies;
|
|
284
|
-
return result;
|
|
285
|
-
}
|
|
286
|
-
catch (err) {
|
|
287
|
-
// 同期処理では素直に例外を投げる
|
|
288
|
-
this.logger.error(String(err));
|
|
289
|
-
throw err;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
/**
|
|
293
|
-
* コードを単語に分割する
|
|
294
|
-
* @param code なでしこのプログラム
|
|
295
|
-
* @param line なでしこのプログラムの行番号
|
|
296
|
-
* @param filename
|
|
297
|
-
* @param preCode
|
|
298
|
-
* @returns トークンのリスト
|
|
299
|
-
*/
|
|
300
|
-
rawtokenize(code, line, filename, preCode = '') {
|
|
301
|
-
if (!code.startsWith(preCode)) {
|
|
302
|
-
throw new Error('codeの先頭にはpreCodeを含める必要があります。');
|
|
303
|
-
}
|
|
304
|
-
// 名前空間のモジュールリストに自身を追加
|
|
305
|
-
const modName = NakoLexer.filenameToModName(filename);
|
|
306
|
-
const modList = this.getModList();
|
|
307
|
-
if (modList.indexOf(modName) < 0) {
|
|
308
|
-
modList.unshift(modName);
|
|
309
|
-
}
|
|
310
|
-
// 全角半角の統一処理
|
|
311
|
-
const preprocessed = this.prepare.convert(code);
|
|
312
|
-
const tokenizationSourceMapping = new SourceMappingOfTokenization(code.length, preprocessed);
|
|
313
|
-
const indentationSyntaxSourceMapping = new SourceMappingOfIndentSyntax(code, [], []);
|
|
314
|
-
const offsetToLineColumn = new OffsetToLineColumn(code);
|
|
315
|
-
// トークン分割
|
|
316
|
-
let tokens;
|
|
317
|
-
try {
|
|
318
|
-
tokens = this.lexer.tokenize(preprocessed.map((v) => v.text).join(''), line, filename);
|
|
319
|
-
}
|
|
320
|
-
catch (err) {
|
|
321
|
-
if (!(err instanceof InternalLexerError)) {
|
|
322
|
-
throw err;
|
|
323
|
-
}
|
|
324
|
-
// エラー位置をソースコード上の位置に変換して返す
|
|
325
|
-
const dest = indentationSyntaxSourceMapping.map(tokenizationSourceMapping.map(err.preprocessedCodeStartOffset), tokenizationSourceMapping.map(err.preprocessedCodeEndOffset));
|
|
326
|
-
const line = dest.startOffset === null ? err.line : offsetToLineColumn.map(dest.startOffset, false).line;
|
|
327
|
-
const map = subtractSourceMapByPreCodeLength({ ...dest, line }, preCode);
|
|
328
|
-
throw new NakoLexerError(err.msg, map.startOffset, map.endOffset, map.line, filename);
|
|
329
|
-
}
|
|
330
|
-
// DNCL ver2 (core #41)
|
|
331
|
-
tokens = convertDNCL2(tokens);
|
|
332
|
-
// DNCL ver1 (#1140)
|
|
333
|
-
tokens = convertDNCL(tokens);
|
|
334
|
-
// インデント構文を変換 #596
|
|
335
|
-
tokens = convertIndentSyntax(tokens);
|
|
336
|
-
// インラインインデントを変換 #1215
|
|
337
|
-
tokens = convertInlineIndent(tokens);
|
|
338
|
-
// ソースコード上の位置に変換
|
|
339
|
-
tokens = tokens.map((token) => {
|
|
340
|
-
const dest = indentationSyntaxSourceMapping.map(tokenizationSourceMapping.map(token.preprocessedCodeOffset || 0), tokenizationSourceMapping.map((token.preprocessedCodeOffset || 0) + (token.preprocessedCodeLength || 0)));
|
|
341
|
-
let line = token.line;
|
|
342
|
-
let column = 0;
|
|
343
|
-
if (token.type === 'eol' && dest.endOffset !== null) {
|
|
344
|
-
// eolはnako_genで `line = ${eolToken.line};` に変換されるため、
|
|
345
|
-
// 行末のeolのlineは次の行の行数を表す必要がある。
|
|
346
|
-
const out = offsetToLineColumn.map(dest.endOffset, false);
|
|
347
|
-
line = out.line;
|
|
348
|
-
column = out.column;
|
|
349
|
-
}
|
|
350
|
-
else if (dest.startOffset !== null) {
|
|
351
|
-
const out = offsetToLineColumn.map(dest.startOffset, false);
|
|
352
|
-
line = out.line;
|
|
353
|
-
column = out.column;
|
|
354
|
-
}
|
|
355
|
-
return {
|
|
356
|
-
...token,
|
|
357
|
-
...subtractSourceMapByPreCodeLength({ line, column, startOffset: dest.startOffset, endOffset: dest.endOffset }, preCode),
|
|
358
|
-
rawJosi: token.josi
|
|
359
|
-
};
|
|
360
|
-
});
|
|
361
|
-
return tokens;
|
|
362
|
-
}
|
|
363
|
-
/**
|
|
364
|
-
* 単語の属性を構文解析に先立ち補正する
|
|
365
|
-
* @param {Token[]} tokens トークンのリスト
|
|
366
|
-
* @param {boolean} isFirst 最初の呼び出しかどうか
|
|
367
|
-
* @param {string} filename
|
|
368
|
-
* @returns コード (なでしこ)
|
|
369
|
-
*/
|
|
370
|
-
converttoken(tokens, isFirst, filename) {
|
|
371
|
-
const tok = this.lexer.replaceTokens(tokens, isFirst, filename);
|
|
372
|
-
return tok;
|
|
373
|
-
}
|
|
374
|
-
/**
|
|
375
|
-
* 環境のリセット
|
|
376
|
-
* {NakoResetOption|undefined}
|
|
377
|
-
*/
|
|
378
|
-
reset(options = undefined) {
|
|
379
|
-
if (!options || options.needToClearPlugin) {
|
|
380
|
-
// (メモ) #1245
|
|
381
|
-
// 通常、リセット処理では、プラグインの!クリアを呼ぶ。
|
|
382
|
-
// しかし、エディタではクリアイベントを呼ぶと、時計などのコンテンツが止まってしまう
|
|
383
|
-
// そのため、例外的にオプションを指定すると、プラグインのクリアイベントを呼ばない
|
|
384
|
-
try {
|
|
385
|
-
this.clearPlugins();
|
|
386
|
-
}
|
|
387
|
-
catch (_) {
|
|
388
|
-
// pass
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
/**
|
|
392
|
-
* なでしこのローカル変数をスタックで管理
|
|
393
|
-
* __varslist[0] プラグイン領域
|
|
394
|
-
* __varslist[1] なでしこグローバル領域
|
|
395
|
-
* __varslist[2] 最初のローカル変数 ( == __vars }
|
|
396
|
-
*/
|
|
397
|
-
this.__varslist = [this.__varslist[0], this.newVaiables(), this.newVaiables()];
|
|
398
|
-
this.__v0 = this.__varslist[0]; // alias of __varslist[0]
|
|
399
|
-
this.__v1 = this.__varslist[1]; // alias of __varslist[1]
|
|
400
|
-
this.__vars = this.__varslist[2]; // alias of __varslist[2]
|
|
401
|
-
this.__locals = this.newVaiables();
|
|
402
|
-
// プラグイン命令以外を削除する。
|
|
403
|
-
this.funclist = new Map();
|
|
404
|
-
for (const name of this.__v0.keys()) {
|
|
405
|
-
const original = this.pluginFunclist[name]; // record
|
|
406
|
-
if (!original) {
|
|
407
|
-
continue;
|
|
408
|
-
}
|
|
409
|
-
this.funclist.set(name, JSON.parse(JSON.stringify(original)));
|
|
410
|
-
}
|
|
411
|
-
this.lexer.setFuncList(this.funclist);
|
|
412
|
-
this.moduleExport = new Map();
|
|
413
|
-
this.lexer.setModuleExport(this.moduleExport);
|
|
414
|
-
this.logger.clear();
|
|
415
|
-
}
|
|
416
|
-
/**
|
|
417
|
-
* typeがcodeのトークンを単語に分割するための処理
|
|
418
|
-
* @param {string} code
|
|
419
|
-
* @param {number} line
|
|
420
|
-
* @param {string} filename
|
|
421
|
-
* @param {number | null} startOffset
|
|
422
|
-
* @returns
|
|
423
|
-
* @private
|
|
424
|
-
*/
|
|
425
|
-
lexCodeToken(code, line, filename, startOffset) {
|
|
426
|
-
// 単語に分割
|
|
427
|
-
let tokens = this.rawtokenize(code, line, filename, '');
|
|
428
|
-
// 文字列内位置からファイル内位置へ変換
|
|
429
|
-
if (startOffset === null) {
|
|
430
|
-
for (const token of tokens) {
|
|
431
|
-
token.startOffset = undefined;
|
|
432
|
-
token.endOffset = undefined;
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
else {
|
|
436
|
-
for (const token of tokens) {
|
|
437
|
-
if (token.startOffset !== undefined) {
|
|
438
|
-
token.startOffset += startOffset;
|
|
439
|
-
}
|
|
440
|
-
if (token.endOffset !== undefined) {
|
|
441
|
-
token.endOffset += startOffset;
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
// convertTokenで消されるコメントのトークンを残す
|
|
446
|
-
const commentTokens = tokens.filter((t) => t.type === 'line_comment' || t.type === 'range_comment')
|
|
447
|
-
.map((v) => ({ ...v })); // clone
|
|
448
|
-
tokens = this.converttoken(tokens, false, filename);
|
|
449
|
-
return { tokens, commentTokens };
|
|
450
|
-
}
|
|
451
|
-
/**
|
|
452
|
-
* 再帰的にrequire文を置換する。
|
|
453
|
-
* .jsであれば削除し、.nako3であればそのファイルのトークン列で置換する。
|
|
454
|
-
* @param {TokenWithSourceMap[]} tokens
|
|
455
|
-
* @param {Set<string>} [includeGuard]
|
|
456
|
-
* @returns {Token[]} 削除された取り込み文のトークン
|
|
457
|
-
*/
|
|
458
|
-
replaceRequireStatements(tokens, includeGuard = new Set()) {
|
|
459
|
-
/** @type {TokenWithSourceMap[]} */
|
|
460
|
-
const deletedTokens = [];
|
|
461
|
-
for (const r of NakoCompiler.listRequireStatements(tokens).reverse()) {
|
|
462
|
-
// C言語のinclude guardと同じ仕組みで無限ループを防ぐ。
|
|
463
|
-
if (includeGuard.has(r.value)) {
|
|
464
|
-
deletedTokens.push(...tokens.splice((r.start || 0), (r.end || 0) - (r.start || 0)));
|
|
465
|
-
continue;
|
|
466
|
-
}
|
|
467
|
-
const filePath = Object.keys(this.dependencies).find((key) => this.dependencies[key].alias.has(r.value));
|
|
468
|
-
if (filePath === undefined) {
|
|
469
|
-
if (!r.firstToken) {
|
|
470
|
-
throw new Error(`ファイル『${String(r.value)}』が読み込まれていません。`);
|
|
471
|
-
}
|
|
472
|
-
throw new NakoLexerError(`ファイル『${String(r.value)}』が読み込まれていません。`, (r.firstToken).startOffset || 0, (r.firstToken).endOffset || 0, (r.firstToken).line, (r.firstToken).file);
|
|
473
|
-
}
|
|
474
|
-
this.dependencies[filePath].addPluginFile();
|
|
475
|
-
const children = cloneAsJSON(this.dependencies[filePath].tokens);
|
|
476
|
-
includeGuard.add(r.value);
|
|
477
|
-
deletedTokens.push(...this.replaceRequireStatements(children, includeGuard));
|
|
478
|
-
deletedTokens.push(...tokens.splice(r.start || 0, (r.end || 0) - (r.start || 0), ...children));
|
|
479
|
-
}
|
|
480
|
-
return deletedTokens;
|
|
481
|
-
}
|
|
482
|
-
/**
|
|
483
|
-
* replaceRequireStatementsのシンタックスハイライト用の実装。
|
|
484
|
-
* @param {TokenWithSourceMap[]} tokens
|
|
485
|
-
* @returns {TokenWithSourceMap[]} 削除された取り込み文のトークン
|
|
486
|
-
*/
|
|
487
|
-
removeRequireStatements(tokens) {
|
|
488
|
-
/** @type {TokenWithSourceMap[]} */
|
|
489
|
-
const deletedTokens = [];
|
|
490
|
-
for (const r of NakoCompiler.listRequireStatements(tokens).reverse()) {
|
|
491
|
-
// プラグイン命令のシンタックスハイライトのために、addPluginFileを呼んで関数のリストをthis.dependencies[filePath].funclistに保存させる。
|
|
492
|
-
const filePath = Object.keys(this.dependencies).find((key) => this.dependencies[key].alias.has(r.value));
|
|
493
|
-
if (filePath !== undefined) {
|
|
494
|
-
this.dependencies[filePath].addPluginFile();
|
|
495
|
-
}
|
|
496
|
-
// 全ての取り込み文を削除する。そうしないとトークン化に時間がかかりすぎる。
|
|
497
|
-
deletedTokens.push(...tokens.splice(r.start || 0, (r.end || 0) - (r.start || 0)));
|
|
498
|
-
}
|
|
499
|
-
return deletedTokens;
|
|
500
|
-
}
|
|
501
|
-
/** 字句解析を行う */
|
|
502
|
-
lex(code, filename = 'main.nako3', preCode = '', syntaxHighlighting = false) {
|
|
503
|
-
// 単語に分割
|
|
504
|
-
let tokens = this.rawtokenize(code, 0, filename, preCode);
|
|
505
|
-
// require文を再帰的に置換する
|
|
506
|
-
const requireStatementTokens = syntaxHighlighting ? this.removeRequireStatements(tokens) : this.replaceRequireStatements(tokens, undefined);
|
|
507
|
-
for (const t of requireStatementTokens) {
|
|
508
|
-
if (t.type === 'word' || t.type === 'not') {
|
|
509
|
-
t.type = 'require';
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
if (requireStatementTokens.length >= 3) {
|
|
513
|
-
// modList を更新
|
|
514
|
-
for (let i = 0; i < requireStatementTokens.length; i += 3) {
|
|
515
|
-
let modName = requireStatementTokens[i + 1].value;
|
|
516
|
-
modName = NakoLexer.filenameToModName(modName);
|
|
517
|
-
if (this.lexer.modList.indexOf(modName) < 0) {
|
|
518
|
-
this.lexer.modList.push(modName);
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
// convertTokenで消されるコメントのトークンを残す
|
|
523
|
-
const commentTokens = tokens.filter((t) => t.type === 'line_comment' || t.type === 'range_comment')
|
|
524
|
-
.map((v) => ({ ...v })); // clone
|
|
525
|
-
tokens = this.converttoken(tokens, true, filename);
|
|
526
|
-
// 'string_ex'トークンから変換された'code'トークンを字句解析する
|
|
527
|
-
for (let i = 0; i < tokens.length; i++) {
|
|
528
|
-
if (tokens[i] && tokens[i].type === 'code') {
|
|
529
|
-
const children = this.lexCodeToken(tokens[i].value, tokens[i].line, filename, tokens[i].startOffset || 0);
|
|
530
|
-
commentTokens.push(...children.commentTokens);
|
|
531
|
-
tokens.splice(i, 1, ...children.tokens);
|
|
532
|
-
i--;
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
this.logger.trace('--- lex ---\n' + JSON.stringify(tokens, null, 2));
|
|
536
|
-
return { commentTokens, tokens, requireTokens: requireStatementTokens };
|
|
537
|
-
}
|
|
538
|
-
/**
|
|
539
|
-
* コードをパースしてASTにする
|
|
540
|
-
*/
|
|
541
|
-
parse(code, filename, preCode = '') {
|
|
542
|
-
// 関数リストを字句解析と構文解析に登録
|
|
543
|
-
this.lexer.setFuncList(this.funclist);
|
|
544
|
-
this.parser.setFuncList(this.funclist);
|
|
545
|
-
// 関数リストを字句解析と構文解析に登録
|
|
546
|
-
this.lexer.setModuleExport(this.moduleExport);
|
|
547
|
-
this.parser.setModuleExport(this.moduleExport);
|
|
548
|
-
// 字句解析を行う
|
|
549
|
-
const lexerOutput = this.lex(code, filename, preCode);
|
|
550
|
-
// 構文木を作成
|
|
551
|
-
let ast;
|
|
552
|
-
try {
|
|
553
|
-
this.parser.genMode = 'sync'; // set default
|
|
554
|
-
ast = this.parser.parse(lexerOutput.tokens, filename);
|
|
555
|
-
}
|
|
556
|
-
catch (err) {
|
|
557
|
-
if (typeof err.startOffset !== 'number') {
|
|
558
|
-
throw NakoSyntaxError.fromNode(err.message, lexerOutput.tokens[this.parser.getIndex()]);
|
|
559
|
-
}
|
|
560
|
-
throw err;
|
|
561
|
-
}
|
|
562
|
-
// 使用したシステム関数の一覧を this.usedFuns に入れる(エディタなどで利用される)
|
|
563
|
-
this.usedFuncs = this.parser.usedFuncs; // 全ての関数呼び出し
|
|
564
|
-
this.deleteUnNakoFuncs(); // システム関数以外を削除
|
|
565
|
-
this.logger.trace('--- ast ---\n' + JSON.stringify(ast, null, 2));
|
|
566
|
-
return ast;
|
|
567
|
-
}
|
|
568
|
-
getUsedFuncs(ast) {
|
|
569
|
-
this.usedFuncs = new Set();
|
|
570
|
-
this._getUsedFuncs(ast);
|
|
571
|
-
return this.deleteUnNakoFuncs();
|
|
572
|
-
}
|
|
573
|
-
_getUsedFuncs(ast) {
|
|
574
|
-
if (!ast) {
|
|
575
|
-
return;
|
|
576
|
-
}
|
|
577
|
-
if ((ast.type === 'func' || ast.type === 'func_pointer') && ast.name) {
|
|
578
|
-
this.usedFuncs.add(ast.name);
|
|
579
|
-
}
|
|
580
|
-
else if (ast.blocks) { // プロパティにblocksを含んでいる?
|
|
581
|
-
for (const a of ast.blocks) {
|
|
582
|
-
this._getUsedFuncs(a);
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
deleteUnNakoFuncs() {
|
|
587
|
-
for (const func of this.usedFuncs) {
|
|
588
|
-
if (!this.commandlist.has(func)) {
|
|
589
|
-
this.usedFuncs.delete(func);
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
return this.usedFuncs;
|
|
593
|
-
}
|
|
594
|
-
/**
|
|
595
|
-
* プログラムをコンパイルしてランタイム用のJavaScriptのコードを返す
|
|
596
|
-
* @param code コード (なでしこ)
|
|
597
|
-
* @param filename
|
|
598
|
-
* @param isTest テストかどうか
|
|
599
|
-
* @param preCode
|
|
600
|
-
*/
|
|
601
|
-
compile(code, filename, isTest = false, preCode = '') {
|
|
602
|
-
const opt = newCompilerOptions();
|
|
603
|
-
opt.testOnly = isTest;
|
|
604
|
-
opt.preCode = preCode;
|
|
605
|
-
const res = this.compileFromCode(code, filename, opt);
|
|
606
|
-
return res.runtimeEnv;
|
|
607
|
-
}
|
|
608
|
-
/** parse & generate */
|
|
609
|
-
compileFromCode(code, filename, options = undefined) {
|
|
610
|
-
if (filename === '') {
|
|
611
|
-
filename = 'main.nako3';
|
|
612
|
-
}
|
|
613
|
-
if (options === undefined) {
|
|
614
|
-
options = newCompilerOptions();
|
|
615
|
-
}
|
|
616
|
-
try {
|
|
617
|
-
if (options.resetEnv) {
|
|
618
|
-
this.reset();
|
|
619
|
-
}
|
|
620
|
-
if (options.resetAll) {
|
|
621
|
-
this.clearPlugins();
|
|
622
|
-
}
|
|
623
|
-
// onBeforeParse
|
|
624
|
-
this.eventList.filter(o => o.eventName === 'beforeParse').map(e => e.callback(code));
|
|
625
|
-
// parse
|
|
626
|
-
const ast = this.parse(code, filename, options.preCode);
|
|
627
|
-
// onBeforeGenerate
|
|
628
|
-
this.eventList.filter(o => o.eventName === 'beforeGenerate').map(e => e.callback(ast));
|
|
629
|
-
// generate
|
|
630
|
-
const genOptions = new NakoGenOptions(options.testOnly);
|
|
631
|
-
const outCode = this.generateCode(ast, genOptions);
|
|
632
|
-
// onAfterGenerate
|
|
633
|
-
this.eventList.filter(o => o.eventName === 'afterGenerate').map(e => e.callback(outCode));
|
|
634
|
-
return outCode;
|
|
635
|
-
}
|
|
636
|
-
catch (e) {
|
|
637
|
-
this.logger.error(e);
|
|
638
|
-
throw e;
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
/**
|
|
642
|
-
* プログラムをコンパイルしてJavaScriptのコードオブジェクトを返す
|
|
643
|
-
* @param ast
|
|
644
|
-
* @param opt テストかどうか
|
|
645
|
-
* @param mode 一般的に 'sync' を指定
|
|
646
|
-
*/
|
|
647
|
-
generateCode(ast, opt, mode = 'sync') {
|
|
648
|
-
// Select Code Generator #637
|
|
649
|
-
// normal mode
|
|
650
|
-
if (mode === 'sync') {
|
|
651
|
-
return generateJS(this, ast, opt);
|
|
652
|
-
}
|
|
653
|
-
// 廃止の非同期モード #1164
|
|
654
|
-
if (mode === '非同期モード') {
|
|
655
|
-
this.logger.error('『!非同期モード』は廃止されました。[詳細](https://github.com/kujirahand/nadesiko3/issues/1164)');
|
|
656
|
-
}
|
|
657
|
-
// その他のコードジェネレータ(PHPなど)
|
|
658
|
-
const genObj = this.codeGenerateor[mode];
|
|
659
|
-
if (!genObj) {
|
|
660
|
-
throw new Error(`コードジェネレータの「${mode}」はサポートされていません。`);
|
|
661
|
-
}
|
|
662
|
-
return genObj.generate(this, ast, opt.isTest);
|
|
663
|
-
}
|
|
664
|
-
/** コードジェネレータを追加する */
|
|
665
|
-
addCodeGenerator(mode, obj) {
|
|
666
|
-
this.codeGenerateor[mode] = obj;
|
|
667
|
-
}
|
|
668
|
-
/** (非推奨)
|
|
669
|
-
* @param code 非同期で実行する
|
|
670
|
-
* @param fname
|
|
671
|
-
* @param isReset
|
|
672
|
-
* @param isTest テストかどうか。stringの場合は1つのテストのみ。
|
|
673
|
-
* @param [preCode]
|
|
674
|
-
* @deprecated 代わりにrunAsyncメソッドを使ってください。(core #52)
|
|
675
|
-
*/
|
|
676
|
-
async _run(code, fname, isReset, isTest, preCode = '') {
|
|
677
|
-
const opts = newCompilerOptions({
|
|
678
|
-
resetEnv: isReset,
|
|
679
|
-
resetAll: isReset,
|
|
680
|
-
testOnly: isTest,
|
|
681
|
-
preCode
|
|
682
|
-
});
|
|
683
|
-
return this._runEx(code, fname, opts);
|
|
684
|
-
}
|
|
685
|
-
/** 各プラグインをリセットする */
|
|
686
|
-
clearPlugins() {
|
|
687
|
-
// 他に実行している「なでしこ」があればクリアする
|
|
688
|
-
this.__globals.forEach((sys) => {
|
|
689
|
-
if (!sys) {
|
|
690
|
-
return;
|
|
691
|
-
}
|
|
692
|
-
// core #56
|
|
693
|
-
sys.__setSysVar('__forceClose', true);
|
|
694
|
-
sys.reset();
|
|
695
|
-
});
|
|
696
|
-
this.__globals = []; // clear
|
|
697
|
-
}
|
|
698
|
-
/**
|
|
699
|
-
* 環境を指定してJavaScriptのコードを実行する
|
|
700
|
-
* @param code JavaScriptのコード
|
|
701
|
-
* @param nakoGlobal 実行環境
|
|
702
|
-
*/
|
|
703
|
-
evalJS(code, nakoGlobal) {
|
|
704
|
-
this.__globalObj = nakoGlobal; // 現在のnakoGlobalを記録
|
|
705
|
-
this.__globalObj.lastJSCode = code;
|
|
706
|
-
// 実行前に環境を初期化するイベントを実行(beforeRun)
|
|
707
|
-
this.eventList.filter(o => o.eventName === 'beforeRun').map(e => e.callback(nakoGlobal));
|
|
708
|
-
try {
|
|
709
|
-
const f = new Function(nakoGlobal.lastJSCode);
|
|
710
|
-
f.apply(nakoGlobal);
|
|
711
|
-
}
|
|
712
|
-
catch (err) {
|
|
713
|
-
// なでしこコードのエラーは抑止してログにのみ記録
|
|
714
|
-
nakoGlobal.numFailures++;
|
|
715
|
-
this.getLogger().error(err);
|
|
716
|
-
throw err;
|
|
717
|
-
}
|
|
718
|
-
// 実行後に終了イベントを実行(finish)
|
|
719
|
-
this.eventList.filter(o => o.eventName === 'finish').map(e => e.callback(nakoGlobal));
|
|
720
|
-
}
|
|
721
|
-
/**
|
|
722
|
-
* (非推奨) 同期的になでしこのプログラムcodeを実行する
|
|
723
|
-
* @param code なでしこのプログラム
|
|
724
|
-
* @param filename ファイル名
|
|
725
|
-
* @param options オプション
|
|
726
|
-
* @returns 実行に利用したグローバルオブジェクト
|
|
727
|
-
* @deprecated 代わりにrunAsyncメソッドを使ってください。(core #52)
|
|
728
|
-
*/
|
|
729
|
-
runSync(code, filename, options = undefined) {
|
|
730
|
-
// コンパイル
|
|
731
|
-
options = newCompilerOptions(options);
|
|
732
|
-
const out = this.compileFromCode(code, filename, options);
|
|
733
|
-
// 実行前に環境を生成
|
|
734
|
-
const nakoGlobal = this.getNakoGlobal(options, out.gen, filename);
|
|
735
|
-
// 実行
|
|
736
|
-
this.evalJS(out.runtimeEnv, nakoGlobal);
|
|
737
|
-
return nakoGlobal;
|
|
738
|
-
}
|
|
739
|
-
/**
|
|
740
|
-
* 非同期になでしこのプログラムcodeを実行する
|
|
741
|
-
* @param code なでしこのプログラム
|
|
742
|
-
* @param filename ファイル名
|
|
743
|
-
* @param options オプション
|
|
744
|
-
* @returns 実行に利用したグローバルオブジェクト
|
|
745
|
-
*/
|
|
746
|
-
async runAsync(code, filename, options = undefined) {
|
|
747
|
-
// コンパイル
|
|
748
|
-
options = newCompilerOptions(options);
|
|
749
|
-
const compiledCode = this.compileFromCode(code, filename, options);
|
|
750
|
-
// 実行前に環境を生成
|
|
751
|
-
const nakoGlobal = this.getNakoGlobal(options, compiledCode.gen, filename);
|
|
752
|
-
// 実行
|
|
753
|
-
this.evalJS(compiledCode.runtimeEnv, nakoGlobal);
|
|
754
|
-
return nakoGlobal;
|
|
755
|
-
}
|
|
756
|
-
getNakoGlobal(options, gen, filename) {
|
|
757
|
-
// オプションを参照
|
|
758
|
-
let g = options.nakoGlobal;
|
|
759
|
-
if (!g) {
|
|
760
|
-
// 空ならば前回の値を参照(リセットするなら新規で作成する)
|
|
761
|
-
if (this.__globals.length > 0 && options.resetAll === false && options.resetEnv === false) {
|
|
762
|
-
g = this.__globals[this.__globals.length - 1];
|
|
763
|
-
}
|
|
764
|
-
else {
|
|
765
|
-
g = new NakoGlobal(this, gen, (this.__globals.length + 1));
|
|
766
|
-
}
|
|
767
|
-
// 名前空間を設定
|
|
768
|
-
g.__varslist[0].set('名前空間', NakoLexer.filenameToModName(filename));
|
|
769
|
-
}
|
|
770
|
-
if (this.__globals.indexOf(g) < 0) {
|
|
771
|
-
this.__globals.push(g);
|
|
772
|
-
}
|
|
773
|
-
return g;
|
|
774
|
-
}
|
|
775
|
-
/**
|
|
776
|
-
* イベントを登録する
|
|
777
|
-
* @param eventName イベント名
|
|
778
|
-
* @param callback コールバック関数
|
|
779
|
-
*/
|
|
780
|
-
addListener(eventName, callback) {
|
|
781
|
-
this.eventList.push({ eventName, callback });
|
|
782
|
-
}
|
|
783
|
-
/**
|
|
784
|
-
* テストを実行する
|
|
785
|
-
* @param code
|
|
786
|
-
* @param fname
|
|
787
|
-
* @param preCode
|
|
788
|
-
* @param testOnly
|
|
789
|
-
*/
|
|
790
|
-
test(code, fname, preCode = '', testOnly = false) {
|
|
791
|
-
const options = newCompilerOptions();
|
|
792
|
-
options.preCode = preCode;
|
|
793
|
-
options.testOnly = testOnly;
|
|
794
|
-
return this.runSync(code, fname, options);
|
|
795
|
-
}
|
|
796
|
-
/**
|
|
797
|
-
* なでしこのプログラムを実行(他に実行しているインスタンスはそのまま)
|
|
798
|
-
* @param code
|
|
799
|
-
* @param fname
|
|
800
|
-
* @param [preCode]
|
|
801
|
-
* @deprecated 代わりに runAsync を使ってください。
|
|
802
|
-
*/
|
|
803
|
-
run(code, fname = 'main.nako3', preCode = '') {
|
|
804
|
-
const options = newCompilerOptions();
|
|
805
|
-
options.preCode = preCode;
|
|
806
|
-
return this.runSync(code, fname, options);
|
|
807
|
-
}
|
|
808
|
-
/**
|
|
809
|
-
* JavaScriptのみで動くコードを取得する場合
|
|
810
|
-
* @param code
|
|
811
|
-
* @param filename
|
|
812
|
-
* @param opt? オプション
|
|
813
|
-
*/
|
|
814
|
-
compileStandalone(code, filename, options) {
|
|
815
|
-
if (options === undefined) {
|
|
816
|
-
options = new NakoGenOptions();
|
|
817
|
-
}
|
|
818
|
-
const ast = this.parse(code, filename);
|
|
819
|
-
return this.generateCode(ast, options).standalone;
|
|
820
|
-
}
|
|
821
|
-
/**
|
|
822
|
-
* プラグイン・オブジェクトを追加
|
|
823
|
-
* @param po プラグイン・オブジェクト
|
|
824
|
-
* @param persistent falseのとき、次以降の実行では使えない
|
|
825
|
-
* @param fpath ファイルパス
|
|
826
|
-
*/
|
|
827
|
-
addPlugin(po, persistent = true, fpath = '') {
|
|
828
|
-
// __v0を取得
|
|
829
|
-
const __v0 = this.__varslist[0];
|
|
830
|
-
// プラグインのメタ情報をチェック (#1034) (#1647)
|
|
831
|
-
let __pluginInfo = __v0.get('__pluginInfo');
|
|
832
|
-
if (!__pluginInfo) {
|
|
833
|
-
__pluginInfo = {};
|
|
834
|
-
__v0.set('__pluginInfo', __pluginInfo);
|
|
835
|
-
}
|
|
836
|
-
// バージョンチェック
|
|
837
|
-
let intVersion = 0;
|
|
838
|
-
let pluginName = 'unknown';
|
|
839
|
-
let metaValue = { pluginName: 'unknown', nakoVersionResult: true, nakoVersion: '0.0.0', path: '' };
|
|
840
|
-
if (po.meta) {
|
|
841
|
-
if (po.meta.value && typeof (po.meta) === 'object') {
|
|
842
|
-
const meta = po.meta;
|
|
843
|
-
metaValue = meta.value || { pluginName: 'unknown', nakoVersion: '0.0.0' };
|
|
844
|
-
pluginName = metaValue.pluginName || 'unknown';
|
|
845
|
-
// version check
|
|
846
|
-
const nakoVersion = (metaValue.nakoVersion || '0.0.0') + '.0.0';
|
|
847
|
-
const versions = nakoVersion.split('.').map((v) => parseInt(v));
|
|
848
|
-
intVersion = versions[1] * 100 + versions[2];
|
|
849
|
-
// fpath
|
|
850
|
-
metaValue.path = fpath;
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
// unknown の場合は、関数名からプラグイン名を自動生成する
|
|
854
|
-
if (pluginName === 'unknown') {
|
|
855
|
-
pluginName = Object.keys(po).join('-');
|
|
856
|
-
}
|
|
857
|
-
// プラグイン名の重複を確認
|
|
858
|
-
if (__pluginInfo[pluginName] !== undefined) {
|
|
859
|
-
// プラグイン名が重複した場合はプラグインとして登録しない
|
|
860
|
-
return;
|
|
861
|
-
}
|
|
862
|
-
// Windowsのパスやファイル名に使えない文字列があると、JSファイル書き出しでエラーになるので置換
|
|
863
|
-
const removeInvalidFilenameChars = (str) => {
|
|
864
|
-
return str.replace(/[^a-zA-z0-9\-_\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FAF\u3400-\u4DBF\uF900-\uFAFF]/g, '_');
|
|
865
|
-
};
|
|
866
|
-
pluginName = removeInvalidFilenameChars(pluginName);
|
|
867
|
-
// プラグイン情報を記録
|
|
868
|
-
__pluginInfo[pluginName] = metaValue;
|
|
869
|
-
// バージョンチェック
|
|
870
|
-
if (PLUGIN_MIN_VERSION_INT > intVersion) {
|
|
871
|
-
const keyStr = Object.keys(po).join(',');
|
|
872
|
-
if (pluginName === 'unknown') {
|
|
873
|
-
pluginName = keyStr.substring(0, 30) + '...';
|
|
874
|
-
}
|
|
875
|
-
if (pluginName !== '') {
|
|
876
|
-
const errMsg = `なでしこプラグイン『${pluginName}』は古い形式なので正しく動作しない可能性があります。` +
|
|
877
|
-
`(ランタイムの要求: ${PLUGIN_MIN_VERSION_INT}/プラグイン: ${intVersion})`;
|
|
878
|
-
console.warn(errMsg, 'see', 'https://github.com/kujirahand/nadesiko3/issues/1647');
|
|
879
|
-
this.logger.warn(errMsg);
|
|
880
|
-
metaValue.nakoVersionResult = false;
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
// 初期化とクリアを変換する
|
|
884
|
-
this.__module[pluginName] = po;
|
|
885
|
-
this.pluginfiles[pluginName] = '*';
|
|
886
|
-
// `初期化`と`クリア`をチェック
|
|
887
|
-
if (typeof (po['初期化']) === 'object') {
|
|
888
|
-
const def = po['初期化'];
|
|
889
|
-
delete po['初期化'];
|
|
890
|
-
const initKey = `!${pluginName}:初期化`;
|
|
891
|
-
po[initKey] = def;
|
|
892
|
-
}
|
|
893
|
-
// プラグインの値を、なでしこシステム変数(Map)にコピー
|
|
894
|
-
for (const key in po) {
|
|
895
|
-
const v = po[key];
|
|
896
|
-
this.funclist.set(key, v);
|
|
897
|
-
if (persistent) {
|
|
898
|
-
this.pluginFunclist[key] = JSON.parse(JSON.stringify(v));
|
|
899
|
-
}
|
|
900
|
-
if (v.type === 'func') {
|
|
901
|
-
__v0.set(key, v.fn);
|
|
902
|
-
if (v.asyncFn) { // asyncFn を正しく実行するために pure に変更する (core#142)
|
|
903
|
-
v.pure = true;
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
else if (v.type === 'const' || v.type === 'var') {
|
|
907
|
-
// メタ情報としての const | var は現在利用していない
|
|
908
|
-
// meta[key] = { readonly: v.type === 'const' }
|
|
909
|
-
__v0.set(key, v.value);
|
|
910
|
-
}
|
|
911
|
-
else {
|
|
912
|
-
console.error('[プラグイン追加エラー]', v);
|
|
913
|
-
throw new Error('プラグインの追加でエラー。');
|
|
914
|
-
}
|
|
915
|
-
// コマンドを登録するか?
|
|
916
|
-
if (key === '初期化' || key.substring(0, 1) === '!') { // 登録しない関数名
|
|
917
|
-
continue;
|
|
918
|
-
}
|
|
919
|
-
this.commandlist.add(key);
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
/**
|
|
923
|
-
* プラグイン・オブジェクトを追加(ブラウザ向け)
|
|
924
|
-
* @param objName オブジェクト名 (今後プラグイン名は、meta.value.pluginNameに指定する)
|
|
925
|
-
* @param po 関数リスト
|
|
926
|
-
* @param persistent falseのとき、次以降の実行では使えない
|
|
927
|
-
*/
|
|
928
|
-
addPluginObject(objName, po, persistent = true) {
|
|
929
|
-
// metaプロパティがなければ互換性のため適当に追加
|
|
930
|
-
if (po.meta === undefined) {
|
|
931
|
-
po.meta = { type: 'const', value: { pluginName: objName, nakoVersion: '0.0.0' } };
|
|
932
|
-
}
|
|
933
|
-
this.addPlugin(po, persistent);
|
|
934
|
-
}
|
|
935
|
-
/**
|
|
936
|
-
* プラグイン・ファイルを追加(Node.js向け)
|
|
937
|
-
* @param objName オブジェクト名(ただし、v3.6.3以降のバージョンでは無効になる)
|
|
938
|
-
* @param fpath ファイルパス
|
|
939
|
-
* @param po 登録するオブジェクト
|
|
940
|
-
* @param persistent falseのとき、次以降の実行では使えない
|
|
941
|
-
* @deprecated 利用は非推奨
|
|
942
|
-
*/
|
|
943
|
-
addPluginFile(_objName, fpath, po, persistent = true) {
|
|
944
|
-
this.addPluginFromFile(fpath, po, persistent);
|
|
945
|
-
}
|
|
946
|
-
/**
|
|
947
|
-
* プラグイン・ファイルを追加(Node.js向け)
|
|
948
|
-
* @param fpath ファイルパス
|
|
949
|
-
* @param po 登録するオブジェクト
|
|
950
|
-
* @param persistent falseのとき、次以降の実行では使えない
|
|
951
|
-
*/
|
|
952
|
-
addPluginFromFile(fpath, po, persistent = true) {
|
|
953
|
-
this.addPlugin(po, persistent, fpath);
|
|
954
|
-
}
|
|
955
|
-
/**
|
|
956
|
-
* 関数を追加する
|
|
957
|
-
* @param {string} key 関数名
|
|
958
|
-
* @param {string[][]} josi 助詞
|
|
959
|
-
* @param {Function} fn 関数
|
|
960
|
-
* @param {boolean} returnNone 値を返す関数の場合はfalseを指定
|
|
961
|
-
* @param {boolean} asyncFn Promiseを返す関数かを指定
|
|
962
|
-
*/
|
|
963
|
-
addFunc(key, josi, fn, returnNone = true, asyncFn = false) {
|
|
964
|
-
const funcObj = { josi, fn, type: 'func', return_none: returnNone, asyncFn, pure: true };
|
|
965
|
-
this.funclist.set(key, funcObj);
|
|
966
|
-
this.pluginFunclist[key] = cloneAsJSON(funcObj);
|
|
967
|
-
this.__varslist[0].set(key, fn);
|
|
968
|
-
}
|
|
969
|
-
/** (非推奨) 互換性のため ... 関数を追加する
|
|
970
|
-
* @deprecated 代わりにaddFuncを使ってください
|
|
971
|
-
*/
|
|
972
|
-
setFunc(key, josi, fn, returnNone = true, asyncFn = false) {
|
|
973
|
-
this.addFunc(key, josi, fn, returnNone, asyncFn);
|
|
974
|
-
}
|
|
975
|
-
/**
|
|
976
|
-
* プラグイン関数を参照する
|
|
977
|
-
* @param key プラグイン関数の関数名
|
|
978
|
-
* @returns プラグイン・オブジェクト
|
|
979
|
-
*/
|
|
980
|
-
getFunc(key) {
|
|
981
|
-
return this.funclist.get(key);
|
|
982
|
-
}
|
|
983
|
-
/** 同期的になでしこのプログラムcodeを実行する
|
|
984
|
-
* @deprecated 代わりにrunAsyncメソッドを使ってください。(core #52)
|
|
985
|
-
*/
|
|
986
|
-
_runEx(code, filename, opts, preCode = '', nakoGlobal = undefined) {
|
|
987
|
-
// コンパイル
|
|
988
|
-
opts.preCode = preCode;
|
|
989
|
-
if (nakoGlobal) {
|
|
990
|
-
opts.nakoGlobal = nakoGlobal;
|
|
991
|
-
}
|
|
992
|
-
return this.runSync(code, filename, opts);
|
|
993
|
-
}
|
|
994
|
-
/** (非推奨) 同期的になでしこのプログラムcodeを実行する
|
|
995
|
-
* @param code
|
|
996
|
-
* @param fname
|
|
997
|
-
* @param opts
|
|
998
|
-
* @param [preCode]
|
|
999
|
-
* @deprecated 代わりにrunAsyncメソッドを使ってください。(core #52)
|
|
1000
|
-
*/
|
|
1001
|
-
runEx(code, fname, opts, preCode = '') {
|
|
1002
|
-
return this._runEx(code, fname, opts, preCode);
|
|
1003
|
-
}
|
|
1004
|
-
/**
|
|
1005
|
-
* (非推奨) なでしこのプログラムを実行(他に実行しているインスタンスもリセットする)
|
|
1006
|
-
* @param code
|
|
1007
|
-
* @param fname
|
|
1008
|
-
* @param [preCode]
|
|
1009
|
-
*/
|
|
1010
|
-
async runReset(code, fname = 'main.nako3', preCode = '') {
|
|
1011
|
-
const opts = newCompilerOptions({ resetAll: true, resetEnv: true, preCode });
|
|
1012
|
-
return this.runAsync(code, fname, opts);
|
|
1013
|
-
}
|
|
1014
|
-
/**
|
|
1015
|
-
* 新規のなでしこ変数管理オブジェクトを生成
|
|
1016
|
-
* @returns 変数管理オブジェクト
|
|
1017
|
-
*/
|
|
1018
|
-
newVaiables(initVars) {
|
|
1019
|
-
return new Map(initVars);
|
|
1020
|
-
}
|
|
1021
|
-
}
|