1use crate::nan_value::NanValue;
2
3use super::symbol::VmSymbolTable;
4
5#[derive(Debug, Clone)]
7pub struct FnChunk {
8 pub name: String,
9 pub arity: u8,
10 pub local_count: u16,
11 pub code: Vec<u8>,
12 pub constants: Vec<NanValue>,
13 pub effects: Vec<u32>,
15 pub thin: bool,
18 pub parent_thin: bool,
21 pub leaf: bool,
25 pub source_file: String,
27 pub line_table: Vec<(u16, u16)>,
30}
31
32#[derive(Debug, Clone)]
34pub struct CallFrame {
35 pub fn_id: u32,
37 pub ip: u32,
39 pub bp: u32,
41 pub local_count: u16,
43 pub arena_mark: u32,
46 pub yard_base: u32,
49 pub yard_mark: u32,
53 pub handoff_mark: u32,
56 pub globals_dirty: bool,
58 pub yard_dirty: bool,
61 pub handoff_dirty: bool,
64 pub thin: bool,
66 pub parent_thin: bool,
69}
70
71#[derive(Debug, Clone)]
73pub struct CodeStore {
74 pub functions: Vec<FnChunk>,
75 pub fn_index: std::collections::HashMap<String, u32>,
77 pub(crate) symbols: VmSymbolTable,
79 pub(crate) record_field_slots: std::collections::HashMap<(u32, u32), u8>,
81}
82
83impl Default for CodeStore {
84 fn default() -> Self {
85 Self::new()
86 }
87}
88
89impl CodeStore {
90 pub fn new() -> Self {
91 CodeStore {
92 functions: Vec::new(),
93 fn_index: std::collections::HashMap::new(),
94 symbols: VmSymbolTable::default(),
95 record_field_slots: std::collections::HashMap::new(),
96 }
97 }
98
99 pub fn add_function(&mut self, chunk: FnChunk) -> u32 {
100 let id = self.functions.len() as u32;
101 self.fn_index.insert(chunk.name.clone(), id);
102 self.functions.push(chunk);
103 id
104 }
105
106 pub fn get(&self, id: u32) -> &FnChunk {
107 &self.functions[id as usize]
108 }
109
110 pub fn find(&self, name: &str) -> Option<u32> {
111 self.fn_index.get(name).copied()
112 }
113
114 pub fn register_record_fields(&mut self, type_id: u32, field_symbol_ids: &[u32]) {
115 for (field_idx, symbol_id) in field_symbol_ids.iter().copied().enumerate() {
116 self.record_field_slots
117 .insert((type_id, symbol_id), field_idx as u8);
118 }
119 }
120
121 pub fn resolve_source_location(&self, fn_id: u32, ip: u32) -> Option<(&str, u16)> {
124 let chunk = self.functions.get(fn_id as usize)?;
125 if chunk.line_table.is_empty() {
126 return None;
127 }
128 let ip16 = ip as u16;
130 let idx = match chunk
131 .line_table
132 .binary_search_by_key(&ip16, |&(off, _)| off)
133 {
134 Ok(i) => i,
135 Err(0) => return None,
136 Err(i) => i - 1,
137 };
138 let (_, line) = chunk.line_table[idx];
139 let file = if chunk.source_file.is_empty() {
140 None
141 } else {
142 Some(chunk.source_file.as_str())
143 };
144 Some((file.unwrap_or(""), line))
145 }
146}
147
148#[derive(Debug, Default, Clone)]
150pub struct VmSourceLoc {
151 pub file: String,
152 pub line: u16,
153 pub fn_name: String,
154}
155
156#[derive(Debug)]
158pub enum VmError {
159 Runtime { msg: String, line: u16 },
161 Type { msg: String, line: u16 },
163 MatchFail(u16),
165 StackUnderflow,
167}
168
169impl VmError {
170 pub fn runtime(msg: impl Into<String>) -> Self {
171 VmError::Runtime {
172 msg: msg.into(),
173 line: 0,
174 }
175 }
176
177 pub fn type_err(msg: impl Into<String>) -> Self {
178 VmError::Type {
179 msg: msg.into(),
180 line: 0,
181 }
182 }
183
184 pub fn with_location(self, loc: Option<VmSourceLoc>) -> Self {
186 let Some(loc) = loc else { return self };
187 if loc.line == 0 {
188 return self;
189 }
190 match self {
191 VmError::Runtime { msg, line: 0 } => VmError::Runtime {
192 msg,
193 line: loc.line,
194 },
195 VmError::Type { msg, line: 0 } => VmError::Type {
196 msg,
197 line: loc.line,
198 },
199 other => other,
200 }
201 }
202}
203
204impl std::fmt::Display for VmError {
205 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
206 match self {
207 VmError::Runtime { msg, line } if *line > 0 => {
208 write!(f, "Runtime error [line {}]: {}", line, msg)
209 }
210 VmError::Runtime { msg, .. } => write!(f, "Runtime error: {}", msg),
211 VmError::Type { msg, line } if *line > 0 => {
212 write!(f, "Type error [line {}]: {}", line, msg)
213 }
214 VmError::Type { msg, .. } => write!(f, "Type error: {}", msg),
215 VmError::MatchFail(line) => write!(f, "Non-exhaustive match at line {}", line),
216 VmError::StackUnderflow => write!(f, "Internal error: stack underflow"),
217 }
218 }
219}
220
221impl std::error::Error for VmError {}