aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Tuomi <jans.tuomi@gmail.com>2023-04-11 17:30:08 +0300
committerJan Tuomi <jans.tuomi@gmail.com>2023-04-11 18:41:56 +0300
commita38b64e1a1bf14dd9c42629f219b0e422aebe52a (patch)
tree8cc08f712ddd3f8573d2b7c8aa243fcd1b6e52df
parente240ef3e9a2854cfe390b47fb0b3d519c095193b (diff)
Add hover and go to definition to extension
-rw-r--r--asm/ext_std.py (renamed from src/ext_std.py)0
-rw-r--r--asm/fibo.atk1613
-rw-r--r--asm/ram_offset.atk162
-rw-r--r--atk16-syntax-highlighting/atk16-syntax-highlighting-0.0.1.vsixbin2321 -> 0 bytes
-rw-r--r--atk16-syntax/README.md (renamed from atk16-syntax-highlighting/README.md)0
-rw-r--r--atk16-syntax/atk16-syntax-0.0.1.vsixbin0 -> 4725 bytes
-rw-r--r--atk16-syntax/extension.js197
-rw-r--r--atk16-syntax/language-configuration.json (renamed from atk16-syntax-highlighting/language-configuration.json)0
-rw-r--r--atk16-syntax/package.json (renamed from atk16-syntax-highlighting/package.json)19
-rw-r--r--atk16-syntax/syntaxes/atk16.tmLanguage.json (renamed from atk16-syntax-highlighting/syntaxes/atk16.tmLanguage.json)6
-rw-r--r--atk16-syntax/themes/atk16-color-theme.json41
-rw-r--r--src/asm_pass0.py48
-rw-r--r--src/asm_pass1.py13
-rw-r--r--src/asm_pass2.py2
-rw-r--r--src/asm_pass3.py2
-rw-r--r--src/asm_pass4.py5
-rwxr-xr-xsrc/assembler.py4
17 files changed, 331 insertions, 21 deletions
diff --git a/src/ext_std.py b/asm/ext_std.py
index 11da8aa..11da8aa 100644
--- a/src/ext_std.py
+++ b/asm/ext_std.py
diff --git a/asm/fibo.atk16 b/asm/fibo.atk16
index 8bb7a6e..198ae8b 100644
--- a/asm/fibo.atk16
+++ b/asm/fibo.atk16
@@ -6,12 +6,11 @@
jpi program
; data segment
-@label ram_offset
- 0x8000
+@include ram_offset
@label program
; set up stack pointer to point to beginning of RAM
- ldi ram_offset RA
+ ldi ram_offset_addr RA
ldr RA RF
; call fibo subroutine with parameter 10
@@ -20,11 +19,9 @@
hlt
@label fibo
-; function fibo
-; parameters:
-; RA n : u16
-; return value:
-; RA fibo(n) : u16
+;? subroutine fibo
+;? param RA N
+;? return RA Nth fibonacci number
; store RB, RC on stack
spu RB
diff --git a/asm/ram_offset.atk16 b/asm/ram_offset.atk16
new file mode 100644
index 0000000..e8c9255
--- /dev/null
+++ b/asm/ram_offset.atk16
@@ -0,0 +1,2 @@
+@label ram_offset_addr
+ 0x8000
diff --git a/atk16-syntax-highlighting/atk16-syntax-highlighting-0.0.1.vsix b/atk16-syntax-highlighting/atk16-syntax-highlighting-0.0.1.vsix
deleted file mode 100644
index 5d4a42d..0000000
--- a/atk16-syntax-highlighting/atk16-syntax-highlighting-0.0.1.vsix
+++ /dev/null
Binary files differ
diff --git a/atk16-syntax-highlighting/README.md b/atk16-syntax/README.md
index da81144..da81144 100644
--- a/atk16-syntax-highlighting/README.md
+++ b/atk16-syntax/README.md
diff --git a/atk16-syntax/atk16-syntax-0.0.1.vsix b/atk16-syntax/atk16-syntax-0.0.1.vsix
new file mode 100644
index 0000000..b6aa9dc
--- /dev/null
+++ b/atk16-syntax/atk16-syntax-0.0.1.vsix
Binary files differ
diff --git a/atk16-syntax/extension.js b/atk16-syntax/extension.js
new file mode 100644
index 0000000..e49dd07
--- /dev/null
+++ b/atk16-syntax/extension.js
@@ -0,0 +1,197 @@
+const vscode = require('vscode');
+const path = require('path');
+
+class ATK16HoverProvider {
+ async provideHover(document, position, token) {
+ const range = document.getWordRangeAtPosition(position, /[\w-/.]+/);
+ const labelText = document.getText(range);
+ const subroutineDefinition = await findSubroutineInDocument(document, labelText);
+ if (subroutineDefinition) {
+ const hoverText = new vscode.MarkdownString(subroutineDefinition);
+ hoverText.isTrusted = true;
+ return new vscode.Hover(hoverText);
+ }
+
+ // Search for labels in included files
+ for (let i = 0; i < document.lineCount; i++) {
+ const line = document.lineAt(i);
+ const includeMatch = line.text.match(/@include\s+(\S+)/);
+ if (includeMatch) {
+ const includedFilePath = path.join(
+ path.dirname(document.fileName),
+ `${includeMatch[1]}.atk16`
+ );
+
+ if (await vscode.workspace.fs.stat(vscode.Uri.file(includedFilePath))) {
+ const includedDocument = await vscode.workspace.openTextDocument(
+ includedFilePath
+ );
+ const subroutineDefinition = await findSubroutineInDocument(includedDocument, labelText);
+ if (subroutineDefinition) {
+ const hoverText = new vscode.MarkdownString(subroutineDefinition);
+ hoverText.isTrusted = true;
+ return new vscode.Hover(hoverText);
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+}
+
+class ATK16DefinitionProvider {
+ async provideDefinition(document, position, token) {
+ const range = document.getWordRangeAtPosition(position, /[\w-/.]+/);
+ const text = document.getText(range);
+
+ const line = document.lineAt(position);
+ const includeMatch = line.text.match(/@include\s+(\S+)/);
+ const useMatch = line.text.match(/@use\s+(\S+):/);
+
+ if (includeMatch && includeMatch[1] === text) {
+ const includedFilePath = path.join(
+ path.dirname(document.fileName),
+ `${includeMatch[1]}.atk16`
+ );
+
+ try {
+ await vscode.workspace.fs.stat(vscode.Uri.file(includedFilePath));
+ return new vscode.Location(
+ vscode.Uri.file(includedFilePath),
+ new vscode.Position(0, 0)
+ );
+ } catch (err) {
+ vscode.window.showErrorMessage(
+ `ATK16: File not found: ${includedFilePath}`
+ );
+ }
+ } else if (useMatch && useMatch[1] === text) {
+ const includedFilePath = path.join(
+ path.dirname(document.fileName),
+ `${useMatch[1]}.py`
+ );
+
+ try {
+ await vscode.workspace.fs.stat(vscode.Uri.file(includedFilePath));
+ return new vscode.Location(
+ vscode.Uri.file(includedFilePath),
+ new vscode.Position(0, 0)
+ );
+ } catch (err) {
+ vscode.window.showErrorMessage(
+ `ATK16: File not found: ${includedFilePath}`
+ );
+ }
+ } else {
+ const labelText = document.getText(range);
+ let labelLocation = await findLabelInDocument(document, labelText);
+ if (labelLocation) {
+ return labelLocation;
+ }
+
+ // Search for labels in included files
+ for (let i = 0; i < document.lineCount; i++) {
+ const line = document.lineAt(i);
+ const includeMatch = line.text.match(/@include\s+(\S+)/);
+ if (includeMatch) {
+ const includedFilePath = path.join(
+ path.dirname(document.fileName),
+ `${includeMatch[1]}.atk16`
+ );
+
+ if (await vscode.workspace.fs.stat(vscode.Uri.file(includedFilePath))) {
+ const includedDocument = await vscode.workspace.openTextDocument(
+ includedFilePath
+ );
+ labelLocation = await findLabelInDocument(includedDocument, labelText);
+ if (labelLocation) {
+ return labelLocation;
+ }
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+}
+
+async function findLabelInDocument(document, labelText) {
+ for (let i = 0; i < document.lineCount; i++) {
+ const line = document.lineAt(i);
+ if (line.text.includes(`@label ${labelText}`)) {
+ return new vscode.Location(document.uri, line.range.start);
+ }
+ }
+ return null;
+}
+
+async function findSubroutineInDocument(document, labelText) {
+ let name = null;
+ const params = [];
+ const returns = [];
+ let extra = "";
+ for (let i = 0; i < document.lineCount; i++) {
+ const line = document.lineAt(i);
+ if (line.text.includes(`@label ${labelText}`)) {
+ let labelDefinition = '';
+ for (let j = i + 1; j < document.lineCount; j++) {
+ const docLine = document.lineAt(j);
+ const docCommentMatch = docLine.text.match(/;\? (.*)/);
+ if (docCommentMatch) {
+ const lineText = docCommentMatch[1];
+ const parts = lineText.split(/\s/);
+ if (lineText.startsWith('subroutine')) {
+ name = parts[parts.length - 1];
+ } else if (lineText.startsWith('param')) {
+ const [_, reg, ...desc] = parts;
+ params.push([reg, desc.join(" ")])
+ } else if (lineText.startsWith('return')) {
+ const [_, reg, ...desc] = parts;
+ returns.push([reg, desc.join(" ")])
+ } else {
+ extra += `${lineText}\n`;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ if (name === null) return null;
+
+ let definition = `### ${name}\n_Subroutine_\n\n`;
+ definition += extra;
+ if (params.length > 0) {
+ definition += "\n\n|Reg|Parameter|\n";
+ definition += "|:---|:---|\n";
+ }
+ for (const [reg, desc] of params) {
+ definition += `|${reg}|${desc}|\n`;
+ }
+
+ if (returns.length > 0) {
+ definition += "\n|Reg|Return|\n";
+ definition += "|:---|:---|\n";
+ }
+ for (const [reg, desc] of returns) {
+ definition += `|${reg}|${desc}|\n`;
+ }
+ return definition;
+}
+
+function activate(context) {
+ context.subscriptions.push(
+ vscode.languages.registerDefinitionProvider('atk16', new ATK16DefinitionProvider()),
+ vscode.languages.registerHoverProvider('atk16', new ATK16HoverProvider())
+ );
+}
+
+function deactivate() {}
+
+module.exports = {
+ activate,
+ deactivate,
+}; \ No newline at end of file
diff --git a/atk16-syntax-highlighting/language-configuration.json b/atk16-syntax/language-configuration.json
index c186b1b..c186b1b 100644
--- a/atk16-syntax-highlighting/language-configuration.json
+++ b/atk16-syntax/language-configuration.json
diff --git a/atk16-syntax-highlighting/package.json b/atk16-syntax/package.json
index ff6be2b..bdfbb74 100644
--- a/atk16-syntax-highlighting/package.json
+++ b/atk16-syntax/package.json
@@ -1,13 +1,17 @@
{
- "name": "atk16-syntax-highlighting",
- "displayName": "ATK16 Syntax Highlighting",
- "description": "Syntax highlighting for ATK16 assembly language",
+ "name": "atk16-syntax",
+ "displayName": "ATK16 Syntax",
+ "description": "Syntax highlighting and support for ATK16 assembly language",
"version": "0.0.1",
- "publisher": "Jan Tuomi",
+ "publisher": "JanTuomi",
"engines": {
"vscode": "^1.60.0"
},
"categories": ["Programming Languages"],
+ "activationEvents": [
+ "onLanguage:atk16"
+ ],
+ "main": "./extension.js",
"contributes": {
"languages": [
{
@@ -23,6 +27,13 @@
"scopeName": "source.atk16",
"path": "./syntaxes/atk16.tmLanguage.json"
}
+ ],
+ "themes": [
+ {
+ "label": "ATK16 Color Theme",
+ "uiTheme": "vs-dark",
+ "path": "./themes/atk16-color-theme.json"
+ }
]
}
}
diff --git a/atk16-syntax-highlighting/syntaxes/atk16.tmLanguage.json b/atk16-syntax/syntaxes/atk16.tmLanguage.json
index 6028be3..1faadf9 100644
--- a/atk16-syntax-highlighting/syntaxes/atk16.tmLanguage.json
+++ b/atk16-syntax/syntaxes/atk16.tmLanguage.json
@@ -3,12 +3,16 @@
"name": "ATK16",
"patterns": [
{
+ "name": "comment.documentation.atk16",
+ "match": ";\\?.*$"
+ },
+ {
"name": "comment.line.semicolon.atk16",
"match": ";.*"
},
{
"name": "keyword.control.directive.atk16",
- "match": "@\\w+"
+ "match": "^@\\w+"
},
{
"name": "variable.other.register.atk16",
diff --git a/atk16-syntax/themes/atk16-color-theme.json b/atk16-syntax/themes/atk16-color-theme.json
new file mode 100644
index 0000000..d1aebb9
--- /dev/null
+++ b/atk16-syntax/themes/atk16-color-theme.json
@@ -0,0 +1,41 @@
+{
+ "name": "ATK16",
+ "type": "dark",
+ "colors": {
+ "editor.background": "#1E1E1E",
+ "editor.foreground": "#D4D4D4",
+ "editor.lineHighlightBackground": "#2D2D30"
+ },
+ "tokenColors": [
+ {
+ "scope": "keyword.control.directive.atk16",
+ "settings": {}
+ },
+ {
+ "scope": "variable.parameter.register.atk16",
+ "settings": {}
+ },
+ {
+ "scope": "constant.numeric.binary.atk16",
+ "settings": {}
+ },
+ {
+ "scope": "keyword.control.instruction.atk16",
+ "settings": {}
+ },
+ {
+ "scope": "support.function.atk16",
+ "settings": {}
+ },
+ {
+ "scope": "entity.name.label.atk16",
+ "settings": {}
+ },
+ {
+ "scope": "comment.documentation.atk16",
+ "settings": {
+ "fontStyle": "italic"
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/asm_pass0.py b/src/asm_pass0.py
new file mode 100644
index 0000000..be6c46a
--- /dev/null
+++ b/src/asm_pass0.py
@@ -0,0 +1,48 @@
+from dataclasses import dataclass
+from asm_ops import *
+from asm_eval import *
+
+@dataclass
+class Result0Line:
+ line_num: int
+ src_file: str
+ line: str
+
+@dataclass
+class Result0:
+ lines: list[Result0Line]
+ options: Options
+ operations: OpExpansionDict
+
+def pass_0(lines: list[str], file_name: str) -> Result0:
+ options = Options()
+ result_lines: list[Result0Line] = []
+ operations: OpExpansionDict = default_expansions.copy()
+
+ for (line_num, line) in enumerate(lines):
+ line = line.split(";")[0].strip()
+ if line == "": continue
+ keyword, *args = line.lower().split()
+ match keyword:
+ case "@include":
+ asm_file_name = args[0]
+ with open(asm_file_name, "r") as f:
+ for incl_line in f.readlines():
+ result_lines.append(Result0Line(
+ src_file=asm_file_name,
+ line_num=line_num,
+ line=incl_line
+ ))
+
+ case _:
+ result_lines.append(Result0Line(
+ src_file=file_name,
+ line_num=line_num,
+ line=line
+ ))
+
+ return Result0(
+ operations=operations,
+ options=options,
+ lines=result_lines
+ )
diff --git a/src/asm_pass1.py b/src/asm_pass1.py
index 606a1a1..fee72ff 100644
--- a/src/asm_pass1.py
+++ b/src/asm_pass1.py
@@ -2,10 +2,12 @@ import importlib
from dataclasses import dataclass
from asm_ops import *
from asm_eval import *
+from asm_pass0 import *
@dataclass
class Result1Line:
line_num: int
+ src_file: str
parts: list[str]
@dataclass
@@ -14,15 +16,13 @@ class Result1:
options: Options
operations: OpExpansionDict
-def pass_1(lines: list[str]) -> Result1:
+def pass_1(result0: Result0) -> Result1:
options = Options()
result_lines: list[Result1Line] = []
operations: OpExpansionDict = default_expansions.copy()
- for (line_num, line) in enumerate(lines):
- line = line.split(";")[0].strip()
- if line == "": continue
- keyword, *args = line.lower().split()
+ for line in result0.lines:
+ keyword, *args = line.line.lower().split()
match keyword:
case "@opt":
opt_name, opt_value = args
@@ -41,7 +41,8 @@ def pass_1(lines: list[str]) -> Result1:
operations[op] = expansion
case _:
result_lines.append(Result1Line(
- line_num=line_num,
+ src_file=line.src_file,
+ line_num=line.line_num,
parts=[keyword, *args]
))
diff --git a/src/asm_pass2.py b/src/asm_pass2.py
index e7f48ac..7272dcf 100644
--- a/src/asm_pass2.py
+++ b/src/asm_pass2.py
@@ -6,6 +6,7 @@ from asm_pass1 import *
@dataclass
class Result2Line:
line_num: int
+ src_file: str
parts: list[str]
original_parts: list[str]
@@ -30,6 +31,7 @@ def pass_2(result1: Result1) -> Result2:
for (idx, parts) in enumerate(output):
result_lines.append(Result2Line(
line_num=line.line_num,
+ src_file=line.src_file,
parts=parts,
original_parts=line.parts if idx == 0 else ["..."]
))
diff --git a/src/asm_pass3.py b/src/asm_pass3.py
index 038e3f7..76e7b84 100644
--- a/src/asm_pass3.py
+++ b/src/asm_pass3.py
@@ -6,6 +6,7 @@ from asm_pass2 import *
@dataclass
class Result3Line:
line_num: int
+ src_file: str
address: int
parts: list[str]
original_parts: list[str]
@@ -34,6 +35,7 @@ def pass_3(result2: Result2) -> Result3:
case _:
result_lines.append(Result3Line(
line_num=line.line_num,
+ src_file=line.src_file,
parts=line.parts,
address=address,
original_parts=line.original_parts,
diff --git a/src/asm_pass4.py b/src/asm_pass4.py
index 3d4f45c..dc65b65 100644
--- a/src/asm_pass4.py
+++ b/src/asm_pass4.py
@@ -6,6 +6,7 @@ from asm_pass3 import *
@dataclass
class Result4Line:
line_num: int
+ src_file: str
address: int
word: int
text: str
@@ -43,6 +44,7 @@ def pass_4(result3: Result3) -> Result4:
word = fn(meta, result3.labels, *args)
result_lines.append(Result4Line(
line_num=line.line_num,
+ src_file=line.src_file,
address=line.address,
word=word,
text=text,
@@ -52,13 +54,14 @@ def pass_4(result3: Result3) -> Result4:
try:
result_lines.append(Result4Line(
line_num=line.line_num,
+ src_file=line.src_file,
address=line.address,
word=eval_expr(result3.labels, keyword),
text=text,
original_text=original_text,
))
except:
- raise Exception(f"Invalid assembly at {line.line_num + 1}\n\n{line}")
+ raise Exception(f"Invalid assembly at {line.src_file}:{line.line_num + 1}\n\n{line}")
address += 1
diff --git a/src/assembler.py b/src/assembler.py
index 3f0c8bc..8f84288 100755
--- a/src/assembler.py
+++ b/src/assembler.py
@@ -4,6 +4,7 @@
import sys
from asm_ops import *
from asm_eval import *
+from asm_pass0 import pass_0
from asm_pass1 import pass_1
from asm_pass2 import pass_2
from asm_pass3 import pass_3
@@ -49,7 +50,8 @@ def parse(line: str) -> list[str]:
result.append(acc)
return list(filter(lambda x: len(x) > 0, result))
-result1 = pass_1(src_lines)
+result0 = pass_0(src_lines, infile_path)
+result1 = pass_1(result0)
result2 = pass_2(result1)
result3 = pass_3(result2)
result4 = pass_4(result3)