assembly-yasm-helper 1.4.0

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 (49) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/.vscodeignore +10 -0
  3. package/.vsixmanifest +44 -0
  4. package/LICENSE +21 -0
  5. package/Themes/tasmDark.tmTheme.json +134 -0
  6. package/Themes/tasmHC.tmTheme.json +136 -0
  7. package/Themes/tasmLight.tmTheme.json +137 -0
  8. package/bin/assembly-yasm-lsp.js +2 -0
  9. package/changelog.md +130 -0
  10. package/configs/language-configuration.json +49 -0
  11. package/icon.png +0 -0
  12. package/lsp/server.js +423 -0
  13. package/out/core/compilerEngine.js +77 -0
  14. package/out/core/completionEngine.js +259 -0
  15. package/out/core/diagnosticEngine.js +305 -0
  16. package/out/core/foldingEngine.js +55 -0
  17. package/out/core/hoverEngine.js +62 -0
  18. package/out/core/referencesEngine.js +39 -0
  19. package/out/core/semanticEngine.js +64 -0
  20. package/out/data/enums.js +35 -0
  21. package/out/data/instructionSignatures.js +422 -0
  22. package/out/data/keywords.js +359 -0
  23. package/out/data/operandTypes.js +10 -0
  24. package/out/data/structs.js +132 -0
  25. package/out/engine/memoryAddressParser.js +45 -0
  26. package/out/engine/memoryState.js +38 -0
  27. package/out/engine/memoryTokenizer.js +28 -0
  28. package/out/extension.js +146 -0
  29. package/out/instructionRegistry.js +27 -0
  30. package/out/providers/compilerProvider.js +77 -0
  31. package/out/providers/completionProvider.js +73 -0
  32. package/out/providers/definitionProvider.js +41 -0
  33. package/out/providers/diagnosticProvider.js +32 -0
  34. package/out/providers/foldingProvider.js +18 -0
  35. package/out/providers/hoverProvider.js +25 -0
  36. package/out/providers/referencesProvider.js +28 -0
  37. package/out/providers/renameProvider.js +70 -0
  38. package/out/providers/semanticTokensProvider.js +29 -0
  39. package/out/providers/signatureHelpProvider.js +125 -0
  40. package/out/providers/symbolProvider.js +66 -0
  41. package/out/registry.js +167 -0
  42. package/out/scanner.js +174 -0
  43. package/out/tokenizer.js +67 -0
  44. package/out/utils.js +89 -0
  45. package/out/vsc/fileData.js +21 -0
  46. package/package.json +241 -0
  47. package/readme.md +80 -0
  48. package/snippets.json +141 -0
  49. package/syntaxes/assembly.tmLanguage.json +315 -0
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ const vscode = require("vscode");
3
+ const { KEYWORD_MAP } = require("../data/keywords");
4
+ const { INSTRUCTION_SIGNATURES } = require("../data/instructionSignatures");
5
+
6
+ // Convert an OperandType bitfield to a readable string
7
+ const _ts = b => ((b&3)===3)?'r/m':(b&1)?'reg':(b&2)?'mem':(b&4)?'imm':(b&8)?'label':'?';
8
+
9
+ class AsmSignatureHelpProvider {
10
+
11
+ constructor(registry) { this.registry = registry; }
12
+
13
+ provideSignatureHelp(document, position, token, context) {
14
+ if (!vscode.workspace.getConfiguration('assembly').get('enableSignatureHelp')) return null;
15
+
16
+ const line = document.lineAt(position.line).text.slice(0, position.character);
17
+
18
+ const commentIdx = line.indexOf(';');
19
+ if (commentIdx !== -1 && commentIdx < position.character) return null;
20
+
21
+ const trimmed = line.trimStart();
22
+ const words = trimmed.split(/\s+/);
23
+ if (words.length < 2) return null;
24
+
25
+ const opcode = words[0].toLowerCase();
26
+ const kw = KEYWORD_MAP.get(opcode);
27
+ const sigs = INSTRUCTION_SIGNATURES[opcode];
28
+ const macro = this.registry?.findMacro(opcode);
29
+
30
+ // count commas after opcode to determine active parameter index
31
+ const afterOpcode = line.slice(line.toLowerCase().indexOf(opcode) + opcode.length);
32
+ const commaCount = (afterOpcode.match(/,/g) || []).length;
33
+
34
+ const result = new vscode.SignatureHelp();
35
+
36
+ if (sigs?.length) {
37
+ result.signatures = sigs.map(form => {
38
+ const label = form.length
39
+ ? `${opcode} ${form.map(_ts).join(', ')}`
40
+ : opcode;
41
+ const sig = new vscode.SignatureInformation(label, kw?.def || '');
42
+ let searchFrom = opcode.length + (form.length ? 1 : 0);
43
+ for (const bits of form) {
44
+ const name = _ts(bits);
45
+ const start = label.indexOf(name, searchFrom);
46
+ const end = start + name.length;
47
+ sig.parameters.push(new vscode.ParameterInformation([start, end]));
48
+ searchFrom = end + 1;
49
+ }
50
+ return sig;
51
+ });
52
+
53
+ // pick the best matching signature based on already-typed operands
54
+ const typedParts = afterOpcode.split(',');
55
+ let activeIdx = 0;
56
+ if (commaCount > 0) {
57
+ const completedOps = typedParts.slice(0, commaCount).map(o => o.trim());
58
+ for (let i = 0; i < sigs.length; i++) {
59
+ if (sigs[i].length < typedParts.length) continue;
60
+ if (completedOps.every((op, j) => !op || (this._classify(op) & sigs[i][j]))) {
61
+ activeIdx = i;
62
+ break;
63
+ }
64
+ }
65
+ }
66
+
67
+ result.activeSignature = activeIdx;
68
+ result.activeParameter = Math.min(commaCount, sigs[activeIdx].length - 1);
69
+
70
+ } else if (kw?.data) {
71
+ // fallback: single signature from keyword data
72
+ const sig = new vscode.SignatureInformation(kw.data, kw.def || '');
73
+ const paramStr = kw.data.slice(opcode.length).trim();
74
+ if (paramStr) {
75
+ let searchFrom = kw.data.length - paramStr.length;
76
+ for (const p of paramStr.split(',')) {
77
+ const name = p.trim();
78
+ const start = kw.data.indexOf(name, searchFrom);
79
+ const end = start + name.length;
80
+ sig.parameters.push(new vscode.ParameterInformation([start, end]));
81
+ searchFrom = end + 1;
82
+ }
83
+ }
84
+ result.signatures = [sig];
85
+ result.activeSignature = 0;
86
+ result.activeParameter = Math.min(commaCount, sig.parameters.length - 1);
87
+
88
+ } else if (macro?.argCount > 0) {
89
+ // user-defined macro: build signature from argCount
90
+ const params = Array.from({ length: macro.argCount }, (_, i) => `%${i + 1}`);
91
+ const label = `${macro.name} ${params.join(', ')}`;
92
+ const sig = new vscode.SignatureInformation(label, `(Macro) ${macro.name} — ${macro.argCount} arg${macro.argCount !== 1 ? 's' : ''}`);
93
+ let searchFrom = macro.name.length + 1;
94
+ for (const p of params) {
95
+ const start = label.indexOf(p, searchFrom);
96
+ const end = start + p.length;
97
+ sig.parameters.push(new vscode.ParameterInformation([start, end]));
98
+ searchFrom = end + 1;
99
+ }
100
+ result.signatures = [sig];
101
+ result.activeSignature = 0;
102
+ result.activeParameter = Math.min(commaCount, params.length - 1);
103
+
104
+ } else {
105
+ return null;
106
+ }
107
+
108
+ return result;
109
+ }
110
+
111
+ // Lightweight operand classifier for signature matching
112
+ _classify(op) {
113
+ if (op.includes('[')) return 2; // MEM
114
+ const tokens = op.split(/\s+/).filter(t => t.length > 0);
115
+ if (tokens.some(t => this._isReg(t))) return 1; // REG
116
+ if (tokens.length === 1 && /^(0x[0-9a-f]+|[0-9][0-9a-f]*[hbd]?)$/i.test(tokens[0])) return 4; // IMM
117
+ return 8 | 4; // LABEL | IMM
118
+ }
119
+
120
+ _isReg(w) {
121
+ return /^(r(ax|bx|cx|dx|si|di|bp|sp)|e(ax|bx|cx|dx|si|di|bp|sp)|[abcd][xhl]|[sd]il|[bs]pl|r\d{1,2}[bdw]?|[xyz]mm\d+|k[0-7]|[cdefgs]s|[re]ip|rflags|eflags)$/i.test(w);
122
+ }
123
+ }
124
+
125
+ module.exports = { AsmSignatureHelpProvider };
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ const vscode = require("vscode");
3
+
4
+ class AsmDocumentSymbolProvider {
5
+ constructor(registry) {
6
+ this.registry = registry;
7
+ }
8
+
9
+ provideDocumentSymbols(document, token) {
10
+ if (!vscode.workspace.getConfiguration('assembly').get('enableDocumentSymbols')) return [];
11
+
12
+ const symbols = [];
13
+
14
+ for (const proc of this.registry.procs) {
15
+ if (proc.line === undefined) continue;
16
+ const range = new vscode.Range(proc.line, 0, proc.line, 0);
17
+ symbols.push(new vscode.DocumentSymbol(
18
+ proc.name,
19
+ '(Procedure)',
20
+ vscode.SymbolKind.Function,
21
+ range,
22
+ range
23
+ ));
24
+ }
25
+
26
+ for (const v of this.registry.vars) {
27
+ if (v.line === undefined) continue;
28
+ const range = new vscode.Range(v.line, 0, v.line, 0);
29
+ symbols.push(new vscode.DocumentSymbol(
30
+ v.name,
31
+ v.type + (v.section ? ' [' + v.section + ']' : ''),
32
+ vscode.SymbolKind.Variable,
33
+ range,
34
+ range
35
+ ));
36
+ }
37
+
38
+ for (const macro of this.registry.macros) {
39
+ if (macro.line === undefined) continue;
40
+ const range = new vscode.Range(macro.line, 0, macro.line, 0);
41
+ symbols.push(new vscode.DocumentSymbol(
42
+ macro.name,
43
+ '(Macro)',
44
+ vscode.SymbolKind.Module,
45
+ range,
46
+ range
47
+ ));
48
+ }
49
+
50
+ for (const label of this.registry.labels) {
51
+ if (label.line === undefined) continue;
52
+ const range = new vscode.Range(label.line, 0, label.line, 0);
53
+ symbols.push(new vscode.DocumentSymbol(
54
+ label.name,
55
+ '(Label)',
56
+ vscode.SymbolKind.Key,
57
+ range,
58
+ range
59
+ ));
60
+ }
61
+
62
+ return symbols;
63
+ }
64
+ }
65
+
66
+ module.exports = { AsmDocumentSymbolProvider };
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+
3
+ const { KEYWORD_MAP } = require("./data/keywords");
4
+
5
+ class SymbolRegistry {
6
+
7
+ constructor() {
8
+ this.clear();
9
+ }
10
+
11
+ clear() {
12
+
13
+ // arrays (สำหรับ iterate)
14
+ this.vars = [];
15
+ this.procs = [];
16
+ this.macros = [];
17
+ this.labels = [];
18
+ this.labelsEE = [];
19
+ this.structs = [];
20
+
21
+ this.structureInfo = [];
22
+ this.includedFiles = [];
23
+
24
+ // fast lookup maps
25
+ this.varMap = new Map();
26
+ this.procMap = new Map();
27
+ this.macroMap = new Map();
28
+ this.labelMap = new Map();
29
+
30
+ // duplicate guard
31
+ this.varSet = new Set();
32
+ this.procSet = new Set();
33
+ this.macroSet = new Set();
34
+ this.labelSet = new Set();
35
+
36
+ // section index
37
+ this.sectionIndex = new Map();
38
+ }
39
+
40
+ // --------------------------------
41
+ // SECTION INDEX
42
+ // --------------------------------
43
+
44
+ addToSection(section, symbol) {
45
+
46
+ if (!section) return;
47
+
48
+ const key = section.toLowerCase();
49
+
50
+ if (!this.sectionIndex.has(key)) {
51
+ this.sectionIndex.set(key, []);
52
+ }
53
+
54
+ this.sectionIndex.get(key).push(symbol);
55
+ }
56
+
57
+ getSectionSymbols(section) {
58
+ return this.sectionIndex.get(section.toLowerCase()) || [];
59
+ }
60
+
61
+ // --------------------------------
62
+ // VARIABLES
63
+ // --------------------------------
64
+
65
+ addVariable(v) {
66
+
67
+ const key = v.name.toLowerCase();
68
+
69
+ if (this.varSet.has(key)) return;
70
+
71
+ this.varSet.add(key);
72
+
73
+ this.vars.push(v);
74
+ this.varMap.set(key, v);
75
+
76
+ this.addToSection(v.section, v);
77
+ }
78
+
79
+ findVariable(name) {
80
+ return this.varMap.get(name.toLowerCase());
81
+ }
82
+
83
+ // --------------------------------
84
+ // PROCEDURES
85
+ // --------------------------------
86
+
87
+ addProcedure(p) {
88
+
89
+ const key = p.name.toLowerCase();
90
+
91
+ if (this.procSet.has(key)) return;
92
+
93
+ this.procSet.add(key);
94
+
95
+ this.procs.push(p);
96
+ this.procMap.set(key, p);
97
+
98
+ this.addToSection(p.section, p);
99
+ }
100
+
101
+ findProcedure(name) {
102
+ return this.procMap.get(name.toLowerCase());
103
+ }
104
+
105
+ // --------------------------------
106
+ // MACROS
107
+ // --------------------------------
108
+
109
+ addMacro(name, line, argCount = 0) {
110
+
111
+ const key = name.toLowerCase();
112
+
113
+ if (this.macroSet.has(key)) return;
114
+
115
+ this.macroSet.add(key);
116
+
117
+ const sym = { name, line, argCount };
118
+ this.macros.push(sym);
119
+ this.macroMap.set(key, sym);
120
+ }
121
+
122
+ findMacro(name) {
123
+ return this.macroMap.get(name.toLowerCase());
124
+ }
125
+
126
+ // --------------------------------
127
+ // LABELS
128
+ // --------------------------------
129
+
130
+ addLabel(name, line) {
131
+
132
+ const key = name.toLowerCase();
133
+
134
+ if (this.labelSet.has(key)) return;
135
+
136
+ this.labelSet.add(key);
137
+
138
+ const sym = { name, line };
139
+ this.labels.push(sym);
140
+ this.labelMap.set(key, sym);
141
+ }
142
+
143
+ findLabel(name) {
144
+
145
+ const key = name.toLowerCase();
146
+
147
+ if (this.labelMap.has(key)) {
148
+ return this.labelMap.get(key);
149
+ }
150
+
151
+ return this.labelsEE.find(
152
+ l => l.name.toLowerCase() === key
153
+ );
154
+ }
155
+
156
+ // --------------------------------
157
+ // KEYWORDS
158
+ // --------------------------------
159
+
160
+ getKeyword(word) {
161
+ if (!word) return undefined;
162
+ return KEYWORD_MAP.get(word.toLowerCase());
163
+ }
164
+
165
+ }
166
+
167
+ module.exports = { SymbolRegistry };
package/out/scanner.js ADDED
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const { AsmTokenizer } = require("./tokenizer");
6
+ const { Utils } = require("./utils");
7
+ const { Info, Procedure, Label } = require("./data/structs");
8
+
9
+ class DocumentScanner {
10
+
11
+ constructor(registry, currentFilePath = null) {
12
+ this.tokenizer = new AsmTokenizer();
13
+ this.registry = registry;
14
+ this.currentFilePath = currentFilePath;
15
+ this.currentSection = "";
16
+
17
+ // regex cache
18
+ this.varRegex = /\b(db|dw|dd|dq|dt|resb|resw|resd|resq|equ)\b/i;
19
+ this.labelRegex = /^\s*((?:%%)?[A-Za-z_.$?][\w.$?]*):/;
20
+ this.procRegex = /^\s*([A-Za-z_.$?][\w.$?]*)\s+proc\b/i;
21
+ this.macroRegex = /^\s*%macro\b/i;
22
+ this.defineRegex = /^\s*%(define|assign)\b/i;
23
+ }
24
+
25
+ async scan(documentLines, clearPrevious = true) {
26
+ if (clearPrevious) {
27
+ this.registry.clear();
28
+ if (this.currentFilePath) {
29
+ this.registry.includedFiles.push(path.normalize(this.currentFilePath));
30
+ }
31
+ }
32
+ this.currentSection = "";
33
+
34
+ for (let x = 0; x < documentLines.length; x++) {
35
+ const ctx = this._buildContext(documentLines[x]);
36
+
37
+ if (this._detectSection(ctx)) continue;
38
+ this._detectLabel(ctx, x);
39
+ this._detectVariable(ctx, x, clearPrevious);
40
+ await this._detectProcedure(ctx, x, documentLines);
41
+ await this._detectInclude(ctx);
42
+ this._detectMacro(ctx, x);
43
+ this._detectDefine(ctx);
44
+ this._detectExtern(ctx, x);
45
+ }
46
+ }
47
+
48
+ // -------------------------------------------------------
49
+ // Context builder — tokenizes each line once
50
+ // -------------------------------------------------------
51
+
52
+ _buildContext(line) {
53
+ const clean = Utils.clearSpace(line);
54
+ return {
55
+ raw: line,
56
+ clean,
57
+ lower: clean.toLowerCase(),
58
+ noComment: line.replace(/;.*$/, "").trim(),
59
+ words: this.tokenizer.tokenize(line)
60
+ };
61
+ }
62
+
63
+ // -------------------------------------------------------
64
+ // Detection methods
65
+ // -------------------------------------------------------
66
+
67
+ _detectSection(ctx) {
68
+ if (!ctx.lower.startsWith("section") && !ctx.lower.startsWith("segment")) return false;
69
+ if (ctx.words.length > 1) this.currentSection = ctx.words[1].toLowerCase();
70
+ return true;
71
+ }
72
+
73
+ _detectLabel(ctx, x) {
74
+ const match = ctx.noComment.match(this.labelRegex);
75
+ if (match && !this.registry.findLabel(match[1])) {
76
+ this.registry.addLabel(match[1], x);
77
+ }
78
+
79
+ // MASM label directive
80
+ if (ctx.lower.startsWith("label") && ctx.words.length >= 2) {
81
+ const name = ctx.words[1];
82
+ this.registry.labelsEE.push(
83
+ new Label(name, ctx.raw.substring(ctx.raw.indexOf(name) + name.length))
84
+ );
85
+ }
86
+ }
87
+
88
+ _detectVariable(ctx, x, clearPrevious) {
89
+ if (!clearPrevious) return;
90
+ if (ctx.words.length < 2 || !this.varRegex.test(ctx.words[1])) return;
91
+
92
+ this.registry.addVariable({
93
+ name: ctx.words[0],
94
+ type: ctx.words[1],
95
+ section: this.currentSection,
96
+ line: x
97
+ });
98
+ }
99
+
100
+ async _detectProcedure(ctx, x, documentLines) {
101
+ const match = ctx.clean.match(this.procRegex);
102
+ if (!match || this.registry.findProcedure(match[1])) return;
103
+
104
+ const name = match[1];
105
+ const des = this._parseProcDocComments(documentLines, x, name);
106
+
107
+ const proc = new Procedure(name, des);
108
+ proc.section = this.currentSection;
109
+ proc.line = x;
110
+ this.registry.addProcedure(proc);
111
+ }
112
+
113
+ _parseProcDocComments(documentLines, procLine, name) {
114
+ const des = new Info("", "");
115
+ const text = [];
116
+
117
+ for (let ptr = procLine - 1; ptr >= 0; ptr--) {
118
+ const prev = Utils.clearSpace(documentLines[ptr]);
119
+ if (!prev.startsWith(";")) break;
120
+ text.push(documentLines[ptr].substring(documentLines[ptr].indexOf(";") + 1));
121
+ }
122
+
123
+ for (const t of text) {
124
+ if (t.startsWith("@out: ")) des.output.push(t.substring(t.indexOf(" ", t.indexOf("@out: "))));
125
+ else if (t.startsWith("@arg: ")) des.params.push(Utils.clearSpace(t.substring(t.indexOf(" ", t.indexOf("@arg: ")))));
126
+ else des.des += t;
127
+ }
128
+
129
+ des.name = name;
130
+ return des;
131
+ }
132
+
133
+ async _detectInclude(ctx) {
134
+ if (!ctx.lower.startsWith("%include") && !ctx.lower.startsWith("include")) return;
135
+
136
+ const fileMatch = ctx.raw.match(/['"](.*?)['"]/);
137
+ if (!fileMatch || !this.currentFilePath) return;
138
+
139
+ const baseDir = path.dirname(this.currentFilePath);
140
+ const normalized = path.normalize(path.resolve(baseDir, fileMatch[1]));
141
+
142
+ if (!fs.existsSync(normalized) || this.registry.includedFiles.includes(normalized)) return;
143
+
144
+ const filedata = fs.readFileSync(normalized, "utf8");
145
+ this.registry.includedFiles.push(normalized);
146
+
147
+ const oldSection = this.currentSection;
148
+ await this.scan(filedata.split(/\r?\n/), false);
149
+ this.currentSection = oldSection;
150
+ }
151
+
152
+ _detectMacro(ctx, x) {
153
+ if (!this.macroRegex.test(ctx.raw) || ctx.words.length <= 1) return;
154
+ const macroName = ctx.words[1];
155
+ const argCount = ctx.words.length > 2 ? parseInt(ctx.words[2], 10) : 0;
156
+ if (!this.registry.findMacro(macroName))
157
+ this.registry.addMacro(macroName, x, isNaN(argCount) ? 0 : argCount);
158
+ }
159
+
160
+ _detectDefine(ctx) {
161
+ if (!this.defineRegex.test(ctx.raw) || ctx.words.length <= 1) return;
162
+ const defineName = ctx.words[1];
163
+ if (!this.registry.defines) this.registry.defines = [];
164
+ if (!this.registry.defines.includes(defineName)) this.registry.defines.push(defineName);
165
+ }
166
+
167
+ _detectExtern(ctx, x) {
168
+ if (ctx.words[0]?.toLowerCase() !== "extern" || ctx.words.length <= 1) return;
169
+ const externName = ctx.words[1];
170
+ if (!this.registry.findLabel(externName)) this.registry.addLabel(externName, x);
171
+ }
172
+ }
173
+
174
+ module.exports = { DocumentScanner };
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+
3
+ class AsmTokenizer {
4
+
5
+ tokenize(line) {
6
+
7
+ const tokens = [];
8
+ let current = "";
9
+ let inString = false;
10
+
11
+ for (let i = 0; i < line.length; i++) {
12
+
13
+ const ch = line[i];
14
+
15
+ // comment
16
+ if (!inString && ch === ';') {
17
+ break;
18
+ }
19
+
20
+ // string start/end
21
+ if (ch === '"' || ch === "'") {
22
+
23
+ current += ch;
24
+
25
+ if (inString) {
26
+ tokens.push(current);
27
+ current = "";
28
+ inString = false;
29
+ } else {
30
+ inString = true;
31
+ }
32
+
33
+ continue;
34
+ }
35
+
36
+ if (inString) {
37
+ current += ch;
38
+ continue;
39
+ }
40
+
41
+ // separators
42
+ if (/\s|,|\[|\]|\(|\)/.test(ch)) {
43
+
44
+ if (current.length) {
45
+ tokens.push(current);
46
+ current = "";
47
+ }
48
+
49
+ if (!/\s/.test(ch)) {
50
+ tokens.push(ch);
51
+ }
52
+
53
+ continue;
54
+ }
55
+
56
+ current += ch;
57
+ }
58
+
59
+ if (current.length) {
60
+ tokens.push(current);
61
+ }
62
+
63
+ return tokens;
64
+ }
65
+ }
66
+
67
+ module.exports = { AsmTokenizer };
package/out/utils.js ADDED
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+
3
+ const { KeywordType } = require("./data/enums");
4
+
5
+ class Utils {
6
+
7
+ static typeMap = {
8
+ [KeywordType.instruction]: "(Command)",
9
+ [KeywordType.memoryAllocation]: "(Memory)",
10
+ [KeywordType.precompiled]: "(Instruction)",
11
+ [KeywordType.register]: "(Register)",
12
+ [KeywordType.savedWord]: "(Saved)",
13
+ [KeywordType.size]: "(Size)",
14
+ [KeywordType.label]: "(Label)",
15
+ [KeywordType.macro]: "(Macro)",
16
+ [KeywordType.method]: "(Procedure)",
17
+ [KeywordType.structure]: "(Structure)",
18
+ [KeywordType.variable]: "(Variable)"
19
+ };
20
+
21
+ static getType(type) {
22
+ return Utils.typeMap[type] || "(Unknown)";
23
+ }
24
+
25
+ static clearSpace(str) {
26
+ return str.trim();
27
+ }
28
+
29
+ static splitLine(line) {
30
+
31
+ return line
32
+ .split(/[,\s\[\]\(\)]+/)
33
+ .filter(Boolean);
34
+
35
+ }
36
+
37
+ static isNumberStr(str) {
38
+
39
+ return (
40
+ /^0x[0-9a-f]+$/i.test(str) ||
41
+ /^[0-9]+$/i.test(str) ||
42
+ /^[0-9a-f]+h$/i.test(str) ||
43
+ /^[01]+b$/i.test(str) ||
44
+ /^[0-9]+d$/i.test(str)
45
+ );
46
+
47
+ }
48
+
49
+ static getNumMsg(word) {
50
+
51
+ let base;
52
+ let value;
53
+
54
+ if (/^0x/i.test(word)) {
55
+
56
+ base = 16;
57
+ value = Number.parseInt(word, 16);
58
+
59
+ } else if (word.endsWith("h")) {
60
+
61
+ base = 16;
62
+ value = Number.parseInt(word.slice(0, -1), 16);
63
+
64
+ } else if (word.endsWith("b")) {
65
+
66
+ base = 2;
67
+ value = Number.parseInt(word.slice(0, -1), 2);
68
+
69
+ } else {
70
+
71
+ base = 10;
72
+ value = Number.parseInt(word, 10);
73
+
74
+ }
75
+
76
+ if (Number.isNaN(value)) return null;
77
+
78
+ let s = `(${base === 16 ? "Hexadecimal" : base === 2 ? "Binary" : "Decimal"} Number) ${word}:\n`;
79
+
80
+ if (base !== 10) s += `\tDecimal: ${value.toString(10)}\n`;
81
+ if (base !== 16) s += `\tHex: ${value.toString(16)}h / 0x${value.toString(16).toUpperCase()}\n`;
82
+ if (base !== 2) s += `\tBinary: ${value.toString(2)}b\n`;
83
+
84
+ return s;
85
+ }
86
+
87
+ }
88
+
89
+ module.exports = { Utils };