microcad_lang/lower/lower/
source.rs1use crate::lower::{Lower, LowerContext, LowerError, LowerErrorsWithSource, ir};
5
6use microcad_lang_base::{Hashed, SrcReferrer, Url, virtual_url};
7use microcad_lang_parse::ast;
8
9impl ir::Source {
10 pub fn from_source(source: &ast::Source) -> Result<std::rc::Rc<Self>, LowerError> {
11 let context = LowerContext::new(source.code.as_str()).with_line_offset(source.line_offset);
12 Ok(std::rc::Rc::new(Self {
13 doc: None,
14 statements: ir::StatementList::lower(&source.ast.statements, &context)?,
15 source: source.code.clone(),
16 name: ir::QualifiedName::default(),
17 url: source.url.clone(),
18 line_offset: source.line_offset,
19 }))
20 }
21
22 pub fn load(
24 path: impl AsRef<std::path::Path> + std::fmt::Debug,
25 ) -> Result<std::rc::Rc<Self>, LowerErrorsWithSource> {
26 let (source, error) = Self::load_with_name(&path, Self::name_from_path(&path));
27 match error {
28 Some(error) => Err(error),
29 None => Ok(source),
30 }
31 }
32
33 pub fn load_with_name(
35 path: impl AsRef<std::path::Path> + std::fmt::Debug,
36 name: ir::QualifiedName,
37 ) -> (std::rc::Rc<Self>, Option<LowerErrorsWithSource>) {
38 let path = path.as_ref();
39 log::trace!(
40 "{load} file {path} [{name}]",
41 path = path.display(),
42 load = microcad_lang_base::mark!(LOAD)
43 );
44
45 let buf = match std::fs::read_to_string(path) {
46 Ok(buf) => buf,
47 Err(error) => {
48 let error = LowerError::LoadSource(name.src_ref(), path.into(), error);
49 let mut source_file = ir::Source::new(
50 None,
51 ir::StatementList::default(),
52 Hashed::new(String::new()),
53 Url::from_file_path(path).unwrap_or(virtual_url("invalid")),
54 );
55 source_file.name = name;
56 return (std::rc::Rc::new(source_file), Some(error.into()));
57 }
58 };
59
60 let (mut source_file, errors) = Self::load_inner(None, path, &buf, 0);
61 source_file.name = name;
62 log::debug!(
63 "Successfully loaded external file {} to {}",
64 path.display(),
65 source_file.name
66 );
67
68 (std::rc::Rc::new(source_file), errors)
69 }
70
71 pub fn load_from_str(
74 name: Option<&str>,
75 path: impl AsRef<std::path::Path>,
76 source_str: &str,
77 ) -> Result<std::rc::Rc<Self>, Vec<LowerError>> {
78 let (source, error) = Self::load_inner(name, path, source_str, 0);
79 match error {
80 Some(error) => Err(error.errors),
81 None => Ok(std::rc::Rc::new(source)),
82 }
83 }
84
85 fn load_inner(
86 name: Option<&str>,
87 path: impl AsRef<std::path::Path>,
88 source_str: &str,
89 line_offset: u32,
90 ) -> (Self, Option<LowerErrorsWithSource>) {
91 log::trace!(
92 "{load} source from string",
93 load = microcad_lang_base::mark!(LOAD)
94 );
95 let lower_context = LowerContext::new(source_str).with_line_offset(line_offset);
96
97 let dummy_source = || {
98 ir::Source::new(
99 None,
100 ir::StatementList::default(),
101 Hashed::new(source_str.into()),
102 Url::from_file_path(&path).unwrap_or(virtual_url("dummy")),
103 )
104 .with_line_offset(line_offset)
105 };
106
107 let ast = match crate::lower::lower::build_ast(source_str, &lower_context) {
108 Ok(ast) => ast,
109 Err(error) => {
110 return (dummy_source(), Some(error));
111 }
112 };
113
114 let mut source_file = match Self::lower(&ast, &lower_context).map_err(|error| vec![error]) {
115 Ok(source_file) => source_file,
116 Err(errors) => {
117 return (
118 dummy_source(),
119 Some(LowerErrorsWithSource {
120 errors,
121 source_code: Some(Hashed::new(source_str.into())),
122 }),
123 );
124 }
125 };
126 if let Some(name) = name {
127 source_file.set_name(ir::Identifier::no_ref(name).into());
128 } else {
129 source_file.set_name(Self::name_from_path(&path));
130 };
131 source_file.set_filename(path);
132 log::debug!("Successfully loaded source from string");
133 (source_file.with_line_offset(line_offset), None)
134 }
135
136 pub fn name_from_path(path: impl AsRef<std::path::Path>) -> ir::QualifiedName {
138 ir::Identifier::no_ref(
139 &path
140 .as_ref()
141 .file_stem()
142 .expect("illegal file name")
143 .to_string_lossy(),
144 )
145 .into()
146 }
147}
148
149impl Lower for ir::Source {
150 type AstNode = ast::Program;
151
152 fn lower(node: &Self::AstNode, context: &LowerContext) -> Result<Self, LowerError> {
153 Ok(ir::Source::new(
154 None, ir::StatementList::lower(&node.statements, context)?,
156 Hashed::new(context.source.to_string()),
157 microcad_lang_base::virtual_url("name"),
158 ))
159 }
160}