feat: simple connection to a php repl

This commit is contained in:
Araozu 2024-11-05 21:18:58 -05:00
parent f3f6e9fd44
commit 73d31ea046
5 changed files with 142 additions and 17 deletions

View File

@ -21,23 +21,25 @@
## v0.1.4 ## v0.1.4
- [ ] Synchronize the THP & PHP repls outputs
- [ ] Test semantic analysis
- [ ] Generate php code from current AST
- [ ] Parse obj/map/dict syntax - [ ] Parse obj/map/dict syntax
- [ ] Parse tuple syntax - [ ] Parse tuple syntax
- [ ] Parse class instantiation syntax - [ ] Parse class instantiation syntax
- [ ] Parse logic operators `&& ||`
- [ ] Parse namespace operator `::`
- [ ] Implement subtyping for numbers
## v0.1.3 ## v0.1.3
- [ ] Test semantic analysis
- [ ] Generate php code from current AST
- [x] Typecheck and semantic check simple assignment - [x] Typecheck and semantic check simple assignment
- [x] Test correct operator precedence - [x] Test correct operator precedence
- [x] Parse assignments - [x] Parse assignments
- [x] Parse dot `.` operator - [x] Parse dot `.` operator
- [ ] Parse logic operators `&& ||`
- [x] Parse Array access `arr[pos]` - [x] Parse Array access `arr[pos]`
- [ ] Parse namespace operator `::` - [x] Spawn a PHP repl, and connect the THP repl to it
- [ ] Implement subtyping for numbers
## v0.1.2 ## v0.1.2

2
Cargo.lock generated
View File

@ -152,7 +152,7 @@ dependencies = [
[[package]] [[package]]
name = "thp" name = "thp"
version = "0.1.2" version = "0.1.3"
dependencies = [ dependencies = [
"ariadne", "ariadne",
"colored", "colored",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "thp" name = "thp"
version = "0.1.2" version = "0.1.3"
edition = "2021" edition = "2021"

View File

@ -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)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{

View File

@ -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::codegen::Transpilable;
use crate::error_handling::PrintableError; 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 /// Executes the REPL, reading from stdin, compiling and emitting PHP to stdout
pub fn run() -> io::Result<()> { 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 stdin = io::stdin();
let mut buffer = String::new(); let mut buffer = String::new();
let mut repl_symbol_table = SymbolTable::new(); let mut repl_symbol_table = SymbolTable::new();
std::populate(&mut repl_symbol_table); std::populate(&mut repl_symbol_table);
println!("REPL: Enter expressions to evaluate. Type Ctrl-D to exit."); // start a thread that prints whatever php sends back
let php_stdout_handle = thread::spawn(move || {
let mut reader = BufReader::new(php_stdout);
loop { 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.");
let result = loop {
// TODO: syncronize the writes to thp stdout
// such that the php output doesnt overlap with this
print!("> "); print!("> ");
io::stdout().flush()?; io::stdout().flush()?;
buffer.clear(); buffer.clear();
@ -30,18 +115,46 @@ pub fn run() -> io::Result<()> {
break Ok(()); break Ok(());
} }
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) => { Err(error) => {
eprintln!("Error reading stdin."); eprintln!("Error reading stdin.");
break Err(error); 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 /// Compiles THP code and returns the generated PHP code as a String
fn compile(input: &String, symbol_table: &mut SymbolTable) { fn compile(input: &String, symbol_table: &mut SymbolTable) -> Option<String> {
// //
// Lexical analysis // Lexical analysis
// //
@ -49,7 +162,7 @@ fn compile(input: &String, symbol_table: &mut SymbolTable) {
Ok(t) => t, Ok(t) => t,
Err(error) => { Err(error) => {
error.print_ariadne(input); error.print_ariadne(input);
return; return None;
} }
}; };
@ -60,7 +173,7 @@ fn compile(input: &String, symbol_table: &mut SymbolTable) {
Ok(ast) => ast, Ok(ast) => ast,
Err(error) => { Err(error) => {
error.print_ariadne(input); error.print_ariadne(input);
return; return None;
} }
}; };
@ -72,7 +185,7 @@ fn compile(input: &String, symbol_table: &mut SymbolTable) {
Ok(_) => {} Ok(_) => {}
Err(error) => { Err(error) => {
error.print_ariadne(input); error.print_ariadne(input);
return; return None;
} }
} }
@ -84,5 +197,5 @@ fn compile(input: &String, symbol_table: &mut SymbolTable) {
// //
// Codegen // Codegen
// //
println!("{}", php_ast.transpile()); Some(php_ast.transpile_without_header())
} }