1use std::fmt;
291use std::fmt::Debug;
292
293use winnow::ascii::digit1;
294use winnow::ascii::hex_digit1;
295use winnow::ascii::space0;
296use winnow::ascii::space1;
297use winnow::ascii::Caseless;
298use winnow::combinator::alt;
299use winnow::combinator::cut_err;
300use winnow::combinator::delimited;
301use winnow::combinator::fail;
302use winnow::combinator::opt;
303use winnow::combinator::preceded;
304use winnow::combinator::separated;
305use winnow::combinator::seq;
306use winnow::error::ContextError;
307use winnow::error::ErrMode;
308use winnow::error::StrContext;
309use winnow::error::StrContextValue;
310use winnow::stream::Stream;
311use winnow::token::take_while;
312use winnow::ModalResult;
313use winnow::Parser;
314
315#[derive(Debug, PartialEq)]
316pub enum Attribute {
317 BSS,
318 Origin { address: u64 },
319 Obj { address: Option<u64> },
320 Over { group: String },
321 Word,
322 File { filename: String },
323 Size { maxsize: u64 },
324}
325
326#[derive(Debug, Clone, PartialEq, Eq)]
328pub enum Expression {
329 Constant(u64),
331
332 Symbol(String),
334
335 Binary {
337 left: Box<Expression>,
338 op: BinaryOp,
339 right: Box<Expression>,
340 },
341
342 Unary {
344 op: UnaryOp,
345 operand: Box<Expression>,
346 },
347
348 Parens(Box<Expression>),
350
351 Function { name: String, arg: Box<Expression> },
353}
354
355impl fmt::Display for Expression {
356 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
357 match self {
358 Expression::Constant(n) => write!(f, "${:x}", n),
359 Expression::Symbol(s) => write!(f, "{}", s),
360 Expression::Binary { left, op, right } => {
361 write!(f, "({} {} {})", left, op, right)
362 }
363 Expression::Unary { op, operand } => write!(f, "({}{})", op, operand),
364 Expression::Parens(expr) => write!(f, "({})", expr),
365 Expression::Function { name, arg } => write!(f, "{}({})", name, arg),
366 }
367 }
368}
369
370#[derive(Debug, Clone, Copy, PartialEq, Eq)]
372pub enum BinaryOp {
373 Add, Sub, Mul, Div, Mod, And, Or, Xor, Shl, Shr, Eq, Ne, Lt, Le, Gt, Ge, LogAnd, LogOr, }
399
400impl fmt::Display for BinaryOp {
401 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
402 let s = match self {
403 BinaryOp::Add => "+",
404 BinaryOp::Sub => "-",
405 BinaryOp::Mul => "*",
406 BinaryOp::Div => "/",
407 BinaryOp::Mod => "%",
408 BinaryOp::And => "&",
409 BinaryOp::Or => "|",
410 BinaryOp::Xor => "^",
411 BinaryOp::Shl => "<<",
412 BinaryOp::Shr => ">>",
413 BinaryOp::Eq => "==",
414 BinaryOp::Ne => "!=",
415 BinaryOp::Lt => "<",
416 BinaryOp::Le => "<=",
417 BinaryOp::Gt => ">",
418 BinaryOp::Ge => ">=",
419 BinaryOp::LogAnd => "&&",
420 BinaryOp::LogOr => "||",
421 };
422 write!(f, "{}", s)
423 }
424}
425
426#[derive(Debug, Clone, Copy, PartialEq, Eq)]
428pub enum UnaryOp {
429 Neg, Not, LogNot, }
433
434impl fmt::Display for UnaryOp {
435 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
436 let s = match self {
437 UnaryOp::Neg => "-",
438 UnaryOp::Not => "~",
439 UnaryOp::LogNot => "!",
440 };
441 write!(f, "{}", s)
442 }
443}
444
445#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
447struct Precedence(u8);
448
449impl Precedence {
450 const LOWEST: Self = Self(0);
451 const LOGICAL_OR: Self = Self(1); const LOGICAL_AND: Self = Self(2); const BITWISE_OR: Self = Self(3); const BITWISE_XOR: Self = Self(4); const BITWISE_AND: Self = Self(5); const EQUALITY: Self = Self(6); const COMPARISON: Self = Self(7); const SHIFT: Self = Self(8); const ADDITIVE: Self = Self(9); const MULTIPLICATIVE: Self = Self(10); }
465
466impl BinaryOp {
467 fn precedence(self) -> Precedence {
468 match self {
469 BinaryOp::LogOr => Precedence::LOGICAL_OR,
470 BinaryOp::LogAnd => Precedence::LOGICAL_AND,
471 BinaryOp::Or => Precedence::BITWISE_OR,
472 BinaryOp::Xor => Precedence::BITWISE_XOR,
473 BinaryOp::And => Precedence::BITWISE_AND,
474 BinaryOp::Eq | BinaryOp::Ne => Precedence::EQUALITY,
475 BinaryOp::Lt | BinaryOp::Le | BinaryOp::Gt | BinaryOp::Ge => Precedence::COMPARISON,
476 BinaryOp::Shl | BinaryOp::Shr => Precedence::SHIFT,
477 BinaryOp::Add | BinaryOp::Sub => Precedence::ADDITIVE,
478 BinaryOp::Mul | BinaryOp::Div | BinaryOp::Mod => Precedence::MULTIPLICATIVE,
479 }
480 }
481
482 fn is_left_associative(self) -> bool {
483 true }
485}
486
487#[derive(Debug, PartialEq)]
488pub enum Size {
489 Byte,
490 Word,
491 Long,
492}
493
494#[derive(Debug, PartialEq)]
495pub enum Command {
496 Include {
498 filename: String,
499 },
500
501 IncLib {
503 filename: String,
504 },
505
506 Origin {
508 address: u64,
509 },
510
511 Workspace {
513 address: u64,
514 },
515
516 Equals {
517 left: String,
518 right: Expression,
519 },
520
521 Regs {
522 register: String,
523 expression: Expression,
524 },
525
526 Group {
527 name: String,
528 attributes: Vec<Attribute>,
529 },
530
531 Section {
532 name: String,
533 group: Option<String>,
534 attributes: Vec<Attribute>,
535 },
536
537 Alias {
538 name: String,
539 target: String,
540 },
541
542 Unit {
543 unitnum: u64,
544 },
545
546 Global {
547 symbols: Vec<String>,
548 },
549
550 XDef {
551 symbols: Vec<String>,
552 },
553
554 XRef {
555 symbols: Vec<String>,
556 },
557
558 Public {
559 public: bool,
560 },
561
562 DC {
563 size: Size,
564 expression: Vec<Expression>,
565 },
566}
567
568fn parse_file_name(input: &mut &str) -> ModalResult<String> {
569 let s = take_while(1.., |c| c != '"').parse_next(input)?;
570 Ok(s.to_string())
571}
572
573fn parse_symbol(input: &mut &str) -> ModalResult<String> {
574 let s = (seq!(
575 take_while(1, (('a'..='z'), ('A'..='Z'), '_')),
576 take_while(0.., (('a'..='z'), ('A'..='Z'), ('0'..='9'), '?', '_', '.'))
577 ))
578 .parse_next(input)?;
579 Ok(format!("{}{}", s.0, s.1))
580}
581
582fn parse_bin_digits(input: &mut &str) -> ModalResult<u64> {
583 let digits = take_while(1.., '0'..='1').parse_next(input)?;
584 match u64::from_str_radix(digits, 2) {
585 Ok(i) => Ok(i),
586 Err(_e) => Err(ErrMode::Cut(ContextError::new())),
587 }
588}
589
590fn parse_decimal_digits(input: &mut &str) -> ModalResult<u64> {
591 let digits = digit1.parse_next(input)?;
592 match digits.parse::<u64>() {
593 Ok(i) => Ok(i),
594 Err(_e) => Err(ErrMode::Cut(ContextError::new())),
595 }
596}
597
598fn parse_hex_digits(input: &mut &str) -> ModalResult<u64> {
599 let digits = hex_digit1.parse_next(input)?;
600 match u64::from_str_radix(digits, 16) {
601 Ok(i) => Ok(i),
602 Err(_e) => Err(ErrMode::Cut(ContextError::new())),
603 }
604}
605
606fn parse_prefixed_digits(input: &mut &str) -> ModalResult<u64> {
607 let i = alt((
608 ('$', cut_err(parse_hex_digits)),
609 ('%', cut_err(parse_bin_digits)),
610 fail.context(StrContext::Label("integer constant")),
611 ))
612 .parse_next(input)?;
613
614 Ok(i.1)
615}
616
617fn parse_integer_constant(input: &mut &str) -> ModalResult<u64> {
618 alt((
619 parse_decimal_digits,
620 parse_prefixed_digits,
621 fail.context(StrContext::Label("integer constant")),
622 ))
623 .parse_next(input)
624}
625
626fn parse_symbol_list(input: &mut &str) -> ModalResult<Vec<String>> {
627 separated(1.., parse_symbol, (space0, ',', space0)).parse_next(input)
628}
629
630fn parse_function_name(input: &mut &str) -> ModalResult<String> {
632 alt((
633 "sectstart",
634 "sectend",
635 "sectbase",
636 "sectof",
637 "offs",
638 "bank",
639 "groupstart",
640 "groupof",
641 "grouporg",
642 "seg",
643 ))
644 .map(|s: &str| s.to_lowercase())
645 .parse_next(input)
646}
647
648fn parse_primary(input: &mut &str) -> ModalResult<Expression> {
650 preceded(
651 space0,
652 alt((
653 (parse_function_name, delimited('(', parse_expression, ')')).map(|(name, arg)| {
655 Expression::Function {
656 name,
657 arg: Box::new(arg),
658 }
659 }),
660 delimited('(', parse_expression, ')').map(|expr| Expression::Parens(Box::new(expr))),
662 parse_integer_constant.map(Expression::Constant),
664 parse_symbol.map(Expression::Symbol),
666 fail.context(StrContext::Label("expression")),
668 )),
669 )
670 .parse_next(input)
671}
672
673fn parse_unary(input: &mut &str) -> ModalResult<Expression> {
675 preceded(
676 space0,
677 alt((
678 preceded('-', cut_err(parse_unary)).map(|operand| Expression::Unary {
680 op: UnaryOp::Neg,
681 operand: Box::new(operand),
682 }),
683 preceded('~', cut_err(parse_unary)).map(|operand| Expression::Unary {
684 op: UnaryOp::Not,
685 operand: Box::new(operand),
686 }),
687 preceded('!', cut_err(parse_unary)).map(|operand| Expression::Unary {
688 op: UnaryOp::LogNot,
689 operand: Box::new(operand),
690 }),
691 parse_primary,
693 )),
694 )
695 .parse_next(input)
696}
697
698fn parse_binary_op(input: &mut &str) -> ModalResult<BinaryOp> {
700 preceded(
701 space0,
702 alt((
703 "<<".value(BinaryOp::Shl),
705 ">>".value(BinaryOp::Shr),
706 "==".value(BinaryOp::Eq),
707 "!=".value(BinaryOp::Ne),
708 "<=".value(BinaryOp::Le),
709 ">=".value(BinaryOp::Ge),
710 "&&".value(BinaryOp::LogAnd),
711 "||".value(BinaryOp::LogOr),
712 '+'.value(BinaryOp::Add),
714 '-'.value(BinaryOp::Sub),
715 '*'.value(BinaryOp::Mul),
716 '/'.value(BinaryOp::Div),
717 '%'.value(BinaryOp::Mod),
718 '&'.value(BinaryOp::And),
719 '|'.value(BinaryOp::Or),
720 '^'.value(BinaryOp::Xor),
721 '<'.value(BinaryOp::Lt),
722 '>'.value(BinaryOp::Gt),
723 )),
724 )
725 .parse_next(input)
726}
727
728fn parse_binary_rhs(
733 input: &mut &str,
734 min_precedence: Precedence,
735 mut lhs: Expression,
736) -> ModalResult<Expression> {
737 loop {
738 let checkpoint = input.checkpoint();
740 let op = match opt(parse_binary_op).parse_next(input) {
741 Ok(Some(op)) => op,
742 Ok(None) => {
743 break;
745 }
746 Err(e) => return Err(e),
747 };
748
749 let precedence = op.precedence();
750
751 if precedence < min_precedence {
754 input.reset(&checkpoint);
755 break;
756 }
757
758 let mut rhs = parse_unary(input)?;
760
761 loop {
763 let checkpoint2 = input.checkpoint();
764 let next_op = match opt(parse_binary_op).parse_next(input) {
765 Ok(Some(op)) => op,
766 Ok(None) => {
767 break;
768 }
769 Err(e) => return Err(e),
770 };
771
772 let next_precedence = next_op.precedence();
773
774 if next_precedence > precedence
777 || (next_precedence == precedence && !next_op.is_left_associative())
778 {
779 input.reset(&checkpoint2);
780 rhs = parse_binary_rhs(input, next_precedence, rhs)?;
781 } else {
782 input.reset(&checkpoint2);
784 break;
785 }
786 }
787
788 lhs = Expression::Binary {
790 left: Box::new(lhs),
791 op,
792 right: Box::new(rhs),
793 };
794 }
795
796 Ok(lhs)
797}
798
799pub fn parse_expression(input: &mut &str) -> ModalResult<Expression> {
801 let lhs = parse_unary(input)?;
802 parse_binary_rhs(input, Precedence::LOWEST, lhs)
803}
804
805fn parse_command_generic_filename(command: &str, input: &mut &str) -> ModalResult<String> {
806 let c = (
807 space0,
808 Caseless(command),
809 space1,
810 "\"",
811 parse_file_name,
812 "\"",
813 )
814 .parse_next(input)?;
815 Ok(c.4.to_string())
816}
817
818fn parse_command_include(input: &mut &str) -> ModalResult<Command> {
819 let filename = parse_command_generic_filename("include", input)?;
820 Ok(Command::Include { filename })
821}
822
823fn parse_command_inclib(input: &mut &str) -> ModalResult<Command> {
824 let filename = parse_command_generic_filename("inclib", input)?;
825 Ok(Command::IncLib { filename })
826}
827
828fn parse_command_origin(input: &mut &str) -> ModalResult<Command> {
829 let c = (space0, Caseless("org"), space1, parse_integer_constant).parse_next(input)?;
830 Ok(Command::Origin { address: c.3 })
831}
832
833fn parse_command_workspace(input: &mut &str) -> ModalResult<Command> {
834 let c = (
835 space0,
836 Caseless("workspace"),
837 space1,
838 parse_integer_constant,
839 )
840 .parse_next(input)?;
841 Ok(Command::Workspace { address: c.3 })
842}
843
844fn parse_command_equals(input: &mut &str) -> ModalResult<Command> {
845 let c = (
846 space0,
847 parse_symbol,
848 alt(((space0, "=", space0), (space1, "EQU", space1))),
849 parse_expression,
850 space0,
851 )
852 .parse_next(input)?;
853
854 Ok(Command::Equals {
855 left: c.1,
856 right: c.3,
857 })
858}
859
860fn parse_command_regs(input: &mut &str) -> ModalResult<Command> {
861 let c = (
862 space0,
863 Caseless("regs"),
864 space1,
865 parse_symbol,
866 "=",
867 parse_expression,
868 )
869 .parse_next(input)?;
870
871 Ok(Command::Regs {
872 register: c.3,
873 expression: c.5,
874 })
875}
876
877fn parse_attribute_bss(input: &mut &str) -> ModalResult<Attribute> {
878 Caseless("bss").parse_next(input)?;
879 Ok(Attribute::BSS)
880}
881
882fn parse_attribute_org(input: &mut &str) -> ModalResult<Attribute> {
883 let c = (Caseless("org"), "(", parse_integer_constant, ")").parse_next(input)?;
884 Ok(Attribute::Origin { address: c.2 })
885}
886
887fn parse_attribute_obj(input: &mut &str) -> ModalResult<Attribute> {
888 let c = (Caseless("obj"), "(", opt(parse_integer_constant), ")").parse_next(input)?;
889 Ok(Attribute::Obj { address: c.2 })
890}
891
892fn parse_attribute_over(input: &mut &str) -> ModalResult<Attribute> {
893 let c = (Caseless("over"), "(", parse_symbol, ")").parse_next(input)?;
894 Ok(Attribute::Over { group: c.2 })
895}
896
897fn parse_attribute_word(input: &mut &str) -> ModalResult<Attribute> {
898 Caseless("word").parse_next(input)?;
899 Ok(Attribute::Word)
900}
901
902fn parse_attribute_file(input: &mut &str) -> ModalResult<Attribute> {
903 let c = (Caseless("file"), "(\"", parse_file_name, "\")").parse_next(input)?;
904 Ok(Attribute::File {
905 filename: c.2.to_string(),
906 })
907}
908
909fn parse_attribute_size(input: &mut &str) -> ModalResult<Attribute> {
910 let c = (Caseless("size"), "(", parse_integer_constant, ")").parse_next(input)?;
911 Ok(Attribute::Size { maxsize: c.2 })
912}
913
914fn parse_attribute(input: &mut &str) -> ModalResult<Attribute> {
915 alt((
916 parse_attribute_bss,
917 parse_attribute_org,
918 parse_attribute_obj,
919 parse_attribute_over,
920 parse_attribute_word,
921 parse_attribute_file,
922 parse_attribute_size,
923 ))
924 .parse_next(input)
925}
926
927fn parse_attribute_list(input: &mut &str) -> ModalResult<Vec<Attribute>> {
928 separated(0.., parse_attribute, (space0, ',', space0)).parse_next(input)
929}
930
931fn parse_optional_attribute_list(input: &mut &str) -> ModalResult<Vec<Attribute>> {
932 let c = opt((space1, parse_attribute_list)).parse_next(input)?;
933 Ok(c.map_or_else(Vec::new, |(_, attr_list)| attr_list))
934}
935
936fn parse_command_group(input: &mut &str) -> ModalResult<Command> {
937 let c = (
938 space0,
939 parse_symbol,
940 space1,
941 Caseless("group"),
942 parse_optional_attribute_list,
943 )
944 .parse_next(input)?;
945
946 Ok(Command::Group {
947 name: c.1,
948 attributes: c.4,
949 })
950}
951
952fn parse_command_section_with_attributes(input: &mut &str) -> ModalResult<Command> {
953 let c = (
954 space0,
955 parse_symbol,
956 space1,
957 Caseless("section"),
958 parse_optional_attribute_list,
959 )
960 .parse_next(input)?;
961
962 Ok(Command::Section {
963 name: c.1,
964 group: None,
965 attributes: c.4,
966 })
967}
968
969fn parse_command_section_with_name(input: &mut &str) -> ModalResult<Command> {
970 let c = (
971 space0,
972 Caseless("section"),
973 space1,
974 parse_symbol,
975 opt((",", parse_symbol)),
976 )
977 .parse_next(input)?;
978
979 let group = c.4.map(|(_, group)| group);
980
981 Ok(Command::Section {
982 name: c.3,
983 group,
984 attributes: vec![],
985 })
986}
987
988fn parse_command_section(input: &mut &str) -> ModalResult<Command> {
989 alt((
990 parse_command_section_with_attributes,
991 parse_command_section_with_name,
992 ))
993 .parse_next(input)
994}
995
996fn parse_command_alias(input: &mut &str) -> ModalResult<Command> {
997 let c = (
998 space0,
999 parse_symbol,
1000 space1,
1001 Caseless("alias"),
1002 space1,
1003 parse_symbol,
1004 )
1005 .parse_next(input)?;
1006
1007 Ok(Command::Alias {
1008 name: c.1,
1009 target: c.5,
1010 })
1011}
1012
1013fn parse_command_unit(input: &mut &str) -> ModalResult<Command> {
1014 let c = (space0, Caseless("unit"), space1, parse_integer_constant).parse_next(input)?;
1015
1016 Ok(Command::Unit { unitnum: c.3 })
1017}
1018
1019fn parse_command_public(input: &mut &str) -> ModalResult<Command> {
1020 let c = (
1021 space0,
1022 Caseless("public"),
1023 space1,
1024 alt((Caseless("on"), Caseless("off")))
1025 .map(|s: &str| s.to_lowercase())
1026 .context(StrContext::Label("public"))
1027 .context(StrContext::Expected(StrContextValue::Description(
1028 "on or off",
1029 ))),
1030 )
1031 .parse_next(input)?;
1032
1033 Ok(Command::Public {
1034 public: c.3 == "on",
1035 })
1036}
1037
1038fn parse_command_generic_symbol_list(command: &str, input: &mut &str) -> ModalResult<Vec<String>> {
1039 let c = (space0, Caseless(command), space1, parse_symbol_list).parse_next(input)?;
1040 Ok(c.3)
1041}
1042
1043fn parse_command_global(input: &mut &str) -> ModalResult<Command> {
1044 let symbols = parse_command_generic_symbol_list("global", input)?;
1045 Ok(Command::Global { symbols })
1046}
1047
1048fn parse_command_xdef(input: &mut &str) -> ModalResult<Command> {
1049 let symbols = parse_command_generic_symbol_list("xdef", input)?;
1050 Ok(Command::XDef { symbols })
1051}
1052
1053fn parse_command_xref(input: &mut &str) -> ModalResult<Command> {
1054 let symbols = parse_command_generic_symbol_list("xref", input)?;
1055 Ok(Command::XRef { symbols })
1056}
1057
1058#[derive(Debug)]
1059pub struct Comment {
1060 pub comment: String,
1061}
1062
1063fn parse_comment(input: &mut &str) -> ModalResult<Comment> {
1064 let c = (space0, ";", space0, take_while(0.., |c| c != '\n')).parse_next(input)?;
1065 Ok(Comment {
1066 comment: c.3.into(),
1067 })
1068}
1069
1070pub fn parse_line(input: &mut &str) -> ModalResult<(Option<Command>, Option<Comment>)> {
1071 let command = opt(alt((
1072 parse_command_include,
1073 parse_command_inclib,
1074 parse_command_origin,
1075 parse_command_workspace,
1076 parse_command_equals,
1077 parse_command_regs,
1078 parse_command_group,
1079 parse_command_section,
1080 parse_command_alias,
1081 parse_command_unit,
1082 parse_command_global,
1083 parse_command_xdef,
1084 parse_command_xref,
1085 parse_command_public,
1086 )))
1087 .parse_next(input)?;
1088
1089 let comment = opt(parse_comment).parse_next(input)?;
1090
1091 Ok((command, comment))
1092}
1093
1094#[cfg(test)]
1095mod test {
1096 use super::*;
1097
1098 fn parse_command(input: &str) -> Command {
1099 let mut input = input;
1100 parse_line.parse_next(&mut input).unwrap().0.unwrap()
1101 }
1102
1103 #[test]
1104 fn test_parse_integer_constant() {
1105 let mut input = "1234";
1106 let output = parse_integer_constant.parse_next(&mut input).unwrap();
1107 assert_eq!(1234, output);
1108
1109 let mut input = "$1234";
1110 let output = parse_integer_constant.parse_next(&mut input).unwrap();
1111 assert_eq!(0x1234, output);
1112
1113 let mut input = "%1010";
1114 let output = parse_integer_constant.parse_next(&mut input).unwrap();
1115 assert_eq!(10, output);
1116 }
1117
1118 #[test]
1119 fn test_parse_command_include() {
1120 let output = parse_command("include \"foo.obj\"");
1121
1122 match output {
1123 Command::Include { filename } => assert_eq!("foo.obj", filename),
1124 _ => panic!("unexpected output: {:?}", output),
1125 }
1126 }
1127
1128 #[test]
1129 fn test_parse_command_inclib() {
1130 let output = parse_command("inclib \"bar.lib\"");
1131
1132 match output {
1133 Command::IncLib { filename } => assert_eq!("bar.lib", filename),
1134 _ => panic!("unexpected output: {:?}", output),
1135 }
1136 }
1137
1138 #[test]
1139 fn test_parse_command_org() {
1140 let output = parse_command("org 1234");
1141 match output {
1142 Command::Origin { address } => assert_eq!(1234, address),
1143 _ => panic!("unexpected output: {:?}", output),
1144 }
1145
1146 let output = parse_command("org $1234");
1147 match output {
1148 Command::Origin { address } => assert_eq!(0x1234, address),
1149 _ => panic!("unexpected output: {:?}", output),
1150 }
1151
1152 let output = parse_command("org %1010");
1153 match output {
1154 Command::Origin { address } => assert_eq!(10, address),
1155 _ => panic!("unexpected output: {:?}", output),
1156 }
1157 }
1158
1159 #[test]
1160 fn test_parse_command_workspace() {
1161 let output = parse_command("workspace 1234");
1162 match output {
1163 Command::Workspace { address } => assert_eq!(1234, address),
1164 _ => panic!("unexpected output: {:?}", output),
1165 }
1166
1167 let output = parse_command("workspace $1234");
1168 match output {
1169 Command::Workspace { address } => assert_eq!(0x1234, address),
1170 _ => panic!("unexpected output: {:?}", output),
1171 }
1172
1173 let output = parse_command("workspace %1010");
1174 match output {
1175 Command::Workspace { address } => assert_eq!(10, address),
1176 _ => panic!("unexpected output: {:?}", output),
1177 }
1178 }
1179
1180 #[test]
1181 fn test_parse_command_equals() {
1182 let output = parse_command("foo = bar");
1183 match output {
1184 Command::Equals { left, right } => {
1185 assert_eq!("foo", left);
1186 let Expression::Symbol(symbol) = right else {
1187 panic!("unexpected value: {:?}", right);
1188 };
1189 assert_eq!("bar", symbol);
1190 }
1191 _ => panic!("unexpected output: {:?}", output),
1192 }
1193 }
1194
1195 #[test]
1196 fn test_parse_command_regs() {
1197 let output = parse_command("regs pc=ENTRY_POINT");
1198
1199 match output {
1200 Command::Regs {
1201 register,
1202 expression,
1203 } => {
1204 assert_eq!("pc", register);
1205 let Expression::Symbol(symbol) = expression else {
1206 panic!("unexpected value: {:?}", expression);
1207 };
1208 assert_eq!("ENTRY_POINT", symbol);
1209 }
1210 _ => panic!("unexpected output: {:?}", output),
1211 }
1212 }
1213
1214 #[test]
1215 fn parse_command_group() {
1216 let output = parse_command("anim group");
1217
1218 match output {
1219 Command::Group { name, attributes } => {
1220 assert_eq!("anim", name);
1221 assert!(attributes.is_empty());
1222 }
1223 _ => panic!("unexpected output: {:?}", output),
1224 }
1225
1226 let output = parse_command("anim group bss");
1227
1228 match output {
1229 Command::Group { name, attributes } => {
1230 assert_eq!("anim", name);
1231 assert_eq!(vec![Attribute::BSS], attributes);
1232 }
1233 _ => panic!("unexpected output: {:?}", output),
1234 }
1235 }
1236
1237 #[test]
1238 fn test_parse_command_section() {
1239 let output = parse_command("anim section");
1240
1241 match output {
1242 Command::Section {
1243 name,
1244 group: _,
1245 attributes,
1246 } => {
1247 assert_eq!("anim", name);
1248 assert!(attributes.is_empty());
1249 }
1250 _ => panic!("unexpected output: {:?}", output),
1251 }
1252
1253 let output = parse_command("anim section bss");
1254
1255 let Command::Section {
1256 name,
1257 group: _,
1258 attributes,
1259 } = output
1260 else {
1261 panic!("unexpected output: {:?}", output);
1262 };
1263 assert_eq!("anim", name);
1264 assert_eq!(vec![Attribute::BSS], attributes);
1265
1266 let output = parse_command("section anim");
1267 let Command::Section {
1268 name,
1269 group,
1270 attributes,
1271 } = output
1272 else {
1273 panic!("unexpected output: {:?}", output);
1274 };
1275 assert_eq!("anim", name);
1276 assert!(group.is_none());
1277 assert!(attributes.is_empty());
1278
1279 let output = parse_command("section anim,squares");
1280 let Command::Section {
1281 name,
1282 group,
1283 attributes,
1284 } = output
1285 else {
1286 panic!("unexpected output: {:?}", output);
1287 };
1288 assert_eq!("anim", name);
1289 let Some(group) = group else {
1290 panic!("unexpected output: {:?}", group);
1291 };
1292 assert_eq!("squares".to_string(), group);
1293 assert!(attributes.is_empty());
1294 }
1295
1296 #[test]
1297 fn test_parse_command_alias() {
1298 let output = parse_command("foo alias bar");
1299 let Command::Alias { name, target } = output else {
1300 panic!("unexpected output: {:?}", output);
1301 };
1302 assert_eq!("foo".to_string(), name);
1303 assert_eq!("bar".to_string(), target);
1304 }
1305
1306 #[test]
1307 fn test_parse_command_unit() {
1308 let output = parse_command("unit %1010");
1309 let Command::Unit { unitnum } = output else {
1310 panic!("unexpected output: {:?}", output);
1311 };
1312 assert_eq!(10, unitnum);
1313 }
1314
1315 #[test]
1316 fn test_parse_command_global() {
1317 let output = parse_command("global foo");
1318
1319 match output {
1320 Command::Global { symbols } => assert_eq!(vec!["foo".to_string()], symbols),
1321 _ => panic!("unexpected output: {:?}", output),
1322 }
1323
1324 let output = parse_command("global foo, bar , baz");
1325
1326 match output {
1327 Command::Global { symbols } => assert_eq!(
1328 vec!["foo".to_string(), "bar".to_string(), "baz".to_string(),],
1329 symbols
1330 ),
1331 _ => panic!("unexpected output: {:?}", output),
1332 }
1333 }
1334
1335 #[test]
1336 fn test_parse_command_xdef() {
1337 let output = parse_command("xdef foo, bar, baz");
1338
1339 match output {
1340 Command::XDef { symbols } => assert_eq!(
1341 vec!["foo".to_string(), "bar".to_string(), "baz".to_string(),],
1342 symbols
1343 ),
1344 _ => panic!("unexpected output: {:?}", output),
1345 }
1346 }
1347
1348 #[test]
1349 fn test_parse_command_xref() {
1350 let output = parse_command("xref foo, bar, baz");
1351
1352 match output {
1353 Command::XRef { symbols } => assert_eq!(
1354 vec!["foo".to_string(), "bar".to_string(), "baz".to_string(),],
1355 symbols
1356 ),
1357 _ => panic!("unexpected output: {:?}", output),
1358 }
1359 }
1360
1361 #[test]
1362 fn test_parse_command_public() {
1363 let output = parse_command("public on");
1364 match output {
1365 Command::Public { public } => assert!(public),
1366 _ => panic!("unexpected output: {:?}", output),
1367 }
1368
1369 let output = parse_command("PUBLIC OFF");
1370 match output {
1371 Command::Public { public } => assert!(!public),
1372 _ => panic!("unexpected output: {:?}", output),
1373 }
1374 }
1375
1376 #[test]
1377 fn test_parse_comment() {
1378 let mut input = "; hello, world!";
1380 let line = parse_line.parse_next(&mut input).unwrap();
1381
1382 assert!(line.0.is_none());
1383 assert_eq!("hello, world!", line.1.unwrap().comment);
1384
1385 let mut input = "global foo; my global\nnot comment content";
1387 let line = parse_line.parse_next(&mut input).unwrap();
1388
1389 match line.0 {
1390 Some(Command::Global { symbols }) => assert_eq!(vec!["foo".to_string()], symbols),
1391 _ => panic!("unexpected output: {:?}", line),
1392 }
1393 assert_eq!("my global", line.1.unwrap().comment);
1394
1395 let mut input = "global foo";
1397 let line = parse_line.parse_next(&mut input).unwrap();
1398
1399 match line.0 {
1400 Some(Command::Global { symbols }) => assert_eq!(vec!["foo".to_string()], symbols),
1401 _ => panic!("unexpected output: {:?}", line),
1402 }
1403 assert!(line.1.is_none());
1404
1405 let mut input = " \t ";
1407 let line = parse_line.parse_next(&mut input).unwrap();
1408 assert!(line.0.is_none());
1409 assert!(line.1.is_none());
1410 }
1411
1412 #[test]
1413 fn test_parse_attribute_list() {
1414 let mut input = "bss,word,file(\"foo\")";
1415 let attributes = parse_attribute_list.parse_next(&mut input).unwrap();
1416 assert_eq!(3, attributes.len());
1417
1418 assert!(matches!(attributes.first(), Some(Attribute::BSS)));
1419 assert!(matches!(attributes.get(1), Some(Attribute::Word)));
1420 let Some(Attribute::File { filename }) = attributes.get(2) else {
1421 panic!("unexpected value: {:?}", attributes.get(2));
1422 };
1423 assert_eq!("foo", filename);
1424
1425 let mut input = "";
1426 let attributes = parse_attribute_list.parse_next(&mut input).unwrap();
1427 assert!(attributes.is_empty());
1428
1429 let mut input = "bss";
1430 let attributes = parse_attribute_list.parse_next(&mut input).unwrap();
1431 assert_eq!(1, attributes.len());
1432 assert!(matches!(attributes.first(), Some(Attribute::BSS)));
1433
1434 let mut input = "size(42)";
1435 let attributes = parse_attribute_list.parse_next(&mut input).unwrap();
1436 assert_eq!(1, attributes.len());
1437 assert!(matches!(
1438 attributes.first(),
1439 Some(Attribute::Size { maxsize: 42 })
1440 ));
1441
1442 let mut input = "over(squares)";
1443 let attributes = parse_attribute_list.parse_next(&mut input).unwrap();
1444 assert_eq!(1, attributes.len());
1445 let Some(Attribute::Over { group }) = attributes.first() else {
1446 panic!("unexpected value: {:?}", attributes.first());
1447 };
1448 assert_eq!("squares", group);
1449
1450 let mut input = "org($1234)";
1451 let attributes = parse_attribute_list.parse_next(&mut input).unwrap();
1452 assert_eq!(1, attributes.len());
1453 let Some(Attribute::Origin { address }) = attributes.first() else {
1454 panic!("unexpected value: {:?}", attributes.first());
1455 };
1456 assert_eq!(0x1234, *address);
1457
1458 let mut input = "obj($4567)";
1459 let attributes = parse_attribute_list.parse_next(&mut input).unwrap();
1460 assert_eq!(1, attributes.len());
1461 let Some(Attribute::Obj { address }) = attributes.first() else {
1462 panic!("unexpected value: {:?}", attributes.first());
1463 };
1464 assert!(matches!(address, Some(0x4567)));
1465
1466 let mut input = "obj()";
1467 let attributes = parse_attribute_list.parse_next(&mut input).unwrap();
1468 assert_eq!(1, attributes.len());
1469 let Some(Attribute::Obj { address }) = attributes.first() else {
1470 panic!("unexpected value: {:?}", attributes.first());
1471 };
1472 assert!(address.is_none());
1473 }
1474
1475 fn parse_expr(input: &str) -> Expression {
1476 let mut input = input;
1477 parse_expression(&mut input).expect("parse failed")
1478 }
1479
1480 #[test]
1481 fn test_constant() {
1482 assert_eq!(parse_expr("42"), Expression::Constant(42));
1483 assert_eq!(parse_expr("$ABCD"), Expression::Constant(0xABCD));
1484 assert_eq!(parse_expr("%1010"), Expression::Constant(0b1010));
1485 }
1486
1487 #[test]
1488 fn test_symbol() {
1489 assert_eq!(parse_expr("foo"), Expression::Symbol("foo".into()));
1490 assert_eq!(parse_expr("_start"), Expression::Symbol("_start".into()));
1491 assert_eq!(parse_expr("var123"), Expression::Symbol("var123".into()));
1492 }
1493
1494 #[test]
1495 fn test_simple_binary() {
1496 let expr = parse_expr("1 + 2");
1497 assert_eq!(
1498 expr,
1499 Expression::Binary {
1500 left: Box::new(Expression::Constant(1)),
1501 op: BinaryOp::Add,
1502 right: Box::new(Expression::Constant(2)),
1503 }
1504 );
1505 }
1506
1507 #[test]
1508 fn test_precedence() {
1509 let expr = parse_expr("1 + 2 * 3");
1511 assert_eq!(
1512 expr,
1513 Expression::Binary {
1514 left: Box::new(Expression::Constant(1)),
1515 op: BinaryOp::Add,
1516 right: Box::new(Expression::Binary {
1517 left: Box::new(Expression::Constant(2)),
1518 op: BinaryOp::Mul,
1519 right: Box::new(Expression::Constant(3)),
1520 }),
1521 }
1522 );
1523 }
1524
1525 #[test]
1526 fn test_left_associativity() {
1527 let expr = parse_expr("1 - 2 - 3");
1529 assert_eq!(
1530 expr,
1531 Expression::Binary {
1532 left: Box::new(Expression::Binary {
1533 left: Box::new(Expression::Constant(1)),
1534 op: BinaryOp::Sub,
1535 right: Box::new(Expression::Constant(2)),
1536 }),
1537 op: BinaryOp::Sub,
1538 right: Box::new(Expression::Constant(3)),
1539 }
1540 );
1541 }
1542
1543 #[test]
1544 fn test_parentheses() {
1545 let expr = parse_expr("(1 + 2) * 3");
1547 assert_eq!(
1548 expr,
1549 Expression::Binary {
1550 left: Box::new(Expression::Parens(Box::new(Expression::Binary {
1551 left: Box::new(Expression::Constant(1)),
1552 op: BinaryOp::Add,
1553 right: Box::new(Expression::Constant(2)),
1554 }))),
1555 op: BinaryOp::Mul,
1556 right: Box::new(Expression::Constant(3)),
1557 }
1558 );
1559 }
1560
1561 #[test]
1562 fn test_unary() {
1563 assert_eq!(
1564 parse_expr("-42"),
1565 Expression::Unary {
1566 op: UnaryOp::Neg,
1567 operand: Box::new(Expression::Constant(42)),
1568 }
1569 );
1570
1571 assert_eq!(
1572 parse_expr("~$FF"),
1573 Expression::Unary {
1574 op: UnaryOp::Not,
1575 operand: Box::new(Expression::Constant(0xFF)),
1576 }
1577 );
1578 }
1579
1580 #[test]
1581 fn test_function_call() {
1582 let expr = parse_expr("sectstart(text)");
1583 assert_eq!(
1584 expr,
1585 Expression::Function {
1586 name: "sectstart".into(),
1587 arg: Box::new(Expression::Symbol("text".into())),
1588 }
1589 );
1590 }
1591
1592 #[test]
1593 fn test_complex_expression() {
1594 let expr = parse_expr("base + (offset & $FFFF) | $8000");
1596
1597 match expr {
1600 Expression::Binary {
1601 left,
1602 op: BinaryOp::Or,
1603 right,
1604 } => {
1605 assert_eq!(*right, Expression::Constant(0x8000));
1607
1608 match *left {
1610 Expression::Binary {
1611 left: base,
1612 op: BinaryOp::Add,
1613 right: mask_expr,
1614 } => {
1615 assert_eq!(*base, Expression::Symbol("base".into()));
1616
1617 match *mask_expr {
1619 Expression::Parens(inner) => match *inner {
1620 Expression::Binary {
1621 left,
1622 op: BinaryOp::And,
1623 right,
1624 } => {
1625 assert_eq!(*left, Expression::Symbol("offset".into()));
1626 assert_eq!(*right, Expression::Constant(0xFFFF));
1627 }
1628 _ => panic!("unexpected inner expression"),
1629 },
1630 _ => panic!("expected parenthesized expression"),
1631 }
1632 }
1633 _ => panic!("unexpected left side"),
1634 }
1635 }
1636 _ => panic!("expected binary OR expression"),
1637 }
1638 }
1639
1640 #[test]
1641 fn test_bitwise_operators() {
1642 parse_expr("a & b");
1643 parse_expr("a | b");
1644 parse_expr("a ^ b");
1645 parse_expr("a << 4");
1646 parse_expr("a >> 2");
1647 }
1648
1649 #[test]
1650 fn test_comparison_operators() {
1651 parse_expr("a == b");
1652 parse_expr("a != b");
1653 parse_expr("a < b");
1654 parse_expr("a <= b");
1655 parse_expr("a > b");
1656 parse_expr("a >= b");
1657 }
1658
1659 #[test]
1660 fn test_logical_operators() {
1661 parse_expr("a && b");
1662 parse_expr("a || b");
1663 parse_expr("!a");
1664 }
1665
1666 #[test]
1667 fn test_whitespace_handling() {
1668 assert_eq!(parse_expr("1+2"), parse_expr("1 + 2"));
1669 assert_eq!(parse_expr(" 1 + 2 "), parse_expr("1+2"));
1670 }
1671
1672 #[test]
1673 fn test_real_world_examples() {
1674 parse_expr("BUFFER_END = BUFFER_START + $1000");
1676 parse_expr("(base & $FFFF0000) | $8000");
1677 parse_expr("sectstart(text) + $100");
1678 parse_expr("-(offset + 4)");
1679 parse_expr("~(flags | $FF)");
1680 }
1681
1682 #[test]
1683 fn test_display() {
1684 let expr = Expression::Binary {
1685 left: Box::new(Expression::Symbol("a".into())),
1686 op: BinaryOp::Add,
1687 right: Box::new(Expression::Constant(0x100)),
1688 };
1689 assert_eq!(format!("{}", expr), "(a + $100)");
1690 }
1691}