Organize crates

This commit is contained in:
Shunsuke Shibayama 2022-08-13 06:38:12 +09:00
parent 6ddef21fec
commit f9d91aa38e
71 changed files with 6 additions and 14 deletions

18
compiler/erg_compiler/.gitignore vendored Normal file
View file

@ -0,0 +1,18 @@
# Generated by Cargo
# will have compiled files and executables
/target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
*.pyc
/.vscode/
/.VSCodeCounter/
/.vs/
/.DS_Store
/*/.DS_Store
/.idea/
/timeit.dat

View file

@ -0,0 +1,26 @@
[package]
name = "erg_compiler"
version = "0.2.0"
description = "Centimetre: the Erg compiler"
authors = ["Shunsuke Shibayama <sbym1346@gmail.com>"]
license = "MIT OR Apache-2.0"
edition = "2021"
repository = "https://github.com/erg-lang/erg/tree/main/src/erg_compiler"
documentation = "https://docs.rs/erg_compiler"
homepage = "https://erg-lang.github.io/"
[features]
# when "debug" feature is turned on, that of parser will also be turned on.
debug = [ "erg_common/debug", "erg_parser/debug" ]
japanese = [ "erg_common/japanese", "erg_parser/japanese" ]
[dependencies]
erg_common = { version = "0.1.4", path = "../erg_common" }
erg_parser = { version = "0.1.1", path = "../erg_parser" }
[lib]
path = "lib.rs"
[[bin]]
name = "cm"
path = "main.rs"

View file

@ -0,0 +1,3 @@
# The Erg compiler (codename: Centimetre)
The overall structure is described in detail in [architecture.md](../../doc/JA/compiler/architecture.md).

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,148 @@
//! defines `Compiler`.
//!
//! コンパイラーを定義する
use std::path::Path;
use erg_common::Str;
use erg_common::{log};
use erg_common::codeobj::{CodeObj, CodeObjFlags};
use erg_common::color::{GREEN, RESET};
use erg_common::config::{Input, ErgConfig, SEMVER, BUILD_INFO};
use erg_common::error::MultiErrorDisplay;
use erg_common::traits::{Runnable, Stream};
use erg_parser::ParserRunner;
use crate::codegen::CodeGenerator;
use crate::effectcheck::SideEffectChecker;
use crate::error::{TyCheckErrors, CompileError, CompileErrors};
use crate::lower::ASTLowerer;
use crate::ownercheck::OwnershipChecker;
/// * registered as global -> Global
/// * defined in the toplevel scope (and called in the inner scope) -> Global
/// * defined and called in the toplevel scope -> Local
/// * not defined in the toplevel and called in the inner scope -> Deref
/// * defined and called in the current scope (except the toplevel) -> Fast
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StoreLoadKind {
Local,
LocalConst,
Global,
GlobalConst,
Deref,
DerefConst,
Fast,
FastConst,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Name {
pub kind: StoreLoadKind,
pub idx: usize,
}
impl Name {
pub const fn new(kind: StoreLoadKind, idx: usize) -> Self { Self{ kind, idx } }
pub const fn local(idx: usize) -> Self { Self{ kind: StoreLoadKind::Local, idx } }
pub const fn global(idx: usize) -> Self { Self{ kind: StoreLoadKind::Global, idx } }
pub const fn deref(idx: usize) -> Self { Self{ kind: StoreLoadKind::Deref, idx } }
pub const fn fast(idx: usize) -> Self { Self{ kind: StoreLoadKind::Fast, idx } }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AccessKind {
Name,
Attr,
Method,
}
impl AccessKind {
pub const fn is_local(&self) -> bool { matches!(self, Self::Name) }
pub const fn is_attr(&self) -> bool { matches!(self, Self::Attr) }
pub const fn is_method(&self) -> bool { matches!(self, Self::Method) }
}
/// Generates a `CodeObj` from an `AST`.
/// The input AST is not typed, so it's typed by `ASTLowerer` according to the cfg.opt_level.
#[derive(Debug)]
pub struct Compiler {
cfg: ErgConfig,
lowerer: ASTLowerer,
code_generator: CodeGenerator,
}
impl Runnable for Compiler {
type Err = CompileError;
type Errs = CompileErrors;
fn new(cfg: ErgConfig) -> Self {
Self {
code_generator: CodeGenerator::new(cfg.copy()),
lowerer: ASTLowerer::new(),
cfg,
}
}
#[inline]
fn input(&self) -> &Input { &self.cfg.input }
#[inline]
fn start_message(&self) -> String { format!("Erg compiler {} {}\n", SEMVER, &*BUILD_INFO) }
#[inline]
fn finish(&mut self) {}
fn clear(&mut self) {
self.code_generator.clear();
}
fn eval(&mut self, src: Str) -> Result<String, CompileErrors> {
let codeobj = self.compile(src, "eval")?;
Ok(codeobj.code_info())
}
}
impl Compiler {
fn convert(&self, errs: TyCheckErrors) -> CompileErrors {
errs.into_iter().map(|e| CompileError::new(e.core, self.input().clone(), e.caused_by)).collect::<Vec<_>>().into()
}
pub fn compile_and_dump_as_pyc<P: AsRef<Path>>(&mut self, src: Str, path: P) -> Result<(), CompileErrors> {
let code = self.compile(src, "exec")?;
code.dump_as_pyc(path, self.cfg.python_ver).expect("failed to dump a .pyc file");
Ok(())
}
pub fn compile(&mut self, src: Str, mode: &str) -> Result<CodeObj, CompileErrors> {
log!("{GREEN}[DEBUG] the compiling process has started.{RESET}");
let mut dynamic = true;
let mut parser = ParserRunner::new(self.cfg.copy());
let ast = parser.parse_from_str(src)?;
if ast.is_empty() {
return Ok(CodeObj::empty(vec![], Str::rc(self.input().enclosed_name()), "<module>", 1))
}
let (hir, warns) = self.lowerer.lower(ast, mode).map_err(|errs| self.convert(errs))?;
if warns.is_empty() { dynamic = false; }
if self.cfg.verbose >= 2 {
let warns = self.convert(warns);
warns.fmt_all_stderr();
}
let effect_checker = SideEffectChecker::new();
let hir = effect_checker.check(hir).map_err(|errs| self.convert(errs))?;
let ownership_checker = OwnershipChecker::new();
let hir = ownership_checker.check(hir).map_err(|errs| self.convert(errs))?;
let mut codeobj = self.code_generator.codegen(hir);
if dynamic {
codeobj.flags += CodeObjFlags::EvmDynamic as u32;
}
log!("{GREEN}code object:\n{}", codeobj.code_info());
log!("[DEBUG] the compiling process has completed, found errors: {}{RESET}", self.code_generator.errs.len());
if self.code_generator.errs.is_empty() {
Ok(codeobj)
} else {
Err(self.code_generator.errs.flush())
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,187 @@
//! implements SideEffectChecker
//! SideEffectCheckerを実装
//! 関数や不変型に副作用がないかチェックする
use erg_common::Str;
use erg_common::color::{GREEN, RESET};
use erg_common::log;
use erg_common::traits::Stream;
use crate::error::{EffectError, EffectErrors, EffectResult};
use crate::hir::{HIR, Expr, Def, Accessor, Signature};
use crate::varinfo::Visibility;
use Visibility::*;
#[derive(Debug)]
pub struct SideEffectChecker {
path_stack: Vec<(Str, Visibility)>,
errs: EffectErrors,
}
impl SideEffectChecker {
pub fn new() -> Self { Self { path_stack: vec![], errs: EffectErrors::empty() } }
fn full_path(&self) -> String {
self.path_stack.iter().fold(String::new(), |acc, (path, vis)| {
if vis.is_public() { acc + "." + &path[..] } else { acc + "::" + &path[..] }
})
}
pub fn check(mut self, hir: HIR) -> EffectResult<HIR> {
self.path_stack.push((hir.name.clone(), Private));
log!("{GREEN}[DEBUG] the side-effect checking process has started.{RESET}");
// トップレベルでは副作用があっても問題なく、純粋性違反がないかのみチェックする
for expr in hir.module.iter() {
match expr {
Expr::Def(def) => { self.check_def(def, true); },
Expr::Call(call) => {
for parg in call.args.pos_args().iter() {
self.check_expr(&parg.expr, true);
}
for kwarg in call.args.kw_args().iter() {
self.check_expr(&kwarg.expr, true);
}
},
other => todo!("{other}"),
}
}
log!("{GREEN}[DEBUG] the side-effect checking process has completed, found errors: {}{RESET}", self.errs.len());
if self.errs.is_empty() {
Ok(hir)
} else {
Err(self.errs)
}
}
fn check_def(&mut self, def: &Def, allow_inner_effect: bool) {
let name_and_vis = match &def.sig {
Signature::Var(var) =>
// TODO: visibility
if let Some(name) = var.inspect() { (name.clone(), Private) }
else { (Str::ever("::<instant>"), Private) },
Signature::Subr(subr) => (subr.name.inspect().clone(), Private),
};
self.path_stack.push(name_and_vis);
// TODO: support raw identifier (``)
let is_procedural = def.sig.is_procedural();
let is_subr = def.sig.is_subr();
let is_const = def.sig.is_const();
let is_type = def.body.is_type();
match (is_procedural, is_subr) {
(true, _) => {
if !allow_inner_effect {
let expr = Expr::Def(def.clone());
self.errs.push(
EffectError::has_effect(&expr, self.full_path())
);
}
for chunk in def.body.block.iter() {
self.check_expr(chunk, allow_inner_effect);
}
},
(false, false) => {
for chunk in def.body.block.iter() {
self.check_expr(chunk, allow_inner_effect);
}
},
(false, true) => { self.check_func(def); },
}
if is_const { self.check_const(def); }
if !is_procedural && is_type { self.check_immut_type(def); }
self.path_stack.pop();
}
fn check_func(&mut self, funcdef: &Def) {
for chunk in funcdef.body.block.iter() {
self.check_expr(chunk, false);
}
}
fn check_immut_type(&mut self, _typedef: &Def) {
todo!()
}
fn check_const(&mut self, _constdef: &Def) {
todo!()
}
/// check if `expr` has side-effects / purity violations.
///
/// returns effects, purity violations will be appended to `self.errs`.
///
/// causes side-effects:
/// ```
/// p!() // 1 side-effect
/// p!(q!()) // 2 side-effects
/// x =
/// y = r!()
/// y + 1 // 1 side-effect
/// ```
/// causes no side-effects:
/// ```
/// q! = p!
/// y = f(p!)
/// ```
/// purity violation:
/// ```
/// for iter, i -> print! i
/// ```
fn check_expr(&mut self, expr: &Expr, allow_self_effect: bool) {
match expr {
Expr::Def(def) => { self.check_def(def, allow_self_effect); },
// 引数がproceduralでも関数呼び出しなら副作用なし
Expr::Call(call) => {
if self.is_procedural(&call.obj) && !allow_self_effect {
self.errs.push(
EffectError::has_effect(expr, self.full_path())
);
}
call.args
.pos_args()
.iter()
.for_each(|parg|self.check_expr(&parg.expr, allow_self_effect));
call.args
.kw_args()
.iter()
.for_each(|kwarg| self.check_expr(&kwarg.expr, allow_self_effect));
},
Expr::UnaryOp(unary) => {
self.check_expr(&unary.expr, allow_self_effect);
},
Expr::BinOp(bin) => {
self.check_expr(&bin.lhs, allow_self_effect);
self.check_expr(&bin.rhs, allow_self_effect);
},
Expr::Lambda(lambda) => {
let is_proc = lambda.is_procedural();
if is_proc {
self.path_stack.push((Str::ever("<lambda!>"), Private));
} else {
self.path_stack.push((Str::ever("<lambda>"), Private));
}
if !allow_self_effect && is_proc {
self.errs.push(EffectError::has_effect(expr, self.full_path()));
}
lambda.body
.iter()
.for_each(|chunk| self.check_expr(chunk, allow_self_effect && is_proc));
self.path_stack.pop();
},
_ => {},
}
}
fn is_procedural(&self, expr: &Expr) -> bool {
match expr {
Expr::Lambda(lambda) => lambda.is_procedural(),
// 引数がproceduralでも関数呼び出しなら副作用なし
Expr::Call(call) => self.is_procedural(&call.obj),
Expr::Accessor(Accessor::Local(local)) => local.name.is_procedural(),
// procedural: x.y! (e.g. Array.sample!)
// !procedural: !x.y
Expr::Accessor(Accessor::Attr(attr)) => attr.name.is_procedural(),
Expr::Accessor(_) => todo!(),
_ => false,
}
}
}

View file

@ -0,0 +1,501 @@
use std::fmt::Display;
use std::ops::Add;
use erg_common::color::{GREEN, RED, YELLOW, RESET};
use erg_common::config::Input;
use erg_common::error::{ErrorCore, ErrorKind::*, ErrorDisplay, MultiErrorDisplay, Location};
use erg_common::traits::{Stream, Locational};
use erg_common::ty::{Type, Predicate};
use erg_common::{Str, fmt_iter};
use erg_common::{impl_stream_for_wrapper, switch_lang};
use erg_parser::error::{ParserRunnerError, ParserRunnerErrors};
use crate::hir::Expr;
/// dname is for "double under name"
pub fn binop_to_dname(op: &str) -> &str {
match op {
"+" => "__add__",
"-" => "__sub__",
"*" => "__mul__",
"/" => "__div__",
"**" => "__pow__",
"%" => "__mod__",
".." => "__rng__",
"<.." => "__lorng__",
"..<" => "__rorng__",
"<..<" => "__orng__",
"and" => "__and__",
"or" => "__or__",
"in" => "__in__",
"contains" => "__contains__",
"subof" => "__subof__",
"supof" => "__supof__",
"is" => "__is__",
"isnot" => "__isnot__",
"==" => "__eq__",
"!=" => "__ne__",
"<" => "__lt__",
"<=" => "__le__",
">" => "__gt__",
">=" => "__ge__",
_ => todo!(),
}
}
pub fn unaryop_to_dname(op: &str) -> &str {
match op {
"+" => "__pos__",
"-" => "__neg__",
"~" => "__invert__",
"!" => "__mutate__",
"..." => "__spread__",
_ => todo!(),
}
}
pub fn readable_name(name: &str) -> &str {
match name {
"__add__" => "`+`",
"__sub__" => "`-`",
"__mul__" => "`*`",
"__div__" => "`/`",
"__pow__" => "`**`",
"__mod__" => "`%`",
"__rng__" => "`..`",
"__lrng__" => "`<..`",
"__rrng__" => "`..<`",
"__lrrng__" => "`<..<`",
"__and__" => "`and`",
"__or__" => "`or`",
"__in__" => "`in`",
"__contains__" => "`contains`",
"__subof__" => "`subof`",
"__supof__" => "`supof`",
"__is__" => "`is`",
"__isnot__" => "`isnot`",
"__eq__" => "`==`",
"__ne__" => "`!=`",
"__lt__" => "`<`",
"__le__" => "`<=`",
"__gt__" => "`>`",
"__ge__" => "`>=`",
"__pos__" => "`+`",
"__neg__" => "`-`",
"__invert__" => "`~`",
"__mutate__" => "`!`",
"__spread__" => "`...`",
other => other,
}
}
#[derive(Debug)]
pub struct CompileError {
pub core: ErrorCore,
pub input: Input,
pub caused_by: Str,
}
impl From<ParserRunnerError> for CompileError {
fn from(err: ParserRunnerError) -> Self {
Self { core: err.core, input: err.input, caused_by: "".into() }
}
}
impl ErrorDisplay for CompileError {
fn core(&self) -> &ErrorCore { &self.core }
fn input(&self) -> &Input { &self.input }
fn caused_by(&self) -> &str { &self.caused_by }
fn ref_inner(&self) -> Option<&Box<Self>> { None }
}
impl CompileError {
pub const fn new(core: ErrorCore, input: Input, caused_by: Str) -> Self { Self{ core, input, caused_by } }
pub fn compiler_bug(errno: usize, input: Input, loc: Location, fn_name: &str, line: u32) -> Self {
Self::new(ErrorCore::new(errno, CompilerSystemError, loc, switch_lang!(
format!("this is a bug of the Erg compiler, please report it to https://github.com/...\ncaused from: {fn_name}:{line}"),
format!("これはErg compilerのバグです、開発者に報告して下さい (https://github.com/...)\n{fn_name}:{line}より発生")
), None), input, "".into())
}
pub fn stack_bug(input: Input, loc: Location, stack_len: u32, block_id: usize, fn_name: &str) -> Self {
Self::new(ErrorCore::new(0, CompilerSystemError, loc, switch_lang!(
format!("the number of elements in the stack is invalid (num of elems: {stack_len}, block id: {block_id})\n\
this is a bug of the Erg compiler, please report it (https://github.com/...)\n\
caused from: {fn_name}"),
format!("スタックの要素数が異常です (要素数: {stack_len}, ブロックID: {block_id})\n\
(https://github.com/...)\n\
{fn_name}")
), None), input, "".into())
}
pub fn feature_error(input: Input, loc: Location, name: &str, caused_by: Str) -> Self {
Self::new(ErrorCore::new(0, FeatureError, loc, switch_lang!(
format!("this feature({name}) is not implemented yet"),
format!("この機能({name})はまだ正式に提供されていません")
), None), input, caused_by)
}
}
#[derive(Debug)]
pub struct TyCheckError {
pub core: ErrorCore,
pub caused_by: Str,
}
impl TyCheckError {
pub const fn new(core: ErrorCore, caused_by: Str) -> Self { Self{ core, caused_by } }
pub fn unreachable(fn_name: &str, line: u32) -> Self { Self::new(ErrorCore::unreachable(fn_name, line), "".into()) }
pub fn checker_bug(errno: usize, loc: Location, fn_name: &str, line: u32) -> Self {
Self::new(ErrorCore::new(errno, CompilerSystemError, loc, switch_lang!(
format!("this is a bug of the Erg compiler, please report it to https://github.com/...\ncaused from: {fn_name}:{line}"),
format!("これはErg compilerのバグです、開発者に報告して下さい (https://github.com/...)\n{fn_name}:{line}より発生")
), None), "".into())
}
pub fn feature_error(loc: Location, name: &str, caused_by: Str) -> Self {
Self::new(ErrorCore::new(0, FeatureError, loc, switch_lang!(
format!("this feature({name}) is not implemented yet"),
format!("この機能({name})はまだ正式に提供されていません")
), None), caused_by)
}
pub fn syntax_error<S: Into<Str>>(errno: usize, loc: Location, caused_by: Str, desc: S, hint: Option<Str>) -> Self {
Self::new(ErrorCore::new(errno, SyntaxError, loc, desc, hint), caused_by)
}
pub fn duplicate_decl_error(loc: Location, caused_by: Str, name: &str) -> Self {
let name = readable_name(name);
Self::new(ErrorCore::new(0, NameError, loc,
switch_lang!(
format!("{name} is already declared"),
format!("{name}は既に宣言されています")
), Option::<Str>::None),
caused_by
)
}
pub fn violate_decl_error(loc: Location, caused_by: Str, name: &str, spec_t: &Type, found_t: &Type) -> Self {
let name = readable_name(name);
Self::new(ErrorCore::new(0, TypeError, loc,
switch_lang!(
format!("{name} was declared as {GREEN}{spec_t}{RESET}, but an {RED}{found_t}{RESET} object is assigned"),
format!("{name}{GREEN}{spec_t}{RESET}型として宣言されましたが、{RED}{found_t}{RESET}型のオブジェクトが代入されています")
), Option::<Str>::None),
caused_by
)
}
pub fn no_type_spec_error(loc: Location, caused_by: Str, name: &str) -> Self {
let name = readable_name(name);
Self::new(ErrorCore::new(0, TypeError, loc,
switch_lang!(
format!("the type of {name} is not specified"),
format!("{name}の型が指定されていません")
), None),
caused_by
)
}
pub fn no_var_error(loc: Location, caused_by: Str, name: &str, similar_name: Option<&Str>) -> Self {
let name = readable_name(name);
let hint = similar_name.map(|n| {
let n = readable_name(n);
switch_lang!(
format!("exists a similar name variable: {n}"),
format!("似た名前の変数があります: {n}")
).into()
});
Self::new(ErrorCore::new(0, NameError, loc, switch_lang!(
format!("{RED}{name}{RESET} is not defined"),
format!("{RED}{name}{RESET}という変数は定義されていません")
), hint), caused_by)
}
pub fn no_attr_error(
loc: Location,
caused_by: Str,
obj_t: &Type,
name: &str,
similar_name: Option<&Str>,
) -> Self {
let hint = similar_name.map(|n| {
let n = readable_name(n);
switch_lang!(
format!("has a similar name attribute: {n}"),
format!("似た名前の属性があります: {n}")
).into()
});
Self::new(ErrorCore::new( 0, AttributeError, loc, switch_lang!(
format!("{obj_t} object has no attribute {RED}{name}{RESET}"),
format!("{obj_t}型オブジェクトに{RED}{name}{RESET}という属性はありません")
), hint),
caused_by,
)
}
pub fn callable_impl_error<'a, C: Locational + Display>(callee: &C, param_ts: impl Iterator<Item=&'a Type>, caused_by: Str) -> Self {
let param_ts = fmt_iter(param_ts);
Self::new(ErrorCore::new(0, NotImplementedError, callee.loc(), switch_lang!(
format!("{callee} is not a Callable object that takes {param_ts} as an argument"),
format!("{callee}{param_ts}を引数に取る呼び出し可能オブジェクトではありません")
), None), caused_by)
}
pub fn type_mismatch_error(
loc: Location,
caused_by: Str,
name: &str,
expect: &Type,
found: &Type,
) -> Self {
let name = readable_name(name);
Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!(
format!("the type of {name} is mismatched:\nexpected: {GREEN}{expect}{RESET}\nbut found: {RED}{found}{RESET}"),
format!("{name}の型が違います。\n予期した型: {GREEN}{expect}{RESET}\n与えられた型: {RED}{found}{RESET}")
), None), caused_by)
}
pub fn return_type_error(
loc: Location,
caused_by: Str,
name: &str,
expect: &Type,
found: &Type,
) -> Self {
let name = readable_name(name);
Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!(
format!("the return type of {name} is mismatched:\nexpected: {GREEN}{expect}{RESET}\nbut found: {RED}{found}{RESET}"),
format!("{name}の戻り値の型が違います。\n予期した型: {GREEN}{expect}{RESET}\n与えられた型: {RED}{found}{RESET}")
), None), caused_by)
}
pub fn uninitialized_error(loc: Location, caused_by: Str, name: &str, t: &Type) -> Self {
let name = readable_name(name);
Self::new(ErrorCore::new(0, NameError, loc, switch_lang!(
format!("{name}: {t} is not initialized"),
format!("{name}: {t}は初期化されていません")
), None), caused_by)
}
pub fn argument_error(loc: Location, caused_by: Str, expect: usize, found: usize) -> Self {
Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!(
format!("the number of positional arguments is mismatched:\nexpected: {GREEN}{expect}{RESET}\nbut found: {RED}{found}{RESET}"),
format!("ポジショナル引数の数が違います。\n予期した個数: {GREEN}{expect}{RESET}\n与えられた個数: {RED}{found}{RESET}")
), None), caused_by)
}
pub fn match_error(loc: Location, caused_by: Str, expr_t: &Type) -> Self {
Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!(
format!("not all patterns of type {expr_t} are covered"),
format!("{expr_t}型の全パターンを網羅していません")
), None), caused_by)
}
pub fn infer_error(loc: Location, caused_by: Str, expr: &str) -> Self {
Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!(
format!("failed to infer the type of {expr}"),
format!("{expr}の型が推論できません")
), None), caused_by)
}
pub fn reassign_error(loc: Location, caused_by: Str, name: &str) -> Self {
let name = readable_name(name);
Self::new(ErrorCore::new(0, AssignError, loc, switch_lang!(
format!("cannot assign twice to the immutable variable {name}"),
format!("定数{name}には再代入できません")
), None), caused_by)
}
pub fn too_many_args_error(
loc: Location,
callee_name: &str,
caused_by: Str,
params_len: usize,
pos_args_len: usize,
kw_args_len: usize
) -> Self {
let name = readable_name(callee_name);
Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!(
format!("too many arguments for {name}:
total expected params: {GREEN}{params_len}{RESET}
passed positional args: {RED}{pos_args_len}{RESET}
passed keyword args: {RED}{kw_args_len}{RESET}"),
format!("{name}に渡された引数の数が多すぎます。
: {GREEN}{params_len}{RESET}
: {RED}{pos_args_len}{RESET}
: {RED}{kw_args_len}{RESET}")
), None), caused_by)
}
pub fn multiple_args_error(
loc: Location,
callee_name: &str,
caused_by: Str,
arg_name: &str,
) -> Self {
let name = readable_name(callee_name);
Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!(
format!("{name}'s argument {RED}{arg_name}{RESET} is passed multiple times"),
format!("{name}の引数{RED}{arg_name}{RESET}が複数回渡されています")
), None), caused_by)
}
pub fn unexpected_kw_arg_error(
loc: Location,
callee_name: &str,
caused_by: Str,
param_name: &str,
) -> Self {
let name = readable_name(callee_name);
Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!(
format!("{name} got unexpected keyword argument {RED}{param_name}{RESET}"),
format!("{name}には予期しないキーワード引数{RED}{param_name}{RESET}が渡されています")
), None), caused_by)
}
pub fn unused_warning(loc: Location, name: &str, caused_by: Str) -> Self {
let name = readable_name(name);
Self::new(ErrorCore::new(0, UnusedWarning, loc, switch_lang!(
format!("{YELLOW}{name}{RESET} is not used"),
format!("{YELLOW}{name}{RESET}は使用されていません")
), None), caused_by)
}
pub fn unification_error(lhs_t: &Type, rhs_t: &Type, lhs_loc: Option<Location>, rhs_loc: Option<Location>, caused_by: Str) -> Self {
let loc = match (lhs_loc, rhs_loc) {
(Some(l), Some(r)) => Location::pair(l, r),
(Some(l), None) => l,
(None, Some(r)) => r,
(None, None) => Location::Unknown,
};
Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!(
format!("unification failed:\nlhs: {YELLOW}{lhs_t}{RESET}\nrhs: {YELLOW}{rhs_t}{RESET}"),
format!("型の単一化に失敗しました:\n左辺: {YELLOW}{lhs_t}{RESET}\n右辺: {YELLOW}{rhs_t}{RESET}")
), None), caused_by)
}
pub fn re_unification_error(lhs_t: &Type, rhs_t: &Type, lhs_loc: Option<Location>, rhs_loc: Option<Location>, caused_by: Str) -> Self {
let loc = match (lhs_loc, rhs_loc) {
(Some(l), Some(r)) => Location::pair(l, r),
(Some(l), None) => l,
(None, Some(r)) => r,
(None, None) => Location::Unknown,
};
Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!(
format!("re-unification failed:\nlhs: {YELLOW}{lhs_t}{RESET}\nrhs: {YELLOW}{rhs_t}{RESET}"),
format!("型の再単一化に失敗しました:\n左辺: {YELLOW}{lhs_t}{RESET}\n右辺: {YELLOW}{rhs_t}{RESET}")
), None), caused_by)
}
pub fn subtyping_error(sub_t: &Type, sup_t: &Type, sub_loc: Option<Location>, sup_loc: Option<Location>, caused_by: Str) -> Self {
let loc = match (sub_loc, sup_loc) {
(Some(l), Some(r)) => Location::pair(l, r),
(Some(l), None) => l,
(None, Some(r)) => r,
(None, None) => Location::Unknown,
};
Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!(
format!("subtype constraints cannot be satisfied:\nsubtype: {YELLOW}{sub_t}{RESET}\nsupertype: {YELLOW}{sup_t}{RESET}"),
format!("部分型制約を満たせません:\nサブタイプ: {YELLOW}{sub_t}{RESET}\nスーパータイプ: {YELLOW}{sup_t}{RESET}")
), None), caused_by)
}
pub fn pred_unification_error(lhs: &Predicate, rhs: &Predicate, caused_by: Str) -> Self {
Self::new(ErrorCore::new(0, TypeError, Location::Unknown, switch_lang!(
format!("predicate unification failed:\nlhs: {YELLOW}{lhs}{RESET}\nrhs: {YELLOW}{rhs}{RESET}"),
format!("述語式の単一化に失敗しました:\n左辺: {YELLOW}{lhs}{RESET}\n右辺: {YELLOW}{rhs}{RESET}")
), None), caused_by)
}
pub fn has_effect<S: Into<Str>>(expr: &Expr, caused_by: S) -> Self {
Self::new(ErrorCore::new(0, HasEffect, expr.loc(), switch_lang!(
format!("this expression causes a side-effect"),
format!("この式には副作用があります")
), None), caused_by.into())
}
pub fn move_error<S: Into<Str>>(name: &str, name_loc: Location, moved_loc: Location, caused_by: S) -> Self {
Self::new(ErrorCore::new(0, MoveError, name_loc, switch_lang!(
format!("{RED}{name}{RESET} was moved in line {}", moved_loc.ln_begin().unwrap()),
format!("{RED}{name}{RESET}{}行目ですでに移動されています", moved_loc.ln_begin().unwrap())
), None), caused_by.into())
}
}
#[derive(Debug)]
pub struct TyCheckErrors(Vec<TyCheckError>);
impl_stream_for_wrapper!(TyCheckErrors, TyCheckError);
impl From<Vec<TyCheckError>> for TyCheckErrors {
fn from(errs: Vec<TyCheckError>) -> Self { Self(errs) }
}
impl Add for TyCheckErrors {
type Output = Self;
fn add(self, other: Self) -> Self {
Self(self.0.into_iter().chain(other.0.into_iter()).collect())
}
}
impl From<TyCheckError> for TyCheckErrors {
fn from(err: TyCheckError) -> Self { Self(vec![err]) }
}
pub type TyCheckResult<T> = Result<T, TyCheckError>;
pub type TyCheckWarning = TyCheckError;
pub type TyCheckWarnings = TyCheckErrors;
pub type EvalError = TyCheckError;
pub type EvalErrors = TyCheckErrors;
pub type EvalResult<T> = TyCheckResult<T>;
pub type EffectError = TyCheckError;
pub type EffectErrors = TyCheckErrors;
pub type EffectResult<T> = Result<T, EffectErrors>;
pub type OwnershipError = TyCheckError;
pub type OwnershipErrors = TyCheckErrors;
pub type OwnershipResult<T> = Result<T, OwnershipErrors>;
pub type LowerError = TyCheckError;
pub type LowerWarning = LowerError;
pub type LowerErrors = TyCheckErrors;
pub type LowerWarnings = LowerErrors;
pub type LowerResult<T> = TyCheckResult<T>;
#[derive(Debug)]
pub struct CompileErrors(Vec<CompileError>);
impl_stream_for_wrapper!(CompileErrors, CompileError);
impl From<ParserRunnerErrors> for CompileErrors {
fn from(err: ParserRunnerErrors) -> Self {
Self(err.into_iter().map(CompileError::from).collect())
}
}
impl From<Vec<CompileError>> for CompileErrors {
fn from(errs: Vec<CompileError>) -> Self { Self(errs) }
}
impl From<CompileError> for CompileErrors {
fn from(err: CompileError) -> Self { Self(vec![err]) }
}
impl MultiErrorDisplay<CompileError> for CompileErrors {}
impl CompileErrors {
pub fn flush(&mut self) -> Self {
Self(self.0.drain(..).collect())
}
}
pub type CompileResult<T> = Result<T, CompileError>;
pub type CompileWarning = CompileError;
pub type CompileWarnings = CompileErrors;

View file

@ -0,0 +1,461 @@
use std::mem;
use erg_common::Str;
use erg_common::{fn_name, set};
use erg_common::value::ValueObj;
use erg_common::dict::Dict;
use erg_common::rccell::RcCell;
use erg_common::set::{Set};
use erg_common::traits::Stream;
use erg_common::ty::{OpKind, TyParam, Type, Predicate, TyBound, ConstObj, SubrKind};
use OpKind::*;
use erg_parser::ast::*;
use erg_parser::token::Token;
use crate::context::{Context, TyVarContext};
use crate::error::{EvalError, EvalResult, TyCheckResult};
/// SubstContext::new([?T; 0], Context(Array(T, N))) => SubstContext{ params: { T: ?T; N: 0 } }
/// SubstContext::substitute([T; !N], Context(Array(T, N))): [?T; !0]
#[derive(Debug)]
struct SubstContext {
params: Dict<Str, TyParam>,
}
impl SubstContext {
pub fn new(substituted: &Type, ty_ctx: &Context) -> Self {
let param_names = ty_ctx.impls.iter()
.filter(|(_, vi)| vi.kind.is_parameter())
.map(|(name, _)| name.inspect().clone());
let self_ = SubstContext{
params: param_names.zip(substituted.typarams().into_iter()).collect(),
};
// REVIEW: 順番は保証されるか? 引数がunnamed_paramsに入る可能性は?
self_
}
fn substitute(&self, quant_t: Type, ty_ctx: &Context, level: usize) -> TyCheckResult<Type> {
let bounds = ty_ctx.bounds();
let tv_ctx = TyVarContext::new(level, bounds);
let (inst, _) = Context::instantiate_t(quant_t, tv_ctx);
for param in inst.typarams() {
self.substitute_tp(&param, ty_ctx)?;
}
Ok(inst)
}
fn substitute_tp(&self, param: &TyParam, ty_ctx: &Context) -> TyCheckResult<()> {
match param {
TyParam::FreeVar(fv) => {
if let Some(name) = fv.unbound_name() {
if let Some(v) = self.params.get(&name) {
ty_ctx.unify_tp(param, v, None, false)?;
}
} else {
if fv.is_unbound() { panic!() }
}
}
TyParam::BinOp{ lhs, rhs, .. } => {
self.substitute_tp(lhs, ty_ctx)?;
self.substitute_tp(rhs, ty_ctx)?;
}
TyParam::UnaryOp{ val, .. } => {
self.substitute_tp(val, ty_ctx)?;
}
TyParam::Array(args)
| TyParam::Tuple(args)
| TyParam::App{ args, .. }
| TyParam::PolyQVar{ args, .. } => {
for arg in args.iter() {
self.substitute_tp(arg, ty_ctx)?;
}
}
TyParam::Type(t) => { self.substitute_t(t, ty_ctx)?; },
TyParam::MonoProj{ obj, attr } => todo!("{obj}.{attr}"),
_ => {}
}
Ok(())
}
fn substitute_t(&self, t: &Type, ty_ctx: &Context) -> TyCheckResult<()> {
match t {
Type::FreeVar(fv) => {
if let Some(name) = fv.unbound_name() {
if let Some(v) = self.params.get(&name) {
if let TyParam::Type(v) = v {
ty_ctx.unify(t, v, None, None)?;
} else {
panic!()
}
}
}
}
t => todo!("{t}"),
}
Ok(())
}
}
#[derive(Debug, Default)]
pub struct Evaluator {
}
impl Evaluator {
#[inline]
pub fn new() -> Self { Self::default() }
#[inline]
pub(crate) fn eval_const_lit(&self, lit: &Literal) -> ValueObj { ValueObj::from(lit) }
fn eval_const_acc(&self, _acc: &Accessor) -> Option<ValueObj> {
todo!()
}
fn eval_const_bin(&self, _bin: &BinOp) -> Option<ValueObj> {
todo!()
}
fn eval_const_unary(&self, _unary: &UnaryOp) -> Option<ValueObj> {
todo!()
}
// TODO: kw args
fn eval_args(&self, _args: &Args) -> Option<Vec<ValueObj>> {
todo!()
}
fn eval_const_call(&self, call: &Call, ctx: &Context) -> Option<ValueObj> {
if let Expr::Accessor(acc) = call.obj.as_ref() {
match acc {
Accessor::Local(name) if name.is_const() => {
if let Some(ConstObj::Subr(subr)) = ctx.consts.get(name.inspect()) {
let args = self.eval_args(&call.args)?;
Some(subr.call(args))
} else {
None
}
}
Accessor::Local(_) => None,
Accessor::Attr(_attr) => todo!(),
Accessor::TupleAttr(_attr) => todo!(),
Accessor::SelfDot(_name) => todo!(),
Accessor::Subscr(_subscr) => todo!(),
}
} else { None }
}
fn eval_const_def(&self, def: &Def) -> Option<ValueObj> {
if def.is_const() {
todo!()
}
None
}
// ConstExprを評価するのではなく、コンパイル時関数の式(AST上ではただのExpr)を評価する
// コンパイル時評価できないならNoneを返す
pub(crate) fn eval_const_expr(&self, expr: &Expr, ctx: &Context) -> Option<ValueObj> {
match expr {
Expr::Lit(lit) => Some(self.eval_const_lit(lit)),
Expr::Accessor(acc) => self.eval_const_acc(acc),
Expr::BinOp(bin) => self.eval_const_bin(bin),
Expr::UnaryOp(unary) => self.eval_const_unary(unary),
Expr::Call(call) => self.eval_const_call(call, ctx),
Expr::Def(def) => self.eval_const_def(def),
other => todo!("{other}"),
}
}
pub(crate) fn eval_const_block(&self, block: &Block, ctx: &Context) -> Option<ValueObj> {
for chunk in block.iter().rev().skip(1).rev() {
self.eval_const_expr(chunk, ctx)?;
}
self.eval_const_expr(block.last().unwrap(), ctx)
}
fn eval_bin_lit(&self, op: OpKind, lhs: ValueObj, rhs: ValueObj) -> EvalResult<ValueObj> {
match op {
Add => lhs.try_add(rhs).ok_or(EvalError::unreachable(fn_name!(), line!())),
Sub => lhs.try_sub(rhs).ok_or(EvalError::unreachable(fn_name!(), line!())),
Mul => lhs.try_mul(rhs).ok_or(EvalError::unreachable(fn_name!(), line!())),
Div => lhs.try_div(rhs).ok_or(EvalError::unreachable(fn_name!(), line!())),
Gt => lhs.try_gt(rhs).ok_or(EvalError::unreachable(fn_name!(), line!())),
Ge => lhs.try_ge(rhs).ok_or(EvalError::unreachable(fn_name!(), line!())),
Eq => lhs.try_eq(rhs).ok_or(EvalError::unreachable(fn_name!(), line!())),
Ne => lhs.try_ne(rhs).ok_or(EvalError::unreachable(fn_name!(), line!())),
other => todo!("{other}"),
}
}
pub(crate) fn eval_bin_tp(&self, op: OpKind, lhs: &TyParam, rhs: &TyParam) -> EvalResult<TyParam> {
match (lhs, rhs) {
(TyParam::ConstObj(ConstObj::Value(lhs)), TyParam::ConstObj(ConstObj::Value(rhs))) =>
self.eval_bin_lit(op, lhs.clone(), rhs.clone())
.map(|v| TyParam::value(v)),
(TyParam::ConstObj(ConstObj::MutValue(lhs)), TyParam::ConstObj(ConstObj::Value(rhs))) =>
self.eval_bin_lit(op, lhs.borrow().clone(), rhs.clone())
.map(|v| TyParam::ConstObj(ConstObj::MutValue(RcCell::new(v)))),
(TyParam::FreeVar(fv), r) => {
if fv.is_linked() {
self.eval_bin_tp(op, &*fv.crack(), r)
} else {
Err(EvalError::unreachable(fn_name!(), line!()))
}
},
(l, TyParam::FreeVar(fv)) => {
if fv.is_linked() {
self.eval_bin_tp(op, l, &*fv.crack())
} else {
Err(EvalError::unreachable(fn_name!(), line!()))
}
},
(e @ TyParam::Erased(_), _)
| (_, e @ TyParam::Erased(_)) => Ok(e.clone()),
(l, r) => todo!("{l} {op} {r}"),
}
}
fn eval_unary_lit(&self, op: OpKind, val: ConstObj) -> EvalResult<ConstObj> {
match op {
Pos => todo!(),
Neg => todo!(),
Invert => todo!(),
Mutate => if let ConstObj::Value(v) = val {
Ok(ConstObj::MutValue(RcCell::new(v)))
} else { todo!() },
other => todo!("{other}"),
}
}
fn eval_unary_tp(&self, op: OpKind, val: &TyParam) -> EvalResult<TyParam> {
match val {
TyParam::ConstObj(c) =>
self.eval_unary_lit(op, c.clone()).map(|c| TyParam::cons(c)),
TyParam::FreeVar(fv) if fv.is_linked() => {
self.eval_unary_tp(op, &*fv.crack())
},
e @ TyParam::Erased(_) => Ok(e.clone()),
other => todo!("{op} {other}"),
}
}
fn eval_app(&self, _name: &Str, _args: &Vec<TyParam>) -> EvalResult<TyParam> {
todo!()
}
/// 量化変数などはそのまま返す
pub(crate) fn eval_tp(&self, p: &TyParam, ctx: &Context) -> EvalResult<TyParam> {
match p {
TyParam::FreeVar(fv) if fv.is_linked() =>
self.eval_tp(&fv.crack(), ctx),
TyParam::Mono(name) =>
ctx.consts.get(name)
.and_then(|c| match c {
ConstObj::Value(v) => Some(TyParam::value(v.clone())),
_ => None,
}).ok_or(EvalError::unreachable(fn_name!(), line!())),
TyParam::BinOp{ op, lhs, rhs } =>
self.eval_bin_tp(*op, lhs, rhs),
TyParam::UnaryOp{ op, val } =>
self.eval_unary_tp(*op, val),
TyParam::App{ name, args } =>
self.eval_app(name, args),
p @ (
TyParam::Type(_) | TyParam::Erased(_) | TyParam::ConstObj(_) | TyParam::FreeVar(_) | TyParam::MonoQVar(_)
) => Ok(p.clone()),
other => todo!("{other}"),
}
}
pub(crate) fn eval_t(&self, substituted: Type, ctx: &Context, level: usize) -> EvalResult<Type> {
match substituted {
Type::FreeVar(fv) if fv.is_linked() =>
self.eval_t(fv.crack().clone(), ctx, level),
Type::Subr(mut subr) => {
let kind = match subr.kind {
SubrKind::FuncMethod(self_t) => {
SubrKind::fn_met(self.eval_t(*self_t, ctx, level)?)
}
SubrKind::ProcMethod{ before, after } => {
let before = self.eval_t(*before, ctx, level)?;
if let Some(after) = after {
let after = self.eval_t(*after, ctx, level)?;
SubrKind::pr_met(before, Some(after))
} else {
SubrKind::pr_met(before, None)
}
}
other => other,
};
for p in subr.non_default_params.iter_mut() {
p.ty = self.eval_t(mem::take(&mut p.ty), ctx, level)?;
}
for p in subr.default_params.iter_mut() {
p.ty = self.eval_t(mem::take(&mut p.ty), ctx, level)?;
}
let return_t = self.eval_t(*subr.return_t, ctx, level)?;
Ok(Type::subr(kind, subr.non_default_params, subr.default_params, return_t))
},
Type::Array{ t, len } => {
let t = self.eval_t(*t, ctx, level)?;
let len = self.eval_tp(&len, ctx)?;
Ok(Type::array(t, len))
},
Type::Refinement(refine) => {
let mut preds = Set::with_capacity(refine.preds.len());
for pred in refine.preds.into_iter() {
preds.insert(self.eval_pred(pred, ctx)?);
}
Ok(Type::refinement(refine.var, *refine.t, preds))
},
// [?T; 0].MutType! == [?T; !0]
Type::MonoProj{ lhs, rhs } => {
for ty_ctx in ctx.get_sorted_supertypes(&lhs) {
if let Ok(obj) = ty_ctx.get_local(&Token::symbol(&rhs), &ctx.name) {
if let ConstObj::Type(quant_t) = obj {
let subst_ctx = SubstContext::new(&lhs, ty_ctx);
let t = subst_ctx.substitute(*quant_t, ty_ctx, level)?;
let t = self.eval_t(t, ctx, level)?;
return Ok(t)
} else { todo!() }
}
}
todo!()
},
Type::Range(l) => Ok(Type::range(self.eval_t(*l, ctx, level)?)),
Type::Iter(l) => Ok(Type::iter(self.eval_t(*l, ctx, level)?)),
Type::Ref(l) => Ok(Type::refer(self.eval_t(*l, ctx, level)?)),
Type::RefMut(l) => Ok(Type::ref_mut(self.eval_t(*l, ctx, level)?)),
Type::Option(l) => Ok(Type::option_mut(self.eval_t(*l, ctx, level)?)),
Type::OptionMut(l) => Ok(Type::option_mut(self.eval_t(*l, ctx, level)?)),
Type::VarArgs(l) => Ok(Type::var_args(self.eval_t(*l, ctx, level)?)),
Type::Poly{ name, mut params } => {
for p in params.iter_mut() {
*p = self.eval_tp(&mem::take(p), ctx)?;
}
Ok(Type::poly(name, params))
},
other if other.is_monomorphic() => Ok(other),
other => todo!("{other}"),
}
}
pub(crate) fn _eval_bound(&self, bound: TyBound, ctx: &Context, level: usize) -> EvalResult<TyBound> {
match bound {
TyBound::Subtype{ sub, sup } =>
Ok(TyBound::subtype(
self.eval_t(sub, ctx, level)?,
self.eval_t(sup, ctx, level)?
)),
TyBound::Supertype{ sup, sub } =>
Ok(TyBound::supertype(
self.eval_t(sup, ctx, level)?,
self.eval_t(sub, ctx, level)?
)),
TyBound::Sandwiched { sub, mid, sup } => {
let sub = self.eval_t(sub, ctx, level)?;
let mid = self.eval_t(mid, ctx, level)?;
let sup = self.eval_t(sup, ctx, level)?;
Ok(TyBound::sandwiched(sub, mid, sup))
},
TyBound::Instance{ name: inst, t } =>
Ok(TyBound::instance(inst, self.eval_t(t, ctx, level)?)),
}
}
pub(crate) fn eval_pred(&self, p: Predicate, ctx: &Context) -> EvalResult<Predicate> {
match p {
Predicate::Value(_) | Predicate::Const(_) => Ok(p),
Predicate::Equal{ lhs, rhs } =>
Ok(Predicate::eq(lhs, self.eval_tp(&rhs, ctx)?)),
Predicate::NotEqual{ lhs, rhs } =>
Ok(Predicate::ne(lhs, self.eval_tp(&rhs, ctx)?)),
Predicate::LessEqual{ lhs, rhs } =>
Ok(Predicate::le(lhs, self.eval_tp(&rhs, ctx)?)),
Predicate::GreaterEqual{ lhs, rhs } =>
Ok(Predicate::ge(lhs, self.eval_tp(&rhs, ctx)?)),
Predicate::And(l, r) =>
Ok(Predicate::and(self.eval_pred(*l, ctx)?, self.eval_pred(*r, ctx)?)),
Predicate::Or(l, r) =>
Ok(Predicate::or(self.eval_pred(*l, ctx)?, self.eval_pred(*r, ctx)?)),
Predicate::Not(l, r) =>
Ok(Predicate::not(self.eval_pred(*l, ctx)?, self.eval_pred(*r, ctx)?)),
}
}
pub(crate) fn get_tp_t(&self, p: &TyParam, bounds: Option<&Set<TyBound>>, ctx: &Context) -> EvalResult<Type> {
let p = self.eval_tp(p, ctx)?;
match p {
TyParam::ConstObj(ConstObj::Value(v)) => Ok(Type::enum_t(set![v])),
TyParam::ConstObj(ConstObj::MutValue(v)) => Ok(v.borrow().class().mutate()),
TyParam::Erased(t) => Ok((&*t).clone()),
TyParam::FreeVar(fv) =>
if let Some(t) = fv.type_of() { Ok(t) } else { todo!() },
TyParam::Type(_) => Ok(Type::Type),
TyParam::Mono(name) =>
ctx.consts.get(&name)
.and_then(|c| match c {
ConstObj::Value(v) => Some(Type::enum_t(set![v.clone()])),
_ => None,
}).ok_or(EvalError::unreachable(fn_name!(), line!())),
TyParam::MonoQVar(name) => {
if let Some(bs) = bounds {
if let Some(bound) = bs.iter().find(|b| b.mentions_as_instance(&name)) {
Ok(bound.t().clone())
} else { todo!() }
} else { todo!() }
},
TyParam::UnaryOp{ op, val } => {
match op {
OpKind::Mutate => Ok(self.get_tp_t(&val, bounds, ctx)?.mutate()),
_ => todo!(),
}
},
other => todo!("{other}"),
}
}
pub(crate) fn _get_tp_class(&self, p: &TyParam, ctx: &Context) -> EvalResult<Type> {
let p = self.eval_tp(p, ctx)?;
match p {
TyParam::ConstObj(ConstObj::Value(v)) => Ok(v.class()),
TyParam::Erased(t) => Ok((&*t).clone()),
| TyParam::FreeVar(fv) =>
if let Some(t) = fv.type_of() { Ok(t) } else { todo!() },
TyParam::Type(_) => Ok(Type::Type),
TyParam::Mono(name) =>
ctx.consts.get(&name)
.and_then(|c| match c {
ConstObj::Value(v) => Some(v.class()),
_ => None,
}).ok_or(EvalError::unreachable(fn_name!(), line!())),
other => todo!("{other}"),
}
}
/// NOTE: lとrが型の場合はContextの方で判定する
pub(crate) fn shallow_eq_tp(&self, lhs: &TyParam, rhs: &TyParam, ctx: &Context) -> bool {
match (lhs, rhs) {
(TyParam::Type(l), TyParam::Type(r)) => l == r,
(TyParam::ConstObj(l), TyParam::ConstObj(r)) => l == r,
(TyParam::Erased(l), TyParam::Erased(r)) => l == r,
(TyParam::FreeVar{ .. }, TyParam::FreeVar{ .. }) => true,
(TyParam::Mono(l), TyParam::Mono(r)) => {
if l == r { true }
else if let (Some(l), Some(r)) = (ctx.consts.get(l), ctx.consts.get(r)) { l == r }
else {
// lとrが型の場合は...
false
}
},
(TyParam::BinOp{ .. }, TyParam::BinOp{ .. }) => todo!(),
(TyParam::UnaryOp{ .. }, TyParam::UnaryOp{ .. }) => todo!(),
(TyParam::App{ .. }, TyParam::App{ .. }) => todo!(),
(TyParam::Mono(m), TyParam::ConstObj(l))
| (TyParam::ConstObj(l), TyParam::Mono(m)) =>
if let Some(o) = ctx.consts.get(m) { o == l } else { true },
(TyParam::MonoQVar(_), _) | (_, TyParam::MonoQVar(_)) => false,
(l, r) => todo!("l: {l}, r: {r}"),
}
}
}

View file

@ -0,0 +1,876 @@
/// defines High-level Intermediate Representation
use std::fmt;
use erg_common::{Str};
use erg_common::value::ValueObj;
use erg_common::error::Location;
use erg_common::traits::{HasType, Locational, Stream, NestedDisplay};
use erg_common::ty::{Type, TyParam, Constraint};
use erg_common::{
impl_locational, impl_locational_for_enum,
impl_stream_for_wrapper, impl_display_for_enum,
impl_nested_display_for_enum, impl_display_from_nested,
};
use erg_parser::token::{Token, TokenKind};
use erg_parser::ast::{VarName, VarPattern, Params, DefId, fmt_lines};
#[derive(Debug, Clone)]
pub struct Literal {
pub data: ValueObj, // for constant folding
pub token: Token, // for Locational
t: Type,
}
impl HasType for Literal {
#[inline]
fn ref_t(&self) -> &Type { &self.t }
#[inline]
fn signature_t(&self) -> Option<&Type> { None }
}
impl NestedDisplay for Literal {
fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result {
write!(f, "{}", self.token)
}
}
impl_display_from_nested!(Literal);
impl Locational for Literal {
#[inline]
fn loc(&self) -> Location { self.token.loc() }
}
impl From<Token> for Literal {
fn from(token: Token) -> Self {
let data = ValueObj::from_str(Type::from(token.kind), token.content.clone());
Self { t: data.t(), data, token }
}
}
impl Literal {
pub fn new(c: ValueObj, lineno: usize, col: usize) -> Self {
let kind = TokenKind::from(&c);
let token = Token::new(kind, c.to_string(), lineno, col);
Self { t: c.t(), data: c, token }
}
#[inline]
pub fn is(&self, kind: TokenKind) -> bool {
self.token.is(kind)
}
}
#[derive(Debug, Clone)]
pub struct PosArg {
pub expr: Expr,
}
impl NestedDisplay for PosArg {
fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, level: usize) -> std::fmt::Result {
self.expr.fmt_nest(f, level)
}
}
impl_display_from_nested!(PosArg);
impl Locational for PosArg {
fn loc(&self) -> Location { self.expr.loc() }
}
impl PosArg {
pub const fn new(expr: Expr) -> Self { Self { expr } }
}
#[derive(Debug, Clone)]
pub struct KwArg {
pub keyword: Token,
pub expr: Expr,
}
impl NestedDisplay for KwArg {
fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, level: usize) -> std::fmt::Result {
write!(f, "{}:\n", self.keyword)?;
self.expr.fmt_nest(f, level + 1)
}
}
impl_display_from_nested!(KwArg);
impl Locational for KwArg {
fn loc(&self) -> Location {
Location::concat(&self.keyword, &self.expr)
}
}
impl KwArg {
pub const fn new(keyword: Token, expr: Expr) -> Self { Self { keyword, expr } }
}
#[derive(Debug, Clone)]
pub struct Args {
pos_args: Vec<PosArg>,
kw_args: Vec<KwArg>,
paren: Option<(Token, Token)>,
}
impl NestedDisplay for Args {
fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, level: usize) -> std::fmt::Result {
if !self.pos_args.is_empty() { fmt_lines(self.pos_args.iter(), f, level)?; }
if !self.kw_args.is_empty() { fmt_lines(self.kw_args.iter(), f, level)?; }
Ok(())
}
}
impl_display_from_nested!(Args);
impl Locational for Args {
fn loc(&self) -> Location {
if let Some((l, r)) = &self.paren {
Location::concat(l, r)
} else if !self.kw_args.is_empty() {
Location::concat(self.kw_args.first().unwrap(), self.kw_args.last().unwrap())
} else if !self.pos_args.is_empty() {
Location::concat(self.pos_args.first().unwrap(), self.pos_args.last().unwrap())
} else {
Location::Unknown
}
}
}
// impl_stream!(Args, KwArg, kw_args);
impl Args {
pub const fn new(pos_args: Vec<PosArg>, kw_args: Vec<KwArg>, paren: Option<(Token, Token)>) -> Self {
Self { pos_args, kw_args, paren }
}
pub const fn empty() -> Self {
Self::new(vec![], vec![], None)
}
#[inline]
pub fn len(&self) -> usize {
self.pos_args.len() + self.kw_args.len()
}
#[inline]
pub fn kw_len(&self) -> usize { self.kw_args.len() }
pub fn pos_args(&self) -> &[PosArg] { &self.pos_args[..] }
pub fn kw_args(&self) -> &[KwArg] { &self.kw_args[..] }
pub fn push_pos(&mut self, pos: PosArg) {
self.pos_args.push(pos);
}
pub fn push_kw(&mut self, kw: KwArg) {
self.kw_args.push(kw);
}
pub fn remove(&mut self, index: usize) -> Expr {
if self.pos_args.get(index).is_some() {
self.pos_args.remove(index).expr
} else {
self.kw_args.remove(index-self.pos_args.len()).expr
}
}
/// try_remove((1, 2, z: 3), 2) == Some(3)
pub fn try_remove(&mut self, index: usize) -> Option<Expr> {
if self.pos_args.get(index).is_some() {
Some(self.pos_args.remove(index).expr)
} else {
self.kw_args.get(index-self.pos_args.len())?;
Some(self.kw_args.remove(index-self.pos_args.len()).expr)
}
}
pub fn try_remove_pos(&mut self, index: usize) -> Option<PosArg> {
self.pos_args.get(index)?;
Some(self.pos_args.remove(index))
}
pub fn try_remove_kw(&mut self, index: usize) -> Option<KwArg> {
self.kw_args.get(index)?;
Some(self.kw_args.remove(index))
}
pub fn get(&self, index: usize) -> Option<&Expr> {
if self.pos_args.get(index).is_some() {
self.pos_args.get(index).map(|a| &a.expr)
} else {
self.kw_args.get(index-self.pos_args.len()).map(|a| &a.expr)
}
}
}
/// represents local variables
#[derive(Debug, Clone)]
pub struct Local {
pub name: Token,
/// オブジェクト自身の名前
__name__: Option<Str>,
t: Type,
}
impl fmt::Display for Local {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let __name__ = if let Some(__name__) = self.__name__() {
format!("(__name__ = {__name__})")
} else { "".to_string() };
if self.t != Type::ASTOmitted {
write!(f, "{} (: {}){}", self.name.content, self.t, __name__)
} else {
write!(f, "{}{}", self.name.content, __name__)
}
}
}
impl HasType for Local {
#[inline]
fn ref_t(&self) -> &Type { &self.t }
#[inline]
fn signature_t(&self) -> Option<&Type> { None }
}
impl Locational for Local {
#[inline]
fn loc(&self) -> Location { self.name.loc() }
}
impl Local {
pub const fn new(name: Token, __name__: Option<Str>, t: Type) -> Self { Self{ name, __name__, t } }
// &strにするとクローンしたいときにアロケーションコストがかかるので&Strのままで
#[inline]
pub fn inspect(&self) -> &Str { &self.name.content }
pub const fn __name__(&self) -> Option<&Str> { self.__name__.as_ref() }
}
#[derive(Debug, Clone)]
pub struct Attribute {
pub obj: Box<Expr>,
pub name: Token,
t: Type
}
impl fmt::Display for Attribute {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({}).{}", self.obj, self.name)
}
}
impl_locational!(Attribute, obj, name);
impl HasType for Attribute {
#[inline]
fn ref_t(&self) -> &Type { &self.t }
#[inline]
fn signature_t(&self) -> Option<&Type> { None }
}
impl Attribute {
pub fn new(obj: Expr, name: Token, t: Type) -> Self {
Self { obj: Box::new(obj), name, t }
}
}
#[derive(Debug, Clone)]
pub struct Subscript {
obj: Box<Expr>,
index: Box<Expr>,
t: Type
}
impl fmt::Display for Subscript {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({})[{}]", self.obj, self.index)
}
}
impl_locational!(Subscript, obj, index);
impl HasType for Subscript {
#[inline]
fn ref_t(&self) -> &Type { &self.t }
#[inline]
fn signature_t(&self) -> Option<&Type> { None }
}
impl Subscript {
pub fn new(obj: Expr, index: Expr, t: Type) -> Self {
Self { obj: Box::new(obj), index: Box::new(index), t }
}
}
#[derive(Debug, Clone)]
pub enum Accessor {
Local(Local),
SelfDot(Local),
Attr(Attribute),
Subscr(Subscript),
}
impl NestedDisplay for Accessor {
fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result {
match self {
Self::Local(name) => write!(f, "{}", name),
Self::SelfDot(attr) => write!(f, ".{}", attr),
Self::Attr(attr) => write!(f, "{}", attr),
Self::Subscr(subscr) => write!(f, "{}", subscr),
}
}
}
impl_display_from_nested!(Accessor);
impl_locational_for_enum!(Accessor; Local, SelfDot, Attr, Subscr);
impl HasType for Accessor {
#[inline]
fn ref_t(&self) -> &Type {
match self {
Self::Local(n) | Self::SelfDot(n) => n.ref_t(),
Self::Attr(a) => a.ref_t(),
Self::Subscr(s) => s.ref_t(),
}
}
#[inline]
fn signature_t(&self) -> Option<&Type> { None }
}
impl Accessor {
pub const fn local(symbol: Token, t: Type) -> Self { Self::Local(Local::new(symbol, None, t)) }
pub const fn self_dot(name: Token, t: Type) -> Self { Self::SelfDot(Local::new(name, None, t)) }
pub fn attr(obj: Expr, name: Token, t: Type) -> Self {
Self::Attr(Attribute::new(obj, name, t))
}
pub fn subscr(obj: Expr, index: Expr, t: Type) -> Self {
Self::Subscr(Subscript::new(obj, index, t))
}
pub fn var_full_name(&self) -> Option<String> {
match self {
Self::Local(local) => Some(local.inspect().to_string()),
Self::Attr(attr) =>
attr.obj.var_full_name().map(|n| n + "." + attr.name.inspect()),
Self::Subscr(_)
| Self::SelfDot(_) => todo!(),
}
}
// 参照するオブジェクト自体が持っている固有の名前
pub fn __name__(&self) -> Option<&str> {
match self {
Self::Local(local)
| Self::SelfDot(local) => local.__name__().map(|s| &s[..]),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct Array {
pub l_sqbr: Token,
pub r_sqbr: Token,
t: Type,
pub elems: Args,
pub guard: Option<Box<Expr>>,
}
impl HasType for Array {
#[inline]
fn ref_t(&self) -> &Type { &self.t }
#[inline]
fn signature_t(&self) -> Option<&Type> { None }
}
impl NestedDisplay for Array {
fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result {
if let Some(guard) = &self.guard {
write!(f, "[{} | {}]", self.elems, guard)
} else {
write!(f, "[{}]", self.elems)
}
}
}
impl_display_from_nested!(Array);
impl_locational!(Array, l_sqbr, r_sqbr);
impl Array {
pub fn new(l_sqbr: Token, r_sqbr: Token, level: usize, elems: Args, guard: Option<Expr>) -> Self {
let elem_t = elems.pos_args().first()
.map(|a| a.expr.t())
.unwrap_or_else(|| Type::free_var(level, Constraint::TypeOf(Type::Type)));
let t = Type::array(elem_t, TyParam::value(elems.len()));
Self { l_sqbr, r_sqbr, t, elems, guard: guard.map(Box::new) }
}
pub fn push(&mut self, elem: Expr) {
self.elems.push_pos(PosArg::new(elem));
}
}
#[derive(Debug, Clone)]
pub struct Dict {
pub l_brace: Token,
pub r_brace: Token,
pub attrs: Args, // TODO: keyをTokenではなくExprにする
}
impl HasType for Dict {
fn ref_t(&self) -> &Type { todo!() }
fn t(&self) -> Type { todo!() }
#[inline]
fn signature_t(&self) -> Option<&Type> { None }
}
impl NestedDisplay for Dict {
fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result {
write!(f, "{{{}}}", self.attrs)
}
}
impl_display_from_nested!(Dict);
impl_locational!(Dict, l_brace, r_brace);
impl Dict {
pub const fn new(l_brace: Token, r_brace: Token, attrs: Args) -> Self {
Self { l_brace, r_brace, attrs }
}
}
#[derive(Debug, Clone)]
pub struct BinOp {
pub op: Token,
pub lhs: Box<Expr>,
pub rhs: Box<Expr>,
pub sig_t: Type, // e.g. (Int, Int) -> Int
}
impl NestedDisplay for BinOp {
fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result {
write!(f, "`{}`:\n", self.op.content)?;
self.lhs.fmt_nest(f, level + 1)?;
write!(f, "\n")?;
self.rhs.fmt_nest(f, level + 1)
}
}
impl HasType for BinOp {
#[inline]
fn ref_t(&self) -> &Type { &self.sig_t.return_t().unwrap() }
#[inline]
fn lhs_t(&self) -> &Type { &self.sig_t.lhs_t() }
#[inline]
fn rhs_t(&self) -> &Type { &self.sig_t.rhs_t() }
#[inline]
fn signature_t(&self) -> Option<&Type> { Some(&self.sig_t) }
}
impl_display_from_nested!(BinOp);
impl_locational!(BinOp, lhs, rhs);
impl BinOp {
pub fn new(op: Token, lhs: Expr, rhs: Expr, sig_t: Type) -> Self {
Self { op, lhs: Box::new(lhs), rhs: Box::new(rhs), sig_t }
}
}
#[derive(Debug, Clone)]
pub struct UnaryOp {
pub op: Token,
pub expr: Box<Expr>,
pub sig_t: Type, // e.g. Neg -> Nat
}
impl HasType for UnaryOp {
#[inline]
fn ref_t(&self) -> &Type { &self.sig_t.return_t().unwrap() }
#[inline]
fn lhs_t(&self) -> &Type { self.expr.ref_t() }
#[inline]
fn rhs_t(&self) -> &Type { panic!("invalid operation") }
#[inline]
fn signature_t(&self) -> Option<&Type> { Some(&self.sig_t) }
}
impl NestedDisplay for UnaryOp {
fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result {
write!(f, "`{}`: {}:\n", self.op, self.sig_t)?;
self.expr.fmt_nest(f, level + 1)
}
}
impl_display_from_nested!(UnaryOp);
impl_locational!(UnaryOp, op, expr);
impl UnaryOp {
pub fn new(op: Token, expr: Expr, sig_t: Type) -> Self {
Self { op, expr: Box::new(expr), sig_t }
}
}
#[derive(Debug, Clone)]
pub struct Call {
pub obj: Box<Expr>,
pub args: Args,
/// 全体の型、e.g. `abs(-1)` -> `Neg -> Nat`
/// necessary for mangling
pub sig_t: Type,
}
impl NestedDisplay for Call {
fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, level: usize) -> std::fmt::Result {
write!(f, "({}): {}:\n", self.obj, self.sig_t)?;
self.args.fmt_nest(f, level + 1)
}
}
impl_display_from_nested!(Call);
impl HasType for Call {
#[inline]
fn ref_t(&self) -> &Type { &self.sig_t.return_t().unwrap() }
#[inline]
fn lhs_t(&self) -> &Type { self.sig_t.lhs_t() }
#[inline]
fn rhs_t(&self) -> &Type { self.sig_t.rhs_t() }
#[inline]
fn signature_t(&self) -> Option<&Type> { Some(&self.sig_t) }
}
impl Locational for Call {
fn loc(&self) -> Location {
Location::concat(self.obj.as_ref(), &self.args)
}
}
impl Call {
pub fn new(obj: Expr, args: Args, sig_t: Type) -> Self {
Self { obj: Box::new(obj), args, sig_t }
}
pub fn is_import_call(&self) -> bool {
self.obj.var_full_name()
.map(|s| &s[..] == "import" || &s[..] == "pyimport")
.unwrap_or(false)
}
}
#[derive(Debug, Clone)]
pub struct Block(Vec<Expr>);
impl HasType for Block {
#[inline]
fn ref_t(&self) -> &Type { self.last().unwrap().ref_t() }
#[inline]
fn t(&self) -> Type { self.last().unwrap().t() }
#[inline]
fn signature_t(&self) -> Option<&Type> { self.last().unwrap().signature_t() }
}
impl NestedDisplay for Block {
fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result {
fmt_lines(self.0.iter(), f, level)
}
}
impl_display_from_nested!(Block);
impl_stream_for_wrapper!(Block, Expr);
impl Locational for Block {
fn loc(&self) -> Location {
Location::concat(self.0.first().unwrap(), self.0.last().unwrap())
}
}
#[derive(Debug, Clone, Hash)]
pub struct VarSignature {
pub pat: VarPattern,
pub t: Type,
}
impl fmt::Display for VarSignature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} (: {})", self.pat, self.t)
}
}
impl Locational for VarSignature {
fn loc(&self) -> Location { self.pat.loc() }
}
impl VarSignature {
pub const fn new(pat: VarPattern, t: Type) -> Self { Self{ pat, t } }
pub fn inspect(&self) -> Option<&Str> { self.pat.inspect() }
}
#[derive(Debug, Clone)]
pub struct SubrSignature {
pub name: VarName,
pub params: Params,
pub t: Type,
}
impl fmt::Display for SubrSignature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{} (: {})", self.name, self.params, self.t)
}
}
impl Locational for SubrSignature {
fn loc(&self) -> Location {
Location::concat(&self.name, &self.params)
}
}
impl SubrSignature {
pub const fn new(name: VarName, params: Params, t: Type) -> Self {
Self{ name, params, t }
}
pub fn is_procedural(&self) -> bool { self.name.is_procedural() }
}
#[derive(Debug, Clone)]
pub struct Lambda {
pub params: Params,
op: Token,
pub body: Block,
pub id: usize,
t: Type
}
impl HasType for Lambda {
#[inline]
fn ref_t(&self) -> &Type { &self.t }
#[inline]
fn signature_t(&self) -> Option<&Type> { None }
}
impl NestedDisplay for Lambda {
fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result {
write!(f, "{} {}\n", self.params, self.op.content)?;
self.body.fmt_nest(f, level + 1)
}
}
impl_display_from_nested!(Lambda);
impl_locational!(Lambda, params, body);
impl Lambda {
pub const fn new(id: usize, params: Params, op: Token, body: Block, t: Type) -> Self {
Self { id, params, op, body, t }
}
pub fn is_procedural(&self) -> bool { self.op.is(TokenKind::ProcArrow) }
}
#[derive(Debug, Clone)]
pub enum Signature {
Var(VarSignature),
Subr(SubrSignature),
}
impl_display_for_enum!(Signature; Var, Subr,);
impl_locational_for_enum!(Signature; Var, Subr,);
impl Signature {
pub const fn is_subr(&self) -> bool { matches!(self, Self::Subr(_)) }
pub fn is_const(&self) -> bool {
match self {
Self::Var(v) => v.pat.is_const(),
Self::Subr(s) => s.name.is_const(),
}
}
pub fn is_procedural(&self) -> bool {
match self {
Self::Var(v) => v.pat.is_procedural(),
Self::Subr(s) => s.name.is_procedural(),
}
}
}
/// represents a declaration of a variable
/// necessary for type field declaration
#[derive(Debug, Clone)]
pub struct Decl {
pub sig: Signature,
t: Type,
}
impl NestedDisplay for Decl {
fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result {
write!(f, "{}: {}", self.sig, self.t)
}
}
impl_display_from_nested!(Decl);
impl Locational for Decl {
#[inline]
fn loc(&self) -> Location { self.sig.loc() }
}
impl Decl {
pub const fn spec_t(&self) -> &Type { &self.t }
pub const fn is_sub(&self) -> bool { self.sig.is_subr() }
}
#[derive(Clone, Debug)]
pub struct DefBody {
pub op: Token,
pub block: Block,
pub id : DefId,
}
impl_locational!(DefBody, op, block);
impl DefBody {
pub const fn new(op: Token, block: Block, id: DefId) -> Self { Self { op, block, id } }
pub fn is_type(&self) -> bool {
match self.block.first().unwrap() {
Expr::Call(call) => {
if let Expr::Accessor(Accessor::Local(local)) = call.obj.as_ref() {
&local.inspect()[..] == "Type"
} else { false }
},
_ => false,
}
}
}
#[derive(Debug, Clone)]
pub struct Def {
pub sig: Signature,
pub body: DefBody,
}
impl NestedDisplay for Def {
fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result {
write!(f, "{} {}\n", self.sig, self.body.op.content)?;
self.body.block.fmt_nest(f, level + 1)
}
}
impl_display_from_nested!(Def);
impl_locational!(Def, sig, body);
impl Def {
pub const fn new(sig: Signature, body: DefBody) -> Self { Self { sig, body } }
}
#[derive(Debug, Clone)]
pub enum Expr {
Lit(Literal),
Accessor(Accessor),
Array(Array),
// Dict(Dict),
// Set(Set),
Dict(Dict),
BinOp(BinOp),
UnaryOp(UnaryOp),
Call(Call),
Lambda(Lambda),
Decl(Decl),
Def(Def),
}
impl_nested_display_for_enum!(Expr; Lit, Accessor, Array, Dict, BinOp, UnaryOp, Call, Lambda, Decl, Def);
impl_display_from_nested!(Expr);
impl_locational_for_enum!(Expr; Lit, Accessor, Array, Dict, BinOp, UnaryOp, Call, Lambda, Decl, Def);
impl HasType for Expr {
fn ref_t(&self) -> &Type {
match self {
Expr::Lit(lit) => lit.ref_t(),
Expr::Accessor(accessor) => accessor.ref_t(),
Expr::Array(array) => array.ref_t(),
Expr::Dict(dict) => dict.ref_t(),
Expr::BinOp(bin) => bin.ref_t(),
Expr::UnaryOp(unary) => unary.ref_t(),
Expr::Call(call) => call.ref_t(),
Expr::Lambda(lambda) => lambda.ref_t(),
_ => &Type::NoneType,
}
}
fn signature_t(&self) -> Option<&Type> {
match self {
Expr::BinOp(bin) => bin.signature_t(),
Expr::UnaryOp(unary) => unary.signature_t(),
Expr::Call(call) => call.signature_t(),
_ => None,
}
}
}
impl Expr {
pub fn receiver_t(&self) -> Option<&Type> {
match self {
Self::Accessor(Accessor::Attr(attr)) => Some(attr.obj.ref_t()),
_other => None,
}
}
pub fn var_full_name(&self) -> Option<String> {
match self {
Expr::Accessor(acc) => acc.var_full_name(),
_ => None,
}
}
/// 参照するオブジェクト自体が持っている名前(e.g. Int.__name__ == Some("int"))
pub fn __name__(&self) -> Option<&str> {
match self {
Expr::Accessor(acc) => acc.__name__(),
_ => todo!(),
}
}
}
/// Toplevel grammar unit
#[derive(Debug, Clone)]
pub struct Module(Vec<Expr>);
impl fmt::Display for Module {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_lines(self.0.iter(), f, 0)
}
}
impl Locational for Module {
fn loc(&self) -> Location {
Location::concat(self.0.first().unwrap(), self.0.last().unwrap())
}
}
impl_stream_for_wrapper!(Module, Expr);
#[derive(Debug)]
pub struct HIR {
pub name: Str,
pub module: Module,
}
impl std::fmt::Display for HIR {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.module)
}
}
impl HIR {
pub const fn new(name: Str, module: Module) -> Self { Self { name, module } }
}

View file

@ -0,0 +1,513 @@
//! defines type information for builtin objects (in `Context`)
//!
//! 組み込みオブジェクトの型情報を(Contextに)定義
use erg_common::{Str};
use erg_common::{set, debug_power_assert};
use erg_common::ty::{Type, TyParam, ConstObj};
use Type::*;
use erg_common::ty::type_constrs::*;
use ParamSpec as PS;
use erg_parser::ast::{VarName};
use crate::varinfo::{Mutability, Visibility, VarInfo, VarKind};
use crate::context::{Context, ParamSpec, DefaultInfo};
use Visibility::*;
use Mutability::*;
use VarKind::*;
use DefaultInfo::*;
// NOTE: TyParam::MonoQuantVarは生成時に型を指定する必要があるが、逆にそちらがあれば型境界を指定しなくてもよい
impl Context {
fn register_decl(&mut self, name: &'static str, t: Type, vis: Visibility) {
let name = VarName::from_static(name);
if self.decls.get(&name).is_some() {
panic!("already registered: {name}");
} else {
self.decls.insert(name, VarInfo::new(t, Immutable, vis, Builtin));
}
}
fn register_impl(&mut self, name: &'static str, t: Type, muty: Mutability, vis: Visibility) {
let name = VarName::from_static(name);
if self.impls.get(&name).is_some() {
panic!("already registered: {name}");
} else {
self.impls.insert(name, VarInfo::new(t, muty, vis, Builtin));
}
}
fn register_const(&mut self, name: &'static str, obj: ConstObj) {
if self.consts.get(name).is_some() {
panic!("already registered: {name}");
} else {
self.consts.insert(Str::ever(name), obj);
}
}
fn register_type(&mut self, t: Type, ctx: Self, muty: Mutability) {
if self.types.contains_key(&t) {
panic!("{} has already been registered", t.name());
} else {
let name = VarName::from_str(Str::rc(t.name()));
self.impls.insert(name, VarInfo::new(Type, muty, Private, Builtin));
self.types.insert(t, ctx);
}
}
fn register_patch(&mut self, name: &'static str, ctx: Self, muty: Mutability) {
if self.patches.contains_key(name) {
panic!("{} has already been registered", name);
} else {
let name = VarName::from_static(name);
self.impls.insert(name.clone(), VarInfo::new(Type, muty, Private, Builtin));
for method_name in ctx.impls.keys() {
if let Some(patches) = self._method_impl_patches.get_mut(method_name) {
patches.push(name.clone());
} else {
self._method_impl_patches.insert(method_name.clone(), vec![name.clone()]);
}
}
debug_power_assert!(ctx.super_classes.len(), ==, 1);
if let Some(target_type) = ctx.super_classes.first() {
for impl_trait in ctx.super_traits.iter() {
self.glue_patch_and_types.push(
(VarName::from_str(ctx.name.clone()), target_type.clone(), impl_trait.clone())
);
}
}
self.patches.insert(name, ctx);
}
}
/// see std/prelude.er
// 型境界はすべて各サブルーチンで定義する
// push_subtype_boundなどはユーザー定義APIの型境界決定のために使用する
fn init_builtin_traits(&mut self) {
let mut eq = Self::poly_trait("Eq", vec![PS::t("R", WithDefault)], vec![], Self::TOP_LEVEL);
// __eq__: |Self <: Eq; R <: Eq()| Self(R).(R) -> Bool
let op_t = fn1_met(poly("Self", vec![mono_q_tp("R")]), mono_q("R"), Bool);
let op_t = quant(op_t, set!{subtype(mono_q("Self"), mono("Eq")), subtype(mono_q("R"), poly("Eq", vec![]))});
eq.register_decl("__eq__", op_t.clone(), Public);
let mut ord = Self::poly_trait("Ord", vec![PS::t("R", WithDefault)], vec![mono("Eq")], Self::TOP_LEVEL);
let op_t = fn1_met(poly("Self", vec![mono_q_tp("R")]), mono_q("R"), Bool);
let op_t = quant(op_t, set!{subtype(mono_q("Self"), mono("Ord")), subtype(mono_q("R"), poly("Ord", vec![]))});
ord.register_decl("__lt__", op_t.clone(), Public);
let mut seq = Self::poly_trait("Seq", vec![PS::t("T", NonDefault)], vec![], Self::TOP_LEVEL);
let self_t = poly_q("Self", vec![TyParam::t(mono_q("T"))]);
let t = fn0_met(self_t.clone(), Nat);
let t = quant(t, set!{subtype(self_t.clone(), mono("Seq"))});
seq.register_decl("__len__", t, Public);
let t = Type::fn1_met(self_t.clone(), Nat, mono_q("T"));
let t = quant(t, set!{subtype(self_t, mono("Seq")), static_instance("T", Type)});
seq.register_decl("get", t, Public);
let (r, o) = (mono_q("R"), mono_q("O"));
let (r_bound, o_bound) = (static_instance("R", Type), static_instance("O", Type));
let params = vec![PS::t("R", WithDefault), PS::t("O", WithDefault)];
let ty_params = vec![mono_q_tp("R"), mono_q_tp("O")];
let mut add_ro = Self::poly_trait("Add", params.clone(), vec![], Self::TOP_LEVEL);
let self_bound = subtype(poly_q("Self", ty_params.clone()), poly("Add", ty_params.clone()));
let op_t = fn1_met(poly_q("Self", ty_params.clone()), r.clone(), o.clone());
let op_t = quant(op_t, set!{r_bound.clone(), o_bound.clone(), self_bound});
add_ro.register_decl("__add__", op_t, Public);
let mut sub_ro = Self::poly_trait("Sub", params.clone(), vec![], Self::TOP_LEVEL);
let self_bound = subtype(poly_q("Self", ty_params.clone()), poly("Sub", ty_params.clone()));
let op_t = fn1_met(poly_q("Self", ty_params.clone()), r.clone(), o.clone());
let op_t = quant(op_t, set!{r_bound.clone(), o_bound.clone(), self_bound});
sub_ro.register_decl("__sub__", op_t, Public);
let mut mul_ro = Self::poly_trait("Mul", params.clone(), vec![], Self::TOP_LEVEL);
let op_t = fn1_met(poly("Mul", ty_params.clone()), r.clone(), o.clone());
mul_ro.register_decl("__mul__", op_t, Public);
let mut div_ro = Self::poly_trait("Div", params.clone(), vec![], Self::TOP_LEVEL);
let op_t = fn1_met(poly("Div", ty_params.clone()), r, o);
div_ro.register_decl("__div__", op_t, Public);
let sup = poly("Add", vec![mono_q_tp("Self"), TyParam::mono_proj(mono_q_tp("Self"), "AddO")]);
let mut add = Self::mono_trait("Add", vec![sup], Self::TOP_LEVEL);
add.register_decl("AddO", Type, Public);
let sup = poly("Sub", vec![mono_q_tp("Self"), TyParam::mono_proj(mono_q_tp("Self"), "SubO")]);
let mut sub = Self::mono_trait("Sub", vec![sup], Self::TOP_LEVEL);
sub.register_decl("SubO", Type, Public);
let sup = poly("Mul", vec![mono_q_tp("Self"), TyParam::mono_proj(mono_q_tp("Self"), "MulO")]);
let mut mul = Self::mono_trait("Mul", vec![sup], Self::TOP_LEVEL);
mul.register_decl("MulO", Type, Public);
let sup = Type::poly("Div", vec![mono_q_tp("Self"), TyParam::mono_proj(mono_q_tp("Self"), "DivO")]);
let mut div = Self::mono_trait("Div", vec![sup], Self::TOP_LEVEL);
div.register_decl("DivO", Type, Public);
let num = Self::mono_trait("Num", vec![
mono("Eq"),
mono("Add"), mono("Sub"), mono("Mul"),
], Self::TOP_LEVEL);
self.register_type(mono("Eq"), eq, Const);
self.register_type(mono("Ord"), ord, Const);
self.register_type(mono("Seq"), seq, Const);
self.register_type(poly("Add", ty_params.clone()), add_ro, Const);
self.register_type(poly("Sub", ty_params.clone()), sub_ro, Const);
self.register_type(poly("Mul", ty_params.clone()), mul_ro, Const);
self.register_type(poly("Div", ty_params), div_ro, Const);
self.register_type(mono("Num"), num, Const);
}
fn init_builtin_classes(&mut self) {
let mut obj = Self::mono_class("Obj", vec![], vec![], Self::TOP_LEVEL);
let t = fn0_met(mono_q("Self"), mono_q("Self"));
let t = quant(t, set!{subtype(mono_q("Self"), mono("Obj"))});
obj.register_impl("clone", t, Const, Public);
obj.register_impl("__module__", Str, Const, Public);
obj.register_impl("__sizeof__", fn0_met(Obj, Nat), Const, Public);
obj.register_impl("__repr__", fn0_met(Obj, Str), Immutable, Public);
obj.register_impl("__str__", fn0_met(Obj, Str), Immutable, Public);
obj.register_impl(
"__dict__",
fn0_met(Obj, Type::dict(Str, Obj)),
Immutable,
Public,
);
obj.register_impl(
"__bytes__",
fn0_met(Obj, Type::mono("Bytes")),
Immutable,
Public,
);
// let mut record = Self::mono_trait("Record", vec![Obj], Self::TOP_LEVEL);
// let mut class = Self::mono_class("Class", vec![Type, Obj], Self::TOP_LEVEL);
let mut float = Self::mono_class("Float", vec![Obj], vec![
mono("Num"),
mono("Ord"), mono("Eq"),
mono("Add"), mono("Sub"), mono("Mul"), mono("Div"),
mono("Mutate"),
], Self::TOP_LEVEL);
let op_t = fn1_met(Float, Float, Float);
float.register_impl("__add__", op_t.clone(), Const, Public);
float.register_impl("__sub__", op_t.clone(), Const, Public);
float.register_impl("__mul__", op_t.clone(), Const, Public);
float.register_impl("__div__", op_t , Const, Public);
float.register_impl("Real", Float, Const, Public);
float.register_impl("Imag", Float, Const, Public);
let mut ratio = Self::mono_class("Ratio", vec![Obj], vec![
mono("Num"),
mono("Ord"), mono("Eq"),
mono("Add"), mono("Sub"), mono("Mul"), mono("Div"),
mono("Mutate"),
], Self::TOP_LEVEL);
let op_t = fn1_met(Ratio, Ratio, Ratio);
ratio.register_impl("__add__", op_t.clone(), Const, Public);
ratio.register_impl("__sub__", op_t.clone(), Const, Public);
ratio.register_impl("__mul__", op_t.clone(), Const, Public);
ratio.register_impl("__div__", op_t , Const, Public);
ratio.register_impl("Real", Ratio, Const, Public);
ratio.register_impl("Imag", Ratio, Const, Public);
let mut int = Self::mono_class("Int", vec![Obj], vec![
mono("Num"),
mono("Rational"), mono("Integral"),
mono("Ord"), mono("Eq"),
mono("Add"), mono("Sub"), mono("Mul"), mono("Div"),
mono("Mutate"),
], Self::TOP_LEVEL);
int.register_impl("abs", fn0_met(Int, Nat), Immutable, Public);
// __div__ is not included in Int (cast to Float)
let op_t = fn1_met(Int, Int, Int);
int.register_impl("__add__", op_t.clone(), Const, Public);
int.register_impl("__sub__", op_t.clone(), Const, Public);
int.register_impl("__mul__", op_t, Const, Public);
int.register_impl("Real", Int, Const, Public);
int.register_impl("Imag", Int, Const, Public);
int.super_traits.push(poly("Add", vec![ty_tp(Int), ty_tp(Int)]));
int.super_traits.push(poly("Sub", vec![ty_tp(Int), ty_tp(Int)]));
int.super_traits.push(poly("Mul", vec![ty_tp(Int), ty_tp(Int)]));
int.super_traits.push(poly("Div", vec![ty_tp(Int), ty_tp(Ratio)]));
let mut nat = Self::mono_class("Nat", vec![Int, Obj], vec![
mono("Num"),
mono("Rational"), mono("Integral"),
mono("Ord"), mono("Eq"),
mono("Add"), mono("Sub"), mono("Mul"), mono("Div"),
mono("Mutate"),
Obj,
], Self::TOP_LEVEL);
// __sub__, __div__ is not included in Nat (cast to Int)
let op_t = fn1_met(Nat, Nat, Nat);
nat.register_impl("__add__", op_t.clone(), Const, Public);
nat.register_impl("__mul__", op_t, Const, Public);
nat.register_impl(
"times!",
Type::pr_met(Nat, None, vec![param_t("p", nd_proc(vec![], NoneType))], vec![], NoneType),
Immutable,
Public
);
nat.register_impl("Real", Nat, Const, Public);
nat.register_impl("Imag", Nat, Const, Public);
nat.super_traits.push(poly("Add", vec![ty_tp(Nat), ty_tp(Nat)]));
nat.super_traits.push(poly("Sub", vec![ty_tp(Nat), ty_tp(Nat)]));
nat.super_traits.push(poly("Mul", vec![ty_tp(Nat), ty_tp(Nat)]));
nat.super_traits.push(poly("Div", vec![ty_tp(Nat), ty_tp(Ratio)]));
let mut bool_ = Self::mono_class("Bool", vec![Nat, Int, Obj], vec![
mono("Num"),
mono("Rational"), mono("Integral"),
mono("Ord"), mono("Eq"),
mono("Add"), mono("Sub"), mono("Mul"), mono("Div"),
mono("Mutate"),
Obj,
], Self::TOP_LEVEL);
bool_.register_impl("__and__", fn1_met(Bool, Bool, Bool), Const, Public);
bool_.register_impl("__or__", fn1_met(Bool, Bool, Bool), Const, Public);
let mut str_ = Self::mono_class("Str", vec![Obj], vec![
mono("Eq"), mono("Mutate"), poly("Seq", vec![ty_tp(Str)]),
], Self::TOP_LEVEL);
str_.register_impl("__add__", fn1_met(Str, Str, Str), Const, Public);
str_.register_impl("replace", Type::fn_met(
Str,
vec![param_t("pat", Str), param_t("into", Str)],
vec![], Str),
Immutable,
Public
);
str_.super_traits.push(poly("Add", vec![ty_tp(Str), ty_tp(Str)]));
let mut array = Self::poly_class("Array", vec![PS::t_nd("T"), PS::named_nd("N", Nat)], vec![Obj], vec![
mono("Eq"), mono("Mutate"),
poly("Seq", vec![ty_tp(mono_q("T"))]), poly("Output", vec![ty_tp(mono_q("T"))])
], Self::TOP_LEVEL);
let n = mono_q_tp("N");
let m = mono_q_tp("M");
let array_t = Type::array(mono_q("T"), n.clone());
let t = Type::fn_met(
array_t.clone(),
vec![param_t("rhs", Type::array(mono_q("T"), m.clone()))],
vec![],
Type::array(mono_q("T"), n + m)
);
let t = quant(t, set!{static_instance("N", Nat), static_instance("M", Nat)});
array.register_impl("concat", t, Immutable, Public);
let mut_type = ConstObj::t(Type::poly("Array!", vec![
TyParam::t(mono_q("T")),
TyParam::mono_q("N").mutate(),
]));
// [T; N].MutType! = [T; !N] (neither [T!; N] nor [T; N]!)
array.register_const("MutType!", mut_type);
let mut type_ = Self::mono_class("Type", vec![Obj], vec![mono("Eq"), mono("Named")], Self::TOP_LEVEL);
type_.register_impl("mro", Type::array(Type, TyParam::erased(Nat)), Immutable, Public);
let module = Self::mono_class("Module", vec![Obj], vec![mono("Eq"), mono("Named")], Self::TOP_LEVEL);
let array_mut_t = Type::poly("Array!", vec![TyParam::t(mono_q("T")), mono_q_tp("N")]);
let mut array_mut = Self::poly_class("Array!", vec![PS::t_nd("T"), PS::named_nd("N", NatMut)], vec![Obj], vec![
mono("Eq"), mono("Mutate"), poly("Seq", vec![ty_tp(mono_q("T"))])
], Self::TOP_LEVEL);
let t = Type::pr_met(
Type::ref_mut(array_mut_t.clone()),
Some(Type::ref_mut(poly("Array!", vec![TyParam::t(mono_q("T")), mono_q_tp("N") + value(1)]))),
vec![param_t("elem", mono_q("T"))],
vec![],
Type::NoneType,
);
let t = quant(t, set!{static_instance("T", Type), static_instance("N", NatMut)});
array_mut.register_impl("push!", t, Immutable, Public);
let range_t = Type::poly("Range", vec![TyParam::t(mono_q("T"))]);
let range = Self::poly_class("Range", vec![PS::t_nd("T")], vec![Obj], vec![
mono("Eq"), mono("Mutate"),
poly("Seq", vec![ty_tp(mono_q("T"))]), poly("Output", vec![ty_tp(mono_q("T"))])
], Self::TOP_LEVEL);
self.register_type(Obj, obj, Const);
// self.register_type(Type::mono("Record"), vec![], record, Const);
// self.register_type(Type::mono("Class"), vec![], class, Const);
self.register_type(Float, float, Const);
self.register_type(Ratio, ratio, Const);
self.register_type(Int, int, Const);
self.register_type(Nat, nat, Const);
self.register_type(Bool, bool_, Const);
self.register_type(Str, str_, Const);
self.register_type(Type, type_, Const);
self.register_type(Module, module, Const);
self.register_type(array_t, array, Const);
self.register_type(range_t, range, Const);
self.register_type(array_mut_t, array_mut, Const);
}
fn init_builtin_funcs(&mut self) {
let t_abs = nd_func(vec![param_t("n", mono("Num"))], Nat);
let t_assert = func(vec![param_t("condition", Bool)], vec![param_t("err_message", Str)], NoneType);
let t_classof = nd_func(vec![param_t("o", Obj)], Type::option(Class));
let t_compile = nd_func(vec![param_t("src", Str)], Code);
let t_cond = nd_func(
vec![param_t("condition", Bool), param_t("then", mono_q("T")), param_t("else", mono_q("T"))],
mono_q("T"),
);
let t_cond = quant(t_cond, set!{static_instance("T", Type)});
let t_discard = nd_func(vec![param_t("o", Obj)], NoneType);
let t_id = nd_func(vec![param_t("o", Obj)], Nat);
// FIXME: quantify
let t_if = func(
vec![param_t("cond", Bool), param_t("then", nd_func(vec![], mono_q("T")))],
vec![param_t("else", nd_func(vec![], mono_q("T")))],
Type::option(mono_q("T")),
);
let t_if = quant(t_if, set!{static_instance("T", Type)});
let t_import = nd_func(vec![param_t("path", Str)], Module);
let t_log = nd_func(vec![param_t("objs", Type::var_args(Obj))], NoneType);
let t_pyimport = nd_func(vec![param_t("path", Str)], Module);
let t_quit = func(vec![], vec![param_t("code", Int)], NoneType);
self.register_impl("abs", t_abs, Const, Private);
self.register_impl("assert", t_assert, Const, Private);
self.register_impl("classof", t_classof, Const, Private);
self.register_impl("compile", t_compile, Const, Private);
self.register_impl("cond", t_cond, Const, Private);
self.register_impl("discard", t_discard, Const, Private);
self.register_impl("id" , t_id, Const, Private);
self.register_impl("if", t_if, Const, Private);
self.register_impl("log", t_log, Const, Private);
self.register_impl("import", t_import, Const, Private);
self.register_impl("pyimport", t_pyimport, Const, Private);
self.register_impl("quit", t_quit, Const, Private);
}
fn init_builtin_procs(&mut self) {
let t_print = nd_proc(vec![param_t("objs", Type::var_args(Type::refer(Obj)))], NoneType);
let t_input = nd_proc(vec![param_t("msg", Str)], Str);
let t_if = proc(
vec![param_t("cond", Bool), param_t("then", nd_proc(vec![], mono_q("T")))],
vec![param_t("else", nd_proc(vec![], mono_q("T")))],
Type::option(mono_q("T"))
);
let t_if = quant(t_if, set!{static_instance("T", Type)});
let t_for = nd_proc(vec![param_t("iter", Type::iter(mono_q("T"))), param_t("p", nd_proc(vec![anon(mono_q("T"))], NoneType))], NoneType);
let t_for = quant(t_for, set!{static_instance("T", Type)});
let t_while = nd_proc(vec![param_t("cond", BoolMut), param_t("p", nd_proc(vec![], NoneType))], NoneType);
self.register_impl("print!", t_print, Const, Private);
self.register_impl("input!", t_input, Const, Private);
self.register_impl("if!", t_if, Const, Private);
self.register_impl("for!", t_for, Const, Private);
self.register_impl("while!", t_while, Const, Private);
}
fn init_builtin_operators(&mut self) {
/* binary */
let l = mono_q("L");
let r = mono_q("R");
let o = mono_q("O");
let params = vec![mono_q_tp("R"), mono_q_tp("O")];
let op_t = Type::func2(l.clone(), r.clone(), o.clone());
let op_t = quant(op_t, set!{
static_instance("R", Type),
static_instance("O", Type),
subtype(l.clone(), poly("Add", params.clone()))
});
self.register_impl("__add__", op_t, Const, Private);
let op_t = Type::func2(l.clone(), r.clone(), o.clone());
let op_t = quant(op_t, set!{
static_instance("R", Type),
static_instance("O", Type),
subtype(l.clone(), poly("Sub", params.clone()))
});
self.register_impl("__sub__", op_t, Const, Private);
let op_t = Type::func2(l.clone(), r.clone(), o.clone());
let op_t = quant(op_t, set!{subtype(l.clone(), poly("Mul", params.clone()))});
self.register_impl("__mul__", op_t, Const, Private);
let op_t = Type::func2(l.clone(), r.clone(), o.clone());
let op_t = quant(op_t, set!{subtype(l, poly("Mul", params.clone()))});
self.register_impl("__div__", op_t, Const, Private);
let m = mono_q("M");
let op_t = Type::func2(m.clone(), m.clone(), m.clone());
let op_t = quant(op_t, set!{subtype(m, poly("Mul", vec![]))});
self.register_impl("__pow__", op_t, Const, Private);
let d = mono_q("D");
let op_t = Type::func2(d.clone(), d.clone(), d.clone());
let op_t = quant(op_t, set!{subtype(d, poly("Div", vec![]))});
self.register_impl("__mod__", op_t, Const, Private);
let e = mono_q("E");
let op_t = Type::func2(e.clone(), e.clone(), Bool);
let op_t = quant(op_t, set!{subtype(e, poly("Eq", vec![]))});
self.register_impl("__eq__", op_t.clone(), Const, Private);
self.register_impl("__ne__", op_t, Const, Private);
let o = mono_q("O");
let op_t = Type::func2(o.clone(), o.clone(), Bool);
let op_t = quant(op_t, set!{subtype(o, poly("Ord", vec![]))});
self.register_impl("__lt__", op_t.clone(), Const, Private);
self.register_impl("__le__", op_t.clone(), Const, Private);
self.register_impl("__gt__", op_t.clone(), Const, Private);
self.register_impl("__ge__", op_t, Const, Private);
self.register_impl("__and__", Type::func2(Bool, Bool, Bool), Const, Private);
self.register_impl("__or__", Type::func2(Bool, Bool, Bool), Const, Private);
/* unary */
// TODO: Boolの+/-は警告を出したい
let n = mono_q("N");
let op_t = fn0_met(n.clone(), n.clone());
let op_t = quant(op_t, set!{subtype(n, mono("Num"))});
self.register_decl("__pos__", op_t.clone(), Private);
self.register_decl("__neg__", op_t, Private);
let t = mono_q("T");
let op_t = Type::func2(t.clone(), t.clone(), Type::range(t.clone()));
let op_t = quant(op_t, set!{subtype(t, mono("Ord"))});
self.register_decl("__rng__", op_t.clone(), Private);
self.register_decl("__lorng__", op_t.clone(), Private);
self.register_decl("__rorng__", op_t.clone(), Private);
self.register_decl("__orng__", op_t, Private);
let op_t = Type::func1(mono_q("T"), Type::mono_proj(mono_q("T"), "MutType!"));
let op_t = quant(op_t, set!{subtype(mono_q("T"), mono("Mutate"))});
self.register_impl("__mutate__", op_t, Const, Private);
}
fn init_builtin_patches(&mut self) {
let m = mono_q_tp("M");
let n = mono_q_tp("N");
let o = mono_q_tp("O");
let p = mono_q_tp("P");
let params = vec![
PS::named_nd("M", Int), PS::named_nd("N", Int),
PS::named_nd("O", Int), PS::named_nd("P", Int),
];
// Interval is a bounding patch connecting M..N and (Add(O..P, M+O..N..P), Sub(O..P, M-P..N-O))
let mut interval = Self::poly_patch("Interval", params, vec![Type::from(&m..=&n)], vec![
poly("Add", vec![TyParam::from(&o..=&p), TyParam::from(m.clone() + o.clone() ..= n.clone() + p.clone())]),
poly("Sub", vec![TyParam::from(&o..=&p), TyParam::from(m.clone() - p.clone() ..= n.clone() - o.clone())]),
], Self::TOP_LEVEL);
let op_t = fn1_met(
Type::from(&m..=&n),
Type::from(&o..=&p),
Type::from(m.clone() + o.clone() ..= n.clone() + p.clone()),
);
interval.register_impl("__add__", op_t, Const, Public);
let op_t = fn1_met(
Type::from(&m..=&n),
Type::from(&o..=&p),
Type::from(m - p ..= n - o),
);
interval.register_impl("__sub__", op_t, Const, Public);
self.register_patch("Interval", interval, Const);
// eq.register_impl("__ne__", op_t, Const, Public);
// ord.register_impl("__le__", op_t.clone(), Const, Public);
// ord.register_impl("__gt__", op_t.clone(), Const, Public);
// ord.register_impl("__ge__", op_t, Const, Public);
}
pub(crate) fn init_py_math_mod() -> Self {
let mut math = Context::module("math".into(), 10);
math.register_impl("pi", Type::Float, Immutable, Public);
math.register_impl("tau", Type::Float, Immutable, Public);
math.register_impl("e", Type::Float, Immutable, Public);
math.register_impl("sin", Type::func1(Float, Float), Immutable, Public);
math.register_impl("cos", Type::func1(Float, Float), Immutable, Public);
math.register_impl("tan", Type::func1(Float, Float), Immutable, Public);
math
}
pub(crate) fn init_py_random_mod() -> Self {
let mut random = Context::module("random".into(), 10);
random.register_impl("seed!", Type::proc(vec![], vec![
param_t("a", Type::mono("Num")), // TODO: NoneType, int, float, str, bytes, bytearray
param_t("version", Type::Int),
], NoneType), Immutable, Public);
random.register_impl("randint!", nd_proc(vec![param_t("a", Int), param_t("b", Int)], Int), Immutable, Public);
let t = nd_proc(vec![param_t("seq", Type::poly("Seq", vec![ty_tp(mono_q("T"))]))], mono_q("T"));
let t = quant(t, set!{static_instance("T", Type)});
random.register_impl("choice!", t, Immutable, Public);
random
}
pub(crate) fn init_builtins() -> Self {
// TODO: capacityを正確に把握する
let mut ctx = Context::module("<builtins>".into(), 40);
ctx.init_builtin_funcs();
ctx.init_builtin_procs();
ctx.init_builtin_operators();
ctx.init_builtin_traits();
ctx.init_builtin_classes();
ctx.init_builtin_patches();
ctx
}
}

View file

@ -0,0 +1,18 @@
//! defines the compiler for Erg (ergc).
extern crate erg_common;
pub extern crate erg_parser;
mod compile;
pub use compile::*;
mod codegen;
pub mod effectcheck;
pub mod error;
pub mod eval;
pub mod hir;
pub mod initialize;
pub mod lower;
pub use lower::ASTLowerer;
pub mod optimize;
pub mod ownercheck;
pub mod context;
pub mod varinfo;

View file

@ -0,0 +1,363 @@
//! implements `ASTLowerer`.
//!
//! ASTLowerer(ASTからHIRへの変換器)を実装
use erg_common::{switch_lang, log, fn_name};
use erg_common::color::{GREEN, RED, RESET};
use erg_common::error::Location;
use erg_common::traits::{Locational, Stream, HasType};
use erg_common::ty::{Type, ParamTy};
use erg_common::get_hash;
use erg_parser::ast;
use erg_parser::ast::{AST};
use crate::hir;
use crate::hir::{HIR};
use crate::error::{LowerError, LowerErrors, LowerResult, LowerWarnings};
use crate::context::{Context, ContextKind, RegistrationMode};
use crate::varinfo::{Visibility};
use Visibility::*;
/// Singleton that checks types of an AST, and convert (lower) it into a HIR
#[derive(Debug)]
pub struct ASTLowerer {
pub(crate) mod_ctx: Context,
errs: LowerErrors,
warns: LowerWarnings,
}
impl ASTLowerer {
pub fn new() -> Self {
Self {
mod_ctx: Context::new(
"<module>".into(),
ContextKind::Module,
vec![],
Some(Context::init_builtins()),
vec![],
vec![],
0
),
errs: LowerErrors::empty(),
warns: LowerWarnings::empty(),
}
}
fn return_t_check(&self, loc: Location, name: &str, expect: &Type, found: &Type) -> LowerResult<()> {
self.mod_ctx
.unify(expect, found, Some(loc), None)
.or_else(|_| Err(LowerError::type_mismatch_error(
loc,
self.mod_ctx.caused_by(),
name,
expect,
found,
)))
}
fn use_check(&self, expr: hir::Expr, mode: &str) -> LowerResult<hir::Expr> {
if mode != "eval" && !expr.ref_t().is_nonelike() {
Err(LowerError::syntax_error(
0,
expr.loc(),
self.mod_ctx.name.clone(),
switch_lang!(
"the evaluation result of the expression is not used",
"式の評価結果が使われていません",
),
Some(switch_lang!(
"if you don't use the value, use `discard` function",
"値を使わない場合は、discard関数を使用してください",
).into())
))
} else {
Ok(expr)
}
}
fn pop_append_errs(&mut self) {
if let Err(mut errs) = self.mod_ctx.pop() {
self.errs.append(&mut errs);
}
}
fn lower_array(&mut self, array: ast::Array, check: bool) -> LowerResult<hir::Array> {
log!("[DEBUG] entered {}({array})", fn_name!());
let mut hir_array = hir::Array::new(array.l_sqbr, array.r_sqbr, self.mod_ctx.level, hir::Args::empty(), None);
for elem in array.elems.into_iters().0 {
hir_array.push(self.lower_expr(elem.expr, check)?);
}
Ok(hir_array)
}
/// call全体で推論できる場合があり、そのときはcheck: falseにする
fn lower_acc(&mut self, acc: ast::Accessor, check: bool) -> LowerResult<hir::Accessor> {
log!("[DEBUG] entered {}({acc})", fn_name!());
match acc {
ast::Accessor::Local(n) => {
let (t, __name__) = if check {
(
self.mod_ctx.get_local_t(&n.symbol, &self.mod_ctx.name)?,
self.mod_ctx.get_local_uniq_obj_name(&n.symbol),
)
} else { (Type::ASTOmitted, None) };
let acc = hir::Accessor::Local(hir::Local::new(n.symbol, __name__, t));
Ok(acc)
}
ast::Accessor::Attr(a) => {
let obj = self.lower_expr(*a.obj, true)?;
let t = if check {
self.mod_ctx.get_attr_t(&obj, &a.name.symbol, &self.mod_ctx.name)?
} else { Type::ASTOmitted };
let acc = hir::Accessor::Attr(hir::Attribute::new(obj, a.name.symbol, t));
Ok(acc)
}
_ => todo!()
}
}
fn lower_bin(&mut self, bin: ast::BinOp) -> LowerResult<hir::BinOp> {
log!("[DEBUG] entered {}({bin})", fn_name!());
let mut args = bin.args.into_iter();
let lhs = hir::PosArg::new(self.lower_expr(*args.next().unwrap(), true)?);
let rhs = hir::PosArg::new(self.lower_expr(*args.next().unwrap(), true)?);
let args = [lhs, rhs];
let t = self.mod_ctx.get_binop_t(&bin.op, &args, &self.mod_ctx.name)?;
let mut args = args.into_iter();
let lhs = args.next().unwrap().expr;
let rhs = args.next().unwrap().expr;
Ok(hir::BinOp::new(bin.op, lhs, rhs, t))
}
fn lower_unary(&mut self, unary: ast::UnaryOp) -> LowerResult<hir::UnaryOp> {
log!("[DEBUG] entered {}({unary})", fn_name!());
let mut args = unary.args.into_iter();
let arg = hir::PosArg::new(self.lower_expr(*args.next().unwrap(), true)?);
let args = [arg];
let t = self.mod_ctx.get_unaryop_t(&unary.op, &args, &self.mod_ctx.name)?;
let mut args = args.into_iter();
let expr = args.next().unwrap().expr;
Ok(hir::UnaryOp::new(unary.op, expr, t))
}
fn lower_call(&mut self, call: ast::Call) -> LowerResult<hir::Call> {
log!("[DEBUG] entered {}({}(...))", fn_name!(), call.obj);
let (pos_args, kw_args, paren) = call.args.deconstruct();
let mut hir_args = hir::Args::new(Vec::with_capacity(pos_args.len()), Vec::with_capacity(kw_args.len()), paren);
for arg in pos_args.into_iter() {
hir_args.push_pos(hir::PosArg::new(self.lower_expr(arg.expr, true)?));
}
for arg in kw_args.into_iter() {
hir_args.push_kw(hir::KwArg::new(arg.keyword, self.lower_expr(arg.expr, true)?));
}
let mut obj = self.lower_expr(*call.obj, false)?;
let t = self.mod_ctx.get_call_t(&mut obj, hir_args.pos_args(), hir_args.kw_args(), &self.mod_ctx.name)?;
Ok(hir::Call::new(obj, hir_args, t))
}
fn lower_lambda(&mut self, lambda: ast::Lambda) -> LowerResult<hir::Lambda> {
log!("[DEBUG] entered {}({lambda})", fn_name!());
let is_procedural = lambda.is_procedural();
let id = get_hash(&lambda.sig);
let name = format!("<lambda_{id}>");
let kind = if is_procedural { ContextKind::Proc } else { ContextKind::Func };
self.mod_ctx.grow(&name, kind, Private)?;
self.mod_ctx.assign_params(&lambda.sig.params, None)
.map_err(|e| { self.pop_append_errs(); e })?;
self.mod_ctx.preregister(lambda.body.ref_payload())
.map_err(|e| { self.pop_append_errs(); e })?;
let body = self.lower_block(lambda.body)
.map_err(|e| { self.pop_append_errs(); e })?;
// impls => named non-default params + named default params + embedded params (will be discarded)
// unnnamed_params => unnamed non-default params + unnamed default params
// non default params = [unnamed non-default params + unnamed default params].sorted() // sort by pos
// default params = [unnamed default params, named default params].sorted()
let (named_non_default_params, named_default_params) = {
let (named_default_params, named_non_default_params_and_embeddeds): (Vec<_>, Vec<_>) = self.mod_ctx.impls.iter()
.filter(|(_, v)| v.kind.is_parameter())
.partition(|(_, v)| v.kind.has_default());
let (_, named_non_default_params): (Vec<_>, Vec<_>)= named_non_default_params_and_embeddeds.into_iter()
.partition(|(_, v)| v.kind.is_embedded_param());
(
named_non_default_params.into_iter()
.map(|(n, v)| (v.kind.pos_as_param().unwrap(), ParamTy::new(Some(n.inspect().clone()), v.t())))
.collect::<Vec<_>>(),
named_default_params.into_iter()
.map(|(n, v)| (v.kind.pos_as_param().unwrap(), ParamTy::new(Some(n.inspect().clone()), v.t())))
.collect::<Vec<_>>()
)
};
let (unnamed_non_default_params, unnamed_default_params) = {
let (unnamed_default_params, unnamed_non_default_params): (Vec<_>, Vec<_>) = self.mod_ctx.unnamed_params.iter()
.map(|v| (v, ParamTy::anonymous(v.t())))
.partition(|(v, _)| v.kind.has_default());
(
unnamed_non_default_params.into_iter()
.map(|(v, pt)| (v.kind.pos_as_param().unwrap(), pt)).collect(),
unnamed_default_params.into_iter()
.map(|(v, pt)| (v.kind.pos_as_param().unwrap(), pt)).collect(),
)
};
let non_default_params = {
let mut a = [unnamed_non_default_params, named_non_default_params].concat();
a.sort_by(|(l, _), (r, _)| l.cmp(r));
a.into_iter().map(|(_, p)| p).collect::<Vec<_>>()
};
let default_params = {
let mut a = [unnamed_default_params, named_default_params].concat();
a.sort_by(|(l, _), (r, _)| l.cmp(r));
a.into_iter().map(|(_, p)| p).collect::<Vec<_>>()
};
let bounds = self.mod_ctx.instantiate_ty_bounds(&lambda.sig.bounds, RegistrationMode::Normal)
.map_err(|e| { self.pop_append_errs(); e })?;
self.pop_append_errs();
let t = if is_procedural {
Type::proc(non_default_params, default_params, body.t())
} else {
Type::func(non_default_params, default_params, body.t())
};
let t = if bounds.is_empty() { t } else { Type::quantified(t, bounds) };
Ok(hir::Lambda::new(id, lambda.sig.params, lambda.op, body, t))
}
fn lower_def(&mut self, def: ast::Def) -> LowerResult<hir::Def> {
log!("[DEBUG] entered {}({})", fn_name!(), def.sig);
// FIXME: Instant
self.mod_ctx.grow(def.sig.name_as_str(), ContextKind::Instant, Private)?;
let res = match def.sig {
ast::Signature::Subr(sig) => self.lower_subr_def(sig, def.body),
ast::Signature::Var(sig) => self.lower_var_def(sig, def.body),
};
// TODO: Context上の関数に型境界情報を追加
self.pop_append_errs();
res
}
fn lower_var_def(&mut self, sig: ast::VarSignature, body: ast::DefBody) -> LowerResult<hir::Def> {
log!("[DEBUG] entered {}({sig})", fn_name!());
self.mod_ctx.preregister(body.block.ref_payload())?;
let block = self.lower_block(body.block)?;
let found_body_t = block.ref_t();
let opt_expect_body_t = self.mod_ctx
.outer.as_ref().unwrap()
.get_current_scope_local_var(sig.inspect().unwrap())
.map(|vi| vi.t.clone());
let name = sig.pat.inspect().unwrap();
if let Some(expect_body_t) = opt_expect_body_t {
if let Err(e) = self.return_t_check(sig.loc(), name, &expect_body_t, &found_body_t) {
self.errs.push(e);
}
}
let id = body.id;
// TODO: cover all VarPatterns
self.mod_ctx.outer.as_mut().unwrap().assign_var(&sig, id, found_body_t)?;
match block.first().unwrap() {
hir::Expr::Call(call) => {
if let ast::VarPattern::VarName(name) = &sig.pat {
if call.is_import_call() {
self.mod_ctx.outer.as_mut()
.unwrap()
.import_mod(name, &call.args.pos_args().first().unwrap().expr)?;
}
} else { todo!() }
},
_other => {}
}
let sig = hir::VarSignature::new(sig.pat, found_body_t.clone());
let body = hir::DefBody::new(body.op, block, body.id);
Ok(hir::Def::new(hir::Signature::Var(sig), body))
}
// NOTE: 呼ばれている間はinner scopeなので注意
fn lower_subr_def(&mut self, sig: ast::SubrSignature, body: ast::DefBody) -> LowerResult<hir::Def> {
log!("[DEBUG] entered {}({sig})", fn_name!());
let t = self.mod_ctx
.outer.as_ref().unwrap()
.get_current_scope_local_var(sig.name.inspect())
.unwrap_or_else(|| {
log!("{}\n", sig.name.inspect());
log!("{}\n", self.mod_ctx.outer.as_ref().unwrap());
panic!()
}) // FIXME: or instantiate
.t.clone();
self.mod_ctx.assign_params(&sig.params, None)?;
self.mod_ctx.preregister(body.block.ref_payload())?;
let block = self.lower_block(body.block)?;
let found_body_t = block.ref_t();
let expect_body_t = t.return_t().unwrap();
if let Err(e) = self.return_t_check(sig.loc(), sig.name.inspect(), expect_body_t, found_body_t) {
self.errs.push(e);
}
let id = body.id;
self.mod_ctx.outer.as_mut().unwrap().assign_subr(&sig, id, found_body_t)?;
let sig = hir::SubrSignature::new(sig.name, sig.params, t);
let body = hir::DefBody::new(body.op, block, body.id);
Ok(hir::Def::new(hir::Signature::Subr(sig), body))
}
// Call.obj == Accessor cannot be type inferred by itself (it can only be inferred with arguments)
// so turn off type checking (check=false)
fn lower_expr(&mut self, expr: ast::Expr, check: bool) -> LowerResult<hir::Expr> {
log!("[DEBUG] entered {}", fn_name!());
match expr {
ast::Expr::Lit(lit) => {
Ok(hir::Expr::Lit(hir::Literal::from(lit.token)))
},
ast::Expr::Array(arr) => {
Ok(hir::Expr::Array(self.lower_array(arr, check)?))
}
ast::Expr::Accessor(acc) => {
Ok(hir::Expr::Accessor(self.lower_acc(acc, check)?))
}
ast::Expr::BinOp(bin) => {
Ok(hir::Expr::BinOp(self.lower_bin(bin)?))
}
ast::Expr::UnaryOp(unary) => {
Ok(hir::Expr::UnaryOp(self.lower_unary(unary)?))
}
ast::Expr::Call(call) => {
Ok(hir::Expr::Call(self.lower_call(call)?))
}
ast::Expr::Lambda(lambda) => {
Ok(hir::Expr::Lambda(self.lower_lambda(lambda)?))
}
ast::Expr::Def(def) => {
Ok(hir::Expr::Def(self.lower_def(def)?))
}
other => todo!("{other}"),
}
}
fn lower_block(&mut self, ast_block: ast::Block) -> LowerResult<hir::Block> {
log!("[DEBUG] entered {}", fn_name!());
let mut hir_block = Vec::with_capacity(ast_block.len());
for expr in ast_block.into_iter() {
let expr = self.lower_expr(expr, true)?;
hir_block.push(expr);
}
Ok(hir::Block::new(hir_block))
}
pub fn lower(&mut self, ast: AST, mode: &str) -> Result<(HIR, LowerWarnings), LowerErrors> {
log!("{GREEN}[DEBUG] the type-checking process has started.");
let mut module = hir::Module::with_capacity(ast.module.len());
self.mod_ctx.preregister(ast.module.ref_payload())?;
for expr in ast.module.into_iter() {
match self.lower_expr(expr, true)
.and_then(|e| self.use_check(e, mode)) {
Ok(expr) => { module.push(expr); }
Err(e) => { self.errs.push(e); },
}
}
let hir = HIR::new(ast.name, module);
log!("[DEBUG] {}() has completed, found errors: {}", fn_name!(), self.errs.len());
if self.errs.is_empty() {
log!("HIR:\n{hir}");
log!("[DEBUG] the type-checking process has completed.{RESET}");
Ok((hir, LowerWarnings::from(self.warns.take_all())))
} else {
log!("{RED}[DEBUG] the type-checking process has failed.{RESET}");
Err(LowerErrors::from(self.errs.take_all()))
}
}
}

View file

@ -0,0 +1,28 @@
extern crate erg_common;
extern crate erg_compiler;
extern crate erg_parser;
use std::process;
use erg_common::deserialize::Deserializer;
use erg_common::config::{ErgConfig};
use erg_common::traits::Runnable;
use erg_compiler::Compiler;
use erg_parser::lex::LexerRunner;
use erg_parser::ParserRunner;
fn main() {
let cfg = ErgConfig::parse();
match cfg.mode {
"lex" => { LexerRunner::run(cfg); }
"parse" => { ParserRunner::run(cfg); }
"compile" | "exec" => { Compiler::run(cfg); }
"read" => { Deserializer::run(cfg); }
other => {
println!("invalid mode: {other}");
process::exit(1);
}
}
}

View file

@ -0,0 +1,17 @@
use crate::hir::HIR;
use crate::error::{CompileWarnings};
#[derive(Debug)]
pub struct HIROptimizer {
}
impl HIROptimizer {
pub fn fold_constants(&mut self, mut _hir: HIR) -> HIR { todo!() }
pub fn eliminate_unused_variables(&mut self, mut _hir: HIR) -> (HIR, CompileWarnings) { todo!() }
pub fn eliminate_dead_code(&mut self, mut _hir: HIR) -> (HIR, CompileWarnings) {
todo!()
}
}

View file

@ -0,0 +1,213 @@
use erg_common::Str;
use erg_common::{log};
use erg_common::color::{GREEN, RESET};
use erg_common::dict::Dict;
use erg_common::error::Location;
use erg_common::set::Set;
use erg_common::traits::{Stream, Locational, HasType};
use erg_common::ty::{ArgsOwnership, Ownership};
use crate::error::{OwnershipError, OwnershipErrors, OwnershipResult};
use crate::hir::{HIR, Def, Signature, Accessor, Block, Expr};
use crate::varinfo::Visibility;
use Visibility::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum WrapperKind {
Ref,
Rc,
Box,
}
#[derive(Debug, Default)]
struct LocalVars {
alive_vars: Set<Str>,
dropped_vars: Dict<Str, Location>,
}
#[derive(Debug)]
pub struct OwnershipChecker {
path_stack: Vec<(Str, Visibility)>,
dict: Dict<Str, LocalVars>,
errs: OwnershipErrors,
}
impl OwnershipChecker {
pub fn new() -> Self {
OwnershipChecker {
path_stack: vec![],
dict: Dict::new(),
errs: OwnershipErrors::empty(),
}
}
fn full_path(&self) -> String {
self.path_stack.iter().fold(String::new(), |acc, (path, vis)| {
if vis.is_public() { acc + "." + &path[..] } else { acc + "::" + &path[..] }
})
}
// moveされた後の変数が使用されていないかチェックする
// ProceduralでないメソッドでRefMutが使われているかはSideEffectCheckerでチェックする
pub fn check(mut self, hir: HIR) -> OwnershipResult<HIR> {
log!("{GREEN}[DEBUG] the ownership checking process has started.{RESET}");
self.path_stack.push((hir.name.clone(), Private));
self.dict.insert(Str::from(self.full_path()), LocalVars::default());
for chunk in hir.module.iter() {
self.check_expr(chunk, Ownership::Owned);
}
log!("{GREEN}[DEBUG] the ownership checking process has completed, found errors: {}{RESET}", self.errs.len());
if self.errs.is_empty() {
Ok(hir)
} else {
Err(self.errs)
}
}
fn check_block(&mut self, block: &Block) {
for chunk in block.iter() {
self.check_expr(chunk, Ownership::Owned);
}
}
fn check_expr(&mut self, expr: &Expr, ownership: Ownership) {
match expr {
Expr::Def(def) => {
self.define(&def);
let name_and_vis = match &def.sig {
Signature::Var(var) =>
// TODO: visibility
if let Some(name) = var.inspect() { (name.clone(), Private) }
else { (Str::ever("::<instant>"), Private) },
Signature::Subr(subr) => (subr.name.inspect().clone(), Private),
};
self.path_stack.push(name_and_vis);
self.dict.insert(Str::from(self.full_path()), LocalVars::default());
self.check_block(&def.body.block);
self.path_stack.pop();
},
Expr::Accessor(Accessor::Local(local)) => {
for n in 0..self.path_stack.len() {
if let Some(moved_loc) = self.nth_outer_scope(n).dropped_vars.get(local.inspect()) {
let moved_loc = *moved_loc;
self.errs.push(OwnershipError::move_error(
local.inspect(),
local.loc(),
moved_loc,
self.full_path(),
));
}
}
if expr.ref_t().is_mut() && ownership.is_owned() {
log!("dropped: {}", local.inspect());
self.drop(local.inspect(), expr.loc());
}
},
Expr::Accessor(Accessor::Attr(a)) => {
if a.ref_t().is_mut() { todo!("ownership checking {a}") }
},
Expr::Accessor(_a) => todo!(),
// TODO: referenced
Expr::Call(call) => {
self.check_expr(&call.obj, ownership);
let args_ownership = call.signature_t().unwrap().args_ownership();
match args_ownership {
ArgsOwnership::Args{ self_, non_defaults, defaults } => {
if let Some(ownership) = self_ {
self.check_expr(&call.obj, ownership);
}
let (nd_ownerships, d_ownerships): (Vec<_>, Vec<_>) = non_defaults.iter()
.enumerate()
.partition(|(i, _)| *i == call.args.pos_args().len());
for (parg, (_, ownership)) in call.args.pos_args()
.iter()
.zip(nd_ownerships.into_iter()) {
self.check_expr(&parg.expr, *ownership);
}
for (kwarg, (_, ownership)) in call.args.kw_args()
.iter()
.zip(d_ownerships.into_iter().chain(defaults.iter().enumerate())) {
self.check_expr(&kwarg.expr, *ownership);
}
},
ArgsOwnership::VarArgs(ownership) => {
for parg in call.args.pos_args().iter() {
self.check_expr(&parg.expr, ownership);
}
for kwarg in call.args.kw_args().iter() {
self.check_expr(&kwarg.expr, ownership);
}
},
other => todo!("{other:?}"),
}
},
// TODO: referenced
Expr::BinOp(binop) => {
self.check_expr(&binop.lhs, ownership);
self.check_expr(&binop.rhs, ownership);
},
Expr::UnaryOp(unary) => {
self.check_expr(&unary.expr, ownership);
},
Expr::Array(arr) => {
for a in arr.elems.pos_args().iter() {
self.check_expr(&a.expr, ownership);
}
},
Expr::Dict(dict) => {
for a in dict.attrs.kw_args().iter() {
// self.check_expr(&a.key);
self.check_expr(&a.expr, ownership);
}
},
// TODO: capturing
Expr::Lambda(lambda) => {
let name_and_vis = (Str::from(format!("<lambda_{}>", lambda.id)), Private);
self.path_stack.push(name_and_vis);
self.dict.insert(Str::from(self.full_path()), LocalVars::default());
self.check_block(&lambda.body);
self.path_stack.pop();
},
_ => {},
}
}
/// TODO: このメソッドを呼ぶとき、スコープを再帰的に検索する
#[inline]
fn current_scope(&mut self) -> &mut LocalVars {
self.dict.get_mut(&self.full_path()[..]).unwrap()
}
#[inline]
fn nth_outer_scope(&mut self, n: usize) -> &mut LocalVars {
let path = self.path_stack.iter()
.take(self.path_stack.len() - n)
.fold(String::new(), |acc, (path, vis)| {
if vis.is_public() { acc + "." + &path[..] } else { acc + "::" + &path[..] }
});
self.dict.get_mut(&path[..]).unwrap()
}
fn define(&mut self, def: &Def) {
match &def.sig {
Signature::Var(sig) => {
for name in sig.pat.inspects() {
self.current_scope().alive_vars.insert(name.clone());
}
},
Signature::Subr(sig) => {
self.current_scope().alive_vars.insert(sig.name.inspect().clone());
},
}
}
fn drop(&mut self, name: &Str, moved_loc: Location) {
for n in 0..self.path_stack.len() {
if self.nth_outer_scope(n).alive_vars.remove(name) {
self.nth_outer_scope(n).dropped_vars.insert(name.clone(), moved_loc);
return
}
}
panic!("variable not found: {name}");
}
}

View file

@ -0,0 +1,4 @@
concat|T: Type, M, N: Nat|(l: [T; M], r: [T; N]): [T; M + N] = l + r
l: [Nat; 6] = concat [1, 2, 3], [4, 5, 6]
assert l == [1, 2, 3, 4, 5, 6]

View file

@ -0,0 +1,6 @@
fib 0 = 0
fib 1 = 1
fib(n: 2..<Inf): Nat = fib(n-1) + fib(n-2)
print! fib 10
assert fib(10) == 55

View file

@ -0,0 +1,6 @@
v = ![]
v.push! 1
w = v
print! w
print! v # this should cause a MoveError

View file

@ -0,0 +1,9 @@
if True, () -> log "hello"
if! True, () => print! "hello"
# if True, () => print! "hello" # this should cause a type error
if True, () ->
_x = "aaa" + input!() # this should cause an effect error
print! "hello" # this should cause an effect error
f x: Int = log x
g x: Int = print! x # this should cause an effect error

View file

@ -0,0 +1,155 @@
use std::fmt;
use erg_common::Str;
use erg_common::ty::{Type};
use erg_common::traits::HasType;
use erg_parser::ast::DefId;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum Mutability {
Immutable,
Const,
}
impl From<&str> for Mutability {
fn from(item: &str) -> Self {
if item.chars().next().unwrap().is_uppercase() {
Self::Const
} else { Self::Immutable }
}
}
impl Mutability {
pub const fn is_const(&self) -> bool { matches!(self, Self::Const) }
}
use Mutability::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum Visibility {
Private,
Public,
}
impl Visibility {
pub const fn is_public(&self) -> bool { matches!(self, Self::Public) }
pub const fn is_private(&self) -> bool { matches!(self, Self::Private) }
}
use Visibility::*;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ParamId {
/// 変数でないパターン
/// e.g. `[x, y]` of `f [x, y], z = ...`
PatNonDefault(usize),
/// e.g. `[x, y]` of `f [x, y] |= [0, 1] = ...`
PatWithDefault(usize),
/// 変数パターン
/// e.g. `z` of `f [x, y], z = ...`
VarNonDefault{ keyword: Str, pos: usize },
/// e.g. `z` of `f [x, y], z |= 0 = ...`
VarWithDefault{ keyword: Str, pos: usize },
/// パターンに埋め込まれた変数パターン
/// この場合デフォルト値はない
/// e.g. `x` or `y` of `f [x, y], z = ...`
Embedded(Str),
}
impl ParamId {
pub const fn var_default(keyword: Str, pos: usize) -> Self { Self::VarWithDefault{ keyword, pos } }
pub const fn var_non_default(keyword: Str, pos: usize) -> Self { Self::VarNonDefault{ keyword, pos } }
pub const fn pos(&self) -> Option<usize> {
match self {
Self::PatNonDefault(pos)
| Self::PatWithDefault(pos)
| Self::VarNonDefault{ pos, .. }
| Self::VarWithDefault{ pos, .. } => Some(*pos),
_ => None,
}
}
pub const fn has_default(&self) -> bool {
matches!(self, Self::PatWithDefault(_) | Self::VarWithDefault{ .. })
}
pub const fn is_embedded(&self) -> bool { matches!(self, Self::Embedded(_)) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum VarKind {
Defined(DefId),
Declared,
Parameter{ def_id: DefId, param_id: ParamId },
Generated,
DoesNotExist,
Builtin,
}
impl VarKind {
pub const fn parameter(def_id: DefId, param_id: ParamId) -> Self {
Self::Parameter{ def_id, param_id }
}
pub const fn pos_as_param(&self) -> Option<usize> {
match self {
Self::Parameter{ param_id, .. } => param_id.pos(),
_ => None,
}
}
pub const fn has_default(&self) -> bool {
match self {
Self::Parameter{ param_id, .. } => param_id.has_default(),
_ => false,
}
}
pub const fn is_parameter(&self) -> bool {
matches!(self, Self::Parameter{ .. })
}
pub const fn is_embedded_param(&self) -> bool {
matches!(self, Self::Parameter{ param_id, .. } if param_id.is_embedded())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct VarInfo {
pub t: Type,
pub muty: Mutability,
pub vis: Visibility,
pub kind: VarKind,
}
impl fmt::Display for VarInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VarInfo{{t: {}, muty: {:?}, vis: {:?} kind: {:?}}}", self.t, self.muty, self.vis, self.kind)
}
}
impl HasType for VarInfo {
#[inline]
fn ref_t(&self) -> &Type { &self.t }
#[inline]
fn signature_t(&self) -> Option<&Type> { None }
}
impl VarInfo {
pub const ILLEGAL: &'static Self = &VarInfo::new(Type::Failure, Immutable, Private, VarKind::DoesNotExist);
pub const fn new(t: Type, muty: Mutability, vis: Visibility, kind: VarKind) -> Self {
Self { t, muty, vis, kind }
}
pub fn same_id_as(&self, id: DefId) -> bool {
match self.kind {
VarKind::Defined(i) | VarKind::Parameter{ def_id: i, .. } => id == i,
_ => false,
}
}
}