nadesiko3 3.2.27
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/LICENSE +21 -0
- package/README.md +107 -0
- package/bin/cnako3 +10 -0
- package/bin/cnako3.bat +11 -0
- package/bin/nako3server.bat +6 -0
- package/demo/ace_editor.html +90 -0
- package/demo/ace_editor_tabs.html +162 -0
- package/demo/basic.html +71 -0
- package/demo/browsers.html +128 -0
- package/demo/css/basic.css +3 -0
- package/demo/css/common.css +157 -0
- package/demo/css/editor.css +8 -0
- package/demo/css/flow.css +3 -0
- package/demo/css/index.css +3 -0
- package/demo/extlib/ace@1.4.12/ace.js +17 -0
- package/demo/extlib/ace@1.4.12/ext-code_lens.min.js +1 -0
- package/demo/extlib/ace@1.4.12/ext-language_tools.min.js +1 -0
- package/demo/extlib/ace@1.4.12/ext-options.min.js +1 -0
- package/demo/extlib/ace@1.4.12/ext-settings_menu.js +8 -0
- package/demo/extlib/ace@1.4.12/keybinding-vscode.js +8 -0
- package/demo/extlib/ace@1.4.12/theme-monokai.js +8 -0
- package/demo/extlib/ace@1.4.12/theme-xcode.js +8 -0
- package/demo/extlib/chart.js@3.2.1/chart.min.js +13 -0
- package/demo/extlib/pure-min.css +11 -0
- package/demo/flow.html +97 -0
- package/demo/graph.html +53 -0
- package/demo/image/nako.png +0 -0
- package/demo/image/nakopad-icon256.png +0 -0
- package/demo/image/turtle.fla +0 -0
- package/demo/image/turtle.png +0 -0
- package/demo/index.html +134 -0
- package/demo/js/common.js +17 -0
- package/demo/js/turtle3d_test.js +44 -0
- package/demo/js/turtle_test.js +44 -0
- package/demo/runscript.html +47 -0
- package/demo/runscript2.html +33 -0
- package/demo/turtle.html +58 -0
- package/demo/turtle2.html +141 -0
- package/demo/turtle3.html +278 -0
- package/demo/turtle3d.html +58 -0
- package/demo/turtle3d2.html +107 -0
- package/demo/version.html +24 -0
- package/doc/SETUP.md +172 -0
- package/doc/about.md +34 -0
- package/doc/browsers.md +60 -0
- package/doc/docgen.md +21 -0
- package/doc/editor.md +44 -0
- package/doc/files.md +37 -0
- package/doc/plugins.md +195 -0
- package/doc/release.md +78 -0
- package/doc/win32.md +57 -0
- package/package.json +196 -0
- package/release/_hash.txt +65 -0
- package/release/_script-tags.txt +13 -0
- package/release/command.json +1 -0
- package/release/command.json.js +1 -0
- package/release/command_cnako3.json +1 -0
- package/release/command_list.json +1 -0
- package/release/editor.js +2 -0
- package/release/editor.js.LICENSE.txt +32 -0
- package/release/josi.json +48 -0
- package/release/nako_gen_async.js +1 -0
- package/release/nako_gen_async.js.LICENSE.txt +595 -0
- package/release/plugin_caniuse.js +1 -0
- package/release/plugin_caniuse.js.LICENSE.txt +411 -0
- package/release/plugin_csv.js +1 -0
- package/release/plugin_csv.js.LICENSE.txt +367 -0
- package/release/plugin_datetime.js +1 -0
- package/release/plugin_datetime.js.LICENSE.txt +471 -0
- package/release/plugin_kansuji.js +1 -0
- package/release/plugin_kansuji.js.LICENSE.txt +491 -0
- package/release/plugin_markup.js +1 -0
- package/release/plugin_markup.js.LICENSE.txt +363 -0
- package/release/plugin_turtle.js +1 -0
- package/release/plugin_turtle.js.LICENSE.txt +435 -0
- package/release/plugin_webworker.js +1 -0
- package/release/plugin_webworker.js.LICENSE.txt +491 -0
- package/release/version.js +2 -0
- package/release/version.js.LICENSE.txt +32 -0
- package/release/wnako3.js +2 -0
- package/release/wnako3.js.LICENSE.txt +1 -0
- package/release/wnako3webworker.js +1 -0
- package/release/wnako3webworker.js.LICENSE.txt +847 -0
- package/release/yoyakugo.json +30 -0
- package/src/browsers.md +60 -0
- package/src/cnako3.js +466 -0
- package/src/commander_ja.js +154 -0
- package/src/enako3.js +69 -0
- package/src/era.json +22 -0
- package/src/index.js +5 -0
- package/src/nako3.js +836 -0
- package/src/nako3_assert.js +37 -0
- package/src/nako3editorfix.sfd +106 -0
- package/src/nako3editorfix.woff +0 -0
- package/src/nako3server.js +51 -0
- package/src/nako_colors.js +86 -0
- package/src/nako_errors.js +176 -0
- package/src/nako_gen.js +1459 -0
- package/src/nako_gen_async.js +1622 -0
- package/src/nako_global.js +113 -0
- package/src/nako_indent.js +480 -0
- package/src/nako_josi_list.js +46 -0
- package/src/nako_lex_rules.js +259 -0
- package/src/nako_lexer.js +576 -0
- package/src/nako_logger.js +138 -0
- package/src/nako_parser3.js +1768 -0
- package/src/nako_parser_base.js +265 -0
- package/src/nako_parser_const.js +37 -0
- package/src/nako_prepare.js +293 -0
- package/src/nako_reserved_words.js +35 -0
- package/src/nako_source_mapping.js +251 -0
- package/src/nako_test.js +37 -0
- package/src/nako_version.js +8 -0
- package/src/plugin_browser.js +191 -0
- package/src/plugin_browser_ajax.js +352 -0
- package/src/plugin_browser_audio.js +109 -0
- package/src/plugin_browser_canvas.js +462 -0
- package/src/plugin_browser_chart.js +296 -0
- package/src/plugin_browser_color.js +49 -0
- package/src/plugin_browser_crypto.js +26 -0
- package/src/plugin_browser_dialog.js +53 -0
- package/src/plugin_browser_dom_basic.js +322 -0
- package/src/plugin_browser_dom_event.js +193 -0
- package/src/plugin_browser_dom_parts.js +163 -0
- package/src/plugin_browser_geolocation.js +51 -0
- package/src/plugin_browser_hotkey.js +25 -0
- package/src/plugin_browser_html.js +59 -0
- package/src/plugin_browser_in_worker.js +44 -0
- package/src/plugin_browser_location.js +21 -0
- package/src/plugin_browser_speech.js +111 -0
- package/src/plugin_browser_storage.js +121 -0
- package/src/plugin_browser_system.js +12 -0
- package/src/plugin_browser_websocket.js +73 -0
- package/src/plugin_caniuse.js +24 -0
- package/src/plugin_csv.js +57 -0
- package/src/plugin_datetime.js +414 -0
- package/src/plugin_express.js +212 -0
- package/src/plugin_kansuji.js +224 -0
- package/src/plugin_keigo.js +55 -0
- package/src/plugin_markup.js +32 -0
- package/src/plugin_math.js +319 -0
- package/src/plugin_node.js +1018 -0
- package/src/plugin_promise.js +94 -0
- package/src/plugin_system.js +2109 -0
- package/src/plugin_test.js +38 -0
- package/src/plugin_turtle.js +646 -0
- package/src/plugin_webworker.js +334 -0
- package/src/plugin_weykturtle3d.js +1216 -0
- package/src/plugin_worker.js +92 -0
- package/src/repl.nako3 +63 -0
- package/src/turtle-elephant.png +0 -0
- package/src/turtle-panda.png +0 -0
- package/src/turtle64.png +0 -0
- package/src/wnako3.js +162 -0
- package/src/wnako3_editor.css +215 -0
- package/src/wnako3_editor.js +1645 -0
- package/src/wnako3_editor_marker_red.png +0 -0
- package/src/wnako3_editor_marker_red.xcf +0 -0
- package/src/wnako3_editor_marker_yellow.png +0 -0
- package/src/wnako3_editor_marker_yellow.xcf +0 -0
- package/src/wnako3webworker.js +70 -0
- package/test/ace_editor/karma.config.js +94 -0
- package/test/ace_editor/test/.babelrc.json +3 -0
- package/test/ace_editor/test/ace_editor_test.js +178 -0
- package/test/ace_editor/test/html/custom_context.html +140 -0
- package/test/async/async_basic_test.js +124 -0
- package/test/browser/karma.config.js +212 -0
- package/test/browser/test/.babelrc.json +3 -0
- package/test/browser/test/compare_util.js +50 -0
- package/test/browser/test/html/canvas_basic.html +1 -0
- package/test/browser/test/html/div_basic.html +2 -0
- package/test/browser/test/html/event_dom_form.html +4 -0
- package/test/browser/test/html/event_dom_scrolldiv.html +5 -0
- package/test/browser/test/image/canvas_test1.png +0 -0
- package/test/browser/test/image/canvas_test2.png +0 -0
- package/test/browser/test/image/canvas_test3.png +0 -0
- package/test/browser/test/image/canvas_test4.png +0 -0
- package/test/browser/test/image/canvas_test7.png +0 -0
- package/test/browser/test/image/canvas_test8.png +0 -0
- package/test/browser/test/image/canvas_test_blank.png +0 -0
- package/test/browser/test/image/elephant_kana.png +0 -0
- package/test/browser/test/image/panda_kana.png +0 -0
- package/test/browser/test/image/turtle_kana.png +0 -0
- package/test/browser/test/import_plugin_checker.js +24 -0
- package/test/browser/test/plugin_browser_test.js +52 -0
- package/test/browser/test/plugin_browser_test_ajax.js +123 -0
- package/test/browser/test/plugin_browser_test_color.js +18 -0
- package/test/browser/test/plugin_browser_test_dialog.js +72 -0
- package/test/browser/test/plugin_browser_test_dom_event.js +598 -0
- package/test/browser/test/plugin_browser_test_dom_parts.js +125 -0
- package/test/browser/test/plugin_browser_test_system.js +9 -0
- package/test/browser/test/plugin_turtle_test.js +817 -0
- package/test/browser/test/plugin_webworker_test.js +87 -0
- package/test/browser/test/require_test.js +71 -0
- package/test/bundled/karma.config.base.js +117 -0
- package/test/bundled/karma.config.js +86 -0
- package/test/bundled/test/.babelrc.json +3 -0
- package/test/bundled/test/bundled_test.js +69 -0
- package/test/bundled/test/html/custom_context.html +65 -0
- package/test/bundled/test/html/custom_debug.html +66 -0
- package/test/bundled/test4b.cmd +52 -0
- package/test/bundled/test_base/.babelrc.json +3 -0
- package/test/bundled/test_base/_checktool_test.js +25 -0
- package/test/bundled/test_base/basic_ajax_test.js +56 -0
- package/test/bundled/test_base/basic_async_test.js +18 -0
- package/test/bundled/test_base/basic_test.js +153 -0
- package/test/bundled/test_base/calc_test.js +132 -0
- package/test/bundled/test_base/css/browsers_box.css +114 -0
- package/test/bundled/test_base/html/custom_context.html +69 -0
- package/test/bundled/test_base/html/custom_debug.html +71 -0
- package/test/bundled/test_base/js/browsers_box.js +72 -0
- package/test/bundled/test_base/plugin_csv_test.js +37 -0
- package/test/bundled/test_base/plugin_datetime_test.js +115 -0
- package/test/bundled/test_base/plugin_kansuji_test.js +49 -0
- package/test/bundled/test_base/plugin_system_test.js +410 -0
- package/test/bundled/test_base/plugin_webworker_test.js +53 -0
- package/test/bundled/test_base/resources/ok.txt +1 -0
- package/test/bundled/test_base/test_utils.js +191 -0
- package/test/common/array_test.js +40 -0
- package/test/common/basic_test.js +317 -0
- package/test/common/calc_test.js +139 -0
- package/test/common/debug_test.js +16 -0
- package/test/common/error_test.js +16 -0
- package/test/common/flow_test.js +360 -0
- package/test/common/func_call.js +136 -0
- package/test/common/func_test.js +149 -0
- package/test/common/indent_test.js +362 -0
- package/test/common/lex_test.js +146 -0
- package/test/common/literal_test.js +72 -0
- package/test/common/nako_logger_test.js +26 -0
- package/test/common/plugin_browser_test.js +24 -0
- package/test/common/plugin_browser_ut_audio_test.js +88 -0
- package/test/common/plugin_browser_ut_color_test.js +21 -0
- package/test/common/plugin_browser_ut_dialog_test.js +100 -0
- package/test/common/plugin_browser_ut_html_test.js +13 -0
- package/test/common/plugin_browser_ut_system_test.js +10 -0
- package/test/common/plugin_csv_test.js +39 -0
- package/test/common/plugin_datetime_test.js +120 -0
- package/test/common/plugin_kansuji_test.js +59 -0
- package/test/common/plugin_promise_test.js +18 -0
- package/test/common/plugin_system_test.js +451 -0
- package/test/common/prepare_test.js +93 -0
- package/test/common/re_test.js +20 -0
- package/test/common/variable_scope_test.js +105 -0
- package/test/jsconfig.json +19 -0
- package/test/karma.config.js +91 -0
- package/test/node/add_test.nako3 +1 -0
- package/test/node/async_test.js +80 -0
- package/test/node/commander_ja_test.js +82 -0
- package/test/node/error_message_test.js +244 -0
- package/test/node/kai_test.nako3 +6 -0
- package/test/node/node_test.js +43 -0
- package/test/node/plugin_broken.js.txt +3 -0
- package/test/node/plugin_browser_ut_ajax_test.js +355 -0
- package/test/node/plugin_browser_ut_location_test.js +32 -0
- package/test/node/plugin_markup_test.js +44 -0
- package/test/node/plugin_math_test.js +42 -0
- package/test/node/plugin_node_test.js +93 -0
- package/test/node/plugin_test.js +16 -0
- package/test/node/relative_import_test_1.nako3 +1 -0
- package/test/node/relative_import_test_2.nako3 +2 -0
- package/test/node/require_nako3_test.js +59 -0
- package/test/node/requiretest.nako3 +4 -0
- package/test/node/requiretest_indirect.nako3 +1 -0
- package/test/node/requiretest_name.nako3 +5 -0
- package/test/node/runtime_error.nako3 +2 -0
- package/test/node/side_effects_test.js +106 -0
- package/test/node/sjis.txt +5 -0
- package/test/node/syntax_error.nako3 +2 -0
- package/test/node/wnako3_editor_test.js +360 -0
- package/tools/README.md +7 -0
- package/tools/nako3edit/html/edit.html +83 -0
- package/tools/nako3edit/html/edit_plugin.js +6 -0
- package/tools/nako3edit/html/files.html +49 -0
- package/tools/nako3edit/html/nako3edit.css +66 -0
- package/tools/nako3edit/index.nako3 +145 -0
- package/tools/nako3edit/run.js +12 -0
- package/tools/nako3server/html/edit.html +104 -0
- package/tools/nako3server/html/edit_plugin.js +6 -0
- package/tools/nako3server/html/files.html +53 -0
- package/tools/nako3server/html/nako3edit.css +66 -0
- package/tools/nako3server/index.nako3 +129 -0
- package/tools/nako3server/run.js +12 -0
package/src/nako3.js
ADDED
|
@@ -0,0 +1,836 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* nadesiko v3
|
|
4
|
+
*/
|
|
5
|
+
const Parser = require('./nako_parser3')
|
|
6
|
+
const NakoLexer = require('./nako_lexer')
|
|
7
|
+
const Prepare = require('./nako_prepare')
|
|
8
|
+
const NakoGenSync = require('./nako_gen')
|
|
9
|
+
const NakoIndent = require('./nako_indent')
|
|
10
|
+
const PluginSystem = require('./plugin_system')
|
|
11
|
+
const PluginMath = require('./plugin_math')
|
|
12
|
+
const PluginPromise = require('./plugin_promise')
|
|
13
|
+
const PluginTest = require('./plugin_test')
|
|
14
|
+
const { SourceMappingOfTokenization, SourceMappingOfIndentSyntax, OffsetToLineColumn, subtractSourceMapByPreCodeLength } = require('./nako_source_mapping')
|
|
15
|
+
const { NakoRuntimeError, NakoLexerError, NakoImportError, NakoSyntaxError, InternalLexerError } = require('./nako_errors')
|
|
16
|
+
const NakoLogger = require('./nako_logger')
|
|
17
|
+
const NakoGlobal = require('./nako_global')
|
|
18
|
+
|
|
19
|
+
/** @type {<T>(x: T) => T} */
|
|
20
|
+
const cloneAsJSON = (x) => JSON.parse(JSON.stringify(x))
|
|
21
|
+
|
|
22
|
+
// Select Code Generator #637
|
|
23
|
+
/**
|
|
24
|
+
* @type {Object}
|
|
25
|
+
*/
|
|
26
|
+
const codeGenerators = {
|
|
27
|
+
// [key: String]
|
|
28
|
+
sync: NakoGenSync
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {String | undefined} mode
|
|
33
|
+
*/
|
|
34
|
+
function NakoGen (mode) {
|
|
35
|
+
if (codeGenerators[mode]) { return codeGenerators[mode] }
|
|
36
|
+
throw new Error(`コードジェネレータの「${mode}」はサポートされていません。`)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @typedef {{
|
|
41
|
+
* type: string
|
|
42
|
+
* value: any
|
|
43
|
+
* line: number
|
|
44
|
+
* column: number
|
|
45
|
+
* file: string
|
|
46
|
+
* josi: string
|
|
47
|
+
* meta?: any
|
|
48
|
+
* rawJosi: string
|
|
49
|
+
* startOffset: number | null
|
|
50
|
+
* endOffset: number | null
|
|
51
|
+
* isDefinition?: boolean
|
|
52
|
+
* }} TokenWithSourceMap
|
|
53
|
+
*
|
|
54
|
+
* @typedef {{
|
|
55
|
+
* resetEnv: boolean
|
|
56
|
+
* testOnly: boolean | string
|
|
57
|
+
* resetAll: boolean
|
|
58
|
+
* }} CompilerOptions
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 一部のプロパティのみ。
|
|
63
|
+
* @typedef {{
|
|
64
|
+
* type: string
|
|
65
|
+
* cond?: TokenWithSourceMap | Ast
|
|
66
|
+
* block?: (TokenWithSourceMap | Ast)[] | TokenWithSourceMap | Ast
|
|
67
|
+
* false_block?: TokenWithSourceMap | Ast
|
|
68
|
+
* name?: TokenWithSourceMap | Ast
|
|
69
|
+
* josi?: string
|
|
70
|
+
* value?: unknown
|
|
71
|
+
* line?: number
|
|
72
|
+
* column?: number
|
|
73
|
+
* file?: string
|
|
74
|
+
* startOffset: number | null
|
|
75
|
+
* endOffset: number | null
|
|
76
|
+
* rawJosi?: string
|
|
77
|
+
* vartype?: string
|
|
78
|
+
* index?: Ast[]
|
|
79
|
+
* end?: {
|
|
80
|
+
* startOffset: number | null
|
|
81
|
+
* endOffset: number | null
|
|
82
|
+
* line?: number
|
|
83
|
+
* column?: number
|
|
84
|
+
* }
|
|
85
|
+
* genMode?: string
|
|
86
|
+
* }} Ast
|
|
87
|
+
*
|
|
88
|
+
* @typedef {(
|
|
89
|
+
* | { type: 'func', josi: string[][], pure?: boolean, fn?: Function, return_none: boolean }
|
|
90
|
+
* | { type: 'var' | 'const', value: any}
|
|
91
|
+
* )} NakoFunction
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
class NakoCompiler {
|
|
95
|
+
/**
|
|
96
|
+
* @param {undefined | {'useBasicPlugin':true|false}} options
|
|
97
|
+
*/
|
|
98
|
+
constructor (options) {
|
|
99
|
+
if (options === undefined) {
|
|
100
|
+
options = { useBasicPlugin: true }
|
|
101
|
+
}
|
|
102
|
+
this.silent = true
|
|
103
|
+
this.filename = 'inline'
|
|
104
|
+
this.options = options
|
|
105
|
+
// 環境のリセット
|
|
106
|
+
/** @type {Record<string, any>[]} */
|
|
107
|
+
this.__varslist = [{}, {}, {}] // このオブジェクトは変更しないこと (this.gen.__varslist と共有する)
|
|
108
|
+
this.__locals = {} // ローカル変数
|
|
109
|
+
this.__self = this
|
|
110
|
+
this.__vars = this.__varslist[2]
|
|
111
|
+
/**
|
|
112
|
+
* @type {NakoGlobal[]}
|
|
113
|
+
*/
|
|
114
|
+
this.__globals = [] // 生成した NakoGlobalのインスタンスを保持
|
|
115
|
+
/** @type {Record<string, Record<string, NakoFunction>>} */
|
|
116
|
+
this.__module = {} // requireなどで取り込んだモジュールの一覧
|
|
117
|
+
/** @type {Record<string, NakoFunction>} */
|
|
118
|
+
this.pluginFunclist = {} // プラグインで定義された関数
|
|
119
|
+
/** @type {Record<string, NakoFunction>} */
|
|
120
|
+
this.funclist = {} // プラグインで定義された関数 + ユーザーが定義した関数
|
|
121
|
+
this.pluginfiles = {} // 取り込んだファイル一覧
|
|
122
|
+
this.isSetter = false // 代入的関数呼び出しを管理(#290)
|
|
123
|
+
this.commandlist = new Set() // プラグインで定義された定数・変数・関数の名前
|
|
124
|
+
/** @type {Record<string, { josi: string[][], fn: string, type: 'func' }>} */
|
|
125
|
+
this.nako_func = {} // __v1に配置するJavaScriptのコードで定義された関数
|
|
126
|
+
|
|
127
|
+
this.logger = new NakoLogger()
|
|
128
|
+
|
|
129
|
+
// 必要なオブジェクトを覚えておく
|
|
130
|
+
this.prepare = new Prepare(this.logger)
|
|
131
|
+
this.parser = new Parser(this.logger)
|
|
132
|
+
this.lexer = new NakoLexer(this.logger)
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* 取り込み文を置換するためのオブジェクト。
|
|
136
|
+
* 正規化されたファイル名がキーになり、取り込み文の引数に指定された正規化されていないファイル名はaliasに入れられる。
|
|
137
|
+
* JavaScriptファイルによるプラグインの場合、contentは空文字列。
|
|
138
|
+
* funclistはシンタックスハイライトの高速化のために事前に取り出した、ファイルが定義する関数名のリスト。
|
|
139
|
+
* @type {Record<string, { tokens: TokenWithSourceMap[], alias: Set<string>, addPluginFile: () => void, funclist: Record<string, object> }>}
|
|
140
|
+
*/
|
|
141
|
+
this.dependencies = {}
|
|
142
|
+
|
|
143
|
+
/** @type {Set<string>} */
|
|
144
|
+
this.usedFuncs = new Set()
|
|
145
|
+
|
|
146
|
+
this.setFunc = this.addFunc // エイリアス
|
|
147
|
+
|
|
148
|
+
this.numFailures = 0
|
|
149
|
+
|
|
150
|
+
if (options.useBasicPlugin) { this.addBasicPlugins() }
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* 基本的なプラグインを追加する
|
|
155
|
+
*/
|
|
156
|
+
addBasicPlugins () {
|
|
157
|
+
this.addPluginObject('PluginSystem', PluginSystem)
|
|
158
|
+
this.addPluginObject('PluginMath', PluginMath)
|
|
159
|
+
this.addPluginObject('PluginPromise', PluginPromise)
|
|
160
|
+
this.addPluginObject('PluginAssert', PluginTest)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* loggerを新しいインスタンスで置き換える。
|
|
165
|
+
*/
|
|
166
|
+
replaceLogger () {
|
|
167
|
+
const logger = this.prepare.logger = this.lexer.logger = this.parser.logger = this.logger = new NakoLogger()
|
|
168
|
+
return logger
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* ファイル内のrequire文の位置を列挙する。出力の配列はstartでソートされている。
|
|
173
|
+
* @param {TokenWithSourceMap[]} tokens rawtokenizeの出力
|
|
174
|
+
*/
|
|
175
|
+
static listRequireStatements (tokens) {
|
|
176
|
+
/** @type {{ start: number, end: number, value: string, firstToken: TokenWithSourceMap, lastToken: TokenWithSourceMap }[]} */
|
|
177
|
+
const requireStatements = []
|
|
178
|
+
for (let i = 0; i + 2 < tokens.length; i++) {
|
|
179
|
+
// not (string|string_ex) '取り込み'
|
|
180
|
+
if (!(tokens[i].type === 'not' &&
|
|
181
|
+
(tokens[i + 1].type === 'string' || tokens[i + 1].type === 'string_ex') &&
|
|
182
|
+
tokens[i + 2].value === '取込')) {
|
|
183
|
+
continue
|
|
184
|
+
}
|
|
185
|
+
requireStatements.push({ start: i, end: i + 3, value: tokens[i + 1].value + '', firstToken: tokens[i], lastToken: tokens[i + 2] })
|
|
186
|
+
i += 2
|
|
187
|
+
}
|
|
188
|
+
return requireStatements
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* プログラムが依存するファイルを再帰的に取得する。
|
|
193
|
+
* - .jsであれば評価してthis.addPluginFileを呼び出し、.nako3であればファイルをfetchしてdependenciesに保存し再帰する。
|
|
194
|
+
* - resolvePathはファイルを検索して正規化する必要がある。
|
|
195
|
+
* - needNako3やneedJsがPromiseを返すなら並列処理し、処理の終了を知らせるためのPromiseを返す。そうでなければ同期的に処理する。
|
|
196
|
+
* (`instanceof Promise` はpolyfillで動作しない場合があるため、Promiseかどうかを明示する必要がある。)
|
|
197
|
+
* - readNako3はソースコードを返す。readJsはrequireあるいはevalする関数を返す。
|
|
198
|
+
* @param {string} code
|
|
199
|
+
* @param {string} filename
|
|
200
|
+
* @param {string} preCode
|
|
201
|
+
* @param {{
|
|
202
|
+
* resolvePath: (name: string, token: TokenWithSourceMap) => { type: 'nako3' | 'js' | 'invalid', filePath: string }
|
|
203
|
+
* readNako3: (filePath: string, token: TokenWithSourceMap) => { sync: true, value: string } | { sync: false, value: Promise<string> }
|
|
204
|
+
* readJs: (filePath: string, token: TokenWithSourceMap) => { sync: true, value: () => object } | { sync: false, value: Promise<() => object> }
|
|
205
|
+
* }} tools
|
|
206
|
+
* @returns {Promise<unknown> | void}
|
|
207
|
+
* @protected
|
|
208
|
+
*/
|
|
209
|
+
_loadDependencies (code, filename, preCode, tools) {
|
|
210
|
+
/** @type {NakoCompiler['dependencies']} */
|
|
211
|
+
const dependencies = {}
|
|
212
|
+
const compiler = new NakoCompiler({ useBasicPlugin: true })
|
|
213
|
+
|
|
214
|
+
/** @param {string} code @param {string} filename @param {string} preCode @returns {Promise<unknown> | void} */
|
|
215
|
+
const inner = (code, filename, preCode) => {
|
|
216
|
+
/** @type {Promise<unknown>[]} */
|
|
217
|
+
const tasks = []
|
|
218
|
+
for (const item of NakoCompiler.listRequireStatements(compiler.rawtokenize(code, 0, filename, preCode)).map((v) => ({ ...v, ...tools.resolvePath(v.value, v.firstToken) }))) {
|
|
219
|
+
// 2回目以降の読み込み
|
|
220
|
+
// eslint-disable-next-line no-prototype-builtins
|
|
221
|
+
if (dependencies.hasOwnProperty(item.filePath)) {
|
|
222
|
+
dependencies[item.filePath].alias.add(item.value)
|
|
223
|
+
continue
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// 初回の読み込み
|
|
227
|
+
dependencies[item.filePath] = { tokens: [], alias: new Set([item.value]), addPluginFile: () => {}, funclist: {} }
|
|
228
|
+
if (item.type === 'js') {
|
|
229
|
+
// jsならプラグインとして読み込む。
|
|
230
|
+
const obj = tools.readJs(item.filePath, item.firstToken)
|
|
231
|
+
if (obj.sync) {
|
|
232
|
+
dependencies[item.filePath].addPluginFile = () => { this.addPluginFile(item.value, item.filePath, dependencies[item.filePath].funclist = obj.value(), false) }
|
|
233
|
+
} else {
|
|
234
|
+
tasks.push(obj.value.then((res) => {
|
|
235
|
+
dependencies[item.filePath].addPluginFile = () => { this.addPluginFile(item.value, item.filePath, dependencies[item.filePath].funclist = res(), false) }
|
|
236
|
+
}))
|
|
237
|
+
}
|
|
238
|
+
} else if (item.type === 'nako3') {
|
|
239
|
+
// nako3ならファイルを読んでdependenciesに保存する。
|
|
240
|
+
const content = tools.readNako3(item.filePath, item.firstToken)
|
|
241
|
+
/** @param {string} code */
|
|
242
|
+
const registerFile = (code) => {
|
|
243
|
+
// シンタックスハイライトの高速化のために、事前にファイルが定義する関数名のリストを取り出しておく。
|
|
244
|
+
// preDefineFuncはトークン列に変更を加えるため、事前にクローンしておく。
|
|
245
|
+
// 「プラグイン名設定」を行う (#956)
|
|
246
|
+
code = `「${item.filePath}」にプラグイン名設定;` + code + ';『メイン』にプラグイン名設定;'
|
|
247
|
+
const tokens = this.rawtokenize(code, 0, item.filePath)
|
|
248
|
+
dependencies[item.filePath].tokens = tokens
|
|
249
|
+
/** @type {import('./nako_lexer').FuncList} */
|
|
250
|
+
const funclist = {}
|
|
251
|
+
NakoLexer.preDefineFunc(cloneAsJSON(tokens), this.logger, funclist)
|
|
252
|
+
dependencies[item.filePath].funclist = funclist
|
|
253
|
+
|
|
254
|
+
// 再帰
|
|
255
|
+
return inner(code, item.filePath, '')
|
|
256
|
+
}
|
|
257
|
+
if (content.sync) {
|
|
258
|
+
registerFile(content.value)
|
|
259
|
+
} else {
|
|
260
|
+
tasks.push(content.value.then((res) => registerFile(res)))
|
|
261
|
+
}
|
|
262
|
+
} else {
|
|
263
|
+
throw new NakoImportError(`ファイル ${item.value} を読み込めません。未対応の拡張子です。`, item.firstToken.line, item.firstToken.file)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (tasks.length > 0) {
|
|
268
|
+
return Promise.all(tasks)
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
try {
|
|
273
|
+
const result = inner(code, filename, preCode)
|
|
274
|
+
|
|
275
|
+
// 非同期な場合のエラーハンドリング
|
|
276
|
+
if (result !== undefined) {
|
|
277
|
+
result.catch((err) => { this.logger.error(err); throw err })
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// すべてが終わってからthis.dependenciesに代入する。そうしないと、「実行」ボタンを連打した場合など、
|
|
281
|
+
// loadDependencies() が並列実行されるときに正しく動作しない。
|
|
282
|
+
this.dependencies = dependencies
|
|
283
|
+
return result
|
|
284
|
+
} catch (err) { // 同期的な場合のエラーハンドリング
|
|
285
|
+
this.logger.error(err)
|
|
286
|
+
throw err
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* コードを単語に分割する
|
|
292
|
+
* @param {string} code なでしこのプログラム
|
|
293
|
+
* @param {number} line なでしこのプログラムの行番号
|
|
294
|
+
* @param {string} filename
|
|
295
|
+
* @param {string} [preCode]
|
|
296
|
+
* @returns {TokenWithSourceMap[]} トークンのリスト
|
|
297
|
+
*/
|
|
298
|
+
rawtokenize (code, line, filename, preCode = '') {
|
|
299
|
+
if (!code.startsWith(preCode)) {
|
|
300
|
+
throw new Error('codeの先頭にはpreCodeを含める必要があります。')
|
|
301
|
+
}
|
|
302
|
+
// インデント構文 (#596)
|
|
303
|
+
const { code: code2, insertedLines, deletedLines } = NakoIndent.convert(code, filename)
|
|
304
|
+
|
|
305
|
+
// 全角半角の統一処理
|
|
306
|
+
const preprocessed = this.prepare.convert(code2)
|
|
307
|
+
|
|
308
|
+
const tokenizationSourceMapping = new SourceMappingOfTokenization(code2.length, preprocessed)
|
|
309
|
+
const indentationSyntaxSourceMapping = new SourceMappingOfIndentSyntax(code2, insertedLines, deletedLines)
|
|
310
|
+
const offsetToLineColumn = new OffsetToLineColumn(code)
|
|
311
|
+
|
|
312
|
+
// トークン分割
|
|
313
|
+
/** @type {import('./nako_lexer').Token[]} */
|
|
314
|
+
let tokens
|
|
315
|
+
try {
|
|
316
|
+
tokens = this.lexer.setInput(preprocessed.map((v) => v.text).join(''), line, filename)
|
|
317
|
+
} catch (err) {
|
|
318
|
+
if (!(err instanceof InternalLexerError)) {
|
|
319
|
+
throw err
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// エラー位置をソースコード上の位置に変換して返す
|
|
323
|
+
const dest = indentationSyntaxSourceMapping.map(tokenizationSourceMapping.map(err.preprocessedCodeStartOffset), tokenizationSourceMapping.map(err.preprocessedCodeEndOffset))
|
|
324
|
+
/** @type {number | undefined} */
|
|
325
|
+
const line = dest.startOffset === null ? err.line : offsetToLineColumn.map(dest.startOffset, false).line
|
|
326
|
+
const map = subtractSourceMapByPreCodeLength({ ...dest, line }, preCode)
|
|
327
|
+
throw new NakoLexerError(err.msg, map.startOffset, map.endOffset, map.line, filename)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// ソースコード上の位置に変換
|
|
331
|
+
return tokens.map((token, i) => {
|
|
332
|
+
const dest = indentationSyntaxSourceMapping.map(
|
|
333
|
+
tokenizationSourceMapping.map(token.preprocessedCodeOffset),
|
|
334
|
+
tokenizationSourceMapping.map(token.preprocessedCodeOffset + token.preprocessedCodeLength)
|
|
335
|
+
)
|
|
336
|
+
let line = token.line
|
|
337
|
+
let column = 0
|
|
338
|
+
if (token.type === 'eol' && dest.endOffset !== null) {
|
|
339
|
+
// eolはparserで `line = ${eolToken.line};` に変換されるため、
|
|
340
|
+
// 行末のeolのlineは次の行の行数を表す必要がある。
|
|
341
|
+
const out = offsetToLineColumn.map(dest.endOffset, false)
|
|
342
|
+
line = out.line
|
|
343
|
+
column = out.column
|
|
344
|
+
} else if (dest.startOffset !== null) {
|
|
345
|
+
const out = offsetToLineColumn.map(dest.startOffset, false)
|
|
346
|
+
line = out.line
|
|
347
|
+
column = out.column
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
...token,
|
|
351
|
+
...subtractSourceMapByPreCodeLength({ line, column, startOffset: dest.startOffset, endOffset: dest.endOffset }, preCode),
|
|
352
|
+
rawJosi: token.josi
|
|
353
|
+
}
|
|
354
|
+
})
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* 単語の属性を構文解析に先立ち補正する
|
|
359
|
+
* @param {TokenWithSourceMap[]} tokens トークンのリスト
|
|
360
|
+
* @param {boolean} isFirst 最初の呼び出しかどうか
|
|
361
|
+
* @returns コード (なでしこ)
|
|
362
|
+
*/
|
|
363
|
+
converttoken (tokens, isFirst) {
|
|
364
|
+
return this.lexer.setInput2(tokens, isFirst)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* 環境のリセット
|
|
369
|
+
*/
|
|
370
|
+
reset () {
|
|
371
|
+
/**
|
|
372
|
+
* なでしこのローカル変数をスタックで管理
|
|
373
|
+
* __varslist[0] プラグイン領域
|
|
374
|
+
* __varslist[1] なでしこグローバル領域
|
|
375
|
+
* __varslist[2] 最初のローカル変数 ( == __vars }
|
|
376
|
+
* @type {Record<string, any>[]}
|
|
377
|
+
*/
|
|
378
|
+
this.__varslist = [this.__varslist[0], {}, {}]
|
|
379
|
+
this.__v0 = this.__varslist[0]
|
|
380
|
+
this.__v1 = this.__varslist[1]
|
|
381
|
+
this.__vars = this.__varslist[2]
|
|
382
|
+
this.__locals = {}
|
|
383
|
+
|
|
384
|
+
// プラグイン命令以外を削除する。
|
|
385
|
+
this.funclist = {}
|
|
386
|
+
for (const name of Object.keys(this.__v0)) {
|
|
387
|
+
const original = this.pluginFunclist[name]
|
|
388
|
+
if (!original) {
|
|
389
|
+
continue
|
|
390
|
+
}
|
|
391
|
+
this.funclist[name] = JSON.parse(JSON.stringify(original))
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
this.lexer.setFuncList(this.funclist)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* typeがcodeのトークンを単語に分割するための処理
|
|
399
|
+
* @param {string} code
|
|
400
|
+
* @param {number} line
|
|
401
|
+
* @param {string} filename
|
|
402
|
+
* @param {number | null} startOffset
|
|
403
|
+
* @returns {{ commentTokens: TokenWithSourceMap[], tokens: TokenWithSourceMap[] }}
|
|
404
|
+
* @private
|
|
405
|
+
*/
|
|
406
|
+
lexCodeToken (code, line, filename, startOffset) {
|
|
407
|
+
// 単語に分割
|
|
408
|
+
let tokens = this.rawtokenize(code, line, filename, '')
|
|
409
|
+
|
|
410
|
+
// 文字列内位置からファイル内位置へ変換
|
|
411
|
+
if (startOffset === null) {
|
|
412
|
+
for (const token of tokens) {
|
|
413
|
+
token.startOffset = null
|
|
414
|
+
token.endOffset = null
|
|
415
|
+
}
|
|
416
|
+
} else {
|
|
417
|
+
for (const token of tokens) {
|
|
418
|
+
if (token.startOffset !== null) {
|
|
419
|
+
token.startOffset += startOffset
|
|
420
|
+
}
|
|
421
|
+
if (token.endOffset !== null) {
|
|
422
|
+
token.endOffset += startOffset
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// convertTokenで消されるコメントのトークンを残す
|
|
428
|
+
const commentTokens = tokens.filter((t) => t.type === 'line_comment' || t.type === 'range_comment')
|
|
429
|
+
.map((v) => ({ ...v })) // clone
|
|
430
|
+
|
|
431
|
+
tokens = this.converttoken(tokens, false)
|
|
432
|
+
|
|
433
|
+
return { tokens, commentTokens }
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* 再帰的にrequire文を置換する。
|
|
438
|
+
* .jsであれば削除し、.nako3であればそのファイルのトークン列で置換する。
|
|
439
|
+
* @param {TokenWithSourceMap[]} tokens
|
|
440
|
+
* @param {Set<string>} [includeGuard]
|
|
441
|
+
* @returns {TokenWithSourceMap[]} 削除された取り込み文のトークン
|
|
442
|
+
*/
|
|
443
|
+
replaceRequireStatements (tokens, includeGuard = new Set()) {
|
|
444
|
+
/** @type {TokenWithSourceMap[]} */
|
|
445
|
+
const deletedTokens = []
|
|
446
|
+
for (const r of NakoCompiler.listRequireStatements(tokens).reverse()) {
|
|
447
|
+
// C言語のinclude guardと同じ仕組みで無限ループを防ぐ。
|
|
448
|
+
if (includeGuard.has(r.value)) {
|
|
449
|
+
deletedTokens.push(...tokens.splice(r.start, r.end - r.start))
|
|
450
|
+
continue
|
|
451
|
+
}
|
|
452
|
+
const filePath = Object.keys(this.dependencies).find((key) => this.dependencies[key].alias.has(r.value))
|
|
453
|
+
if (filePath === undefined) {
|
|
454
|
+
throw new NakoLexerError(`ファイル ${r.value} が読み込まれていません。`, r.firstToken.startOffset, r.firstToken.endOffset, r.firstToken.line, r.firstToken.file)
|
|
455
|
+
}
|
|
456
|
+
this.dependencies[filePath].addPluginFile()
|
|
457
|
+
const children = cloneAsJSON(this.dependencies[filePath].tokens)
|
|
458
|
+
includeGuard.add(r.value)
|
|
459
|
+
deletedTokens.push(...this.replaceRequireStatements(children, includeGuard))
|
|
460
|
+
deletedTokens.push(...tokens.splice(r.start, r.end - r.start, ...children))
|
|
461
|
+
}
|
|
462
|
+
return deletedTokens
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* replaceRequireStatementsのシンタックスハイライト用の実装。
|
|
467
|
+
* @param {TokenWithSourceMap[]} tokens
|
|
468
|
+
* @returns {TokenWithSourceMap[]} 削除された取り込み文のトークン
|
|
469
|
+
*/
|
|
470
|
+
removeRequireStatements (tokens) {
|
|
471
|
+
/** @type {TokenWithSourceMap[]} */
|
|
472
|
+
const deletedTokens = []
|
|
473
|
+
for (const r of NakoCompiler.listRequireStatements(tokens).reverse()) {
|
|
474
|
+
// プラグイン命令のシンタックスハイライトのために、addPluginFileを呼んで関数のリストをthis.dependencies[filePath].funclistに保存させる。
|
|
475
|
+
const filePath = Object.keys(this.dependencies).find((key) => this.dependencies[key].alias.has(r.value))
|
|
476
|
+
if (filePath !== undefined) {
|
|
477
|
+
this.dependencies[filePath].addPluginFile()
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// 全ての取り込み文を削除する。そうしないとトークン化に時間がかかりすぎる。
|
|
481
|
+
deletedTokens.push(...tokens.splice(r.start, r.end - r.start))
|
|
482
|
+
}
|
|
483
|
+
return deletedTokens
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* @param {string} code
|
|
488
|
+
* @param {string} filename
|
|
489
|
+
* @param {string} [preCode]
|
|
490
|
+
* @returns {{ commentTokens: TokenWithSourceMap[], tokens: TokenWithSourceMap[], requireTokens: TokenWithSourceMap[] }}
|
|
491
|
+
*/
|
|
492
|
+
lex (code, filename, preCode = '', syntaxHighlighting = false) {
|
|
493
|
+
// 単語に分割
|
|
494
|
+
let tokens = this.rawtokenize(code, 0, filename, preCode)
|
|
495
|
+
|
|
496
|
+
// require文を再帰的に置換する
|
|
497
|
+
const requireStatementTokens = syntaxHighlighting ? this.removeRequireStatements(tokens) : this.replaceRequireStatements(tokens, undefined)
|
|
498
|
+
for (const t of requireStatementTokens) {
|
|
499
|
+
if (t.type === 'word' || t.type === 'not') {
|
|
500
|
+
t.type = 'require'
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// convertTokenで消されるコメントのトークンを残す
|
|
505
|
+
/** @type {TokenWithSourceMap[]} */
|
|
506
|
+
const commentTokens = tokens.filter((t) => t.type === 'line_comment' || t.type === 'range_comment')
|
|
507
|
+
.map((v) => ({ ...v })) // clone
|
|
508
|
+
|
|
509
|
+
tokens = this.converttoken(tokens, true)
|
|
510
|
+
|
|
511
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
512
|
+
if (tokens[i].type === 'code') {
|
|
513
|
+
const children = this.lexCodeToken(tokens[i].value, tokens[i].line, filename, tokens[i].startOffset)
|
|
514
|
+
commentTokens.push(...children.commentTokens)
|
|
515
|
+
tokens.splice(i, 1, ...children.tokens)
|
|
516
|
+
i--
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
this.logger.trace('--- lex ---\n' + JSON.stringify(tokens, null, 2))
|
|
521
|
+
|
|
522
|
+
return { commentTokens, tokens, requireTokens: requireStatementTokens }
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* コードをパースしてASTにする
|
|
527
|
+
* @param {string} code なでしこのプログラム
|
|
528
|
+
* @param {string} filename
|
|
529
|
+
* @param {string} [preCode]
|
|
530
|
+
* @return {Ast}
|
|
531
|
+
*/
|
|
532
|
+
parse (code, filename, preCode = '') {
|
|
533
|
+
// 関数を字句解析と構文解析に登録
|
|
534
|
+
this.lexer.setFuncList(this.funclist)
|
|
535
|
+
this.parser.setFuncList(this.funclist)
|
|
536
|
+
|
|
537
|
+
const lexerOutput = this.lex(code, filename, preCode)
|
|
538
|
+
|
|
539
|
+
// 構文木を作成
|
|
540
|
+
/** @type {Ast} */
|
|
541
|
+
let ast
|
|
542
|
+
try {
|
|
543
|
+
ast = this.parser.parse(lexerOutput.tokens)
|
|
544
|
+
} catch (err) {
|
|
545
|
+
if (typeof err.startOffset !== 'number') {
|
|
546
|
+
throw NakoSyntaxError.fromNode(err.message, lexerOutput.tokens[this.parser.index])
|
|
547
|
+
}
|
|
548
|
+
throw err
|
|
549
|
+
}
|
|
550
|
+
this.usedFuncs = this.getUsedFuncs(ast)
|
|
551
|
+
this.logger.trace('--- ast ---\n' + JSON.stringify(ast, null, 2))
|
|
552
|
+
return ast
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* @param {Ast} ast
|
|
557
|
+
*/
|
|
558
|
+
getUsedFuncs (ast) {
|
|
559
|
+
const queue = [ast]
|
|
560
|
+
this.usedFuncs = new Set()
|
|
561
|
+
|
|
562
|
+
while (queue.length > 0) {
|
|
563
|
+
const ast_ = queue.pop()
|
|
564
|
+
|
|
565
|
+
if (ast_ !== null && ast_ !== undefined && ast_.block !== null && ast_.block !== undefined) {
|
|
566
|
+
this.getUsedAndDefFuncs(queue, JSON.parse(JSON.stringify(ast_.block)))
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
return this.deleteUnNakoFuncs()
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
getUsedAndDefFuncs (astQueue, blockQueue) {
|
|
574
|
+
while (blockQueue.length > 0) {
|
|
575
|
+
const block = blockQueue.pop()
|
|
576
|
+
|
|
577
|
+
if (block !== null && block !== undefined) {
|
|
578
|
+
this.getUsedAndDefFunc(block, astQueue, blockQueue)
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
getUsedAndDefFunc (block, astQueue, blockQueue) {
|
|
584
|
+
if (['func', 'func_pointer'].includes(block.type) && block.name !== null && block.name !== undefined) {
|
|
585
|
+
this.usedFuncs.add(block.name)
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// eslint-disable-next-line no-useless-call
|
|
589
|
+
astQueue.push.apply(astQueue, [block, block.block])
|
|
590
|
+
blockQueue.push.apply(blockQueue, [block.value].concat(block.args))
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
deleteUnNakoFuncs () {
|
|
594
|
+
for (const func of this.usedFuncs) {
|
|
595
|
+
if (!this.commandlist.has(func)) {
|
|
596
|
+
this.usedFuncs.delete(func)
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
return this.usedFuncs
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* プログラムをコンパイルしてJavaScriptのコードを返す
|
|
605
|
+
* @param {string} code コード (なでしこ)
|
|
606
|
+
* @param {string} filename
|
|
607
|
+
* @param {boolean | string} isTest テストかどうか。stringの場合は1つのテストのみ。
|
|
608
|
+
* @param {string} [preCode]
|
|
609
|
+
*/
|
|
610
|
+
compile (code, filename, isTest, preCode = '') {
|
|
611
|
+
const ast = this.parse(code, filename, preCode)
|
|
612
|
+
return NakoGen(ast.genMode).generate(this, ast, isTest).runtimeEnv
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* @param {string} code
|
|
617
|
+
* @param {string} fname
|
|
618
|
+
* @param {boolean} isReset
|
|
619
|
+
* @param {boolean | string} isTest テストかどうか。stringの場合は1つのテストのみ。
|
|
620
|
+
* @param {string} [preCode]
|
|
621
|
+
*/
|
|
622
|
+
_run (code, fname, isReset, isTest, preCode = '') {
|
|
623
|
+
const opts = {
|
|
624
|
+
resetLog: isReset,
|
|
625
|
+
testOnly: isTest
|
|
626
|
+
}
|
|
627
|
+
return this._runEx(code, fname, opts, preCode)
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
clearPlugins () {
|
|
631
|
+
// 他に実行している「なでしこ」があればクリアする
|
|
632
|
+
this.__globals.forEach((sys) => {
|
|
633
|
+
sys.reset()
|
|
634
|
+
})
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* @param {string} code
|
|
639
|
+
* @param {string} fname
|
|
640
|
+
* @param {Partial<CompilerOptions>} opts
|
|
641
|
+
* @param {string} [preCode]
|
|
642
|
+
* @param {NakoGlobal} [nakoGlobal] ナデシコ命令でスコープを共有するため
|
|
643
|
+
*/
|
|
644
|
+
_runEx (code, fname, opts, preCode = '', nakoGlobal) {
|
|
645
|
+
// コンパイル
|
|
646
|
+
let out
|
|
647
|
+
try {
|
|
648
|
+
const optsAll = Object.assign({ resetEnv: true, testOnly: false, resetAll: true }, opts)
|
|
649
|
+
if (optsAll.resetEnv) { this.reset() }
|
|
650
|
+
if (optsAll.resetAll) { this.clearPlugins() }
|
|
651
|
+
const ast = this.parse(code, fname, preCode)
|
|
652
|
+
out = NakoGen(ast.genMode).generate(this, ast, optsAll.testOnly)
|
|
653
|
+
} catch (e) {
|
|
654
|
+
this.logger.error(e)
|
|
655
|
+
throw e
|
|
656
|
+
}
|
|
657
|
+
// 実行
|
|
658
|
+
nakoGlobal = nakoGlobal || new NakoGlobal(this, out.gen)
|
|
659
|
+
if (this.__globals.indexOf(nakoGlobal) < 0) {
|
|
660
|
+
this.__globals.push(nakoGlobal)
|
|
661
|
+
}
|
|
662
|
+
try {
|
|
663
|
+
// eslint-disable-next-line no-new-func
|
|
664
|
+
new Function(out.runtimeEnv).apply(nakoGlobal)
|
|
665
|
+
return nakoGlobal
|
|
666
|
+
} catch (e) {
|
|
667
|
+
let err = e
|
|
668
|
+
if (!(e instanceof NakoRuntimeError)) {
|
|
669
|
+
err = new NakoRuntimeError(e, nakoGlobal.__varslist[0].line)
|
|
670
|
+
}
|
|
671
|
+
this.logger.error(err)
|
|
672
|
+
throw err
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* @param {string} code
|
|
678
|
+
* @param {string} fname
|
|
679
|
+
* @param {Partial<CompilerOptions>} opts
|
|
680
|
+
* @param {string} [preCode]
|
|
681
|
+
*/
|
|
682
|
+
runEx (code, fname, opts, preCode = '') {
|
|
683
|
+
return this._runEx(code, fname, opts, preCode)
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* @param {string} code
|
|
688
|
+
* @param {string} fname
|
|
689
|
+
* @param {string} [preCode]
|
|
690
|
+
* @param {string | undefined} [testName]
|
|
691
|
+
*/
|
|
692
|
+
test (code, fname, preCode = '', testName = undefined) {
|
|
693
|
+
return this._runEx(code, fname, { testOnly: testName || true }, preCode)
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* なでしこのプログラムを実行(他に実行しているインスタンスはそのまま)
|
|
698
|
+
* @param {string} code
|
|
699
|
+
* @param {string} fname
|
|
700
|
+
* @param {string} [preCode]
|
|
701
|
+
*/
|
|
702
|
+
run (code, fname, preCode = '') {
|
|
703
|
+
return this._runEx(code, fname, { resetAll: false }, preCode)
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* なでしこのプログラムを実行(他に実行しているインスタンスもリセットする)
|
|
708
|
+
* @param {string} code
|
|
709
|
+
* @param {string} fname
|
|
710
|
+
* @param {string} [preCode]
|
|
711
|
+
*/
|
|
712
|
+
runReset (code, fname, preCode = '') {
|
|
713
|
+
return this._runEx(code, fname, { resetAll: true }, preCode)
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* JavaScriptのみで動くコードを取得する場合
|
|
718
|
+
* @param {string} code
|
|
719
|
+
* @param {string} filename
|
|
720
|
+
* @param {boolean | string} isTest
|
|
721
|
+
* @param {string} [preCode]
|
|
722
|
+
*/
|
|
723
|
+
compileStandalone (code, filename, isTest, preCode = '') {
|
|
724
|
+
const ast = this.parse(code, filename, preCode)
|
|
725
|
+
return NakoGen(ast.genMode).generate(this, ast, isTest).standalone
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/**
|
|
729
|
+
* プラグイン・オブジェクトを追加
|
|
730
|
+
* @param po プラグイン・オブジェクト
|
|
731
|
+
* @param {boolean} [persistent] falseのとき、次以降の実行では使えない
|
|
732
|
+
*/
|
|
733
|
+
addPlugin (po, persistent = true) {
|
|
734
|
+
// 変数のメタ情報を確認
|
|
735
|
+
const __v0 = this.__varslist[0]
|
|
736
|
+
if (__v0.meta === undefined) { __v0.meta = {} }
|
|
737
|
+
|
|
738
|
+
// プラグインの値をオブジェクトにコピー
|
|
739
|
+
for (const key in po) {
|
|
740
|
+
const v = po[key]
|
|
741
|
+
this.funclist[key] = v
|
|
742
|
+
if (persistent) {
|
|
743
|
+
this.pluginFunclist[key] = JSON.parse(JSON.stringify(v))
|
|
744
|
+
}
|
|
745
|
+
if (v.type === 'func') {
|
|
746
|
+
__v0[key] = v.fn
|
|
747
|
+
} else if (v.type === 'const' || v.type === 'var') {
|
|
748
|
+
__v0[key] = v.value
|
|
749
|
+
__v0.meta[key] = {
|
|
750
|
+
readonly: (v.type === 'const')
|
|
751
|
+
}
|
|
752
|
+
} else {
|
|
753
|
+
throw new Error('プラグインの追加でエラー。')
|
|
754
|
+
}
|
|
755
|
+
// コマンドを登録するか?
|
|
756
|
+
if (key === '初期化' || key.substr(0, 1) === '!') { // 登録しない関数名
|
|
757
|
+
continue
|
|
758
|
+
}
|
|
759
|
+
this.commandlist.add(key)
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
/**
|
|
764
|
+
* プラグイン・オブジェクトを追加(ブラウザ向け)
|
|
765
|
+
* @param objName オブジェクト名
|
|
766
|
+
* @param po 関数リスト
|
|
767
|
+
* @param {boolean} [persistent] falseのとき、次以降の実行では使えない
|
|
768
|
+
*/
|
|
769
|
+
addPluginObject (objName, po, persistent = true) {
|
|
770
|
+
this.__module[objName] = po
|
|
771
|
+
this.pluginfiles[objName] = '*'
|
|
772
|
+
// 初期化をチェック
|
|
773
|
+
if (typeof (po['初期化']) === 'object') {
|
|
774
|
+
const def = po['初期化']
|
|
775
|
+
delete po['初期化']
|
|
776
|
+
const initKey = `!${objName}:初期化`
|
|
777
|
+
po[initKey] = def
|
|
778
|
+
}
|
|
779
|
+
// メタ情報をチェック (#1034)
|
|
780
|
+
if (po.meta && po.meta.value && typeof (po.meta) === 'object') {
|
|
781
|
+
const meta = po.meta
|
|
782
|
+
delete po.meta
|
|
783
|
+
const pluginName = meta.value.pluginName || objName
|
|
784
|
+
const metaKey = `__${pluginName}`.replace('-', '__')
|
|
785
|
+
po[metaKey] = meta
|
|
786
|
+
}
|
|
787
|
+
this.addPlugin(po, persistent)
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
/**
|
|
791
|
+
* プラグイン・ファイルを追加(Node.js向け)
|
|
792
|
+
* @param {string} objName オブジェクト名
|
|
793
|
+
* @param {string} fpath ファイルパス
|
|
794
|
+
* @param po 登録するオブジェクト
|
|
795
|
+
* @param {boolean} [persistent] falseのとき、次以降の実行では使えない
|
|
796
|
+
*/
|
|
797
|
+
addPluginFile (objName, fpath, po, persistent = true) {
|
|
798
|
+
this.addPluginObject(objName, po, persistent)
|
|
799
|
+
if (this.pluginfiles[objName] === undefined) {
|
|
800
|
+
this.pluginfiles[objName] = fpath
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
* 関数を追加する
|
|
806
|
+
* @param {string} key 関数名
|
|
807
|
+
* @param {string[][]} josi 助詞
|
|
808
|
+
* @param {Function} fn 関数
|
|
809
|
+
* @param {boolean} returnNone 値を返す関数の場合はfalseを設定する。
|
|
810
|
+
*/
|
|
811
|
+
addFunc (key, josi, fn, returnNone = true) {
|
|
812
|
+
this.funclist[key] = { josi, fn, type: 'func', return_none: returnNone }
|
|
813
|
+
this.pluginFunclist[key] = cloneAsJSON(this.funclist[key])
|
|
814
|
+
this.__varslist[0][key] = fn
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
* プラグイン関数を参照する
|
|
819
|
+
* @param {string} key プラグイン関数の関数名
|
|
820
|
+
* @returns {NakoFunction} プラグイン・オブジェクト
|
|
821
|
+
*/
|
|
822
|
+
getFunc (key) {
|
|
823
|
+
return this.funclist[key]
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* コードジェネレータを追加する
|
|
828
|
+
* @param {string} mode
|
|
829
|
+
* @param {any} obj
|
|
830
|
+
*/
|
|
831
|
+
addCodeGenerator (mode, obj) {
|
|
832
|
+
codeGenerators[mode] = obj
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
module.exports = NakoCompiler
|