1pub mod cst_parser_chirho;
13pub mod layout_chirho;
14pub mod lexer_chirho;
15pub mod lower_chirho;
16#[cfg(test)]
17mod proptest_chirho;
18
19use haskelujah_diagnostics_chirho::{DiagnosticBundleChirho, DiagnosticChirho, ErrorCodeChirho};
20use haskelujah_span_chirho::{ByteOffsetChirho, SpanChirho};
21use haskelujah_syntax_chirho::{ModuleHeaderChirho, SourceFileChirho};
22
23pub const DEFAULT_MODULE_NAME_CHIRHO: &str = "Main";
24
25#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct ParsedModuleChirho {
27 pub module_header_chirho: ModuleHeaderChirho,
28 pub source_file_chirho: SourceFileChirho,
29}
30
31pub fn scan_module_header_chirho(
37 source_file_chirho: SourceFileChirho,
38) -> Result<ParsedModuleChirho, DiagnosticBundleChirho> {
39 let file_id_chirho = source_file_chirho.file_id_chirho();
40 let mut saw_non_comment_code_chirho = false;
41 let mut byte_offset_chirho: usize = 0;
42
43 for raw_line_chirho in source_file_chirho.contents_chirho().lines() {
44 let line_start_chirho = byte_offset_chirho;
45 let line_len_chirho = raw_line_chirho.len();
46 byte_offset_chirho += line_len_chirho + 1; let trimmed_line_chirho = raw_line_chirho.trim();
50
51 if trimmed_line_chirho.is_empty() {
52 continue;
53 }
54
55 if trimmed_line_chirho.starts_with("--") {
56 continue;
57 }
58
59 if let Some(module_header_chirho) =
60 parse_module_header_line_chirho(trimmed_line_chirho, file_id_chirho, line_start_chirho)?
61 {
62 return Ok(ParsedModuleChirho {
63 module_header_chirho,
64 source_file_chirho,
65 });
66 }
67
68 saw_non_comment_code_chirho = true;
69 break;
70 }
71
72 let span_chirho = if saw_non_comment_code_chirho {
73 SpanChirho::new_chirho(
74 file_id_chirho,
75 ByteOffsetChirho::new_chirho(0),
76 ByteOffsetChirho::new_chirho(0),
77 )
78 } else {
79 SpanChirho::DUMMY_CHIRHO
80 };
81
82 Ok(ParsedModuleChirho {
83 module_header_chirho: ModuleHeaderChirho {
84 module_name_chirho: DEFAULT_MODULE_NAME_CHIRHO.to_owned(),
85 span_chirho,
86 },
87 source_file_chirho,
88 })
89}
90
91fn parse_module_header_line_chirho(
92 trimmed_line_chirho: &str,
93 file_id_chirho: haskelujah_span_chirho::FileIdChirho,
94 line_start_offset_chirho: usize,
95) -> Result<Option<ModuleHeaderChirho>, DiagnosticBundleChirho> {
96 if !trimmed_line_chirho.starts_with("module ") {
97 return Ok(None);
98 }
99
100 let remainder_chirho = trimmed_line_chirho
101 .strip_prefix("module ")
102 .expect("module prefix was checked");
103 let remainder_chirho = remainder_chirho.trim();
104 let module_name_chirho = remainder_chirho
105 .strip_suffix(" where")
106 .or_else(|| remainder_chirho.strip_suffix("where"))
107 .map(str::trim)
108 .filter(|module_name_chirho| !module_name_chirho.is_empty());
109
110 match module_name_chirho {
111 Some(module_name_chirho) => {
112 let span_chirho = SpanChirho::new_chirho(
113 file_id_chirho,
114 ByteOffsetChirho::from_usize_chirho(line_start_offset_chirho),
115 ByteOffsetChirho::from_usize_chirho(
116 line_start_offset_chirho + trimmed_line_chirho.len(),
117 ),
118 );
119 Ok(Some(ModuleHeaderChirho {
120 module_name_chirho: module_name_chirho.to_owned(),
121 span_chirho,
122 }))
123 }
124 None => {
125 let span_chirho = SpanChirho::new_chirho(
126 file_id_chirho,
127 ByteOffsetChirho::from_usize_chirho(line_start_offset_chirho),
128 ByteOffsetChirho::from_usize_chirho(
129 line_start_offset_chirho + trimmed_line_chirho.len(),
130 ),
131 );
132 Err(DiagnosticChirho::error_with_code_chirho(
133 ErrorCodeChirho::error_chirho(1),
134 "malformed module header; expected `module <Name> where`",
135 span_chirho,
136 )
137 .into())
138 }
139 }
140}
141
142#[cfg(test)]
143mod tests_chirho {
144 use super::{DEFAULT_MODULE_NAME_CHIRHO, scan_module_header_chirho};
145 use haskelujah_span_chirho::SourceMapChirho;
146 use haskelujah_syntax_chirho::SourceFileChirho;
147
148 #[test]
149 fn parses_explicit_module_header_chirho() {
150 let mut source_map_chirho = SourceMapChirho::new_chirho();
151 let source_file_chirho = SourceFileChirho::from_source_map_chirho(
152 &mut source_map_chirho,
153 "SampleChirho.hs",
154 "module SampleChirho where\nvalueChirho = 1\n",
155 );
156
157 let parsed_module_chirho = scan_module_header_chirho(source_file_chirho)
158 .expect("parser should accept a valid module header");
159
160 assert_eq!(
161 parsed_module_chirho.module_header_chirho.module_name_chirho,
162 "SampleChirho"
163 );
164 }
165
166 #[test]
167 fn defaults_to_main_when_no_module_header_exists_chirho() {
168 let mut source_map_chirho = SourceMapChirho::new_chirho();
169 let source_file_chirho = SourceFileChirho::from_source_map_chirho(
170 &mut source_map_chirho,
171 "MainChirho.hs",
172 "mainChirho = putStrLn \"hi\"\n",
173 );
174
175 let parsed_module_chirho = scan_module_header_chirho(source_file_chirho)
176 .expect("parser should accept scripts without a module header");
177
178 assert_eq!(
179 parsed_module_chirho.module_header_chirho.module_name_chirho,
180 DEFAULT_MODULE_NAME_CHIRHO
181 );
182 }
183}