diff --git a/src/ast.rs b/src/ast.rs index afb35a0..c19b1c6 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,4 +1,4 @@ -use std::fmt; +use std::{fmt, path::PathBuf}; use crate::Span; @@ -73,6 +73,10 @@ pub enum DataValues { values: Vec, }, String(String), + File { + path: PathBuf, + data: Vec + } } #[derive(Debug, Clone)] diff --git a/src/constfold.rs b/src/constfold.rs index 9a8e47a..495c0d2 100644 --- a/src/constfold.rs +++ b/src/constfold.rs @@ -18,7 +18,7 @@ pub fn fold_script(script: &mut ast::Script) { fold_expr(value); } } - ast::DataValues::String(_) => (), + ast::DataValues::String(_) | ast::DataValues::File { .. } => (), } } } diff --git a/src/emit.rs b/src/emit.rs index fd22731..d095c04 100644 --- a/src/emit.rs +++ b/src/emit.rs @@ -173,6 +173,9 @@ pub fn emit(script: &ast::Script) -> Vec { segment_data.push(c as u8); } } + ast::DataValues::File { data, .. } => { + segment_data.extend_from_slice(data); + } } } data_section.active( diff --git a/src/includes.rs b/src/includes.rs new file mode 100644 index 0000000..28057de --- /dev/null +++ b/src/includes.rs @@ -0,0 +1,31 @@ +use std::fs::File; +use std::io::prelude::*; +use std::path::Path; + +use crate::ast; +use anyhow::{anyhow, Result}; + +pub fn resolve_includes(script: &mut ast::Script, path: &Path) -> Result<()> { + let script_dir = path.parent().expect("Script path has no parent"); + for data in &mut script.data { + for values in &mut data.data { + match values { + ast::DataValues::File { + ref path, + ref mut data, + } => { + let mut full_path = script_dir.to_path_buf(); + full_path.push(path); + File::open(&full_path) + .map_err(|e| { + anyhow!("Failed to load data from {}: {}", full_path.display(), e) + })? + .read_to_end(data)?; + } + _ => (), + } + } + } + + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index 513a3ce..10437ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,22 +8,26 @@ mod emit; mod intrinsics; mod parser; mod typecheck; +mod includes; type Span = std::ops::Range; pub fn compile_file>(path: P) -> Result> { + let path = path.as_ref(); let mut input = String::new(); File::open(path)?.read_to_string(&mut input)?; - compile_str(&input) + compile_str(&input, path) } -pub fn compile_str(input: &str) -> Result> { +pub fn compile_str(input: &str, path: &Path) -> Result> { let mut script = match parser::parse(&input) { Ok(script) => script, Err(_) => bail!("Parse failed"), }; + includes::resolve_includes(&mut script, path)?; + constfold::fold_script(&mut script); if let Err(_) = typecheck::tc_script(&mut script, &input) { bail!("Type check failed"); diff --git a/src/parser.rs b/src/parser.rs index b95fc94..b88ed1d 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -863,13 +863,25 @@ fn script_parser() -> impl Parser> + C ) .map(|(type_, values)| ast::DataValues::Array { type_, values }); - let data_string = string.map(|s| ast::DataValues::String(s)); + let data_string = string.clone().map(|s| ast::DataValues::String(s)); + + let data_file = just(Token::Ident("file".to_string())) + .ignore_then( + string + .clone() + .delimited_by(Token::Ctrl('('), Token::Ctrl(')')), + ) + .map(|s| ast::DataValues::File { + path: s.into(), + data: vec![], + }); let data = just(Token::Ident("data".to_string())) .ignore_then(expression.clone()) .then( data_i8 .or(data_string) + .or(data_file) .repeated() .delimited_by(Token::Ctrl('{'), Token::Ctrl('}')), ) diff --git a/src/typecheck.rs b/src/typecheck.rs index 61917f4..0df2c54 100644 --- a/src/typecheck.rs +++ b/src/typecheck.rs @@ -215,7 +215,7 @@ pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> { } } } - ast::DataValues::String(_) => (), + ast::DataValues::String(_) | ast::DataValues::File { .. } => (), } } }