1use crate::ast::raw_ast::{Comment, Directive, Instruction, Label, Span};
2use getset::Getters;
3use pest::Stack;
4use std::collections::HashMap;
5
6#[derive(Debug, Getters)]
7pub struct Program {
8 #[get = "pub"]
9 items: Vec<ProgramItem>,
10}
11
12#[derive(Debug)]
13pub struct StandardTransform<'a> {
14 label_buffer: Stack<Label>,
15 forward_next_comment: bool,
16 look_table: LineColumnLookTable<'a>,
17 hybrid_inline_comment: bool,
18}
19
20#[allow(dead_code)]
21#[derive(Debug, Copy, Clone)]
22pub struct LineColumn {
23 line: usize,
24 column: usize,
25}
26
27#[derive(Debug, Clone)]
28enum RawProgramItem {
29 Comment(Comment),
30 Instruction(Vec<Label>, Instruction, Option<Comment>),
31 Directive(Vec<Label>, Directive, Option<Comment>),
32 EOL(Vec<Label>),
33}
34
35#[derive(Debug, Clone)]
36pub enum ProgramItem {
37 Comment(Comment, LineColumn),
38 Instruction(Vec<Label>, Instruction, Option<Comment>, LineColumn),
39 Directive(Vec<Label>, Directive, Option<Comment>, LineColumn),
40 EOL(Vec<Label>),
41}
42
43impl<'a> StandardTransform<'a> {
44 pub fn new(hybrid_inline_comment: bool, file_content: &'a str) -> Self {
45 Self {
46 label_buffer: Stack::new(),
47 forward_next_comment: true,
48 look_table: LineColumnLookTable::new(file_content),
49 hybrid_inline_comment,
50 }
51 }
52
53 pub fn transform(&mut self, program: crate::ast::raw_ast::Program) -> Program {
54 let mut labelled_items: Vec<_> = program
55 .items()
56 .into_iter()
57 .map(|p| self.hybrid_label(p))
58 .collect();
59 if !self.label_buffer.is_empty() {
60 let mut labels = vec![];
61 while let Some(item) = self.label_buffer.pop() {
62 labels.push(item);
63 }
64 labelled_items.push(Some(RawProgramItem::EOL(labels)));
65 }
66 let labelled_items: Vec<_> = labelled_items
67 .into_iter()
68 .filter_map(|p| p)
69 .map(|p| self.add_line_info(p))
70 .collect();
71 Program {
72 items: if self.hybrid_inline_comment {
73 let mut res = vec![];
74 for (index, current) in labelled_items.iter().enumerate() {
75 let next = labelled_items.get(index + 1);
76 res.push(self.hybrid_comment(current.clone(), next.map(|i| i.clone())));
77 }
78 res.into_iter().filter_map(|i| i).collect()
79 } else {
80 labelled_items
81 },
82 }
83 }
84
85 fn hybrid_label(
86 &mut self,
87 program_item: crate::ast::raw_ast::ProgramItem,
88 ) -> Option<RawProgramItem> {
89 match program_item {
90 crate::ast::raw_ast::ProgramItem::Label(label) => {
91 self.label_buffer.push(label);
92 None
93 }
94 crate::ast::raw_ast::ProgramItem::Instruction(instruction) => {
95 let mut labels = vec![];
96 while let Some(item) = self.label_buffer.pop() {
97 labels.push(item);
98 }
99 Some(RawProgramItem::Instruction(labels, instruction, None))
100 }
101 crate::ast::raw_ast::ProgramItem::Directive(directive) => {
102 let mut labels = vec![];
103 while let Some(item) = self.label_buffer.pop() {
104 labels.push(item);
105 }
106 Some(RawProgramItem::Directive(labels, directive, None))
107 }
108 crate::ast::raw_ast::ProgramItem::Comment(comment) => {
109 Some(RawProgramItem::Comment(comment))
110 }
111 }
112 }
113
114 fn add_line_info(&mut self, program_item: RawProgramItem) -> ProgramItem {
115 match program_item {
116 RawProgramItem::Comment(comment) => {
117 let lc = self.look_table.get_line_and_column(comment.span());
118 ProgramItem::Comment(comment, lc)
119 }
120 RawProgramItem::Instruction(label, instruction, comment) => {
121 let lc = self.look_table.get_line_and_column(instruction.span());
122 ProgramItem::Instruction(label, instruction, comment, lc)
123 }
124 RawProgramItem::Directive(label, directive, comment) => {
125 let lc = self.look_table.get_line_and_column(directive.span());
126 ProgramItem::Directive(label, directive, comment, lc)
127 }
128 RawProgramItem::EOL(label) => ProgramItem::EOL(label),
129 }
130 }
131
132 fn hybrid_comment(
133 &mut self,
134 curr: ProgramItem,
135 next: Option<ProgramItem>,
136 ) -> Option<ProgramItem> {
137 match curr {
138 ProgramItem::Comment(comment, comment_lc) => {
139 if self.forward_next_comment {
140 Some(ProgramItem::Comment(comment, comment_lc))
141 } else {
142 self.forward_next_comment = true;
143 None
144 }
145 }
146 ProgramItem::Instruction(labels, instruction, comment, lc) => {
147 if let Some(ProgramItem::Comment(comment, lc_comment)) = next {
148 if lc_comment.at_the_same_line(&lc) {
149 self.forward_next_comment = false;
150 return Some(ProgramItem::Instruction(
151 labels,
152 instruction,
153 Some(comment),
154 lc,
155 ));
156 }
157 }
158 Some(ProgramItem::Instruction(labels, instruction, comment, lc))
159 }
160 ProgramItem::Directive(labels, directive, comment, lc) => {
161 if let Some(ProgramItem::Comment(comment, lc_comment)) = next {
162 if lc_comment.at_the_same_line(&lc) {
163 self.forward_next_comment = false;
164 return Some(ProgramItem::Directive(labels, directive, Some(comment), lc));
165 }
166 }
167 Some(ProgramItem::Directive(labels, directive, comment, lc))
168 }
169 ProgramItem::EOL(labels) => Some(ProgramItem::EOL(labels)),
170 }
171 }
172}
173
174#[derive(Debug)]
175struct LineColumnLookTable<'a> {
176 line_start_indices: HashMap<usize, (usize, usize)>, lines: Vec<&'a str>,
178}
179
180impl<'a> LineColumnLookTable<'a> {
181 pub fn new(file_content: &'a str) -> Self {
183 let mut line_start_indices = HashMap::new();
184 let mut char_count = 0; let mut line_number = 1; for line in file_content.lines() {
188 line_start_indices.insert(char_count, (line_number, 1));
190 char_count += line.len() + 1; line_number += 1;
192 }
193 let lines: Vec<&str> = file_content.lines().collect();
194
195 LineColumnLookTable {
196 line_start_indices,
197 lines,
198 }
199 }
200
201 pub fn get_line_and_column(&self, span: &Span) -> LineColumn {
203 let start = *span.start();
204 for (start_index, (line_number, _)) in self.line_start_indices.iter() {
206 let line_len = self.lines[*line_number - 1].len();
207 if start >= *start_index && start < *start_index + line_len {
208 let column = start - *start_index + 1;
210 return LineColumn {
211 line: *line_number,
212 column,
213 };
214 }
215 }
216
217 unreachable!()
218 }
219}
220
221impl LineColumn {
222 pub fn at_the_same_line(&self, other: &LineColumn) -> bool {
223 self.line == other.line
224 }
225}
226
227impl ProgramItem {
228 pub fn is_comment(&self) -> bool {
229 matches!(self, ProgramItem::Comment(..))
230 }
231
232 pub fn is_instruction(&self) -> bool {
233 matches!(self, ProgramItem::Instruction(..))
234 }
235
236 pub fn is_directive(&self) -> bool {
237 matches!(self, ProgramItem::Directive(..))
238 }
239
240 pub fn is_eol(&self) -> bool {
241 matches!(self, ProgramItem::EOL(..))
242 }
243}