1const NUM_CHARS: usize = 18;
5
6const NUM_LINES: usize = 16;
8
9#[derive(Debug, PartialEq)]
11pub struct Label(pub String, pub usize);
12
13#[derive(Debug, PartialEq)]
16pub struct Line(pub usize, pub Option<Label>, pub Vec<String>);
17
18pub fn lex_program(src: &str) -> Vec<Line> {
20 let mut next_op = 0;
21 let mut lines = Vec::new();
22
23 for (index, line) in src.lines().take(NUM_LINES).enumerate() {
24 let (maybe_label, words) = lex_line(line);
25 let label = if let Some(label) = maybe_label {
26 Some(Label(label, next_op))
27 } else {
28 None
29 };
30
31 if words.len() > 0 {
32 next_op += 1;
33 }
34
35 lines.push(Line(index, label, words));
36 }
37
38 lines
39}
40
41fn lex_line(line: &str) -> (Option<String>, Vec<String>) {
43 let mut label = None;
44 let mut words = Vec::new();
45 let mut word = String::new();
46
47 for c in line.to_uppercase().chars().take(NUM_CHARS) {
48 if is_comment_delimiter(c) {
49 break;
50 } else if is_whitespace(c) {
51 if word.len() > 0 {
52 words.push(word.clone());
53 word.clear();
54 }
55 } else if label.is_some() || !is_label_delimiter(c) {
56 word.push(c)
57 } else {
58 label = Some(word.clone());
59 word.clear();
60 }
61 }
62
63 if word.len() > 0 {
64 words.push(word.clone());
65 }
66
67 (label, words)
68}
69
70fn is_whitespace(c: char) -> bool {
72 c == ' ' || c == ','
73}
74
75fn is_comment_delimiter(c: char) -> bool {
77 c == '#'
78}
79
80fn is_label_delimiter(c: char) -> bool {
82 c == ':'
83}
84
85#[test]
86fn test_is_whitespace() {
87 assert!(is_whitespace(' '));
88 assert!(is_whitespace(','));
89 assert!(!is_whitespace('1'));
90 assert!(!is_whitespace('A'));
91}
92
93#[test]
94fn test_is_comment_delimiter() {
95 assert!(is_comment_delimiter('#'));
96 assert!(!is_comment_delimiter('1'));
97 assert!(!is_comment_delimiter('A'));
98}
99
100#[test]
101fn test_is_label_delimiter() {
102 assert!(is_label_delimiter(':'));
103 assert!(!is_label_delimiter('1'));
104 assert!(!is_label_delimiter('A'));
105}
106
107#[test]
108fn test_lex_line() {
109 let (lbl, lex) = lex_line("LABEL: MOV UP ACC # comment");
110 assert_eq!(lbl, Some("LABEL".to_string()));
111 assert_eq!(lex.len(), 3);
112 assert_eq!(lex[0], "MOV");
113 assert_eq!(lex[1], "UP");
114 assert_eq!(lex[2], "ACC");
115
116 let (lbl, lex) = lex_line("ADD 1");
117 assert_eq!(lbl, None);
118 assert_eq!(lex.len(), 2);
119 assert_eq!(lex[0], "ADD");
120 assert_eq!(lex[1], "1");
121
122 let (lbl, lex) = lex_line(":ADD 1 2 3");
123 assert_eq!(lbl, Some("".to_string()));
124 assert_eq!(lex.len(), 4);
125 assert_eq!(lex[0], "ADD");
126 assert_eq!(lex[1], "1");
127 assert_eq!(lex[2], "2");
128 assert_eq!(lex[3], "3");
129
130 let (lbl, lex) = lex_line(",,LABEL:,,ADD,1,,,,,");
131 assert_eq!(lbl, Some("LABEL".to_string()));
132 assert_eq!(lex.len(), 2);
133 assert_eq!(lex[0], "ADD");
134 assert_eq!(lex[1], "1");
135
136 let (lbl, lex) = lex_line("# LABEL: MOV UP ACC");
137 assert_eq!(lbl, None);
138 assert_eq!(lex.len(), 0);
139
140 let (lbl, lex) = lex_line("LABEL: MOV LEFT RIGHT");
141 assert_eq!(lbl, Some("LABEL".to_string()));
142 assert_eq!(lex.len(), 3);
143 assert_eq!(lex[0], "MOV");
144 assert_eq!(lex[1], "LEFT");
145 assert_eq!(lex[2], "RI");
146}
147
148#[test]
149fn test_lex_program() {
150 let lines = lex_program("MOV UP ACC\nADD 1\nMOV ACC DOWN");
151 assert_eq!(lines.len(), 3);
152
153 let lines = lex_program("1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n");
154 assert_eq!(lines.len(), 16);
155
156 let lines = lex_program("1:\n2:\n3: ADD 1\n4: ADD 1\n");
157 assert_eq!(lines.len(), 4);
158 assert_eq!(lines[0].1, Some(Label("1".to_string(), 0)));
159 assert_eq!(lines[1].1, Some(Label("2".to_string(), 0)));
160 assert_eq!(lines[2].1, Some(Label("3".to_string(), 0)));
161 assert_eq!(lines[3].1, Some(Label("4".to_string(), 1)));
162}
163