done with this theming stuff for now.

This commit is contained in:
ed
2026-08-15 01:15:43 -04:00
parent d23b6a2a36
commit 1a5b618484
10 changed files with 290 additions and 69 deletions
Binary file not shown.
+36 -26
View File
@@ -3,8 +3,12 @@
"type": "dark", "type": "dark",
"semanticHighlighting": true, "semanticHighlighting": true,
"colors": { "colors": {
"editor.background": "#121212", // 121212
"editor.foreground": "#c6b2a3", // 111212
// 211f1e
// 191817
"editor.background": "#191817",
"editor.foreground": "#dfc6ba",
"editor.lineHighlightBackground": "#1c1c1c", "editor.lineHighlightBackground": "#1c1c1c",
"editor.selectionBackground": "#164371", "editor.selectionBackground": "#164371",
"editor.selectionForeground": "#c8c8c8", "editor.selectionForeground": "#c8c8c8",
@@ -13,15 +17,15 @@
"editorIndentGuide.background1": "#181818", "editorIndentGuide.background1": "#181818",
"editorIndentGuide.activeBackground1": "#202020", "editorIndentGuide.activeBackground1": "#202020",
"editorRuler.foreground": "#505050", "editorRuler.foreground": "#505050",
"editorGutter.background": "#121212", "editorGutter.background": "#211f1e",
"editorBracketMatch.background": "#3b514d", "editorBracketMatch.background": "#3b514d",
"editor.foldBackground": "#0c0c0c81", "editor.foldBackground": "#0c0c0c6a",
"editor.wordHighlightBackground": "#1212124d", "editor.wordHighlightBackground": "#211f1e4d",
"editor.wordHighlightStrongBackground": "#303030", "editor.wordHighlightStrongBackground": "#303030",
"editorCursor.foreground": "#00fff4", "editorCursor.foreground": "#00fff4",
"editorWhitespace.foreground": "#181818", "editorWhitespace.foreground": "#181818",
// "editorLineHighlightBorder": "#1c1c1c", // "editorLineHighlightBorder": "#1c1c1c",
"editorWidget.background": "#121212", "editorWidget.background": "#211f1e",
"editorSuggestWidget.background": "#2c334b", "editorSuggestWidget.background": "#2c334b",
"editorHoverWidget.background": "#2c334b" "editorHoverWidget.background": "#2c334b"
}, },
@@ -30,16 +34,17 @@
"keyword": { "foreground": "#d8bd5b" }, "keyword": { "foreground": "#d8bd5b" },
"string": { "foreground": "#d46a54" }, "string": { "foreground": "#d46a54" },
"number": { "foreground": "#b5cea8" }, "number": { "foreground": "#b5cea8" },
"operator": { "foreground": "#54cfcf" }, "operator": { "foreground": "#be8e78" },
"class": { "foreground": "#54a4d6" }, "class": { "foreground": "#54a4d6" },
"struct": { "foreground": "#54a4d6" }, "struct": { "foreground": "#54a4d6" },
"enum": { "foreground": "#54a4d6" }, "enum": { "foreground": "#54a4d6" },
"type": { "foreground": "#54a4d6" }, "type": { "foreground": "#54a4d6" },
"interface": { "foreground": "#7984ab" }, "interface": { "foreground": "#7984ab" },
"function": { "foreground": "#cccab5" }, "function": { "foreground": "#cccab5" },
// "function": { "foreground": "#6090a9" },
"method": { "foreground": "#6090a9" }, "method": { "foreground": "#6090a9" },
"variable": { "foreground": "#bc966c" }, "variable": { "foreground": "#bc966c" },
"parameter": { "foreground": "#867660" }, "parameter": { "foreground": "#ce8365" },
"property": { "foreground": "#acb8c8" }, "property": { "foreground": "#acb8c8" },
"*.static": { "foreground": "#9e95c6" }, "*.static": { "foreground": "#9e95c6" },
"macro": { "foreground": "#5ea852" }, "macro": { "foreground": "#5ea852" },
@@ -48,25 +53,29 @@
"enumMember": { "foreground": "#a373b0" }, "enumMember": { "foreground": "#a373b0" },
"label": { "foreground": "#c8c8c8", "fontStyle": "bold" }, "label": { "foreground": "#c8c8c8", "fontStyle": "bold" },
"tapeAtomKeyword": { "foreground": "#d8bd5b", "fontStyle": "bold" }, "tapeAtomKeyword": { "foreground": "#d8bd5b", "fontStyle": "bold" },
"tapeAtomName": { "foreground": "#cccab5", "fontStyle": "bold" }, "tapeAtomName": { "foreground": "#b1b7d6", "fontStyle": "bold" },
"tapeComponentKeyword": { "foreground": "#d68a36", "fontStyle": "bold" }, "tapeComponentKeyword": { "foreground": "#d68a36", "fontStyle": "bold" },
"tapeComponentName": { "foreground": "#6090a9", "fontStyle": "bold" }, "tapeComponentName": { "foreground": "#b1b7d6", "fontStyle": "bold" },
"tapeAnnotation": { "foreground": "#d8bd5b" }, "tapeAnnotation": { "foreground": "#d8bd5b" },
"tapeBindType": { "foreground": "#54a4d6" }, "tapeBindType": { "foreground": "#54a4d6" },
"tapePhase": { "foreground": "#b8d7a3", "fontStyle": "italic" }, "tapePhase": { "foreground": "#b8d7a3", "fontStyle": "italic" },
"tapeLabel": { "foreground": "#959595", "fontStyle": "bold" }, "tapeLabel": { "foreground": "#959595", "fontStyle": "bold" },
"tapeCpuInstruction": { "foreground": "#6090a9" }, // "tapeCpuInstruction": { "foreground": "#6d9aa0" },
"tapeGteInstruction": { "foreground": "#9e95c6" }, // "tapeCpuInstruction": { "foreground": "#cf7539" },
// "tapeCpuInstruction": { "foreground": "#d16b3a" },
"tapeCpuInstruction": { "foreground": "#d5895a" },
"tapeGteInstruction": { "foreground": "#988bcb" },
"tapeGpuInstruction": { "foreground": "#bf7dac" }, "tapeGpuInstruction": { "foreground": "#bf7dac" },
"tapeComponentInstruction": { "foreground": "#8baa5d" }, "tapeComponentInstruction": { "foreground": "#8baa5d" },
"tapeGprRegister": { "foreground": "#bc966c" }, // "tapeGprRegister": { "foreground": "#92d4d9" },
"tapeCop2Register": { "foreground": "#9e95c6" }, "tapeGprRegister": { "foreground": "#a2bfa8" },
"tapeCop2Register": { "foreground": "#945cd9" },
"tapeDuffleType": { "foreground": "#54a4d6" }, "tapeDuffleType": { "foreground": "#54a4d6" },
"tapeAttribute": { "foreground": "#73a07c" }, "tapeAttribute": { "foreground": "#73a07c" },
"tapeGprRegister.tapeRead": { "foreground": "#8baa5d", "fontStyle": "italic" }, "tapeGprRegister.tapeRead": { "foreground": "#5bb8b0", "fontStyle": "italic" },
"tapeGprRegister.tapeWrite": { "foreground": "#d68a36", "fontStyle": "bold" }, "tapeGprRegister.tapeWrite": { "foreground": "#2d8f8c", "fontStyle": "bold" },
"tapeCop2Register.tapeRead": { "foreground": "#a373b0", "fontStyle": "italic" }, "tapeCop2Register.tapeRead": { "foreground": "#b08ae0", "fontStyle": "italic" },
"tapeCop2Register.tapeWrite": { "foreground": "#d46a54", "fontStyle": "bold" }, "tapeCop2Register.tapeWrite": { "foreground": "#7b3ec4", "fontStyle": "bold" },
// "*.tapeAuto": { }, // "*.tapeAuto": { },
"tapeControlFlow": { "foreground": "#63d169", "fontStyle": "bold" }, "tapeControlFlow": { "foreground": "#63d169", "fontStyle": "bold" },
"tapeDelaySlot": { "foreground": "#ff5647" } "tapeDelaySlot": { "foreground": "#ff5647" }
@@ -77,20 +86,21 @@
{ "scope": ["string", "string.quoted"], "settings": { "foreground": "#d46a54" } }, { "scope": ["string", "string.quoted"], "settings": { "foreground": "#d46a54" } },
{ "scope": ["string.quoted.other"], "settings": { "foreground": "#d69d85" } }, { "scope": ["string.quoted.other"], "settings": { "foreground": "#d69d85" } },
{ "scope": ["constant.numeric"], "settings": { "foreground": "#b5cea8" } }, { "scope": ["constant.numeric"], "settings": { "foreground": "#b5cea8" } },
{ "scope": ["punctuation", "keyword.operator"], "settings": { "foreground": "#00ffff" } }, { "scope": ["punctuation", "keyword.operator"], "settings": { "foreground": "#be8e78" } },
{ "scope": ["keyword.operator.overload"], "settings": { "foreground": "#00ff64" } }, { "scope": ["keyword.operator.overload"], "settings": { "foreground": "#b87e76" } },
{ "scope": ["entity.name.type", "entity.name.type.class", "entity.name.type.struct", "entity.name.type.enum"], "settings": { "foreground": "#54a4d6" } }, { "scope": ["entity.name.type", "entity.name.type.class", "entity.name.type.struct", "entity.name.type.enum"], "settings": { "foreground": "#54a4d6" } },
{ "scope": ["entity.name.type.interface"], "settings": { "foreground": "#7984ab" } }, { "scope": ["entity.name.type.interface"], "settings": { "foreground": "#7984ab" } },
{ "scope": ["entity.name.function"], "settings": { "foreground": "#cccab5" } }, { "scope": ["entity.name.function"], "settings": { "foreground": "#cccab5" } },
{ "scope": ["entity.name.function.member"], "settings": { "foreground": "#6090a9" } }, { "scope": ["entity.name.function.member"], "settings": { "foreground": "#6090a9" } },
{ "scope": ["variable.other.local"], "settings": { "foreground": "#bc966c" } }, { "scope": ["variable.other.local"], "settings": { "foreground": "#bc966c" } },
{ "scope": ["variable.parameter"], "settings": { "foreground": "#867660", "fontStyle": "underline" } }, { "scope": ["variable.parameter"], "settings": { "foreground": "#ce8365" } },
{ "scope": ["variable.other.property"], "settings": { "foreground": "#acb8c8" } }, { "scope": ["variable.other.property"], "settings": { "foreground": "#acb8c8" } },
{ "scope": ["variable.other.constant"], "settings": { "foreground": "#9e95c6" } }, { "scope": ["variable.other.constant"], "settings": { "foreground": "#9e95c6" } },
{ "scope": ["variable.other.global", "variable.other.defaultLibrary"], "settings": { "foreground": "#bf7dac" } }, { "scope": ["variable.other.global", "variable.other.defaultLibrary"], "settings": { "foreground": "#bf7dac" } },
{ "scope": ["support.type", "support.function"], "settings": { "foreground": "#8baa5d" } }, { "scope": ["support.type", "support.function"], "settings": { "foreground": "#8baa5d" } },
{ "scope": ["meta.preprocessor"], "settings": { "foreground": "#5ea852" } }, { "scope": ["meta.preprocessor"], "settings": { "foreground": "#5ea852" } },
{ "scope": ["variable.parameter.preprocessor"], "settings": { "foreground": "#636363" } }, // { "scope": ["variable.parameter.preprocessor"], "settings": { "foreground": "#636363" } },
{ "scope": ["variable.parameter.preprocessor"], "settings": { "foreground": "#bc966c" } },
{ "scope": ["keyword.control.directive"], "settings": { "foreground": "#d68a36" } }, { "scope": ["keyword.control.directive"], "settings": { "foreground": "#d68a36" } },
{ "scope": ["entity.name.namespace"], "settings": { "foreground": "#8e8e8e" } }, { "scope": ["entity.name.namespace"], "settings": { "foreground": "#8e8e8e" } },
{ "scope": ["entity.name.type.parameter"], "settings": { "foreground": "#b8d7a3" } }, { "scope": ["entity.name.type.parameter"], "settings": { "foreground": "#b8d7a3" } },
@@ -108,14 +118,14 @@
{ "scope": ["entity.name.type.duffle.bind"], "settings": { "foreground": "#54a4d6" } }, { "scope": ["entity.name.type.duffle.bind"], "settings": { "foreground": "#54a4d6" } },
{ "scope": ["entity.name.tag.duffle.phase"], "settings": { "foreground": "#b8d7a3", "fontStyle": "italic" } }, { "scope": ["entity.name.tag.duffle.phase"], "settings": { "foreground": "#b8d7a3", "fontStyle": "italic" } },
{ "scope": ["entity.name.label.duffle.atom"], "settings": { "foreground": "#c8c8c8", "fontStyle": "bold" } }, { "scope": ["entity.name.label.duffle.atom"], "settings": { "foreground": "#c8c8c8", "fontStyle": "bold" } },
{ "scope": ["support.function.duffle.cpu"], "settings": { "foreground": "#6090a9" } }, { "scope": ["support.function.duffle.cpu"], "settings": { "foreground": "#6d9aa0" } },
{ "scope": ["support.function.duffle.gte"], "settings": { "foreground": "#9e95c6" } }, { "scope": ["support.function.duffle.gte"], "settings": { "foreground": "#988bcb" } },
{ "scope": ["support.function.duffle.gpu"], "settings": { "foreground": "#bf7dac" } }, { "scope": ["support.function.duffle.gpu"], "settings": { "foreground": "#bf7dac" } },
{ "scope": ["support.function.duffle.component"], "settings": { "foreground": "#8baa5d" } }, { "scope": ["support.function.duffle.component"], "settings": { "foreground": "#8baa5d" } },
{ "scope": ["keyword.control.duffle.branch"], "settings": { "foreground": "#76ff7d", "fontStyle": "bold" } }, { "scope": ["keyword.control.duffle.branch"], "settings": { "foreground": "#76ff7d", "fontStyle": "bold" } },
{ "scope": ["keyword.operator.duffle.delayslot"], "settings": { "foreground": "#ff5647" } }, { "scope": ["keyword.operator.duffle.delayslot"], "settings": { "foreground": "#ff5647" } },
{ "scope": ["variable.other.constant.duffle.gpr"], "settings": { "foreground": "#bc966c" } }, { "scope": ["variable.other.constant.duffle.gpr"], "settings": { "foreground": "#3fa8a6" } },
{ "scope": ["variable.other.constant.duffle.cop2"], "settings": { "foreground": "#9e95c6" } }, { "scope": ["variable.other.constant.duffle.cop2"], "settings": { "foreground": "#945cd9" } },
{ "scope": ["storage.type.duffle.type"], "settings": { "foreground": "#54a4d6" } }, { "scope": ["storage.type.duffle.type"], "settings": { "foreground": "#54a4d6" } },
{ "scope": ["storage.modifier.duffle.attr"], "settings": { "foreground": "#73a07c" } } { "scope": ["storage.modifier.duffle.attr"], "settings": { "foreground": "#73a07c" } }
] ]
Binary file not shown.
+22 -9
View File
@@ -23,6 +23,7 @@ const TOKEN_TYPES = [
"tapeDuffleType", "tapeDuffleType",
"tapeAttribute", "tapeAttribute",
"keyword", "keyword",
"macro",
]; ];
const TOKEN_MODIFIERS = ["declaration", "tapeRead", "tapeWrite", "tapeAuto"]; const TOKEN_MODIFIERS = ["declaration", "tapeRead", "tapeWrite", "tapeAuto"];
@@ -38,11 +39,14 @@ const ANNOTATIONS = new Set([
]); ]);
const DSL_KEYWORDS = new Set([ const DSL_KEYWORDS = new Set([
"FI_", "I_", "NI_", "Relative_", "Struct_", "Enum_", "Union_", "FI_", "I_", "NI_", "Relative_", "Struct_", "Enum_", "Union_", "Array_",
"TypeR_", "TypeV_", "align_", "internal", "local_persist", "global", "Slice_", "TypeR_", "TypeV_", "align_", "internal", "local_persist", "global",
"RO_", "LP_", "gknown", "expect_", "cexpr_",
"asm", "asm_words", "asm_rpins", "asm_clobber",
"O_", "S_", "C_", "T_", "tmpl", "glue", "r_", "v_", "tr_", "tv_", "O_", "S_", "C_", "T_", "tmpl", "glue", "r_", "v_", "tr_", "tv_",
"rgcc", "rlit", "r_use", "r_set", "r_mod", "r_imm", "r_mem", "rgcc", "r_use", "r_set", "r_mod", "r_imm", "r_mem",
"enc_op", "enc_rs", "enc_rt", "enc_rd", "enc_imm", "enc_i", "enc_r", "u1_", "u2_", "u4_", "u8_", "s1_", "s2_", "s4_", "s8_",
"u1_r", "u2_r", "u4_r", "u8_r", "u1_v", "u2_v", "u4_v", "u8_v",
]); ]);
const DELAY_SLOT_KEYWORDS = new Set(["LdSlot_", "BdSlot_"]); const DELAY_SLOT_KEYWORDS = new Set(["LdSlot_", "BdSlot_"]);
@@ -67,15 +71,22 @@ function registerType(name, index) {
function instructionType(name, index) { function instructionType(name, index) {
const domain = index.macros.get(name); const domain = index.macros.get(name);
if (domain === "control") return "tapeControlFlow";
if (domain === "cpu") return "tapeCpuInstruction"; if (domain === "cpu") return "tapeCpuInstruction";
if (domain === "gte") return "tapeGteInstruction"; if (domain === "gte") return "tapeGteInstruction";
if (domain === "gpu") return "tapeGpuInstruction"; if (domain === "gpu") return "tapeGpuInstruction";
if (domain === "component") return "tapeComponentInstruction"; if (domain === "component") {
if (/^mac_gte_/.test(name)) return "tapeGteInstruction";
if (/^mac_gp/.test(name)) return "tapeGpuInstruction";
if (/^mac_/.test(name)) return "tapeComponentInstruction";
return "macro";
}
if (domain === "utility") return "macro";
if (/^gte_(?!cr_)/.test(name)) return "tapeGteInstruction"; if (/^gte_(?!cr_)/.test(name)) return "tapeGteInstruction";
if (/^gp[01]_/.test(name)) return "tapeGpuInstruction"; if (/^gp[01]_/.test(name)) return "tapeGpuInstruction";
if (/^mac_gte_/.test(name)) return "tapeGteInstruction"; if (/^mac_gte_/.test(name)) return "tapeGteInstruction";
if (/^mac_gp/.test(name)) return "tapeGpuInstruction"; if (/^mac_gp/.test(name)) return "tapeGpuInstruction";
if (/^mac_/.test(name)) return "tapeCpuInstruction"; if (/^mac_/.test(name)) return "tapeComponentInstruction";
return null; return null;
} }
@@ -123,7 +134,7 @@ function classifyDocument(source, filePath, workspaceIndex, shouldCancel = () =>
} else if (ATOM_KEYWORDS.has(token.text)) { } else if (ATOM_KEYWORDS.has(token.text)) {
type = "tapeAtomKeyword"; type = "tapeAtomKeyword";
} else if (COMPONENT_KEYWORDS.has(token.text)) { } else if (COMPONENT_KEYWORDS.has(token.text)) {
type = "tapeComponentKeyword"; type = "keyword";
} else if (ANNOTATIONS.has(token.text)) { } else if (ANNOTATIONS.has(token.text)) {
type = "tapeAnnotation"; type = "tapeAnnotation";
} else if (context && context.callee === "atom_bind" && context.argIndex === 0) { } else if (context && context.callee === "atom_bind" && context.argIndex === 0) {
@@ -159,19 +170,21 @@ function classifyDocument(source, filePath, workspaceIndex, shouldCancel = () =>
} }
if (!type && index.bindTypes.has(token.text)) type = "tapeBindType"; if (!type && index.bindTypes.has(token.text)) type = "tapeBindType";
if (!type && index.types.has(token.text)) type = "tapeDuffleType";
if (!type && DSL_KEYWORDS.has(token.text)) type = "keyword"; if (!type && DSL_KEYWORDS.has(token.text)) type = "keyword";
if (!type && index.types.has(token.text)) type = "tapeDuffleType";
if (!type && index.attributes.has(token.text)) type = "tapeAttribute"; if (!type && index.attributes.has(token.text)) type = "tapeAttribute";
if (!type) type = registerType(token.text, index); if (!type) type = registerType(token.text, index);
if (!type && DELAY_SLOT_KEYWORDS.has(token.text)) type = "tapeDelaySlot"; if (!type && DELAY_SLOT_KEYWORDS.has(token.text)) type = "tapeDelaySlot";
if (!type) { if (!type) {
const domain = index.macros.get(token.text); const domain = index.macros.get(token.text);
if (domain && CONTROL_FLOW_PREFIXES.test(token.text)) { if (domain === "control" || (domain && CONTROL_FLOW_PREFIXES.test(token.text))) {
type = "tapeControlFlow"; type = "tapeControlFlow";
} }
} }
if (!type && isRegUseAccess(scanned.tokens, tokenIndex)) type = "tapeGprRegister"; if (!type && isRegUseAccess(scanned.tokens, tokenIndex)) type = "tapeGprRegister";
if (!type) type = instructionType(token.text, index); if (!type) type = instructionType(token.text, index);
if (!type && /^(?:Slice_|A[0-9]+_)/.test(token.text)) type = "tapeDuffleType";
if (!type && /_[RV]$/.test(token.text)) type = "tapeDuffleType";
if (!type && index.atoms.has(token.text)) type = "tapeAtomName"; if (!type && index.atoms.has(token.text)) type = "tapeAtomName";
if (!type && index.components.has(token.text)) type = "tapeComponentName"; if (!type && index.components.has(token.text)) type = "tapeComponentName";
if (!type && index.phases.has(token.text)) type = "tapePhase"; if (!type && index.phases.has(token.text)) type = "tapePhase";
+4 -2
View File
@@ -39,7 +39,8 @@
{ "id": "tapeCop2Register", "superType": "variable", "description": "COP2 data or control register alias" }, { "id": "tapeCop2Register", "superType": "variable", "description": "COP2 data or control register alias" },
{ "id": "tapeDuffleType", "superType": "type", "description": "Duffle type or type constructor" }, { "id": "tapeDuffleType", "superType": "type", "description": "Duffle type or type constructor" },
{ "id": "tapeAttribute", "superType": "keyword", "description": "Duffle linkage or storage attribute" }, { "id": "tapeAttribute", "superType": "keyword", "description": "Duffle linkage or storage attribute" },
{ "id": "keyword", "description": "Standard keyword (DSL built-in macros)" } { "id": "keyword", "description": "Standard keyword (DSL built-in macros)" },
{ "id": "macro", "description": "Standard macro (utility #define with no instruction domain)" }
], ],
"semanticTokenModifiers": [ "semanticTokenModifiers": [
{ "id": "tapeRead", "description": "Register declared in atom_reads" }, { "id": "tapeRead", "description": "Register declared in atom_reads" },
@@ -68,7 +69,8 @@
"tapeCop2Register": ["variable.other.constant.duffle.cop2"], "tapeCop2Register": ["variable.other.constant.duffle.cop2"],
"tapeDuffleType": ["storage.type.duffle.type"], "tapeDuffleType": ["storage.type.duffle.type"],
"tapeAttribute": ["storage.modifier.duffle.attr"], "tapeAttribute": ["storage.modifier.duffle.attr"],
"keyword": ["keyword"] "keyword": ["keyword"],
"macro": ["entity.name.function.preprocessor"]
} }
} }
], ],
+126 -22
View File
@@ -8,12 +8,21 @@ const BASE_TYPES = [
"U1", "U2", "U4", "U8", "MipsAtom", "MipsCode", "Reg", "U1", "U2", "U4", "U8", "MipsAtom", "MipsCode", "Reg",
]; ];
const C_BUILTINS = new Set([
"void", "type", "char", "short", "int", "long", "float", "double",
"unsigned", "signed", "bool", "size_t", "uint8_t", "uint16_t", "uint32_t",
"int8_t", "int16_t", "int32_t",
]);
const BASE_ATTRIBUTES = [ const BASE_ATTRIBUTES = [
"FI_", "I_", "NI_", "Relative_", "Struct_", "Enum_", "Union_", "FI_", "I_", "NI_", "Relative_", "Struct_", "Enum_", "Union_", "Array_",
"TypeR_", "TypeV_", "align_", "internal", "local_persist", "global", "Slice_", "TypeR_", "TypeV_", "align_", "internal", "local_persist", "global",
"RO_", "LP_", "gknown", "expect_", "cexpr_",
"asm", "asm_words", "asm_rpins", "asm_clobber",
"O_", "S_", "C_", "T_", "tmpl", "glue", "r_", "v_", "tr_", "tv_", "O_", "S_", "C_", "T_", "tmpl", "glue", "r_", "v_", "tr_", "tv_",
"rgcc", "rlit", "r_use", "r_set", "r_mod", "r_imm", "r_mem", "rgcc", "r_use", "r_set", "r_mod", "r_imm", "r_mem",
"enc_op", "enc_rs", "enc_rt", "enc_rd", "enc_imm", "enc_i", "enc_r", "u1_", "u2_", "u4_", "u8_", "s1_", "s2_", "s4_", "s8_",
"u1_r", "u2_r", "u4_r", "u8_r", "u1_v", "u2_v", "u4_v", "u8_v",
]; ];
function createIndex() { function createIndex() {
@@ -28,6 +37,7 @@ function createIndex() {
phases: new Set(), phases: new Set(),
labels: new Set(), labels: new Set(),
attributes: new Set(BASE_ATTRIBUTES), attributes: new Set(BASE_ATTRIBUTES),
componentCallees: new Map(),
}; };
} }
@@ -38,6 +48,7 @@ function cloneIndex(source) {
} }
for (const [name, domain] of source.macros) result.macros.set(name, domain); for (const [name, domain] of source.macros) result.macros.set(name, domain);
for (const [name, domain] of source.registers) result.registers.set(name, domain); for (const [name, domain] of source.registers) result.registers.set(name, domain);
for (const [name, callees] of source.componentCallees) result.componentCallees.set(name, callees.slice());
return result; return result;
} }
@@ -48,18 +59,78 @@ function mergeIndexes(...sources) {
for (const key of ["atoms", "components", "componentAliases", "bindTypes", "types", "phases", "labels", "attributes"]) { for (const key of ["atoms", "components", "componentAliases", "bindTypes", "types", "phases", "labels", "attributes"]) {
for (const value of source[key]) result[key].add(value); for (const value of source[key]) result[key].add(value);
} }
for (const [name, domain] of source.macros) result.macros.set(name, domain); for (const [name, domain] of source.macros) {
const existing = result.macros.get(name);
if (!existing || domainRank(domain) >= domainRank(existing)) result.macros.set(name, domain);
}
for (const [name, domain] of source.registers) result.registers.set(name, domain); for (const [name, domain] of source.registers) result.registers.set(name, domain);
for (const [name, callees] of source.componentCallees) {
const existing = result.componentCallees.get(name) || [];
result.componentCallees.set(name, existing.concat(callees));
}
} }
return result; return resolveComponentDomains(result);
} }
function domainFromPath(filePath) { function domainFromPath(filePath) {
const base = path.basename(filePath.replaceAll("\\", "/")).toLowerCase(); const base = path.basename(filePath.replaceAll("\\", "/")).toLowerCase();
if (base === "mips.h" || base === "mips.atom.c") return "cpu"; if (base === "mips.h") return "cpu";
if (base === "gte.h" || base === "gte.atom.c") return "gte"; if (base === "gte.h") return "gte";
if (base === "gp.h" || base === "gp.atom.c") return "gpu"; if (base === "gp.h") return "gpu";
return "component"; return null;
}
function prefixDomain(name) {
if (/^(?:branch_|jump_|call_)/.test(name)) return "control";
if (/^gte_(?!cr_)/.test(name) || name.startsWith("mac_gte_") || name.startsWith("ac_gte_")) return "gte";
if (/^gp[01]_/.test(name) || name.startsWith("mac_gp_") || name.startsWith("ac_gp_")) return "gpu";
return null;
}
function collectBraceIdentifiers(tokens, openBraceIndex) {
const names = [];
let depth = 0;
for (let tokenIndex = openBraceIndex; tokenIndex < tokens.length; tokenIndex += 1) {
if (tokens[tokenIndex].text === "{") depth += 1;
if (tokens[tokenIndex].text === "}") {
depth -= 1;
if (depth === 0) break;
}
if (tokens[tokenIndex].kind === "identifier") names.push(tokens[tokenIndex].text);
}
return names;
}
function resolveComponentDomains(index) {
const hardwareRank = { cpu: 1, gpu: 2, gte: 3, control: 4 };
let changed = true;
while (changed) {
changed = false;
for (const [alias, callees] of index.componentCallees) {
let best = index.macros.get(alias) || "component";
let bestRank = hardwareRank[best] || 0;
for (const callee of callees) {
const domain = prefixDomain(callee) || index.macros.get(callee);
const rank = hardwareRank[domain] || 0;
if (rank > bestRank) {
best = domain;
bestRank = rank;
}
}
if (bestRank > 0 && index.macros.get(alias) !== best) {
index.macros.set(alias, best);
changed = true;
}
}
}
return index;
}
function domainRank(domain) {
if (domain === "control") return 4;
if (domain === "cpu" || domain === "gte" || domain === "gpu") return 3;
if (domain === "component") return 2;
return 1;
} }
function registerKind(name) { function registerKind(name) {
@@ -103,22 +174,23 @@ function scanSource(source, filePath) {
declarations.set(token.start, { role, modifiers }); declarations.set(token.start, { role, modifiers });
} }
function componentDomain(name) {
if (name.startsWith("ac_gte_") || name.startsWith("mac_gte_")) return "gte";
if (name.startsWith("ac_gp_") || name.startsWith("mac_gp_")) return "gpu";
return "cpu";
}
function addComponent(token) { function addComponent(token) {
index.components.add(token.text); index.components.add(token.text);
mark(token, "componentName"); mark(token, "componentName");
const alias = componentAlias(token.text); const alias = componentAlias(token.text);
if (alias) { if (alias) {
index.componentAliases.add(alias); index.componentAliases.add(alias);
index.macros.set(alias, componentDomain(token.text)); index.macros.set(alias, prefixDomain(alias) || prefixDomain(token.text) || "component");
} }
} }
function bindComponentCallees(alias, callees) {
if (!alias) return;
index.componentAliases.add(alias);
index.componentCallees.set(alias, callees);
if (!index.macros.has(alias)) index.macros.set(alias, "component");
}
for (let tokenIndex = 0; tokenIndex < tokens.length; tokenIndex += 1) { for (let tokenIndex = 0; tokenIndex < tokens.length; tokenIndex += 1) {
const token = tokens[tokenIndex]; const token = tokens[tokenIndex];
if (token.kind !== "identifier") continue; if (token.kind !== "identifier") continue;
@@ -173,8 +245,19 @@ function scanSource(source, filePath) {
if (name && name.kind === "identifier" && name.line === token.line) { if (name && name.kind === "identifier" && name.line === token.line) {
if (/^(?:RegUse_|Struct_|Enum_|Union_|TypeR_|TypeV_|Relative_|Binds_)/.test(name.text)) { if (/^(?:RegUse_|Struct_|Enum_|Union_|TypeR_|TypeV_|Relative_|Binds_)/.test(name.text)) {
index.types.add(name.text); index.types.add(name.text);
} else if (/^(?:ac_|mac_)/.test(name.text)) {
const alias = name.text.startsWith("ac_") ? componentAlias(name.text) : name.text;
const rest = [];
for (let restIndex = tokenIndex + 2; restIndex < tokens.length && tokens[restIndex].line === name.line; restIndex += 1) {
if (tokens[restIndex].kind === "identifier") rest.push(tokens[restIndex].text);
}
if (alias) {
index.componentAliases.add(alias);
index.macros.set(alias, prefixDomain(alias) || "component");
if (rest.length) index.componentCallees.set(alias, rest);
}
} else { } else {
index.macros.set(name.text, domain); index.macros.set(name.text, domain || "utility");
} }
} }
} }
@@ -185,10 +268,10 @@ function scanSource(source, filePath) {
let lastIdentifier = null; let lastIdentifier = null;
while (endIndex < tokens.length && tokens[endIndex].text !== ";") { while (endIndex < tokens.length && tokens[endIndex].text !== ";") {
if (tokens[endIndex].text === "{") hasBrace = true; if (tokens[endIndex].text === "{") hasBrace = true;
if (tokens[endIndex].kind === "identifier") lastIdentifier = tokens[endIndex]; if (tokens[endIndex].kind === "identifier" && !C_BUILTINS.has(tokens[endIndex].text)) lastIdentifier = tokens[endIndex];
endIndex += 1; endIndex += 1;
} }
if (!hasBrace && lastIdentifier) { if (!hasBrace && lastIdentifier && !C_BUILTINS.has(lastIdentifier.text)) {
index.types.add(lastIdentifier.text); index.types.add(lastIdentifier.text);
mark(lastIdentifier, "duffleType"); mark(lastIdentifier, "duffleType");
} }
@@ -213,7 +296,27 @@ function scanSource(source, filePath) {
} }
for (const call of balanced.calls) { for (const call of balanced.calls) {
if (domain === "component") continue; if (call.callee === "MipsAtomComp_") {
const name = tokens[call.openTokenIndex + 1];
const brace = tokens[call.closeTokenIndex + 1];
if (name && name.kind === "identifier" && brace && brace.text === "{") {
bindComponentCallees(componentAlias(name.text), collectBraceIdentifiers(tokens, call.closeTokenIndex + 1));
}
}
if (call.callee === "MipsAtomComp_Proc_") {
const functionName = findFunctionNameBefore(tokens, call.calleeTokenIndex);
let braceIndex = -1;
for (let tokenIndex = call.openTokenIndex + 1; tokenIndex < call.closeTokenIndex; tokenIndex += 1) {
if (tokens[tokenIndex].text === "{") {
braceIndex = tokenIndex;
break;
}
}
if (functionName && braceIndex >= 0) {
bindComponentCallees(componentAlias(functionName.text), collectBraceIdentifiers(tokens, braceIndex));
}
}
if (!domain) continue;
const name = tokens[call.calleeTokenIndex]; const name = tokens[call.calleeTokenIndex];
const after = tokens[call.closeTokenIndex + 1]; const after = tokens[call.closeTokenIndex + 1];
if (!name || !after || after.text !== "{") continue; if (!name || !after || after.text !== "{") continue;
@@ -221,7 +324,7 @@ function scanSource(source, filePath) {
} }
return { return {
index: cloneIndex(index), index: resolveComponentDomains(cloneIndex(index)),
declarations, declarations,
tokens, tokens,
contexts, contexts,
@@ -233,5 +336,6 @@ module.exports = {
createIndex, createIndex,
domainFromPath, domainFromPath,
mergeIndexes, mergeIndexes,
resolveComponentDomains,
scanSource, scanSource,
}; };
@@ -29,11 +29,11 @@
{ {
"match": "\\b(MipsAtomComp_)\\s*\\(\\s*(ac_[A-Za-z0-9_]*)", "match": "\\b(MipsAtomComp_)\\s*\\(\\s*(ac_[A-Za-z0-9_]*)",
"captures": { "captures": {
"1": { "name": "keyword.control.duffle.component" }, "1": { "name": "keyword" },
"2": { "name": "entity.name.function.duffle.component" } "2": { "name": "entity.name.function.duffle.component" }
} }
}, },
{ "match": "\\bMipsAtomComp_Proc_\\b", "name": "keyword.control.duffle.component" } { "match": "\\bMipsAtomComp_Proc_\\b", "name": "keyword" }
] ]
}, },
"annotation-arguments": { "annotation-arguments": {
@@ -64,7 +64,7 @@
"name": "storage.type.duffle.type" "name": "storage.type.duffle.type"
}, },
"attributes": { "attributes": {
"match": "\\b(?:FI_|I_|NI_|Relative_|align_|internal|local_persist|global|O_|S_|C_|T_|tmpl|glue|r_|v_|tr_|tv_|rgcc|rlit|r_use|r_set|r_mod|r_imm|r_mem|enc_op|enc_rs|enc_rt|enc_rd|enc_imm|enc_i|enc_r)\\b", "match": "\\b(?:FI_|I_|NI_|Relative_|Struct_|Enum_|Union_|Array_|Slice_|TypeR_|TypeV_|align_|internal|local_persist|global|RO_|LP_|gknown|expect_|cexpr_|asm|asm_words|asm_rpins|asm_clobber|O_|S_|C_|T_|tmpl|glue|r_|v_|tr_|tv_|rgcc|r_use|r_set|r_mod|r_imm|r_mem|u[1248]_|u[1248]_r|u[1248]_v|s[1248]_)\\b",
"name": "keyword" "name": "keyword"
} }
} }
+38 -3
View File
@@ -45,7 +45,7 @@ test("classifyDocument separates CPU, GTE, GPU, and component domains", () => {
workspace.macros.set("load_word", "cpu"); workspace.macros.set("load_word", "cpu");
workspace.macros.set("gte_cmdw_rtpt", "gte"); workspace.macros.set("gte_cmdw_rtpt", "gte");
workspace.macros.set("gp1_word_DisplayOn", "gpu"); workspace.macros.set("gp1_word_DisplayOn", "gpu");
workspace.macros.set("mac_yield", "component"); workspace.macros.set("mac_yield", "control");
workspace.componentAliases.add("mac_yield"); workspace.componentAliases.add("mac_yield");
const source = "load_word(R_T0, R_T1, 0), gte_cmdw_rtpt, gp1_word_DisplayOn(), mac_yield(), C2_MAC0, gte_cr_OFX_Code"; const source = "load_word(R_T0, R_T1, 0), gte_cmdw_rtpt, gp1_word_DisplayOn(), mac_yield(), C2_MAC0, gte_cr_OFX_Code";
@@ -54,20 +54,55 @@ test("classifyDocument separates CPU, GTE, GPU, and component domains", () => {
assert.equal(byText(result, "load_word")[0].type, "tapeCpuInstruction"); assert.equal(byText(result, "load_word")[0].type, "tapeCpuInstruction");
assert.equal(byText(result, "gte_cmdw_rtpt")[0].type, "tapeGteInstruction"); assert.equal(byText(result, "gte_cmdw_rtpt")[0].type, "tapeGteInstruction");
assert.equal(byText(result, "gp1_word_DisplayOn")[0].type, "tapeGpuInstruction"); assert.equal(byText(result, "gp1_word_DisplayOn")[0].type, "tapeGpuInstruction");
assert.equal(byText(result, "mac_yield")[0].type, "tapeComponentInstruction"); assert.equal(byText(result, "mac_yield")[0].type, "tapeControlFlow");
assert.equal(byText(result, "C2_MAC0")[0].type, "tapeCop2Register"); assert.equal(byText(result, "C2_MAC0")[0].type, "tapeCop2Register");
assert.equal(byText(result, "gte_cr_OFX_Code")[0].type, "tapeCop2Register"); assert.equal(byText(result, "gte_cr_OFX_Code")[0].type, "tapeCop2Register");
}); });
test("component invocations keep the domain resolved from their emitted instructions", () => {
const workspace = createIndex();
workspace.macros.set("mac_load_word_imm", "cpu");
workspace.macros.set("mac_gcmd_push", "gpu");
workspace.macros.set("mac_gte_store_f3", "gte");
workspace.macros.set("mac_load_v3s4", "cpu");
const source = "mac_load_word_imm(dst, imm), mac_gcmd_push(cmd), mac_gte_store_f3(cursor), mac_load_v3s4()";
const result = classifyDocument(source, "C:/x/code/hello_camera/hello_camera.atom.c", workspace);
assert.equal(byText(result, "mac_load_word_imm")[0].type, "tapeCpuInstruction");
assert.equal(byText(result, "mac_gcmd_push")[0].type, "tapeGpuInstruction");
assert.equal(byText(result, "mac_gte_store_f3")[0].type, "tapeGteInstruction");
assert.equal(byText(result, "mac_load_v3s4")[0].type, "tapeCpuInstruction");
});
test("utility macros without a hardware domain use the standard macro token", () => {
const workspace = createIndex();
workspace.macros.set("load_word", "cpu");
workspace.macros.set("assert", "utility");
workspace.macros.set("stringify", "utility");
workspace.macros.set("u4_hi", "utility");
const source = "load_word(R_T0, R_T1, 0), assert(ok), stringify(name), u4_hi(imm)";
const result = classifyDocument(source, "C:/x/code/hello_camera/hello_camera.c", workspace);
assert.equal(byText(result, "load_word")[0].type, "tapeCpuInstruction");
assert.equal(byText(result, "assert")[0].type, "macro");
assert.equal(byText(result, "stringify")[0].type, "macro");
assert.equal(byText(result, "u4_hi")[0].type, "macro");
});
test("document-local declarations override an empty workspace index", () => { test("document-local declarations override an empty workspace index", () => {
const source = [ const source = [
"MipsAtomComp_(ac_new_component) { nop };", "MipsAtomComp_(ac_new_component) { nop };",
"MipsAtomComp_Proc_(ab, { nop })",
"mac_new_component(),", "mac_new_component(),",
].join("\n"); ].join("\n");
const result = classifyDocument(source, "C:/x/code/duffle/math.atom.c", createIndex()); const result = classifyDocument(source, "C:/x/code/duffle/math.atom.c", createIndex());
assert.equal(byText(result, "MipsAtomComp_")[0].type, "keyword");
assert.equal(byText(result, "MipsAtomComp_Proc_")[0].type, "keyword");
assert.equal(byText(result, "ac_new_component")[0].type, "tapeComponentName"); assert.equal(byText(result, "ac_new_component")[0].type, "tapeComponentName");
assert.equal(byText(result, "mac_new_component")[0].type, "tapeCpuInstruction"); assert.equal(byText(result, "mac_new_component")[0].type, "tapeComponentInstruction");
}); });
test("classifier returns ordered non-overlapping spans and partial malformed output", () => { test("classifier returns ordered non-overlapping spans and partial malformed output", () => {
+1 -1
View File
@@ -57,7 +57,7 @@ test("every semantic token has a scope mapping; DSL-specific tokens also have gr
const grammarScopes = collectScopeNames(grammar); const grammarScopes = collectScopeNames(grammar);
const grammarRequired = new Set([ const grammarRequired = new Set([
"tapeAtomKeyword", "tapeAtomName", "tapeComponentKeyword", "tapeComponentName", "tapeAtomKeyword", "tapeAtomName", "tapeComponentName",
"tapeAnnotation", "tapeBindType", "tapePhase", "tapeLabel", "tapeAnnotation", "tapeBindType", "tapePhase", "tapeLabel",
"tapeDelaySlot", "tapeDuffleType", "keyword", "tapeDelaySlot", "tapeDuffleType", "keyword",
]); ]);
+60 -3
View File
@@ -26,7 +26,8 @@ test("scanSource discovers current atom and component forms", () => {
assert.equal(result.index.components.has("ac_store_pair"), true); assert.equal(result.index.components.has("ac_store_pair"), true);
assert.equal(result.index.componentAliases.has("mac_load_pair"), true); assert.equal(result.index.componentAliases.has("mac_load_pair"), true);
assert.equal(result.index.componentAliases.has("mac_store_pair"), true); assert.equal(result.index.componentAliases.has("mac_store_pair"), true);
assert.equal(result.index.macros.get("mac_store_pair"), "cpu"); assert.equal(result.index.macros.get("mac_store_pair"), "component");
assert.equal(result.index.componentCallees.get("mac_store_pair").includes("store_word"), true);
assert.equal(result.index.phases.has("cube_g4"), true); assert.equal(result.index.phases.has("cube_g4"), true);
assert.equal(result.index.registers.get("R_PrimCursor"), "gpr"); assert.equal(result.index.registers.get("R_PrimCursor"), "gpr");
assert.deepEqual(result.errors, []); assert.deepEqual(result.errors, []);
@@ -58,12 +59,68 @@ test("scanSource discovers binds, labels, registers, typedefs, and macro domains
}); });
test("domainFromPath uses the declaration file rather than parent directory names", () => { test("domainFromPath uses the declaration file rather than parent directory names", () => {
assert.equal(domainFromPath("C:/x/code/hello_gte/hello_gte.atom.c"), "component"); assert.equal(domainFromPath("C:/x/code/hello_gte/hello_gte.atom.c"), null);
assert.equal(domainFromPath("C:/x/code/duffle/mips.h"), "cpu"); assert.equal(domainFromPath("C:/x/code/duffle/mips.h"), "cpu");
assert.equal(domainFromPath("C:/x/code/duffle/gte.atom.c"), "gte"); assert.equal(domainFromPath("C:/x/code/duffle/gte.h"), "gte");
assert.equal(domainFromPath("C:/x/code/duffle/gp.h"), "gpu"); assert.equal(domainFromPath("C:/x/code/duffle/gp.h"), "gpu");
}); });
test("component aliases inherit the domain of the instructions they emit", () => {
const headers = mergeIndexes(
scanSource("#define load_word(a,b,c) 1\n#define store_word(a,b,c) 1\n#define shift_aright_var(a,b,c) 1\n#define jump_reg(rd) 1\n", "C:/x/code/duffle/mips.h").index,
scanSource("#define gte_sw(rt, base, off) 1\n", "C:/x/code/duffle/gte.h").index
);
const math = scanSource(
[
"MipsAtomComp_(ac_load_v3s4) { load_word(R_T0, R_T1, 0) };",
"#define mac_load_p3s4 mac_load_v3s4",
].join("\n"),
"C:/x/code/duffle/math.atom.c"
);
const shift = scanSource(
"MipsAtomComp_(ac_shift_aright_var_v3_self) { shift_aright_var(R_T0, R_T0, R_T1) };",
"C:/x/code/duffle/gte.atom.c"
);
const gte = scanSource(
"MipsAtomComp_(ac_gte_store_f3) { gte_sw(C2_SXY0, R_T0, 0) };",
"C:/x/code/duffle/gte.atom.c"
);
const yieldAtom = scanSource(
"MipsAtomComp_(ac_yield) { load_word(R_AtomJmp, R_TapePtr, 0), jump_reg(R_AtomJmp), nop };",
"C:/x/code/duffle/lottes_tape.h"
);
const merged = mergeIndexes(headers, math.index, shift.index, gte.index, yieldAtom.index);
assert.equal(merged.macros.get("mac_load_v3s4"), "cpu");
assert.equal(merged.macros.get("mac_load_p3s4"), "cpu");
assert.equal(merged.macros.get("mac_shift_aright_var_v3_self"), "cpu");
assert.equal(merged.macros.get("mac_gte_store_f3"), "gte");
assert.equal(merged.macros.get("mac_yield"), "control");
});
test("scanSource tags utility header defines as utility, not a hardware domain", () => {
const source = [
"#define assert(cond) ((void)(cond))",
"#define stringify(name) #name",
"#define u4_hi(imm) ((imm) >> 16)",
].join("\n");
const result = scanSource(source, "C:/projects/Pikuma/ps1/code/duffle/dsl.h");
assert.equal(result.index.macros.get("assert"), "utility");
assert.equal(result.index.macros.get("stringify"), "utility");
assert.equal(result.index.macros.get("u4_hi"), "utility");
});
test("mergeIndexes prefers a hardware domain over a later utility define", () => {
const left = createIndex();
left.macros.set("sub_s", "utility");
const right = createIndex();
right.macros.set("sub_s", "cpu");
assert.equal(mergeIndexes(left, right).macros.get("sub_s"), "cpu");
assert.equal(mergeIndexes(right, left).macros.get("sub_s"), "cpu");
});
test("mergeIndexes preserves domain-specific aliases", () => { test("mergeIndexes preserves domain-specific aliases", () => {
const left = createIndex(); const left = createIndex();
left.macros.set("load_word", "cpu"); left.macros.set("load_word", "cpu");