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.
- package/core/.editorconfig +6 -0
- package/core/.eslintrc.cjs +33 -0
- package/core/.github/dependabot.yml +7 -0
- package/core/.github/workflows/nodejs.yml +37 -0
- package/core/.github/workflows/super-linter.yml +61 -0
- package/core/.github/workflows/textlint.yml +199 -0
- package/core/LICENSE +21 -0
- package/core/README.md +66 -0
- package/core/batch/build_nako_version.nako3 +42 -0
- package/core/command/snako.mjs +105 -0
- package/core/command/snako.mts +116 -0
- package/core/index.mjs +21 -0
- package/core/index.mts +21 -0
- package/core/package.json +47 -0
- package/core/sample/hello.nako3 +7 -0
- package/core/sample/hoge.mjs +4 -0
- package/core/sample/hoge.mts +6 -0
- package/core/src/nako3.mjs +858 -0
- package/core/src/nako3.mts +967 -0
- package/core/src/nako_colors.mjs +78 -0
- package/core/src/nako_colors.mts +86 -0
- package/core/src/nako_core_version.mjs +8 -0
- package/core/src/nako_core_version.mts +19 -0
- package/core/src/nako_csv.mjs +185 -0
- package/core/src/nako_csv.mts +188 -0
- package/core/src/nako_errors.mjs +173 -0
- package/core/src/nako_errors.mts +197 -0
- package/core/src/nako_from_dncl.mjs +255 -0
- package/core/src/nako_from_dncl.mts +250 -0
- package/core/src/nako_gen.mjs +1648 -0
- package/core/src/nako_gen.mts +1719 -0
- package/core/src/nako_gen_async.mjs +1659 -0
- package/core/src/nako_gen_async.mts +1732 -0
- package/core/src/nako_global.mjs +107 -0
- package/core/src/nako_global.mts +138 -0
- package/core/src/nako_indent.mjs +445 -0
- package/core/src/nako_indent.mts +492 -0
- package/core/src/nako_josi_list.mjs +38 -0
- package/core/src/nako_josi_list.mts +45 -0
- package/core/src/nako_lex_rules.mjs +253 -0
- package/core/src/nako_lex_rules.mts +260 -0
- package/core/src/nako_lexer.mjs +609 -0
- package/core/src/nako_lexer.mts +612 -0
- package/core/src/nako_logger.mjs +199 -0
- package/core/src/nako_logger.mts +232 -0
- package/core/src/nako_parser3.mjs +2439 -0
- package/core/src/nako_parser3.mts +2195 -0
- package/core/src/nako_parser_base.mjs +370 -0
- package/core/src/nako_parser_base.mts +370 -0
- package/core/src/nako_parser_const.mjs +37 -0
- package/core/src/nako_parser_const.mts +37 -0
- package/core/src/nako_prepare.mjs +304 -0
- package/core/src/nako_prepare.mts +315 -0
- package/core/src/nako_reserved_words.mjs +38 -0
- package/core/src/nako_reserved_words.mts +38 -0
- package/core/src/nako_source_mapping.mjs +207 -0
- package/core/src/nako_source_mapping.mts +262 -0
- package/core/src/nako_test.mjs +37 -0
- package/core/src/nako_types.mjs +25 -0
- package/core/src/nako_types.mts +151 -0
- package/core/src/plugin_csv.mjs +49 -0
- package/core/src/plugin_csv.mts +50 -0
- package/core/src/plugin_math.mjs +328 -0
- package/core/src/plugin_math.mts +326 -0
- package/core/src/plugin_promise.mjs +91 -0
- package/core/src/plugin_promise.mts +91 -0
- package/core/src/plugin_system.mjs +2832 -0
- package/core/src/plugin_system.mts +2690 -0
- package/core/src/plugin_test.mjs +34 -0
- package/core/src/plugin_test.mts +34 -0
- package/core/test/array_test.mjs +34 -0
- package/core/test/basic_test.mjs +344 -0
- package/core/test/calc_test.mjs +140 -0
- package/core/test/core_module_test.mjs +23 -0
- package/core/test/debug_test.mjs +16 -0
- package/core/test/dncl_test.mjs +94 -0
- package/core/test/error_message_test.mjs +210 -0
- package/core/test/error_test.mjs +16 -0
- package/core/test/flow_test.mjs +373 -0
- package/core/test/func_call.mjs +160 -0
- package/core/test/func_test.mjs +149 -0
- package/core/test/indent_test.mjs +364 -0
- package/core/test/lex_test.mjs +168 -0
- package/core/test/literal_test.mjs +73 -0
- package/core/test/nako_lexer_test.mjs +35 -0
- package/core/test/nako_logger_test.mjs +76 -0
- package/core/test/nako_logger_test.mts +78 -0
- package/core/test/plugin_csv_test.mjs +38 -0
- package/core/test/plugin_promise_test.mjs +18 -0
- package/core/test/plugin_system_test.mjs +630 -0
- package/core/test/prepare_test.mjs +96 -0
- package/core/test/re_test.mjs +22 -0
- package/core/test/side_effects_test.mjs +92 -0
- package/core/test/variable_scope_test.mjs +149 -0
- package/core/tsconfig.json +101 -0
- package/package.json +4 -2
- package/release/_hash.txt +12 -12
- package/release/_script-tags.txt +14 -14
- package/release/editor.js +1 -1
- package/release/stats.json +1 -1
- package/release/version.js +1 -1
- package/release/wnako3.js +1 -1
- package/src/nako_version.mjs +2 -2
- package/src/nako_version.mts +2 -2
- package/test/async/async_basic_test.mjs +3 -3
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/** prepareとtokenizeのソースマッピング */
|
|
2
|
+
export class SourceMappingOfTokenization {
|
|
3
|
+
/**
|
|
4
|
+
* @param {number} sourceCodeLength
|
|
5
|
+
* @param {PreprocessItem[]} preprocessed
|
|
6
|
+
*/
|
|
7
|
+
constructor(sourceCodeLength, preprocessed) {
|
|
8
|
+
/** @private @readonly */
|
|
9
|
+
this.sourceCodeLength = sourceCodeLength;
|
|
10
|
+
/** @private @readonly */
|
|
11
|
+
this.preprocessed = preprocessed;
|
|
12
|
+
let i = 0;
|
|
13
|
+
/** @private @readonly @type {number[]} */
|
|
14
|
+
this.cumulativeSum = [];
|
|
15
|
+
for (const el of preprocessed) {
|
|
16
|
+
this.cumulativeSum.push(i);
|
|
17
|
+
i += el.text.length;
|
|
18
|
+
}
|
|
19
|
+
/** @private */
|
|
20
|
+
this.lastIndex = 0;
|
|
21
|
+
/** @private */
|
|
22
|
+
this.lastPreprocessedCodePosition = 0;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* preprocess後の文字列上のoffsetからソースコード上のoffsetへ変換
|
|
26
|
+
* @param {number} preprocessedCodePosition
|
|
27
|
+
* @returns {number}
|
|
28
|
+
*/
|
|
29
|
+
map(preprocessedCodePosition) {
|
|
30
|
+
const i = this.findIndex(preprocessedCodePosition);
|
|
31
|
+
return Math.min(this.preprocessed[i].sourcePosition + (preprocessedCodePosition - this.cumulativeSum[i]), i === this.preprocessed.length - 1 ? this.sourceCodeLength : this.preprocessed[i + 1].sourcePosition - 1);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* @param {number} preprocessedCodePosition
|
|
35
|
+
* @returns {number}
|
|
36
|
+
*/
|
|
37
|
+
findIndex(preprocessedCodePosition) {
|
|
38
|
+
// 連続アクセスに対する高速化
|
|
39
|
+
if (preprocessedCodePosition < this.lastPreprocessedCodePosition) {
|
|
40
|
+
this.lastIndex = 0;
|
|
41
|
+
}
|
|
42
|
+
this.lastPreprocessedCodePosition = preprocessedCodePosition;
|
|
43
|
+
for (let i = this.lastIndex; i < this.preprocessed.length - 1; i++) {
|
|
44
|
+
if (preprocessedCodePosition < this.cumulativeSum[i + 1]) {
|
|
45
|
+
this.lastIndex = i;
|
|
46
|
+
return i;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
this.lastIndex = this.preprocessed.length - 1;
|
|
50
|
+
return this.preprocessed.length - 1;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export class SourceMappingOfIndentSyntax {
|
|
54
|
+
/**
|
|
55
|
+
* @param {string} codeAfterProcessingIndentationSyntax
|
|
56
|
+
* @param {readonly number[]} linesInsertedByIndentationSyntax
|
|
57
|
+
* @param {readonly { lineNumber: number, len: number }[]} linesDeletedByIndentationSyntax
|
|
58
|
+
*/
|
|
59
|
+
constructor(codeAfterProcessingIndentationSyntax, linesInsertedByIndentationSyntax, linesDeletedByIndentationSyntax) {
|
|
60
|
+
/** @private @type {{ offset: number, len: number }[]} */
|
|
61
|
+
this.lines = [];
|
|
62
|
+
/** @private @readonly */
|
|
63
|
+
this.linesInsertedByIndentationSyntax = linesInsertedByIndentationSyntax;
|
|
64
|
+
/** @private @readonly */
|
|
65
|
+
this.linesDeletedByIndentationSyntax = linesDeletedByIndentationSyntax;
|
|
66
|
+
let offset = 0;
|
|
67
|
+
for (const line of codeAfterProcessingIndentationSyntax.split('\n')) {
|
|
68
|
+
this.lines.push({ offset, len: line.length });
|
|
69
|
+
offset += line.length + 1;
|
|
70
|
+
}
|
|
71
|
+
/** @private */
|
|
72
|
+
this.lastLineNumber = 0;
|
|
73
|
+
/** @private */
|
|
74
|
+
this.lastOffset = 0;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* @param {number | null} startOffset
|
|
78
|
+
* @param {number | null} endOffset
|
|
79
|
+
* @returns {{ startOffset: number | null, endOffset: number | null }}
|
|
80
|
+
*/
|
|
81
|
+
map(startOffset, endOffset) {
|
|
82
|
+
if (startOffset === null) {
|
|
83
|
+
return { startOffset, endOffset };
|
|
84
|
+
}
|
|
85
|
+
// 何行目かを判定
|
|
86
|
+
const tokenLine = this.getLineNumber(startOffset);
|
|
87
|
+
for (const insertedLine of this.linesInsertedByIndentationSyntax) {
|
|
88
|
+
// インデント構文の処理後のソースコードの `insertedLine` 行目にあるトークンのソースマップ情報を削除する。
|
|
89
|
+
if (tokenLine === insertedLine) {
|
|
90
|
+
startOffset = null;
|
|
91
|
+
endOffset = null;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
// インデント構文の処理後のソースコードの `insertedLine` 行目以降にあるトークンのoffsetから
|
|
95
|
+
// `linesInsertedByIndentationSyntax[i]` 行目の文字数(\rを含む) を引く。
|
|
96
|
+
if (tokenLine > insertedLine) {
|
|
97
|
+
// "\n"の分1足す
|
|
98
|
+
startOffset -= this.lines[insertedLine].len + 1;
|
|
99
|
+
if (endOffset !== null) {
|
|
100
|
+
endOffset -= this.lines[insertedLine].len + 1;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
for (const deletedLine of this.linesDeletedByIndentationSyntax) {
|
|
105
|
+
if (tokenLine >= deletedLine.lineNumber) {
|
|
106
|
+
// "\n"の分1足す
|
|
107
|
+
if (startOffset !== null) {
|
|
108
|
+
startOffset += deletedLine.len + 1;
|
|
109
|
+
}
|
|
110
|
+
if (endOffset !== null) {
|
|
111
|
+
endOffset += deletedLine.len + 1;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return { startOffset, endOffset };
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* @param {number} offset
|
|
119
|
+
* @returns {number}
|
|
120
|
+
* @private
|
|
121
|
+
*/
|
|
122
|
+
getLineNumber(offset) {
|
|
123
|
+
// 連続アクセスに対する高速化
|
|
124
|
+
if (offset < this.lastOffset) {
|
|
125
|
+
this.lastLineNumber = 0;
|
|
126
|
+
}
|
|
127
|
+
this.lastOffset = offset;
|
|
128
|
+
for (let i = this.lastLineNumber; i < this.lines.length - 1; i++) {
|
|
129
|
+
if (offset < this.lines[i + 1].offset) {
|
|
130
|
+
this.lastLineNumber = i;
|
|
131
|
+
return i;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
this.lastLineNumber = this.lines.length - 1;
|
|
135
|
+
return this.lines.length - 1;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/** offsetから (line, column) へ変換する。 */
|
|
139
|
+
export class OffsetToLineColumn {
|
|
140
|
+
/**
|
|
141
|
+
* @param {string} code
|
|
142
|
+
*/
|
|
143
|
+
constructor(code) {
|
|
144
|
+
/** @private @type {number[]} */
|
|
145
|
+
this.lineOffsets = [];
|
|
146
|
+
// 各行の先頭位置を先に計算しておく
|
|
147
|
+
let offset = 0;
|
|
148
|
+
for (const line of code.split('\n')) {
|
|
149
|
+
this.lineOffsets.push(offset);
|
|
150
|
+
offset += line.length + 1;
|
|
151
|
+
}
|
|
152
|
+
/** @private */
|
|
153
|
+
this.lastLineNumber = 0;
|
|
154
|
+
/** @private */
|
|
155
|
+
this.lastOffset = 0;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* @param {number} offset
|
|
159
|
+
* @param {boolean} oneBasedLineNumber trueのときlineを1から始める
|
|
160
|
+
* @returns {{ line: number, column: number }}
|
|
161
|
+
*/
|
|
162
|
+
map(offset, oneBasedLineNumber) {
|
|
163
|
+
// 連続アクセスに対する高速化
|
|
164
|
+
if (offset < this.lastOffset) {
|
|
165
|
+
this.lastLineNumber = 0;
|
|
166
|
+
}
|
|
167
|
+
this.lastOffset = offset;
|
|
168
|
+
for (let i = this.lastLineNumber; i < this.lineOffsets.length - 1; i++) {
|
|
169
|
+
if (offset < this.lineOffsets[i + 1]) {
|
|
170
|
+
this.lastLineNumber = i;
|
|
171
|
+
return {
|
|
172
|
+
line: i + (oneBasedLineNumber ? 1 : 0),
|
|
173
|
+
column: offset - this.lineOffsets[i]
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
this.lastLineNumber = this.lineOffsets.length - 1;
|
|
178
|
+
return {
|
|
179
|
+
line: this.lineOffsets.length - 1 + (oneBasedLineNumber ? 1 : 0),
|
|
180
|
+
column: offset - this.lineOffsets[this.lineOffsets.length - 1]
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* preCodeの分、ソースマップのoffset、行数、列数を減らす。
|
|
186
|
+
* @type {<T extends {line?: number, column?: number, startOffset: number | null, endOffset: number | null }>(sourceMap: T, preCode: string) => T}
|
|
187
|
+
*/
|
|
188
|
+
export function subtractSourceMapByPreCodeLength(sourceMap, preCode) {
|
|
189
|
+
// offsetは単純に引くだけでよい
|
|
190
|
+
if (typeof sourceMap.startOffset === 'number') {
|
|
191
|
+
sourceMap.startOffset -= preCode.length;
|
|
192
|
+
}
|
|
193
|
+
if (typeof sourceMap.endOffset === 'number') {
|
|
194
|
+
sourceMap.endOffset -= preCode.length;
|
|
195
|
+
}
|
|
196
|
+
// たとえば preCode = 'abc\ndef\nghi' のとき、line -= 2 して、先頭行なら column -= 3 もする。
|
|
197
|
+
if (preCode !== '') {
|
|
198
|
+
const lines = preCode.split('\n');
|
|
199
|
+
if (typeof sourceMap.line === 'number') {
|
|
200
|
+
sourceMap.line -= lines.length - 1;
|
|
201
|
+
}
|
|
202
|
+
if (sourceMap.line === 0 && typeof sourceMap.column === 'number') {
|
|
203
|
+
sourceMap.column -= lines[lines.length - 1].length;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return sourceMap;
|
|
207
|
+
}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
|
|
2
|
+
interface PreprocessItem {
|
|
3
|
+
text: string;
|
|
4
|
+
sourcePosition: number
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/** prepareとtokenizeのソースマッピング */
|
|
8
|
+
export class SourceMappingOfTokenization {
|
|
9
|
+
private readonly sourceCodeLength: number;
|
|
10
|
+
private readonly preprocessed: PreprocessItem[];
|
|
11
|
+
private readonly cumulativeSum: number[];
|
|
12
|
+
private lastIndex: number;
|
|
13
|
+
private lastPreprocessedCodePosition: number;
|
|
14
|
+
/**
|
|
15
|
+
* @param {number} sourceCodeLength
|
|
16
|
+
* @param {PreprocessItem[]} preprocessed
|
|
17
|
+
*/
|
|
18
|
+
constructor (sourceCodeLength: number, preprocessed: PreprocessItem[]) {
|
|
19
|
+
/** @private @readonly */
|
|
20
|
+
this.sourceCodeLength = sourceCodeLength
|
|
21
|
+
|
|
22
|
+
/** @private @readonly */
|
|
23
|
+
this.preprocessed = preprocessed
|
|
24
|
+
|
|
25
|
+
let i = 0
|
|
26
|
+
/** @private @readonly @type {number[]} */
|
|
27
|
+
this.cumulativeSum = []
|
|
28
|
+
for (const el of preprocessed) {
|
|
29
|
+
this.cumulativeSum.push(i)
|
|
30
|
+
i += el.text.length
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** @private */
|
|
34
|
+
this.lastIndex = 0
|
|
35
|
+
/** @private */
|
|
36
|
+
this.lastPreprocessedCodePosition = 0
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* preprocess後の文字列上のoffsetからソースコード上のoffsetへ変換
|
|
41
|
+
* @param {number} preprocessedCodePosition
|
|
42
|
+
* @returns {number}
|
|
43
|
+
*/
|
|
44
|
+
map (preprocessedCodePosition: number): number {
|
|
45
|
+
const i = this.findIndex(preprocessedCodePosition)
|
|
46
|
+
return Math.min(
|
|
47
|
+
this.preprocessed[i].sourcePosition + (preprocessedCodePosition - this.cumulativeSum[i]),
|
|
48
|
+
i === this.preprocessed.length - 1 ? this.sourceCodeLength : this.preprocessed[i + 1].sourcePosition - 1
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @param {number} preprocessedCodePosition
|
|
54
|
+
* @returns {number}
|
|
55
|
+
*/
|
|
56
|
+
findIndex (preprocessedCodePosition: number): number {
|
|
57
|
+
// 連続アクセスに対する高速化
|
|
58
|
+
if (preprocessedCodePosition < this.lastPreprocessedCodePosition) {
|
|
59
|
+
this.lastIndex = 0
|
|
60
|
+
}
|
|
61
|
+
this.lastPreprocessedCodePosition = preprocessedCodePosition
|
|
62
|
+
|
|
63
|
+
for (let i = this.lastIndex; i < this.preprocessed.length - 1; i++) {
|
|
64
|
+
if (preprocessedCodePosition < this.cumulativeSum[i + 1]) {
|
|
65
|
+
this.lastIndex = i
|
|
66
|
+
return i
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this.lastIndex = this.preprocessed.length - 1
|
|
71
|
+
return this.preprocessed.length - 1
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export class SourceMappingOfIndentSyntax {
|
|
76
|
+
private lines: { offset: number, len: number }[];
|
|
77
|
+
private readonly linesInsertedByIndentationSyntax: number[];
|
|
78
|
+
private readonly linesDeletedByIndentationSyntax: { lineNumber: number, len: number }[];
|
|
79
|
+
lastLineNumber: number;
|
|
80
|
+
lastOffset: number;
|
|
81
|
+
/**
|
|
82
|
+
* @param {string} codeAfterProcessingIndentationSyntax
|
|
83
|
+
* @param {readonly number[]} linesInsertedByIndentationSyntax
|
|
84
|
+
* @param {readonly { lineNumber: number, len: number }[]} linesDeletedByIndentationSyntax
|
|
85
|
+
*/
|
|
86
|
+
constructor (
|
|
87
|
+
codeAfterProcessingIndentationSyntax: string,
|
|
88
|
+
linesInsertedByIndentationSyntax: number[],
|
|
89
|
+
linesDeletedByIndentationSyntax: { lineNumber: number, len: number }[]
|
|
90
|
+
) {
|
|
91
|
+
/** @private @type {{ offset: number, len: number }[]} */
|
|
92
|
+
this.lines = []
|
|
93
|
+
|
|
94
|
+
/** @private @readonly */
|
|
95
|
+
this.linesInsertedByIndentationSyntax = linesInsertedByIndentationSyntax
|
|
96
|
+
|
|
97
|
+
/** @private @readonly */
|
|
98
|
+
this.linesDeletedByIndentationSyntax = linesDeletedByIndentationSyntax
|
|
99
|
+
|
|
100
|
+
let offset = 0
|
|
101
|
+
for (const line of codeAfterProcessingIndentationSyntax.split('\n')) {
|
|
102
|
+
this.lines.push({ offset, len: line.length })
|
|
103
|
+
offset += line.length + 1
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** @private */
|
|
107
|
+
this.lastLineNumber = 0
|
|
108
|
+
/** @private */
|
|
109
|
+
this.lastOffset = 0
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @param {number | null} startOffset
|
|
114
|
+
* @param {number | null} endOffset
|
|
115
|
+
* @returns {{ startOffset: number | null, endOffset: number | null }}
|
|
116
|
+
*/
|
|
117
|
+
map (startOffset: number | null, endOffset:number | null): { startOffset: number | null, endOffset: number | null } {
|
|
118
|
+
if (startOffset === null) {
|
|
119
|
+
return { startOffset, endOffset }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 何行目かを判定
|
|
123
|
+
const tokenLine = this.getLineNumber(startOffset)
|
|
124
|
+
|
|
125
|
+
for (const insertedLine of this.linesInsertedByIndentationSyntax) {
|
|
126
|
+
// インデント構文の処理後のソースコードの `insertedLine` 行目にあるトークンのソースマップ情報を削除する。
|
|
127
|
+
if (tokenLine === insertedLine) {
|
|
128
|
+
startOffset = null
|
|
129
|
+
endOffset = null
|
|
130
|
+
break
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// インデント構文の処理後のソースコードの `insertedLine` 行目以降にあるトークンのoffsetから
|
|
134
|
+
// `linesInsertedByIndentationSyntax[i]` 行目の文字数(\rを含む) を引く。
|
|
135
|
+
if (tokenLine > insertedLine) {
|
|
136
|
+
// "\n"の分1足す
|
|
137
|
+
startOffset -= this.lines[insertedLine].len + 1
|
|
138
|
+
if (endOffset !== null) {
|
|
139
|
+
endOffset -= this.lines[insertedLine].len + 1
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
for (const deletedLine of this.linesDeletedByIndentationSyntax) {
|
|
144
|
+
if (tokenLine >= deletedLine.lineNumber) {
|
|
145
|
+
// "\n"の分1足す
|
|
146
|
+
if (startOffset !== null) {
|
|
147
|
+
startOffset += deletedLine.len + 1
|
|
148
|
+
}
|
|
149
|
+
if (endOffset !== null) {
|
|
150
|
+
endOffset += deletedLine.len + 1
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return { startOffset, endOffset }
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @param {number} offset
|
|
160
|
+
* @returns {number}
|
|
161
|
+
* @private
|
|
162
|
+
*/
|
|
163
|
+
getLineNumber (offset: number): number {
|
|
164
|
+
// 連続アクセスに対する高速化
|
|
165
|
+
if (offset < this.lastOffset) {
|
|
166
|
+
this.lastLineNumber = 0
|
|
167
|
+
}
|
|
168
|
+
this.lastOffset = offset
|
|
169
|
+
|
|
170
|
+
for (let i = this.lastLineNumber; i < this.lines.length - 1; i++) {
|
|
171
|
+
if (offset < this.lines[i + 1].offset) {
|
|
172
|
+
this.lastLineNumber = i
|
|
173
|
+
return i
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
this.lastLineNumber = this.lines.length - 1
|
|
178
|
+
return this.lines.length - 1
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** offsetから (line, column) へ変換する。 */
|
|
183
|
+
export class OffsetToLineColumn {
|
|
184
|
+
private lineOffsets: number[];
|
|
185
|
+
private lastLineNumber: number;
|
|
186
|
+
private lastOffset: number;
|
|
187
|
+
/**
|
|
188
|
+
* @param {string} code
|
|
189
|
+
*/
|
|
190
|
+
constructor (code: string) {
|
|
191
|
+
/** @private @type {number[]} */
|
|
192
|
+
this.lineOffsets = []
|
|
193
|
+
|
|
194
|
+
// 各行の先頭位置を先に計算しておく
|
|
195
|
+
let offset = 0
|
|
196
|
+
for (const line of code.split('\n')) {
|
|
197
|
+
this.lineOffsets.push(offset)
|
|
198
|
+
offset += line.length + 1
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** @private */
|
|
202
|
+
this.lastLineNumber = 0
|
|
203
|
+
/** @private */
|
|
204
|
+
this.lastOffset = 0
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* @param {number} offset
|
|
209
|
+
* @param {boolean} oneBasedLineNumber trueのときlineを1から始める
|
|
210
|
+
* @returns {{ line: number, column: number }}
|
|
211
|
+
*/
|
|
212
|
+
map (offset: number, oneBasedLineNumber: boolean):{ line: number, column: number } {
|
|
213
|
+
// 連続アクセスに対する高速化
|
|
214
|
+
if (offset < this.lastOffset) {
|
|
215
|
+
this.lastLineNumber = 0
|
|
216
|
+
}
|
|
217
|
+
this.lastOffset = offset
|
|
218
|
+
|
|
219
|
+
for (let i = this.lastLineNumber; i < this.lineOffsets.length - 1; i++) {
|
|
220
|
+
if (offset < this.lineOffsets[i + 1]) {
|
|
221
|
+
this.lastLineNumber = i
|
|
222
|
+
return {
|
|
223
|
+
line: i + (oneBasedLineNumber ? 1 : 0),
|
|
224
|
+
column: offset - this.lineOffsets[i]
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
this.lastLineNumber = this.lineOffsets.length - 1
|
|
230
|
+
return {
|
|
231
|
+
line: this.lineOffsets.length - 1 + (oneBasedLineNumber ? 1 : 0),
|
|
232
|
+
column: offset - this.lineOffsets[this.lineOffsets.length - 1]
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* preCodeの分、ソースマップのoffset、行数、列数を減らす。
|
|
239
|
+
* @type {<T extends {line?: number, column?: number, startOffset: number | null, endOffset: number | null }>(sourceMap: T, preCode: string) => T}
|
|
240
|
+
*/
|
|
241
|
+
export function subtractSourceMapByPreCodeLength (sourceMap: any, preCode: string) {
|
|
242
|
+
// offsetは単純に引くだけでよい
|
|
243
|
+
if (typeof sourceMap.startOffset === 'number') {
|
|
244
|
+
sourceMap.startOffset -= preCode.length
|
|
245
|
+
}
|
|
246
|
+
if (typeof sourceMap.endOffset === 'number') {
|
|
247
|
+
sourceMap.endOffset -= preCode.length
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// たとえば preCode = 'abc\ndef\nghi' のとき、line -= 2 して、先頭行なら column -= 3 もする。
|
|
251
|
+
if (preCode !== '') {
|
|
252
|
+
const lines = preCode.split('\n')
|
|
253
|
+
if (typeof sourceMap.line === 'number') {
|
|
254
|
+
sourceMap.line -= lines.length - 1
|
|
255
|
+
}
|
|
256
|
+
if (sourceMap.line === 0 && typeof sourceMap.column === 'number') {
|
|
257
|
+
sourceMap.column -= lines[lines.length - 1].length
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return sourceMap
|
|
262
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
class NakoTest {
|
|
2
|
+
constructor () {
|
|
3
|
+
/** @type {{ name: string, f: () => void }[]} */
|
|
4
|
+
this.tests = []
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/** @param {any} a, @param {any} b */
|
|
8
|
+
static assertStrictEqual (a, b) {
|
|
9
|
+
if (a !== b) {
|
|
10
|
+
throw new Error('')
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** @param {unknown} a */
|
|
15
|
+
static inspect (a) {
|
|
16
|
+
switch (typeof a) {
|
|
17
|
+
case 'bigint': return `bigint値『${a}』`
|
|
18
|
+
case 'boolean': return a + ''
|
|
19
|
+
case 'function': return `関数『${a.name}』`
|
|
20
|
+
case 'number': return `数値${a}`
|
|
21
|
+
case 'object':
|
|
22
|
+
if (a === null) {
|
|
23
|
+
return a + ''
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
return `オブジェクト『${JSON.stringify(a)}』`
|
|
27
|
+
} catch (e) { // 循環参照など
|
|
28
|
+
return `オブジェクト『${a}』`
|
|
29
|
+
}
|
|
30
|
+
case 'string': return `文字列『${a}』`
|
|
31
|
+
case 'symbol': return `シンボル『${a.toString()}』`
|
|
32
|
+
case 'undefined': return a + ''
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = NakoTest
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* なでしこ3 の TypeScript のための型定義
|
|
3
|
+
*/
|
|
4
|
+
export function NewEmptyToken(type = '?', value = {}, line = 0, file = 'main.nako3') {
|
|
5
|
+
return {
|
|
6
|
+
type,
|
|
7
|
+
value,
|
|
8
|
+
line,
|
|
9
|
+
column: 0,
|
|
10
|
+
file,
|
|
11
|
+
josi: ''
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* コンパイルオプション
|
|
16
|
+
*/
|
|
17
|
+
export class CompilerOptions {
|
|
18
|
+
constructor(initObj = {}) {
|
|
19
|
+
this.testOnly = initObj.testOnly || false;
|
|
20
|
+
this.resetEnv = initObj.resetEnv || false;
|
|
21
|
+
this.resetAll = initObj.resetAll || false;
|
|
22
|
+
this.preCode = initObj.preCode || '';
|
|
23
|
+
this.nakoGlobal = initObj.nakoGlobal || null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* なでしこ3 の TypeScript のための型定義
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { NakoGlobal } from './nako_global.mjs'
|
|
6
|
+
|
|
7
|
+
// 関数に関する定義
|
|
8
|
+
export type FuncArgs = string[][]
|
|
9
|
+
|
|
10
|
+
// FuncListの定義
|
|
11
|
+
export interface FuncListItem {
|
|
12
|
+
type: 'func' | 'var' | 'const' | 'test_func';
|
|
13
|
+
value?: any;
|
|
14
|
+
josi?: FuncArgs;
|
|
15
|
+
isVariableJosi?: boolean;
|
|
16
|
+
fn?: null | ((...args: any[]) => any) | string;
|
|
17
|
+
varnames?: string[];
|
|
18
|
+
funcPointers?: any[];
|
|
19
|
+
asyncFn?: boolean;
|
|
20
|
+
// eslint-disable-next-line camelcase
|
|
21
|
+
return_none?: boolean;
|
|
22
|
+
pure?: boolean;
|
|
23
|
+
name?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// 関数の一覧
|
|
27
|
+
export type FuncList = {[key: string]: FuncListItem};
|
|
28
|
+
|
|
29
|
+
// トークンのメタ情報
|
|
30
|
+
export interface TokenMeta {
|
|
31
|
+
josi: FuncArgs | undefined;
|
|
32
|
+
varnames: string[] | undefined;
|
|
33
|
+
funcPointers: any[] | undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// トークン
|
|
37
|
+
export interface Token {
|
|
38
|
+
type: string;
|
|
39
|
+
value: any;
|
|
40
|
+
line: number;
|
|
41
|
+
column: number;
|
|
42
|
+
file: string;
|
|
43
|
+
josi: string;
|
|
44
|
+
meta?: FuncListItem;
|
|
45
|
+
rawJosi?: string;
|
|
46
|
+
startOffset?: number | undefined;
|
|
47
|
+
endOffset?: number | undefined;
|
|
48
|
+
isDefinition?: boolean;
|
|
49
|
+
funcPointer?: boolean;
|
|
50
|
+
tag?: string;
|
|
51
|
+
preprocessedCodeOffset?: number | undefined;
|
|
52
|
+
preprocessedCodeLength?: number | undefined;
|
|
53
|
+
// eslint-disable-next-line no-use-before-define
|
|
54
|
+
name?: Token | Ast; // NakoPaserBase.nodeToStrの問題を回避するため
|
|
55
|
+
start?: number;
|
|
56
|
+
end?: number;
|
|
57
|
+
firstToken?: Token;
|
|
58
|
+
lastToken?: Token;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function NewEmptyToken (type = '?', value: any = {}, line = 0, file = 'main.nako3'): Token {
|
|
62
|
+
return {
|
|
63
|
+
type,
|
|
64
|
+
value,
|
|
65
|
+
line,
|
|
66
|
+
column: 0,
|
|
67
|
+
file,
|
|
68
|
+
josi: ''
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface Ast {
|
|
73
|
+
type: string;
|
|
74
|
+
cond?: Ast[] | Ast;
|
|
75
|
+
expr?: Ast[] | Ast; // todo: cond と共通化できそう
|
|
76
|
+
block?: Ast[] | Ast;
|
|
77
|
+
target?: Ast[] | Ast | null; // 反復
|
|
78
|
+
blocks?: Ast[]; // todo: 逐次実行でのみ使われるので、今後削除可能
|
|
79
|
+
errorBlock?: Ast[] | Ast; // todo: 逐次実行でのみ使われるので、今後削除可能
|
|
80
|
+
errBlock?: Ast[] | Ast; // todo: エラー監視の中でのみ使われる
|
|
81
|
+
cases?: any[]; // 条件分岐
|
|
82
|
+
operator?: string; // 演算子の場合
|
|
83
|
+
left?: Ast | Ast[]; // 演算子の場合
|
|
84
|
+
right?: Ast | Ast[]; // 演算子の場合
|
|
85
|
+
// eslint-disable-next-line camelcase
|
|
86
|
+
false_block?: Ast[] | Ast; // if
|
|
87
|
+
from?: Ast[] | Ast; // for
|
|
88
|
+
to?: Ast[] | Ast; // for
|
|
89
|
+
inc?: Ast[] | Ast | null; // for
|
|
90
|
+
word?: Ast | null; // for
|
|
91
|
+
name?: Token | Ast | null | string;
|
|
92
|
+
names?: Ast[];
|
|
93
|
+
args?: Ast[]; // 関数の引数
|
|
94
|
+
asyncFn?: boolean; // 関数の定義
|
|
95
|
+
meta?: any; // 関数の定義
|
|
96
|
+
setter?: boolean; // 関数の定義
|
|
97
|
+
index?: Ast[]; // 配列へのアクセスに利用
|
|
98
|
+
josi?: string;
|
|
99
|
+
value?: any;
|
|
100
|
+
mode?: string; // 文字列の展開などで利用
|
|
101
|
+
line: number;
|
|
102
|
+
column?: number;
|
|
103
|
+
file?: string;
|
|
104
|
+
startOffset?: number | undefined;
|
|
105
|
+
endOffset?: number | undefined;
|
|
106
|
+
rawJosi?: string;
|
|
107
|
+
vartype?: string;
|
|
108
|
+
end?: {
|
|
109
|
+
startOffset: number | undefined;
|
|
110
|
+
endOffset: number | undefined;
|
|
111
|
+
line?: number;
|
|
112
|
+
column?: number;
|
|
113
|
+
}
|
|
114
|
+
tag?: string;
|
|
115
|
+
genMode?: string;
|
|
116
|
+
checkInit?: boolean;
|
|
117
|
+
options?: { [key: string]: boolean };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface SourceMap {
|
|
121
|
+
startOffset: number | undefined;
|
|
122
|
+
endOffset: number | undefined;
|
|
123
|
+
file: string | undefined;
|
|
124
|
+
line: number;
|
|
125
|
+
column: number;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* コンパイルオプション
|
|
130
|
+
*/
|
|
131
|
+
export class CompilerOptions {
|
|
132
|
+
resetEnv: boolean; // 現在の環境をリセット
|
|
133
|
+
testOnly: boolean; // テストだけを実行する
|
|
134
|
+
resetAll: boolean; // 全ての環境をリセット
|
|
135
|
+
preCode: string; // 環境を構築するためのコード
|
|
136
|
+
nakoGlobal: NakoGlobal | null; // 実行に使う環境
|
|
137
|
+
constructor (initObj: any = {}) {
|
|
138
|
+
this.testOnly = initObj.testOnly || false
|
|
139
|
+
this.resetEnv = initObj.resetEnv || false
|
|
140
|
+
this.resetAll = initObj.resetAll || false
|
|
141
|
+
this.preCode = initObj.preCode || ''
|
|
142
|
+
this.nakoGlobal = initObj.nakoGlobal || null
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export type NakoComEventName = 'finish' | 'beforeRun' | 'beforeGenerate' | 'afterGenerate' | 'beforeParse'
|
|
147
|
+
|
|
148
|
+
export interface NakoEvent {
|
|
149
|
+
eventName: NakoComEventName
|
|
150
|
+
callback: (event: any) => void
|
|
151
|
+
}
|