nadesiko3 3.2.45 → 3.2.48

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/src/nako_gen.js CHANGED
@@ -40,14 +40,23 @@ class NakoGen {
40
40
  // async method
41
41
  if (gen.numAsyncFn > 0) {
42
42
  let canAsync = true
43
- if (window && window.navigator && window.navigator.userAgent) {
43
+ if (typeof(window) == 'object' && window.navigator && window.navigator.userAgent) {
44
44
  const ua = window.navigator.userAgent
45
45
  canAsync = (ua.indexOf('MSIE') === -1)
46
46
  }
47
47
  if (canAsync) {
48
- js = '// <nadesiko3::gen::async>\n' +
49
- `(async () => {\n${js}\n})();\n` +
50
- '// </nadesiko3::gen::async>\n'
48
+ js = `
49
+ // <nadesiko3::gen::async>
50
+ (async () => { // async::main
51
+ ${js}
52
+ }).call(this).catch(err => {
53
+ if (!(err instanceof this.NakoRuntimeError)) {
54
+ err = new this.NakoRuntimeError(err, this.__varslist[0].line);
55
+ }
56
+ this.logger.error(err);
57
+ throw err;
58
+ }); // async::main
59
+ // <nadesiko3::gen::async>\n`
51
60
  }
52
61
  }
53
62
 
@@ -1218,7 +1227,7 @@ try {
1218
1227
 
1219
1228
  // 関数呼び出しで、引数の末尾にthisを追加する-システム情報を参照するため
1220
1229
  args.push('__self')
1221
- const funcDef = 'function'
1230
+ let funcDef = 'function'
1222
1231
  let funcBegin = ''
1223
1232
  let funcEnd = ''
1224
1233
  // setter?
@@ -1292,6 +1301,7 @@ try {
1292
1301
 
1293
1302
  let funcCall = `${res.js}(${argsCode})`
1294
1303
  if (func.asyncFn) {
1304
+ funcDef = `async ${funcDef}`
1295
1305
  funcCall = `await ${funcCall}`
1296
1306
  this.numAsyncFn++
1297
1307
  }
@@ -1325,6 +1335,7 @@ try {
1325
1335
 
1326
1336
  let code = ''
1327
1337
  if (func.return_none) {
1338
+ // 戻り値のない関数の場合
1328
1339
  if (funcEnd === '') {
1329
1340
  if (funcBegin === '') {
1330
1341
  code = `${funcCall};\n`
@@ -1335,6 +1346,7 @@ try {
1335
1346
  code = `${funcBegin}try {\n${indent(funcCall, 1)};\n} finally {\n${indent(funcEnd, 1)}}\n`
1336
1347
  }
1337
1348
  } else {
1349
+ // 戻り値のある関数の場合
1338
1350
  let sorePrefex = ''
1339
1351
  if (this.speedMode.invalidSore === 0) {
1340
1352
  sorePrefex = `${this.varname('それ')} = `
@@ -1,5 +1,5 @@
1
1
  const NakoColors = require('./nako_colors')
2
-
2
+ const {NakoRuntimeError} = require('./nako_errors')
3
3
  /**
4
4
  * コンパイルされたなでしこのプログラムで、グローバル空間のthisが指すオブジェクト
5
5
  */
@@ -23,6 +23,7 @@ class NakoGlobal {
23
23
  this.__stack = []
24
24
  this.__labels = []
25
25
  this.__genMode = gen.genMode
26
+ this.NakoRuntimeError = NakoRuntimeError
26
27
 
27
28
  // PluginSystemとdestroy()から参照するため
28
29
  this.__module = { ...compiler.__module } // shallow copy
@@ -2,7 +2,7 @@
2
2
  * なでしこ3字句解析のためのルール
3
3
  */
4
4
 
5
- const kanakanji = /^[\u3005\u4E00-\u9FCF_a-zA-Z0-9ァ-ヶー]+/
5
+ const kanakanji = /^[\u3005\u4E00-\u9FCF_a-zA-Z0-9ァ-ヶー\u2460-\u24FF\u2776-\u277F\u3251-\u32BF]+/
6
6
  const nakoJosiList = require('./nako_josi_list')
7
7
  const josiRE = nakoJosiList.josiRE
8
8
  const removeJosiMap = nakoJosiList.removeJosiMap
@@ -53,9 +53,9 @@ module.exports = {
53
53
  { name: 'lteq', pattern: /^(≦|<=|=<)/ },
54
54
  { name: 'noteq', pattern: /^(≠|<>|!=)/ },
55
55
  { name: '←', pattern: /^(←|<--)/ }, // 関数呼び出し演算子 #891 #899
56
- { name: 'eq', pattern: /^=/ },
56
+ { name: 'eq', pattern: /^(=|🟰)/ },
57
57
  { name: 'line_comment', pattern: /^!(インデント構文|ここまでだるい)[^\n]*/ },
58
- { name: 'not', pattern: /^!/ },
58
+ { name: 'not', pattern: /^(!|💡)/ }, // #1184
59
59
  { name: 'gt', pattern: /^>/ },
60
60
  { name: 'lt', pattern: /^</ },
61
61
  { name: 'and', pattern: /^(かつ|&&)/ },
@@ -96,7 +96,7 @@ module.exports = {
96
96
  // 単語句
97
97
  {
98
98
  name: 'word',
99
- pattern: /^[_a-zA-Z\u3005\u4E00-\u9FCFぁ-んァ-ヶ]/,
99
+ pattern: /^[_a-zA-Z\u3005\u4E00-\u9FCFぁ-んァ-ヶ\u2460-\u24FF\u2776-\u277F\u3251-\u32BF]/,
100
100
  cbParser: cbWordParser
101
101
  }
102
102
  ],
@@ -551,13 +551,13 @@ class NakoParser extends NakoParserBase {
551
551
  }
552
552
  }
553
553
 
554
- /** @returns {Ast | null} */
555
- yGetArg () {
556
- // 値を一つ読む
557
- const value1 = this.yValue()
558
- if (value1 === null) { return null }
559
- // 計算式がある場合を考慮
560
- const args = [value1]
554
+ /**
555
+ * 1つ目の値を与え、その後に続く計算式を取得し、優先規則に沿って並び替えして戻す
556
+ * @param {Ast | null} firstValue
557
+ * @returns {Ast | null}
558
+ */
559
+ yGetArgOperator (firstValue) {
560
+ const args = [firstValue]
561
561
  while (!this.isEOF()) {
562
562
  // 演算子がある?
563
563
  const op = this.peek()
@@ -568,7 +568,7 @@ class NakoParser extends NakoParserBase {
568
568
  if (v === null) {
569
569
  throw NakoSyntaxError.fromNode(
570
570
  `計算式で演算子『${op.value}』後に値がありません`,
571
- value1)
571
+ firstValue)
572
572
  }
573
573
  args.push(v)
574
574
  continue
@@ -580,6 +580,15 @@ class NakoParser extends NakoParserBase {
580
580
  return this.infixToAST(args)
581
581
  }
582
582
 
583
+ /** @returns {Ast | null} */
584
+ yGetArg () {
585
+ // 値を一つ読む
586
+ const value1 = this.yValue()
587
+ if (value1 === null) { return null }
588
+ // 計算式がある場合を考慮
589
+ return this.yGetArgOperator(value1)
590
+ }
591
+
583
592
  infixToPolish (list) {
584
593
  // 中間記法から逆ポーランドに変換
585
594
  const priority = (t) => {
@@ -832,8 +841,11 @@ class NakoParser extends NakoParserBase {
832
841
  yReturn () {
833
842
  const map = this.peekSourceMap()
834
843
  if (!this.check('戻る')) { return null }
835
- this.get() // skip '戻る'
844
+ const modoru = this.get() // skip '戻る'
836
845
  const v = this.popStack(['で', 'を'])
846
+ if (this.stack.length > 0) {
847
+ throw NakoSyntaxError.fromNode('『戻』文の直前に未解決の引数があります。『(式)を戻す』のように式をカッコで括ってください。', modoru)
848
+ }
837
849
  return {
838
850
  type: 'return',
839
851
  value: v,
@@ -1101,7 +1113,11 @@ class NakoParser extends NakoParserBase {
1101
1113
  this.pushStack(r)
1102
1114
  continue
1103
1115
  }
1104
- return r
1116
+ // 関数呼び出しの直後に、四則演算があるか?
1117
+ if (!this.checkTypes(operatorList)) { return r } // なければ関数呼び出しを戻す
1118
+ // 四則演算があった場合、計算してスタックに載せる
1119
+ this.pushStack(this.yGetArgOperator(r))
1120
+ continue
1105
1121
  }
1106
1122
  // 値のとき → スタックに載せる
1107
1123
  const t = this.yGetArg()
@@ -122,7 +122,11 @@ class NakoPrepare {
122
122
  0x3011: ']', // '】'
123
123
  // 読点は「,」に変換する (#877)
124
124
  0x3001: ',', // 読点 --- JSON記法で「,」と「、」を区別したいので読点は変換しないことに。(#276)
125
- 0xFF0C: ',' // 読点 ',' 論文などで利用、ただし句点はドットと被るので変換しない (#735)
125
+ 0xFF0C: ',', // 読点 ',' 論文などで利用、ただし句点はドットと被るので変換しない (#735)
126
+ 0x2716: '*', // ×の絵文字 (#1183)
127
+ 0x2795: '+', // +の絵文字 (#1183)
128
+ 0x2796: '-', // -の絵文字 (#1183)
129
+ 0x2797: '÷' // ÷の絵文字 (#1183)
126
130
  }
127
131
  }
128
132
 
@@ -1,8 +1,8 @@
1
1
  // なでしこバージョン
2
2
  const nakoVersion = {
3
- version: '3.2.45',
3
+ version: '3.2.48',
4
4
  major: 3,
5
5
  minor: 2,
6
- patch: 45
6
+ patch: 48
7
7
  }
8
8
  module.exports = nakoVersion
@@ -380,7 +380,7 @@ module.exports = {
380
380
  const txt = await res.text()
381
381
  return txt
382
382
  },
383
- return_none: true
383
+ return_none: false
384
384
  },
385
385
  'AJAX_JSON取得': { // @AJAXでURLにアクセスしJSONの結果を得て、送信時AJAXオプションの値を参照。 // @AJAX_JSONしゅとく
386
386
  type: 'func',
@@ -394,6 +394,6 @@ module.exports = {
394
394
  const txt = await res.json()
395
395
  return txt
396
396
  },
397
- return_none: true
397
+ return_none: false
398
398
  },
399
399
  }
@@ -392,7 +392,6 @@ module.exports = {
392
392
  }
393
393
  table.appendChild(tr)
394
394
  }
395
- sys.__exec('DOM親要素設定', [table, sys])
396
395
  return table
397
396
  }
398
397
  }
@@ -902,6 +902,35 @@ const PluginNode = {
902
902
  },
903
903
  return_none: true
904
904
  },
905
+ // @新AJAX
906
+ 'AJAXテキスト取得': { // @AJAXでURLにアクセスしテキスト形式で結果を得る。送信時AJAXオプションの値を参照。 // @AJAXてきすとしゅとく
907
+ type: 'func',
908
+ josi: [['から']],
909
+ pure: true,
910
+ asyncFn: true,
911
+ fn: async function (url, sys) {
912
+ let options = sys.__v0['AJAXオプション']
913
+ if (options === '') { options = { method: 'GET' } }
914
+ const res = await fetch(url, options)
915
+ const txt = await res.text()
916
+ return txt
917
+ },
918
+ return_none: false
919
+ },
920
+ 'AJAX_JSON取得': { // @AJAXでURLにアクセスしJSONの結果を得て、送信時AJAXオプションの値を参照。 // @AJAX_JSONしゅとく
921
+ type: 'func',
922
+ josi: [['から']],
923
+ pure: true,
924
+ asyncFn: true,
925
+ fn: async function (url, sys) {
926
+ let options = sys.__v0['AJAXオプション']
927
+ if (options === '') { options = { method: 'GET' } }
928
+ const res = await fetch(url, options)
929
+ const txt = await res.json()
930
+ return txt
931
+ },
932
+ return_none: false
933
+ },
905
934
  // @文字コード
906
935
  '文字コード変換サポート判定': { // @文字コードCODEをサポートしているか確認 // @もじこーどさぽーとはんてい
907
936
  type: 'func',
@@ -194,7 +194,7 @@ const PluginSystem = {
194
194
  },
195
195
  return_none: true
196
196
  },
197
- '継続表示': { // @Sを改行なしで表示 // @けいぞくひょうじ
197
+ '継続表示': { // @Sを改行なしで表示(ただし「表示」命令を使うことで画面出力される) // @けいぞくひょうじ
198
198
  type: 'func',
199
199
  josi: [['を', 'と']],
200
200
  pure: true,
@@ -475,6 +475,15 @@ const PluginSystem = {
475
475
  },
476
476
  return_none: true
477
477
  },
478
+ '敬具': { // @ソースコードを読む人を気持ちよくする // @けいぐ
479
+ type: 'func',
480
+ josi: [],
481
+ pure: true,
482
+ fn: function (sys) {
483
+ sys.__reisetu += 100 // bonus point
484
+ },
485
+ return_none: true
486
+ },
478
487
  '礼節レベル取得': { // @(お遊び)敬語を何度使ったか返す // @おねがいします
479
488
  type: 'func',
480
489
  josi: [],
@@ -693,7 +702,7 @@ const PluginSystem = {
693
702
  return parseFloat(v)
694
703
  }
695
704
  },
696
- 'NAN判定': { // @値VがNaNかどうかを判定 // @NANはんてい
705
+ 'NAN判定': { // @値VがNaNかどうかを判定(命令『非数判定』を使う事を推奨) // @NANはんてい
697
706
  type: 'func',
698
707
  josi: [['を']],
699
708
  pure: true,
@@ -701,6 +710,15 @@ const PluginSystem = {
701
710
  return isNaN(v)
702
711
  }
703
712
  },
713
+ '非数判定': { // @値Vが非数かどうかを判定(NAN判定より堅牢) // @ひすうはんてい
714
+ type: 'func',
715
+ josi: [['を']],
716
+ pure: true,
717
+ fn: function (v) {
718
+ // https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN
719
+ return Number.isNaN(v)
720
+ }
721
+ },
704
722
  'HEX': { // @値Vを16進数に変換 // @HEX
705
723
  type: 'func',
706
724
  josi: [['の']],
@@ -136,4 +136,9 @@ describe('calc_test.js', () => {
136
136
  cmp('N=「」;もし、N=0ならば「OK」と表示。', 'OK')
137
137
  cmp('N=「」;もし、N===0ならば「NG」と表示。違えば「OK」と表示。', 'OK')
138
138
  })
139
+ it('なでしこ式関数呼び出しで、途中に四則演算がある場合の処理 (#1188)', () => {
140
+ cmp('3.14のINT+2を表示。', '5')
141
+ cmp('3の5倍×2を表示', '30')
142
+ cmp('1+3の2倍×2を表示', '16')
143
+ })
139
144
  })
@@ -146,4 +146,10 @@ describe('lex_test', () => {
146
146
  it('助詞の前後に空白があるとエラーになる問題 #1079', () => {
147
147
  cmp('x=1;x と 2 と "3" を連続表示', '123')
148
148
  })
149
+ it('丸付き数字が変数名として使えない #1185', () => {
150
+ cmp('⓪=0;①=1;㊿=50;❿=10;⓪+①+㊿+❿を表示', '61')
151
+ })
152
+ it('絵文字の四則演算を認識する #1183', () => {
153
+ cmp('リンゴ🟰3✖5;ミカン🟰9➗3;リンゴ+ミカンを表示', '18')
154
+ })
149
155
  })
@@ -1,6 +1,6 @@
1
1
  const assert = require('assert')
2
2
  const CNako3 = require('../../src/cnako3')
3
-
3
+ const path = require('path')
4
4
  describe('plugin_test', () => {
5
5
  const nako = new CNako3()
6
6
  // nako.logger.addListener('trace', ({ browserConsole }) => { console.log(...browserConsole) })
@@ -11,6 +11,8 @@ describe('plugin_test', () => {
11
11
  assert.strictEqual(ret.log, res)
12
12
  }
13
13
  it('「取り込む」', () => {
14
- cmp('!「nadesiko3-hoge」を取り込む。\n3と5をHOGE足して、表示。', '8')
14
+ const plug = path.join(__dirname, '..', '..', 'src', 'plugin_keigo.js')
15
+ cmp(`!「${plug}」を取り込む。\n拝啓。お世話になっております。礼節レベル取得して表示。`, '1')
15
16
  })
16
17
  })
18
+