From 39951a59e95857145c9f935285488c8d215621d0 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 15 Nov 2023 17:55:07 +0100 Subject: [PATCH] add #include --- mers/src/main.rs | 30 +++- mers_lib/src/info/mod.rs | 6 + mers_lib/src/parsing/mod.rs | 4 +- mers_lib/src/parsing/statements.rs | 165 ++++++++++++++++---- mers_lib/src/program/parsed/assign_to.rs | 7 +- mers_lib/src/program/parsed/block.rs | 7 +- mers_lib/src/program/parsed/chain.rs | 4 +- mers_lib/src/program/parsed/function.rs | 4 +- mers_lib/src/program/parsed/if.rs | 7 +- mers_lib/src/program/parsed/include_mers.rs | 60 +++++++ mers_lib/src/program/parsed/init_to.rs | 4 +- mers_lib/src/program/parsed/mod.rs | 11 +- mers_lib/src/program/parsed/tuple.rs | 7 +- mers_lib/src/program/parsed/value.rs | 4 +- mers_lib/src/program/parsed/variable.rs | 11 +- mers_lib/src/program/run/mod.rs | 12 ++ 16 files changed, 283 insertions(+), 60 deletions(-) create mode 100644 mers_lib/src/program/parsed/include_mers.rs diff --git a/mers/src/main.rs b/mers/src/main.rs index 03b58fb..2c9e39f 100755 --- a/mers/src/main.rs +++ b/mers/src/main.rs @@ -1,6 +1,6 @@ use clap::{Parser, Subcommand, ValueEnum}; use mers_lib::prelude_compile::*; -use std::{fmt::Display, fs, path::PathBuf}; +use std::{fmt::Display, fs, path::PathBuf, process::exit}; mod cfg_globals; @@ -67,17 +67,33 @@ fn main() { let (mut info_parsed, mut info_run, mut info_check) = config.infos(); let mut source = match args.command { Command::Run { file } => { - let str = fs::read_to_string(file).unwrap(); + let str = match fs::read_to_string(&file) { + Ok(s) => s, + Err(e) => { + eprintln!("Can't read file {file:?}: {e}"); + exit(10); + } + }; Source::new(str) } Command::Exec { source } => Source::new(source), }; - let parsed = parse(&mut source).unwrap(); + let parsed = match parse(&mut source) { + Ok(v) => v, + Err(e) => { + eprintln!("{}", e.display(&source)); + exit(20); + } + }; #[cfg(debug_assertions)] dbg!(&parsed); - let run = parsed - .compile(&mut info_parsed, Default::default()) - .unwrap(); + let run = match parsed.compile(&mut info_parsed, Default::default()) { + Ok(v) => v, + Err(e) => { + eprintln!("{}", e.display(&source)); + exit(24); + } + }; #[cfg(debug_assertions)] dbg!(&run); match args.check { @@ -89,7 +105,7 @@ fn main() { Ok(v) => v, Err(e) => { eprint!("{}", e.display(&source)); - std::process::exit(36); + exit(28); } }; if args.check == Check::Yes { diff --git a/mers_lib/src/info/mod.rs b/mers_lib/src/info/mod.rs index feb822c..b7241a5 100755 --- a/mers_lib/src/info/mod.rs +++ b/mers_lib/src/info/mod.rs @@ -23,6 +23,7 @@ pub trait Local: Default + Debug { fn get_var_mut(&mut self, id: &Self::VariableIdentifier) -> Option<&mut Self::VariableData>; // fn add_type(&mut self, id: Self::TypesIdentifier, new_type: Self::TypesType); // fn get_type(&self, id: Self::TypesIdentifier) -> Option<&Self::TypesType>; + fn duplicate(&self) -> Self; } impl Info { @@ -47,6 +48,11 @@ impl Local for Info { fn get_var_mut(&mut self, id: &Self::VariableIdentifier) -> Option<&mut Self::VariableData> { self.scopes.iter_mut().find_map(|l| l.get_var_mut(id)) } + fn duplicate(&self) -> Self { + Self { + scopes: self.scopes.iter().map(|v| v.duplicate()).collect(), + } + } } impl Default for Info { diff --git a/mers_lib/src/parsing/mod.rs b/mers_lib/src/parsing/mod.rs index e737428..be92b1d 100755 --- a/mers_lib/src/parsing/mod.rs +++ b/mers_lib/src/parsing/mod.rs @@ -1,12 +1,12 @@ use std::sync::Arc; -use crate::program::{self, parsed::block::Block}; +use crate::program::{self, parsed::block::Block, run::CheckError}; pub mod errors; pub mod statements; pub mod types; -pub fn parse(src: &mut Source) -> Result, ()> { +pub fn parse(src: &mut Source) -> Result, CheckError> { let pos_in_src = src.get_pos(); let statements = statements::parse_multiple(src, "")?; let block = Block { diff --git a/mers_lib/src/parsing/statements.rs b/mers_lib/src/parsing/statements.rs index 58bd272..7b23943 100755 --- a/mers_lib/src/parsing/statements.rs +++ b/mers_lib/src/parsing/statements.rs @@ -1,10 +1,16 @@ -use super::Source; +use std::fs; + +use colored::{Color, Colorize}; + +use super::{Source, SourcePos}; use crate::{ data::Data, - program::{self, parsed::MersStatement}, + program::{self, parsed::MersStatement, run::CheckError}, }; -pub fn parse(src: &mut Source) -> Result>, ()> { +pub fn parse( + src: &mut Source, +) -> Result>, CheckError> { src.section_begin("statement".to_string()); let mut first = if let Some(s) = parse_no_chain(src)? { s @@ -16,7 +22,14 @@ pub fn parse(src: &mut Source) -> Result { let pos_in_src = src.get_pos(); src.next_word(); - let source = parse(src)?.expect("todo"); + let source = parse(src)?.ok_or_else(|| { + CheckError::new() + .src(vec![( + (pos_in_src, src.get_pos()).into(), + Some(Color::BrightBlack), + )]) + .msg(format!("EOF after =")) + })?; first = Box::new(program::parsed::assign_to::AssignTo { pos_in_src: (pos_in_src, src.get_pos()).into(), target: first, @@ -73,7 +93,10 @@ pub fn parse(src: &mut Source) -> Result Result>, ()> { +pub fn parse_multiple( + src: &mut Source, + end: &str, +) -> Result>, CheckError> { src.section_begin("block".to_string()); let mut statements = vec![]; loop { @@ -92,10 +115,68 @@ pub fn parse_multiple(src: &mut Source, end: &str) -> Result Result>, ()> { +) -> Result>, CheckError> { src.section_begin("statement no chain".to_string()); src.skip_whitespace(); match src.peek_char() { + Some('#') => { + let pos_in_src = src.get_pos(); + src.next_char(); + if src.peek_char().is_none() { + return Err(CheckError::new() + .src(vec![((pos_in_src, src.get_pos()).into(), Some(Color::Red))]) + .msg(format!("EOF after #"))); + } + if src.peek_char().is_some_and(|ch| ch.is_whitespace()) { + src.skip_whitespace(); + return Err(CheckError::new() + .src(vec![((pos_in_src, src.get_pos()).into(), Some(Color::Red))]) + .msg(format!("Whitespace after #"))); + } + match src.next_word() { + "include" => { + let end_in_src = src.get_pos(); + src.skip_whitespace(); + let string_in_src = src.get_pos(); + if src.next_char() == Some('"') { + let s = parse_string(src, string_in_src)?; + match fs::read_to_string(&s) { + Ok(s) => { + return Ok(Some(Box::new( + program::parsed::include_mers::IncludeMers { + pos_in_src: (pos_in_src, src.get_pos()).into(), + include: super::parse(&mut Source::new(s))?, + }, + ))); + } + Err(e) => { + return Err(CheckError::new() + .src(vec![ + ((pos_in_src, end_in_src).into(), None), + ((string_in_src, src.get_pos()).into(), Some(Color::Red)), + ]) + .msg(format!("Can't load file '{s}': {e}"))); + } + } + } else { + return Err(CheckError::new() + .src(vec![ + ((pos_in_src, end_in_src).into(), Some(Color::BrightBlack)), + ((string_in_src, src.get_pos()).into(), Some(Color::Red)), + ]) + .msg(format!( + "#include must be followed by a string literal like \"file.mers\" (\" expected)." + ))); + } + } + other => { + let msg = format!("Unknown #statement: {other}"); + return Err(CheckError::new() + .src(vec![((pos_in_src, src.get_pos()).into(), Some(Color::Red))]) + .msg(msg)); + } + } + } Some('{') => { let pos_in_src = src.get_pos(); src.next_char(); @@ -118,28 +199,7 @@ pub fn parse_no_chain( src.section_begin("string literal".to_string()); let pos_in_src = src.get_pos(); src.next_char(); - let mut s = String::new(); - loop { - if let Some(ch) = src.next_char() { - if ch == '\\' { - s.push(match src.next_char() { - Some('\\') => '\\', - Some('r') => '\r', - Some('n') => '\n', - Some('t') => '\t', - Some('"') => '"', - Some(o) => todo!("err: unknown backslash escape '\\{o}'"), - None => todo!("err: eof in backslash escape"), - }); - } else if ch == '"' { - break; - } else { - s.push(ch); - } - } else { - todo!("err: eof in string") - } - } + let s = parse_string(src, pos_in_src)?; return Ok(Some(Box::new(program::parsed::value::Value { pos_in_src: (pos_in_src, src.get_pos()).into(), data: Data::new(crate::data::string::String(s)), @@ -222,3 +282,50 @@ pub fn parse_no_chain( } })) } + +/// expects to be called *after* a " character is consumed from src +pub fn parse_string(src: &mut Source, double_quote: SourcePos) -> Result { + let mut s = String::new(); + loop { + if let Some(ch) = src.next_char() { + if ch == '\\' { + let backslash_in_src = src.get_pos(); + s.push(match src.next_char() { + Some('\\') => '\\', + Some('r') => '\r', + Some('n') => '\n', + Some('t') => '\t', + Some('"') => '"', + Some(o) => { + return Err(CheckError::new() + .src(vec![( + (backslash_in_src, src.get_pos()).into(), + Some(Color::Red), + )]) + .msg(format!("unknown backslash escape '\\{o}'"))); + } + None => { + return Err(CheckError::new() + .src(vec![( + (backslash_in_src, src.get_pos()).into(), + Some(Color::Red), + )]) + .msg(format!("EOF in backslash escape"))); + } + }); + } else if ch == '"' { + break; + } else { + s.push(ch); + } + } else { + return Err(CheckError::new() + .src(vec![( + (double_quote, src.get_pos()).into(), + Some(Color::BrightBlack), + )]) + .msg(format!("EOF in string literal"))); + } + } + Ok(s) +} diff --git a/mers_lib/src/program/parsed/assign_to.rs b/mers_lib/src/program/parsed/assign_to.rs index a09abef..d9c45f4 100755 --- a/mers_lib/src/program/parsed/assign_to.rs +++ b/mers_lib/src/program/parsed/assign_to.rs @@ -1,4 +1,7 @@ -use crate::program::{self, run::SourceRange}; +use crate::program::{ + self, + run::{CheckError, SourceRange}, +}; use super::{CompInfo, MersStatement}; @@ -17,7 +20,7 @@ impl MersStatement for AssignTo { &self, info: &mut crate::info::Info, comp: CompInfo, - ) -> Result, String> { + ) -> Result, CheckError> { Ok(Box::new(program::run::assign_to::AssignTo { pos_in_src: self.pos_in_src, is_init: false, diff --git a/mers_lib/src/program/parsed/block.rs b/mers_lib/src/program/parsed/block.rs index 2ad39b2..282b9f8 100755 --- a/mers_lib/src/program/parsed/block.rs +++ b/mers_lib/src/program/parsed/block.rs @@ -1,6 +1,9 @@ use crate::{ info, - program::{self, run::SourceRange}, + program::{ + self, + run::{CheckError, SourceRange}, + }, }; use super::{CompInfo, MersStatement}; @@ -18,7 +21,7 @@ impl MersStatement for Block { &self, info: &mut info::Info, comp: CompInfo, - ) -> Result, String> { + ) -> Result, CheckError> { Ok(Box::new(program::run::block::Block { pos_in_src: self.pos_in_src, statements: self diff --git a/mers_lib/src/program/parsed/chain.rs b/mers_lib/src/program/parsed/chain.rs index c4c290e..34243e4 100755 --- a/mers_lib/src/program/parsed/chain.rs +++ b/mers_lib/src/program/parsed/chain.rs @@ -1,4 +1,4 @@ -use crate::program::run::SourceRange; +use crate::program::run::{CheckError, SourceRange}; use crate::{info, program}; use super::{CompInfo, MersStatement}; @@ -17,7 +17,7 @@ impl MersStatement for Chain { &self, info: &mut info::Info, comp: CompInfo, - ) -> Result, String> { + ) -> Result, CheckError> { Ok(Box::new(program::run::chain::Chain { pos_in_src: self.pos_in_src, first: self.first.compile(info, comp)?, diff --git a/mers_lib/src/program/parsed/function.rs b/mers_lib/src/program/parsed/function.rs index 4a77656..eaf4b54 100755 --- a/mers_lib/src/program/parsed/function.rs +++ b/mers_lib/src/program/parsed/function.rs @@ -1,4 +1,4 @@ -use crate::program::run::SourceRange; +use crate::program::run::{CheckError, SourceRange}; use std::sync::{Arc, Mutex}; use crate::{ @@ -24,7 +24,7 @@ impl MersStatement for Function { &self, info: &mut crate::info::Info, mut comp: CompInfo, - ) -> Result, String> { + ) -> Result, CheckError> { comp.is_init = true; let arg_target = Arc::new(self.arg.compile(info, comp)?); comp.is_init = false; diff --git a/mers_lib/src/program/parsed/if.rs b/mers_lib/src/program/parsed/if.rs index dcfd52e..bbbde1a 100755 --- a/mers_lib/src/program/parsed/if.rs +++ b/mers_lib/src/program/parsed/if.rs @@ -1,4 +1,7 @@ -use crate::program::{self, run::SourceRange}; +use crate::program::{ + self, + run::{CheckError, SourceRange}, +}; use super::{CompInfo, MersStatement}; @@ -18,7 +21,7 @@ impl MersStatement for If { &self, info: &mut crate::info::Info, comp: CompInfo, - ) -> Result, String> { + ) -> Result, CheckError> { Ok(Box::new(program::run::r#if::If { pos_in_src: self.pos_in_src, condition: self.condition.compile(info, comp)?, diff --git a/mers_lib/src/program/parsed/include_mers.rs b/mers_lib/src/program/parsed/include_mers.rs new file mode 100644 index 0000000..52e8c61 --- /dev/null +++ b/mers_lib/src/program/parsed/include_mers.rs @@ -0,0 +1,60 @@ +use std::sync::{Arc, Mutex}; + +use colored::Colorize; + +use crate::{ + data::{self, Data}, + info::{self, Local}, + program::{ + self, + run::{CheckError, SourceRange}, + }, +}; + +use super::{CompInfo, MersStatement}; + +#[derive(Debug)] +pub struct IncludeMers { + pub pos_in_src: SourceRange, + pub include: Box, +} +impl MersStatement for IncludeMers { + fn has_scope(&self) -> bool { + true + } + fn compile_custom( + &self, + info: &mut info::Info, + comp: CompInfo, + ) -> Result, CheckError> { + let compiled = match self.include.compile(info, comp) { + Ok(v) => Arc::new(v), + Err(e) => { + return Err(CheckError::new() + .src(vec![(self.pos_in_src, Some(colored::Color::Red))]) + .msg("Error in inner mers statement! (note: inner errors may refer to a different file)".red().to_string()) + .err(e)) + } + }; + let compiled2 = Arc::clone(&compiled); + Ok(Box::new(program::run::chain::Chain { + pos_in_src: self.pos_in_src, + first: Box::new(program::run::value::Value { + pos_in_src: self.pos_in_src, + val: Data::empty_tuple(), + }), + chained: Box::new(program::run::function::Function { + pos_in_src: self.pos_in_src, + func_no_info: data::function::Function { + info: Arc::new(info::Info::neverused()), + info_check: Arc::new(Mutex::new(info::Info::neverused())), + out: Arc::new(move |_, i| compiled.check(&mut i.duplicate(), None)), + run: Arc::new(move |_, i| compiled2.run(&mut i.duplicate())), + }, + }), + })) + } + fn source_range(&self) -> SourceRange { + self.pos_in_src + } +} diff --git a/mers_lib/src/program/parsed/init_to.rs b/mers_lib/src/program/parsed/init_to.rs index ac74432..890c969 100755 --- a/mers_lib/src/program/parsed/init_to.rs +++ b/mers_lib/src/program/parsed/init_to.rs @@ -1,5 +1,5 @@ -use crate::program; use crate::program::run::SourceRange; +use crate::program::{self, run::CheckError}; use super::{CompInfo, MersStatement}; @@ -18,7 +18,7 @@ impl MersStatement for InitTo { &self, info: &mut crate::info::Info, mut comp: CompInfo, - ) -> Result, String> { + ) -> Result, CheckError> { comp.is_init = true; let target = self.target.compile(info, comp)?; comp.is_init = false; diff --git a/mers_lib/src/program/parsed/mod.rs b/mers_lib/src/program/parsed/mod.rs index e62d055..a706722 100755 --- a/mers_lib/src/program/parsed/mod.rs +++ b/mers_lib/src/program/parsed/mod.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, fmt::Debug}; use crate::info; -use super::run::SourceRange; +use super::run::{CheckError, SourceRange}; #[cfg(feature = "parse")] pub mod assign_to; @@ -15,6 +15,8 @@ pub mod function; #[cfg(feature = "parse")] pub mod r#if; #[cfg(feature = "parse")] +pub mod include_mers; +#[cfg(feature = "parse")] pub mod init_to; #[cfg(feature = "parse")] pub mod tuple; @@ -29,12 +31,12 @@ pub trait MersStatement: Debug + Send + Sync { &self, info: &mut Info, comp: CompInfo, - ) -> Result, String>; + ) -> Result, CheckError>; fn compile( &self, info: &mut Info, comp: CompInfo, - ) -> Result, String> { + ) -> Result, CheckError> { if self.has_scope() { info.create_scope(); } @@ -77,4 +79,7 @@ impl info::Local for Local { fn get_var_mut(&mut self, id: &Self::VariableIdentifier) -> Option<&mut Self::VariableData> { self.vars.get_mut(id) } + fn duplicate(&self) -> Self { + self.clone() + } } diff --git a/mers_lib/src/program/parsed/tuple.rs b/mers_lib/src/program/parsed/tuple.rs index 7cdf17f..591ceb3 100755 --- a/mers_lib/src/program/parsed/tuple.rs +++ b/mers_lib/src/program/parsed/tuple.rs @@ -1,6 +1,9 @@ use crate::{ info, - program::{self, run::SourceRange}, + program::{ + self, + run::{CheckError, SourceRange}, + }, }; use super::{CompInfo, MersStatement}; @@ -18,7 +21,7 @@ impl MersStatement for Tuple { &self, info: &mut info::Info, comp: CompInfo, - ) -> Result, String> { + ) -> Result, CheckError> { Ok(Box::new(program::run::tuple::Tuple { pos_in_src: self.pos_in_src, elems: self diff --git a/mers_lib/src/program/parsed/value.rs b/mers_lib/src/program/parsed/value.rs index f20033e..2edf10e 100755 --- a/mers_lib/src/program/parsed/value.rs +++ b/mers_lib/src/program/parsed/value.rs @@ -1,4 +1,4 @@ -use crate::program::run::SourceRange; +use crate::program::run::{CheckError, SourceRange}; use crate::{data::Data, program}; use super::{CompInfo, MersStatement}; @@ -17,7 +17,7 @@ impl MersStatement for Value { &self, _info: &mut crate::info::Info, _comp: CompInfo, - ) -> Result, String> { + ) -> Result, CheckError> { Ok(Box::new(program::run::value::Value { pos_in_src: self.pos_in_src, val: self.data.clone(), diff --git a/mers_lib/src/program/parsed/variable.rs b/mers_lib/src/program/parsed/variable.rs index f1fcfd3..1ceea16 100755 --- a/mers_lib/src/program/parsed/variable.rs +++ b/mers_lib/src/program/parsed/variable.rs @@ -1,6 +1,9 @@ use crate::{ info::Local, - program::{self, run::SourceRange}, + program::{ + self, + run::{CheckError, SourceRange}, + }, }; use super::{CompInfo, MersStatement}; @@ -20,7 +23,7 @@ impl MersStatement for Variable { &self, info: &mut crate::info::Info, comp: CompInfo, - ) -> Result, String> { + ) -> Result, CheckError> { if comp.is_init { info.init_var( self.var.clone(), @@ -37,7 +40,9 @@ impl MersStatement for Variable { var: if let Some(v) = info.get_var(&self.var) { *v } else { - return Err(format!("No variable named '{}' found!", self.var)); + return Err(CheckError::new() + .src(vec![(self.pos_in_src, Some(colored::Color::Red))]) + .msg(format!("No variable named '{}' found!", self.var))); }, })) } diff --git a/mers_lib/src/program/run/mod.rs b/mers_lib/src/program/run/mod.rs index e82c894..4994395 100755 --- a/mers_lib/src/program/run/mod.rs +++ b/mers_lib/src/program/run/mod.rs @@ -268,6 +268,15 @@ impl info::Local for Local { None => None, } } + fn duplicate(&self) -> Self { + Self { + vars: self + .vars + .iter() + .map(|v| Arc::new(RwLock::new(v.read().unwrap().clone()))) + .collect(), + } + } } impl info::Local for CheckLocal { type VariableIdentifier = usize; @@ -290,4 +299,7 @@ impl info::Local for CheckLocal { None => None, } } + fn duplicate(&self) -> Self { + self.clone() + } }