nadesiko3 3.3.48 → 3.3.49

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.
Files changed (105) hide show
  1. package/core/.editorconfig +6 -0
  2. package/core/.eslintrc.cjs +33 -0
  3. package/core/.github/dependabot.yml +7 -0
  4. package/core/.github/workflows/nodejs.yml +37 -0
  5. package/core/.github/workflows/super-linter.yml +61 -0
  6. package/core/.github/workflows/textlint.yml +199 -0
  7. package/core/LICENSE +21 -0
  8. package/core/README.md +66 -0
  9. package/core/batch/build_nako_version.nako3 +42 -0
  10. package/core/command/snako.mjs +105 -0
  11. package/core/command/snako.mts +116 -0
  12. package/core/index.mjs +21 -0
  13. package/core/index.mts +21 -0
  14. package/core/package.json +47 -0
  15. package/core/sample/hello.nako3 +7 -0
  16. package/core/sample/hoge.mjs +4 -0
  17. package/core/sample/hoge.mts +6 -0
  18. package/core/src/nako3.mjs +858 -0
  19. package/core/src/nako3.mts +967 -0
  20. package/core/src/nako_colors.mjs +78 -0
  21. package/core/src/nako_colors.mts +86 -0
  22. package/core/src/nako_core_version.mjs +8 -0
  23. package/core/src/nako_core_version.mts +19 -0
  24. package/core/src/nako_csv.mjs +185 -0
  25. package/core/src/nako_csv.mts +188 -0
  26. package/core/src/nako_errors.mjs +173 -0
  27. package/core/src/nako_errors.mts +197 -0
  28. package/core/src/nako_from_dncl.mjs +255 -0
  29. package/core/src/nako_from_dncl.mts +250 -0
  30. package/core/src/nako_gen.mjs +1648 -0
  31. package/core/src/nako_gen.mts +1719 -0
  32. package/core/src/nako_gen_async.mjs +1659 -0
  33. package/core/src/nako_gen_async.mts +1732 -0
  34. package/core/src/nako_global.mjs +107 -0
  35. package/core/src/nako_global.mts +138 -0
  36. package/core/src/nako_indent.mjs +445 -0
  37. package/core/src/nako_indent.mts +492 -0
  38. package/core/src/nako_josi_list.mjs +38 -0
  39. package/core/src/nako_josi_list.mts +45 -0
  40. package/core/src/nako_lex_rules.mjs +253 -0
  41. package/core/src/nako_lex_rules.mts +260 -0
  42. package/core/src/nako_lexer.mjs +609 -0
  43. package/core/src/nako_lexer.mts +612 -0
  44. package/core/src/nako_logger.mjs +199 -0
  45. package/core/src/nako_logger.mts +232 -0
  46. package/core/src/nako_parser3.mjs +2439 -0
  47. package/core/src/nako_parser3.mts +2195 -0
  48. package/core/src/nako_parser_base.mjs +370 -0
  49. package/core/src/nako_parser_base.mts +370 -0
  50. package/core/src/nako_parser_const.mjs +37 -0
  51. package/core/src/nako_parser_const.mts +37 -0
  52. package/core/src/nako_prepare.mjs +304 -0
  53. package/core/src/nako_prepare.mts +315 -0
  54. package/core/src/nako_reserved_words.mjs +38 -0
  55. package/core/src/nako_reserved_words.mts +38 -0
  56. package/core/src/nako_source_mapping.mjs +207 -0
  57. package/core/src/nako_source_mapping.mts +262 -0
  58. package/core/src/nako_test.mjs +37 -0
  59. package/core/src/nako_types.mjs +25 -0
  60. package/core/src/nako_types.mts +151 -0
  61. package/core/src/plugin_csv.mjs +49 -0
  62. package/core/src/plugin_csv.mts +50 -0
  63. package/core/src/plugin_math.mjs +328 -0
  64. package/core/src/plugin_math.mts +326 -0
  65. package/core/src/plugin_promise.mjs +91 -0
  66. package/core/src/plugin_promise.mts +91 -0
  67. package/core/src/plugin_system.mjs +2832 -0
  68. package/core/src/plugin_system.mts +2690 -0
  69. package/core/src/plugin_test.mjs +34 -0
  70. package/core/src/plugin_test.mts +34 -0
  71. package/core/test/array_test.mjs +34 -0
  72. package/core/test/basic_test.mjs +344 -0
  73. package/core/test/calc_test.mjs +140 -0
  74. package/core/test/core_module_test.mjs +23 -0
  75. package/core/test/debug_test.mjs +16 -0
  76. package/core/test/dncl_test.mjs +94 -0
  77. package/core/test/error_message_test.mjs +210 -0
  78. package/core/test/error_test.mjs +16 -0
  79. package/core/test/flow_test.mjs +373 -0
  80. package/core/test/func_call.mjs +160 -0
  81. package/core/test/func_test.mjs +149 -0
  82. package/core/test/indent_test.mjs +364 -0
  83. package/core/test/lex_test.mjs +168 -0
  84. package/core/test/literal_test.mjs +73 -0
  85. package/core/test/nako_lexer_test.mjs +35 -0
  86. package/core/test/nako_logger_test.mjs +76 -0
  87. package/core/test/nako_logger_test.mts +78 -0
  88. package/core/test/plugin_csv_test.mjs +38 -0
  89. package/core/test/plugin_promise_test.mjs +18 -0
  90. package/core/test/plugin_system_test.mjs +630 -0
  91. package/core/test/prepare_test.mjs +96 -0
  92. package/core/test/re_test.mjs +22 -0
  93. package/core/test/side_effects_test.mjs +92 -0
  94. package/core/test/variable_scope_test.mjs +149 -0
  95. package/core/tsconfig.json +101 -0
  96. package/package.json +4 -2
  97. package/release/_hash.txt +12 -12
  98. package/release/_script-tags.txt +14 -14
  99. package/release/editor.js +1 -1
  100. package/release/stats.json +1 -1
  101. package/release/version.js +1 -1
  102. package/release/wnako3.js +1 -1
  103. package/src/nako_version.mjs +2 -2
  104. package/src/nako_version.mts +2 -2
  105. package/test/async/async_basic_test.mjs +3 -3
@@ -0,0 +1,253 @@
1
+ /**
2
+ * なでしこ3字句解析のためのルール
3
+ */
4
+ import { josiRE, removeJosiMap } from './nako_josi_list.mjs';
5
+ const kanakanji = /^[\u3005\u4E00-\u9FCF_a-zA-Z0-9ァ-ヶー\u2460-\u24FF\u2776-\u277F\u3251-\u32BF]+/;
6
+ const hira = /^[ぁ-ん]/;
7
+ const allHiragana = /^[ぁ-ん]+$/;
8
+ const wordHasIjoIka = /^.+(以上|以下|超|未満)$/;
9
+ const errorRead = (ch) => {
10
+ return function () { throw new Error('突然の『' + ch + '』があります。'); };
11
+ };
12
+ export const unitRE = /^(円|ドル|元|歩|㎡|坪|度|℃|°|個|つ|本|冊|才|歳|匹|枚|皿|セット|羽|人|件|行|列|機|品|m|mm|cm|km|g|kg|t|px|dot|pt|em|b|mb|kb|gb)/;
13
+ export const rules = [
14
+ // 上から順にマッチさせていく
15
+ { name: 'ここまで', pattern: /^;;;/ },
16
+ { name: 'eol', pattern: /^\n/ },
17
+ { name: 'eol', pattern: /^;/ },
18
+ // eslint-disable-next-line no-control-regex
19
+ { name: 'space', pattern: /^(\x20|\x09|・)+/ },
20
+ { name: 'comma', pattern: /^,/ },
21
+ { name: 'line_comment', pattern: /^#[^\n]*/ },
22
+ { name: 'line_comment', pattern: /^\/\/[^\n]*/ },
23
+ { name: 'range_comment', pattern: /^\/\*/, cbParser: cbRangeComment },
24
+ { name: 'def_test', pattern: /^●テスト:/ },
25
+ { name: 'def_func', pattern: /^●/ },
26
+ // 数値の判定 --- この後nako_lexerにて単位を読む処理が入る(#994)
27
+ { name: 'number', pattern: /^0[xX][0-9a-fA-F]+(_[0-9a-fA-F]+)*/, readJosi: true, cb: parseNumber },
28
+ { name: 'number', pattern: /^0[oO][0-7]+(_[0-7]+)*/, readJosi: true, cb: parseNumber },
29
+ { name: 'number', pattern: /^0[bB][0-1]+(_[0-1]+)*/, readJosi: true, cb: parseNumber },
30
+ // 下の三つは小数点が挟まっている場合、小数点から始まっている場合、小数点がない場合の十進法の数値にマッチします
31
+ { name: 'number', pattern: /^\d+(_\d+)*\.(\d+(_\d+)*)?([eE][+|-]?\d+(_\d+)*)?/, readJosi: true, cb: parseNumber },
32
+ { name: 'number', pattern: /^\.\d+(_\d+)*([eE][+|-]?\d+(_\d+)*)?/, readJosi: true, cb: parseNumber },
33
+ { name: 'number', pattern: /^\d+(_\d+)*([eE][+|-]?\d+(_\d+)*)?/, readJosi: true, cb: parseNumber },
34
+ { name: 'ここから', pattern: /^(ここから),?/ },
35
+ { name: 'ここまで', pattern: /^(ここまで|💧)/ },
36
+ { name: 'もし', pattern: /^もしも?/ },
37
+ // 「ならば」は助詞として定義している
38
+ { name: '違えば', pattern: /^違(えば)?/ },
39
+ // 「回」「間」「繰返」「反復」「抜」「続」「戻」「代入」「条件分岐」などは NakoLexer._replaceWord で word から変換
40
+ // @see nako_reserved_words.js
41
+ { name: 'shift_r0', pattern: /^>>>/ },
42
+ { name: 'shift_r', pattern: /^>>/ },
43
+ { name: 'shift_l', pattern: /^<</ },
44
+ { name: '===', pattern: /^===/ },
45
+ { name: '!==', pattern: /^!==/ },
46
+ { name: 'gteq', pattern: /^(≧|>=|=>)/ },
47
+ { name: 'lteq', pattern: /^(≦|<=|=<)/ },
48
+ { name: 'noteq', pattern: /^(≠|<>|!=)/ },
49
+ { name: '←', pattern: /^(←|<--)/ },
50
+ { name: 'eq', pattern: /^(=|🟰)/ },
51
+ { name: 'line_comment', pattern: /^(!|💡)(インデント構文|ここまでだるい)[^\n]*/ },
52
+ { name: 'not', pattern: /^(!|💡)/ },
53
+ { name: 'gt', pattern: /^>/ },
54
+ { name: 'lt', pattern: /^</ },
55
+ { name: 'and', pattern: /^(かつ|&&)/ },
56
+ { name: 'or', pattern: /^(または|或いは|あるいは|\|\|)/ },
57
+ { name: '@', pattern: /^@/ },
58
+ { name: '+', pattern: /^\+/ },
59
+ { name: '-', pattern: /^-/ },
60
+ { name: '*', pattern: /^(×|\*)/ },
61
+ { name: '÷÷', pattern: /^÷÷/ },
62
+ { name: '÷', pattern: /^(÷|\/)/ },
63
+ { name: '%', pattern: /^%/ },
64
+ { name: '^', pattern: /^\^/ },
65
+ { name: '&', pattern: /^&/ },
66
+ { name: '[', pattern: /^\[/ },
67
+ { name: ']', pattern: /^]/, readJosi: true },
68
+ { name: '(', pattern: /^\(/ },
69
+ { name: ')', pattern: /^\)/, readJosi: true },
70
+ { name: '|', pattern: /^\|/ },
71
+ { name: 'string', pattern: /^🌿/, cbParser: src => cbString('🌿', '🌿', src) },
72
+ { name: 'string_ex', pattern: /^🌴/, cbParser: src => cbString('🌴', '🌴', src) },
73
+ { name: 'string_ex', pattern: /^「/, cbParser: src => cbString('「', '」', src) },
74
+ { name: 'string', pattern: /^『/, cbParser: src => cbString('『', '』', src) },
75
+ { name: 'string_ex', pattern: /^“/, cbParser: src => cbString('“', '”', src) },
76
+ { name: 'string_ex', pattern: /^"/, cbParser: src => cbString('"', '"', src) },
77
+ { name: 'string', pattern: /^'/, cbParser: src => cbString('\'', '\'', src) },
78
+ { name: '」', pattern: /^」/, cbParser: errorRead('」') },
79
+ { name: '』', pattern: /^』/, cbParser: errorRead('』') },
80
+ { name: 'func', pattern: /^\{関数\},?/ },
81
+ { name: '{', pattern: /^\{/ },
82
+ { name: '}', pattern: /^\}/, readJosi: true },
83
+ { name: ':', pattern: /^:/ },
84
+ { name: '_eol', pattern: /^_\s*\n/ },
85
+ { name: 'dec_lineno', pattern: /^‰/ },
86
+ // 絵文字変数 = (絵文字)英数字*
87
+ { name: 'word', pattern: /^[\uD800-\uDBFF][\uDC00-\uDFFF][_a-zA-Z0-9]*/, readJosi: true },
88
+ { name: 'word', pattern: /^[\u1F60-\u1F6F][_a-zA-Z0-9]*/, readJosi: true },
89
+ { name: 'word', pattern: /^《.+?》/, readJosi: true },
90
+ // 単語句
91
+ {
92
+ name: 'word',
93
+ pattern: /^[_a-zA-Z\u3005\u4E00-\u9FCFぁ-んァ-ヶ\u2460-\u24FF\u2776-\u277F\u3251-\u32BF]/,
94
+ cbParser: cbWordParser
95
+ }
96
+ ];
97
+ export function trimOkurigana(s) {
98
+ // ひらがなから始まらない場合、送り仮名を削除。(例)置換する
99
+ if (!hira.test(s)) {
100
+ return s.replace(/[ぁ-ん]+/g, '');
101
+ }
102
+ // 全てひらがな? (例) どうぞ
103
+ if (allHiragana.test(s)) {
104
+ return s;
105
+ }
106
+ // 末尾のひらがなのみ (例)お願いします →お願
107
+ return s.replace(/[ぁ-ん]+$/g, '');
108
+ }
109
+ // Utility for Rule
110
+ function cbRangeComment(src) {
111
+ let res = '';
112
+ const josi = '';
113
+ let numEOL = 0;
114
+ src = src.substring(2); // skip /*
115
+ const i = src.indexOf('*/');
116
+ if (i < 0) { // not found
117
+ res = src;
118
+ src = '';
119
+ }
120
+ else {
121
+ res = src.substring(0, i);
122
+ src = src.substring(i + 2);
123
+ }
124
+ // 改行を数える
125
+ for (let i = 0; i < res.length; i++) {
126
+ if (res.charAt(i) === '\n') {
127
+ numEOL++;
128
+ }
129
+ }
130
+ res = res.replace(/(^\s+|\s+$)/, ''); // trim
131
+ return { src, res, josi, numEOL };
132
+ }
133
+ /**
134
+ * @param {string} src
135
+ */
136
+ function cbWordParser(src, isTrimOkurigana = true) {
137
+ /*
138
+ kanji = [\u3005\u4E00-\u9FCF]
139
+ hiragana = [ぁ-ん]
140
+ katakana = [ァ-ヶー]
141
+ emoji = [\u1F60-\u1F6F]
142
+ uni_extra = [\uD800-\uDBFF] [\uDC00-\uDFFF]
143
+ alphabet = [_a-zA-Z]
144
+ numchars = [0-9]
145
+ */
146
+ let res = '';
147
+ let josi = '';
148
+ while (src !== '') {
149
+ if (res.length > 0) {
150
+ // 助詞の判定
151
+ const j = josiRE.exec(src);
152
+ if (j) {
153
+ josi = j[0].replace(/^\s+/, '');
154
+ src = src.substr(j[0].length);
155
+ // 助詞の直後にある「,」を飛ばす #877
156
+ if (src.charAt(0) === ',') {
157
+ src = src.substr(1);
158
+ }
159
+ break;
160
+ }
161
+ }
162
+ // カタカナ漢字英数字か?
163
+ const m = kanakanji.exec(src);
164
+ if (m) {
165
+ res += m[0];
166
+ src = src.substr(m[0].length);
167
+ continue;
168
+ }
169
+ // ひらがな?
170
+ const h = hira.test(src);
171
+ if (h) {
172
+ res += src.charAt(0);
173
+ src = src.substr(1);
174
+ continue;
175
+ }
176
+ break; // other chars
177
+ }
178
+ // 「間」の特殊ルール (#831)
179
+ // 「等しい間」や「一致する間」なら「間」をsrcに戻す。ただし「システム時間」はそのままにする。
180
+ if (/[ぁ-ん]間$/.test(res)) {
181
+ src = res.charAt(res.length - 1) + src;
182
+ res = res.slice(0, -1);
183
+ }
184
+ // 「以上」「以下」「超」「未満」 #918
185
+ const ii = wordHasIjoIka.exec(res);
186
+ if (ii) {
187
+ src = ii[1] + josi + src;
188
+ josi = '';
189
+ res = res.substr(0, res.length - ii[1].length);
190
+ }
191
+ // 助詞「こと」「である」「です」などは「**すること」のように使うので削除 #936 #939 #974
192
+ if (removeJosiMap[josi]) {
193
+ josi = '';
194
+ }
195
+ // 漢字カタカナ英語から始まる語句 --- 送り仮名を省略
196
+ if (isTrimOkurigana) {
197
+ res = trimOkurigana(res);
198
+ }
199
+ // 助詞だけの語句の場合
200
+ if (res === '' && josi !== '') {
201
+ res = josi;
202
+ josi = '';
203
+ }
204
+ return { src, res, josi, numEOL: 0 };
205
+ }
206
+ function cbString(beginTag, closeTag, src) {
207
+ let res = '';
208
+ let josi = '';
209
+ let numEOL = 0;
210
+ src = src.substr(beginTag.length); // skip beginTag
211
+ const i = src.indexOf(closeTag);
212
+ if (i < 0) { // not found
213
+ res = src;
214
+ src = '';
215
+ }
216
+ else {
217
+ res = src.substr(0, i);
218
+ src = src.substr(i + closeTag.length);
219
+ // res の中に beginTag があればエラーにする #953
220
+ if (res.indexOf(beginTag) >= 0) {
221
+ if (beginTag === '『') {
222
+ throw new Error('「『」で始めた文字列の中に「『」を含めることはできません。');
223
+ }
224
+ else {
225
+ throw new Error(`『${beginTag}』で始めた文字列の中に『${beginTag}』を含めることはできません。`);
226
+ }
227
+ }
228
+ }
229
+ // 文字列直後の助詞を取得
230
+ const j = josiRE.exec(src);
231
+ if (j) {
232
+ josi = j[0].replace(/^\s+/, '');
233
+ src = src.substr(j[0].length);
234
+ // 助詞の後のカンマ #877
235
+ if (src.charAt(0) === ',') {
236
+ src = src.substr(1);
237
+ }
238
+ }
239
+ // 助詞「こと」「である」「です」などは「**すること」のように使うので削除 #936 #939 #974
240
+ if (removeJosiMap[josi]) {
241
+ josi = '';
242
+ }
243
+ // 改行を数える
244
+ for (let i = 0; i < res.length; i++) {
245
+ if (res.charAt(i) === '\n') {
246
+ numEOL++;
247
+ }
248
+ }
249
+ return { src, res, josi, numEOL };
250
+ }
251
+ function parseNumber(n) {
252
+ return Number(n.replace(/_/g, ''));
253
+ }
@@ -0,0 +1,260 @@
1
+ /**
2
+ * なでしこ3字句解析のためのルール
3
+ */
4
+
5
+ import { josiRE, removeJosiMap } from './nako_josi_list.mjs'
6
+
7
+ const kanakanji = /^[\u3005\u4E00-\u9FCF_a-zA-Z0-9ァ-ヶー\u2460-\u24FF\u2776-\u277F\u3251-\u32BF]+/
8
+ const hira = /^[ぁ-ん]/
9
+ const allHiragana = /^[ぁ-ん]+$/
10
+ const wordHasIjoIka = /^.+(以上|以下|超|未満)$/
11
+ const errorRead = (ch: string): any => {
12
+ return function () { throw new Error('突然の『' + ch + '』があります。') }
13
+ }
14
+
15
+ export const unitRE = /^(円|ドル|元|歩|㎡|坪|度|℃|°|個|つ|本|冊|才|歳|匹|枚|皿|セット|羽|人|件|行|列|機|品|m|mm|cm|km|g|kg|t|px|dot|pt|em|b|mb|kb|gb)/
16
+
17
+ interface NakoLexParseResult {
18
+ src: string;
19
+ res: string;
20
+ josi: string;
21
+ numEOL: number;
22
+ }
23
+
24
+ export interface NakoLexRule {
25
+ name: string;
26
+ pattern: RegExp;
27
+ readJosi?: boolean;
28
+ cb?: (v: string) => any;
29
+ cbParser?: (v: string, b?: boolean) => NakoLexParseResult;
30
+ }
31
+
32
+ export const rules: NakoLexRule[] = [
33
+ // 上から順にマッチさせていく
34
+ { name: 'ここまで', pattern: /^;;;/ }, // #925
35
+ { name: 'eol', pattern: /^\n/ },
36
+ { name: 'eol', pattern: /^;/ },
37
+ // eslint-disable-next-line no-control-regex
38
+ { name: 'space', pattern: /^(\x20|\x09|・)+/ }, // #877,#1015
39
+ { name: 'comma', pattern: /^,/ },
40
+ { name: 'line_comment', pattern: /^#[^\n]*/ },
41
+ { name: 'line_comment', pattern: /^\/\/[^\n]*/ },
42
+ { name: 'range_comment', pattern: /^\/\*/, cbParser: cbRangeComment },
43
+ { name: 'def_test', pattern: /^●テスト:/ },
44
+ { name: 'def_func', pattern: /^●/ },
45
+ // 数値の判定 --- この後nako_lexerにて単位を読む処理が入る(#994)
46
+ { name: 'number', pattern: /^0[xX][0-9a-fA-F]+(_[0-9a-fA-F]+)*/, readJosi: true, cb: parseNumber },
47
+ { name: 'number', pattern: /^0[oO][0-7]+(_[0-7]+)*/, readJosi: true, cb: parseNumber },
48
+ { name: 'number', pattern: /^0[bB][0-1]+(_[0-1]+)*/, readJosi: true, cb: parseNumber },
49
+ // 下の三つは小数点が挟まっている場合、小数点から始まっている場合、小数点がない場合の十進法の数値にマッチします
50
+ { name: 'number', pattern: /^\d+(_\d+)*\.(\d+(_\d+)*)?([eE][+|-]?\d+(_\d+)*)?/, readJosi: true, cb: parseNumber },
51
+ { name: 'number', pattern: /^\.\d+(_\d+)*([eE][+|-]?\d+(_\d+)*)?/, readJosi: true, cb: parseNumber },
52
+ { name: 'number', pattern: /^\d+(_\d+)*([eE][+|-]?\d+(_\d+)*)?/, readJosi: true, cb: parseNumber },
53
+ { name: 'ここから', pattern: /^(ここから),?/ },
54
+ { name: 'ここまで', pattern: /^(ここまで|💧)/ },
55
+ { name: 'もし', pattern: /^もしも?/ },
56
+ // 「ならば」は助詞として定義している
57
+ { name: '違えば', pattern: /^違(えば)?/ },
58
+ // 「回」「間」「繰返」「反復」「抜」「続」「戻」「代入」「条件分岐」などは NakoLexer._replaceWord で word から変換
59
+ // @see nako_reserved_words.js
60
+ { name: 'shift_r0', pattern: /^>>>/ },
61
+ { name: 'shift_r', pattern: /^>>/ },
62
+ { name: 'shift_l', pattern: /^<</ },
63
+ { name: '===', pattern: /^===/ }, // #999
64
+ { name: '!==', pattern: /^!==/ }, // #999
65
+ { name: 'gteq', pattern: /^(≧|>=|=>)/ },
66
+ { name: 'lteq', pattern: /^(≦|<=|=<)/ },
67
+ { name: 'noteq', pattern: /^(≠|<>|!=)/ },
68
+ { name: '←', pattern: /^(←|<--)/ }, // 関数呼び出し演算子 #891 #899
69
+ { name: 'eq', pattern: /^(=|🟰)/ },
70
+ { name: 'line_comment', pattern: /^(!|💡)(インデント構文|ここまでだるい)[^\n]*/ }, // #1184
71
+ { name: 'not', pattern: /^(!|💡)/ }, // #1184
72
+ { name: 'gt', pattern: /^>/ },
73
+ { name: 'lt', pattern: /^</ },
74
+ { name: 'and', pattern: /^(かつ|&&)/ },
75
+ { name: 'or', pattern: /^(または|或いは|あるいは|\|\|)/ },
76
+ { name: '@', pattern: /^@/ },
77
+ { name: '+', pattern: /^\+/ },
78
+ { name: '-', pattern: /^-/ },
79
+ { name: '*', pattern: /^(×|\*)/ },
80
+ { name: '÷÷', pattern: /^÷÷/ }, // 整数の割り算
81
+ { name: '÷', pattern: /^(÷|\/)/ }, // 普通の割り算
82
+ { name: '%', pattern: /^%/ },
83
+ { name: '^', pattern: /^\^/ },
84
+ { name: '&', pattern: /^&/ },
85
+ { name: '[', pattern: /^\[/ },
86
+ { name: ']', pattern: /^]/, readJosi: true },
87
+ { name: '(', pattern: /^\(/ },
88
+ { name: ')', pattern: /^\)/, readJosi: true },
89
+ { name: '|', pattern: /^\|/ },
90
+ { name: 'string', pattern: /^🌿/, cbParser: src => cbString('🌿', '🌿', src) },
91
+ { name: 'string_ex', pattern: /^🌴/, cbParser: src => cbString('🌴', '🌴', src) },
92
+ { name: 'string_ex', pattern: /^「/, cbParser: src => cbString('「', '」', src) },
93
+ { name: 'string', pattern: /^『/, cbParser: src => cbString('『', '』', src) },
94
+ { name: 'string_ex', pattern: /^“/, cbParser: src => cbString('“', '”', src) },
95
+ { name: 'string_ex', pattern: /^"/, cbParser: src => cbString('"', '"', src) },
96
+ { name: 'string', pattern: /^'/, cbParser: src => cbString('\'', '\'', src) },
97
+ { name: '」', pattern: /^」/, cbParser: errorRead('」') }, // error
98
+ { name: '』', pattern: /^』/, cbParser: errorRead('』') }, // error
99
+ { name: 'func', pattern: /^\{関数\},?/ },
100
+ { name: '{', pattern: /^\{/ },
101
+ { name: '}', pattern: /^\}/, readJosi: true },
102
+ { name: ':', pattern: /^:/ },
103
+ { name: '_eol', pattern: /^_\s*\n/ },
104
+ { name: 'dec_lineno', pattern: /^‰/ },
105
+ // 絵文字変数 = (絵文字)英数字*
106
+ { name: 'word', pattern: /^[\uD800-\uDBFF][\uDC00-\uDFFF][_a-zA-Z0-9]*/, readJosi: true },
107
+ { name: 'word', pattern: /^[\u1F60-\u1F6F][_a-zA-Z0-9]*/, readJosi: true }, // 絵文字
108
+ { name: 'word', pattern: /^《.+?》/, readJosi: true }, // 《特別名前トークン》(#672)
109
+ // 単語句
110
+ {
111
+ name: 'word',
112
+ pattern: /^[_a-zA-Z\u3005\u4E00-\u9FCFぁ-んァ-ヶ\u2460-\u24FF\u2776-\u277F\u3251-\u32BF]/,
113
+ cbParser: cbWordParser
114
+ }
115
+ ]
116
+
117
+ export function trimOkurigana (s: string): string {
118
+ // ひらがなから始まらない場合、送り仮名を削除。(例)置換する
119
+ if (!hira.test(s)) {
120
+ return s.replace(/[ぁ-ん]+/g, '')
121
+ }
122
+ // 全てひらがな? (例) どうぞ
123
+ if (allHiragana.test(s)) { return s }
124
+ // 末尾のひらがなのみ (例)お願いします →お願
125
+ return s.replace(/[ぁ-ん]+$/g, '')
126
+ }
127
+
128
+ // Utility for Rule
129
+ function cbRangeComment (src: string): NakoLexParseResult {
130
+ let res = ''
131
+ const josi = ''
132
+ let numEOL = 0
133
+ src = src.substring(2) // skip /*
134
+ const i = src.indexOf('*/')
135
+ if (i < 0) { // not found
136
+ res = src
137
+ src = ''
138
+ } else {
139
+ res = src.substring(0, i)
140
+ src = src.substring(i + 2)
141
+ }
142
+ // 改行を数える
143
+ for (let i = 0; i < res.length; i++) { if (res.charAt(i) === '\n') { numEOL++ } }
144
+
145
+ res = res.replace(/(^\s+|\s+$)/, '') // trim
146
+ return { src, res, josi, numEOL }
147
+ }
148
+
149
+ /**
150
+ * @param {string} src
151
+ */
152
+ function cbWordParser (src: string, isTrimOkurigana = true): NakoLexParseResult {
153
+ /*
154
+ kanji = [\u3005\u4E00-\u9FCF]
155
+ hiragana = [ぁ-ん]
156
+ katakana = [ァ-ヶー]
157
+ emoji = [\u1F60-\u1F6F]
158
+ uni_extra = [\uD800-\uDBFF] [\uDC00-\uDFFF]
159
+ alphabet = [_a-zA-Z]
160
+ numchars = [0-9]
161
+ */
162
+ let res = ''
163
+ let josi = ''
164
+ while (src !== '') {
165
+ if (res.length > 0) {
166
+ // 助詞の判定
167
+ const j = josiRE.exec(src)
168
+ if (j) {
169
+ josi = j[0].replace(/^\s+/, '')
170
+ src = src.substr(j[0].length)
171
+ // 助詞の直後にある「,」を飛ばす #877
172
+ if (src.charAt(0) === ',') { src = src.substr(1) }
173
+ break
174
+ }
175
+ }
176
+ // カタカナ漢字英数字か?
177
+ const m = kanakanji.exec(src)
178
+ if (m) {
179
+ res += m[0]
180
+ src = src.substr(m[0].length)
181
+ continue
182
+ }
183
+ // ひらがな?
184
+ const h = hira.test(src)
185
+ if (h) {
186
+ res += src.charAt(0)
187
+ src = src.substr(1)
188
+ continue
189
+ }
190
+ break // other chars
191
+ }
192
+ // 「間」の特殊ルール (#831)
193
+ // 「等しい間」や「一致する間」なら「間」をsrcに戻す。ただし「システム時間」はそのままにする。
194
+ if (/[ぁ-ん]間$/.test(res)) {
195
+ src = res.charAt(res.length - 1) + src
196
+ res = res.slice(0, -1)
197
+ }
198
+ // 「以上」「以下」「超」「未満」 #918
199
+ const ii = wordHasIjoIka.exec(res)
200
+ if (ii) {
201
+ src = ii[1] + josi + src
202
+ josi = ''
203
+ res = res.substr(0, res.length - ii[1].length)
204
+ }
205
+ // 助詞「こと」「である」「です」などは「**すること」のように使うので削除 #936 #939 #974
206
+ if (removeJosiMap[josi]) { josi = '' }
207
+
208
+ // 漢字カタカナ英語から始まる語句 --- 送り仮名を省略
209
+ if (isTrimOkurigana) {
210
+ res = trimOkurigana(res)
211
+ }
212
+ // 助詞だけの語句の場合
213
+ if (res === '' && josi !== '') {
214
+ res = josi
215
+ josi = ''
216
+ }
217
+ return { src, res, josi, numEOL: 0 }
218
+ }
219
+
220
+ function cbString (beginTag: string, closeTag: string, src: string): NakoLexParseResult {
221
+ let res = ''
222
+ let josi = ''
223
+ let numEOL = 0
224
+ src = src.substr(beginTag.length) // skip beginTag
225
+ const i = src.indexOf(closeTag)
226
+ if (i < 0) { // not found
227
+ res = src
228
+ src = ''
229
+ } else {
230
+ res = src.substr(0, i)
231
+ src = src.substr(i + closeTag.length)
232
+ // res の中に beginTag があればエラーにする #953
233
+ if (res.indexOf(beginTag) >= 0) {
234
+ if (beginTag === '『') {
235
+ throw new Error('「『」で始めた文字列の中に「『」を含めることはできません。')
236
+ } else {
237
+ throw new Error(`『${beginTag}』で始めた文字列の中に『${beginTag}』を含めることはできません。`)
238
+ }
239
+ }
240
+ }
241
+ // 文字列直後の助詞を取得
242
+ const j = josiRE.exec(src)
243
+ if (j) {
244
+ josi = j[0].replace(/^\s+/, '')
245
+ src = src.substr(j[0].length)
246
+ // 助詞の後のカンマ #877
247
+ if (src.charAt(0) === ',') { src = src.substr(1) }
248
+ }
249
+ // 助詞「こと」「である」「です」などは「**すること」のように使うので削除 #936 #939 #974
250
+ if (removeJosiMap[josi]) { josi = '' }
251
+
252
+ // 改行を数える
253
+ for (let i = 0; i < res.length; i++) { if (res.charAt(i) === '\n') { numEOL++ } }
254
+
255
+ return { src, res, josi, numEOL }
256
+ }
257
+
258
+ function parseNumber (n: string): number {
259
+ return Number(n.replace(/_/g, ''))
260
+ }