microcad_lang/parse/
source_file.rs1use crate::{parse::*, parser::*, rc::*, tree_display::*};
5use std::fs::read_to_string;
6
7impl SourceFile {
8 pub fn load(
10 path: impl AsRef<std::path::Path> + std::fmt::Debug,
11 ) -> Result<Rc<Self>, ParseErrorWithSource> {
12 Self::load_with_name(&path, Self::name_from_path(&path))
13 }
14
15 pub fn load_with_name(
17 path: impl AsRef<std::path::Path> + std::fmt::Debug,
18 name: QualifiedName,
19 ) -> Result<Rc<Self>, ParseErrorWithSource> {
20 let path = path.as_ref();
21 log::trace!(
22 "{load} file {path} [{name}]",
23 path = path.display(),
24 load = crate::mark!(LOAD)
25 );
26
27 let buf = read_to_string(path).map_err(|error| {
28 ParseError::LoadSource(name.src_ref(), path.into(), error)
29 })?;
30
31 let mut source_file: Self = Parser::parse_rule(Rule::source_file, &buf, 0)
32 .map_err(|error| error.with_source(buf))?;
33 assert_ne!(source_file.hash, 0);
34 source_file.set_filename(path);
35 source_file.name = name;
36 log::debug!(
37 "Successfully loaded external file {} to {}",
38 path.display(),
39 source_file.name
40 );
41 log::trace!("Syntax tree:\n{}", FormatTree(&source_file));
42
43 Ok(Rc::new(source_file))
44 }
45
46 pub fn load_from_str(
49 name: Option<&str>,
50 path: impl AsRef<std::path::Path>,
51 source_str: &str,
52 ) -> ParseResult<Rc<Self>> {
53 log::trace!("{load} source from string", load = crate::mark!(LOAD));
54 let mut source_file: Self =
55 Parser::parse_rule(crate::parser::Rule::source_file, source_str, 0)?;
56 if let Some(name) = name {
57 source_file.set_name(QualifiedName::from_id(Identifier::no_ref(name)));
58 } else {
59 source_file.set_name(Self::name_from_path(&path));
60 };
61 source_file.set_filename(path);
62 log::debug!("Successfully loaded source from string");
63 log::trace!("Syntax tree:\n{}", FormatTree(&source_file));
64 Ok(Rc::new(source_file))
65 }
66
67 fn calculate_hash(value: &str) -> u64 {
68 use std::hash::{Hash, Hasher};
69 let mut hasher = rustc_hash::FxHasher::default();
70 value.hash(&mut hasher);
71 hasher.finish()
72 }
73
74 pub fn name_from_path(path: impl AsRef<std::path::Path>) -> QualifiedName {
76 QualifiedName::from_id(Identifier::no_ref(
77 &path
78 .as_ref()
79 .file_stem()
80 .expect("illegal file name")
81 .to_string_lossy(),
82 ))
83 }
84}
85
86impl Parse for SourceFile {
87 fn parse(mut pair: Pair) -> ParseResult<Self> {
88 let hash = Self::calculate_hash(pair.as_str());
90 pair.set_source_hash(hash);
91
92 Ok(SourceFile::new(
93 crate::find_rule_opt!(pair, doc_block)?,
94 crate::find_rule!(pair, statement_list)?,
95 pair.as_span().as_str().to_string(),
96 hash,
97 ))
98 }
99}
100
101#[test]
102fn parse_source_file() {
103 let source_file = Parser::parse_rule::<SourceFile>(
104 Rule::source_file,
105 r#"use std::log::info;
106 part Foo(r: Scalar) {
107 info("Hello, world, {r}!");
108 }
109 Foo(20.0);
110 "#,
111 0,
112 )
113 .expect("test error");
114
115 assert_eq!(source_file.statements.len(), 3);
116}