Remove rust-defined python modules

This commit is contained in:
Shunsuke Shibayama 2022-10-19 01:50:14 +09:00
parent d932249ec3
commit e0d6b93abd
31 changed files with 451 additions and 327 deletions

View file

@ -118,7 +118,7 @@ impl Input {
}
}
pub fn resolve(&self, path: &Path) -> Result<PathBuf, std::io::Error> {
pub fn local_resolve(&self, path: &Path) -> Result<PathBuf, std::io::Error> {
let mut dir = if let Self::File(mut path) = self.clone() {
path.pop();
path
@ -127,10 +127,24 @@ impl Input {
};
dir.push(path);
dir.set_extension("er");
dir.canonicalize().or_else(|_| {
dir.canonicalize()
.or_else(|_| {
dir.pop();
dir.push(path);
dir.push("__init__.er"); // {path}/__init__.er
dir.canonicalize()
})
.or_else(|_| {
dir.pop(); // {path}
dir.set_extension("d.er");
dir.canonicalize()
})
.or_else(|_| {
dir.pop(); // {path}.d.er
dir.push(format!("{}.d", path.display())); // {path}.d
dir.push("__init__.d.er"); // {path}.d/__init__.d.er
dir.canonicalize()
})
}
}

View file

@ -12,6 +12,7 @@ use erg_common::config::{ErgConfig, Input};
use erg_common::env::erg_std_path;
use erg_common::error::{ErrorDisplay, Location};
use erg_common::opcode::Opcode;
use erg_common::option_enum_unwrap;
use erg_common::traits::{Locational, Stream};
use erg_common::Str;
use erg_common::{
@ -595,6 +596,10 @@ impl CodeGenerator {
log!(info "entered {} ({acc})", fn_name!());
match acc {
Accessor::Ident(ident) => {
if &ident.inspect()[..] == "#ModuleType" && !self.module_type_loaded {
self.load_module_type();
self.module_type_loaded = true;
}
self.emit_load_name_instr(ident);
}
Accessor::Attr(a) => {
@ -1250,6 +1255,7 @@ impl CodeGenerator {
fn emit_call(&mut self, call: Call) {
log!(info "entered {} ({call})", fn_name!());
// Python cannot distinguish at compile time between a method call and a attribute call
if let Some(attr_name) = call.attr_name {
self.emit_call_method(*call.obj, attr_name, call.args);
} else {
@ -1275,11 +1281,7 @@ impl CodeGenerator {
"if" | "if!" => self.emit_if_instr(args),
"match" | "match!" => self.emit_match_instr(args, true),
"with!" => self.emit_with_instr(args),
"import" => {
if !self.module_type_loaded {
self.load_module_type();
self.module_type_loaded = true;
}
"pyimport" | "py" => {
self.emit_load_name_instr(local);
self.emit_args(args, Name);
}
@ -1445,6 +1447,33 @@ impl CodeGenerator {
self.stack_dec_n((1 + attrs_len + 0) - 1);
}
fn get_root(acc: &Accessor) -> Identifier {
match acc {
Accessor::Ident(ident) => ident.clone(),
Accessor::Attr(attr) => {
if let Expr::Accessor(acc) = attr.obj.as_ref() {
Self::get_root(acc)
} else {
todo!("{:?}", attr.obj)
}
}
}
}
fn emit_import(&mut self, acc: Accessor) {
self.emit_load_const(0i32);
self.emit_load_const(ValueObj::None);
let full_name = Str::from(acc.show());
let name = self
.local_search(&full_name, Name)
.unwrap_or_else(|| self.register_name(full_name));
self.write_instr(IMPORT_NAME);
self.write_arg(name.idx as u8);
let root = Self::get_root(&acc);
self.emit_store_instr(root, Name);
self.stack_dec();
}
fn emit_expr(&mut self, expr: Expr) {
log!(info "entered {} ({expr})", fn_name!());
if expr.ln_begin().unwrap_or_else(|| panic!("{expr}")) > self.cur_block().prev_lineno {
@ -1574,15 +1603,31 @@ impl CodeGenerator {
self.emit_load_const(code);
}
Expr::Compound(chunks) => {
if !self.module_type_loaded {
let is_module_loading_chunks = chunks
.get(2)
.map(|chunk| {
option_enum_unwrap!(chunk, Expr::Call)
.map(|call| {
call.obj.show_acc().as_ref().map(|s| &s[..]) == Some("exec")
})
.unwrap_or(false)
})
.unwrap_or(false);
if !self.module_type_loaded && is_module_loading_chunks {
self.load_module_type();
self.module_type_loaded = true;
}
self.emit_frameless_block(chunks, vec![]);
for expr in chunks.into_iter() {
self.emit_expr(expr);
}
if is_module_loading_chunks {
self.stack_dec_n(2);
}
}
Expr::TypeAsc(tasc) => {
self.emit_expr(*tasc.expr);
}
Expr::Import(acc) => self.emit_import(acc),
other => {
CompileError::feature_error(self.cfg.input.clone(), other.loc(), "??", "".into())
.write_to_stderr();

View file

@ -1047,8 +1047,12 @@ impl Context {
);
generic_module.register_trait(g_module_t.clone(), generic_module_eq);
let module_t = module(mono_q_tp("Path"));
let py_module_t = py_module(mono_q_tp("Path"));
let mut module = Self::builtin_poly_class("Module", vec![PS::named_nd("Path", Str)], 2);
module.register_superclass(g_module_t.clone(), &generic_module);
let mut py_module =
Self::builtin_poly_class("PyModule", vec![PS::named_nd("Path", Str)], 2);
py_module.register_superclass(g_module_t.clone(), &generic_module);
/* Array */
let mut array_ =
Self::builtin_poly_class("Array", vec![PS::t_nd("T"), PS::named_nd("N", Nat)], 10);
@ -1536,7 +1540,8 @@ impl Context {
Const,
Some("ModuleType"),
);
self.register_builtin_type(module_t, module, Private, Const, Some("Module"));
self.register_builtin_type(module_t, module, Private, Const, Some("ModuleType"));
self.register_builtin_type(py_module_t, py_module, Private, Const, Some("ModuleType"));
self.register_builtin_type(arr_t, array_, Private, Const, Some("list"));
self.register_builtin_type(set_t, set_, Private, Const, Some("set"));
self.register_builtin_type(g_dict_t, generic_dict, Private, Const, Some("dict"));
@ -1708,7 +1713,7 @@ impl Context {
let t_pyimport = nd_func(
vec![anon(tp_enum(Str, set! {mono_q_tp("Path")}))],
None,
module(mono_q_tp("Path")),
py_module(mono_q_tp("Path")),
);
let t_pyimport = quant(t_pyimport, set! {static_instance("Path", Str)});
let t_quit = func(vec![], None, vec![kw("code", Int)], NoneType);

View file

@ -3,9 +3,5 @@ pub mod importlib;
pub mod io;
pub mod math;
pub mod os;
pub mod random;
pub mod re;
pub mod socket;
pub mod sys;
pub mod time;
pub mod urllib;

View file

@ -1,49 +0,0 @@
use erg_common::set;
use erg_common::vis::Visibility;
use crate::ty::constructors::{
kw, mono, mono_q, nd_proc, poly, proc, quant, static_instance, ty_tp,
};
use crate::ty::Type;
use Type::*;
use crate::context::Context;
use crate::varinfo::Mutability;
use Mutability::*;
use Visibility::*;
impl Context {
pub(crate) fn init_py_random_mod() -> Self {
let mut random = Context::builtin_module("random", 10);
random.register_builtin_py_impl(
"seed!",
proc(
vec![],
None,
vec![
kw("a", mono("Num")), // TODO: NoneType, int, float, str, bytes, bytearray
kw("version", Int),
],
NoneType,
),
Immutable,
Public,
Some("seed"),
);
random.register_builtin_py_impl(
"randint!",
nd_proc(vec![kw("a", Int), kw("b", Int)], None, Int),
Immutable,
Public,
Some("randint"),
);
let t = nd_proc(
vec![kw("seq", poly("Seq", vec![ty_tp(mono_q("T"))]))],
None,
mono_q("T"),
);
let t = quant(t, set! {static_instance("T", Type)});
random.register_builtin_py_impl("choice!", t, Immutable, Public, Some("choice"));
random
}
}

View file

@ -1,28 +0,0 @@
use erg_common::vis::Visibility;
use crate::ty::constructors::{func, kw};
use crate::ty::Type;
use Type::*;
use crate::context::Context;
use crate::varinfo::Mutability;
use Mutability::*;
use Visibility::*;
impl Context {
pub(crate) fn init_py_re_mod() -> Self {
let mut re = Context::builtin_module("re", 10);
re.register_builtin_impl(
"sub",
func(
vec![kw("pattern", Str), kw("repl", Str), kw("string", Str)],
None,
vec![kw("count", Nat)],
Str,
),
Immutable,
Public,
);
re
}
}

View file

@ -1,25 +0,0 @@
use erg_common::vis::Visibility;
use crate::ty::constructors::{proc0, proc1};
use crate::ty::Type;
use Type::*;
use crate::context::Context;
use crate::varinfo::Mutability;
use Mutability::*;
use Visibility::*;
impl Context {
pub(crate) fn init_py_time_mod() -> Self {
let mut time = Context::builtin_module("time", 15);
time.register_builtin_py_impl(
"sleep!",
proc1(Float, NoneType),
Immutable,
Public,
Some("sleep"),
);
time.register_builtin_py_impl("time!", proc0(Float), Immutable, Public, Some("time"));
time
}
}

View file

@ -1,54 +0,0 @@
use std::path::PathBuf;
use erg_common::vis::Visibility;
use crate::ty::constructors::{kw, module_from_path, mono, or, proc};
use crate::ty::Type;
use Type::*;
use crate::context::Context;
use crate::mod_cache::SharedModuleCache;
use crate::varinfo::Mutability;
use Mutability::*;
use Visibility::*;
impl Context {
pub(crate) fn init_py_urllib_mod() -> Self {
let mut urllib = Context::builtin_module("urllib", 4);
urllib.py_mod_cache = Some(SharedModuleCache::new());
let mut request_class = Context::builtin_mono_class("Request", 5);
request_class.register_builtin_impl("data", mono("Bytes"), Immutable, Public);
urllib.register_builtin_type(
mono("urllib.request.Request"),
request_class,
Public,
Const,
Some("urllib.request.Request"),
);
urllib.register_builtin_impl("request", module_from_path("request"), Immutable, Public);
let mut request = Context::builtin_module("urllib.request", 15);
let t = proc(
vec![kw("url", or(Str, mono("urllib.request.Request")))],
None,
vec![
kw("data", or(mono("Bytes"), NoneType)),
kw("timeout", or(Nat, NoneType)),
],
mono("http.client.HTTPResponse"),
);
request.register_builtin_py_impl("urlopen!", t, Immutable, Public, Some("urlopen"));
urllib.register_builtin_impl("parse", module_from_path("parse"), Immutable, Public);
let parse = Context::builtin_module("urllib.parse", 15);
urllib
.py_mod_cache
.as_ref()
.unwrap()
.register(PathBuf::from("request"), None, request);
urllib
.py_mod_cache
.as_ref()
.unwrap()
.register(PathBuf::from("parse"), None, parse);
urllib
}
}

View file

@ -17,7 +17,7 @@ use ast::VarName;
use erg_parser::ast::{self, Identifier};
use erg_parser::token::Token;
use crate::ty::constructors::{anon, free_var, func, module, mono, poly, proj, subr_t, v_enum};
use crate::ty::constructors::{anon, free_var, func, mono, poly, proj, subr_t};
use crate::ty::free::Constraint;
use crate::ty::typaram::TyParam;
use crate::ty::value::{GenTypeObj, TypeObj, ValueObj};
@ -116,7 +116,7 @@ impl Context {
self.locals.get_key_value(name)
}
pub(crate) fn get_singular_ctx(
pub(crate) fn get_singular_ctx_by_hir_expr(
&self,
obj: &hir::Expr,
namespace: &Str,
@ -127,9 +127,9 @@ impl Context {
}
hir::Expr::Accessor(hir::Accessor::Attr(attr)) => {
// REVIEW: 両方singularとは限らない?
let ctx = self.get_singular_ctx(&attr.obj, namespace)?;
let ctx = self.get_singular_ctx_by_hir_expr(&attr.obj, namespace)?;
let attr = hir::Expr::Accessor(hir::Accessor::Ident(attr.ident.clone()));
ctx.get_singular_ctx(&attr, namespace)
ctx.get_singular_ctx_by_hir_expr(&attr, namespace)
}
// TODO: change error
_ => Err(TyCheckError::no_var_error(
@ -180,6 +180,32 @@ impl Context {
.ok_or(err)
}
pub(crate) fn get_singular_ctx(
&self,
obj: &ast::Expr,
namespace: &Str,
) -> SingleTyCheckResult<&Context> {
match obj {
ast::Expr::Accessor(ast::Accessor::Ident(ident)) => {
self.get_singular_ctx_by_ident(ident, namespace)
}
ast::Expr::Accessor(ast::Accessor::Attr(attr)) => {
// REVIEW: 両方singularとは限らない?
let ctx = self.get_singular_ctx(&attr.obj, namespace)?;
let attr = ast::Expr::Accessor(ast::Accessor::Ident(attr.ident.clone()));
ctx.get_singular_ctx(&attr, namespace)
}
_ => Err(TyCheckError::no_var_error(
self.cfg.input.clone(),
line!() as usize,
obj.loc(),
self.caused_by(),
&obj.to_string(),
None,
)),
}
}
pub(crate) fn get_mut_singular_ctx(
&mut self,
obj: &ast::Expr,
@ -291,7 +317,7 @@ impl Context {
})
}
fn get_import_call_t(
/*fn get_import_call_t(
&self,
pos_args: &[hir::PosArg],
kw_args: &[hir::KwArg],
@ -346,7 +372,7 @@ impl Context {
py_name: Some(Str::ever("__import__")),
..VarInfo::default()
})
}
}*/
pub(crate) fn rec_get_var_info(
&self,
@ -430,7 +456,7 @@ impl Context {
return Err(e);
}
}
if let Ok(singular_ctx) = self.get_singular_ctx(obj, namespace) {
if let Ok(singular_ctx) = self.get_singular_ctx_by_hir_expr(obj, namespace) {
match singular_ctx.rec_get_var_info(ident, AccessKind::Attr, input, namespace) {
Ok(vi) => {
return Ok(vi);
@ -608,7 +634,7 @@ impl Context {
}
}
}
if let Ok(singular_ctx) = self.get_singular_ctx(obj, namespace) {
if let Ok(singular_ctx) = self.get_singular_ctx_by_hir_expr(obj, namespace) {
if let Some(vi) = singular_ctx
.locals
.get(attr_name.inspect())
@ -1133,13 +1159,14 @@ impl Context {
) -> TyCheckResult<VarInfo> {
if let hir::Expr::Accessor(hir::Accessor::Ident(local)) = obj {
if local.vis().is_private() {
#[allow(clippy::single_match)]
match &local.inspect()[..] {
"match" => {
return self.get_match_call_t(pos_args, kw_args);
}
"import" | "pyimport" | "py" => {
/*"import" | "pyimport" | "py" => {
return self.get_import_call_t(pos_args, kw_args);
}
}*/
// handle assert casting
/*"assert" => {
if let Some(arg) = pos_args.first() {
@ -1263,7 +1290,7 @@ impl Context {
obj: &hir::Expr,
name: &str,
) -> Option<&'a str> {
if let Ok(ctx) = self.get_singular_ctx(obj, &self.name) {
if let Ok(ctx) = self.get_singular_ctx_by_hir_expr(obj, &self.name) {
if let Some(name) = ctx.get_similar_name(name) {
return Some(name);
}
@ -1707,13 +1734,19 @@ impl Context {
// TODO: erg std
pub(crate) fn resolve_path(&self, path: &Path) -> PathBuf {
if let Ok(path) = self.cfg.input.resolve(path) {
if let Ok(path) = self.cfg.input.local_resolve(path) {
path
} else if let Ok(path) = erg_pystd_path()
.join(format!("{}.d.er", path.display()))
.canonicalize()
{
path
} else if let Ok(path) = erg_pystd_path()
.join(format!("{}.d", path.display()))
.join("__init__.d.er")
.canonicalize()
{
path
} else {
PathBuf::from(format!("<builtins>.{}", path.display()))
}
@ -1722,10 +1755,9 @@ impl Context {
// FIXME: 現在の実装だとimportしたモジュールはどこからでも見れる
pub(crate) fn get_mod(&self, name: &str) -> Option<&Context> {
let t = self.get_var_info(name).map(|(_, vi)| vi.t.clone()).ok()?;
match t {
Type::Poly { name, mut params } if &name[..] == "Module" => {
if t.is_module() {
let path =
option_enum_unwrap!(params.remove(0), TyParam::Value:(ValueObj::Str:(_)))?;
option_enum_unwrap!(t.typarams().remove(0), TyParam::Value:(ValueObj::Str:(_)))?;
let path = self.resolve_path(Path::new(&path[..]));
self.mod_cache
.as_ref()
@ -1735,8 +1767,8 @@ impl Context {
.as_ref()
.and_then(|cache| cache.ref_ctx(&path))
})
}
_ => None,
} else {
None
}
}

View file

@ -637,7 +637,23 @@ impl Context {
ast::PreDeclTypeSpec::Simple(simple) => {
self.instantiate_simple_t(simple, opt_decl_t, tmp_tv_ctx)
}
_ => todo!(),
ast::PreDeclTypeSpec::Attr { namespace, t } => {
let ctx = self.get_singular_ctx(namespace.as_ref(), &self.name)?;
if let Some((typ, _)) = ctx.rec_get_type(t.ident.inspect()) {
// TODO: visibility check
Ok(typ.clone())
} else {
Err(TyCheckErrors::from(TyCheckError::no_var_error(
self.cfg.input.clone(),
line!() as usize,
t.loc(),
self.caused_by(),
t.ident.inspect(),
self.get_similar_name(t.ident.inspect()),
)))
}
}
other => todo!("{other}"),
}
}
@ -647,7 +663,7 @@ impl Context {
opt_decl_t: Option<&ParamTy>,
tmp_tv_ctx: Option<&TyVarInstContext>,
) -> TyCheckResult<Type> {
match &simple.name.inspect()[..] {
match &simple.ident.inspect()[..] {
"_" | "Obj" => Ok(Type::Obj),
"Nat" => Ok(Type::Nat),
"Int" => Ok(Type::Int),

View file

@ -275,24 +275,6 @@ pub enum RegistrationMode {
Normal,
}
/// Some Erg functions require additional operation by the compiler.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OperationKind {
Import,
PyImport,
Del,
AssertCast,
}
impl OperationKind {
pub const fn is_erg_import(&self) -> bool {
matches!(self, Self::Import)
}
pub const fn is_py_import(&self) -> bool {
matches!(self, Self::PyImport)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ContextInfo {
mod_id: usize,

View file

@ -1,9 +1,7 @@
use std::option::Option;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use crate::ty::free::HasLevel;
use erg_common::config::{ErgConfig, Input};
use erg_common::env::erg_pystd_path;
use erg_common::config::ErgConfig;
use erg_common::levenshtein::get_similar_name;
use erg_common::python_util::BUILTIN_PYTHON_MODS;
use erg_common::set::Set;
@ -12,11 +10,11 @@ use erg_common::vis::Visibility;
use erg_common::Str;
use erg_common::{enum_unwrap, get_hash, log, option_enum_unwrap, set};
use ast::{DefId, Identifier, VarName};
use erg_parser::ast::{self, Decorator};
use ast::{Decorator, DefId, Identifier, OperationKind, VarName};
use erg_parser::ast;
use crate::ty::constructors::{free_var, func, func1, proc, ref_, ref_mut, v_enum};
use crate::ty::free::{Constraint, Cyclicity, FreeKind};
use crate::ty::free::{Constraint, Cyclicity, FreeKind, HasLevel};
use crate::ty::value::{GenTypeObj, TypeKind, TypeObj, ValueObj};
use crate::ty::{HasType, ParamTy, SubrType, Type};
@ -37,7 +35,6 @@ use RegistrationMode::*;
use Visibility::*;
use super::instantiate::TyVarInstContext;
use super::OperationKind;
impl Context {
/// If it is a constant that is defined, there must be no variable of the same name defined across all scopes
@ -1041,14 +1038,7 @@ impl Context {
mod_cache: &SharedModuleCache,
py_mod_cache: &SharedModuleCache,
) -> CompileResult<PathBuf> {
let mut dir = if let Input::File(mut path) = self.cfg.input.clone() {
path.pop();
path
} else {
PathBuf::new()
};
dir.push(format!("{__name__}.er"));
let path = match dir.canonicalize() {
let path = match self.cfg.input.local_resolve(Path::new(&__name__[..])) {
Ok(path) => path,
Err(err) => {
let err = TyCheckErrors::from(TyCheckError::import_error(
@ -1118,14 +1108,6 @@ impl Context {
py_mod_cache.register(builtin_path.clone(), None, Self::init_py_os_mod());
Ok(builtin_path)
}
"random" => {
py_mod_cache.register(builtin_path.clone(), None, Self::init_py_random_mod());
Ok(builtin_path)
}
"re" => {
py_mod_cache.register(builtin_path.clone(), None, Self::init_py_re_mod());
Ok(builtin_path)
}
"socket" => {
py_mod_cache.register(builtin_path.clone(), None, Self::init_py_socket_mod());
Ok(builtin_path)
@ -1134,14 +1116,6 @@ impl Context {
py_mod_cache.register(builtin_path.clone(), None, Self::init_py_sys_mod());
Ok(builtin_path)
}
"time" => {
py_mod_cache.register(builtin_path.clone(), None, Self::init_py_time_mod());
Ok(builtin_path)
}
"urllib" => {
py_mod_cache.register(builtin_path.clone(), None, Self::init_py_urllib_mod());
Ok(builtin_path)
}
_ => self.import_py_mod(mod_name),
}
}
@ -1150,27 +1124,12 @@ impl Context {
get_similar_name(BUILTIN_PYTHON_MODS.into_iter(), name).map(Str::rc)
}
fn find_decl_in_pystd(__name__: &str) -> std::io::Result<PathBuf> {
let mut as_std_path = erg_pystd_path().join(__name__);
as_std_path.set_extension("d.er");
as_std_path.canonicalize()
}
fn import_py_mod(&self, mod_name: &Literal) -> CompileResult<PathBuf> {
let __name__ = enum_unwrap!(mod_name.value.clone(), ValueObj::Str);
let mod_cache = self.mod_cache.as_ref().unwrap();
let py_mod_cache = self.py_mod_cache.as_ref().unwrap();
let mut dir = if let Input::File(mut path) = self.cfg.input.clone() {
path.pop();
path
} else {
PathBuf::new()
};
dir.push(format!("{__name__}.d.er"));
let path = match dir
.canonicalize()
.or_else(|_| Self::find_decl_in_pystd(&__name__))
{
let path = self.resolve_path(Path::new(&__name__[..]));
let path = match path.canonicalize() {
Ok(path) => path,
Err(err) => {
let err = TyCheckError::import_error(

View file

@ -892,6 +892,7 @@ impl Context {
}
Ok(())
}
hir::Expr::Import(_) => unreachable!(),
}
}

View file

@ -12,7 +12,9 @@ use erg_common::{
impl_nested_display_for_enum, impl_stream_for_wrapper,
};
use erg_parser::ast::{fmt_lines, DefId, DefKind, NonDefaultParamSignature, TypeSpec, VarName};
use erg_parser::ast::{
fmt_lines, DefId, DefKind, NonDefaultParamSignature, OperationKind, TypeSpec, VarName,
};
use erg_parser::token::{Token, TokenKind};
use crate::ty::constructors::{array_t, dict_t, set_t, tuple_t};
@ -21,7 +23,6 @@ use crate::ty::value::{TypeKind, ValueObj};
use crate::ty::{HasType, Type};
use crate::context::eval::type_from_token_kind;
use crate::context::OperationKind;
use crate::error::readable_name;
use crate::varinfo::VarInfo;
use crate::{impl_t, impl_t_for_enum};
@ -70,6 +71,16 @@ impl Literal {
}
}
impl Literal {
pub fn new(value: ValueObj, token: Token) -> Self {
Self {
t: value.t(),
value,
token,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PosArg {
pub expr: Expr,
@ -1789,12 +1800,13 @@ pub enum Expr {
TypeAsc(TypeAscription),
Code(Block), // code object
Compound(Block), // compound statement
Import(Accessor),
}
impl_nested_display_for_chunk_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Record, BinOp, UnaryOp, Call, Lambda, Decl, Def, ClassDef, AttrDef, Code, Compound, TypeAsc, Set);
impl_nested_display_for_chunk_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Record, BinOp, UnaryOp, Call, Lambda, Decl, Def, ClassDef, AttrDef, Code, Compound, TypeAsc, Set, Import);
impl_display_from_nested!(Expr);
impl_locational_for_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Record, BinOp, UnaryOp, Call, Lambda, Decl, Def, ClassDef, AttrDef, Code, Compound, TypeAsc, Set);
impl_t_for_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Record, BinOp, UnaryOp, Call, Lambda, Decl, Def, ClassDef, AttrDef, Code, Compound, TypeAsc, Set);
impl_locational_for_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Record, BinOp, UnaryOp, Call, Lambda, Decl, Def, ClassDef, AttrDef, Code, Compound, TypeAsc, Set, Import);
impl_t_for_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Record, BinOp, UnaryOp, Call, Lambda, Decl, Def, ClassDef, AttrDef, Code, Compound, TypeAsc, Set, Import);
impl Default for Expr {
fn default() -> Self {

View file

@ -0,0 +1 @@
.client = pyimport "./client"

View file

@ -0,0 +1 @@
.HTTPResponse: ClassType

View file

@ -0,0 +1,3 @@
.seed!: (a := Num, version := Int) => NoneType
.randint!: (a: Int, b: Int) => Int
.choice!: |T: Type, S <: Seq(T)|(seq: S) => T

View file

@ -0,0 +1 @@
.sub: (pattern: Str, repl: Str, string: Str, count := Nat) -> Str

View file

@ -0,0 +1,2 @@
.sleep!: Float => NoneType
.time!: () => Float

View file

@ -0,0 +1,2 @@
.parse = pyimport "./parse"
.request = pyimport "./request"

View file

@ -0,0 +1,5 @@
http = pyimport "http"
.Request: ClassType
.Request.data: Bytes
.urlopen!: (url: Str or .Request, data: Bytes or NoneType := NoneType, timeout: Nat or NoneType := NoneType) -> http.client.HTTPResponse

View file

@ -7,7 +7,7 @@ use erg_common::traits::{Locational, Stream};
use erg_common::Str;
use erg_common::{enum_unwrap, log};
use erg_parser::ast::DefId;
use erg_parser::ast::{DefId, OperationKind};
use erg_parser::token::{Token, TokenKind};
use crate::ty::free::fresh_varname;
@ -15,7 +15,6 @@ use crate::ty::typaram::TyParam;
use crate::ty::value::ValueObj;
use crate::ty::{HasType, Type};
use crate::context::OperationKind;
use crate::hir::*;
use crate::mod_cache::SharedModuleCache;
@ -34,19 +33,141 @@ impl<'a> Linker<'a> {
for chunk in main.module.iter_mut() {
self.replace_import(chunk);
}
for chunk in main.module.iter_mut() {
self.resolve_pymod_path(chunk);
}
log!(info "linked: {main}");
main
}
/// ```erg
/// urllib = pyimport "urllib"
/// urllib.request.urlopen! "https://example.com"
/// ```
/// ↓
/// ```python
/// urllib = __import__("urllib.request")
/// import urllib.request
/// urllib.request.urlopen("https://example.com")
/// ```
fn resolve_pymod_path(&self, expr: &mut Expr) {
match expr {
Expr::Lit(_) => {}
Expr::Accessor(acc) => {
if matches!(acc, Accessor::Attr(_)) && acc.ref_t().is_py_module() {
let import = Expr::Import(acc.clone());
*expr = Expr::Compound(Block::new(vec![import, mem::take(expr)]));
}
}
Expr::Array(array) => match array {
Array::Normal(arr) => {
for elem in arr.elems.pos_args.iter_mut() {
self.resolve_pymod_path(&mut elem.expr);
}
}
Array::WithLength(arr) => {
self.resolve_pymod_path(&mut arr.elem);
self.resolve_pymod_path(&mut arr.len);
}
_ => todo!(),
},
Expr::Tuple(tuple) => match tuple {
Tuple::Normal(tup) => {
for elem in tup.elems.pos_args.iter_mut() {
self.resolve_pymod_path(&mut elem.expr);
}
}
},
Expr::Set(set) => match set {
Set::Normal(st) => {
for elem in st.elems.pos_args.iter_mut() {
self.resolve_pymod_path(&mut elem.expr);
}
}
Set::WithLength(st) => {
self.resolve_pymod_path(&mut st.elem);
self.resolve_pymod_path(&mut st.len);
}
},
Expr::Dict(dict) => match dict {
Dict::Normal(dic) => {
for elem in dic.kvs.iter_mut() {
self.resolve_pymod_path(&mut elem.key);
self.resolve_pymod_path(&mut elem.value);
}
}
other => todo!("{other}"),
},
Expr::Record(record) => {
for attr in record.attrs.iter_mut() {
for chunk in attr.body.block.iter_mut() {
self.resolve_pymod_path(chunk);
}
}
}
Expr::BinOp(binop) => {
self.resolve_pymod_path(&mut binop.lhs);
self.resolve_pymod_path(&mut binop.rhs);
}
Expr::UnaryOp(unaryop) => {
self.resolve_pymod_path(&mut unaryop.expr);
}
Expr::Call(call) => {
self.resolve_pymod_path(&mut call.obj);
for arg in call.args.pos_args.iter_mut() {
self.resolve_pymod_path(&mut arg.expr);
}
for arg in call.args.kw_args.iter_mut() {
self.resolve_pymod_path(&mut arg.expr);
}
}
Expr::Decl(_decl) => {}
Expr::Def(def) => {
for chunk in def.body.block.iter_mut() {
self.resolve_pymod_path(chunk);
}
}
Expr::Lambda(lambda) => {
for chunk in lambda.body.iter_mut() {
self.resolve_pymod_path(chunk);
}
}
Expr::ClassDef(class_def) => {
for def in class_def.methods.iter_mut() {
self.resolve_pymod_path(def);
}
}
Expr::AttrDef(attr_def) => {
// REVIEW:
for chunk in attr_def.block.iter_mut() {
self.resolve_pymod_path(chunk);
}
}
Expr::TypeAsc(tasc) => self.resolve_pymod_path(&mut tasc.expr),
Expr::Code(chunks) | Expr::Compound(chunks) => {
for chunk in chunks.iter_mut() {
self.resolve_pymod_path(chunk);
}
}
Expr::Import(_) => unreachable!(),
}
}
fn replace_import(&self, expr: &mut Expr) {
match expr {
Expr::Lit(_) => {}
Expr::Accessor(acc) => match acc {
Expr::Accessor(acc) => {
/*if acc.ref_t().is_py_module() {
let import = Expr::Import(acc.clone());
*expr = Expr::Compound(Block::new(vec![import, mem::take(expr)]));
}*/
match acc {
Accessor::Attr(attr) => {
self.replace_import(&mut attr.obj);
}
Accessor::Ident(_) => {}
},
}
}
Expr::Array(array) => match array {
Array::Normal(arr) => {
for elem in arr.elems.pos_args.iter_mut() {
@ -108,6 +229,7 @@ impl<'a> Linker<'a> {
self.replace_py_import(expr);
}
_ => {
self.replace_import(&mut call.obj);
for arg in call.args.pos_args.iter_mut() {
self.replace_import(&mut arg.expr);
}
@ -144,6 +266,7 @@ impl<'a> Linker<'a> {
self.replace_import(chunk);
}
}
Expr::Import(_) => unreachable!(),
}
}
@ -163,7 +286,7 @@ impl<'a> Linker<'a> {
let path =
enum_unwrap!(expr.ref_t().typarams().remove(0), TyParam::Value:(ValueObj::Str:(_)));
let path = Path::new(&path[..]);
let path = self.cfg.input.resolve(path).unwrap();
let path = self.cfg.input.local_resolve(path).unwrap();
// In the case of REPL, entries cannot be used up
let hir = if self.cfg.input.is_repl() {
self.mod_cache

View file

@ -13,7 +13,7 @@ use erg_common::vis::Visibility;
use erg_common::{enum_unwrap, fmt_option, fn_name, get_hash, log, switch_lang, Str};
use erg_parser::ast;
use erg_parser::ast::AST;
use erg_parser::ast::{OperationKind, AST};
use erg_parser::build_ast::ASTBuilder;
use erg_parser::token::{Token, TokenKind};
use erg_parser::Parser;
@ -27,9 +27,7 @@ use crate::ty::value::{GenTypeObj, TypeKind, TypeObj, ValueObj};
use crate::ty::{HasType, ParamTy, Type};
use crate::context::instantiate::TyVarInstContext;
use crate::context::{
ClassDefType, Context, ContextKind, OperationKind, RegistrationMode, TraitInstance,
};
use crate::context::{ClassDefType, Context, ContextKind, RegistrationMode, TraitInstance};
use crate::error::{
CompileError, CompileErrors, LowerError, LowerErrors, LowerResult, LowerWarnings,
SingleLowerResult,
@ -1455,7 +1453,7 @@ impl ASTLowerer {
Ok(hir::Block::new(hir_block))
}
fn declare_var_alias(
fn declare_or_import_var(
&mut self,
sig: ast::VarSignature,
mut body: ast::DefBody,
@ -1470,11 +1468,13 @@ impl ASTLowerer {
)));
}
let chunk = self.declare_chunk(body.block.remove(0))?;
let acc = enum_unwrap!(
enum_unwrap!(&chunk, hir::Expr::TypeAsc).expr.as_ref(),
hir::Expr::Accessor
);
let py_name = acc.local_name().map(Str::rc);
let py_name = if let hir::Expr::TypeAsc(tasc) = &chunk {
enum_unwrap!(tasc.expr.as_ref(), hir::Expr::Accessor)
.local_name()
.map(Str::rc)
} else {
sig.inspect().cloned()
};
let block = hir::Block::new(vec![chunk]);
let found_body_t = block.ref_t();
let ident = match &sig.pat {
@ -1489,7 +1489,7 @@ impl ASTLowerer {
Ok(hir::Def::new(hir::Signature::Var(sig), body))
}
fn declare_alias(&mut self, def: ast::Def) -> LowerResult<hir::Def> {
fn declare_alias_or_import(&mut self, def: ast::Def) -> LowerResult<hir::Def> {
log!(info "entered {}({})", fn_name!(), def.sig);
let name = if let Some(name) = def.sig.name_as_str() {
name.clone()
@ -1510,11 +1510,19 @@ impl ASTLowerer {
&name,
)));
}
#[allow(clippy::let_and_return)]
let res = match def.sig {
ast::Signature::Subr(_sig) => todo!(),
ast::Signature::Var(sig) => self.declare_var_alias(sig, def.body),
ast::Signature::Subr(sig) => {
return Err(LowerErrors::from(LowerError::declare_error(
self.cfg.input.clone(),
line!() as usize,
sig.loc(),
self.ctx.caused_by(),
)));
}
ast::Signature::Var(sig) => self.declare_or_import_var(sig, def.body),
};
self.pop_append_errs();
// self.pop_append_errs();
res
}
@ -1636,7 +1644,7 @@ impl ASTLowerer {
fn declare_chunk(&mut self, expr: ast::Expr) -> LowerResult<hir::Expr> {
log!(info "entered {}", fn_name!());
match expr {
ast::Expr::Def(def) => Ok(hir::Expr::Def(self.declare_alias(def)?)),
ast::Expr::Def(def) => Ok(hir::Expr::Def(self.declare_alias_or_import(def)?)),
ast::Expr::ClassDef(defs) => Err(LowerErrors::from(LowerError::feature_error(
self.cfg.input.clone(),
defs.loc(),
@ -1644,6 +1652,14 @@ impl ASTLowerer {
self.ctx.caused_by(),
))),
ast::Expr::TypeAsc(tasc) => Ok(hir::Expr::TypeAsc(self.declare_ident(tasc)?)),
ast::Expr::Call(call)
if call
.additional_operation()
.map(|op| op.is_import())
.unwrap_or(false) =>
{
Ok(hir::Expr::Call(self.lower_call(call)?))
}
other => Err(LowerErrors::from(LowerError::declare_error(
self.cfg.input.clone(),
line!() as usize,

View file

@ -58,12 +58,12 @@ impl Reorderer {
}
Expr::Methods(methods) => match &methods.class {
TypeSpec::PreDeclTy(PreDeclTypeSpec::Simple(simple)) => {
self.link_methods(simple.name.inspect().clone(), &mut new, methods)
self.link_methods(simple.ident.inspect().clone(), &mut new, methods)
}
TypeSpec::TypeApp { spec, .. } => {
if let TypeSpec::PreDeclTy(PreDeclTypeSpec::Simple(simple)) = spec.as_ref()
{
self.link_methods(simple.name.inspect().clone(), &mut new, methods)
self.link_methods(simple.ident.inspect().clone(), &mut new, methods)
} else {
let similar_name = self
.def_root_pos_map

View file

@ -64,6 +64,11 @@ pub fn module(path: TyParam) -> Type {
poly("Module", vec![path])
}
#[inline]
pub fn py_module(path: TyParam) -> Type {
poly("PyModule", vec![path])
}
pub fn module_from_path<P: Into<PathBuf>>(path: P) -> Type {
let s = ValueObj::Str(Str::rc(path.into().to_str().unwrap()));
module(TyParam::Value(s))

View file

@ -1800,6 +1800,24 @@ impl Type {
}
}
pub fn is_module(&self) -> bool {
match self {
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_module(),
Self::Refinement(refine) => refine.t.is_module(),
Self::Poly { name, .. } => &name[..] == "PyModule" || &name[..] == "Module",
_ => false,
}
}
pub fn is_py_module(&self) -> bool {
match self {
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_py_module(),
Self::Refinement(refine) => refine.t.is_py_module(),
Self::Poly { name, .. } => &name[..] == "PyModule",
_ => false,
}
}
pub fn is_quantified(&self) -> bool {
match self {
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_quantified(),

View file

@ -667,7 +667,7 @@ impl TyParam {
Self::MonoQVar(_) | Self::PolyQVar { .. } => true,
Self::FreeVar(fv) => {
if fv.is_unbound() {
true
false
} else {
fv.crack().has_qvar()
}

View file

@ -17,6 +17,27 @@ use erg_common::{fmt_vec_split_with, Str};
use crate::token::{Token, TokenKind};
/// Some Erg functions require additional operation by the compiler.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OperationKind {
Import,
PyImport,
Del,
AssertCast,
}
impl OperationKind {
pub const fn is_erg_import(&self) -> bool {
matches!(self, Self::Import)
}
pub const fn is_py_import(&self) -> bool {
matches!(self, Self::PyImport)
}
pub const fn is_import(&self) -> bool {
matches!(self, Self::Import | Self::PyImport)
}
}
pub fn fmt_lines<'a, T: NestedDisplay + 'a>(
mut iter: impl Iterator<Item = &'a T>,
f: &mut fmt::Formatter<'_>,
@ -985,6 +1006,15 @@ impl Call {
.and_then(|pred| option_enum_unwrap!(pred, Expr::BinOp))
.map(|bin| bin.args[1].as_ref())
}
pub fn additional_operation(&self) -> Option<OperationKind> {
self.obj.get_name().and_then(|s| match &s[..] {
"import" => Some(OperationKind::Import),
"pyimport" | "py" => Some(OperationKind::PyImport),
"Del" => Some(OperationKind::Del),
_ => None,
})
}
}
/// e.g. `Data::{x = 1; y = 2}`
@ -1471,16 +1501,16 @@ impl ConstArgs {
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct SimpleTypeSpec {
pub name: VarName,
pub ident: Identifier,
pub args: ConstArgs, // args can be nested (e.g. Vec Vec Int)
}
impl fmt::Display for SimpleTypeSpec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.args.is_empty() {
write!(f, "{}", self.name)
write!(f, "{}", self.ident)
} else {
write!(f, "{}{}", self.name, self.args)
write!(f, "{}{}", self.ident, self.args)
}
}
}
@ -1488,18 +1518,18 @@ impl fmt::Display for SimpleTypeSpec {
impl Locational for SimpleTypeSpec {
fn loc(&self) -> Location {
if let Some(last) = self.args.kw_args.last() {
Location::concat(&self.name, last)
Location::concat(&self.ident, last)
} else if let Some(last) = self.args.pos_args.last() {
Location::concat(&self.name, last)
Location::concat(&self.ident, last)
} else {
self.name.loc()
self.ident.loc()
}
}
}
impl SimpleTypeSpec {
pub const fn new(name: VarName, args: ConstArgs) -> Self {
Self { name, args }
pub const fn new(ident: Identifier, args: ConstArgs) -> Self {
Self { ident, args }
}
}
@ -1515,12 +1545,12 @@ impl SimpleTypeSpec {
pub enum PreDeclTypeSpec {
Simple(SimpleTypeSpec),
Attr {
namespace: Vec<VarName>,
namespace: Box<Expr>,
t: SimpleTypeSpec,
},
Subscr {
namespace: Vec<VarName>,
name: VarName,
namespace: Box<Expr>,
ident: Identifier,
index: Token,
},
}
@ -1528,17 +1558,15 @@ pub enum PreDeclTypeSpec {
impl fmt::Display for PreDeclTypeSpec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PreDeclTypeSpec::Simple(ts) => write!(f, "{}", ts),
PreDeclTypeSpec::Simple(ts) => write!(f, "{ts}"),
PreDeclTypeSpec::Attr { namespace, t } => {
write!(f, "{}.{}", namespace.join("."), t)
write!(f, "{namespace}{t}")
}
PreDeclTypeSpec::Subscr {
namespace,
name,
ident,
index,
} => {
write!(f, "{}.{}[{}]", namespace.join("."), name, index)
}
} => write!(f, "{namespace}{ident}[{index}]"),
}
}
}
@ -1547,10 +1575,10 @@ impl Locational for PreDeclTypeSpec {
fn loc(&self) -> Location {
match self {
Self::Simple(s) => s.loc(),
Self::Attr { namespace, t } => Location::concat(&namespace[0], t),
Self::Attr { namespace, t } => Location::concat(namespace.as_ref(), t),
Self::Subscr {
namespace, index, ..
} => Location::concat(&namespace[0], index),
} => Location::concat(namespace.as_ref(), index),
}
}
}

View file

@ -2780,17 +2780,26 @@ impl Parser {
}
}
fn ident_to_type_spec(ident: Identifier) -> SimpleTypeSpec {
SimpleTypeSpec::new(ident, ConstArgs::empty())
}
fn accessor_to_type_spec(accessor: Accessor) -> Result<TypeSpec, ParseError> {
let t_spec = match accessor {
Accessor::Ident(ident) => {
let predecl =
PreDeclTypeSpec::Simple(SimpleTypeSpec::new(ident.name, ConstArgs::empty()));
let predecl = PreDeclTypeSpec::Simple(Self::ident_to_type_spec(ident));
TypeSpec::PreDeclTy(predecl)
}
Accessor::TypeApp(tapp) => {
let spec = Self::expr_to_type_spec(*tapp.obj)?;
TypeSpec::type_app(spec, tapp.type_args)
}
Accessor::Attr(attr) => {
let namespace = attr.obj;
let t = Self::ident_to_type_spec(attr.ident);
let predecl = PreDeclTypeSpec::Attr { namespace, t };
TypeSpec::PreDeclTy(predecl)
}
other => {
let err = ParseError::simple_syntax_error(line!() as usize, other.loc());
return Err(err);
@ -2814,7 +2823,7 @@ impl Parser {
kw_args.push(ConstKwArg::new(arg.keyword, const_expr));
}
Ok(PreDeclTypeSpec::Simple(SimpleTypeSpec::new(
ident.name,
ident,
ConstArgs::new(pos_args, kw_args, paren),
)))
}
@ -2831,9 +2840,11 @@ impl Parser {
(ParamPattern::VarName(name), Some(t_spec_with_op)) => {
ParamTySpec::new(Some(name.into_token()), t_spec_with_op.t_spec)
}
(ParamPattern::VarName(name), None) => ParamTySpec::anonymous(TypeSpec::PreDeclTy(
PreDeclTypeSpec::Simple(SimpleTypeSpec::new(name, ConstArgs::empty())),
)),
(ParamPattern::VarName(name), None) => {
ParamTySpec::anonymous(TypeSpec::PreDeclTy(PreDeclTypeSpec::Simple(
SimpleTypeSpec::new(Identifier::new(None, name), ConstArgs::empty()),
)))
}
_ => todo!(),
};
non_defaults.push(param);
@ -2849,7 +2860,7 @@ impl Parser {
}
(ParamPattern::VarName(name), None) => {
ParamTySpec::anonymous(TypeSpec::PreDeclTy(PreDeclTypeSpec::Simple(
SimpleTypeSpec::new(name, ConstArgs::empty()),
SimpleTypeSpec::new(Identifier::new(None, name), ConstArgs::empty()),
)))
}
_ => todo!(),

View file

@ -1,7 +1,9 @@
math = pyimport "math"
sys = pyimport "sys"
sub = pyimport "subprocess"
urllib = pyimport "urllib"
print! math.pi
print! urllib.request.urlopen!("https://example.com")
discard sub.run! ["echo", "hello"], shell := True
sys.exit 111