diff --git a/Cargo.toml b/Cargo.toml index 2dd13bf..f947f10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -docopt = "1.1.1" phf = { version = "0.11.1", features = ["macros"] } serde = { version = "1.0.152", features = ["derive"] } serde_json = "1.0.94" diff --git a/README.md b/README.md index 10eca93..be5dd8e 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,3 @@ A compiler/tooling project for the Proto programming language. This project is a The language description can be found [here](docs/language_desc.md).
- -# Tools - -This project will come with a few tools: - -1. [prc](src/prc/README.md), the proto compiler which can be called standalone. - -2. [proto](src/proto/README.md), the toolkit for the Proto programming language. - -3. [proton](src/proton/README.md), the virtual machine for proto. diff --git a/docs/language_desc.md b/docs/language_desc.md index e0b6ec8..7e192c3 100644 --- a/docs/language_desc.md +++ b/docs/language_desc.md @@ -1,6 +1,6 @@ # Proto Language Description (v0.0.1) -Proto is a typed programming language. The long term goal is to compile to native code through LLVM. To support the REPL, it will have a Virtual Machine written in Rust ([_proton_](../src/proton/README.md)). This VM could eventually power compile time expressions. +Proto is a typed programming language. The long term goal is to compile to native code through LLVM. To support the REPL, it will have a Virtual Machine written in Rust. This VM could eventually power compile time expressions. Instead of dwelling in the future, let us describe what the language will look like. This document will get updated as more language features get added in future versions. diff --git a/src/analysis_a/dependency_res.rs b/src/analysis_a/dependency_res.rs new file mode 100644 index 0000000..aca4236 --- /dev/null +++ b/src/analysis_a/dependency_res.rs @@ -0,0 +1,206 @@ +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; + +use crate::{ + frontend::{ + ast::{DependencyPath, PathAction}, + source::SourceRef, + }, + pir::ir::{ExprRef, InsRef, KeyValueBindings, PIRIns, PIRModule, PIRModulePass}, +}; + +#[derive(Clone)] +#[allow(dead_code)] +pub struct DependencyResolvr<'a> { + module: Option<&'a PIRModule>, + file_path: String, + resolution_errors: Vec<(String, SourceRef)>, +} + +// expect to first process the module and get a Vec as the result +// we can then use these indices to get the corresponding ins from the module +// for each of these dependencies, we will: +// - process the actions until we get to a file path. The remainder of the actions will +// be the path to the dependency. +impl<'a> DependencyResolvr<'a> { + pub fn resolve(&mut self, dependencies: Vec) { + let module = self.get_module(); + let ins_pool = &module.ins_pool.pool; + + // for each dependency, we will: + // - get the Instruction (UseDependency) + // - each UseDependency has a list of DependencyPaths: + // - each of the DependencyPaths has a list of actions which will be used to get + // the path + // - we will process the actions (while updating depenedency path) until we: + // * get to a file path. Stop. + // * get to a folder. Continue. + // * path is invalid. Error. + // - once we have a valid file path, whatever is left of the actions + // will be the path to the dependency + // - cluster the dependencies by their paths and return that structure + + let mut res: HashMap> = HashMap::new(); + + for index in dependencies { + let dep = ins_pool.get(index).unwrap(); + if let PIRIns::UseDependency { paths, src: _ } = dep { + for path in paths { + let resolved_res = self.evaluate_dependency_path(path); + match resolved_res { + Ok((res_path, rem_actions)) => { + // add to res + let res_vec = res.entry(index).or_insert(Vec::new()); + res_vec.push((res_path, rem_actions)); + } + Err(msg) => { + self.resolution_errors.push((msg, path.source_ref())); + } + } + } + } + } + + // show contents of res array + for (index, m) in res { + let dep = ins_pool.get(index).unwrap(); + println!("{} resolves to the following path:", dep.as_str()); + for (path, actions) in m { + println!("\t{}: {}", path.display(), actions.as_str()); + } + } + } + + fn evaluate_dependency_path( + &mut self, + dep: &DependencyPath, + ) -> Result<(PathBuf, DependencyPath), String> { + let mut start_dir = PathBuf::from(&self.file_path.clone()); + start_dir.pop(); + let mut search_dep_path = start_dir; + let mut skip_to = 0; + + for action in &dep.actions { + match action { + PathAction::ToParentDir(_) => { + search_dep_path.pop(); + } + PathAction::ImportAll(_) => { + break; + } + PathAction::SearchFor(path_section) => { + // append path_section to search_dep_path + search_dep_path.push(path_section.as_str()); + } + PathAction::SearchCoreModulesFor(_) => todo!(), + PathAction::SearchProjectRootFor(_) => todo!(), + PathAction::SearchCurrentFileFor(_) => { + search_dep_path = PathBuf::from(&self.file_path); + search_dep_path.set_extension("pr"); + break; + } + PathAction::NameLastItemAs(_) => todo!(), + } + + // check that updated path exists + let (path_exists, is_file, is_dir) = self.path_exists(&search_dep_path); + if !path_exists { + // report error + let msg = format!("Unable to resolve dependency path: {}", dep.as_str()); + return Err(msg); + } + + // update skip_to by 1, with a max of actions.len() + skip_to += 1; + if skip_to >= dep.actions.len() { + break; + } + + if is_dir { + // continue + continue; + } + + if is_file { + // stop + break; + } else { + } + } + + // we have to verify that the path is a file + let (path_exists, is_file, _) = self.path_exists(&search_dep_path); + if !path_exists { + // report error + let msg = format!("Unable to resolve dependency path: {}", dep.as_str()); + return Err(msg); + } + + if !is_file { + // report error + let msg = format!("Dependency path must contain a file: {}", dep.as_str()); + return Err(msg); + } + + // we have a valid file path + // we need to collect the remainder of the actions into a Vector of PathActions + let mut remainder = Vec::new(); + for action in &dep.actions[skip_to..] { + remainder.push(action.clone()); + } + let remainder = DependencyPath { actions: remainder }; + Ok((search_dep_path, remainder)) + } + + // checks if path exists and also if it is a file + fn path_exists(&self, path: &PathBuf) -> (bool, bool, bool) { + let mut file_path = path.clone(); + file_path.set_extension("pr"); + let file_path = Path::new(&file_path); + let file_exists = file_path.exists(); + let dir_exists = path.exists(); + (dir_exists || file_exists, file_exists, dir_exists) + } +} + +impl<'a> PIRModulePass<'a, (), (), (), Vec, ()> for DependencyResolvr<'a> { + fn process_ins(&mut self, _: &InsRef) -> Result<(), ()> { + Ok(()) + } + + fn process_expr(&mut self, _: &ExprRef) -> Result<(), ()> { + Ok(()) + } + + fn process_pairs(&mut self, _: &KeyValueBindings) -> Result<(), ()> { + Ok(()) + } + + fn process(&mut self) -> Result, ()> { + let module = self.get_module(); + let ins_pool = &module.ins_pool.pool; + let mut dependencies = Vec::new(); + + for (index, ins) in ins_pool.iter().enumerate() { + if matches!(ins, PIRIns::UseDependency { paths: _, src: _ }) { + dependencies.push(index); + } + } + + Ok(dependencies) + } + + fn new(module: &'a PIRModule) -> Self { + Self { + module: Some(module), + file_path: module.path.clone(), + resolution_errors: Vec::new(), + } + } + + fn get_module(&mut self) -> &'a PIRModule { + self.module.unwrap() + } +} diff --git a/src/analysis_a/mod.rs b/src/analysis_a/mod.rs new file mode 100644 index 0000000..bffc631 --- /dev/null +++ b/src/analysis_a/mod.rs @@ -0,0 +1 @@ +pub mod dependency_res; diff --git a/src/frontend/ast.rs b/src/frontend/ast.rs index d16a931..79ea3f5 100644 --- a/src/frontend/ast.rs +++ b/src/frontend/ast.rs @@ -208,7 +208,6 @@ pub enum PathAction { SearchFor(Expr), SearchCoreModulesFor(Expr), SearchProjectRootFor(Expr), - SearchLastModuleFor(Expr), SearchCurrentFileFor(Expr), NameLastItemAs(Expr), } @@ -222,7 +221,6 @@ impl PathAction { PathAction::SearchFor(_) => true, PathAction::SearchCoreModulesFor(_) => true, PathAction::SearchProjectRootFor(_) => true, - PathAction::SearchLastModuleFor(_) => true, PathAction::SearchCurrentFileFor(_) => true, PathAction::ImportAll(_) => false, } @@ -235,7 +233,6 @@ impl PathAction { PathAction::NameLastItemAs(_) => true, PathAction::SearchCoreModulesFor(_) => true, PathAction::SearchProjectRootFor(_) => true, - PathAction::SearchLastModuleFor(_) => true, PathAction::SearchCurrentFileFor(_) => false, PathAction::ImportAll(_) => true, } @@ -248,7 +245,6 @@ impl PathAction { PathAction::NameLastItemAs(_) => false, PathAction::SearchCoreModulesFor(_) => true, PathAction::SearchProjectRootFor(_) => true, - PathAction::SearchLastModuleFor(_) => true, PathAction::SearchCurrentFileFor(_) => false, PathAction::ImportAll(_) => false, } @@ -261,7 +257,6 @@ impl PathAction { PathAction::NameLastItemAs(alias) => format!(" as {}", alias.as_str()), PathAction::SearchCoreModulesFor(e) => format!("@{}", e.as_str()), PathAction::SearchProjectRootFor(m) => format!("${}", m.as_str()), - PathAction::SearchLastModuleFor(n) => n.as_str(), PathAction::SearchCurrentFileFor(inner_m) => format!("!{}", inner_m.as_str()), PathAction::ImportAll(_) => "*".to_string(), } @@ -273,7 +268,6 @@ impl PathAction { PathAction::SearchFor(e) => e.source_ref(), PathAction::SearchCoreModulesFor(e) => e.source_ref(), PathAction::SearchProjectRootFor(e) => e.source_ref(), - PathAction::SearchLastModuleFor(e) => e.source_ref(), PathAction::SearchCurrentFileFor(e) => e.source_ref(), PathAction::NameLastItemAs(e) => e.source_ref(), PathAction::ImportAll(src) => src.clone(), @@ -303,6 +297,17 @@ impl DependencyPath { let last_action = self.actions.last().unwrap(); first_action.source_ref().combine(last_action.source_ref()) } + + pub fn as_str(&self) -> String { + let mut s = String::new(); + for (index, action) in self.actions.iter().enumerate() { + s.push_str(&action.as_str()); + if index < self.actions.len() - 1 { + s.push_str("::"); + } + } + s + } } #[derive(Debug, Clone)] diff --git a/src/frontend/source.rs b/src/frontend/source.rs index 772361b..c24e490 100644 --- a/src/frontend/source.rs +++ b/src/frontend/source.rs @@ -392,6 +392,11 @@ impl SourceReporter { println!("{}", pastel(&output)); } + pub fn show_error(&self, msg: String) { + let output = format!("*[_, *, l_red:d_black]Error:[/] *[*, l_white:d_black]{msg}[/]"); + println!("{}", pastel(&output)); + } + fn report_with_ref(&self, src: &SourceRef, msg: String, tip: Option) { let err_col = "d_red"; let target_col = "l_magenta"; diff --git a/src/ir8/mod.rs b/src/ir8/mod.rs deleted file mode 100644 index 75c4997..0000000 --- a/src/ir8/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod codeviewer; -pub mod lowir; -pub mod tomato; -pub mod visitir; diff --git a/src/ir8/tomato.rs b/src/ir8/tomato.rs deleted file mode 100644 index a8b0765..0000000 --- a/src/ir8/tomato.rs +++ /dev/null @@ -1,36 +0,0 @@ -use super::codeviewer::CodeViewer; -#[allow(unused_imports)] -use super::lowir::{ExprPool, InsPool, InsRef, KeyValueBindings, LowIRModule}; -#[allow(unused_imports)] -use super::visitir::apply_to_module; -use std::fs; -use std::io::Error; - -#[allow(dead_code)] -pub struct Tomato { - pub filepath: String, - viewer: CodeViewer, -} - -#[allow(dead_code)] -impl Tomato { - pub fn new(filename: String, ins_pool: InsPool, expr_pool: ExprPool) -> Tomato { - Tomato { - filepath: filename, - viewer: CodeViewer::new(ins_pool, expr_pool), - } - } - - pub fn format(&mut self, ir_module: &LowIRModule) -> Result<(), Error> { - let res = apply_to_module(&mut self.viewer, &ir_module); - let res = self.viewer.unwrap(res); - - let contents = res.join("\n"); - self.update_file_contents(contents) - } - - fn update_file_contents(&self, contents: String) -> Result<(), Error> { - // open file - fs::write(&self.filepath, contents) - } -} diff --git a/src/ir8/visitir.rs b/src/ir8/visitir.rs deleted file mode 100644 index 6335f01..0000000 --- a/src/ir8/visitir.rs +++ /dev/null @@ -1,30 +0,0 @@ -use super::lowir::{ExprRef, InsRef, KeyValueBindings, LowIRModule}; - -#[allow(dead_code)] -pub enum VisitIRError {} - -#[allow(dead_code)] -pub trait VisitsLowIR { - fn visit_ins(&mut self, ins: &InsRef) -> Result; - fn visit_expr(&mut self, expr: &ExprRef) -> Result; - fn visit_pairs(&mut self, kv: &KeyValueBindings) -> Result; -} - -pub fn apply_visitor>( - visitor: &mut V, - ins: &InsRef, -) -> Result { - visitor.visit_ins(ins) -} - -pub fn apply_to_module>( - visitor: &mut V, - module: &LowIRModule, -) -> Vec> { - let mut result = vec![]; - for ins_ref in module.top_level.iter() { - let res = apply_visitor(visitor, &ins_ref); - result.push(res); - } - result -} diff --git a/src/main.rs b/src/main.rs index 8eb364a..311da50 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,55 +1,166 @@ use std::{env, fs, path::PathBuf}; -use docopt::Docopt; +use analysis_a::dependency_res::DependencyResolvr; use frontend::{ lexer::Lexer, parser::Parser, source::{SourceFile, SourceReporter}, token::Token, }; -use ir8::{lowir::LowIRModule, tomato::Tomato}; -use serde::Deserialize; +use pir::ir::{PIRModule, PIRModulePass}; +// use crate::tools::pfmt::Pfmt; + +mod analysis_a; mod frontend; -mod ir8; mod pastel; +mod pir; +mod tools; const USAGE: &str = " -Usage: proto (-h | -l | -p) -f +Usage: proto command [options]? -Options: - -h, --help Show this message. - -l Run lexer and show its output. - -p Run parser and show its output. - -f File to be processed. +commands: + - c | compile [configurations]? + - [configurations]: + (*) backends: + - pir (default): use PIR backend. + - cpp: use C++ backend. + (*) stages: + - lex: stop after lexing. + - parse: stop after parsing. + - fmt (default): stop after formatting. + (*) flags: + - nofmt: do not format the file. Default: false. + - dbg: show debug info. Default: false. + - help: show this help message. Default: false. + - h | help: show this help message. "; -#[derive(Debug, Deserialize)] #[allow(dead_code)] -struct Args { - flag_h: bool, - flag_l: bool, - flag_p: bool, - flag_f: String, +enum Backend { + PIR, // will go to PVM + CPP, // will go to C++ } +#[allow(dead_code)] enum Stage { Lexer, Parser, + PfmtFile, + DependencyResolvr, +} + +#[allow(dead_code)] +enum Command { + Compile, +} + +#[allow(dead_code)] +struct ProtoConfig { + cmd: Option, + backend: Backend, + target_file: String, + max_stage: Stage, + show_help: bool, + dbg_info: bool, +} + +fn create_config(args: Vec) -> ProtoConfig { + let mut args = args.iter().skip(1); + + // make sure there is at least 1 more arg + if args.len() < 1 { + return ProtoConfig { + cmd: None, + backend: Backend::PIR, + target_file: "".to_string(), + max_stage: Stage::PfmtFile, + show_help: true, + dbg_info: false, + }; + } + + // expect one of: + // c which will accept a file name and configurations + let command = args.next().unwrap(); + match command.as_str() { + "c" | "compile" => { + // make sure there is at least 1 more arg + // which is the file name + if args.len() < 1 { + return ProtoConfig { + cmd: None, + backend: Backend::PIR, + target_file: "".to_string(), + max_stage: Stage::PfmtFile, + show_help: true, + dbg_info: false, + }; + } + let target_file = args.next().unwrap(); + let mut backend = Backend::PIR; + let mut max_stage = Stage::PfmtFile; + let mut show_help = false; + let mut dbg_info = false; + for arg in args { + match arg.as_str() { + "pir" => backend = Backend::PIR, + "cpp" => backend = Backend::CPP, + "lex" => max_stage = Stage::Lexer, + "parse" => max_stage = Stage::Parser, + "fmt" => max_stage = Stage::PfmtFile, + "dep" => max_stage = Stage::DependencyResolvr, + "dbg" => dbg_info = true, + "help" => show_help = true, + _ => {} + } + } + ProtoConfig { + cmd: Some(Command::Compile), + backend, + target_file: target_file.to_string(), + max_stage, + show_help, + dbg_info, + } + } + "h" | "help" => ProtoConfig { + backend: Backend::PIR, + target_file: "".to_string(), + max_stage: Stage::PfmtFile, + show_help: true, + dbg_info: false, + cmd: None, + }, + _ => ProtoConfig { + backend: Backend::PIR, + target_file: "".to_string(), + max_stage: Stage::PfmtFile, + show_help: true, + dbg_info: false, + cmd: None, + }, + } +} + +fn show_help() { + println!("{}", USAGE); } fn main() { - let args: Args = Docopt::new(USAGE) - .and_then(|d| d.deserialize()) - .unwrap_or_else(|e| e.exit()); + let args = env::args().collect::>(); + let config = create_config(args); - let stage = if args.flag_l { - Stage::Lexer - } else { - Stage::Parser - }; + if config.show_help { + show_help(); + } - let fpath = args.flag_f; + if let None = config.cmd { + return; + } + + let fpath = config.target_file; let cwd = env::current_dir().unwrap(); let path = format!("{}/{}", cwd.display(), fpath); let path = fs::canonicalize(PathBuf::from(path)).unwrap(); @@ -59,7 +170,7 @@ fn main() { let reporter = SourceReporter::new(src.clone()); let mut lexer = Lexer::new(src); - if let Stage::Lexer = stage { + if let Stage::Lexer = config.max_stage { loop { let maybe_tok = lexer.next_token(); match maybe_tok { @@ -71,40 +182,52 @@ fn main() { Err(le) => reporter.report_lexer_error(&le), } } - } else { - let mut parser = Parser::new(lexer); - parser.parse(); + } - if !parser.lexer_errors.is_empty() { - for le in parser.lexer_errors { - reporter.report_lexer_error(&le); - } + let mut parser = Parser::new(lexer); + parser.parse(); + + if !parser.lexer_errors.is_empty() { + for le in parser.lexer_errors { + reporter.report_lexer_error(&le); } - // else { - // reporter.show_info("No errors during lexing.".to_string()); - // } + } - if !parser.parser_errors.is_empty() { - for pe in parser.parser_errors { - reporter.report_parser_error(pe); - } + if config.dbg_info { + reporter.show_info("No errors during lexing.".to_string()); + } + + if !parser.parser_errors.is_empty() { + for pe in parser.parser_errors { + reporter.report_parser_error(pe); } - // else { - // reporter.show_info("No errors during parsing.".to_string()); + } + + if config.dbg_info { + reporter.show_info("No errors during parsing.".to_string()); + } + + let module = parser.compilation_module; + let mut ir_mod = PIRModule::new(module, path); + + if let Stage::PfmtFile = config.max_stage { + // let mut pfmt = Pfmt::new(&mut ir_mod); + // let res = pfmt.process(); + // match res { + // Ok(msg) if config.dbg_info => reporter.show_info(msg), + // Err(e) => reporter.show_error(e), + // _ => {} // } + } - let module = parser.compilation_module; - let mut ir_mod = LowIRModule::new(); - ir_mod.lowir(module); - let mut tomato = Tomato::new( - path.clone(), - ir_mod.ins_pool.clone(), - ir_mod.expr_pool.clone(), - ); - - match tomato.format(&ir_mod) { - Ok(()) => reporter.show_info(format!("formatted {}", path)), - Err(e) => reporter.show_info(format!("failed to format {}: {}", path, e)), + if let Stage::DependencyResolvr = config.max_stage { + let mut dep_resolvr = DependencyResolvr::new(&mut ir_mod); + let res = dep_resolvr.process(); + match res { + Ok(indices) => { + let _ = dep_resolvr.resolve(indices); + } + Err(_) => todo!(), } } } diff --git a/src/ir8/lowir.rs b/src/pir/ir.rs similarity index 70% rename from src/ir8/lowir.rs rename to src/pir/ir.rs index 5652beb..963a3d2 100644 --- a/src/ir8/lowir.rs +++ b/src/pir/ir.rs @@ -7,8 +7,6 @@ use crate::frontend::{ types::Type, }; -// use super::visitir::{VisitIRError, VisitsLowIR}; - #[derive(Debug, Clone)] #[allow(dead_code)] pub struct KeyValueBindings { @@ -42,7 +40,7 @@ impl KeyValueBindings { #[derive(Debug, Clone)] #[allow(dead_code)] -pub enum LowIRExpr { +pub enum PIRExpr { Id(Token, Option), Number(Token, Option), StringLiteral(Token, Option), @@ -79,37 +77,37 @@ pub enum LowIRExpr { } #[allow(dead_code)] -impl LowIRExpr { +impl PIRExpr { pub fn type_info(&self) -> Option { match &self { - LowIRExpr::Id(_, t) => t.clone(), - LowIRExpr::Number(_, t) => t.clone(), - LowIRExpr::Binary(_, _, _, t) => t.clone(), - LowIRExpr::Boolean(_, t) => t.clone(), - LowIRExpr::Unary(_, _, t) => t.clone(), - LowIRExpr::Comparison(_, _, _, t) => t.clone(), - LowIRExpr::FnCall { + PIRExpr::Id(_, t) => t.clone(), + PIRExpr::Number(_, t) => t.clone(), + PIRExpr::Binary(_, _, _, t) => t.clone(), + PIRExpr::Boolean(_, t) => t.clone(), + PIRExpr::Unary(_, _, t) => t.clone(), + PIRExpr::Comparison(_, _, _, t) => t.clone(), + PIRExpr::FnCall { func: _, args: _, span: _, fn_type, } => fn_type.clone(), - LowIRExpr::Grouped(_, t, _) => t.clone(), - LowIRExpr::ScopeInto { + PIRExpr::Grouped(_, t, _) => t.clone(), + PIRExpr::ScopeInto { module: _, target: _, src: _, resolved_type, } => resolved_type.clone(), - LowIRExpr::DirectiveExpr { + PIRExpr::DirectiveExpr { directive: _, expr: _, resolved_type, src: _, } => resolved_type.clone(), - LowIRExpr::StringLiteral(_, t) => t.clone(), - LowIRExpr::CharacterLiteral(_, t) => t.clone(), - LowIRExpr::NamedStructInit { + PIRExpr::StringLiteral(_, t) => t.clone(), + PIRExpr::CharacterLiteral(_, t) => t.clone(), + PIRExpr::NamedStructInit { name: _, fields: _, src: _, @@ -133,7 +131,7 @@ impl ExprRef { #[derive(Debug, Clone)] pub struct ExprPool { - pub pool: Vec, + pub pool: Vec, } #[allow(dead_code)] @@ -142,37 +140,37 @@ impl ExprPool { ExprPool { pool: Vec::new() } } - fn add(&mut self, expr: LowIRExpr) -> ExprRef { + fn add(&mut self, expr: PIRExpr) -> ExprRef { self.pool.push(expr); ExprRef { loc: self.pool.len() - 1, } } - pub fn lowir(&mut self, expr: Expr) -> ExprRef { + pub fn to_pir(&mut self, expr: Expr) -> ExprRef { match expr { - Expr::Id(id, t) => self.add(LowIRExpr::Id(id, t)), - Expr::Number(n, t) => self.add(LowIRExpr::Number(n, t)), - Expr::StringLiteral(s, t) => self.add(LowIRExpr::StringLiteral(s, t)), - Expr::CharacterLiteral(c, t) => self.add(LowIRExpr::CharacterLiteral(c, t)), + Expr::Id(id, t) => self.add(PIRExpr::Id(id, t)), + Expr::Number(n, t) => self.add(PIRExpr::Number(n, t)), + Expr::StringLiteral(s, t) => self.add(PIRExpr::StringLiteral(s, t)), + Expr::CharacterLiteral(c, t) => self.add(PIRExpr::CharacterLiteral(c, t)), Expr::Binary(op, lhs, rhs, t) => { - let lhs = self.lowir(*lhs); - let rhs = self.lowir(*rhs); - self.add(LowIRExpr::Binary(op, lhs, rhs, t)) + let lhs = self.to_pir(*lhs); + let rhs = self.to_pir(*rhs); + self.add(PIRExpr::Binary(op, lhs, rhs, t)) } Expr::Comparison(op, lhs, rhs, t) => { - let lhs = self.lowir(*lhs); - let rhs = self.lowir(*rhs); - self.add(LowIRExpr::Comparison(op, lhs, rhs, t)) + let lhs = self.to_pir(*lhs); + let rhs = self.to_pir(*rhs); + self.add(PIRExpr::Comparison(op, lhs, rhs, t)) } - Expr::Boolean(b, t) => self.add(LowIRExpr::Boolean(b, t)), + Expr::Boolean(b, t) => self.add(PIRExpr::Boolean(b, t)), Expr::Unary(op, expr, t) => { - let expr = self.lowir(*expr); - self.add(LowIRExpr::Unary(op, expr, t)) + let expr = self.to_pir(*expr); + self.add(PIRExpr::Unary(op, expr, t)) } Expr::Grouped(expr, t, src) => { - let expr = self.lowir(*expr); - self.add(LowIRExpr::Grouped(expr, t, src)) + let expr = self.to_pir(*expr); + self.add(PIRExpr::Grouped(expr, t, src)) } Expr::FnCall { func, @@ -180,12 +178,12 @@ impl ExprPool { span, fn_type, } => { - let func = self.lowir(*func); + let func = self.to_pir(*func); let args = args .into_iter() - .map(|arg| self.lowir(arg)) + .map(|arg| self.to_pir(arg)) .collect::>(); - self.add(LowIRExpr::FnCall { + self.add(PIRExpr::FnCall { func, args, span, @@ -198,9 +196,9 @@ impl ExprPool { src, resolved_type, } => { - let module = self.lowir(*module); - let target = self.lowir(*target); - self.add(LowIRExpr::ScopeInto { + let module = self.to_pir(*module); + let target = self.to_pir(*target); + self.add(PIRExpr::ScopeInto { module, target, src, @@ -213,9 +211,9 @@ impl ExprPool { resolved_type, src, } => { - let directive = self.lowir(*directive); - let expr = expr.map(|expr| self.lowir(*expr)); - self.add(LowIRExpr::DirectiveExpr { + let directive = self.to_pir(*directive); + let expr = expr.map(|expr| self.to_pir(*expr)); + self.add(PIRExpr::DirectiveExpr { directive, expr, resolved_type, @@ -228,9 +226,9 @@ impl ExprPool { src, resolved_type, } => { - let name = self.lowir(*name); - let fields = self.lowir_bindings(fields); - self.add(LowIRExpr::NamedStructInit { + let name = self.to_pir(*name); + let fields = self.pir_bindings(fields); + self.add(PIRExpr::NamedStructInit { name, fields, src, @@ -240,14 +238,14 @@ impl ExprPool { } } - fn lowir_bindings(&mut self, bindings: TreeKVBindings) -> KeyValueBindings { + fn pir_bindings(&mut self, bindings: TreeKVBindings) -> KeyValueBindings { let nbindings = bindings .pairs .iter() .map(|pair| { - let key = self.lowir(pair.0.clone()); + let key = self.to_pir(pair.0.clone()); if let Some(value) = pair.1.clone() { - let value = self.lowir(value); + let value = self.to_pir(value); (key, Some(value)) } else { (key, None) @@ -260,14 +258,14 @@ impl ExprPool { } } - pub fn get(&self, expr_ref: &ExprRef) -> LowIRExpr { + pub fn get(&self, expr_ref: &ExprRef) -> PIRExpr { self.pool[expr_ref.loc].clone() } } #[derive(Debug, Clone)] #[allow(dead_code)] -pub enum LowIRIns { +pub enum PIRIns { SingleLineComment { comment: String, src: SourceRef, @@ -335,18 +333,18 @@ pub enum LowIRIns { }, } -impl LowIRIns { +impl PIRIns { pub fn as_str(&self) -> String { match self { - LowIRIns::SingleLineComment { comment, src: _ } => comment.clone(), - LowIRIns::NamedStructDecl { + PIRIns::SingleLineComment { comment, src: _ } => comment.clone(), + PIRIns::NamedStructDecl { name, fields, src: _, } => { format!(":{} {}", name.as_str(), fields.as_str()) } - LowIRIns::ConstantDecl { + PIRIns::ConstantDecl { const_name, const_type: t, init_expr, @@ -365,7 +363,7 @@ impl LowIRIns { format!("let {} = {};", const_name.as_str(), init_expr.as_str()) } }, - LowIRIns::VariableDecl(name, t, init, _) => match (t, init) { + PIRIns::VariableDecl(name, t, init, _) => match (t, init) { (None, None) => format!("mut {};", name.as_str()), (None, Some(init)) => format!("mut {} = {};", name.as_str(), init.as_str()), (Some(c_type), None) => { @@ -378,11 +376,11 @@ impl LowIRIns { init.as_str() ), }, - LowIRIns::AssignmentIns(target, value) => { + PIRIns::AssignmentIns(target, value) => { format!("{} = {};", target.as_str(), value.as_str()) } - LowIRIns::ExpressionIns(expr, _) => format!("{};", expr.as_str()), - LowIRIns::FunctionDef { + PIRIns::ExpressionIns(expr, _) => format!("{};", expr.as_str()), + PIRIns::FunctionDef { name, params, return_type, @@ -410,7 +408,7 @@ impl LowIRIns { str_rep } } - LowIRIns::CodeBlock { + PIRIns::CodeBlock { src: _, instructions, } => { @@ -425,7 +423,7 @@ impl LowIRIns { } ins_str + " }" } - LowIRIns::Module { + PIRIns::Module { name, body, src: _, @@ -438,23 +436,23 @@ impl LowIRIns { "pub ".to_string() + &mod_str } } - LowIRIns::Return { src: _, value } => match value { + PIRIns::Return { src: _, value } => match value { Some(v) => format!("return {};", v.as_str()), None => "return;".to_string(), }, - LowIRIns::InfiniteLoop { src: _, body } => { + PIRIns::InfiniteLoop { src: _, body } => { format!("loop {}", body.as_str()) } - LowIRIns::WhileLoop { + PIRIns::WhileLoop { src: _, condition, body, } => { format!("while {} {}", condition.as_str(), body.as_str()) } - LowIRIns::Break(_) => "break;".to_string(), - LowIRIns::Continue(_) => "continue;".to_string(), - LowIRIns::UseDependency { paths, src: _ } => { + PIRIns::Break(_) => "break;".to_string(), + PIRIns::Continue(_) => "continue;".to_string(), + PIRIns::UseDependency { paths, src: _ } => { let mut path_str = String::new(); for (i, path) in paths.iter().enumerate() { for (j, part) in path.actions.iter().enumerate() { @@ -469,7 +467,7 @@ impl LowIRIns { } format!("use {};", path_str) } - LowIRIns::DirectiveInstruction { + PIRIns::DirectiveInstruction { directive, block, src: _, @@ -480,7 +478,7 @@ impl LowIRIns { format!("@{}", directive.as_str()) } } - LowIRIns::ConditionalBranchIns { pairs, src: _ } => { + PIRIns::ConditionalBranchIns { pairs, src: _ } => { let mut str_rep = String::new(); for i in 0..pairs.len() { let (cond, ins) = &pairs[i]; @@ -524,7 +522,7 @@ impl InsRef { #[derive(Debug, Clone)] #[allow(dead_code)] pub struct InsPool { - pub pool: Vec, + pub pool: Vec, } #[allow(dead_code)] @@ -533,7 +531,7 @@ impl InsPool { InsPool { pool: Vec::new() } } - fn exists(&self, ins: &LowIRIns) -> Option { + fn exists(&self, ins: &PIRIns) -> Option { for (i, ins2) in self.pool.iter().enumerate() { if ins.as_str() == ins2.as_str() { return Some(InsRef { loc: i }); @@ -542,23 +540,23 @@ impl InsPool { None } - fn add(&mut self, ins: LowIRIns) -> InsRef { + fn add(&mut self, ins: PIRIns) -> InsRef { self.pool.push(ins); InsRef { loc: self.pool.len() - 1, } } - pub fn lowir(&mut self, epool: &mut ExprPool, ins: Instruction) -> InsRef { + pub fn to_pir(&mut self, epool: &mut ExprPool, ins: Instruction) -> InsRef { match ins { Instruction::SingleLineComment { comment, src } => { - let ins = LowIRIns::SingleLineComment { comment, src }; + let ins = PIRIns::SingleLineComment { comment, src }; self.add(ins) } Instruction::NamedStructDecl { name, fields, src } => { - let name_ref = epool.lowir(name); - let fields = epool.lowir_bindings(fields); - let ins = LowIRIns::NamedStructDecl { + let name_ref = epool.to_pir(name); + let fields = epool.pir_bindings(fields); + let ins = PIRIns::NamedStructDecl { name: name_ref, fields, src, @@ -572,8 +570,8 @@ impl InsPool { src_ref, is_public, } => { - let init_expr = epool.lowir(init_expr); - let ins = LowIRIns::ConstantDecl { + let init_expr = epool.to_pir(init_expr); + let ins = PIRIns::ConstantDecl { const_name, const_type, init_expr, @@ -583,19 +581,19 @@ impl InsPool { self.add(ins) } Instruction::VariableDecl(name, var_type, init_expr, span) => { - let init_expr = init_expr.map(|e| epool.lowir(e)); - let ins = LowIRIns::VariableDecl(name, var_type, init_expr, span); + let init_expr = init_expr.map(|e| epool.to_pir(e)); + let ins = PIRIns::VariableDecl(name, var_type, init_expr, span); self.add(ins) } Instruction::AssignmentIns(dest, target) => { - let dest = epool.lowir(dest); - let target = epool.lowir(target); - let ins = LowIRIns::AssignmentIns(dest, target); + let dest = epool.to_pir(dest); + let target = epool.to_pir(target); + let ins = PIRIns::AssignmentIns(dest, target); self.add(ins) } Instruction::ExpressionIns(expr, semi) => { - let expr = epool.lowir(expr); - let ins = LowIRIns::ExpressionIns(expr, semi); + let expr = epool.to_pir(expr); + let ins = PIRIns::ExpressionIns(expr, semi); self.add(ins) } Instruction::FunctionDef { @@ -606,9 +604,9 @@ impl InsPool { is_public, src, } => { - let params = params.into_iter().map(|p| epool.lowir(p)).collect(); - let body = self.lowir(epool, *body); - let ins = LowIRIns::FunctionDef { + let params = params.into_iter().map(|p| epool.to_pir(p)).collect(); + let body = self.to_pir(epool, *body); + let ins = PIRIns::FunctionDef { name, params, return_type, @@ -619,8 +617,8 @@ impl InsPool { self.add(ins) } Instruction::InfiniteLoop { src, body } => { - let body = self.lowir(epool, *body); - let ins = LowIRIns::InfiniteLoop { src, body }; + let body = self.to_pir(epool, *body); + let ins = PIRIns::InfiniteLoop { src, body }; self.add(ins) } Instruction::WhileLoop { @@ -628,9 +626,9 @@ impl InsPool { condition, body, } => { - let condition = epool.lowir(condition); - let body = self.lowir(epool, *body); - let ins = LowIRIns::WhileLoop { + let condition = epool.to_pir(condition); + let body = self.to_pir(epool, *body); + let ins = PIRIns::WhileLoop { src, condition, body, @@ -640,9 +638,9 @@ impl InsPool { Instruction::CodeBlock { src, instructions } => { let instructions = instructions .into_iter() - .map(|i| self.lowir(epool, i)) + .map(|i| self.to_pir(epool, i)) .collect(); - let ins = LowIRIns::CodeBlock { src, instructions }; + let ins = PIRIns::CodeBlock { src, instructions }; self.add(ins) } Instruction::Module { @@ -651,9 +649,9 @@ impl InsPool { src, is_public, } => { - let name = epool.lowir(name); - let body = self.lowir(epool, *body); - let ins = LowIRIns::Module { + let name = epool.to_pir(name); + let body = self.to_pir(epool, *body); + let ins = PIRIns::Module { name, body, src, @@ -662,20 +660,20 @@ impl InsPool { self.add(ins) } Instruction::Return { src, value } => { - let value = value.map(|v| epool.lowir(v)); - let ins = LowIRIns::Return { src, value }; + let value = value.map(|v| epool.to_pir(v)); + let ins = PIRIns::Return { src, value }; self.add(ins) } Instruction::Break(src) => { - let ins = LowIRIns::Break(src); + let ins = PIRIns::Break(src); self.add(ins) } Instruction::Continue(src) => { - let ins = LowIRIns::Continue(src); + let ins = PIRIns::Continue(src); self.add(ins) } Instruction::UseDependency { paths, src } => { - let ins = LowIRIns::UseDependency { paths, src }; + let ins = PIRIns::UseDependency { paths, src }; self.add(ins) } Instruction::DirectiveInstruction { @@ -683,9 +681,9 @@ impl InsPool { block, src, } => { - let directive = epool.lowir(directive); - let block = block.map(|b| self.lowir(epool, *b)); - let ins = LowIRIns::DirectiveInstruction { + let directive = epool.to_pir(directive); + let block = block.map(|b| self.to_pir(epool, *b)); + let ins = PIRIns::DirectiveInstruction { directive, block, src, @@ -697,47 +695,68 @@ impl InsPool { .into_iter() .map(|(cond, body)| { if let Some(cond) = cond { - let cond = epool.lowir(cond); - let body = self.lowir(epool, *body); + let cond = epool.to_pir(cond); + let body = self.to_pir(epool, *body); (Some(cond), body) } else { - let body = self.lowir(epool, *body); + let body = self.to_pir(epool, *body); (None, body) } }) .collect(); - let ins = LowIRIns::ConditionalBranchIns { pairs, src }; + let ins = PIRIns::ConditionalBranchIns { pairs, src }; self.add(ins) } } } - pub fn get(&self, ins_ref: &InsRef) -> LowIRIns { + pub fn get(&self, ins_ref: &InsRef) -> PIRIns { self.pool[ins_ref.loc].clone() } } #[derive(Debug, Clone)] -pub struct LowIRModule { +pub struct PIRModule { pub ins_pool: InsPool, pub expr_pool: ExprPool, pub top_level: Vec, + pub path: String, } #[allow(dead_code)] -impl LowIRModule { - pub fn new() -> LowIRModule { - LowIRModule { +impl PIRModule { + pub fn new(cm: CompilationModule, path: String) -> PIRModule { + let mut pirmod = PIRModule { ins_pool: InsPool::new(), expr_pool: ExprPool::new(), top_level: Vec::new(), + path, + }; + for ins in cm.instructions { + let id = pirmod.ins_pool.to_pir(&mut pirmod.expr_pool, ins); + pirmod.top_level.push(id); } + pirmod } +} - pub fn lowir(&mut self, cm: CompilationModule) { - for ins in cm.instructions { - let id = self.ins_pool.lowir(&mut self.expr_pool, ins); - self.top_level.push(id); +#[allow(dead_code)] +pub trait PIRModulePass<'a, InsRes, ExprRes, KVRes, ModRes, Error> { + fn process_ins(&mut self, ins: &InsRef) -> Result; + fn process_expr(&mut self, expr: &ExprRef) -> Result; + fn process_pairs(&mut self, kv: &KeyValueBindings) -> Result; + fn process_module(&mut self) -> Result, Error> { + let mut res = vec![]; + + let module = self.get_module(); + for ins_ref in module.top_level.iter() { + let ins_res = self.process_ins(&ins_ref)?; + res.push(ins_res); } + + Ok(res) } + fn process(&mut self) -> Result; + fn new(module: &'a PIRModule) -> Self; + fn get_module(&mut self) -> &'a PIRModule; } diff --git a/src/pir/mod.rs b/src/pir/mod.rs new file mode 100644 index 0000000..19d2d13 --- /dev/null +++ b/src/pir/mod.rs @@ -0,0 +1 @@ +pub mod ir; diff --git a/src/prc/README.md b/src/prc/README.md deleted file mode 100644 index 7c7cf1f..0000000 --- a/src/prc/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# prc - -This is the proto compiler. It is the standalone compiler and can be used to compile a proto source file (with `.pr` extension) to an executable. diff --git a/src/proto/README.md b/src/proto/README.md deleted file mode 100644 index 3993030..0000000 --- a/src/proto/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# proto - -The REPL for trying out ideas in a less permanent space. Much like python, php and other languages provide. - -As a project manager for proto. It will be able to: - -- [ ] create new proto projects. - -- [ ] create new proto projects in existing folders. - -- [ ] modify existing projects to run the code formatter or linter or static analysis without compilation. Will be fleshed out before work begins. - -- [ ] compile proto projects. - -- [ ] fetch proto [units](../../docs/tbd.md) from GitHub or else where for project use. - -> will work on document generation - -> will work on testing diff --git a/src/proton/README.md b/src/proton/README.md deleted file mode 100644 index 3c194e7..0000000 --- a/src/proton/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# proton - -The virtual machine for proto. diff --git a/src/tools/mod.rs b/src/tools/mod.rs new file mode 100644 index 0000000..5ae395a --- /dev/null +++ b/src/tools/mod.rs @@ -0,0 +1,2 @@ +pub mod pcodeview; +pub mod pfmt; diff --git a/src/ir8/codeviewer.rs b/src/tools/pcodeview.rs similarity index 67% rename from src/ir8/codeviewer.rs rename to src/tools/pcodeview.rs index 9773148..84b73fa 100644 --- a/src/ir8/codeviewer.rs +++ b/src/tools/pcodeview.rs @@ -1,5 +1,6 @@ -use super::lowir::{ExprPool, ExprRef, InsPool, InsRef, KeyValueBindings, LowIRExpr, LowIRIns}; -use super::visitir::{VisitIRError, VisitsLowIR}; +use crate::pir::ir::{ + ExprRef, InsRef, KeyValueBindings, PIRExpr, PIRIns, PIRModule, PIRModulePass, +}; #[derive(Clone)] #[allow(dead_code)] @@ -9,35 +10,14 @@ enum CodeBlockType { } #[allow(dead_code)] -pub struct CodeViewer { - ins_pool: InsPool, - expr_pool: ExprPool, +pub struct PCodeView<'a> { + module: Option<&'a PIRModule>, left_padding: u16, block_type: CodeBlockType, } #[allow(dead_code)] -impl CodeViewer { - pub fn new(ins_pool: InsPool, expr_pool: ExprPool) -> Self { - Self { - left_padding: 0, - ins_pool, - expr_pool, - block_type: CodeBlockType::Regular, - } - } - - pub fn unwrap(&mut self, res: Vec>) -> Vec { - let mut result = vec![]; - for r in res { - match r { - Ok(s) => result.push(s.clone()), - Err(_) => {} - } - } - result - } - +impl<'a> PCodeView<'a> { pub fn increase_padding(&mut self) { self.left_padding += 2; } @@ -50,74 +30,35 @@ impl CodeViewer { let padding = " ".repeat(self.left_padding as usize); format!("{}{}", padding, text) } - - pub fn get_ins_pool(self) -> InsPool { - self.ins_pool - } - - pub fn get_expr_pool(self) -> ExprPool { - self.expr_pool - } } -impl VisitsLowIR for CodeViewer { - fn visit_pairs(&mut self, kv: &KeyValueBindings) -> Result { - let mut view = String::from("{"); - let kv_pair_len = kv.pairs.len(); - if kv_pair_len > 0 { - self.increase_padding(); - } else { - view.push_str(" }"); - return Ok(view); - } - - let mut count = 0; - for (key, value) in &kv.pairs { - let key = self.visit_expr(&key)?; - if let Some(value) = value { - let value = self.visit_expr(&value)?; - view.push('\n'); - view.push_str(&self.pad_text(format!("{} = {}", key, value))); - } else { - view.push('\n'); - view.push_str(&self.pad_text(format!("{}", key))); - } - count += 1; - if count < kv_pair_len { - view.push(','); - } - } - self.decrease_padding(); - view.push('\n'); - view.push_str(&self.pad_text("}".to_string())); - Ok(view) - } - - fn visit_ins(&mut self, ins: &InsRef) -> Result { +impl<'a> PIRModulePass<'a, String, String, String, String, ()> for PCodeView<'a> { + fn process_ins(&mut self, ins: &InsRef) -> Result { // get instruction from pool - let ins_node = self.ins_pool.get(&ins); + let module = self.module.unwrap(); + let ins_node = module.ins_pool.get(&ins); match ins_node { - LowIRIns::SingleLineComment { comment, src: _ } => { + PIRIns::SingleLineComment { comment, src: _ } => { let comment = comment.clone(); Ok(self.pad_text(comment)) } - LowIRIns::NamedStructDecl { + PIRIns::NamedStructDecl { name, fields, src: _, } => { - let name = self.visit_expr(&name)?; + let name = self.process_expr(&name)?; let mut view = String::new(); view.push_str(&self.pad_text(format!(":{} ", name))); if fields.pairs.len() > 0 { - view.push_str(&self.visit_pairs(&fields)?); + view.push_str(&self.process_pairs(&fields)?); } else { view.push_str("{ }"); } Ok(view) } - LowIRIns::ConstantDecl { + PIRIns::ConstantDecl { const_name, const_type: _, init_expr, @@ -125,32 +66,32 @@ impl VisitsLowIR for CodeViewer { is_public, } => { let const_name = const_name.as_str(); - let init_expr = self.visit_expr(&init_expr)?; + let init_expr = self.process_expr(&init_expr)?; let mut view = format!("let {} = {}", const_name, init_expr); if is_public { view = format!("pub {}", view); } Ok(self.pad_text(view)) } - LowIRIns::VariableDecl(var_name, _, init_expr, _) => { + PIRIns::VariableDecl(var_name, _, init_expr, _) => { let var_name = var_name.as_str(); if let Some(init_expr) = init_expr { - let init_expr = self.visit_expr(&init_expr)?; + let init_expr = self.process_expr(&init_expr)?; Ok(self.pad_text(format!("mut {} = {}", var_name, init_expr))) } else { Ok(self.pad_text(format!("mut {}", var_name))) } } - LowIRIns::AssignmentIns(target, val) => { - let target = self.visit_expr(&target)?; - let val = self.visit_expr(&val)?; + PIRIns::AssignmentIns(target, val) => { + let target = self.process_expr(&target)?; + let val = self.process_expr(&val)?; Ok(self.pad_text(format!("{} = {};", target, val))) } - LowIRIns::ExpressionIns(expr, _) => { - let expr = self.visit_expr(&expr)?; + PIRIns::ExpressionIns(expr, _) => { + let expr = self.process_expr(&expr)?; Ok(self.pad_text(format!("{};", expr))) } - LowIRIns::FunctionDef { + PIRIns::FunctionDef { name, params, return_type, @@ -161,13 +102,13 @@ impl VisitsLowIR for CodeViewer { let name = name.as_str(); let mut param_strs = vec![]; for param in params { - param_strs.push(self.visit_expr(¶m)?); + param_strs.push(self.process_expr(¶m)?); } let params = param_strs.join(", "); let return_type = return_type.as_str(); let block_type = self.block_type.clone(); self.block_type = CodeBlockType::InstructionPart; - let body = self.visit_ins(&body)?; + let body = self.process_ins(&body)?; self.block_type = block_type; let mut view = format!("fn {name}({params}) {return_type} {body}"); if is_public { @@ -175,26 +116,26 @@ impl VisitsLowIR for CodeViewer { } Ok(self.pad_text(view)) } - LowIRIns::InfiniteLoop { src: _, body } => { + PIRIns::InfiniteLoop { src: _, body } => { let block_type = self.block_type.clone(); self.block_type = CodeBlockType::InstructionPart; - let body = self.visit_ins(&body)?; + let body = self.process_ins(&body)?; self.block_type = block_type; Ok(self.pad_text(format!("loop {}", body))) } - LowIRIns::WhileLoop { + PIRIns::WhileLoop { src: _, condition, body, } => { - let condition = self.visit_expr(&condition)?; + let condition = self.process_expr(&condition)?; let block_type = self.block_type.clone(); self.block_type = CodeBlockType::InstructionPart; - let body = self.visit_ins(&body)?; + let body = self.process_ins(&body)?; self.block_type = block_type; Ok(self.pad_text(format!("while {} {}", condition, body))) } - LowIRIns::CodeBlock { + PIRIns::CodeBlock { src: _, instructions, } => { @@ -215,7 +156,7 @@ impl VisitsLowIR for CodeViewer { self.increase_padding(); let mut ins_strs = vec![]; for ins in instructions { - ins_strs.push(self.visit_ins(&ins)?); + ins_strs.push(self.process_ins(&ins)?); } self.decrease_padding(); view.push_str(&ins_strs.join("\n\n")); @@ -223,16 +164,16 @@ impl VisitsLowIR for CodeViewer { } Ok(view) } - LowIRIns::Module { + PIRIns::Module { name, body, src: _, is_public, } => { - let name = self.visit_expr(&name)?; + let name = self.process_expr(&name)?; let block_type = self.block_type.clone(); self.block_type = CodeBlockType::InstructionPart; - let body = self.visit_ins(&body)?; + let body = self.process_ins(&body)?; self.block_type = block_type; let mut view = format!("mod {} {}", name, body); if is_public { @@ -240,17 +181,17 @@ impl VisitsLowIR for CodeViewer { } Ok(self.pad_text(view)) } - LowIRIns::Return { src: _, value } => { + PIRIns::Return { src: _, value } => { if let Some(value) = value { - let value = self.visit_expr(&value)?; + let value = self.process_expr(&value)?; Ok(self.pad_text(format!("return {};", value))) } else { Ok(self.pad_text("return;".to_string())) } } - LowIRIns::Break(_) => Ok(self.pad_text("break;".to_string())), - LowIRIns::Continue(_) => Ok(self.pad_text("continue;".to_string())), - LowIRIns::UseDependency { paths, src: _ } => { + PIRIns::Break(_) => Ok(self.pad_text("break;".to_string())), + PIRIns::Continue(_) => Ok(self.pad_text("continue;".to_string())), + PIRIns::UseDependency { paths, src: _ } => { let mut path_str = String::new(); for (i, path) in paths.iter().enumerate() { for (j, part) in path.actions.iter().enumerate() { @@ -265,45 +206,45 @@ impl VisitsLowIR for CodeViewer { } Ok(format!("use {};", path_str)) } - LowIRIns::DirectiveInstruction { + PIRIns::DirectiveInstruction { directive, block, src: _, } => { - let directive = self.visit_expr(&directive)?; + let directive = self.process_expr(&directive)?; if let Some(block) = block { let block_type = self.block_type.clone(); self.block_type = CodeBlockType::InstructionPart; - let block = self.visit_ins(&block)?; + let block = self.process_ins(&block)?; self.block_type = block_type; Ok(self.pad_text(format!("@{} {}", directive, block))) } else { Ok(self.pad_text(format!("@{};", directive))) } } - LowIRIns::ConditionalBranchIns { pairs, src: _ } => { + PIRIns::ConditionalBranchIns { pairs, src: _ } => { let mut view = String::new(); let block_type = self.block_type.clone(); self.block_type = CodeBlockType::InstructionPart; for i in 0..pairs.len() { let (cond, ins) = &pairs[i]; if i == 0 { - let cond = self.visit_expr(&cond.clone().unwrap())?; - let ins = self.visit_ins(&ins)?; + let cond = self.process_expr(&cond.clone().unwrap())?; + let ins = self.process_ins(&ins)?; // self.increase_padding(); // for the first pair, we do if view.push_str(&self.pad_text(format!("if {} {}", cond, ins))); // self.decrease_padding(); } else if i < pairs.len() - 1 { - let cond = self.visit_expr(&cond.clone().unwrap())?; - let ins = self.visit_ins(&ins)?; + let cond = self.process_expr(&cond.clone().unwrap())?; + let ins = self.process_ins(&ins)?; self.increase_padding(); // for the rest, we do else if view.push_str(&format!(" else if {} {}", cond, ins)); self.decrease_padding(); } else { // for the last, we do else - let ins = self.visit_ins(&ins)?; + let ins = self.process_ins(&ins)?; self.increase_padding(); view.push_str(&format!(" else {}", ins)); self.decrease_padding(); @@ -315,91 +256,141 @@ impl VisitsLowIR for CodeViewer { } } - fn visit_expr(&mut self, expr: &ExprRef) -> Result { + fn process_expr(&mut self, expr: &ExprRef) -> Result { // get expr from pool - let expr_node = self.expr_pool.get(&expr); + let module = self.module.unwrap(); + let expr_node = module.expr_pool.get(&expr); match expr_node { - LowIRExpr::Id(token, id_type) => { + PIRExpr::Id(token, id_type) => { if let Some(id_type) = id_type { Ok(format!("{} {}", token.as_str(), id_type.as_str())) } else { Ok(token.as_str()) } } - LowIRExpr::Number(num, _) => Ok(num.as_str()), - LowIRExpr::StringLiteral(lit, _) => Ok(lit.as_str()), - LowIRExpr::CharacterLiteral(lit, _) => Ok(lit.as_str()), - LowIRExpr::Binary(operator, lhs, rhs, _) => { - let lhs = self.visit_expr(&lhs)?; - let rhs = self.visit_expr(&rhs)?; + PIRExpr::Number(num, _) => Ok(num.as_str()), + PIRExpr::StringLiteral(lit, _) => Ok(lit.as_str()), + PIRExpr::CharacterLiteral(lit, _) => Ok(lit.as_str()), + PIRExpr::Binary(operator, lhs, rhs, _) => { + let lhs = self.process_expr(&lhs)?; + let rhs = self.process_expr(&rhs)?; Ok(format!("{} {} {}", lhs, operator.as_str(), rhs)) } - LowIRExpr::Comparison(operator, lhs, rhs, _) => { - let lhs = self.visit_expr(&lhs)?; - let rhs = self.visit_expr(&rhs)?; + PIRExpr::Comparison(operator, lhs, rhs, _) => { + let lhs = self.process_expr(&lhs)?; + let rhs = self.process_expr(&rhs)?; Ok(format!("{} {} {}", lhs, operator.as_str(), rhs)) } - LowIRExpr::Boolean(lit, _) => Ok(lit.as_str()), - LowIRExpr::Unary(operator, expr, _) => { - let expr = self.visit_expr(&expr)?; + PIRExpr::Boolean(lit, _) => Ok(lit.as_str()), + PIRExpr::Unary(operator, expr, _) => { + let expr = self.process_expr(&expr)?; Ok(format!("{}{}", operator.as_str(), expr)) } - LowIRExpr::Grouped(inner_expr, _, _) => { - let inner_expr = self.visit_expr(&inner_expr)?; + PIRExpr::Grouped(inner_expr, _, _) => { + let inner_expr = self.process_expr(&inner_expr)?; Ok(format!("({})", inner_expr)) } - LowIRExpr::FnCall { + PIRExpr::FnCall { func, args, span: _, fn_type: _, } => { - let func = self.visit_expr(&func)?; + let func = self.process_expr(&func)?; let args_str = args .iter() - .map(|arg| self.visit_expr(&arg)) - .collect::, VisitIRError>>()?; + .map(|arg| self.process_expr(&arg)) + .collect::, ()>>()?; let args_str = args_str.join(", "); Ok(format!("{}({})", func, args_str)) } - LowIRExpr::ScopeInto { + PIRExpr::ScopeInto { module, target, src: _, resolved_type: _, } => { - let module = self.visit_expr(&module)?; - let target = self.visit_expr(&target)?; + let module = self.process_expr(&module)?; + let target = self.process_expr(&target)?; Ok(format!("{}::{}", module, target)) } - LowIRExpr::DirectiveExpr { + PIRExpr::DirectiveExpr { directive, expr, resolved_type: _, src: _, } => { - let directive = self.visit_expr(&directive)?; + let directive = self.process_expr(&directive)?; if let Some(expr) = expr { - let expr = self.visit_expr(&expr)?; + let expr = self.process_expr(&expr)?; Ok(format!("@{} {}", directive, expr)) } else { Ok(format!("@{}", directive)) } } - LowIRExpr::NamedStructInit { + PIRExpr::NamedStructInit { name, fields, src: _, resolved_type: _, } => { - let name = self.visit_expr(&name)?; + let name = self.process_expr(&name)?; let block_type = self.block_type.clone(); self.block_type = CodeBlockType::InstructionPart; - let fields = self.visit_pairs(&fields)?; + let fields = self.process_pairs(&fields)?; self.block_type = block_type; Ok(format!(":{} {}", name, fields)) } } } + + fn process_pairs(&mut self, kv: &KeyValueBindings) -> Result { + let mut view = String::from("{"); + let kv_pair_len = kv.pairs.len(); + if kv_pair_len > 0 { + self.increase_padding(); + } else { + view.push_str(" }"); + return Ok(view); + } + + let mut count = 0; + for (key, value) in &kv.pairs { + let key = self.process_expr(&key)?; + if let Some(value) = value { + let value = self.process_expr(&value)?; + view.push('\n'); + view.push_str(&self.pad_text(format!("{} = {}", key, value))); + } else { + view.push('\n'); + view.push_str(&self.pad_text(format!("{}", key))); + } + count += 1; + if count < kv_pair_len { + view.push(','); + } + } + self.decrease_padding(); + view.push('\n'); + view.push_str(&self.pad_text("}".to_string())); + Ok(view) + } + + fn process(&mut self) -> Result { + let content = self.process_module()?; + Ok(content.join("\n")) + } + + fn new(module: &'a PIRModule) -> Self { + Self { + left_padding: 0, + module: Some(module), + block_type: CodeBlockType::Regular, + } + } + + fn get_module(&mut self) -> &'a PIRModule { + self.module.unwrap() + } } diff --git a/src/tools/pfmt.rs b/src/tools/pfmt.rs new file mode 100644 index 0000000..4a6267a --- /dev/null +++ b/src/tools/pfmt.rs @@ -0,0 +1,50 @@ +use std::fs; + +use crate::pir::ir::{ExprRef, InsRef, KeyValueBindings, PIRModule, PIRModulePass}; + +use super::pcodeview::PCodeView; + +pub struct Pfmt<'a> { + module: Option<&'a PIRModule>, +} + +impl<'a> PIRModulePass<'a, (), (), (), String, String> for Pfmt<'a> { + fn new(module: &'a PIRModule) -> Self { + Self { + module: Some(module), + } + } + + fn process_ins(&mut self, _: &InsRef) -> Result<(), String> { + Ok(()) + } + + fn process_expr(&mut self, _: &ExprRef) -> Result<(), String> { + Ok(()) + } + + fn process_pairs(&mut self, _: &KeyValueBindings) -> Result<(), String> { + Ok(()) + } + + fn process(&mut self) -> Result { + let mut module = self.module.unwrap(); + let mut code_view = PCodeView::new(&mut module); + let contents = code_view.process().unwrap(); + + // write out to module path + let write_res = fs::write(&module.path, contents); + if let Err(e) = write_res { + Err(format!( + "Failed to write to file at path `{}`. \n{}", + module.path, e + )) + } else { + Ok(format!("Successfully formatted `{}`", module.path)) + } + } + + fn get_module(&mut self) -> &'a PIRModule { + self.module.unwrap() + } +}