Update transpiler

This commit is contained in:
Shunsuke Shibayama 2022-12-02 23:56:39 +09:00
parent b433a703dc
commit 5e29de9b53
3 changed files with 203 additions and 44 deletions

View file

@ -23,9 +23,7 @@ use erg_common::{
debug_power_assert, enum_unwrap, fn_name, fn_name_full, impl_stream_for_wrapper, log, debug_power_assert, enum_unwrap, fn_name, fn_name_full, impl_stream_for_wrapper, log,
switch_unreachable, switch_unreachable,
}; };
use erg_parser::ast::ConstExpr; use erg_parser::ast::{DefId, DefKind};
use erg_parser::ast::DefId;
use erg_parser::ast::DefKind;
use CommonOpcode::*; use CommonOpcode::*;
use erg_parser::ast::PreDeclTypeSpec; use erg_parser::ast::PreDeclTypeSpec;
@ -36,7 +34,6 @@ use erg_parser::token::EQUAL;
use erg_parser::token::{Token, TokenKind}; use erg_parser::token::{Token, TokenKind};
use crate::compile::{AccessKind, Name, StoreLoadKind}; use crate::compile::{AccessKind, Name, StoreLoadKind};
use crate::context::eval::type_from_token_kind;
use crate::error::CompileError; use crate::error::CompileError;
use crate::hir::{ use crate::hir::{
Accessor, Args, Array, AttrDef, BinOp, Block, Call, ClassDef, Def, DefBody, Expr, Identifier, Accessor, Args, Array, AttrDef, BinOp, Block, Call, ClassDef, Def, DefBody, Expr, Identifier,
@ -1741,16 +1738,7 @@ impl PyCodeGenerator {
#[allow(clippy::single_match)] #[allow(clippy::single_match)]
match t_spec { match t_spec {
TypeSpec::Enum(enum_t) => { TypeSpec::Enum(enum_t) => {
let elems = enum_t let elems = ValueObj::vec_from_const_args(enum_t);
.deconstruct()
.0
.into_iter()
.map(|elem| {
let ConstExpr::Lit(lit) = elem.expr else { todo!() };
let t = type_from_token_kind(lit.token.kind);
ValueObj::from_str(t, lit.token.content).unwrap()
})
.collect::<Vec<_>>();
self.emit_load_const(elems); self.emit_load_const(elems);
self.write_instr(CONTAINS_OP); self.write_instr(CONTAINS_OP);
self.write_arg(0); self.write_arg(0);
@ -2249,7 +2237,7 @@ impl PyCodeGenerator {
debug_assert_eq!(self.stack_len(), init_stack_len + 1); debug_assert_eq!(self.stack_len(), init_stack_len + 1);
} }
fn get_root(acc: &Accessor) -> Identifier { pub(crate) fn get_root(acc: &Accessor) -> Identifier {
match acc { match acc {
Accessor::Ident(ident) => ident.clone(), Accessor::Ident(ident) => ident.clone(),
Accessor::Attr(attr) => { Accessor::Attr(attr) => {

View file

@ -7,11 +7,12 @@ use erg_common::log;
use erg_common::traits::{Runnable, Stream}; use erg_common::traits::{Runnable, Stream};
use erg_common::Str; use erg_common::Str;
use erg_parser::ast::{ParamPattern, VarName}; use erg_parser::ast::{ParamPattern, TypeSpec, VarName};
use erg_parser::token::TokenKind; use erg_parser::token::TokenKind;
use crate::artifact::{CompleteArtifact, ErrorArtifact}; use crate::artifact::{CompleteArtifact, ErrorArtifact};
use crate::build_hir::HIRBuilder; use crate::build_hir::HIRBuilder;
use crate::codegen::PyCodeGenerator;
use crate::context::{Context, ContextProvider}; 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};
@ -21,9 +22,29 @@ use crate::hir::{
}; };
use crate::link::Linker; use crate::link::Linker;
use crate::mod_cache::SharedModuleCache; use crate::mod_cache::SharedModuleCache;
use crate::ty::value::ValueObj;
use crate::ty::Type; use crate::ty::Type;
use crate::varinfo::VarInfo; use crate::varinfo::VarInfo;
#[derive(Debug)]
pub enum LastLineOperation {
Discard,
Return,
StoreTmp(Str),
}
use LastLineOperation::*;
impl LastLineOperation {
pub const fn is_return(&self) -> bool {
matches!(self, LastLineOperation::Return)
}
pub const fn is_store_tmp(&self) -> bool {
matches!(self, LastLineOperation::StoreTmp(_))
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct PyScript { pub struct PyScript {
pub filename: Str, pub filename: Str,
@ -164,7 +185,9 @@ pub struct ScriptGenerator {
level: usize, level: usize,
fresh_var_n: usize, fresh_var_n: usize,
namedtuple_loaded: bool, namedtuple_loaded: bool,
in_op_loaded: bool,
range_ops_loaded: bool, range_ops_loaded: bool,
builtin_types_loaded: bool,
prelude: String, prelude: String,
} }
@ -174,7 +197,9 @@ impl ScriptGenerator {
level: 0, level: 0,
fresh_var_n: 0, fresh_var_n: 0,
namedtuple_loaded: false, namedtuple_loaded: false,
in_op_loaded: false,
range_ops_loaded: false, range_ops_loaded: false,
builtin_types_loaded: false,
prelude: String::new(), prelude: String::new(),
} }
} }
@ -196,9 +221,11 @@ impl ScriptGenerator {
fn replace_import(src: &str) -> String { fn replace_import(src: &str) -> String {
src.replace("from _erg_nat import Nat", "") src.replace("from _erg_nat import Nat", "")
.replace("from _erg_result import Error", "") .replace("from _erg_result import Error", "")
.replace("from _erg_result import is_ok", "")
.replace("from _erg_bool import Bool", "") .replace("from _erg_bool import Bool", "")
.replace("from _erg_str import Str", "") .replace("from _erg_str import Str", "")
.replace("from _erg_array import Array", "") .replace("from _erg_array import Array", "")
.replace("from _erg_range import Range", "")
} }
fn load_namedtuple(&mut self) { fn load_namedtuple(&mut self) {
@ -213,6 +240,29 @@ impl ScriptGenerator {
self.prelude += &Self::replace_import(include_str!("lib/std/_erg_range.py")); self.prelude += &Self::replace_import(include_str!("lib/std/_erg_range.py"));
} }
fn load_in_op(&mut self) {
self.prelude += &Self::replace_import(include_str!("lib/std/_erg_result.py"));
self.prelude += &Self::replace_import(include_str!("lib/std/_erg_range.py"));
self.prelude += &Self::replace_import(include_str!("lib/std/_erg_in_operator.py"));
}
fn load_builtin_types(&mut self) {
if self.range_ops_loaded {
self.prelude += &Self::replace_import(include_str!("lib/std/_erg_array.py"));
} else if self.in_op_loaded {
self.prelude += &Self::replace_import(include_str!("lib/std/_erg_nat.py"));
self.prelude += &Self::replace_import(include_str!("lib/std/_erg_bool.py"));
self.prelude += &Self::replace_import(include_str!("lib/std/_erg_str.py"));
self.prelude += &Self::replace_import(include_str!("lib/std/_erg_array.py"));
} else {
self.prelude += &Self::replace_import(include_str!("lib/std/_erg_result.py"));
self.prelude += &Self::replace_import(include_str!("lib/std/_erg_nat.py"));
self.prelude += &Self::replace_import(include_str!("lib/std/_erg_bool.py"));
self.prelude += &Self::replace_import(include_str!("lib/std/_erg_str.py"));
self.prelude += &Self::replace_import(include_str!("lib/std/_erg_array.py"));
}
}
fn transpile_expr(&mut self, expr: Expr) -> String { fn transpile_expr(&mut self, expr: Expr) -> String {
match expr { match expr {
Expr::Lit(lit) => lit.token.content.to_string(), Expr::Lit(lit) => lit.token.content.to_string(),
@ -240,10 +290,24 @@ impl ScriptGenerator {
code.push(')'); code.push(')');
code code
} }
TokenKind::InOp => {
if !self.in_op_loaded {
self.load_in_op();
self.in_op_loaded = true;
}
let mut code = "in_operator(".to_string();
code += &self.transpile_expr(*bin.lhs);
code.push(',');
code += &self.transpile_expr(*bin.rhs);
code.push(')');
code
}
_ => { _ => {
let mut code = "(".to_string(); let mut code = "(".to_string();
code += &self.transpile_expr(*bin.lhs); code += &self.transpile_expr(*bin.lhs);
code.push(' ');
code += &bin.op.content; code += &bin.op.content;
code.push(' ');
code += &self.transpile_expr(*bin.rhs); code += &self.transpile_expr(*bin.rhs);
code += ")"; code += ")";
code code
@ -293,7 +357,7 @@ impl ScriptGenerator {
let name = format!("instant_block_{}__", self.fresh_var_n); let name = format!("instant_block_{}__", self.fresh_var_n);
self.fresh_var_n += 1; self.fresh_var_n += 1;
let mut code = format!("def {name}():\n"); let mut code = format!("def {name}():\n");
code += &self.transpile_block(attr.body.block, true); code += &self.transpile_block(attr.body.block, Return);
self.prelude += &code; self.prelude += &code;
values += &format!("{name}(),"); values += &format!("{name}(),");
} else { } else {
@ -331,7 +395,16 @@ impl ScriptGenerator {
other => todo!("transpiling {other}"), other => todo!("transpiling {other}"),
}, },
Expr::Accessor(acc) => match acc { Expr::Accessor(acc) => match acc {
Accessor::Ident(ident) => Self::transpile_ident(ident), Accessor::Ident(ident) => {
match &ident.inspect()[..] {
"Str" | "Bool" | "Nat" | "Array" if !self.builtin_types_loaded => {
self.load_builtin_types();
self.builtin_types_loaded = true;
}
_ => {}
}
Self::transpile_ident(ident)
}
Accessor::Attr(attr) => { Accessor::Attr(attr) => {
format!( format!(
"({}).{}", "({}).{}",
@ -349,7 +422,7 @@ impl ScriptGenerator {
let name = format!("instant_block_{}__", self.fresh_var_n); let name = format!("instant_block_{}__", self.fresh_var_n);
self.fresh_var_n += 1; self.fresh_var_n += 1;
let mut code = format!("def {name}():\n"); let mut code = format!("def {name}():\n");
code += &self.transpile_block(adef.block, true); code += &self.transpile_block(adef.block, Return);
self.prelude += &code; self.prelude += &code;
format!("{name}()") format!("{name}()")
} else { } else {
@ -367,7 +440,16 @@ impl ScriptGenerator {
} }
code code
} }
other => todo!("transpile {other}"), 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:?}"),
} }
} }
@ -382,14 +464,49 @@ impl ScriptGenerator {
} }
Some("if" | "if!") => { Some("if" | "if!") => {
let cond = self.transpile_expr(call.args.remove(0)); let cond = self.transpile_expr(call.args.remove(0));
let Expr::Lambda(mut block) = call.args.remove(0) else { todo!() }; let Expr::Lambda(mut then_block) = call.args.remove(0) else { todo!() };
let then = self.transpile_expr(block.body.remove(0)); let else_block = call.args.try_remove(0).map(|ex| {
if let Some(Expr::Lambda(mut block)) = call.args.try_remove(0) { if let Expr::Lambda(blk) = ex {
let els = self.transpile_expr(block.body.remove(0)); blk
format!("{then} if {cond} else {els}") } else {
} else { todo!()
format!("{then} if {cond} else None") }
});
if then_block.body.len() == 1
&& else_block
.as_ref()
.map(|blk| blk.body.len() == 1)
.unwrap_or(true)
{
let then = self.transpile_expr(then_block.body.remove(0));
if let Some(mut else_block) = else_block {
let els = self.transpile_expr(else_block.body.remove(0));
return format!("{then} if {cond} else {els}");
} else {
return format!("{then} if {cond} else None");
}
} }
let tmp = Str::from(format!("if_tmp_{}__", self.fresh_var_n));
self.fresh_var_n += 1;
let tmp_func = Str::from(format!("if_tmp_func_{}__", self.fresh_var_n));
self.fresh_var_n += 1;
let mut code = format!("def {tmp_func}():\n");
code += &" ".repeat(self.level);
code += "if ";
code += &format!("{cond}:\n");
log!(err "{then_block}");
code += &self.transpile_block(then_block.body, StoreTmp(tmp.clone()));
if let Some(else_block) = else_block {
code += &" ".repeat(self.level);
code += "else:\n";
code += &self.transpile_block(else_block.body, StoreTmp(tmp.clone()));
}
code += &" ".repeat(self.level);
code += &format!("return {tmp}\n");
self.prelude += &code;
// NOTE: In Python, the variable environment of a function is determined at call time
// This is a very bad design, but can be used for this code
format!("{tmp_func}()")
} }
Some("for" | "for!") => { Some("for" | "for!") => {
let mut code = "for ".to_string(); let mut code = "for ".to_string();
@ -399,7 +516,7 @@ impl ScriptGenerator {
let ParamPattern::VarName(param) = &sig.pat else { todo!() }; let ParamPattern::VarName(param) = &sig.pat else { todo!() };
code += &format!("{}__ ", &param.token().content); code += &format!("{}__ ", &param.token().content);
code += &format!("in {}:\n", self.transpile_expr(iter)); code += &format!("in {}:\n", self.transpile_expr(iter));
code += &self.transpile_block(block.body, false); code += &self.transpile_block(block.body, Discard);
code code
} }
Some("while" | "while!") => { Some("while" | "while!") => {
@ -407,24 +524,55 @@ impl ScriptGenerator {
let cond = call.args.remove(0); let cond = call.args.remove(0);
let Expr::Lambda(block) = call.args.remove(0) else { todo!() }; let Expr::Lambda(block) = call.args.remove(0) else { todo!() };
code += &format!("{}:\n", self.transpile_expr(cond)); code += &format!("{}:\n", self.transpile_expr(cond));
code += &self.transpile_block(block.body, false); code += &self.transpile_block(block.body, Discard);
code code
} }
// TODO:
Some("match" | "match!") => { Some("match" | "match!") => {
let mut code = "match ".to_string(); let tmp = Str::from(format!("match_tmp_{}__", self.fresh_var_n));
self.fresh_var_n += 1;
let tmp_func = Str::from(format!("match_tmp_func_{}__", self.fresh_var_n));
self.fresh_var_n += 1;
let mut code = format!("def {tmp_func}():\n");
self.level += 1;
code += &" ".repeat(self.level);
code += "match ";
let cond = call.args.remove(0); let cond = call.args.remove(0);
code += &format!("{}:\n", self.transpile_expr(cond)); code += &format!("{}:\n", self.transpile_expr(cond));
while let Some(Expr::Lambda(arm)) = call.args.try_remove(0) { while let Some(Expr::Lambda(arm)) = call.args.try_remove(0) {
self.level += 1; self.level += 1;
code += &" ".repeat(self.level); code += &" ".repeat(self.level);
let target = arm.params.non_defaults.get(0).unwrap(); let target = arm.params.non_defaults.get(0).unwrap();
let ParamPattern::VarName(param) = &target.pat else { todo!() }; match &target.pat {
code += &format!("case {}__:\n", &param.token().content); ParamPattern::VarName(param) => {
code += &self.transpile_block(arm.body, false); code += &format!("case {}__:\n", &param.token().content);
self.level -= 1; code += &self.transpile_block(arm.body, StoreTmp(tmp.clone()));
self.level -= 1;
}
ParamPattern::Discard(_) => {
match target.t_spec.as_ref().map(|t| &t.t_spec) {
Some(TypeSpec::Enum(enum_t)) => {
let values = ValueObj::vec_from_const_args(enum_t.clone());
if values.len() == 1 {
code += &format!("case {}:\n", values[0]);
} else {
todo!()
}
}
Some(_) => todo!(),
None => {
code += "case _:\n";
}
}
code += &self.transpile_block(arm.body, StoreTmp(tmp.clone()));
self.level -= 1;
}
_ => todo!(),
}
} }
code code += &" ".repeat(self.level);
code += &format!("return {tmp}\n");
self.prelude += &code;
format!("{tmp_func}()")
} }
_ => self.transpile_simple_call(call), _ => self.transpile_simple_call(call),
} }
@ -483,14 +631,22 @@ impl ScriptGenerator {
code code
} }
fn transpile_block(&mut self, block: Block, return_last: bool) -> String { fn transpile_block(&mut self, block: Block, last_op: LastLineOperation) -> String {
self.level += 1; self.level += 1;
let mut code = String::new(); let mut code = String::new();
let last = block.len().saturating_sub(1); let last = block.len().saturating_sub(1);
for (i, chunk) in block.into_iter().enumerate() { for (i, chunk) in block.into_iter().enumerate() {
code += &" ".repeat(self.level); code += &" ".repeat(self.level);
if i == last && return_last { if i == last {
code += "return "; match last_op {
Return => {
code += "return ";
}
Discard => {}
StoreTmp(ref tmp) => {
code += &format!("{tmp} = ");
}
}
} }
code += &self.transpile_expr(chunk); code += &self.transpile_expr(chunk);
code.push('\n'); code.push('\n');
@ -504,12 +660,12 @@ impl ScriptGenerator {
let name = format!("lambda_{}__", self.fresh_var_n); let name = format!("lambda_{}__", self.fresh_var_n);
self.fresh_var_n += 1; self.fresh_var_n += 1;
let mut code = format!("def {name}({}):\n", self.transpile_params(lambda.params)); let mut code = format!("def {name}({}):\n", self.transpile_params(lambda.params));
code += &self.transpile_block(lambda.body, true); code += &self.transpile_block(lambda.body, Return);
self.prelude += &code; self.prelude += &code;
name name
} else { } else {
let mut code = format!("(lambda {}:", self.transpile_params(lambda.params)); let mut code = format!("(lambda {}:", self.transpile_params(lambda.params));
code += &self.transpile_block(lambda.body, false); code += &self.transpile_block(lambda.body, Discard);
code.pop(); // \n code.pop(); // \n
code.push(')'); code.push(')');
code code
@ -524,7 +680,7 @@ impl ScriptGenerator {
let name = format!("instant_block_{}__", self.fresh_var_n); let name = format!("instant_block_{}__", self.fresh_var_n);
self.fresh_var_n += 1; self.fresh_var_n += 1;
let mut code = format!("def {name}():\n"); let mut code = format!("def {name}():\n");
code += &self.transpile_block(def.body.block, true); code += &self.transpile_block(def.body.block, Return);
self.prelude += &code; self.prelude += &code;
format!("{name}()") format!("{name}()")
} else { } else {
@ -539,7 +695,7 @@ impl ScriptGenerator {
Self::transpile_ident(subr.ident), Self::transpile_ident(subr.ident),
self.transpile_params(subr.params) self.transpile_params(subr.params)
); );
code += &self.transpile_block(def.body.block, true); code += &self.transpile_block(def.body.block, Return);
code code
} }
} }
@ -569,7 +725,7 @@ impl ScriptGenerator {
if classdef.need_to_gen_new { if classdef.need_to_gen_new {
code += &format!("def new(x): return {class_name}.__call__(x)\n"); code += &format!("def new(x): return {class_name}.__call__(x)\n");
} }
code += &self.transpile_block(classdef.methods, false); code += &self.transpile_block(classdef.methods, Discard);
code code
} }
} }

View file

@ -19,6 +19,9 @@ use erg_common::shared::Shared;
use erg_common::vis::Field; use erg_common::vis::Field;
use erg_common::{dict, fmt_iter, impl_display_from_debug, switch_lang}; use erg_common::{dict, fmt_iter, impl_display_from_debug, switch_lang};
use erg_common::{RcArray, Str}; use erg_common::{RcArray, Str};
use erg_parser::ast::{ConstArgs, ConstExpr};
use crate::context::eval::type_from_token_kind;
use super::codeobj::CodeObj; use super::codeobj::CodeObj;
use super::constructors::{array_t, dict_t, mono, poly, refinement, set_t, tuple_t}; use super::constructors::{array_t, dict_t, mono, poly, refinement, set_t, tuple_t};
@ -731,6 +734,18 @@ impl ValueObj {
} }
} }
pub fn vec_from_const_args(args: ConstArgs) -> Vec<Self> {
args.deconstruct()
.0
.into_iter()
.map(|elem| {
let ConstExpr::Lit(lit) = elem.expr else { todo!() };
let t = type_from_token_kind(lit.token.kind);
ValueObj::from_str(t, lit.token.content).unwrap()
})
.collect::<Vec<_>>()
}
pub fn class(&self) -> Type { pub fn class(&self) -> Type {
match self { match self {
Self::Int(_) => Type::Int, Self::Int(_) => Type::Int,