mirror of
https://github.com/erg-lang/erg.git
synced 2025-07-07 21:25:31 +00:00
feat: add unsound
module
This commit is contained in:
parent
fc75151f2a
commit
08770e84fc
9 changed files with 130 additions and 8 deletions
|
@ -502,6 +502,8 @@ impl Input {
|
|||
.canonicalize()
|
||||
{
|
||||
Some(normalize_path(path))
|
||||
} else if path == Path::new("unsound") {
|
||||
Some(PathBuf::from("unsound"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -259,6 +259,16 @@ impl<Parser: ASTBuildable, Builder: Buildable> PackageBuilder<Parser, Builder> {
|
|||
let ValueObj::Str(__name__) = &mod_name.value else {
|
||||
return Ok(());
|
||||
};
|
||||
if call
|
||||
.additional_operation()
|
||||
.is_some_and(|op| op.is_erg_import())
|
||||
&& __name__ == "unsound"
|
||||
{
|
||||
if let Some(mod_ctx) = self.get_context() {
|
||||
mod_ctx.context.build_module_unsound();
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
let import_path = match cfg.input.resolve_path(Path::new(&__name__[..])) {
|
||||
Some(path) => path,
|
||||
None => {
|
||||
|
|
|
@ -1153,6 +1153,69 @@ impl Context {
|
|||
.register(PathBuf::from("<builtins>"), None, None, module);
|
||||
}
|
||||
|
||||
pub(crate) fn build_module_unsound(&self) {
|
||||
use erg_common::pathutil::NormalizedPathBuf;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::hir::{
|
||||
Accessor, Args, Block, Def, DefBody, Expr, Identifier, Module, Params, Signature,
|
||||
SubrSignature, VarSignature, HIR,
|
||||
};
|
||||
use erg_parser::ast::{DefId, NonDefaultParamSignature, TypeBoundSpecs};
|
||||
use erg_parser::token::Token;
|
||||
|
||||
let path = NormalizedPathBuf::from(Path::new("unsound"));
|
||||
let mut ctx = Context::new_module(
|
||||
"unsound",
|
||||
self.cfg.inherit(path.to_path_buf()),
|
||||
self.shared().clone(),
|
||||
);
|
||||
let eval_t = func1(Type::Str, Type::Obj);
|
||||
ctx.register_builtin_erg_impl(
|
||||
"pyeval",
|
||||
eval_t,
|
||||
Mutability::Immutable,
|
||||
Visibility::BUILTIN_PUBLIC,
|
||||
);
|
||||
let pyeval = Identifier::public("pyeval");
|
||||
let sig = VarSignature::new(pyeval.clone(), None);
|
||||
let sig = Signature::Var(sig);
|
||||
let eval = Expr::Accessor(Accessor::Ident(Identifier::public("eval")));
|
||||
let block = Block::new(vec![eval]);
|
||||
let body = DefBody::new(Token::DUMMY, block, DefId(0));
|
||||
let eval = Def::new(sig, body);
|
||||
let T = type_q("T");
|
||||
let perform_t = func1(proc0(T.clone()), T).quantify();
|
||||
ctx.register_builtin_erg_impl(
|
||||
"perform",
|
||||
perform_t,
|
||||
Mutability::Immutable,
|
||||
Visibility::BUILTIN_PUBLIC,
|
||||
);
|
||||
let perform = Identifier::public("perform");
|
||||
let params = Params::single(crate::hir::NonDefaultParamSignature::new(
|
||||
NonDefaultParamSignature::simple("p!".into()),
|
||||
VarInfo::const_default_public(),
|
||||
None,
|
||||
));
|
||||
let sig = SubrSignature::new(
|
||||
set! {},
|
||||
perform.clone(),
|
||||
TypeBoundSpecs::empty(),
|
||||
params,
|
||||
None,
|
||||
);
|
||||
let sig = Signature::Subr(sig);
|
||||
let call = Identifier::private("p!").call(Args::empty());
|
||||
let block = Block::new(vec![Expr::Call(call)]);
|
||||
let body = DefBody::new(Token::DUMMY, block, DefId(0));
|
||||
let perform = Def::new(sig, body);
|
||||
let module = Module::new(vec![Expr::Def(eval), Expr::Def(perform)]);
|
||||
let hir = HIR::new("unsound".into(), module);
|
||||
let ctx = ModuleContext::new(ctx, dict! {});
|
||||
self.mod_cache().register(path, None, Some(hir), ctx);
|
||||
}
|
||||
|
||||
pub fn new_module<S: Into<Str>>(
|
||||
name: S,
|
||||
cfg: ErgConfig,
|
||||
|
|
|
@ -497,6 +497,10 @@ impl Identifier {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn call(self, args: Args) -> Call {
|
||||
Call::new(Expr::Accessor(Accessor::Ident(self)), None, args)
|
||||
}
|
||||
|
||||
pub fn is_py_api(&self) -> bool {
|
||||
self.vi.py_name.is_some()
|
||||
}
|
||||
|
|
9
crates/erg_compiler/lib/std/unsound.d.er
Normal file
9
crates/erg_compiler/lib/std/unsound.d.er
Normal file
|
@ -0,0 +1,9 @@
|
|||
'''
|
||||
Evaluate a string as Python code.
|
||||
'''
|
||||
.pyeval: (code: Str, globals := {Str: Obj}, locals := {Str: Obj}) -> Obj
|
||||
'''
|
||||
Execute the procedure circumventing effect analysis.
|
||||
If you want to use side effects locally in a function, use the `isolate` function instead.
|
||||
'''
|
||||
.perform: |T|(p!: () => T) -> T
|
|
@ -6,10 +6,10 @@ use std::rc::Rc;
|
|||
use erg_common::config::ErgConfig;
|
||||
use erg_common::dict::Dict as Dic;
|
||||
use erg_common::fresh::SharedFreshNameGenerator;
|
||||
use erg_common::log;
|
||||
use erg_common::pathutil::squash;
|
||||
use erg_common::traits::{Locational, Stream};
|
||||
use erg_common::Str;
|
||||
use erg_common::{enum_unwrap, log};
|
||||
|
||||
use erg_parser::ast::{DefId, OperationKind};
|
||||
use erg_parser::token::{Token, TokenKind, DOT, EQUAL};
|
||||
|
@ -402,10 +402,14 @@ impl<'a> HIRLinker<'a> {
|
|||
.remove(path.as_path())
|
||||
.and_then(|entry| entry.hir.map(|hir| (hir, entry.module.context.cfg.clone())))
|
||||
};
|
||||
let mod_name = enum_unwrap!(expr, Expr::Call)
|
||||
.args
|
||||
.get_left_or_key("Path")
|
||||
.unwrap();
|
||||
let Expr::Call(call) = expr else {
|
||||
log!(err "{expr}");
|
||||
return;
|
||||
};
|
||||
let Some(mod_name) = call.args.get_left_or_key("Path") else {
|
||||
log!(err "{call}");
|
||||
return;
|
||||
};
|
||||
// let sig = option_enum_unwrap!(&def.sig, Signature::Var)
|
||||
// .unwrap_or_else(|| todo!("module subroutines are not allowed"));
|
||||
if let Some((hir, cfg)) = hir_cfg {
|
||||
|
@ -464,9 +468,20 @@ impl<'a> HIRLinker<'a> {
|
|||
/// x = __import__("a.x").x
|
||||
/// ```
|
||||
fn replace_py_import(&self, expr: &mut Expr) {
|
||||
let args = &mut enum_unwrap!(expr, Expr::Call).args;
|
||||
let mod_name_lit = enum_unwrap!(args.remove_left_or_key("Path").unwrap(), Expr::Literal);
|
||||
let mod_name_str = enum_unwrap!(mod_name_lit.value.clone(), ValueObj::Str);
|
||||
let args = if let Expr::Call(call) = expr {
|
||||
&mut call.args
|
||||
} else {
|
||||
log!(err "{expr}");
|
||||
return;
|
||||
};
|
||||
let Some(Expr::Literal(mod_name_lit)) = args.remove_left_or_key("Path") else {
|
||||
log!(err "{args}");
|
||||
return;
|
||||
};
|
||||
let ValueObj::Str(mod_name_str) = mod_name_lit.value.clone() else {
|
||||
log!(err "{mod_name_lit}");
|
||||
return;
|
||||
};
|
||||
let mut dir = self.cfg.input.dir();
|
||||
let mod_path = self
|
||||
.cfg
|
||||
|
|
|
@ -4076,6 +4076,10 @@ impl NonDefaultParamSignature {
|
|||
Self { pat, t_spec }
|
||||
}
|
||||
|
||||
pub fn simple(name: Str) -> Self {
|
||||
Self::new(ParamPattern::VarName(VarName::from_str(name)), None)
|
||||
}
|
||||
|
||||
pub const fn inspect(&self) -> Option<&Str> {
|
||||
self.pat.inspect()
|
||||
}
|
||||
|
|
10
examples/magic.er
Normal file
10
examples/magic.er
Normal file
|
@ -0,0 +1,10 @@
|
|||
unsound = import "unsound"
|
||||
|
||||
i = unsound.pyeval "1 + 1"
|
||||
assert i in Nat
|
||||
assert i == 2
|
||||
|
||||
print x =
|
||||
unsound.perform do! print! x
|
||||
|
||||
print "hello"
|
|
@ -196,6 +196,11 @@ fn exec_long() -> Result<(), ()> {
|
|||
expect_success("tests/should_ok/long.er", 257)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_magic() -> Result<(), ()> {
|
||||
expect_success("examples/magic.er", 0)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_mangling() -> Result<(), ()> {
|
||||
expect_success("tests/should_ok/mangling.er", 0)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue