nadesiko3 3.3.2 → 3.3.3
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/demo/runscript3.html +35 -0
- package/package.json +1 -1
- package/release/_hash.txt +16 -16
- package/release/_script-tags.txt +14 -14
- 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/editor.js +1 -1
- package/release/plugin_weykturtle3d.js.LICENSE.txt +3 -0
- package/release/stats.json +1 -1
- package/release/version.js +1 -1
- package/release/wnako3.js +1 -1
- package/src/cnako3mod.mjs +78 -53
- package/src/nako3.mjs +18 -10
- package/src/nako_errors.mjs +2 -2
- package/src/nako_from_dncl.mjs +4 -3
- package/src/nako_indent.mjs +26 -22
- package/src/nako_prepare.mjs +45 -6
- package/src/nako_version.mjs +2 -2
- package/src/plugin_keigo.mjs +1 -1
- package/src/wnako3.mjs +19 -9
- package/src/wnako3_editor.mjs +4 -4
- package/tools/nako3edit/html/daisyui/LICENSE +22 -0
- package/tools/nako3edit/html/daisyui/full.css +1 -0
- package/tools/nako3edit/html/daisyui/tailwind.min.css +1 -0
- package/tools/nako3edit/html/daisyui/version_2.14.1 +1 -0
- package/tools/nako3edit/html/edit.html +104 -19
- package/tools/nako3edit/html/files.html +84 -8
- package/tools/nako3edit/html/nako3edit.css +6 -7
- package/tools/nako3edit/index.nako3 +44 -5
package/src/cnako3mod.mjs
CHANGED
|
@@ -89,7 +89,7 @@ export class CNako3 extends NakoCompiler {
|
|
|
89
89
|
source: app.eval || '',
|
|
90
90
|
man: app.man || '',
|
|
91
91
|
one_liner: app.eval || false,
|
|
92
|
-
debug: this.debug,
|
|
92
|
+
debug: this.debug || false,
|
|
93
93
|
trace: app.trace,
|
|
94
94
|
warn: app.warn,
|
|
95
95
|
repl: app.repl || false,
|
|
@@ -124,7 +124,9 @@ export class CNako3 extends NakoCompiler {
|
|
|
124
124
|
|
|
125
125
|
// 実行する
|
|
126
126
|
async execCommand () {
|
|
127
|
+
// コマンドを解析
|
|
127
128
|
const opt = this.checkArguments()
|
|
129
|
+
// 使い方の表示か?
|
|
128
130
|
if (opt.man) {
|
|
129
131
|
this.cnakoMan(opt.man)
|
|
130
132
|
return
|
|
@@ -155,10 +157,11 @@ export class CNako3 extends NakoCompiler {
|
|
|
155
157
|
}
|
|
156
158
|
try {
|
|
157
159
|
if (opt.test) {
|
|
158
|
-
this.loadDependencies(src, opt.mainfile, '')
|
|
160
|
+
await this.loadDependencies(src, opt.mainfile, '')
|
|
159
161
|
this.test(src, opt.mainfile)
|
|
160
162
|
} else {
|
|
161
|
-
|
|
163
|
+
// run はコンパイルと実行を行うメソッド
|
|
164
|
+
await this.run(src, opt.mainfile)
|
|
162
165
|
}
|
|
163
166
|
if (opt.test && this.numFailures > 0) {
|
|
164
167
|
process.exit(1)
|
|
@@ -308,9 +311,15 @@ export class CNako3 extends NakoCompiler {
|
|
|
308
311
|
const log = []
|
|
309
312
|
const tools = {
|
|
310
313
|
resolvePath: (name, token) => {
|
|
314
|
+
// JSプラグインのパスを解決する
|
|
311
315
|
if (/\.(js|mjs)(\.txt)?$/.test(name) || /^[^.]*$/.test(name)) {
|
|
312
|
-
|
|
316
|
+
const jspath = CNako3.findJSPluginFile(name, this.filename, __dirname, log)
|
|
317
|
+
if (jspath === '') {
|
|
318
|
+
throw new NakoImportError(`ファイル『${name}』が見つかりません。以下のパスを検索しました。\n${log.join('\n')}`, token.file, token.line)
|
|
319
|
+
}
|
|
320
|
+
return { filePath: jspath, type: 'js' }
|
|
313
321
|
}
|
|
322
|
+
// なでしこプラグインのパスを解決する
|
|
314
323
|
if (/\.nako3?(\.txt)?$/.test(name)) {
|
|
315
324
|
if (path.isAbsolute(name)) {
|
|
316
325
|
return { filePath: path.resolve(name), type: 'nako3' }
|
|
@@ -326,12 +335,17 @@ export class CNako3 extends NakoCompiler {
|
|
|
326
335
|
},
|
|
327
336
|
readNako3: (name, token) => {
|
|
328
337
|
if (!fs.existsSync(name)) {
|
|
329
|
-
throw new NakoImportError(`ファイル ${name} が存在しません。`, token.
|
|
338
|
+
throw new NakoImportError(`ファイル ${name} が存在しません。`, token.file, token.line)
|
|
330
339
|
}
|
|
331
340
|
return { sync: true, value: fs.readFileSync(name).toString() }
|
|
332
341
|
},
|
|
333
342
|
readJs: (filePath, token) => {
|
|
334
343
|
const content = {sync: false, value: null}
|
|
344
|
+
if (process.platform === 'win32') {
|
|
345
|
+
if (filePath.substring(1, 3) === ':\\') {
|
|
346
|
+
filePath = 'file://' + filePath
|
|
347
|
+
}
|
|
348
|
+
}
|
|
335
349
|
content.value = (
|
|
336
350
|
new Promise((resolve, reject) => {
|
|
337
351
|
import(filePath).then((mod) => {
|
|
@@ -339,7 +353,7 @@ export class CNako3 extends NakoCompiler {
|
|
|
339
353
|
const obj = Object.assign({}, mod)
|
|
340
354
|
resolve(() => { return obj.default })
|
|
341
355
|
}).catch((err) => {
|
|
342
|
-
const err2 = new NakoImportError(
|
|
356
|
+
const err2 = new NakoImportError(`ファイル『${filePath}』が読み込めません。${err}`, token.file, token.line)
|
|
343
357
|
reject(err2)
|
|
344
358
|
})
|
|
345
359
|
})
|
|
@@ -356,84 +370,95 @@ export class CNako3 extends NakoCompiler {
|
|
|
356
370
|
* @param {string} [preCode]
|
|
357
371
|
*/
|
|
358
372
|
async run (code, fname, preCode = '') {
|
|
359
|
-
|
|
373
|
+
// 取り込む文の処理
|
|
374
|
+
await this.loadDependencies(code, fname, preCode).catch((err) => {
|
|
375
|
+
this.logger.error(err)
|
|
376
|
+
})
|
|
377
|
+
// 実行
|
|
360
378
|
return this._runEx(code, fname, {}, preCode)
|
|
361
379
|
}
|
|
362
380
|
|
|
363
381
|
/**
|
|
364
382
|
* プラグインファイルの検索を行う
|
|
365
|
-
* @param {string} pname
|
|
366
|
-
* @param {string} filename
|
|
383
|
+
* @param {string} pname プラグインの名前
|
|
384
|
+
* @param {string} filename 取り込み元ファイル名
|
|
367
385
|
* @param {string} srcDir このファイルが存在するディレクトリ
|
|
368
386
|
* @param {string[]} [log]
|
|
369
|
-
* @return {string}
|
|
387
|
+
* @return {string} フルパス、失敗した時は、''を返す
|
|
370
388
|
*/
|
|
371
|
-
static
|
|
389
|
+
static findJSPluginFile (pname, filename, srcDir, log = []) {
|
|
372
390
|
log.length = 0
|
|
391
|
+
const cachePath = {}
|
|
373
392
|
/** @type {string[]} */
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
return pname
|
|
379
|
-
}
|
|
380
|
-
// 各パスを調べる
|
|
381
|
-
const exists = (f, desc) => {
|
|
382
|
-
const result = fs.existsSync(f)
|
|
393
|
+
const exists = (f, _desc) => {
|
|
394
|
+
// 同じパスを何度も検索することがないように
|
|
395
|
+
if (cachePath[f]) { return false }
|
|
396
|
+
cachePath[f] = true
|
|
383
397
|
log.push(f)
|
|
384
|
-
|
|
385
|
-
return
|
|
398
|
+
const stat = fs.statSync(f, {throwIfNoEntry: false})
|
|
399
|
+
if (!stat) { return false }
|
|
400
|
+
return stat.isFile()
|
|
386
401
|
}
|
|
402
|
+
// 普通にファイルをチェック
|
|
387
403
|
const fCheck = (pathTest) => {
|
|
388
|
-
//
|
|
404
|
+
// 素直に指定されたパスをチェック
|
|
389
405
|
let fpath = path.join(pathTest, pname)
|
|
390
406
|
if (exists(fpath, 'direct')) { return fpath }
|
|
391
|
-
|
|
392
|
-
// プラグイン名を分解してチェック
|
|
393
|
-
const m = pname.match(/^(plugin_|nadesiko3-)([a-zA-Z0-9_-]+)/)
|
|
394
|
-
if (!m) { return false }
|
|
395
|
-
const name = m[2]
|
|
396
|
-
// plugin_xxx.js
|
|
397
|
-
// eslint-disable-next-line camelcase
|
|
398
|
-
const plugin_xxx_js = 'plugin_' + name + '.js'
|
|
399
|
-
fpath = path.join(pathTest, plugin_xxx_js)
|
|
400
|
-
if (exists(fpath, 'plugin_xxx.js')) { return fpath }
|
|
401
|
-
fpath = path.join(pathTest, 'src', plugin_xxx_js)
|
|
402
|
-
if (exists(fpath, 'src/plugin_xxx.mjs')) { return fpath }
|
|
403
|
-
// nadesiko3-xxx
|
|
404
|
-
// eslint-disable-next-line camelcase
|
|
405
|
-
const nadesiko3_xxx = 'nadesiko3-' + name
|
|
406
|
-
fpath = path.join(pathTest, nadesiko3_xxx)
|
|
407
|
-
if (exists(fpath, 'nadesiko3-xxx')) { return fpath }
|
|
408
|
-
fpath = path.join(pathTest, 'node_modules', nadesiko3_xxx)
|
|
409
|
-
if (exists(fpath, 'node_modules/nadesiko3-xxx')) { return fpath }
|
|
410
407
|
return false
|
|
411
408
|
}
|
|
409
|
+
// ファイル および node_modules 以下を調べる
|
|
410
|
+
const fCheckEx = (pathTest) => {
|
|
411
|
+
const defPath = fCheck(pathTest)
|
|
412
|
+
if (defPath) { return defPath }
|
|
413
|
+
const fpath = path.join(pathTest, 'node_modules', pname)
|
|
414
|
+
const json = path.join(fpath, 'package.json')
|
|
415
|
+
if (exists(json)) {
|
|
416
|
+
// package.jsonを見つけたので、メインファイルを調べて取り込む (CommonJSモジュール対策)
|
|
417
|
+
const json_txt = fs.readFileSync(json, 'utf-8')
|
|
418
|
+
const obj = JSON.parse(json_txt)
|
|
419
|
+
if (!obj['main']) { return false }
|
|
420
|
+
const mainFile = path.join(pathTest, 'node_modules', pname, obj['main'])
|
|
421
|
+
return mainFile
|
|
422
|
+
}
|
|
423
|
+
return false
|
|
424
|
+
}
|
|
425
|
+
// 各パスを検索していく
|
|
426
|
+
const p1 = pname.substring(0, 1)
|
|
427
|
+
// フルパス指定か?
|
|
428
|
+
if (p1 === '/') {
|
|
429
|
+
if (exists(pname)) { return pname }
|
|
430
|
+
const fileFullpath = fCheckEx(pname)
|
|
431
|
+
if (fileFullpath) { return fileFullpath }
|
|
432
|
+
return '' // フルパスの場合別のフォルダは調べない
|
|
433
|
+
}
|
|
412
434
|
// 相対パスか?
|
|
413
|
-
if (p1 === '.') {
|
|
435
|
+
if (p1 === '.' || pname.indexOf('/') >= 0) {
|
|
414
436
|
// 相対パス指定なので、なでしこのプログラムからの相対指定を調べる
|
|
415
437
|
const pathRelative = path.resolve(path.dirname(filename))
|
|
416
|
-
const fileRelative =
|
|
438
|
+
const fileRelative = fCheckEx(pathRelative)
|
|
417
439
|
if (fileRelative) { return fileRelative }
|
|
440
|
+
return '' // 相対パスの場合も別のフォルダは調べない
|
|
418
441
|
}
|
|
419
|
-
//
|
|
442
|
+
// plugin_xxx.mjs のようにファイル名のみが指定された場合のみ、いくつかのパスを調べる
|
|
443
|
+
// 母艦パス(元ファイルと同じフォルダ)か?
|
|
420
444
|
const pathScript = path.resolve(path.dirname(filename))
|
|
421
|
-
const fileScript =
|
|
445
|
+
const fileScript = fCheckEx(pathScript)
|
|
422
446
|
if (fileScript) { return fileScript }
|
|
423
447
|
|
|
424
448
|
// ランタイムパス/src
|
|
425
|
-
const pathRuntimeSrc = path.resolve(srcDir)
|
|
449
|
+
const pathRuntimeSrc = path.resolve(srcDir) // cnako3mod.mjs は ランタイム/src に配置されていることが前提
|
|
426
450
|
const fileRuntimeSrc = fCheck(pathRuntimeSrc)
|
|
427
451
|
if (fileRuntimeSrc) { return fileRuntimeSrc }
|
|
452
|
+
|
|
428
453
|
// ランタイムパス
|
|
429
|
-
const pathRuntime = path.dirname(
|
|
430
|
-
const fileRuntime =
|
|
454
|
+
const pathRuntime = path.resolve(path.dirname(srcDir))
|
|
455
|
+
const fileRuntime = fCheckEx(pathRuntime)
|
|
431
456
|
if (fileRuntime) { return fileRuntime }
|
|
432
457
|
|
|
433
458
|
// 環境変数 NAKO_HOMEか?
|
|
434
459
|
if (process.env.NAKO_HOME) {
|
|
435
460
|
const NAKO_HOME = path.resolve(process.env.NAKO_HOME)
|
|
436
|
-
const fileHome =
|
|
461
|
+
const fileHome = fCheckEx(NAKO_HOME)
|
|
437
462
|
if (fileHome) { return fileHome }
|
|
438
463
|
// NAKO_HOME/src ?
|
|
439
464
|
const pathNakoHomeSrc = path.join(NAKO_HOME, 'src')
|
|
@@ -443,10 +468,10 @@ export class CNako3 extends NakoCompiler {
|
|
|
443
468
|
// 環境変数 NODE_PATH (global) 以下にあるか?
|
|
444
469
|
if (process.env.NODE_PATH) {
|
|
445
470
|
const pathNode = path.resolve(process.env.NODE_PATH)
|
|
446
|
-
const fileNode =
|
|
471
|
+
const fileNode = fCheckEx(pathNode)
|
|
447
472
|
if (fileNode) { return fileNode }
|
|
448
473
|
}
|
|
449
|
-
// Node
|
|
450
|
-
return
|
|
474
|
+
// Nodeのパス検索には任せない(importで必ず失敗するので)
|
|
475
|
+
return ''
|
|
451
476
|
}
|
|
452
477
|
}
|
package/src/nako3.mjs
CHANGED
|
@@ -113,7 +113,7 @@ export class NakoCompiler {
|
|
|
113
113
|
this.logger = new NakoLogger()
|
|
114
114
|
|
|
115
115
|
// 必要なオブジェクトを覚えておく
|
|
116
|
-
this.prepare =
|
|
116
|
+
this.prepare = NakoPrepare.getInstance(this.logger)
|
|
117
117
|
this.parser = new NakoParser(this.logger)
|
|
118
118
|
this.lexer = new NakoLexer(this.logger)
|
|
119
119
|
|
|
@@ -201,7 +201,12 @@ export class NakoCompiler {
|
|
|
201
201
|
const inner = (code, filename, preCode) => {
|
|
202
202
|
/** @type {Promise<unknown>[]} */
|
|
203
203
|
const tasks = []
|
|
204
|
-
|
|
204
|
+
// 取り込みが必要な情報一覧を調べる(トークン分割して、取り込みタグを得る)
|
|
205
|
+
const tags = NakoCompiler.listRequireStatements(compiler.rawtokenize(code, 0, filename, preCode))
|
|
206
|
+
// パスを解決する
|
|
207
|
+
const tagsResolvePath = tags.map((v) => ({ ...v, ...tools.resolvePath(v.value, v.firstToken) }))
|
|
208
|
+
// 取り込み開始
|
|
209
|
+
for (const item of tagsResolvePath) {
|
|
205
210
|
// 2回目以降の読み込み
|
|
206
211
|
// eslint-disable-next-line no-prototype-builtins
|
|
207
212
|
if (dependencies.hasOwnProperty(item.filePath)) {
|
|
@@ -233,7 +238,7 @@ export class NakoCompiler {
|
|
|
233
238
|
// シンタックスハイライトの高速化のために、事前にファイルが定義する関数名のリストを取り出しておく。
|
|
234
239
|
// preDefineFuncはトークン列に変更を加えるため、事前にクローンしておく。
|
|
235
240
|
// 「プラグイン名設定」を行う (#956)
|
|
236
|
-
code =
|
|
241
|
+
code = `『${item.filePath}』にプラグイン名設定;` + code + ';『メイン』にプラグイン名設定;'
|
|
237
242
|
const tokens = this.rawtokenize(code, 0, item.filePath)
|
|
238
243
|
dependencies[item.filePath].tokens = tokens
|
|
239
244
|
/** @type {import('./nako_lexer').FuncList} */
|
|
@@ -250,7 +255,7 @@ export class NakoCompiler {
|
|
|
250
255
|
tasks.push(content.value.then((res) => registerFile(res)))
|
|
251
256
|
}
|
|
252
257
|
} else {
|
|
253
|
-
throw new NakoImportError(
|
|
258
|
+
throw new NakoImportError(`ファイル『${item.value}』を読み込めません。ファイルが存在しないか未対応の拡張子です。`, item.firstToken.file, item.firstToken.line)
|
|
254
259
|
}
|
|
255
260
|
}
|
|
256
261
|
|
|
@@ -264,15 +269,20 @@ export class NakoCompiler {
|
|
|
264
269
|
|
|
265
270
|
// 非同期な場合のエラーハンドリング
|
|
266
271
|
if (result !== undefined) {
|
|
267
|
-
result.catch((err) => {
|
|
272
|
+
result.catch((err) => {
|
|
273
|
+
// 読み込みに失敗しても処理は続ける方針なので、失敗しても例外は投げない
|
|
274
|
+
// たぶん、その後の構文解析でエラーになるため
|
|
275
|
+
this.logger.warn(err.msg)
|
|
276
|
+
})
|
|
268
277
|
}
|
|
269
278
|
|
|
270
279
|
// すべてが終わってからthis.dependenciesに代入する。そうしないと、「実行」ボタンを連打した場合など、
|
|
271
280
|
// loadDependencies() が並列実行されるときに正しく動作しない。
|
|
272
281
|
this.dependencies = dependencies
|
|
273
282
|
return result
|
|
274
|
-
} catch (err) {
|
|
275
|
-
|
|
283
|
+
} catch (err) {
|
|
284
|
+
// 同期処理では素直に例外を投げる
|
|
285
|
+
this.logger.warn(err.msg)
|
|
276
286
|
throw err
|
|
277
287
|
}
|
|
278
288
|
}
|
|
@@ -443,9 +453,7 @@ export class NakoCompiler {
|
|
|
443
453
|
}
|
|
444
454
|
const filePath = Object.keys(this.dependencies).find((key) => this.dependencies[key].alias.has(r.value))
|
|
445
455
|
if (filePath === undefined) {
|
|
446
|
-
|
|
447
|
-
// TODO: エラーが出るが、無視しても
|
|
448
|
-
// throw new NakoLexerError(`ファイル ${r.value} が読み込まれていません。`, r.firstToken.startOffset, r.firstToken.endOffset, r.firstToken.line, r.firstToken.file)
|
|
456
|
+
throw new NakoLexerError(`ファイル ${r.value} が読み込まれていません。`, r.firstToken.startOffset, r.firstToken.endOffset, r.firstToken.line, r.firstToken.file)
|
|
449
457
|
}
|
|
450
458
|
this.dependencies[filePath].addPluginFile()
|
|
451
459
|
const children = cloneAsJSON(this.dependencies[filePath].tokens)
|
package/src/nako_errors.mjs
CHANGED
|
@@ -155,10 +155,10 @@ export class NakoRuntimeError extends NakoError {
|
|
|
155
155
|
export class NakoImportError extends NakoError {
|
|
156
156
|
/**
|
|
157
157
|
* @param {string} msg
|
|
158
|
-
* @param {number} line
|
|
159
158
|
* @param {string} file
|
|
159
|
+
* @param {number} line
|
|
160
160
|
*/
|
|
161
|
-
constructor (msg,
|
|
161
|
+
constructor (msg, file, line) {
|
|
162
162
|
super('取り込みエラー', msg, file, line)
|
|
163
163
|
this.file = file
|
|
164
164
|
this.line = line
|
package/src/nako_from_dncl.mjs
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* DNCLに対応する構文
|
|
3
3
|
*/
|
|
4
4
|
// import { NakoIndentError } from './nako_errors.mjs'
|
|
5
|
-
import { NakoPrepare } from './nako_prepare.mjs'
|
|
5
|
+
import { NakoPrepare, checkNakoMode } from './nako_prepare.mjs'
|
|
6
6
|
|
|
7
|
+
// DNCLモードのキーワード
|
|
7
8
|
const DNCL_KEYWORDS = ['!DNCLモード']
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -16,7 +17,7 @@ export function convertDNCL(src, filename) {
|
|
|
16
17
|
// 改行を合わせる
|
|
17
18
|
src = src.replace(/(\r\n|\r)/g, '\n')
|
|
18
19
|
// 「!DNCLモード」を使うかチェック
|
|
19
|
-
if (!
|
|
20
|
+
if (!checkNakoMode(src, DNCL_KEYWORDS)) { return src }
|
|
20
21
|
let result = dncl2nako(src, filename)
|
|
21
22
|
// console.log("=====\n" + result)
|
|
22
23
|
// process.exit()
|
|
@@ -218,7 +219,7 @@ function dncl2nako(src, filename) {
|
|
|
218
219
|
* @returns {string} converted source
|
|
219
220
|
*/
|
|
220
221
|
function conv2half(src) {
|
|
221
|
-
const prepare =
|
|
222
|
+
const prepare = NakoPrepare.getInstance(null) // `※`, `//`, `/*` といったパターン全てに対応するために必要
|
|
222
223
|
// 全角半角の統一
|
|
223
224
|
let result = ''
|
|
224
225
|
let flagStr = false
|
package/src/nako_indent.mjs
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { NakoIndentError } from './nako_errors.mjs'
|
|
2
|
-
import { NakoPrepare } from './nako_prepare.mjs'
|
|
2
|
+
import { NakoPrepare, checkNakoMode } from './nako_prepare.mjs'
|
|
3
|
+
|
|
4
|
+
// インデント構文のキーワード
|
|
5
|
+
const INDENT_MODE_KEYWORDS = ['!インデント構文', '!ここまでだるい']
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
8
|
* インデント構文指定があればコードを変換する
|
|
@@ -8,34 +11,25 @@ import { NakoPrepare } from './nako_prepare.mjs'
|
|
|
8
11
|
* @returns {{ code: string, insertedLines: number[], deletedLines: { lineNumber: number, len: number }[] }}
|
|
9
12
|
*/
|
|
10
13
|
function convert (code, filename) {
|
|
11
|
-
//
|
|
12
|
-
if (
|
|
14
|
+
// インデント構文の適用が必要か?
|
|
15
|
+
if (checkNakoMode(code, INDENT_MODE_KEYWORDS)) {
|
|
13
16
|
return convertGo(code, filename)
|
|
14
17
|
}
|
|
15
18
|
return { code, insertedLines: [], deletedLines: [] }
|
|
16
19
|
}
|
|
17
20
|
|
|
18
|
-
// ありえない改行マークを定義
|
|
19
|
-
const SpecialRetMark = '🌟🌟改行🌟🌟s4j#WjcSb😀/FcX3🌟🌟'
|
|
20
|
-
|
|
21
21
|
/**
|
|
22
|
+
* インデント構文指定があるかチェックする
|
|
22
23
|
* @param {string} code
|
|
23
24
|
* @returns {boolean}
|
|
24
25
|
*/
|
|
25
|
-
function isIndentSyntaxEnabled
|
|
26
|
-
|
|
27
|
-
const keywords = ['!インデント構文', '!ここまでだるい']
|
|
28
|
-
const lines = code.split('\n', 30)
|
|
29
|
-
for (const line of lines) {
|
|
30
|
-
const sline = line.replace(/^(!|💡)/, '!')
|
|
31
|
-
const s9 = sline.substring(0, 8)
|
|
32
|
-
if (keywords.indexOf(s9) >= 0) {
|
|
33
|
-
return true
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return false
|
|
26
|
+
function isIndentSyntaxEnabled(code) {
|
|
27
|
+
return checkNakoMode(code, INDENT_MODE_KEYWORDS)
|
|
37
28
|
}
|
|
38
29
|
|
|
30
|
+
// ありえない改行マークを定義
|
|
31
|
+
const SpecialRetMark = '🌟🌟改行🌟🌟s4j#WjcSb😀/FcX3🌟🌟'
|
|
32
|
+
|
|
39
33
|
/**
|
|
40
34
|
* ソースコードのある1行の中のコメントを全て取り除く。
|
|
41
35
|
* 事前にreplaceRetMarkによって文字列や範囲コメント内の改行文字が置換されている必要がある。
|
|
@@ -43,7 +37,7 @@ function isIndentSyntaxEnabled (code) {
|
|
|
43
37
|
* @return {string}
|
|
44
38
|
*/
|
|
45
39
|
function removeCommentsFromLine (src) {
|
|
46
|
-
const prepare =
|
|
40
|
+
const prepare = NakoPrepare.getInstance(null) // `※`, `//`, `/*` といったパターン全てに対応するために必要
|
|
47
41
|
const len = src.length
|
|
48
42
|
let result = ''
|
|
49
43
|
let eos = ''
|
|
@@ -211,9 +205,10 @@ function convertGo (code, filename) {
|
|
|
211
205
|
// |
|
|
212
206
|
lastIndent = indent
|
|
213
207
|
while (indentStack.length > 0) {
|
|
208
|
+
/** @type {number} */
|
|
214
209
|
const n = indentStack.pop()
|
|
215
210
|
if (n === indent) {
|
|
216
|
-
if (lineTrimed.
|
|
211
|
+
if (lineTrimed.substring(0, 3) !== '違えば') {
|
|
217
212
|
insertedLines.push(lines2.length)
|
|
218
213
|
lines2.push(makeIndent(n) + END)
|
|
219
214
|
}
|
|
@@ -263,6 +258,11 @@ function convertGo (code, filename) {
|
|
|
263
258
|
return { code: lines3.join('\n'), insertedLines, deletedLines }
|
|
264
259
|
}
|
|
265
260
|
|
|
261
|
+
/**
|
|
262
|
+
* count分だけ字下げする
|
|
263
|
+
* @param {number} count
|
|
264
|
+
* @returns {string}
|
|
265
|
+
*/
|
|
266
266
|
function makeIndent (count) {
|
|
267
267
|
let s = ''
|
|
268
268
|
for (let i = 0; i < count; i++) {
|
|
@@ -311,8 +311,12 @@ function countIndent (line) {
|
|
|
311
311
|
return cnt
|
|
312
312
|
}
|
|
313
313
|
|
|
314
|
+
/**
|
|
315
|
+
* @param {string} src
|
|
316
|
+
* @returns {string}
|
|
317
|
+
*/
|
|
314
318
|
function replaceRetMark (src) {
|
|
315
|
-
const prepare =
|
|
319
|
+
const prepare = NakoPrepare.getInstance(null) // `※`, `//`, `/*` といったパターン全てに対応するために必要
|
|
316
320
|
const len = src.length
|
|
317
321
|
let result = ''
|
|
318
322
|
let eos = ''
|
|
@@ -477,5 +481,5 @@ export default {
|
|
|
477
481
|
getBlockStructure,
|
|
478
482
|
getIndent,
|
|
479
483
|
countIndent,
|
|
480
|
-
isIndentSyntaxEnabled
|
|
484
|
+
isIndentSyntaxEnabled,
|
|
481
485
|
}
|
package/src/nako_prepare.mjs
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
/**
|
|
8
8
|
* 置換後の位置から置換前の位置へマッピングできる文字列
|
|
9
9
|
*/
|
|
10
|
-
class Replace {
|
|
10
|
+
export class Replace {
|
|
11
11
|
/**
|
|
12
12
|
* @param {string} code
|
|
13
13
|
*/
|
|
@@ -65,15 +65,33 @@ class Replace {
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
/** @type {NakoPrepare} */
|
|
69
|
+
let nakoPrepareObj = null
|
|
70
|
+
/**
|
|
71
|
+
* 字句解析を行う前に全角文字を半角に揃える
|
|
72
|
+
* [memo]
|
|
73
|
+
* ただし、文字列部分だけは、そのまま全角で出力するようにする
|
|
74
|
+
* for https://github.com/kujirahand/nadesiko3/issues/94
|
|
75
|
+
*/
|
|
71
76
|
export class NakoPrepare {
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 唯一のインスタンスを返す
|
|
80
|
+
* @param {import("./nako_logger.mjs") | null} logger
|
|
81
|
+
* @returns {NakoPrepare}
|
|
82
|
+
*/
|
|
83
|
+
static getInstance(logger) {
|
|
84
|
+
if (nakoPrepareObj === null) {
|
|
85
|
+
nakoPrepareObj = new NakoPrepare(logger)
|
|
86
|
+
}
|
|
87
|
+
return nakoPrepareObj
|
|
88
|
+
}
|
|
89
|
+
|
|
72
90
|
/**
|
|
73
|
-
* @param {import("./nako_logger")} logger
|
|
91
|
+
* @param {import("./nako_logger.mjs") | null} logger
|
|
74
92
|
*/
|
|
75
93
|
constructor (logger) {
|
|
76
|
-
this.logger = logger
|
|
94
|
+
if (logger !== null) { this.logger = logger }
|
|
77
95
|
|
|
78
96
|
// 参考) https://hydrocul.github.io/wiki/blog/2014/1101-hyphen-minus-wave-tilde.html
|
|
79
97
|
this.HYPHENS = { // ハイフン問題
|
|
@@ -294,3 +312,24 @@ export class NakoPrepare {
|
|
|
294
312
|
}
|
|
295
313
|
}
|
|
296
314
|
|
|
315
|
+
/**
|
|
316
|
+
* なでしこのソースコードのモード(!インデント構文など)が設定されているか調べる
|
|
317
|
+
* @param {string} code
|
|
318
|
+
* @param {Array<string>} modeNames
|
|
319
|
+
* @returns {boolean}
|
|
320
|
+
*/
|
|
321
|
+
export function checkNakoMode(code, modeNames) {
|
|
322
|
+
// 先頭の256文字について調べる
|
|
323
|
+
code = code.substring(0, 256)
|
|
324
|
+
// 全角半角の揺れを吸収
|
|
325
|
+
code = code.replace(/(!|💡)/, '!')
|
|
326
|
+
// 範囲コメントを削除
|
|
327
|
+
code = code.replace(/\/\*.*?\*\//g, '')
|
|
328
|
+
// 毎文調べる
|
|
329
|
+
const lines = code.split(/[;。\n]/, 30)
|
|
330
|
+
for (let line of lines) {
|
|
331
|
+
line = line.replace(/^\s+/, '').replace(/\s+$/, '') // trim
|
|
332
|
+
if (modeNames.indexOf(line) >= 0) return true
|
|
333
|
+
}
|
|
334
|
+
return false
|
|
335
|
+
}
|
package/src/nako_version.mjs
CHANGED
package/src/plugin_keigo.mjs
CHANGED
package/src/wnako3.mjs
CHANGED
|
@@ -18,7 +18,7 @@ class WebNakoCompiler extends NakoCompiler {
|
|
|
18
18
|
/**
|
|
19
19
|
* ブラウザでtype="なでしこ"というスクリプトを得て実行する
|
|
20
20
|
*/
|
|
21
|
-
runNakoScript () {
|
|
21
|
+
async runNakoScript () {
|
|
22
22
|
// スクリプトタグの中身を得る
|
|
23
23
|
let nakoScriptCount = 0
|
|
24
24
|
const scripts = document.querySelectorAll('script')
|
|
@@ -26,7 +26,14 @@ class WebNakoCompiler extends NakoCompiler {
|
|
|
26
26
|
const script = scripts[i]
|
|
27
27
|
if (script.type.match(NAKO_SCRIPT_RE)) {
|
|
28
28
|
nakoScriptCount++
|
|
29
|
-
|
|
29
|
+
// URLからスクリプト名を見つける
|
|
30
|
+
const url = (typeof(window.location) == 'object') ? window.location.href : 'url_unknown'
|
|
31
|
+
const fname = `${url}#script${nakoScriptCount}.nako3`
|
|
32
|
+
const code = script.text
|
|
33
|
+
// 依存するライブラリをロード
|
|
34
|
+
await this.loadDependencies(code, fname)
|
|
35
|
+
// プログラムを実行
|
|
36
|
+
this.run(script.text, fname)
|
|
30
37
|
}
|
|
31
38
|
}
|
|
32
39
|
console.log('実行したなでしこの個数=', nakoScriptCount)
|
|
@@ -59,11 +66,11 @@ class WebNakoCompiler extends NakoCompiler {
|
|
|
59
66
|
value: (async () => {
|
|
60
67
|
const res = await fetch(filePath)
|
|
61
68
|
if (!res.ok) {
|
|
62
|
-
throw new NakoImportError(
|
|
69
|
+
throw new NakoImportError(`ファイル『${filePath}』のダウンロードに失敗しました: ${res.status} ${res.statusText}`, token.file, token.line)
|
|
63
70
|
}
|
|
64
71
|
const text = await res.text()
|
|
65
72
|
if (!text.includes('navigator.nako3.addPluginObject')) {
|
|
66
|
-
throw new NakoImportError(`ファイル ${filePath} の中に文字列 "navigator.nako3.addPluginObject" が存在しません。現在、ブラウザ版のなでしこ言語v3は自動登録するプラグインのみをサポートしています。`, token.
|
|
73
|
+
throw new NakoImportError(`ファイル ${filePath} の中に文字列 "navigator.nako3.addPluginObject" が存在しません。現在、ブラウザ版のなでしこ言語v3は自動登録するプラグインのみをサポートしています。`, token.file, token.line)
|
|
67
74
|
}
|
|
68
75
|
// textの例: `navigator.nako3.addPluginObject('PluginRequireTest', { requiretest: { type: 'var', value: 100 } })`
|
|
69
76
|
return () => {
|
|
@@ -74,7 +81,7 @@ class WebNakoCompiler extends NakoCompiler {
|
|
|
74
81
|
// eslint-disable-next-line no-new-func
|
|
75
82
|
Function(text)()
|
|
76
83
|
} catch (err) {
|
|
77
|
-
throw new NakoImportError(`プラグイン ${filePath} の取り込みに失敗: ${err instanceof Error ? err.message : err + ''}`, token.
|
|
84
|
+
throw new NakoImportError(`プラグイン ${filePath} の取り込みに失敗: ${err instanceof Error ? err.message : err + ''}`, token.file, token.line)
|
|
78
85
|
} finally {
|
|
79
86
|
navigator.nako3 = globalNako3
|
|
80
87
|
}
|
|
@@ -93,7 +100,7 @@ class WebNakoCompiler extends NakoCompiler {
|
|
|
93
100
|
value: (async () => {
|
|
94
101
|
const res = await fetch(filePath)
|
|
95
102
|
if (!res.ok) {
|
|
96
|
-
throw new NakoImportError(`ファイル ${filePath} のダウンロードに失敗しました: ${res.status} ${res.statusText}`, token.
|
|
103
|
+
throw new NakoImportError(`ファイル ${filePath} のダウンロードに失敗しました: ${res.status} ${res.statusText}`, token.file, token.line)
|
|
97
104
|
}
|
|
98
105
|
return await res.text()
|
|
99
106
|
})()
|
|
@@ -114,17 +121,20 @@ class WebNakoCompiler extends NakoCompiler {
|
|
|
114
121
|
const href = href_dir + '/' + name
|
|
115
122
|
pathname = new URL(href).pathname
|
|
116
123
|
} catch (e) {
|
|
117
|
-
throw new NakoImportError(`取り込み文の引数でパスが解決できません。https:// か http:// で始まるアドレスを指定してください。\n${e}`, token.
|
|
124
|
+
throw new NakoImportError(`取り込み文の引数でパスが解決できません。https:// か http:// で始まるアドレスを指定してください。\n${e}`, token.file, token.line)
|
|
118
125
|
}
|
|
119
126
|
}
|
|
120
127
|
}
|
|
121
|
-
|
|
128
|
+
// .js および .mjs なら JSプラグイン
|
|
129
|
+
if (pathname.endsWith('.js') || pathname.endsWith('.js.txt') || pathname.endsWith('.mjs') || pathname.endsWith('.mjs.txt')) {
|
|
122
130
|
return { filePath: name, type: 'js' }
|
|
123
131
|
}
|
|
132
|
+
// .nako3 なら なでしこ3プラグイン
|
|
124
133
|
if (pathname.endsWith('.nako3') || pathname.endsWith('.nako3.txt')) {
|
|
125
134
|
return { filePath: name, type: 'nako3' }
|
|
126
135
|
}
|
|
127
|
-
|
|
136
|
+
// ファイル拡張子が未指定の場合
|
|
137
|
+
throw new NakoImportError(`ファイル『${name}』は拡張子が(.nako3|.js|.js.txt|.mjs|.mjs.txt)以外なので取り込めません。`, token.file, token.line)
|
|
128
138
|
}
|
|
129
139
|
})
|
|
130
140
|
}
|
package/src/wnako3_editor.mjs
CHANGED
|
@@ -723,7 +723,7 @@ export class LanguageFeatures {
|
|
|
723
723
|
* @param {number} endRow
|
|
724
724
|
*/
|
|
725
725
|
static toggleCommentLines (state, { doc }, startRow, endRow) {
|
|
726
|
-
const prepare =
|
|
726
|
+
const prepare = NakoPrepare.getInstance(new NakoLogger())
|
|
727
727
|
/**
|
|
728
728
|
* @param {string} line
|
|
729
729
|
* @returns {{ type: 'blank' | 'code' } | { type: 'comment', start: number, len: number }}
|
|
@@ -734,11 +734,11 @@ export class LanguageFeatures {
|
|
|
734
734
|
if (indent === line) {
|
|
735
735
|
return { type: 'blank' }
|
|
736
736
|
}
|
|
737
|
-
line = line.
|
|
737
|
+
line = line.substring(indent.length)
|
|
738
738
|
|
|
739
739
|
// 先頭がコメントの開始文字かどうか確認する
|
|
740
|
-
const ch2 = line.
|
|
741
|
-
if (ch2.
|
|
740
|
+
const ch2 = line.substring(0, 2).split('').map((c) => prepare.convert1ch(c)).join('')
|
|
741
|
+
if (ch2.substring(0, 1) === '#') {
|
|
742
742
|
return { type: 'comment', start: indent.length, len: 1 + (line.charAt(1) === ' ' ? 1 : 0) }
|
|
743
743
|
}
|
|
744
744
|
if (ch2 === '//') {
|