phpc: goodbye

I'm removing the original phpc prototype and will be reimplementing a better
version as the Trunk Compiler project.
This commit is contained in:
Ryan Chandler 2022-08-04 20:07:25 +01:00
parent 2cad6fde6a
commit 82702d4070
No known key found for this signature in database
GPG Key ID: F113BCADDB3B0CCA
7 changed files with 0 additions and 387 deletions

View File

@ -1,11 +0,0 @@
[package]
name = "phpc"
version = "0.1.0"
edition = "2021"
[dependencies]
structopt = { version = "0.3.26", features = ["color"] }
trunk_parser = { path = "../trunk_parser" }
trunk_lexer = { path = "../trunk_lexer" }
rust-format = "0.3.4"
uuid = { version = "1.1.2", features = ["v4"] }

View File

@ -1,7 +0,0 @@
<?php
function get_name() {
return "Ryan";
}
echo get_name();

View File

@ -1,10 +0,0 @@
<?php
$guess = readline("Guess a number between 1 and 3: ");
$number = rand(1, 3);
if ($guess == $number) {
echo "You guessed the number correctly, well done!";
} else {
echo "The correct answer is " . $number . ". Better luck next time!";
}

View File

@ -1,157 +0,0 @@
use trunk_lexer::Lexer;
use trunk_parser::{Parser, Statement, Expression, InfixOp};
pub fn compile(file: String) -> Result<String, CompileError> {
let contents = match std::fs::read_to_string(file) {
Ok(contents) => contents,
Err(_) => return Err(CompileError::FailedToReadFile),
};
let mut lexer = Lexer::new(None);
let tokens = lexer.tokenize(&contents).unwrap();
let mut parser = Parser::new(None);
let ast = parser.parse(tokens).unwrap();
let (fns, main): (Vec<_>, Vec<_>) = ast.iter().partition(|statement| match statement {
Statement::Function { .. } => true,
_ => false,
});
let mut source = String::new();
source.push_str("mod runtime;\nuse runtime::*;");
for function in fns {
compile_function(function, &mut source)?;
}
source.push_str("fn main() {");
for statement in main {
compile_statement(statement, &mut source)?;
}
source.push('}');
Ok(source)
}
fn compile_function(function: &Statement, source: &mut String) -> Result<(), CompileError> {
let (name, params, body) = match function {
Statement::Function { name, params, body, .. } => (name, params, body),
_ => unreachable!(),
};
source.push_str("fn ");
source.push_str(&name.name);
source.push('(');
for param in params {
source.push_str(match &param.name {
Expression::Variable { name } => &name,
_ => unreachable!(),
});
source.push_str(": PhpValue, ");
}
source.push_str(") -> PhpValue {");
for statement in body {
compile_statement(statement, source)?;
}
source.push('}');
Ok(())
}
fn compile_statement(statement: &Statement, source: &mut String) -> Result<(), CompileError> {
match statement {
Statement::Return { value } => {
source.push_str("return");
if let Some(value) = value {
source.push(' ');
source.push_str(&compile_expression(value)?);
} else {
todo!();
}
source.push(';');
},
Statement::Echo { values } => {
for value in values {
source.push_str("_php_echo(");
source.push_str(&compile_expression(value)?);
source.push_str(");");
}
},
Statement::If { condition, then, else_ifs, r#else } => {
source.push_str("if ");
source.push_str(&compile_expression(condition)?);
source.push('{');
for statement in then {
compile_statement(statement, source)?;
}
source.push('}');
if let Some(r#else) = r#else {
source.push_str("else {");
for statement in r#else {
compile_statement(statement, source)?;
}
source.push('}');
}
},
Statement::Expression { expr } => {
source.push_str(&compile_expression(expr)?);
},
_ => todo!(),
};
Ok(())
}
fn compile_expression(expression: &Expression) -> Result<String, CompileError> {
let result = match expression {
Expression::ConstantString { value } => {
format!(r#"PhpValue::from("{}")"#, value)
},
Expression::Call { target, args } => {
let mut buffer = String::new();
buffer.push_str(&compile_expression(target)?);
buffer.push('(');
for arg in args {
buffer.push_str(&compile_expression(arg)?);
buffer.push_str(", ");
}
buffer.push(')');
buffer
},
Expression::Identifier { name } => name.to_string(),
Expression::Int { i } => format!("PhpValue::from({})", i),
Expression::Infix { lhs, op, rhs } => {
let lhs = compile_expression(lhs)?;
let rhs = compile_expression(rhs)?;
match op {
InfixOp::Assign => format!("let {} = {};", lhs, rhs),
InfixOp::Equals => format!("{}.eq(({}).clone())", lhs, rhs),
InfixOp::Concat => format!("_php_concat({}, {})", lhs, rhs),
_ => todo!(),
}
},
Expression::Variable { name } => name.to_string(),
_ => todo!(),
};
Ok(result)
}
#[derive(Debug)]
pub enum CompileError {
FailedToReadFile,
}

View File

@ -1,109 +0,0 @@
use std::process::{Command, exit};
use structopt::StructOpt;
use phpc::compile;
use uuid::Uuid;
#[derive(Debug, StructOpt)]
#[structopt(name = "phpc", about = "Compile a PHP script to Rust.")]
struct Args {
file: String,
}
fn main() {
let args = Args::from_args();
println!("> Compiling PHP script...");
let compiled = compile(args.file.clone()).unwrap();
let path = std::path::Path::new(&args.file);
let file_stem = path.file_stem().unwrap().to_str().unwrap();
let temp_dir = std::env::temp_dir();
let temp_path = format!("{}{}", temp_dir.to_str().unwrap(), Uuid::new_v4());
println!("> Initialising Cargo project in {}...", &temp_path);
std::fs::create_dir(&temp_path).unwrap();
let mut cmd = Command::new("cargo");
cmd.args(["init", ".", "--name", &file_stem])
.current_dir(&temp_path);
match cmd.output() {
Ok(o) => {
print!("{}", String::from_utf8_lossy(&o.stdout));
},
Err(e) => {
eprintln!("Failed to generate Cargo project. Error: {:?}", e);
exit(1);
},
};
let cargo_stub = include_str!("../../phpc_runtime/Cargo.toml").replace("phpc_runtime", file_stem);
println!("> Modifying Cargo configuration...");
match std::fs::write(format!("{}/Cargo.toml", &temp_path), cargo_stub) {
Ok(_) => {},
Err(e) => {
eprintln!("Failed to modify Cargo configuration. Error: {:?}", e);
exit(1);
},
};
let runtime_stub = include_str!("../../phpc_runtime/src/lib.rs");
println!("> Writing runtime module...");
match std::fs::write(format!("{}/src/runtime.rs", &temp_path), runtime_stub) {
Ok(_) => {},
Err(e) => {
eprintln!("Failed to write runtime library. Error: {:?}", e);
exit(1);
}
};
println!("> Writing compiled PHP code...");
match std::fs::write(format!("{}/src/main.rs", &temp_path), compiled) {
Ok(_) => {},
Err(e) => {
eprintln!("Failed to write compiled PHP code. Error: {:?}", e);
exit(1);
},
};
println!("> Compiling project with Cargo...");
let mut cmd = Command::new("cargo");
cmd.args(["build", "--release"])
.current_dir(&temp_path);
match cmd.output() {
Ok(o) => {
if o.status.success() {
print!("{}", String::from_utf8_lossy(&o.stdout));
} else {
print!("{}", String::from_utf8_lossy(&o.stderr));
}
},
Err(e) => {
eprintln!("Failed to compile project with Cargo. Error: {:?}", e);
exit(1);
},
};
let executable_path = format!("{}/target/release/{}", &temp_path, &file_stem);
match std::fs::copy(executable_path, format!("./{}", &file_stem)) {
Ok(_) => {
println!("> Executable copied.");
},
Err(e) => {
eprintln!("Failed to copy executable file. Error: {:?}", e);
exit(1);
},
};
}

View File

@ -1,7 +0,0 @@
[package]
name = "phpc_runtime"
version = "0.1.0"
edition = "2021"
[dependencies]
rand = "0.8.5"

View File

@ -1,86 +0,0 @@
use std::{fmt::Display, io::{Write, BufRead}};
use rand::Rng;
#[derive(Clone)]
pub enum PhpValue {
String(String),
Int(i64),
}
impl PhpValue {
pub fn eq(&self, other: Self) -> bool {
match (self, &other) {
(Self::Int(a), Self::String(b)) | (Self::String(b), Self::Int(a)) => match b.parse::<i64>() {
Ok(b) => *a == b,
_ => false,
},
_ => todo!(),
}
}
}
impl Into<i64> for PhpValue {
fn into(self) -> i64 {
match self {
Self::Int(i) => i,
_ => todo!(),
}
}
}
impl From<i64> for PhpValue {
fn from(value: i64) -> Self {
Self::Int(value)
}
}
impl From<String> for PhpValue {
fn from(value: String) -> Self {
Self::String(value)
}
}
impl From<&str> for PhpValue {
fn from(value: &str) -> Self {
Self::String(value.into())
}
}
impl Display for PhpValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::String(string) => write!(f, "{}", string),
Self::Int(i) => write!(f, "{}", i),
_ => todo!(),
}
}
}
pub fn _php_echo(value: PhpValue) {
print!("{value}");
}
pub fn _php_concat(left: PhpValue, right: PhpValue) -> PhpValue {
format!("{}{}", left, right).into()
}
// TODO: Make the `prompt` argument optional.
pub fn readline(prompt: PhpValue) -> PhpValue {
print!("{}", prompt);
std::io::stdout().flush().unwrap();
let mut result = String::new();
std::io::stdin().lock().read_line(&mut result).unwrap();
PhpValue::from(result.trim_end())
}
pub fn rand(from: PhpValue, to: PhpValue) -> PhpValue {
let from: i64 = from.into();
let to: i64 = to.into();
let mut rng = rand::thread_rng();
PhpValue::from(rng.gen_range(from..to))
}