Skip to content

Commit

Permalink
feat: add unsound module
Browse files Browse the repository at this point in the history
  • Loading branch information
mtshiba committed Oct 15, 2023
1 parent fc75151 commit 08770e8
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 8 deletions.
2 changes: 2 additions & 0 deletions crates/erg_common/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,8 @@ impl Input {
.canonicalize()
{
Some(normalize_path(path))
} else if path == Path::new("unsound") {
Some(PathBuf::from("unsound"))
} else {
None
}
Expand Down
10 changes: 10 additions & 0 deletions crates/erg_compiler/build_package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down
63 changes: 63 additions & 0 deletions crates/erg_compiler/context/initialize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions crates/erg_compiler/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
9 changes: 9 additions & 0 deletions crates/erg_compiler/lib/std/unsound.d.er
Original file line number Diff line number Diff line change
@@ -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
31 changes: 23 additions & 8 deletions crates/erg_compiler/link_hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions crates/erg_parser/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
10 changes: 10 additions & 0 deletions examples/magic.er
Original file line number Diff line number Diff line change
@@ -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"
5 changes: 5 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 08770e8

Please sign in to comment.