1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
---
# Sublime-syntax grammar for Solana SBPF disassembly as emitted by
# `solana_sbpf::static_analysis::Analysis::disassemble_instruction`.
#
# Token classes -> standard Sublime scopes so any theme colours them:
# - control flow (ja, jeq*, exit, return) -> keyword.control
# - calls (call, callx, syscall) -> keyword.other
# - ALU mnemonics (mov*, add*, sub*, ...) -> keyword.operator
# - load/store mnemonics (ldx*, stx*, lddw, ...) -> storage.type
# - registers (r0..r11) -> variable.language
# - numeric literals (0x..., decimals, +/- offs) -> constant.numeric
# - call/syscall targets -> entity.name.function
# - punctuation -> punctuation
# - line comments -> comment.line
#
# Kept declarative — no Rust code touches this file. To extend, add
# patterns under `match:` and pick an appropriate scope.
name: SBPF Assembly
file_extensions:
scope: source.sbpf
variables:
reg: '\br(?:1[01]|[0-9])\b'
hex: '0x[0-9a-fA-F]+'
dec: '[+-]?[0-9]+'
contexts:
main:
- match: '^\s*//.*$'
scope: comment.line.double-slash.sbpf
- match: '\b(?:exit|return)\b'
scope: keyword.control.return.sbpf
- match: '\b(?:ja|jeq|jne|jgt|jge|jlt|jle|jsgt|jsge|jslt|jsle|jset)(?:32)?\b'
scope: keyword.control.branch.sbpf
- match: '\b(?:call|callx)\b'
scope: keyword.other.call.sbpf
push: call_target
- match: '\bsyscall\b'
scope: keyword.other.syscall.sbpf
push: call_target
- match: '\b(?:mov|add|sub|mul|div|mod|or|and|xor|lsh|rsh|neg|arsh|le|be|hor)(?:32|64)?\b'
scope: keyword.operator.alu.sbpf
- match: '\b(?:lddw|ld|ldx[bhwdw]+|st[bhwdw]+|stx[bhwdw]+|ldxw|ldxh|ldxb|ldxdw|stxw|stxh|stxb|stxdw|stw|sth|stb|stdw)\b'
scope: storage.type.memory.sbpf
- include: registers
- include: numbers
- include: punct
# After a call/syscall mnemonic, color the next identifier as the
# target name so syscalls like `sol_log_` and internal labels like
# `function_42` stand out from default operands.
call_target:
- match: '\s+'
- match: '\[invalid\]'
scope: invalid.illegal.sbpf
pop: true
- match: '{{reg}}'
scope: variable.language.register.sbpf
pop: true
- match: '[A-Za-z_][\w@/\.]*'
scope: entity.name.function.sbpf
pop: true
- match: '{{hex}}|{{dec}}'
scope: constant.numeric.target.sbpf
pop: true
- match: '\S'
pop: true
registers:
- match: '{{reg}}'
scope: variable.language.register.sbpf
numbers:
- match: '{{hex}}'
scope: constant.numeric.hex.sbpf
- match: '{{dec}}'
scope: constant.numeric.dec.sbpf
punct:
- match: '\['
scope: punctuation.section.brackets.begin.sbpf
- match: '\]'
scope: punctuation.section.brackets.end.sbpf
- match: ','
scope: punctuation.separator.sbpf
- match: '[+\-*]'
scope: keyword.operator.arithmetic.sbpf