1use std::collections::HashMap;
2
3use crate::debug_info::DebugInfoBuilder;
4use crate::{OpCode, Program, Value};
5
6pub struct BytecodeBuilder {
7 code: Vec<u8>,
8}
9
10#[derive(Debug)]
11pub enum AssemblerError {
12 DuplicateLabel(String),
13 UnknownLabel(String),
14}
15
16impl std::fmt::Display for AssemblerError {
17 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18 match self {
19 AssemblerError::DuplicateLabel(label) => write!(f, "duplicate label '{label}'"),
20 AssemblerError::UnknownLabel(label) => write!(f, "unknown label '{label}'"),
21 }
22 }
23}
24
25impl std::error::Error for AssemblerError {}
26
27struct Fixup {
28 at: usize,
29 label: String,
30}
31
32pub struct Assembler {
33 code: Vec<u8>,
34 constants: Vec<Value>,
35 int_constants: HashMap<i64, u32>,
36 float_constants: HashMap<u64, u32>,
37 bool_constants: HashMap<bool, u32>,
38 string_constants: HashMap<String, u32>,
39 bytes_constants: HashMap<Vec<u8>, u32>,
40 labels: HashMap<String, u32>,
41 fixups: Vec<Fixup>,
42 debug: DebugInfoBuilder,
43}
44
45impl Default for Assembler {
46 fn default() -> Self {
47 Self::new()
48 }
49}
50
51impl Assembler {
52 pub fn new() -> Self {
53 Self {
54 code: Vec::new(),
55 constants: Vec::new(),
56 int_constants: HashMap::new(),
57 float_constants: HashMap::new(),
58 bool_constants: HashMap::new(),
59 string_constants: HashMap::new(),
60 bytes_constants: HashMap::new(),
61 labels: HashMap::new(),
62 fixups: Vec::new(),
63 debug: DebugInfoBuilder::new(),
64 }
65 }
66
67 pub fn position(&self) -> u32 {
68 self.code.len() as u32
69 }
70
71 pub fn label(&mut self, name: &str) -> Result<(), AssemblerError> {
72 if self.labels.contains_key(name) {
73 return Err(AssemblerError::DuplicateLabel(name.to_string()));
74 }
75 let pos = self.position();
76 self.labels.insert(name.to_string(), pos);
77 Ok(())
78 }
79
80 pub fn set_source(&mut self, source: String) {
81 self.debug.set_source(source);
82 }
83
84 pub fn mark_line(&mut self, line: u32) {
85 let offset = self.code.len() as u32;
86 self.debug.mark_line(offset, line);
87 }
88
89 pub fn add_function(&mut self, name: String, args: Vec<String>) {
90 self.debug.add_function(name, args);
91 }
92
93 pub fn add_local(&mut self, name: String, index: u8) {
94 self.debug.add_local(name, index);
95 }
96
97 pub fn add_local_with_range(
98 &mut self,
99 name: String,
100 index: u8,
101 declared_line: Option<u32>,
102 last_line: Option<u32>,
103 ) {
104 self.debug
105 .add_local_with_range(name, index, declared_line, last_line);
106 }
107
108 pub fn add_constant(&mut self, value: Value) -> u32 {
109 match value {
110 Value::Int(number) => {
111 if let Some(index) = self.int_constants.get(&number).copied() {
112 return index;
113 }
114 let index = self.constants.len() as u32;
115 self.constants.push(Value::Int(number));
116 self.int_constants.insert(number, index);
117 index
118 }
119 Value::Float(number) => {
120 let bits = number.to_bits();
121 if let Some(index) = self.float_constants.get(&bits).copied() {
122 return index;
123 }
124 let index = self.constants.len() as u32;
125 self.constants.push(Value::Float(number));
126 self.float_constants.insert(bits, index);
127 index
128 }
129 Value::Bool(flag) => {
130 if let Some(index) = self.bool_constants.get(&flag).copied() {
131 return index;
132 }
133 let index = self.constants.len() as u32;
134 self.constants.push(Value::Bool(flag));
135 self.bool_constants.insert(flag, index);
136 index
137 }
138 Value::String(text) => {
139 if let Some(index) = self.string_constants.get(text.as_str()).copied() {
140 return index;
141 }
142 let index = self.constants.len() as u32;
143 self.constants.push(Value::String(text.clone()));
144 self.string_constants.insert(text.as_ref().clone(), index);
145 index
146 }
147 Value::Bytes(bytes) => {
148 if let Some(index) = self.bytes_constants.get(bytes.as_ref()).copied() {
149 return index;
150 }
151 let index = self.constants.len() as u32;
152 self.constants.push(Value::Bytes(bytes.clone()));
153 self.bytes_constants.insert(bytes.as_ref().clone(), index);
154 index
155 }
156 other => {
157 let index = self.constants.len() as u32;
158 self.constants.push(other);
159 index
160 }
161 }
162 }
163
164 pub fn push_const(&mut self, value: Value) -> u32 {
165 let index = self.add_constant(value);
166 self.ldc(index);
167 index
168 }
169
170 pub fn finish_program(mut self) -> Result<Program, AssemblerError> {
171 for fixup in self.fixups.drain(..) {
172 let target = self
173 .labels
174 .get(&fixup.label)
175 .copied()
176 .ok_or_else(|| AssemblerError::UnknownLabel(fixup.label.clone()))?;
177 let bytes = target.to_le_bytes();
178 self.code[fixup.at..fixup.at + 4].copy_from_slice(&bytes);
179 }
180 Ok(Program::with_debug(
181 self.constants,
182 self.code,
183 self.debug.finish(),
184 ))
185 }
186
187 pub fn nop(&mut self) {
188 self.emit_opcode(OpCode::Nop);
189 }
190
191 pub fn ret(&mut self) {
192 self.emit_opcode(OpCode::Ret);
193 }
194
195 pub fn ldc(&mut self, index: u32) {
196 self.emit_opcode(OpCode::Ldc);
197 self.emit_u32(index);
198 }
199
200 pub fn add(&mut self) {
201 self.emit_opcode(OpCode::Add);
202 }
203
204 pub fn sub(&mut self) {
205 self.emit_opcode(OpCode::Sub);
206 }
207
208 pub fn mul(&mut self) {
209 self.emit_opcode(OpCode::Mul);
210 }
211
212 pub fn div(&mut self) {
213 self.emit_opcode(OpCode::Div);
214 }
215
216 pub fn modulo(&mut self) {
217 self.emit_opcode(OpCode::Mod);
218 }
219
220 pub fn and(&mut self) {
221 self.emit_opcode(OpCode::And);
222 }
223
224 pub fn or(&mut self) {
225 self.emit_opcode(OpCode::Or);
226 }
227
228 pub fn neg(&mut self) {
229 self.emit_opcode(OpCode::Neg);
230 }
231
232 pub fn not(&mut self) {
233 self.emit_opcode(OpCode::Not);
234 }
235
236 pub fn ceq(&mut self) {
237 self.emit_opcode(OpCode::Ceq);
238 }
239
240 pub fn clt(&mut self) {
241 self.emit_opcode(OpCode::Clt);
242 }
243
244 pub fn cgt(&mut self) {
245 self.emit_opcode(OpCode::Cgt);
246 }
247
248 pub fn br(&mut self, target: u32) {
249 self.emit_opcode(OpCode::Br);
250 self.emit_u32(target);
251 }
252
253 pub fn br_label(&mut self, label: &str) {
254 self.emit_opcode(OpCode::Br);
255 let at = self.code.len();
256 self.emit_u32(0);
257 self.fixups.push(Fixup {
258 at,
259 label: label.to_string(),
260 });
261 }
262
263 pub fn brfalse(&mut self, target: u32) {
264 self.emit_opcode(OpCode::Brfalse);
265 self.emit_u32(target);
266 }
267
268 pub fn brfalse_label(&mut self, label: &str) {
269 self.emit_opcode(OpCode::Brfalse);
270 let at = self.code.len();
271 self.emit_u32(0);
272 self.fixups.push(Fixup {
273 at,
274 label: label.to_string(),
275 });
276 }
277
278 pub fn pop(&mut self) {
279 self.emit_opcode(OpCode::Pop);
280 }
281
282 pub fn dup(&mut self) {
283 self.emit_opcode(OpCode::Dup);
284 }
285
286 pub fn ldloc(&mut self, index: u8) {
287 self.emit_opcode(OpCode::Ldloc);
288 self.emit_u8(index);
289 }
290
291 pub fn stloc(&mut self, index: u8) {
292 self.emit_opcode(OpCode::Stloc);
293 self.emit_u8(index);
294 }
295
296 pub fn call(&mut self, index: u16, argc: u8) {
297 self.emit_opcode(OpCode::Call);
298 self.emit_u16(index);
299 self.emit_u8(argc);
300 }
301
302 pub fn shl(&mut self) {
303 self.emit_opcode(OpCode::Shl);
304 }
305
306 pub fn shr(&mut self) {
307 self.emit_opcode(OpCode::Shr);
308 }
309
310 pub fn lshr(&mut self) {
311 self.emit_opcode(OpCode::Lshr);
312 }
313
314 fn emit_opcode(&mut self, opcode: OpCode) {
315 self.code.push(opcode as u8);
316 }
317
318 fn emit_u8(&mut self, value: u8) {
319 self.code.push(value);
320 }
321
322 fn emit_u16(&mut self, value: u16) {
323 self.code.extend_from_slice(&value.to_le_bytes());
324 }
325
326 fn emit_u32(&mut self, value: u32) {
327 self.code.extend_from_slice(&value.to_le_bytes());
328 }
329}
330
331impl Default for BytecodeBuilder {
332 fn default() -> Self {
333 Self::new()
334 }
335}
336
337impl BytecodeBuilder {
338 pub fn new() -> Self {
339 Self { code: Vec::new() }
340 }
341
342 pub fn position(&self) -> u32 {
343 self.code.len() as u32
344 }
345
346 pub fn finish(self) -> Vec<u8> {
347 self.code
348 }
349
350 pub fn nop(&mut self) {
351 self.emit_opcode(OpCode::Nop);
352 }
353
354 pub fn ret(&mut self) {
355 self.emit_opcode(OpCode::Ret);
356 }
357
358 pub fn ldc(&mut self, index: u32) {
359 self.emit_opcode(OpCode::Ldc);
360 self.emit_u32(index);
361 }
362
363 pub fn add(&mut self) {
364 self.emit_opcode(OpCode::Add);
365 }
366
367 pub fn sub(&mut self) {
368 self.emit_opcode(OpCode::Sub);
369 }
370
371 pub fn mul(&mut self) {
372 self.emit_opcode(OpCode::Mul);
373 }
374
375 pub fn div(&mut self) {
376 self.emit_opcode(OpCode::Div);
377 }
378
379 pub fn modulo(&mut self) {
380 self.emit_opcode(OpCode::Mod);
381 }
382
383 pub fn and(&mut self) {
384 self.emit_opcode(OpCode::And);
385 }
386
387 pub fn or(&mut self) {
388 self.emit_opcode(OpCode::Or);
389 }
390
391 pub fn neg(&mut self) {
392 self.emit_opcode(OpCode::Neg);
393 }
394
395 pub fn not(&mut self) {
396 self.emit_opcode(OpCode::Not);
397 }
398
399 pub fn ceq(&mut self) {
400 self.emit_opcode(OpCode::Ceq);
401 }
402
403 pub fn clt(&mut self) {
404 self.emit_opcode(OpCode::Clt);
405 }
406
407 pub fn cgt(&mut self) {
408 self.emit_opcode(OpCode::Cgt);
409 }
410
411 pub fn br(&mut self, target: u32) {
412 self.emit_opcode(OpCode::Br);
413 self.emit_u32(target);
414 }
415
416 pub fn brfalse(&mut self, target: u32) {
417 self.emit_opcode(OpCode::Brfalse);
418 self.emit_u32(target);
419 }
420
421 pub fn pop(&mut self) {
422 self.emit_opcode(OpCode::Pop);
423 }
424
425 pub fn dup(&mut self) {
426 self.emit_opcode(OpCode::Dup);
427 }
428
429 pub fn ldloc(&mut self, index: u8) {
430 self.emit_opcode(OpCode::Ldloc);
431 self.emit_u8(index);
432 }
433
434 pub fn stloc(&mut self, index: u8) {
435 self.emit_opcode(OpCode::Stloc);
436 self.emit_u8(index);
437 }
438
439 pub fn call(&mut self, index: u16, argc: u8) {
440 self.emit_opcode(OpCode::Call);
441 self.emit_u16(index);
442 self.emit_u8(argc);
443 }
444
445 pub fn shl(&mut self) {
446 self.emit_opcode(OpCode::Shl);
447 }
448
449 pub fn shr(&mut self) {
450 self.emit_opcode(OpCode::Shr);
451 }
452
453 pub fn lshr(&mut self) {
454 self.emit_opcode(OpCode::Lshr);
455 }
456
457 fn emit_opcode(&mut self, opcode: OpCode) {
458 self.code.push(opcode as u8);
459 }
460
461 fn emit_u8(&mut self, value: u8) {
462 self.code.push(value);
463 }
464
465 fn emit_u16(&mut self, value: u16) {
466 self.code.extend_from_slice(&value.to_le_bytes());
467 }
468
469 fn emit_u32(&mut self, value: u32) {
470 self.code.extend_from_slice(&value.to_le_bytes());
471 }
472}
473
474#[derive(Debug, Clone, PartialEq, Eq)]
475pub struct AsmParseError {
476 pub line: usize,
477 pub message: String,
478}
479
480impl std::fmt::Display for AsmParseError {
481 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
482 write!(f, "line {}: {}", self.line, self.message)
483 }
484}
485
486impl std::error::Error for AsmParseError {}
487
488#[derive(Clone, Copy, Debug, PartialEq, Eq)]
489enum AsmSection {
490 Data,
491 Code,
492}
493
494pub fn assemble(source: &str) -> Result<Program, AsmParseError> {
495 let mut assembler = Assembler::new();
496 assembler.set_source(source.to_string());
497 let mut consts: HashMap<String, u32> = HashMap::new();
498 let mut locals: HashMap<String, u8> = HashMap::new();
499 let mut next_local: u8 = 0;
500 let mut section = AsmSection::Code;
501
502 for (line_idx, raw_line) in source.lines().enumerate() {
503 let line_no = line_idx + 1;
504 let line = strip_comments(raw_line).trim();
505 if line.is_empty() {
506 continue;
507 }
508
509 if line.ends_with(':') {
510 return Err(AsmParseError {
511 line: line_no,
512 message: "label definitions must use '.label NAME'".to_string(),
513 });
514 }
515
516 if let Some(rest) = line.strip_prefix('.') {
517 let mut parts = rest.split_whitespace();
518 let directive = parts.next().unwrap_or("").to_ascii_lowercase();
519 match directive.as_str() {
520 "data" => {
521 section = AsmSection::Data;
522 }
523 "code" => {
524 section = AsmSection::Code;
525 }
526 "label" => {
527 let name = next_token(&mut parts, line_no, "label name")?;
528 if section != AsmSection::Code {
529 return Err(AsmParseError {
530 line: line_no,
531 message: "labels are only valid in code section".to_string(),
532 });
533 }
534 assembler.label(name).map_err(|err| AsmParseError {
535 line: line_no,
536 message: format!("label error: {err:?}"),
537 })?;
538 }
539 "const" => {
540 let name = next_token(&mut parts, line_no, "const name")?;
541 if consts.contains_key(name) {
542 return Err(AsmParseError {
543 line: line_no,
544 message: format!("duplicate const '{name}'"),
545 });
546 }
547 let rest = rest_after_n_tokens(line, 2).unwrap_or("");
548 if rest.is_empty() {
549 return Err(AsmParseError {
550 line: line_no,
551 message: "missing const value".to_string(),
552 });
553 }
554 let value = parse_literal(rest, line_no)?;
555 let index = assembler.add_constant(value);
556 consts.insert(name.to_string(), index);
557 }
558 "local" => {
559 let name = next_token(&mut parts, line_no, "local name")?;
560 if locals.contains_key(name) {
561 return Err(AsmParseError {
562 line: line_no,
563 message: format!("duplicate local '{name}'"),
564 });
565 }
566
567 let index = if let Some(token) = parts.next() {
568 parse_u8(token, line_no)?
569 } else {
570 let index = next_local;
571 next_local = next_local.checked_add(1).ok_or(AsmParseError {
572 line: line_no,
573 message: "local index overflow".to_string(),
574 })?;
575 index
576 };
577 locals.insert(name.to_string(), index);
578 }
579 other => {
580 return Err(AsmParseError {
581 line: line_no,
582 message: format!("unknown directive '.{other}'"),
583 });
584 }
585 }
586
587 if parts.next().is_some() {
588 return Err(AsmParseError {
589 line: line_no,
590 message: "unexpected extra tokens".to_string(),
591 });
592 }
593 continue;
594 }
595
596 let mut parts = line.split_whitespace();
597 let op = parts.next().ok_or_else(|| AsmParseError {
598 line: line_no,
599 message: "missing opcode".to_string(),
600 })?;
601 let op = op.to_ascii_lowercase();
602
603 if section == AsmSection::Data {
604 match op.as_str() {
605 "const" => {
606 let name = next_token(&mut parts, line_no, "const name")?;
607 if consts.contains_key(name) {
608 return Err(AsmParseError {
609 line: line_no,
610 message: format!("duplicate const '{name}'"),
611 });
612 }
613 let rest = rest_after_n_tokens(line, 2).unwrap_or("");
614 if rest.is_empty() {
615 return Err(AsmParseError {
616 line: line_no,
617 message: "missing const value".to_string(),
618 });
619 }
620 let value = parse_literal(rest, line_no)?;
621 let index = assembler.add_constant(value);
622 consts.insert(name.to_string(), index);
623 }
624 "string" => {
625 let name = next_token(&mut parts, line_no, "string name")?;
626 if consts.contains_key(name) {
627 return Err(AsmParseError {
628 line: line_no,
629 message: format!("duplicate const '{name}'"),
630 });
631 }
632 let rest = rest_after_n_tokens(line, 2).unwrap_or("");
633 if rest.is_empty() {
634 return Err(AsmParseError {
635 line: line_no,
636 message: "missing string literal".to_string(),
637 });
638 }
639 let value = Value::string(parse_string_literal(rest, line_no)?);
640 let index = assembler.add_constant(value);
641 consts.insert(name.to_string(), index);
642 }
643 other => {
644 return Err(AsmParseError {
645 line: line_no,
646 message: format!("unexpected opcode '{other}' in data section"),
647 });
648 }
649 }
650 continue;
651 }
652
653 assembler.mark_line(line_no as u32);
654 let mut check_extra = true;
655 let opcode = OpCode::parse_mnemonic(op.as_str()).ok_or_else(|| AsmParseError {
656 line: line_no,
657 message: format!("unknown opcode '{op}'"),
658 })?;
659 match opcode {
660 OpCode::Nop => assembler.nop(),
661 OpCode::Ret => assembler.ret(),
662 OpCode::Ldc => {
663 check_extra = false;
664 let rest = rest_after_n_tokens(line, 1).unwrap_or("");
665 if rest.is_empty() {
666 return Err(AsmParseError {
667 line: line_no,
668 message: "missing ldc literal".to_string(),
669 });
670 }
671 if let Some(&index) = consts.get(rest) {
672 assembler.ldc(index);
673 } else {
674 assembler.push_const(parse_literal(rest, line_no)?);
675 }
676 }
677 OpCode::Add => assembler.add(),
678 OpCode::Sub => assembler.sub(),
679 OpCode::Mul => assembler.mul(),
680 OpCode::Div => assembler.div(),
681 OpCode::Neg => assembler.neg(),
682 OpCode::Not => assembler.not(),
683 OpCode::Ceq => assembler.ceq(),
684 OpCode::Clt => assembler.clt(),
685 OpCode::Cgt => assembler.cgt(),
686 OpCode::Br => {
687 let target = next_token(&mut parts, line_no, "jump target")?;
688 if target.parse::<u32>().is_ok() {
689 return Err(AsmParseError {
690 line: line_no,
691 message: "numeric jump targets are not supported".to_string(),
692 });
693 }
694 assembler.br_label(target);
695 }
696 OpCode::Brfalse => {
697 let target = next_token(&mut parts, line_no, "jump target")?;
698 if target.parse::<u32>().is_ok() {
699 return Err(AsmParseError {
700 line: line_no,
701 message: "numeric jump targets are not supported".to_string(),
702 });
703 }
704 assembler.brfalse_label(target);
705 }
706 OpCode::Pop => assembler.pop(),
707 OpCode::Dup => assembler.dup(),
708 OpCode::Ldloc => {
709 let token = next_token(&mut parts, line_no, "local index")?;
710 let index = if let Ok(value) = token.parse::<u8>() {
711 value
712 } else {
713 *locals.get(token).ok_or(AsmParseError {
714 line: line_no,
715 message: format!("unknown local '{token}'"),
716 })?
717 };
718 assembler.ldloc(index);
719 }
720 OpCode::Stloc => {
721 let token = next_token(&mut parts, line_no, "local index")?;
722 let index = if let Ok(value) = token.parse::<u8>() {
723 value
724 } else {
725 *locals.get(token).ok_or(AsmParseError {
726 line: line_no,
727 message: format!("unknown local '{token}'"),
728 })?
729 };
730 assembler.stloc(index);
731 }
732 OpCode::Call => {
733 let index = parse_u16(next_token(&mut parts, line_no, "call id")?, line_no)?;
734 let argc = parse_u8(next_token(&mut parts, line_no, "arg count")?, line_no)?;
735 assembler.call(index, argc);
736 }
737 OpCode::Shl => assembler.shl(),
738 OpCode::Shr => assembler.shr(),
739 OpCode::Lshr => assembler.lshr(),
740 OpCode::Mod => assembler.modulo(),
741 OpCode::And => assembler.and(),
742 OpCode::Or => assembler.or(),
743 }
744
745 if check_extra && parts.next().is_some() {
746 return Err(AsmParseError {
747 line: line_no,
748 message: "unexpected extra tokens".to_string(),
749 });
750 }
751 }
752
753 assembler.finish_program().map_err(|err| AsmParseError {
754 line: 0,
755 message: format!("assembler error: {err:?}"),
756 })
757}
758
759fn strip_comments(line: &str) -> &str {
760 let hash_idx = line.find('#');
761 let slash_idx = line.find("//");
762 match (hash_idx, slash_idx) {
763 (Some(h), Some(s)) => &line[..h.min(s)],
764 (Some(h), None) => &line[..h],
765 (None, Some(s)) => &line[..s],
766 (None, None) => line,
767 }
768}
769
770fn next_token<'a>(
771 parts: &mut impl Iterator<Item = &'a str>,
772 line_no: usize,
773 what: &str,
774) -> Result<&'a str, AsmParseError> {
775 parts.next().ok_or_else(|| AsmParseError {
776 line: line_no,
777 message: format!("missing {what}"),
778 })
779}
780
781fn parse_u8(token: &str, line_no: usize) -> Result<u8, AsmParseError> {
782 token.parse::<u8>().map_err(|_| AsmParseError {
783 line: line_no,
784 message: format!("invalid u8 '{token}'"),
785 })
786}
787
788fn parse_u16(token: &str, line_no: usize) -> Result<u16, AsmParseError> {
789 token.parse::<u16>().map_err(|_| AsmParseError {
790 line: line_no,
791 message: format!("invalid u16 '{token}'"),
792 })
793}
794
795fn parse_f64(token: &str, line_no: usize, what: &str) -> Result<f64, AsmParseError> {
796 token.parse::<f64>().map_err(|_| AsmParseError {
797 line: line_no,
798 message: format!("invalid {what} '{token}'"),
799 })
800}
801
802fn parse_literal(token: &str, line_no: usize) -> Result<Value, AsmParseError> {
803 let token = token.trim();
804 if token.starts_with('"') {
805 return Ok(Value::string(parse_string_literal(token, line_no)?));
806 }
807 if token.eq_ignore_ascii_case("true") {
808 Ok(Value::Bool(true))
809 } else if token.eq_ignore_ascii_case("false") {
810 Ok(Value::Bool(false))
811 } else {
812 match token.parse::<i64>() {
813 Ok(value) => Ok(Value::Int(value)),
814 Err(_) => parse_f64(token, line_no, "const literal").map(Value::Float),
815 }
816 }
817}
818
819fn parse_string_literal(token: &str, line_no: usize) -> Result<String, AsmParseError> {
820 let mut chars = token.char_indices();
821 if chars.next().map(|(_, ch)| ch) != Some('"') {
822 return Err(AsmParseError {
823 line: line_no,
824 message: "string literal must start with '\"'".to_string(),
825 });
826 }
827
828 let mut out = String::new();
829 let mut escaped = false;
830 let mut end_idx = None;
831
832 for (idx, ch) in chars {
833 if escaped {
834 let mapped = match ch {
835 'n' => '\n',
836 'r' => '\r',
837 't' => '\t',
838 '\\' => '\\',
839 '"' => '"',
840 '0' => '\0',
841 other => {
842 return Err(AsmParseError {
843 line: line_no,
844 message: format!("invalid escape '\\{other}'"),
845 });
846 }
847 };
848 out.push(mapped);
849 escaped = false;
850 continue;
851 }
852
853 match ch {
854 '\\' => escaped = true,
855 '"' => {
856 end_idx = Some(idx);
857 break;
858 }
859 other => out.push(other),
860 }
861 }
862
863 let Some(end_idx) = end_idx else {
864 return Err(AsmParseError {
865 line: line_no,
866 message: "unterminated string literal".to_string(),
867 });
868 };
869
870 if token[end_idx + 1..].trim().is_empty() {
871 Ok(out)
872 } else {
873 Err(AsmParseError {
874 line: line_no,
875 message: "unexpected trailing characters after string literal".to_string(),
876 })
877 }
878}
879
880fn rest_after_n_tokens(line: &str, n: usize) -> Option<&str> {
881 let mut count = 0;
882 let mut in_token = false;
883 let mut end_idx = 0;
884 for (idx, ch) in line.char_indices() {
885 if ch.is_whitespace() {
886 if in_token {
887 in_token = false;
888 count += 1;
889 if count == n {
890 end_idx = idx;
891 break;
892 }
893 }
894 } else if !in_token {
895 in_token = true;
896 }
897 }
898
899 if in_token {
900 count += 1;
901 end_idx = line.len();
902 }
903
904 if count < n {
905 None
906 } else {
907 Some(line[end_idx..].trim_start())
908 }
909}