diff --git a/README.md b/README.md index 72248c2..929a42b 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,9 @@

Wyst is a programing language that has similar syntax to C and transpiles to Rust

-<<<<<<< HEAD -# Re Write -We are re-writing the parser and transpiler due to the messy code that is very hard to expand -======= +# Hello World ```c +// main.wst #include int main() { @@ -19,7 +17,11 @@ int main() { return 0; } ``` ->>>>>>> 30ed01f (Update README.md) + +# Compile it +```bash +wyst main.wst -o +``` # Alpha We are on pure alpha. if you would like to contribute please open a new issue. diff --git a/lib/stdio.rs b/lib/stdio.rs new file mode 100644 index 0000000..2a7ac7d --- /dev/null +++ b/lib/stdio.rs @@ -0,0 +1,6 @@ +#[macro_export] +macro_rules! printf { + ($($arg:tt)*) => { + println!($($arg)*); + }; +} \ No newline at end of file diff --git a/src/compiler.rs b/src/compiler.rs index d1d0945..c79f8ab 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -6,30 +6,29 @@ use crate::{ utils::{Problem, ProblemCap, ProblemType}, }; -pub fn transpile(input_file: String, output_file: String, is_main: bool) -> u8 { +pub fn transpile_file(input_file: String, output_file: String, is_main: bool) -> u8 { let mut ecode: u8 = 0; let mut trsp = Transpiler { ..Default::default() }; if let Ok(file_contents) = fs::read_to_string(input_file) { - let output_code = trsp.transpile_code(file_contents, 0); - println!("{output_code}"); + let mut module_code = String::new(); + let mut output_code = String::new(); + match trsp.transpile_code(file_contents, 0) { + Ok(rcode) => output_code+=rcode.as_str(), + Err(e) => {ecode=1;println!("{e}")} + } if is_main { - // output_code += "fn main() {"; - // output_code += vars - // .get_var( - // "main".to_string(), - // &mut trsp, - // State { - // line: 0, - // column: 0, - // file: None, - // }, - // ) - // .as_str(); - // output_code += "();}"; + for module in trsp.clone().mod_manager.modules { + module_code += "mod "; + module_code += module.rs_mod.as_str(); + module_code += ";\n"; + } + } + output_code = format!("{module_code}\n{output_code}"); + if let Ok(_) = fs::write(Path::new("build").join(output_file), output_code) { + trsp.problems.append(&mut trsp.mod_manager.write()); } - if let Ok(_) = fs::write(Path::new("build").join(output_file), output_code) {} } else { trsp.problems.push(ProblemCap::Error(Problem { problem_msg: "File not found".to_string(), @@ -69,11 +68,11 @@ pub fn compile_rust(exe_file: String) { let output = Command::new("rustc") .arg(Path::new("build").join("main.rs")) .arg("-o") - .arg(exe_file) + .arg(exe_file.clone()) .output() .expect("Error compiling rust: "); if &output.stderr.len() == &0 { - println!("Compiled successfully"); + println!("Compiled successfully: \x1b[34;4m{}", exe_file.clone()); } else { println!("{}", str::from_utf8(&output.stderr).expect("Error encoding stderr")); } diff --git a/src/main.rs b/src/main.rs index d61a7ea..cfaccb3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,10 @@ use std::{fs, process}; - mod compiler; mod transpiler; mod utils; use clap::{App, Arg}; -use compiler::{compile_rust, transpile}; +use compiler::{compile_rust, transpile_file}; fn main() { let matches = App::new("Wyst") @@ -44,8 +43,10 @@ fn main() { let file = matches.value_of("file").unwrap().to_string(); let output = matches.value_of("output").unwrap_or("out").to_string(); if let Ok(_) = fs::create_dir("build") {} - let ecode = transpile(file, "main.rs".to_string(), true); - compile_rust(output); + let ecode = transpile_file(file, "main.rs".to_string(), true); + if ecode == 0 { + compile_rust(output); + } if !matches.is_present("build") { fs::remove_dir_all("build").expect("Error removing build"); } diff --git a/src/transpiler.rs b/src/transpiler.rs index 687552f..8718927 100644 --- a/src/transpiler.rs +++ b/src/transpiler.rs @@ -1,9 +1,9 @@ use pest::iterators::{Pair, Pairs}; -use crate::utils::{ProblemCap}; +use crate::utils::{ModManager, ProblemCap}; use serde::{Deserialize, Serialize}; // The Parser -use pest::Parser; +use pest::{Parser, error::Error}; use pest_derive::Parser; #[derive(Parser)] #[grammar = "wyst.pest"] @@ -16,13 +16,16 @@ pub struct State { pub file: Option, } -#[derive(Deserialize, Serialize, Debug, Clone)] +#[derive(Debug, Clone)] +#[allow(dead_code)] pub struct Transpiler { pub problems: Vec, pub state: State, pub inject: Inject, + pub mod_manager: ModManager, } -#[derive(Deserialize, Serialize, Debug, Clone)] +#[derive(Debug, Clone)] +#[allow(dead_code)] pub struct Inject { pub state: State, pub inject: bool, @@ -46,6 +49,7 @@ impl Default for Transpiler { Transpiler { inject: Inject::default(), problems: Vec::new(), + mod_manager: ModManager::new(), state: State { line: 1, column: 0, @@ -75,7 +79,7 @@ impl Transpiler { let tokens = to_indexable(pair.clone().into_inner()); match pair.as_rule() { Rule::func_def => { - res += format!("fn {}({}) -> {} {}", self.transpile(tokens[1].clone()), + res += format!("pub fn {}({}) -> {} {}\n", self.transpile(tokens[1].clone()), self.transpile_pairs(tokens[2].clone().into_inner()).as_str(), self.transpile(tokens[0].clone()), self.transpile(tokens[3].clone()) @@ -92,16 +96,29 @@ impl Transpiler { Rule::code_expr => { res += &self.transpile_pairs(pair.clone().into_inner()); } - //TODO: use LibManager to manage the includes - Rule::include_global => {} - Rule::include => {} + + Rule::include_global => { + res += "#[allow(unused_imports)]\nuse "; + res += self.mod_manager.add(tokens[0].as_str().to_string(), true).as_str(); + res += "::*;\n"; + } + Rule::include => { + res += "#[allow(unused_imports)]\nuse "; + res += self.mod_manager.add(tokens[0].as_str().to_string(), false).as_str(); + res += "::*;\n"; + } Rule::identifier => { let ident = pair.as_str().trim(); - res+=match ident { - "void" => {"()"} - _ => {ident} - }; + if self.mod_manager.macros.contains(&ident.to_string()) { + res+=ident; + res+="!"; + } else { + res+=match ident { + "void" => {"()"} + _ => {ident} + }; + } } Rule::def_identifier => { @@ -112,6 +129,26 @@ impl Transpiler { }; } + Rule::call => { + res += " "; + res += self.transpile(tokens[0].clone()).as_str(); + res += self.transpile(tokens[1].clone()).as_str(); + } + + Rule::round => { + res += "("; + res += self.transpile_pairs(pair.into_inner()).as_str(); + res += ")"; + } + + Rule::expr => { + res += self.transpile_pairs(pair.into_inner()).as_str(); + } + + Rule::expr_ => { + res += self.transpile_pairs(pair.into_inner()).as_str(); + } + _ => { res += " "; res += pair.as_str(); @@ -120,7 +157,7 @@ impl Transpiler { } res } - pub fn transpile_code(&mut self, code: String, indent: usize) -> String { + pub fn transpile_code(&mut self, code: String, indent: usize) -> Result> { let mut res = String::new(); match WystParser::parse(Rule::top_expr, &code) { Ok(parsed) => { @@ -129,7 +166,7 @@ impl Transpiler { } } Err(e) => { - println!("{}", e); + return Err(e); } } if indent > 0 { @@ -140,6 +177,6 @@ impl Transpiler { res += "\n"; res += " ".repeat((indent - 1) * 2).as_str(); } - res + Ok(res) } } diff --git a/src/utils.rs b/src/utils.rs index 169095a..740fb1b 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,5 +1,9 @@ -use crate::transpiler::State; +use std::fs; +use std::path::Path; +use crate::transpiler::{Rule, State, Transpiler, WystParser}; use serde::{Deserialize, Serialize}; +use dirs::home_dir; +use pest::Parser; #[derive(Clone, Debug, Serialize, Deserialize)] pub enum ProblemType { @@ -19,4 +23,104 @@ pub struct Problem { pub problem_type: ProblemType, pub problem_msg: String, pub state: State, +} + +#[derive(Debug, Clone)] +pub struct Module { + pub rs_mod: String, + pub file_path: String, + pub code: String +} + +#[derive(Debug, Clone)] +pub struct ModManager { + pub modules: Vec, + pub macros: Vec +} + +impl ModManager { + pub fn new() -> ModManager { + ModManager {modules: Vec::new(), macros: Vec::new()} + } + pub fn check(&mut self, path: String) -> Option { + for module in self.modules.clone() { + if module.file_path == path { + return Some(module); + } + } + None + } + pub fn add(&mut self, file_path: String, global: bool) -> String { + let mut problems: Vec = Vec::new(); + let mod_name = format!("mod_{}", self.modules.len()); + let home_dirpb = home_dir().unwrap(); + let home_dir = home_dirpb.to_str().unwrap(); + let mut file_path = file_path; + if global { + let mut tmp = Path::new(home_dir).join("wyst").join("lib").join(file_path.clone()).to_str().unwrap().to_string(); + if !Path::new(&tmp).exists() { + tmp = Path::new("lib").join(file_path).to_str().unwrap().to_string(); + } + file_path = tmp; + } + if let Some(modl) = self.check(file_path.clone()) { + return modl.rs_mod; + } + if let Ok(code) = fs::read_to_string(file_path.clone()) { + self.modules.push(Module { rs_mod: mod_name.clone(), file_path: file_path.clone(), code: code.clone() }); + match file_path.rsplit_once('.').unwrap_or(("", "")).1 { + "rs" => { + match WystParser::parse(Rule::rust_code, &code) { + Ok(parsed) => { + for pair in parsed { + for p in pair.into_inner() { + if p.as_rule() == Rule::rust_macro_lint { + self.macros.push(p.into_inner().as_str().to_string()); + } + } + } + } + Err(_) => {} + } + } + _ => {} + } + } else { + problems.push(ProblemCap::Error(Problem { + problem_msg: format!("File not found '{}'", file_path), + problem_type: ProblemType::FileNotFound, + state: State { + line: 0, + column: 0, + file: None, + }, + })) + } + mod_name + } + pub fn write(&mut self) -> Vec { + let mut problems: Vec = Vec::new(); + for module in self.modules.clone() { + let file_path = module.file_path; + match file_path.rsplit_once('.').unwrap_or(("", "")).1 { + "wst" => { + let mut trsp = Transpiler { + mod_manager: self.clone(), + ..Default::default() + }; + let rust_code = trsp.transpile_code(module.code, 0).expect(""); + fs::write(Path::new("build").join((module.rs_mod+".rs").as_str()), rust_code).expect("Error writing rust file: "); + problems.append(&mut trsp.problems); + self.modules = trsp.mod_manager.modules; + } + + "rs" => { + fs::write(Path::new("build").join((module.rs_mod+".rs").as_str()), &module.code).unwrap() + } + + _ => {} + } + } + problems + } } \ No newline at end of file diff --git a/src/wyst.pest b/src/wyst.pest index 6e1ddea..ff665ab 100644 --- a/src/wyst.pest +++ b/src/wyst.pest @@ -32,8 +32,15 @@ var_def_set = { var_def ~ "=" ~ expr } var_set = { def_identifier ~ "=" ~ expr } func_def = { def_identifier ~ identifier ~ round_def ~ curly } -expr = { - (identifier ~ round) | +rust_macro_lint = { "macro_rules" ~ "!" ~ identifier } +rust_code = { (rust_macro_lint | ANY)* } + +plus = {"+"} + +call = {identifier ~ round} + +expr_ = { + call | round | curly | char | @@ -45,11 +52,18 @@ expr = { string } +expr = { + (expr_ ~ plus ~ expr_) | + expr_ +} + code_expr = { - (var_def_set ~ semicolon) | - (var_def ~ semicolon) | - (var_set ~ semicolon) | - (expr ~ semicolon) + ( + var_def_set| + var_def | + var_set | + expr + ) ~ semicolon } top_expr = {