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 call_value(&mut self, argc: u8) {
303 self.emit_opcode(OpCode::CallValue);
304 self.emit_u8(argc);
305 }
306
307 pub fn shl(&mut self) {
308 self.emit_opcode(OpCode::Shl);
309 }
310
311 pub fn shr(&mut self) {
312 self.emit_opcode(OpCode::Shr);
313 }
314
315 pub fn lshr(&mut self) {
316 self.emit_opcode(OpCode::Lshr);
317 }
318
319 fn emit_opcode(&mut self, opcode: OpCode) {
320 self.code.push(opcode as u8);
321 }
322
323 fn emit_u8(&mut self, value: u8) {
324 self.code.push(value);
325 }
326
327 fn emit_u16(&mut self, value: u16) {
328 self.code.extend_from_slice(&value.to_le_bytes());
329 }
330
331 fn emit_u32(&mut self, value: u32) {
332 self.code.extend_from_slice(&value.to_le_bytes());
333 }
334}
335
336impl Default for BytecodeBuilder {
337 fn default() -> Self {
338 Self::new()
339 }
340}
341
342impl BytecodeBuilder {
343 pub fn new() -> Self {
344 Self { code: Vec::new() }
345 }
346
347 pub fn position(&self) -> u32 {
348 self.code.len() as u32
349 }
350
351 pub fn finish(self) -> Vec<u8> {
352 self.code
353 }
354
355 pub fn nop(&mut self) {
356 self.emit_opcode(OpCode::Nop);
357 }
358
359 pub fn ret(&mut self) {
360 self.emit_opcode(OpCode::Ret);
361 }
362
363 pub fn ldc(&mut self, index: u32) {
364 self.emit_opcode(OpCode::Ldc);
365 self.emit_u32(index);
366 }
367
368 pub fn add(&mut self) {
369 self.emit_opcode(OpCode::Add);
370 }
371
372 pub fn sub(&mut self) {
373 self.emit_opcode(OpCode::Sub);
374 }
375
376 pub fn mul(&mut self) {
377 self.emit_opcode(OpCode::Mul);
378 }
379
380 pub fn div(&mut self) {
381 self.emit_opcode(OpCode::Div);
382 }
383
384 pub fn modulo(&mut self) {
385 self.emit_opcode(OpCode::Mod);
386 }
387
388 pub fn and(&mut self) {
389 self.emit_opcode(OpCode::And);
390 }
391
392 pub fn or(&mut self) {
393 self.emit_opcode(OpCode::Or);
394 }
395
396 pub fn neg(&mut self) {
397 self.emit_opcode(OpCode::Neg);
398 }
399
400 pub fn not(&mut self) {
401 self.emit_opcode(OpCode::Not);
402 }
403
404 pub fn ceq(&mut self) {
405 self.emit_opcode(OpCode::Ceq);
406 }
407
408 pub fn clt(&mut self) {
409 self.emit_opcode(OpCode::Clt);
410 }
411
412 pub fn cgt(&mut self) {
413 self.emit_opcode(OpCode::Cgt);
414 }
415
416 pub fn br(&mut self, target: u32) {
417 self.emit_opcode(OpCode::Br);
418 self.emit_u32(target);
419 }
420
421 pub fn brfalse(&mut self, target: u32) {
422 self.emit_opcode(OpCode::Brfalse);
423 self.emit_u32(target);
424 }
425
426 pub fn pop(&mut self) {
427 self.emit_opcode(OpCode::Pop);
428 }
429
430 pub fn dup(&mut self) {
431 self.emit_opcode(OpCode::Dup);
432 }
433
434 pub fn ldloc(&mut self, index: u8) {
435 self.emit_opcode(OpCode::Ldloc);
436 self.emit_u8(index);
437 }
438
439 pub fn stloc(&mut self, index: u8) {
440 self.emit_opcode(OpCode::Stloc);
441 self.emit_u8(index);
442 }
443
444 pub fn call(&mut self, index: u16, argc: u8) {
445 self.emit_opcode(OpCode::Call);
446 self.emit_u16(index);
447 self.emit_u8(argc);
448 }
449
450 pub fn call_value(&mut self, argc: u8) {
451 self.emit_opcode(OpCode::CallValue);
452 self.emit_u8(argc);
453 }
454
455 pub fn shl(&mut self) {
456 self.emit_opcode(OpCode::Shl);
457 }
458
459 pub fn shr(&mut self) {
460 self.emit_opcode(OpCode::Shr);
461 }
462
463 pub fn lshr(&mut self) {
464 self.emit_opcode(OpCode::Lshr);
465 }
466
467 fn emit_opcode(&mut self, opcode: OpCode) {
468 self.code.push(opcode as u8);
469 }
470
471 fn emit_u8(&mut self, value: u8) {
472 self.code.push(value);
473 }
474
475 fn emit_u16(&mut self, value: u16) {
476 self.code.extend_from_slice(&value.to_le_bytes());
477 }
478
479 fn emit_u32(&mut self, value: u32) {
480 self.code.extend_from_slice(&value.to_le_bytes());
481 }
482}
483
484#[derive(Debug, Clone, PartialEq, Eq)]
485pub struct AsmParseError {
486 pub line: usize,
487 pub message: String,
488}
489
490impl std::fmt::Display for AsmParseError {
491 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
492 write!(f, "line {}: {}", self.line, self.message)
493 }
494}
495
496impl std::error::Error for AsmParseError {}
497
498#[derive(Clone, Copy, Debug, PartialEq, Eq)]
499enum AsmSection {
500 Data,
501 Code,
502}
503
504pub fn assemble(source: &str) -> Result<Program, AsmParseError> {
505 let mut assembler = Assembler::new();
506 assembler.set_source(source.to_string());
507 let mut consts: HashMap<String, u32> = HashMap::new();
508 let mut locals: HashMap<String, u8> = HashMap::new();
509 let mut next_local: u8 = 0;
510 let mut section = AsmSection::Code;
511
512 for (line_idx, raw_line) in source.lines().enumerate() {
513 let line_no = line_idx + 1;
514 let line = strip_comments(raw_line).trim();
515 if line.is_empty() {
516 continue;
517 }
518
519 if line.ends_with(':') {
520 return Err(AsmParseError {
521 line: line_no,
522 message: "label definitions must use '.label NAME'".to_string(),
523 });
524 }
525
526 if let Some(rest) = line.strip_prefix('.') {
527 let mut parts = rest.split_whitespace();
528 let directive = parts.next().unwrap_or("").to_ascii_lowercase();
529 match directive.as_str() {
530 "data" => {
531 section = AsmSection::Data;
532 }
533 "code" => {
534 section = AsmSection::Code;
535 }
536 "label" => {
537 let name = next_token(&mut parts, line_no, "label name")?;
538 if section != AsmSection::Code {
539 return Err(AsmParseError {
540 line: line_no,
541 message: "labels are only valid in code section".to_string(),
542 });
543 }
544 assembler.label(name).map_err(|err| AsmParseError {
545 line: line_no,
546 message: format!("label error: {err:?}"),
547 })?;
548 }
549 "const" => {
550 let name = next_token(&mut parts, line_no, "const name")?;
551 if consts.contains_key(name) {
552 return Err(AsmParseError {
553 line: line_no,
554 message: format!("duplicate const '{name}'"),
555 });
556 }
557 let rest = rest_after_n_tokens(line, 2).unwrap_or("");
558 if rest.is_empty() {
559 return Err(AsmParseError {
560 line: line_no,
561 message: "missing const value".to_string(),
562 });
563 }
564 let value = parse_literal(rest, line_no)?;
565 let index = assembler.add_constant(value);
566 consts.insert(name.to_string(), index);
567 }
568 "local" => {
569 let name = next_token(&mut parts, line_no, "local name")?;
570 if locals.contains_key(name) {
571 return Err(AsmParseError {
572 line: line_no,
573 message: format!("duplicate local '{name}'"),
574 });
575 }
576
577 let index = if let Some(token) = parts.next() {
578 parse_u8(token, line_no)?
579 } else {
580 let index = next_local;
581 next_local = next_local.checked_add(1).ok_or(AsmParseError {
582 line: line_no,
583 message: "local index overflow".to_string(),
584 })?;
585 index
586 };
587 locals.insert(name.to_string(), index);
588 }
589 other => {
590 return Err(AsmParseError {
591 line: line_no,
592 message: format!("unknown directive '.{other}'"),
593 });
594 }
595 }
596
597 if parts.next().is_some() {
598 return Err(AsmParseError {
599 line: line_no,
600 message: "unexpected extra tokens".to_string(),
601 });
602 }
603 continue;
604 }
605
606 let mut parts = line.split_whitespace();
607 let op = parts.next().ok_or_else(|| AsmParseError {
608 line: line_no,
609 message: "missing opcode".to_string(),
610 })?;
611 let op = op.to_ascii_lowercase();
612
613 if section == AsmSection::Data {
614 match op.as_str() {
615 "const" => {
616 let name = next_token(&mut parts, line_no, "const name")?;
617 if consts.contains_key(name) {
618 return Err(AsmParseError {
619 line: line_no,
620 message: format!("duplicate const '{name}'"),
621 });
622 }
623 let rest = rest_after_n_tokens(line, 2).unwrap_or("");
624 if rest.is_empty() {
625 return Err(AsmParseError {
626 line: line_no,
627 message: "missing const value".to_string(),
628 });
629 }
630 let value = parse_literal(rest, line_no)?;
631 let index = assembler.add_constant(value);
632 consts.insert(name.to_string(), index);
633 }
634 "string" => {
635 let name = next_token(&mut parts, line_no, "string name")?;
636 if consts.contains_key(name) {
637 return Err(AsmParseError {
638 line: line_no,
639 message: format!("duplicate const '{name}'"),
640 });
641 }
642 let rest = rest_after_n_tokens(line, 2).unwrap_or("");
643 if rest.is_empty() {
644 return Err(AsmParseError {
645 line: line_no,
646 message: "missing string literal".to_string(),
647 });
648 }
649 let value = Value::string(parse_string_literal(rest, line_no)?);
650 let index = assembler.add_constant(value);
651 consts.insert(name.to_string(), index);
652 }
653 other => {
654 return Err(AsmParseError {
655 line: line_no,
656 message: format!("unexpected opcode '{other}' in data section"),
657 });
658 }
659 }
660 continue;
661 }
662
663 assembler.mark_line(line_no as u32);
664 let mut check_extra = true;
665 let opcode = OpCode::parse_mnemonic(op.as_str()).ok_or_else(|| AsmParseError {
666 line: line_no,
667 message: format!("unknown opcode '{op}'"),
668 })?;
669 match opcode {
670 OpCode::Nop => assembler.nop(),
671 OpCode::Ret => assembler.ret(),
672 OpCode::Ldc => {
673 check_extra = false;
674 let rest = rest_after_n_tokens(line, 1).unwrap_or("");
675 if rest.is_empty() {
676 return Err(AsmParseError {
677 line: line_no,
678 message: "missing ldc literal".to_string(),
679 });
680 }
681 if let Some(&index) = consts.get(rest) {
682 assembler.ldc(index);
683 } else {
684 assembler.push_const(parse_literal(rest, line_no)?);
685 }
686 }
687 OpCode::Add => assembler.add(),
688 OpCode::Sub => assembler.sub(),
689 OpCode::Mul => assembler.mul(),
690 OpCode::Div => assembler.div(),
691 OpCode::Neg => assembler.neg(),
692 OpCode::Not => assembler.not(),
693 OpCode::Ceq => assembler.ceq(),
694 OpCode::Clt => assembler.clt(),
695 OpCode::Cgt => assembler.cgt(),
696 OpCode::Br => {
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.br_label(target);
705 }
706 OpCode::Brfalse => {
707 let target = next_token(&mut parts, line_no, "jump target")?;
708 if target.parse::<u32>().is_ok() {
709 return Err(AsmParseError {
710 line: line_no,
711 message: "numeric jump targets are not supported".to_string(),
712 });
713 }
714 assembler.brfalse_label(target);
715 }
716 OpCode::Pop => assembler.pop(),
717 OpCode::Dup => assembler.dup(),
718 OpCode::Ldloc => {
719 let token = next_token(&mut parts, line_no, "local index")?;
720 let index = if let Ok(value) = token.parse::<u8>() {
721 value
722 } else {
723 *locals.get(token).ok_or(AsmParseError {
724 line: line_no,
725 message: format!("unknown local '{token}'"),
726 })?
727 };
728 assembler.ldloc(index);
729 }
730 OpCode::Stloc => {
731 let token = next_token(&mut parts, line_no, "local index")?;
732 let index = if let Ok(value) = token.parse::<u8>() {
733 value
734 } else {
735 *locals.get(token).ok_or(AsmParseError {
736 line: line_no,
737 message: format!("unknown local '{token}'"),
738 })?
739 };
740 assembler.stloc(index);
741 }
742 OpCode::Call => {
743 let index = parse_u16(next_token(&mut parts, line_no, "call id")?, line_no)?;
744 let argc = parse_u8(next_token(&mut parts, line_no, "arg count")?, line_no)?;
745 assembler.call(index, argc);
746 }
747 OpCode::CallValue => {
748 let argc = parse_u8(next_token(&mut parts, line_no, "arg count")?, line_no)?;
749 assembler.call_value(argc);
750 }
751 OpCode::Shl => assembler.shl(),
752 OpCode::Shr => assembler.shr(),
753 OpCode::Lshr => assembler.lshr(),
754 OpCode::Mod => assembler.modulo(),
755 OpCode::And => assembler.and(),
756 OpCode::Or => assembler.or(),
757 }
758
759 if check_extra && parts.next().is_some() {
760 return Err(AsmParseError {
761 line: line_no,
762 message: "unexpected extra tokens".to_string(),
763 });
764 }
765 }
766
767 assembler.finish_program().map_err(|err| AsmParseError {
768 line: 0,
769 message: format!("assembler error: {err:?}"),
770 })
771}
772
773fn strip_comments(line: &str) -> &str {
774 let hash_idx = line.find('#');
775 let slash_idx = line.find("//");
776 match (hash_idx, slash_idx) {
777 (Some(h), Some(s)) => &line[..h.min(s)],
778 (Some(h), None) => &line[..h],
779 (None, Some(s)) => &line[..s],
780 (None, None) => line,
781 }
782}
783
784fn next_token<'a>(
785 parts: &mut impl Iterator<Item = &'a str>,
786 line_no: usize,
787 what: &str,
788) -> Result<&'a str, AsmParseError> {
789 parts.next().ok_or_else(|| AsmParseError {
790 line: line_no,
791 message: format!("missing {what}"),
792 })
793}
794
795fn parse_u8(token: &str, line_no: usize) -> Result<u8, AsmParseError> {
796 token.parse::<u8>().map_err(|_| AsmParseError {
797 line: line_no,
798 message: format!("invalid u8 '{token}'"),
799 })
800}
801
802fn parse_u16(token: &str, line_no: usize) -> Result<u16, AsmParseError> {
803 token.parse::<u16>().map_err(|_| AsmParseError {
804 line: line_no,
805 message: format!("invalid u16 '{token}'"),
806 })
807}
808
809fn parse_f64(token: &str, line_no: usize, what: &str) -> Result<f64, AsmParseError> {
810 token.parse::<f64>().map_err(|_| AsmParseError {
811 line: line_no,
812 message: format!("invalid {what} '{token}'"),
813 })
814}
815
816fn parse_literal(token: &str, line_no: usize) -> Result<Value, AsmParseError> {
817 let token = token.trim();
818 if token.starts_with('"') {
819 return Ok(Value::string(parse_string_literal(token, line_no)?));
820 }
821 if token.eq_ignore_ascii_case("true") {
822 Ok(Value::Bool(true))
823 } else if token.eq_ignore_ascii_case("false") {
824 Ok(Value::Bool(false))
825 } else {
826 match token.parse::<i64>() {
827 Ok(value) => Ok(Value::Int(value)),
828 Err(_) => parse_f64(token, line_no, "const literal").map(Value::Float),
829 }
830 }
831}
832
833fn parse_string_literal(token: &str, line_no: usize) -> Result<String, AsmParseError> {
834 let mut chars = token.char_indices();
835 if chars.next().map(|(_, ch)| ch) != Some('"') {
836 return Err(AsmParseError {
837 line: line_no,
838 message: "string literal must start with '\"'".to_string(),
839 });
840 }
841
842 let mut out = String::new();
843 let mut escaped = false;
844 let mut end_idx = None;
845
846 for (idx, ch) in chars {
847 if escaped {
848 let mapped = match ch {
849 'n' => '\n',
850 'r' => '\r',
851 't' => '\t',
852 '\\' => '\\',
853 '"' => '"',
854 '0' => '\0',
855 other => {
856 return Err(AsmParseError {
857 line: line_no,
858 message: format!("invalid escape '\\{other}'"),
859 });
860 }
861 };
862 out.push(mapped);
863 escaped = false;
864 continue;
865 }
866
867 match ch {
868 '\\' => escaped = true,
869 '"' => {
870 end_idx = Some(idx);
871 break;
872 }
873 other => out.push(other),
874 }
875 }
876
877 let Some(end_idx) = end_idx else {
878 return Err(AsmParseError {
879 line: line_no,
880 message: "unterminated string literal".to_string(),
881 });
882 };
883
884 if token[end_idx + 1..].trim().is_empty() {
885 Ok(out)
886 } else {
887 Err(AsmParseError {
888 line: line_no,
889 message: "unexpected trailing characters after string literal".to_string(),
890 })
891 }
892}
893
894fn rest_after_n_tokens(line: &str, n: usize) -> Option<&str> {
895 let mut count = 0;
896 let mut in_token = false;
897 let mut end_idx = 0;
898 for (idx, ch) in line.char_indices() {
899 if ch.is_whitespace() {
900 if in_token {
901 in_token = false;
902 count += 1;
903 if count == n {
904 end_idx = idx;
905 break;
906 }
907 }
908 } else if !in_token {
909 in_token = true;
910 }
911 }
912
913 if in_token {
914 count += 1;
915 end_idx = line.len();
916 }
917
918 if count < n {
919 None
920 } else {
921 Some(line[end_idx..].trim_start())
922 }
923}