Krolyxon
Commit c48ef97

Branches

Tags

Refs →

bench51srcuisyntax.rs

122 lines · 3.8 KB · Rust

1//! Minimal 8051 assembly syntax highlighting.
2
3use ratatui::style::{Color, Modifier, Style};
4use ratatui::text::Span;
5
6use crate::{isa, sfr};
7
8const COMMENT: Style = Style::new().fg(Color::DarkGray);
9const LABEL: Style = Style::new().fg(Color::Yellow);
10const MNEMONIC: Style = Style::new().fg(Color::Cyan).add_modifier(Modifier::BOLD);
11const DIRECTIVE: Style = Style::new().fg(Color::Magenta);
12const REGISTER: Style = Style::new().fg(Color::LightBlue);
13const NUMBER: Style = Style::new().fg(Color::LightGreen);
14const STRING: Style = Style::new().fg(Color::Green);
15const SFR: Style = Style::new().fg(Color::LightMagenta);
16
17fn is_directive(w: &str) -> bool {
18 matches!(
19 w,
20 "ORG"
21 | "DB"
22 | "DW"
23 | "DS"
24 | "END"
25 | "EQU"
26 | "SET"
27 | "BIT"
28 | "DATA"
29 | "IDATA"
30 | "XDATA"
31 | "CODE"
32 | "CSEG"
33 | "XSEG"
34 | "DSEG"
35 | "AT"
36 | "USING"
37 | "NAME"
38 | "BYTE"
39 | "WORD"
40 )
41}
42
43fn word_style(w: &str, first_word: bool) -> Style {
44 let u = w.to_ascii_uppercase();
45 let reg = matches!(u.as_str(), "A" | "AB" | "C" | "DPTR" | "PC")
46 || (u.len() == 2
47 && u.starts_with('R')
48 && u.as_bytes()[1].is_ascii_digit()
49 && u.as_bytes()[1] < b'8');
50 if reg {
51 REGISTER
52 } else if is_directive(&u) {
53 DIRECTIVE
54 } else if first_word && isa::is_mnemonic(&u) {
55 MNEMONIC
56 } else if sfr::SFRS.iter().chain(sfr::BITS).any(|(n, _)| *n == u) {
57 SFR
58 } else if matches!(
59 u.as_str(),
60 "HIGH" | "LOW" | "MOD" | "AND" | "OR" | "XOR" | "NOT" | "SHL" | "SHR"
61 ) {
62 DIRECTIVE
63 } else {
64 Style::new()
65 }
66}
67
68/// Splits a source line into styled spans, applying `base` underneath.
69pub fn highlight(line: &str, base: Style) -> Vec<Span<'static>> {
70 let chars: Vec<char> = line.chars().collect();
71 let mut spans = Vec::new();
72 let mut i = 0;
73 let mut seen_word = false;
74 let push = |spans: &mut Vec<Span<'static>>, s: String, st: Style| {
75 spans.push(Span::styled(s, base.patch(st)))
76 };
77 while i < chars.len() {
78 let c = chars[i];
79 let start = i;
80 if c == ';' {
81 push(&mut spans, chars[i..].iter().collect(), COMMENT);
82 break;
83 } else if c == '\'' || c == '"' {
84 i += 1;
85 while i < chars.len() && chars[i] != c {
86 i += 1;
87 }
88 i = (i + 1).min(chars.len());
89 push(&mut spans, chars[start..i].iter().collect(), STRING);
90 } else if c.is_ascii_alphabetic() || c == '_' || c == '?' {
91 while i < chars.len()
92 && (chars[i].is_ascii_alphanumeric() || chars[i] == '_' || chars[i] == '?')
93 {
94 i += 1;
95 }
96 let w: String = chars[start..i].iter().collect();
97 let is_label = !seen_word && chars.get(i) == Some(&':');
98 let style = if is_label {
99 LABEL
100 } else {
101 let st = word_style(&w, !seen_word);
102 seen_word = true;
103 st
104 };
105 push(&mut spans, w, style);
106 } else if c.is_ascii_digit() || c == '#' || c == '$' {
107 i += 1;
108 while i < chars.len() && chars[i].is_ascii_alphanumeric() {
109 i += 1;
110 }
111 push(&mut spans, chars[start..i].iter().collect(), NUMBER);
112 } else {
113 while i < chars.len()
114 && !(chars[i].is_ascii_alphanumeric() || "_?;'\"#$".contains(chars[i]))
115 {
116 i += 1;
117 }
118 push(&mut spans, chars[start..i].iter().collect(), Style::new());
119 }
120 }
121 spans
122}