mirror of
https://github.com/danog/parser.git
synced 2024-11-26 20:04:57 +01:00
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:
parent
2cad6fde6a
commit
82702d4070
@ -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"] }
|
@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
function get_name() {
|
||||
return "Ryan";
|
||||
}
|
||||
|
||||
echo get_name();
|
@ -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!";
|
||||
}
|
157
phpc/src/lib.rs
157
phpc/src/lib.rs
@ -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 ¶m.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,
|
||||
}
|
109
phpc/src/main.rs
109
phpc/src/main.rs
@ -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);
|
||||
},
|
||||
};
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
[package]
|
||||
name = "phpc_runtime"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
rand = "0.8.5"
|
@ -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))
|
||||
}
|
Loading…
Reference in New Issue
Block a user