Implement Patch transpiling

This commit is contained in:
Shunsuke Shibayama 2022-12-03 13:31:05 +09:00
parent 15a7be482c
commit 5e1d5b4523
2 changed files with 274 additions and 197 deletions

View file

@ -47,8 +47,8 @@ use AccessKind::*;
/// patch method -> function /// patch method -> function
/// patch attr -> variable /// patch attr -> variable
fn debind(name: Option<&str>) -> Option<Str> { fn debind(ident: &Identifier) -> Option<Str> {
match name { match ident.vi.py_name.as_ref().map(|s| &s[..]) {
Some(name) if name.starts_with("Function::") => { Some(name) if name.starts_with("Function::") => {
Some(Str::from(name.replace("Function::", ""))) Some(Str::from(name.replace("Function::", "")))
} }
@ -875,7 +875,7 @@ impl PyCodeGenerator {
if is_record { if is_record {
a.ident.dot = Some(DOT); a.ident.dot = Some(DOT);
} }
if let Some(varname) = debind(a.ident.vi.py_name.as_ref().map(|s| &s[..])) { if let Some(varname) = debind(&a.ident) {
a.ident.dot = None; a.ident.dot = None;
a.ident.name = VarName::from_str(varname); a.ident.name = VarName::from_str(varname);
self.emit_load_name_instr(a.ident); self.emit_load_name_instr(a.ident);
@ -2012,7 +2012,7 @@ impl PyCodeGenerator {
} else { } else {
return self.emit_call_update_310(obj, args); return self.emit_call_update_310(obj, args);
} }
} else if let Some(func_name) = debind(method_name.vi.py_name.as_ref().map(|s| &s[..])) { } else if let Some(func_name) = debind(&method_name) {
return self.emit_call_fake_method(obj, func_name, method_name, args); return self.emit_call_fake_method(obj, func_name, method_name, args);
} }
let is_py_api = obj.is_py_api(); let is_py_api = obj.is_py_api();

View file

@ -17,8 +17,8 @@ use crate::context::{Context, ContextProvider};
use crate::desugar_hir::HIRDesugarer; use crate::desugar_hir::HIRDesugarer;
use crate::error::{CompileError, CompileErrors}; use crate::error::{CompileError, CompileErrors};
use crate::hir::{ use crate::hir::{
Accessor, Array, Block, Call, ClassDef, Def, Dict, Expr, Identifier, Lambda, Params, Set, Accessor, Args, Array, BinOp, Block, Call, ClassDef, Def, Dict, Expr, Identifier, Lambda,
Signature, Tuple, HIR, Params, PatchDef, Record, Set, Signature, Tuple, HIR,
}; };
use crate::link::Linker; use crate::link::Linker;
use crate::mod_cache::SharedModuleCache; use crate::mod_cache::SharedModuleCache;
@ -26,6 +26,30 @@ use crate::ty::value::ValueObj;
use crate::ty::Type; use crate::ty::Type;
use crate::varinfo::VarInfo; use crate::varinfo::VarInfo;
/// patch method -> function
/// patch attr -> variable
fn debind(ident: &Identifier) -> Option<Str> {
match ident.vi.py_name.as_ref().map(|s| &s[..]) {
Some(name) if name.starts_with("Function::") => {
Some(Str::from(name.replace("Function::", "")))
}
Some(patch_method) if patch_method.contains("::") || patch_method.contains('.') => {
if ident.vis().is_private() {
Some(Str::from(format!("{patch_method}__")))
} else {
Some(Str::rc(patch_method))
}
}
_ => None,
}
}
fn demangle(name: &str) -> String {
name.trim_start_matches("::<module>")
.replace("::", "__")
.replace('.', "_")
}
#[derive(Debug)] #[derive(Debug)]
pub enum LastLineOperation { pub enum LastLineOperation {
Discard, Discard,
@ -267,11 +291,135 @@ impl ScriptGenerator {
match expr { match expr {
Expr::Lit(lit) => lit.token.content.to_string(), Expr::Lit(lit) => lit.token.content.to_string(),
Expr::Call(call) => self.transpile_call(call), Expr::Call(call) => self.transpile_call(call),
Expr::BinOp(bin) => match bin.op.kind { Expr::BinOp(bin) => self.transpile_binop(bin),
TokenKind::Closed Expr::UnaryOp(unary) => {
| TokenKind::LeftOpen let mut code = "(".to_string();
| TokenKind::RightOpen if unary.op.kind != TokenKind::Mutate {
| TokenKind::Open => { code += &unary.op.content;
}
code += &self.transpile_expr(*unary.expr);
code += ")";
code
}
Expr::Array(array) => match array {
Array::Normal(arr) => {
let mut code = "[".to_string();
for elem in arr.elems.pos_args {
code += &format!("{},", self.transpile_expr(elem.expr));
}
code += "]";
code
}
other => todo!("transpiling {other}"),
},
Expr::Set(set) => match set {
Set::Normal(st) => {
let mut code = "{".to_string();
for elem in st.elems.pos_args {
code += &format!("{},", self.transpile_expr(elem.expr));
}
code += "}";
code
}
other => todo!("transpiling {other}"),
},
Expr::Record(rec) => self.transpile_record(rec),
Expr::Tuple(tuple) => match tuple {
Tuple::Normal(tup) => {
let mut code = "(".to_string();
for elem in tup.elems.pos_args {
code += &format!("{},", self.transpile_expr(elem.expr));
}
code += ")";
code
}
},
Expr::Dict(dict) => match dict {
Dict::Normal(dic) => {
let mut code = "{".to_string();
for kv in dic.kvs {
code += &format!(
"({}): ({}),",
self.transpile_expr(kv.key),
self.transpile_expr(kv.value)
);
}
code += "}";
code
}
other => todo!("transpiling {other}"),
},
Expr::Accessor(acc) => self.transpile_acc(acc),
Expr::Def(def) => self.transpile_def(def),
Expr::Lambda(lambda) => self.transpile_lambda(lambda),
Expr::ClassDef(classdef) => self.transpile_classdef(classdef),
Expr::PatchDef(patchdef) => self.transpile_patchdef(patchdef),
Expr::AttrDef(mut adef) => {
let mut code = format!("{} = ", self.transpile_expr(Expr::Accessor(adef.attr)));
if adef.block.len() > 1 {
let name = format!("instant_block_{}__", self.fresh_var_n);
self.fresh_var_n += 1;
let mut code = format!("def {name}():\n");
code += &self.transpile_block(adef.block, Return);
self.prelude += &code;
format!("{name}()")
} else {
let expr = adef.block.remove(0);
code += &self.transpile_expr(expr);
code
}
}
// TODO:
Expr::Compound(comp) => {
let mut code = "".to_string();
for expr in comp.into_iter() {
code += &self.transpile_expr(expr);
code += &format!("\n{}", " ".repeat(self.level));
}
code
}
Expr::Import(acc) => {
let full_name = Str::from(acc.show());
let root = PyCodeGenerator::get_root(&acc);
self.prelude += &format!(
"{} = __import__(\"{full_name}\")\n",
Self::transpile_ident(root)
);
String::new()
}
other => todo!("transpile {other}"),
}
}
fn transpile_record(&mut self, rec: Record) -> String {
if !self.namedtuple_loaded {
self.load_namedtuple();
self.namedtuple_loaded = true;
}
let mut attrs = "[".to_string();
let mut values = "(".to_string();
for mut attr in rec.attrs.into_iter() {
attrs += &format!("'{}',", Self::transpile_ident(attr.sig.into_ident()));
if attr.body.block.len() > 1 {
let name = format!("instant_block_{}__", self.fresh_var_n);
self.fresh_var_n += 1;
let mut code = format!("def {name}():\n");
code += &self.transpile_block(attr.body.block, Return);
self.prelude += &code;
values += &format!("{name}(),");
} else {
let expr = attr.body.block.remove(0);
values += &format!("{},", self.transpile_expr(expr));
}
}
attrs += "]";
values += ")";
format!("NamedTuple__('Record', {attrs}){values}")
}
fn transpile_binop(&mut self, bin: BinOp) -> String {
match bin.op.kind {
TokenKind::Closed | TokenKind::LeftOpen | TokenKind::RightOpen | TokenKind::Open => {
if !self.range_ops_loaded { if !self.range_ops_loaded {
self.load_range_ops(); self.load_range_ops();
self.range_ops_loaded = true; self.range_ops_loaded = true;
@ -312,89 +460,11 @@ impl ScriptGenerator {
code += ")"; code += ")";
code code
} }
},
Expr::UnaryOp(unary) => {
let mut code = "(".to_string();
if unary.op.kind != TokenKind::Mutate {
code += &unary.op.content;
}
code += &self.transpile_expr(*unary.expr);
code += ")";
code
}
Expr::Array(array) => match array {
Array::Normal(arr) => {
let mut code = "[".to_string();
for elem in arr.elems.pos_args {
code += &format!("{},", self.transpile_expr(elem.expr));
}
code += "]";
code
}
other => todo!("transpiling {other}"),
},
Expr::Set(set) => match set {
Set::Normal(st) => {
let mut code = "{".to_string();
for elem in st.elems.pos_args {
code += &format!("{},", self.transpile_expr(elem.expr));
}
code += "}";
code
}
other => todo!("transpiling {other}"),
},
Expr::Record(rec) => {
if !self.namedtuple_loaded {
self.load_namedtuple();
self.namedtuple_loaded = true;
}
let mut attrs = "[".to_string();
let mut values = "(".to_string();
for mut attr in rec.attrs.into_iter() {
attrs += &format!("'{}',", Self::transpile_ident(attr.sig.into_ident()));
if attr.body.block.len() > 1 {
let name = format!("instant_block_{}__", self.fresh_var_n);
self.fresh_var_n += 1;
let mut code = format!("def {name}():\n");
code += &self.transpile_block(attr.body.block, Return);
self.prelude += &code;
values += &format!("{name}(),");
} else {
let expr = attr.body.block.remove(0);
values += &format!("{},", self.transpile_expr(expr));
} }
} }
attrs += "]";
values += ")"; fn transpile_acc(&mut self, acc: Accessor) -> String {
format!("NamedTuple__('Record', {attrs}){values}") match acc {
}
Expr::Tuple(tuple) => match tuple {
Tuple::Normal(tup) => {
let mut code = "(".to_string();
for elem in tup.elems.pos_args {
code += &format!("{},", self.transpile_expr(elem.expr));
}
code += ")";
code
}
},
Expr::Dict(dict) => match dict {
Dict::Normal(dic) => {
let mut code = "{".to_string();
for kv in dic.kvs {
code += &format!(
"({}): ({}),",
self.transpile_expr(kv.key),
self.transpile_expr(kv.value)
);
}
code += "}";
code
}
other => todo!("transpiling {other}"),
},
Expr::Accessor(acc) => match acc {
Accessor::Ident(ident) => { Accessor::Ident(ident) => {
match &ident.inspect()[..] { match &ident.inspect()[..] {
"Str" | "Bool" | "Nat" | "Array" if !self.builtin_types_loaded => { "Str" | "Bool" | "Nat" | "Array" if !self.builtin_types_loaded => {
@ -406,51 +476,17 @@ impl ScriptGenerator {
Self::transpile_ident(ident) Self::transpile_ident(ident)
} }
Accessor::Attr(attr) => { Accessor::Attr(attr) => {
if let Some(name) = debind(&attr.ident) {
demangle(&name)
} else {
format!( format!(
"({}).{}", "({}).{}",
self.transpile_expr(*attr.obj), self.transpile_expr(*attr.obj),
Self::transpile_ident(attr.ident) Self::transpile_ident(attr.ident)
) )
} }
},
Expr::Def(def) => self.transpile_def(def),
Expr::Lambda(lambda) => self.transpile_lambda(lambda),
Expr::ClassDef(classdef) => self.transpile_classdef(classdef),
Expr::AttrDef(mut adef) => {
let mut code = format!("{} = ", self.transpile_expr(Expr::Accessor(adef.attr)));
if adef.block.len() > 1 {
let name = format!("instant_block_{}__", self.fresh_var_n);
self.fresh_var_n += 1;
let mut code = format!("def {name}():\n");
code += &self.transpile_block(adef.block, Return);
self.prelude += &code;
format!("{name}()")
} else {
let expr = adef.block.remove(0);
code += &self.transpile_expr(expr);
code
} }
} }
// TODO:
Expr::Compound(comp) => {
let mut code = "".to_string();
for expr in comp.into_iter() {
code += &self.transpile_expr(expr);
code += &format!("\n{}", " ".repeat(self.level));
}
code
}
Expr::Import(acc) => {
let full_name = Str::from(acc.show());
let root = PyCodeGenerator::get_root(&acc);
self.prelude += &format!(
"{} = __import__(\"{full_name}\")\n",
Self::transpile_ident(root)
);
String::new()
}
other => todo!("transpile {other:?}"),
}
} }
fn transpile_call(&mut self, mut call: Call) -> String { fn transpile_call(&mut self, mut call: Call) -> String {
@ -462,7 +498,33 @@ impl ScriptGenerator {
} }
code code
} }
Some("if" | "if!") => { Some("not") => format!("(not ({}))", self.transpile_expr(call.args.remove(0))),
Some("if" | "if!") => self.transpile_if(call),
Some("for" | "for!") => {
let mut code = "for ".to_string();
let iter = call.args.remove(0);
let Expr::Lambda(block) = call.args.remove(0) else { todo!() };
let sig = block.params.non_defaults.get(0).unwrap();
let ParamPattern::VarName(param) = &sig.pat else { todo!() };
code += &format!("{}__ ", &param.token().content);
code += &format!("in {}:\n", self.transpile_expr(iter));
code += &self.transpile_block(block.body, Discard);
code
}
Some("while" | "while!") => {
let mut code = "while ".to_string();
let cond = call.args.remove(0);
let Expr::Lambda(block) = call.args.remove(0) else { todo!() };
code += &format!("{}:\n", self.transpile_expr(cond));
code += &self.transpile_block(block.body, Discard);
code
}
Some("match" | "match!") => self.transpile_match(call),
_ => self.transpile_simple_call(call),
}
}
fn transpile_if(&mut self, mut call: Call) -> String {
let cond = self.transpile_expr(call.args.remove(0)); let cond = self.transpile_expr(call.args.remove(0));
let Expr::Lambda(mut then_block) = call.args.remove(0) else { todo!() }; let Expr::Lambda(mut then_block) = call.args.remove(0) else { todo!() };
let else_block = call.args.try_remove(0).map(|ex| { let else_block = call.args.try_remove(0).map(|ex| {
@ -508,26 +570,8 @@ impl ScriptGenerator {
// This is a very bad design, but can be used for this code // This is a very bad design, but can be used for this code
format!("{tmp_func}()") format!("{tmp_func}()")
} }
Some("for" | "for!") => {
let mut code = "for ".to_string(); fn transpile_match(&mut self, mut call: Call) -> String {
let iter = call.args.remove(0);
let Expr::Lambda(block) = call.args.remove(0) else { todo!() };
let sig = block.params.non_defaults.get(0).unwrap();
let ParamPattern::VarName(param) = &sig.pat else { todo!() };
code += &format!("{}__ ", &param.token().content);
code += &format!("in {}:\n", self.transpile_expr(iter));
code += &self.transpile_block(block.body, Discard);
code
}
Some("while" | "while!") => {
let mut code = "while ".to_string();
let cond = call.args.remove(0);
let Expr::Lambda(block) = call.args.remove(0) else { todo!() };
code += &format!("{}:\n", self.transpile_expr(cond));
code += &self.transpile_block(block.body, Discard);
code
}
Some("match" | "match!") => {
let tmp = Str::from(format!("match_tmp_{}__", self.fresh_var_n)); let tmp = Str::from(format!("match_tmp_{}__", self.fresh_var_n));
self.fresh_var_n += 1; self.fresh_var_n += 1;
let tmp_func = Str::from(format!("match_tmp_func_{}__", self.fresh_var_n)); let tmp_func = Str::from(format!("match_tmp_func_{}__", self.fresh_var_n));
@ -574,13 +618,19 @@ impl ScriptGenerator {
self.prelude += &code; self.prelude += &code;
format!("{tmp_func}()") format!("{tmp_func}()")
} }
_ => self.transpile_simple_call(call),
}
}
fn transpile_simple_call(&mut self, mut call: Call) -> String { fn transpile_simple_call(&mut self, call: Call) -> String {
let is_py_api = if let Some(attr) = &call.attr_name { let is_py_api = if let Some(attr) = &call.attr_name {
attr.is_py_api() let is_py_api = attr.is_py_api();
if let Some(name) = debind(attr) {
let name = demangle(&name);
return format!(
"{name}({}, {})",
self.transpile_expr(*call.obj),
self.transpile_args(call.args, is_py_api, false)
);
}
is_py_api
} else { } else {
call.obj.is_py_api() call.obj.is_py_api()
}; };
@ -588,12 +638,20 @@ impl ScriptGenerator {
if let Some(attr) = call.attr_name { if let Some(attr) = call.attr_name {
code += &format!(".{}", Self::transpile_ident(attr)); code += &format!(".{}", Self::transpile_ident(attr));
} }
code += &self.transpile_args(call.args, is_py_api, true);
code
}
fn transpile_args(&mut self, mut args: Args, is_py_api: bool, paren: bool) -> String {
let mut code = String::new();
if paren {
code.push('('); code.push('(');
while let Some(arg) = call.args.try_remove_pos(0) { }
while let Some(arg) = args.try_remove_pos(0) {
code += &self.transpile_expr(arg.expr); code += &self.transpile_expr(arg.expr);
code.push(','); code.push(',');
} }
while let Some(arg) = call.args.try_remove_kw(0) { while let Some(arg) = args.try_remove_kw(0) {
let escape = if is_py_api { "" } else { "__" }; let escape = if is_py_api { "" } else { "__" };
code += &format!( code += &format!(
"{}{escape}={},", "{}{escape}={},",
@ -601,13 +659,15 @@ impl ScriptGenerator {
self.transpile_expr(arg.expr) self.transpile_expr(arg.expr)
); );
} }
if paren {
code.push(')'); code.push(')');
}
code code
} }
fn transpile_ident(ident: Identifier) -> String { fn transpile_ident(ident: Identifier) -> String {
if let Some(py_name) = ident.vi.py_name { if let Some(py_name) = ident.vi.py_name {
py_name.to_string() demangle(&py_name)
} else if ident.dot.is_some() { } else if ident.dot.is_some() {
ident.name.into_token().content.to_string() ident.name.into_token().content.to_string()
} else { } else {
@ -732,4 +792,21 @@ impl ScriptGenerator {
code += &self.transpile_block(classdef.methods, Discard); code += &self.transpile_block(classdef.methods, Discard);
code code
} }
fn transpile_patchdef(&mut self, patch_def: PatchDef) -> String {
let mut code = String::new();
for chunk in patch_def.methods.into_iter() {
let Expr::Def(mut def) = chunk else { todo!() };
let name = format!(
"{}{}",
demangle(&patch_def.sig.ident().to_string_without_type()),
demangle(&def.sig.ident().to_string_without_type()),
);
def.sig.ident_mut().name = VarName::from_str(Str::from(name));
code += &" ".repeat(self.level);
code += &self.transpile_def(def);
code.push('\n');
}
code
}
} }