diff --git a/CHANGELOG.md b/CHANGELOG.md index c29f36d..263fd7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,23 +21,25 @@ ## v0.1.4 +- [ ] Synchronize the THP & PHP repls outputs +- [ ] Test semantic analysis +- [ ] Generate php code from current AST - [ ] Parse obj/map/dict syntax - [ ] Parse tuple syntax - [ ] Parse class instantiation syntax +- [ ] Parse logic operators `&& ||` +- [ ] Parse namespace operator `::` +- [ ] Implement subtyping for numbers ## v0.1.3 -- [ ] Test semantic analysis -- [ ] Generate php code from current AST - [x] Typecheck and semantic check simple assignment - [x] Test correct operator precedence - [x] Parse assignments - [x] Parse dot `.` operator -- [ ] Parse logic operators `&& ||` - [x] Parse Array access `arr[pos]` -- [ ] Parse namespace operator `::` -- [ ] Implement subtyping for numbers +- [x] Spawn a PHP repl, and connect the THP repl to it ## v0.1.2 diff --git a/Cargo.lock b/Cargo.lock index 3bfe35f..ab66920 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -152,7 +152,7 @@ dependencies = [ [[package]] name = "thp" -version = "0.1.2" +version = "0.1.3" dependencies = [ "ariadne", "colored", diff --git a/Cargo.toml b/Cargo.toml index 48c40f7..3921805 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "thp" -version = "0.1.2" +version = "0.1.3" edition = "2021" diff --git a/src/codegen/php/statement_list.rs b/src/codegen/php/statement_list.rs index 7455654..d21a978 100644 --- a/src/codegen/php/statement_list.rs +++ b/src/codegen/php/statement_list.rs @@ -12,6 +12,16 @@ impl Transpilable for PFile<'_> { } } +impl PFile<'_> { + pub fn transpile_without_header(&self) -> String { + let mut fragments = vec![]; + for statement in self.statements.iter() { + fragments.push(statement.transpile()); + } + fragments.join("\n") + } +} + #[cfg(test)] mod tests { use crate::{ diff --git a/src/repl/mod.rs b/src/repl/mod.rs index 747f189..d393f31 100755 --- a/src/repl/mod.rs +++ b/src/repl/mod.rs @@ -1,4 +1,7 @@ -use ::std::io::{self, Write}; +use ::std::io::{self, BufRead, BufReader, Error, Read, Write}; +use ::std::process::{Command, Stdio}; +use ::std::thread; +use ::std::time::Duration; use crate::codegen::Transpilable; use crate::error_handling::PrintableError; @@ -12,13 +15,95 @@ use crate::php_ast::transformers::PHPTransformable; /// Executes the REPL, reading from stdin, compiling and emitting PHP to stdout pub fn run() -> io::Result<()> { + // attempt to spawn a php repl + + let php_repl = Command::new("php") + .arg("-a") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn(); + let mut php_repl = match php_repl { + Ok(c) => c, + Err(error) => { + eprintln!("Couldn't open a PHP REPL session: {:?}", error); + return Err(error); + } + }; + + let mut php_stdin = match php_repl.stdin.take() { + Some(handle) => handle, + None => { + eprintln!("Error: couldn't get stdin handle from PHP REPL"); + return Err(Error::new( + io::ErrorKind::Other, + "Can't get PHP REPL stdin handle", + )); + } + }; + + let mut php_stdout = match php_repl.stdout.take() { + Some(h) => h, + None => { + eprintln!("Error: couldn't get stdout handle from PHP REPL"); + return Err(Error::new( + io::ErrorKind::Other, + "Can't get PHP REPL stdout handle", + )); + } + }; + let stdin = io::stdin(); let mut buffer = String::new(); let mut repl_symbol_table = SymbolTable::new(); std::populate(&mut repl_symbol_table); + // start a thread that prints whatever php sends back + let php_stdout_handle = thread::spawn(move || { + let mut reader = BufReader::new(php_stdout); + + loop { + // sleep for 50ms + thread::sleep(Duration::from_millis(50)); + + // read a line from php + let mut line = String::new(); + match reader.read_line(&mut line) { + Ok(n) => { + if n == 0 { + // EOF + break; + } + if n == 1 { + // just a newline + continue; + } + + // Suppress some php outputs + if line == "Interactive shell\n" { + continue; + } + // Ignore anything that starts with `php > ` + if line.starts_with("php > ") { + continue; + } + + print!("php output: `{line}`") + } + Err(error) => { + // log error and exit + eprint!("Error while reading from PHP STDOUT: {:?}", error); + break; + } + }; + } + + println!("php stdout thread finished"); + }); + println!("REPL: Enter expressions to evaluate. Type Ctrl-D to exit."); - loop { + let result = loop { + // TODO: syncronize the writes to thp stdout + // such that the php output doesnt overlap with this print!("> "); io::stdout().flush()?; buffer.clear(); @@ -30,18 +115,46 @@ pub fn run() -> io::Result<()> { break Ok(()); } Ok(_) => { - compile(&buffer, &mut repl_symbol_table); + match compile(&buffer, &mut repl_symbol_table) { + Some(php_code) => { + // TODO: this cant be efficient, fix + let php_code = format!("{php_code}\n"); + + // send php code + //println!("{php_code}"); + match php_stdin.write_all(php_code.as_bytes()) { + Ok(_) => {} + Err(error) => { + eprintln!("Error writing the generated code to the PHP process."); + break Err(error); + } + }; + + // the php repl should respond with its output, and that + // will be printed by another thread + } + None => {} + } } Err(error) => { eprintln!("Error reading stdin."); break Err(error); } }; - } + }; + + // kill the php process + php_repl.kill().expect("Couldnt KILL child php process..."); + + php_stdout_handle + .join() + .expect("STDOUT thread failed to join..."); + + result } -/// Full pipeline from THP source code to PHP output -fn compile(input: &String, symbol_table: &mut SymbolTable) { +/// Compiles THP code and returns the generated PHP code as a String +fn compile(input: &String, symbol_table: &mut SymbolTable) -> Option { // // Lexical analysis // @@ -49,7 +162,7 @@ fn compile(input: &String, symbol_table: &mut SymbolTable) { Ok(t) => t, Err(error) => { error.print_ariadne(input); - return; + return None; } }; @@ -60,7 +173,7 @@ fn compile(input: &String, symbol_table: &mut SymbolTable) { Ok(ast) => ast, Err(error) => { error.print_ariadne(input); - return; + return None; } }; @@ -72,7 +185,7 @@ fn compile(input: &String, symbol_table: &mut SymbolTable) { Ok(_) => {} Err(error) => { error.print_ariadne(input); - return; + return None; } } @@ -84,5 +197,5 @@ fn compile(input: &String, symbol_table: &mut SymbolTable) { // // Codegen // - println!("{}", php_ast.transpile()); + Some(php_ast.transpile_without_header()) }