mirror of
https://github.com/erg-lang/erg.git
synced 2025-10-03 05:54:33 +00:00
Merge branch 'main' into codegen-bug
This commit is contained in:
commit
4053e1646f
38 changed files with 901 additions and 221 deletions
2
.github/workflows/rust.yml
vendored
2
.github/workflows/rust.yml
vendored
|
@ -54,7 +54,7 @@ jobs:
|
|||
os: [windows-latest, ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- run: rustup update stable
|
||||
- run: cargo build --all --all-targets --verbose
|
||||
|
|
|
@ -2,6 +2,7 @@ use std::collections::HashMap;
|
|||
use std::path::PathBuf;
|
||||
use std::time::SystemTime;
|
||||
|
||||
use erg_common::pathutil::NormalizedPathBuf;
|
||||
use erg_common::traits::{Locational, Stream};
|
||||
use erg_compiler::artifact::IncompleteArtifact;
|
||||
use erg_compiler::erg_parser::parse::Parsable;
|
||||
|
@ -146,26 +147,26 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
|
|||
/// self is __included__
|
||||
pub fn dependencies_of(&self, uri: &NormalizedUrl) -> Vec<NormalizedUrl> {
|
||||
let graph = self.get_graph().unwrap();
|
||||
let path = util::uri_to_path(uri);
|
||||
let path = NormalizedPathBuf::from(util::uri_to_path(uri));
|
||||
graph.sort().unwrap();
|
||||
let self_node = graph.get_node(&path).unwrap();
|
||||
graph
|
||||
.ref_inner()
|
||||
.iter()
|
||||
.filter(|node| node.id == path || self_node.depends_on(&node.id))
|
||||
.map(|node| NormalizedUrl::new(Url::from_file_path(&node.id).unwrap()))
|
||||
.map(|node| NormalizedUrl::new(Url::from_file_path(node.id.to_path_buf()).unwrap()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// self is __not included__
|
||||
pub fn dependents_of(&self, uri: &NormalizedUrl) -> Vec<NormalizedUrl> {
|
||||
let graph = self.get_graph().unwrap();
|
||||
let path = util::uri_to_path(uri);
|
||||
let path = NormalizedPathBuf::from(util::uri_to_path(uri));
|
||||
graph
|
||||
.ref_inner()
|
||||
.iter()
|
||||
.filter(|node| node.depends_on(&path))
|
||||
.map(|node| NormalizedUrl::new(Url::from_file_path(&node.id).unwrap()))
|
||||
.map(|node| NormalizedUrl::new(Url::from_file_path(node.id.to_path_buf()).unwrap()))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -216,7 +216,11 @@ impl Input {
|
|||
match &self.kind {
|
||||
InputKind::File(filename) => format!(
|
||||
"{}_{}",
|
||||
filename.file_stem().and_then(|f| f.to_str()).unwrap_or("_"),
|
||||
filename
|
||||
.file_stem()
|
||||
.and_then(|f| f.to_str())
|
||||
.unwrap_or("_")
|
||||
.trim_end_matches(".d"),
|
||||
self.id
|
||||
),
|
||||
InputKind::REPL | InputKind::Pipe(_) => format!("stdin_{}", self.id),
|
||||
|
@ -248,9 +252,11 @@ impl Input {
|
|||
|
||||
pub fn unescaped_file_stem(&self) -> &str {
|
||||
match &self.kind {
|
||||
InputKind::File(filename) => {
|
||||
filename.file_stem().and_then(|f| f.to_str()).unwrap_or("_")
|
||||
}
|
||||
InputKind::File(filename) => filename
|
||||
.file_stem()
|
||||
.and_then(|f| f.to_str())
|
||||
.unwrap_or("_")
|
||||
.trim_end_matches(".d"),
|
||||
InputKind::REPL | InputKind::Pipe(_) => "stdin",
|
||||
InputKind::DummyREPL(_stdin) => "stdin",
|
||||
InputKind::Str(_) => "string",
|
||||
|
@ -260,9 +266,11 @@ impl Input {
|
|||
|
||||
pub fn unescaped_filename(&self) -> &str {
|
||||
match &self.kind {
|
||||
InputKind::File(filename) => {
|
||||
filename.file_name().and_then(|f| f.to_str()).unwrap_or("_")
|
||||
}
|
||||
InputKind::File(filename) => filename
|
||||
.file_name()
|
||||
.and_then(|f| f.to_str())
|
||||
.unwrap_or("_")
|
||||
.trim_end_matches(".d"),
|
||||
InputKind::REPL | InputKind::Pipe(_) => "stdin",
|
||||
InputKind::DummyREPL(_stdin) => "stdin",
|
||||
InputKind::Str(_) => "string",
|
||||
|
@ -283,7 +291,9 @@ impl Input {
|
|||
pub fn module_name(&self) -> String {
|
||||
match &self.kind {
|
||||
InputKind::File(filename) => {
|
||||
let file_stem = if filename.file_stem() == Some(OsStr::new("__init__")) {
|
||||
let file_stem = if filename.file_stem() == Some(OsStr::new("__init__"))
|
||||
|| filename.file_stem() == Some(OsStr::new("__init__.d"))
|
||||
{
|
||||
filename.parent().and_then(|p| p.file_stem())
|
||||
} else {
|
||||
filename.file_stem()
|
||||
|
@ -291,6 +301,7 @@ impl Input {
|
|||
file_stem
|
||||
.and_then(|f| f.to_str())
|
||||
.unwrap_or("_")
|
||||
.trim_end_matches(".d")
|
||||
.to_string()
|
||||
}
|
||||
InputKind::REPL | InputKind::Pipe(_) => "<stdin>".to_string(),
|
||||
|
@ -748,11 +759,11 @@ impl Output {
|
|||
pub fn lsp_log(file: &str, line: u32, msg: &str) {
|
||||
let file_path = erg_path().join("els.log");
|
||||
let mut f = if file_path.exists() {
|
||||
File::options().write(true).open(file_path).unwrap()
|
||||
File::options().append(true).open(file_path).unwrap()
|
||||
} else {
|
||||
File::create(file_path).unwrap()
|
||||
};
|
||||
f.write_all(format!("{file}@{line}: {msg}").as_bytes())
|
||||
f.write_all(format!("{file}@{line}: {msg}\n").as_bytes())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ pub mod levenshtein;
|
|||
pub mod macros;
|
||||
pub mod opcode;
|
||||
pub mod opcode308;
|
||||
pub mod opcode309;
|
||||
pub mod opcode310;
|
||||
pub mod opcode311;
|
||||
pub mod pathutil;
|
||||
|
|
|
@ -31,14 +31,14 @@ impl_u8_enum! {Opcode308;
|
|||
BINARY_TRUE_DIVIDE = 27,
|
||||
INPLACE_FLOOR_DIVIDE = 28,
|
||||
INPLACE_TRUE_DIVIDE = 29,
|
||||
GET_LEN = 30,
|
||||
MATCH_MAPPING = 31,
|
||||
MATCH_SEQUENCE = 32,
|
||||
MATCH_KEYS = 33,
|
||||
PUSH_EXC_INFO = 35,
|
||||
CHECK_EXC_MATCH = 36,
|
||||
CHECK_EG_MATCH = 37,
|
||||
WITH_EXCEPT_START = 49,
|
||||
// GET_LEN = 30,
|
||||
// MATCH_MAPPING = 31,
|
||||
// MATCH_SEQUENCE = 32,
|
||||
// MATCH_KEYS = 33,
|
||||
// PUSH_EXC_INFO = 35,
|
||||
// CHECK_EXC_MATCH = 36,
|
||||
// CHECK_EG_MATCH = 37,
|
||||
// WITH_EXCEPT_START = 49,
|
||||
GET_AITER = 50,
|
||||
GET_ANEXT = 51,
|
||||
BEFORE_ASYNC_WITH = 52,
|
||||
|
@ -92,9 +92,10 @@ impl_u8_enum! {Opcode308;
|
|||
POP_JUMP_IF_FALSE = 114,
|
||||
POP_JUMP_IF_TRUE = 115,
|
||||
LOAD_GLOBAL = 116,
|
||||
IS_OP = 117,
|
||||
CONTAINS_OP = 118,
|
||||
RERAISE = 119,
|
||||
// IS_OP = 117,
|
||||
// CONTAINS_OP = 118,
|
||||
// RERAISE = 119,
|
||||
SETUP_FINALLY = 122,
|
||||
LOAD_FAST = 124,
|
||||
STORE_FAST = 125,
|
||||
DELETE_FAST = 126,
|
||||
|
|
163
crates/erg_common/opcode309.rs
Normal file
163
crates/erg_common/opcode309.rs
Normal file
|
@ -0,0 +1,163 @@
|
|||
//! defines `Opcode` (represents Python bytecode opcodes).
|
||||
//!
|
||||
//! Opcode(Pythonバイトコードオペコードを表す)を定義する
|
||||
|
||||
#![allow(dead_code)]
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
use crate::impl_u8_enum;
|
||||
|
||||
impl_u8_enum! {Opcode309;
|
||||
POP_TOP = 1,
|
||||
ROT_TWO = 2,
|
||||
ROT_THREE = 3,
|
||||
DUP_TOP = 4,
|
||||
DUP_TOP2 = 5,
|
||||
ROT_FOUR = 6,
|
||||
NOP = 9,
|
||||
UNARY_POSITIVE = 10,
|
||||
UNARY_NEGATIVE = 11,
|
||||
UNARY_NOT = 12,
|
||||
UNARY_INVERT = 15,
|
||||
BINARY_MATRIX_MULTIPLY = 16,
|
||||
INPLACE_MATRIX_MULTIPLY = 17,
|
||||
BINARY_POWER = 19,
|
||||
BINARY_MULTIPLY = 20,
|
||||
BINARY_MODULO = 22,
|
||||
BINARY_ADD = 23,
|
||||
BINARY_SUBTRACT = 24,
|
||||
BINARY_SUBSCR = 25,
|
||||
BINARY_FLOOR_DIVIDE = 26,
|
||||
BINARY_TRUE_DIVIDE = 27,
|
||||
INPLACE_FLOOR_DIVIDE = 28,
|
||||
INPLACE_TRUE_DIVIDE = 29,
|
||||
// GET_LEN = 30,
|
||||
// MATCH_MAPPING = 31,
|
||||
// MATCH_SEQUENCE = 32,
|
||||
// MATCH_KEYS = 33,
|
||||
// PUSH_EXC_INFO = 35,
|
||||
// CHECK_EXC_MATCH = 36,
|
||||
// CHECK_EG_MATCH = 37,
|
||||
RERAISE = 48,
|
||||
WITH_EXCEPT_START = 49,
|
||||
GET_AITER = 50,
|
||||
GET_ANEXT = 51,
|
||||
BEFORE_ASYNC_WITH = 52,
|
||||
// BEGIN_FINALLY = 53,
|
||||
END_ASYNC_FOR = 54,
|
||||
// TODO:
|
||||
INPLACE_ADD = 55,
|
||||
INPLACE_SUBTRACT = 56,
|
||||
INPLACE_MULTIPLY = 57,
|
||||
INPLACE_MODULO = 59,
|
||||
STORE_SUBSCR = 60,
|
||||
BINARY_AND = 64,
|
||||
BINARY_XOR = 65,
|
||||
BINARY_OR = 66,
|
||||
GET_ITER = 68,
|
||||
GET_YIELD_FROM_ITER = 69,
|
||||
PRINT_EXPR = 70,
|
||||
LOAD_BUILD_CLASS = 71,
|
||||
// LOAD_ASSERTION_ERROR = 74,
|
||||
// WITH_CLEANUP_START = 81,
|
||||
WITH_CLEANUP_FINISH = 82,
|
||||
RETURN_VALUE = 83,
|
||||
IMPORT_STAR = 84,
|
||||
SETUP_ANNOTATIONS = 85,
|
||||
YIELD_VALUE = 86,
|
||||
POP_BLOCK = 87,
|
||||
// END_FINALLY = 88,
|
||||
POP_EXCEPT = 89,
|
||||
/* ↓ These opcodes take an arg */
|
||||
STORE_NAME = 90,
|
||||
DELETE_NAME = 91,
|
||||
UNPACK_SEQUENCE = 92,
|
||||
FOR_ITER = 93,
|
||||
UNPACK_EX = 94,
|
||||
STORE_ATTR = 95,
|
||||
STORE_GLOBAL = 97,
|
||||
LOAD_CONST = 100,
|
||||
LOAD_NAME = 101,
|
||||
BUILD_TUPLE = 102,
|
||||
BUILD_LIST = 103,
|
||||
BUILD_SET = 104,
|
||||
BUILD_MAP = 105, // build a Dict object
|
||||
LOAD_ATTR = 106,
|
||||
COMPARE_OP = 107,
|
||||
IMPORT_NAME = 108,
|
||||
IMPORT_FROM = 109,
|
||||
JUMP_FORWARD = 110,
|
||||
JUMP_IF_FALSE_OR_POP = 111,
|
||||
JUMP_IF_TRUE_OR_POP = 112,
|
||||
JUMP_ABSOLUTE = 113,
|
||||
POP_JUMP_IF_FALSE = 114,
|
||||
POP_JUMP_IF_TRUE = 115,
|
||||
LOAD_GLOBAL = 116,
|
||||
IS_OP = 117,
|
||||
CONTAINS_OP = 118,
|
||||
LOAD_FAST = 124,
|
||||
STORE_FAST = 125,
|
||||
DELETE_FAST = 126,
|
||||
RAISE_VARARGS = 130,
|
||||
CALL_FUNCTION = 131,
|
||||
MAKE_FUNCTION = 132,
|
||||
LOAD_CLOSURE = 135,
|
||||
LOAD_DEREF = 136,
|
||||
STORE_DEREF = 137,
|
||||
CALL_FUNCTION_KW = 141,
|
||||
CALL_FUNCTION_EX = 142,
|
||||
SETUP_WITH = 143,
|
||||
EXTENDED_ARG = 144,
|
||||
BUILD_TUPLE_UNPACK_WITH_CALL = 158,
|
||||
LOAD_METHOD = 160,
|
||||
CALL_METHOD = 161,
|
||||
CALL_FINALLY = 162,
|
||||
POP_FINALLY = 163,
|
||||
// Erg-specific opcodes (must have a unary `ERG_`)
|
||||
// Define in descending order from 219, 255
|
||||
ERG_POP_NTH = 196,
|
||||
ERG_PEEK_NTH = 197, // get ref to the arg-th element from TOS
|
||||
ERG_INC = 198, // name += 1; arg: typecode
|
||||
ERG_DEC = 199, // name -= 1
|
||||
ERG_LOAD_FAST_IMMUT = 200,
|
||||
ERG_STORE_FAST_IMMUT = 201,
|
||||
ERG_MOVE_FAST = 202,
|
||||
ERG_CLONE_FAST = 203,
|
||||
ERG_COPY_FAST = 204,
|
||||
ERG_REF_FAST = 205,
|
||||
ERG_REF_MUT_FAST = 206,
|
||||
ERG_MOVE_OUTER = 207,
|
||||
ERG_CLONE_OUTER = 208,
|
||||
ERG_COPY_OUTER = 209,
|
||||
ERG_REF_OUTER = 210,
|
||||
ERG_REF_MUT_OUTER = 211,
|
||||
ERG_LESS_THAN = 212,
|
||||
ERG_LESS_EQUAL = 213,
|
||||
ERG_EQUAL = 214,
|
||||
ERG_NOT_EQUAL = 215,
|
||||
ERG_MAKE_SLOT = 216,
|
||||
ERG_MAKE_TYPE = 217,
|
||||
ERG_MAKE_PURE_FUNCTION = 218,
|
||||
ERG_CALL_PURE_FUNCTION = 219,
|
||||
/* ↑ These opcodes take an arg ↑ */
|
||||
/* ↓ These opcodes take no arg ↓ */
|
||||
// ... = 220,
|
||||
ERG_LOAD_EMPTY_SLOT = 242,
|
||||
ERG_LOAD_EMPTY_STR = 243,
|
||||
ERG_LOAD_1_NAT = 244,
|
||||
ERG_LOAD_1_INT = 245,
|
||||
ERG_LOAD_1_REAL = 246,
|
||||
ERG_LOAD_NONE = 247,
|
||||
ERG_MUTATE = 248, // !x
|
||||
// `[] =` (it doesn't cause any exceptions)
|
||||
ERG_STORE_SUBSCR = 249,
|
||||
// ... = 250,
|
||||
// `= []` (it doesn't cause any exceptions)
|
||||
ERG_BINARY_SUBSCR = 251,
|
||||
ERG_BINARY_RANGE = 252,
|
||||
// `/?` (rhs may be 0, it may cause a runtime panic)
|
||||
ERG_TRY_BINARY_DIVIDE = 253,
|
||||
// `/` (rhs could not be 0, it doesn't cause any exceptions)
|
||||
ERG_BINARY_TRUE_DIVIDE = 254,
|
||||
NOT_IMPLEMENTED = 255,
|
||||
}
|
|
@ -10,7 +10,7 @@ use crate::normalize_path;
|
|||
/// `PathBuf` may give false equivalence decisions in non-case-sensitive file systems.
|
||||
/// Use this for dictionary keys, etc.
|
||||
/// See also: `els::util::NormalizedUrl`
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
|
||||
pub struct NormalizedPathBuf(PathBuf);
|
||||
|
||||
impl<P: Into<PathBuf>> From<P> for NormalizedPathBuf {
|
||||
|
@ -41,7 +41,7 @@ impl Deref for NormalizedPathBuf {
|
|||
|
||||
impl NormalizedPathBuf {
|
||||
pub fn new(path: PathBuf) -> Self {
|
||||
NormalizedPathBuf(normalize_path(path))
|
||||
NormalizedPathBuf(normalize_path(path.canonicalize().unwrap_or(path)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ use erg_common::fresh::SharedFreshNameGenerator;
|
|||
use erg_common::io::Input;
|
||||
use erg_common::opcode::{CommonOpcode, CompareOp};
|
||||
use erg_common::opcode308::Opcode308;
|
||||
use erg_common::opcode309::Opcode309;
|
||||
use erg_common::opcode310::Opcode310;
|
||||
use erg_common::opcode311::{BinOpCode, Opcode311};
|
||||
use erg_common::option_enum_unwrap;
|
||||
|
@ -1783,10 +1784,10 @@ impl PyCodeGenerator {
|
|||
self.write_instr(Opcode308::JUMP_FORWARD); // jump to end
|
||||
self.write_arg(0);
|
||||
// else block
|
||||
let idx_else_begin = if self.py_version.minor >= Some(11) {
|
||||
self.lasti() - idx_pop_jump_if_false - 2
|
||||
} else {
|
||||
self.lasti()
|
||||
let idx_else_begin = match self.py_version.minor {
|
||||
Some(11) => self.lasti() - idx_pop_jump_if_false - 2,
|
||||
Some(10) => self.lasti(),
|
||||
_ => self.lasti(),
|
||||
};
|
||||
self.fill_jump(idx_pop_jump_if_false + 1, idx_else_begin - 2);
|
||||
match args.remove(0) {
|
||||
|
@ -1805,8 +1806,12 @@ impl PyCodeGenerator {
|
|||
self.stack_dec();
|
||||
}
|
||||
} else {
|
||||
self.write_instr(Opcode308::JUMP_FORWARD);
|
||||
self.write_arg(1);
|
||||
self.write_instr(Opcode311::JUMP_FORWARD);
|
||||
let jump_to = match self.py_version.minor {
|
||||
Some(11 | 10) => 1,
|
||||
_ => 2,
|
||||
};
|
||||
self.write_arg(jump_to);
|
||||
// no else block
|
||||
let idx_end = if self.py_version.minor >= Some(11) {
|
||||
self.lasti() - idx_pop_jump_if_false - 1
|
||||
|
@ -1861,7 +1866,7 @@ impl PyCodeGenerator {
|
|||
self.write_arg(idx_for_iter / 2);
|
||||
}
|
||||
Some(9 | 8 | 7) => {
|
||||
self.write_instr(Opcode308::JUMP_ABSOLUTE);
|
||||
self.write_instr(Opcode309::JUMP_ABSOLUTE);
|
||||
self.write_arg(idx_for_iter);
|
||||
}
|
||||
_ => todo!("not supported Python version"),
|
||||
|
@ -2077,7 +2082,7 @@ impl PyCodeGenerator {
|
|||
self.write_arg(0);
|
||||
self.write_instr(Opcode311::PUSH_EXC_INFO);
|
||||
self.write_arg(0);
|
||||
self.write_instr(Opcode308::WITH_EXCEPT_START);
|
||||
self.write_instr(Opcode309::WITH_EXCEPT_START);
|
||||
self.write_arg(0);
|
||||
self.write_instr(Opcode311::POP_JUMP_FORWARD_IF_TRUE);
|
||||
self.write_arg(4);
|
||||
|
@ -2151,6 +2156,59 @@ impl PyCodeGenerator {
|
|||
self.emit_load_name_instr(stash);
|
||||
}
|
||||
|
||||
fn emit_with_instr_309(&mut self, mut args: Args) {
|
||||
log!(info "entered {}", fn_name!());
|
||||
if !matches!(args.get(1).unwrap(), Expr::Lambda(_)) {
|
||||
return self.deopt_instr(ControlKind::With, args);
|
||||
}
|
||||
let expr = args.remove(0);
|
||||
let Expr::Lambda(lambda) = args.remove(0) else { unreachable!() };
|
||||
let params = self.gen_param_names(&lambda.params);
|
||||
self.emit_expr(expr);
|
||||
let idx_setup_with = self.lasti();
|
||||
self.write_instr(Opcode310::SETUP_WITH);
|
||||
self.write_arg(0);
|
||||
// push __exit__, __enter__() to the stack
|
||||
self.stack_inc_n(2);
|
||||
let lambda_line = lambda.body.last().unwrap().ln_begin().unwrap_or(0);
|
||||
self.emit_with_block(lambda.body, params);
|
||||
let stash = Identifier::private_with_line(self.fresh_gen.fresh_varname(), lambda_line);
|
||||
self.emit_store_instr(stash.clone(), Name);
|
||||
self.write_instr(POP_BLOCK);
|
||||
self.write_arg(0);
|
||||
self.emit_load_const(ValueObj::None);
|
||||
self.write_instr(Opcode310::DUP_TOP);
|
||||
self.write_arg(0);
|
||||
self.stack_inc();
|
||||
self.write_instr(Opcode310::DUP_TOP);
|
||||
self.write_arg(0);
|
||||
self.stack_inc();
|
||||
self.write_instr(Opcode310::CALL_FUNCTION);
|
||||
self.write_arg(3);
|
||||
self.stack_dec_n((1 + 3) - 1);
|
||||
self.emit_pop_top();
|
||||
let idx_jump_forward = self.lasti();
|
||||
self.write_instr(Opcode311::JUMP_FORWARD);
|
||||
self.write_arg(0);
|
||||
self.edit_code(idx_setup_with + 1, self.lasti() - idx_setup_with - 2);
|
||||
self.write_instr(Opcode310::WITH_EXCEPT_START);
|
||||
self.write_arg(0);
|
||||
let idx_pop_jump_if_true = self.lasti();
|
||||
self.write_instr(Opcode310::POP_JUMP_IF_TRUE);
|
||||
self.write_arg(0);
|
||||
self.write_instr(Opcode309::RERAISE);
|
||||
self.write_arg(1);
|
||||
self.edit_code(idx_pop_jump_if_true + 1, self.lasti());
|
||||
// self.emit_pop_top();
|
||||
// self.emit_pop_top();
|
||||
self.emit_pop_top();
|
||||
self.write_instr(Opcode310::POP_EXCEPT);
|
||||
self.write_arg(0);
|
||||
let idx_end = self.lasti();
|
||||
self.edit_code(idx_jump_forward + 1, idx_end - idx_jump_forward - 2);
|
||||
self.emit_load_name_instr(stash);
|
||||
}
|
||||
|
||||
fn emit_with_instr_308(&mut self, mut args: Args) {
|
||||
log!(info "entered {}", fn_name!());
|
||||
if !matches!(args.get(1).unwrap(), Expr::Lambda(_)) {
|
||||
|
@ -2161,7 +2219,7 @@ impl PyCodeGenerator {
|
|||
let params = self.gen_param_names(&lambda.params);
|
||||
self.emit_expr(expr);
|
||||
let idx_setup_with = self.lasti();
|
||||
self.write_instr(Opcode308::SETUP_WITH);
|
||||
self.write_instr(Opcode309::SETUP_WITH);
|
||||
self.write_arg(0);
|
||||
// push __exit__, __enter__() to the stack
|
||||
// self.stack_inc_n(2);
|
||||
|
@ -2219,7 +2277,8 @@ impl PyCodeGenerator {
|
|||
"with!" => match self.py_version.minor {
|
||||
Some(11) => self.emit_with_instr_311(args),
|
||||
Some(10) => self.emit_with_instr_310(args),
|
||||
Some(9 | 8 | 7) => self.emit_with_instr_308(args),
|
||||
Some(9) => self.emit_with_instr_309(args),
|
||||
Some(8) => self.emit_with_instr_308(args),
|
||||
_ => todo!("not supported Python version"),
|
||||
},
|
||||
// "pyimport" | "py" are here
|
||||
|
@ -2281,7 +2340,7 @@ impl PyCodeGenerator {
|
|||
}
|
||||
self.emit_expr(var_args.expr.clone());
|
||||
if pos_len > 0 {
|
||||
self.write_instr(Opcode308::BUILD_TUPLE_UNPACK_WITH_CALL);
|
||||
self.write_instr(Opcode309::BUILD_TUPLE_UNPACK_WITH_CALL);
|
||||
self.write_arg(2);
|
||||
}
|
||||
}
|
||||
|
@ -2752,6 +2811,10 @@ impl PyCodeGenerator {
|
|||
self.emit_load_name_instr(Identifier::public("Str"));
|
||||
}
|
||||
other => match &other.qual_name()[..] {
|
||||
"Bytes" => {
|
||||
self.emit_push_null();
|
||||
self.emit_load_name_instr(Identifier::public("Bytes"));
|
||||
}
|
||||
"Array" => {
|
||||
self.emit_push_null();
|
||||
self.emit_load_name_instr(Identifier::public("Array"));
|
||||
|
|
|
@ -14,6 +14,7 @@ use OpKind::*;
|
|||
use erg_parser::ast::Dict as AstDict;
|
||||
use erg_parser::ast::Set as AstSet;
|
||||
use erg_parser::ast::*;
|
||||
use erg_parser::desugar::Desugarer;
|
||||
use erg_parser::token::{Token, TokenKind};
|
||||
|
||||
use crate::ty::constructors::{
|
||||
|
@ -303,8 +304,28 @@ impl Context {
|
|||
|
||||
fn call(&self, subr: ConstSubr, args: ValueArgs, loc: Location) -> EvalResult<ValueObj> {
|
||||
match subr {
|
||||
ConstSubr::User(_user) => {
|
||||
feature_error!(self, loc, "calling user-defined subroutines").map_err(Into::into)
|
||||
ConstSubr::User(user) => {
|
||||
// HACK: should avoid cloning
|
||||
let mut subr_ctx = Context::instant(
|
||||
user.name.clone(),
|
||||
self.cfg.clone(),
|
||||
2,
|
||||
self.shared.clone(),
|
||||
self.clone(),
|
||||
);
|
||||
// TODO: var_args
|
||||
for (arg, sig) in args
|
||||
.pos_args
|
||||
.into_iter()
|
||||
.zip(user.params.non_defaults.iter())
|
||||
{
|
||||
let name = VarName::from_str(sig.inspect().unwrap().clone());
|
||||
subr_ctx.consts.insert(name, arg);
|
||||
}
|
||||
for (name, arg) in args.kw_args.into_iter() {
|
||||
subr_ctx.consts.insert(VarName::from_str(name), arg);
|
||||
}
|
||||
subr_ctx.eval_const_block(&user.block())
|
||||
}
|
||||
ConstSubr::Builtin(builtin) => builtin.call(args, self).map_err(|mut e| {
|
||||
if e.0.loc.is_unknown() {
|
||||
|
@ -316,6 +337,16 @@ impl Context {
|
|||
self.caused_by(),
|
||||
))
|
||||
}),
|
||||
ConstSubr::Gen(gen) => gen.call(args, self).map_err(|mut e| {
|
||||
if e.0.loc.is_unknown() {
|
||||
e.0.loc = loc;
|
||||
}
|
||||
EvalErrors::from(EvalError::new(
|
||||
*e.0,
|
||||
self.cfg.input.clone(),
|
||||
self.caused_by(),
|
||||
))
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -429,7 +460,9 @@ impl Context {
|
|||
fn eval_const_record(&self, record: &Record) -> EvalResult<ValueObj> {
|
||||
match record {
|
||||
Record::Normal(rec) => self.eval_const_normal_record(rec),
|
||||
Record::Mixed(_rec) => unreachable_error!(self), // should be desugared
|
||||
Record::Mixed(mixed) => self.eval_const_normal_record(
|
||||
&Desugarer::desugar_shortened_record_inner(mixed.clone()),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1478,24 +1511,29 @@ impl Context {
|
|||
}
|
||||
}
|
||||
|
||||
fn convert_type_to_array(&self, ty: Type) -> Result<Vec<ValueObj>, ()> {
|
||||
fn convert_type_to_array(&self, ty: Type) -> Result<Vec<ValueObj>, Type> {
|
||||
match ty {
|
||||
Type::Poly { name, params } if &name[..] == "Array" || &name[..] == "Array!" => {
|
||||
let t = self
|
||||
.convert_tp_into_type(params[0].clone())
|
||||
.map_err(|_| ())?;
|
||||
let Ok(t) = self.convert_tp_into_type(params[0].clone()) else {
|
||||
return Err(poly(name, params));
|
||||
};
|
||||
let TyParam::Value(ValueObj::Nat(len)) = params[1] else { unreachable!() };
|
||||
Ok(vec![ValueObj::builtin_type(t); len as usize])
|
||||
}
|
||||
_ => Err(()),
|
||||
_ => Err(ty),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn convert_value_into_array(&self, val: ValueObj) -> Result<Vec<ValueObj>, ()> {
|
||||
pub(crate) fn convert_value_into_array(
|
||||
&self,
|
||||
val: ValueObj,
|
||||
) -> Result<Vec<ValueObj>, ValueObj> {
|
||||
match val {
|
||||
ValueObj::Array(arr) => Ok(arr.to_vec()),
|
||||
ValueObj::Type(t) => self.convert_type_to_array(t.into_typ()),
|
||||
_ => Err(()),
|
||||
ValueObj::Type(t) => self
|
||||
.convert_type_to_array(t.into_typ())
|
||||
.map_err(ValueObj::builtin_type),
|
||||
_ => Err(val),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1784,9 +1822,8 @@ impl Context {
|
|||
})? {
|
||||
if let Ok(obj) = ty_ctx.get_const_local(&Token::symbol(&attr_name), &self.name) {
|
||||
if let ValueObj::Subr(subr) = obj {
|
||||
let is_method = subr.sig_t().self_t().is_some();
|
||||
let mut pos_args = vec![];
|
||||
if is_method {
|
||||
if subr.sig_t().is_method() {
|
||||
match ValueObj::try_from(lhs) {
|
||||
Ok(value) => {
|
||||
pos_args.push(value);
|
||||
|
|
|
@ -41,6 +41,9 @@ impl Generalizer {
|
|||
fn generalize_tp(&mut self, free: TyParam, uninit: bool) -> TyParam {
|
||||
match free {
|
||||
TyParam::Type(t) => TyParam::t(self.generalize_t(*t, uninit)),
|
||||
TyParam::Value(ValueObj::Type(t)) => {
|
||||
TyParam::t(self.generalize_t(t.into_typ(), uninit))
|
||||
}
|
||||
TyParam::FreeVar(fv) if fv.is_generalized() => TyParam::FreeVar(fv),
|
||||
TyParam::FreeVar(fv) if fv.is_linked() => {
|
||||
self.generalize_tp(fv.crack().clone(), uninit)
|
||||
|
@ -122,7 +125,7 @@ impl Generalizer {
|
|||
TyParam::unary(op, val)
|
||||
}
|
||||
other if other.has_no_unbound_var() => other,
|
||||
other => todo!("{other}"),
|
||||
other => todo!("{other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -897,7 +897,7 @@ impl Context {
|
|||
Immutable,
|
||||
Visibility::BUILTIN_PUBLIC,
|
||||
);
|
||||
let str_getitem_t = fn1_kw_met(Str, kw(KW_IDX, Nat), Str);
|
||||
let str_getitem_t = fn1_kw_met(Str, kw(KW_IDX, Nat | poly(RANGE, vec![ty_tp(Int)])), Str);
|
||||
str_.register_builtin_erg_impl(
|
||||
FUNDAMENTAL_GETITEM,
|
||||
str_getitem_t,
|
||||
|
@ -1181,7 +1181,7 @@ impl Context {
|
|||
);
|
||||
/* Array */
|
||||
let mut array_ =
|
||||
Self::builtin_poly_class(ARRAY, vec![PS::t_nd(TY_T), PS::named_nd(TY_N, Nat)], 10);
|
||||
Self::builtin_poly_class(ARRAY, vec![PS::t_nd(TY_T), PS::default(TY_N, Nat)], 10);
|
||||
array_.register_superclass(mono(GENERIC_ARRAY), &generic_array);
|
||||
array_
|
||||
.register_marker_trait(self, poly(OUTPUT, vec![ty_tp(T.clone())]))
|
||||
|
@ -1240,11 +1240,16 @@ impl Context {
|
|||
Predicate::le(var, N.clone() - value(1usize)),
|
||||
);
|
||||
// __getitem__: |T, N|(self: [T; N], _: {I: Nat | I <= N}) -> T
|
||||
let array_getitem_t = fn1_kw_met(
|
||||
// and (self: [T; N], _: Range(Int)) -> [T; _]
|
||||
let array_getitem_t = (fn1_kw_met(
|
||||
array_t(T.clone(), N.clone()),
|
||||
anon(input.clone()),
|
||||
T.clone(),
|
||||
)
|
||||
) & fn1_kw_met(
|
||||
array_t(T.clone(), N.clone()),
|
||||
anon(poly(RANGE, vec![ty_tp(Int)])),
|
||||
unknown_len_array_t(T.clone()),
|
||||
))
|
||||
.quantify();
|
||||
let get_item = ValueObj::Subr(ConstSubr::Builtin(BuiltinConstSubr::new(
|
||||
FUNDAMENTAL_GETITEM,
|
||||
|
@ -1330,6 +1335,12 @@ impl Context {
|
|||
array_t(T.clone(), TyParam::erased(Nat)),
|
||||
);
|
||||
array_.register_py_builtin(FUNC_DEDUP, t.quantify(), Some(FUNC_DEDUP), 28);
|
||||
/* Slice */
|
||||
let mut slice = Self::builtin_mono_class(SLICE, 3);
|
||||
slice.register_superclass(Obj, &obj);
|
||||
slice.register_builtin_erg_impl(KW_START, Int, Immutable, Visibility::BUILTIN_PUBLIC);
|
||||
slice.register_builtin_erg_impl(KW_STOP, Int, Immutable, Visibility::BUILTIN_PUBLIC);
|
||||
slice.register_builtin_erg_impl(KW_STEP, Int, Immutable, Visibility::BUILTIN_PUBLIC);
|
||||
/* GenericSet */
|
||||
let mut generic_set = Self::builtin_mono_class(GENERIC_SET, 1);
|
||||
generic_set.register_superclass(Obj, &obj);
|
||||
|
@ -1534,6 +1545,29 @@ impl Context {
|
|||
Str,
|
||||
);
|
||||
bytes.register_py_builtin(FUNC_DECODE, decode_t, Some(FUNC_DECODE), 6);
|
||||
let bytes_getitem_t = fn1_kw_met(mono(BYTES), kw(KW_IDX, Nat), Int)
|
||||
& fn1_kw_met(
|
||||
mono(BYTES),
|
||||
kw(KW_IDX, poly(RANGE, vec![ty_tp(Int)])),
|
||||
mono(BYTES),
|
||||
);
|
||||
bytes.register_builtin_erg_impl(
|
||||
FUNDAMENTAL_GETITEM,
|
||||
bytes_getitem_t,
|
||||
Immutable,
|
||||
Visibility::BUILTIN_PUBLIC,
|
||||
);
|
||||
bytes
|
||||
.register_marker_trait(self, poly(INDEXABLE, vec![ty_tp(Nat), ty_tp(Int)]))
|
||||
.unwrap();
|
||||
let mut bytes_eq = Self::builtin_methods(Some(mono(EQ)), 2);
|
||||
bytes_eq.register_builtin_erg_impl(
|
||||
OP_EQ,
|
||||
fn1_met(mono(BYTES), mono(BYTES), Bool),
|
||||
Const,
|
||||
Visibility::BUILTIN_PUBLIC,
|
||||
);
|
||||
bytes.register_trait(mono(BYTES), bytes_eq);
|
||||
/* GenericTuple */
|
||||
let mut generic_tuple = Self::builtin_mono_class(GENERIC_TUPLE, 1);
|
||||
generic_tuple.register_superclass(Obj, &obj);
|
||||
|
@ -2370,6 +2404,7 @@ impl Context {
|
|||
Some(ARRAY),
|
||||
);
|
||||
self.register_builtin_type(arr_t, array_, vis.clone(), Const, Some(ARRAY));
|
||||
self.register_builtin_type(mono(SLICE), slice, vis.clone(), Const, Some(FUNC_SLICE));
|
||||
self.register_builtin_type(
|
||||
mono(GENERIC_SET),
|
||||
generic_set,
|
||||
|
|
|
@ -242,7 +242,7 @@ pub(crate) fn structural_func(mut args: ValueArgs, ctx: &Context) -> EvalValueRe
|
|||
pub(crate) fn __array_getitem__(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
|
||||
let slf = ctx
|
||||
.convert_value_into_array(args.remove_left_or_key("Self").unwrap())
|
||||
.unwrap();
|
||||
.unwrap_or_else(|err| panic!("{err}, {args}"));
|
||||
let index = enum_unwrap!(args.remove_left_or_key("Index").unwrap(), ValueObj::Nat);
|
||||
if let Some(v) = slf.get(index as usize) {
|
||||
Ok(v.clone())
|
||||
|
|
|
@ -220,6 +220,12 @@ impl Context {
|
|||
)
|
||||
.quantify();
|
||||
let t_round = nd_func(vec![kw(KW_NUMBER, Float)], None, Int);
|
||||
let t_slice = func(
|
||||
vec![kw(KW_START, Int)],
|
||||
None,
|
||||
vec![kw(KW_STOP, Int), kw(KW_STEP, Int)],
|
||||
mono(SLICE),
|
||||
);
|
||||
let t_sorted = nd_func(
|
||||
vec![kw(KW_ITERABLE, poly(ITERABLE, vec![ty_tp(T.clone())]))],
|
||||
None,
|
||||
|
@ -393,6 +399,13 @@ impl Context {
|
|||
vis.clone(),
|
||||
Some(FUNC_ROUND),
|
||||
);
|
||||
self.register_builtin_py_impl(
|
||||
FUNC_SLICE,
|
||||
t_slice,
|
||||
Immutable,
|
||||
vis.clone(),
|
||||
Some(FUNC_SLICE),
|
||||
);
|
||||
self.register_builtin_py_impl(
|
||||
FUNC_SORTED,
|
||||
t_sorted,
|
||||
|
|
|
@ -33,7 +33,10 @@ use crate::module::SharedCompilerResource;
|
|||
use crate::ty::constructors::*;
|
||||
use crate::ty::free::Constraint;
|
||||
use crate::ty::value::ValueObj;
|
||||
use crate::ty::{BuiltinConstSubr, ConstSubr, ParamTy, Predicate, TyParam, Type, Visibility};
|
||||
use crate::ty::{
|
||||
BuiltinConstSubr, ClosureData, ConstSubr, GenConstSubr, ParamTy, Predicate, TyParam, Type,
|
||||
Visibility,
|
||||
};
|
||||
use crate::varinfo::{AbsLocation, Mutability, VarInfo, VarKind};
|
||||
use Mutability::*;
|
||||
use ParamSpec as PS;
|
||||
|
@ -281,6 +284,7 @@ const NAMED_FUNC: &str = "NamedFunc";
|
|||
const FUNC: &str = "Func";
|
||||
const QUANTIFIED: &str = "Quantified";
|
||||
const QUANTIFIED_FUNC: &str = "QuantifiedFunc";
|
||||
const SLICE: &str = "Slice";
|
||||
const FUNC_OBJECT: &str = "object";
|
||||
const FUNC_INT: &str = "int";
|
||||
const FUNC_INT__: &str = "int__";
|
||||
|
@ -332,6 +336,7 @@ const FUNC_POW: &str = "pow";
|
|||
const FUNC_QUIT: &str = "quit";
|
||||
const FUNC_REPR: &str = "repr";
|
||||
const FUNC_ROUND: &str = "round";
|
||||
const FUNC_SLICE: &str = "slice";
|
||||
const FUNC_SORTED: &str = "sorted";
|
||||
const FUNC_SUM: &str = "sum";
|
||||
const FUNC_IF: &str = "if";
|
||||
|
@ -780,23 +785,53 @@ impl Context {
|
|||
muty: Mutability,
|
||||
py_name: Option<&'static str>,
|
||||
) {
|
||||
// FIXME: panic
|
||||
if let Some((_, root_ctx)) = self.poly_types.get_mut(&t.local_name()) {
|
||||
root_ctx.methods_list.push((ClassDefType::Simple(t), ctx));
|
||||
} else {
|
||||
let val = match ctx.kind {
|
||||
let ret_val = match ctx.kind {
|
||||
ContextKind::Class => ValueObj::builtin_class(t.clone()),
|
||||
ContextKind::Trait => ValueObj::builtin_trait(t.clone()),
|
||||
_ => ValueObj::builtin_type(t.clone()),
|
||||
};
|
||||
let qual_name = t.qual_name();
|
||||
let name = VarName::from_str(t.local_name());
|
||||
// e.g Array!: |T, N|(_: {T}, _: {N}) -> {Array!(T, N)}
|
||||
let params = t
|
||||
.typarams()
|
||||
// e.g Array!: |T, N|(_: {T}, _:= {N}) -> {Array!(T, N)}
|
||||
let nd_params = ctx
|
||||
.params_spec
|
||||
.iter()
|
||||
.filter_map(|ps| (!ps.has_default()).then_some(ParamTy::from(ps)))
|
||||
.collect::<Vec<_>>();
|
||||
let d_params = ctx
|
||||
.params_spec
|
||||
.iter()
|
||||
.filter_map(|ps| ps.has_default().then_some(ParamTy::from(ps)))
|
||||
.collect::<Vec<_>>();
|
||||
let meta_t = func(
|
||||
nd_params.clone(),
|
||||
None,
|
||||
d_params.clone(),
|
||||
v_enum(set! { ret_val }),
|
||||
)
|
||||
.quantify();
|
||||
let subr = move |data: ClosureData, args, _ctx: &Context| {
|
||||
let passed = Vec::<TyParam>::from(args);
|
||||
let lack = data.nd_params.len() + data.d_params.len() - passed.len();
|
||||
let erased = data
|
||||
.d_params
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|tp| ParamTy::Pos(tp_enum(self.get_tp_t(&tp).unwrap_or(Obj), set! { tp })))
|
||||
.collect();
|
||||
let meta_t = func(params, None, vec![], v_enum(set! { val.clone() })).quantify();
|
||||
.take(lack)
|
||||
.map(|pt| TyParam::erased(pt.typ().clone()));
|
||||
let params = passed.into_iter().chain(erased).collect::<Vec<_>>();
|
||||
Ok(ValueObj::builtin_type(poly(data.qual_name, params)))
|
||||
};
|
||||
let subr = ConstSubr::Gen(GenConstSubr::new(
|
||||
t.local_name(),
|
||||
ClosureData::new(nd_params, d_params, qual_name),
|
||||
subr,
|
||||
meta_t.clone(),
|
||||
Some(t.clone()),
|
||||
));
|
||||
if ERG_MODE {
|
||||
self.locals.insert(
|
||||
name.clone(),
|
||||
|
@ -812,7 +847,7 @@ impl Context {
|
|||
),
|
||||
);
|
||||
}
|
||||
self.consts.insert(name.clone(), val);
|
||||
self.consts.insert(name.clone(), ValueObj::Subr(subr));
|
||||
self.register_methods(&t, &ctx);
|
||||
self.poly_types.insert(name, (t, ctx));
|
||||
}
|
||||
|
|
|
@ -29,8 +29,8 @@ use Type::*;
|
|||
use crate::context::instantiate_spec::ConstTemplate;
|
||||
use crate::context::{Context, RegistrationMode, TraitImpl, TyVarCache, Variance};
|
||||
use crate::error::{
|
||||
binop_to_dname, readable_name, unaryop_to_dname, SingleTyCheckResult, TyCheckError,
|
||||
TyCheckErrors, TyCheckResult,
|
||||
binop_to_dname, ordinal_num, readable_name, unaryop_to_dname, SingleTyCheckResult,
|
||||
TyCheckError, TyCheckErrors, TyCheckResult,
|
||||
};
|
||||
use crate::varinfo::{AbsLocation, Mutability, VarInfo, VarKind};
|
||||
use crate::{feature_error, hir};
|
||||
|
@ -831,10 +831,10 @@ impl Context {
|
|||
if let Some(attr_name) = attr_name.as_ref() {
|
||||
let mut vi =
|
||||
self.search_method_info(obj, attr_name, pos_args, kw_args, input, namespace)?;
|
||||
vi.t = self.resolve_overload(vi.t, pos_args, kw_args, attr_name)?;
|
||||
vi.t = self.resolve_overload(obj, vi.t, pos_args, kw_args, attr_name)?;
|
||||
Ok(vi)
|
||||
} else {
|
||||
let t = self.resolve_overload(obj.t(), pos_args, kw_args, obj)?;
|
||||
let t = self.resolve_overload(obj, obj.t(), pos_args, kw_args, obj)?;
|
||||
Ok(VarInfo {
|
||||
t,
|
||||
..VarInfo::default()
|
||||
|
@ -844,6 +844,7 @@ impl Context {
|
|||
|
||||
fn resolve_overload(
|
||||
&self,
|
||||
obj: &hir::Expr,
|
||||
instance: Type,
|
||||
pos_args: &[hir::PosArg],
|
||||
kw_args: &[hir::KwArg],
|
||||
|
@ -853,7 +854,7 @@ impl Context {
|
|||
if intersecs.len() == 1 {
|
||||
Ok(instance)
|
||||
} else {
|
||||
let input_t = subr_t(
|
||||
let mut input_t = subr_t(
|
||||
SubrKind::Proc,
|
||||
pos_args
|
||||
.iter()
|
||||
|
@ -867,6 +868,18 @@ impl Context {
|
|||
Obj,
|
||||
);
|
||||
for ty in intersecs.iter() {
|
||||
match (ty.is_method(), input_t.is_method()) {
|
||||
(true, false) => {
|
||||
let Type::Subr(sub) = &mut input_t else { unreachable!() };
|
||||
sub.non_default_params
|
||||
.insert(0, ParamTy::kw(Str::ever("self"), obj.t()));
|
||||
}
|
||||
(false, true) => {
|
||||
let Type::Subr(sub) = &mut input_t else { unreachable!() };
|
||||
sub.non_default_params.remove(0);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if self.subtype_of(ty, &input_t) {
|
||||
return Ok(ty.clone());
|
||||
}
|
||||
|
@ -1486,7 +1499,12 @@ impl Context {
|
|||
let missing_params = subr
|
||||
.non_default_params
|
||||
.iter()
|
||||
.map(|pt| pt.name().cloned().unwrap_or(Str::ever("_")))
|
||||
.enumerate()
|
||||
.map(|(i, pt)| {
|
||||
pt.name().cloned().unwrap_or_else(|| {
|
||||
Str::from(format!("({} param)", ordinal_num(i + 1)))
|
||||
})
|
||||
})
|
||||
.filter(|pt| !passed_params.contains(pt))
|
||||
.collect::<Vec<_>>();
|
||||
if !missing_params.is_empty() {
|
||||
|
@ -1666,6 +1684,8 @@ impl Context {
|
|||
} else {
|
||||
passed_params.insert(name.clone());
|
||||
}
|
||||
} else {
|
||||
passed_params.insert(Str::from(format!("({} param)", ordinal_num(nth))));
|
||||
}
|
||||
self.sub_unify(arg_t, param_t, arg, param.name())
|
||||
.map_err(|errs| {
|
||||
|
|
|
@ -602,7 +602,25 @@ impl Context {
|
|||
)?;
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
Type::And(l, r) => {
|
||||
if let Some(self_t) = l.self_t() {
|
||||
self.sub_unify(
|
||||
callee.ref_t(),
|
||||
self_t,
|
||||
callee,
|
||||
Some(&Str::ever("self")),
|
||||
)?;
|
||||
}
|
||||
if let Some(self_t) = r.self_t() {
|
||||
self.sub_unify(
|
||||
callee.ref_t(),
|
||||
self_t,
|
||||
callee,
|
||||
Some(&Str::ever("self")),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
other => unreachable!("{other}"),
|
||||
}
|
||||
Ok(t)
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ use crate::module::{
|
|||
};
|
||||
use crate::ty::value::ValueObj;
|
||||
use crate::ty::GuardType;
|
||||
use crate::ty::ParamTy;
|
||||
use crate::ty::{Predicate, Type, Visibility, VisibilityModifier};
|
||||
use crate::varinfo::{AbsLocation, Mutability, VarInfo, VarKind};
|
||||
use Type::*;
|
||||
|
@ -233,6 +234,16 @@ impl ParamSpec {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn default<S: Into<Str>>(name: S, t: Type) -> Self {
|
||||
Self::new(
|
||||
Some(name),
|
||||
t,
|
||||
false,
|
||||
DefaultInfo::WithDefault,
|
||||
AbsLocation::unknown(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn t<S: Into<Str>>(name: S, is_var_params: bool, default: DefaultInfo) -> Self {
|
||||
Self::new(
|
||||
Some(name),
|
||||
|
@ -252,6 +263,20 @@ impl ParamSpec {
|
|||
AbsLocation::unknown(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn has_default(&self) -> bool {
|
||||
self.default_info.has_default()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ParamSpec> for ParamTy {
|
||||
fn from(param: &ParamSpec) -> Self {
|
||||
if let Some(name) = ¶m.name {
|
||||
ParamTy::kw(name.clone(), param.t.clone())
|
||||
} else {
|
||||
ParamTy::Pos(param.t.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -434,6 +459,7 @@ pub struct Context {
|
|||
/// => locals: {"x": T, "y": T}
|
||||
/// TODO: impl params desugaring and replace to `Dict`
|
||||
pub(crate) params: Vec<(Option<VarName>, VarInfo)>,
|
||||
pub(crate) params_spec: Vec<ParamSpec>,
|
||||
pub(crate) locals: Dict<VarName, VarInfo>,
|
||||
pub(crate) consts: Dict<VarName, ValueObj>,
|
||||
// {"Nat": ctx, "Int": ctx, ...}
|
||||
|
@ -589,7 +615,7 @@ impl Context {
|
|||
level: usize,
|
||||
) -> Self {
|
||||
let mut params_ = Vec::new();
|
||||
for param in params.into_iter() {
|
||||
for param in params.clone().into_iter() {
|
||||
let id = DefId(get_hash(&(&name, ¶m)));
|
||||
if let Some(name) = param.name {
|
||||
let kind = VarKind::parameter(id, param.is_var_params, param.default_info);
|
||||
|
@ -635,6 +661,7 @@ impl Context {
|
|||
method_to_classes: Dict::default(),
|
||||
method_impl_patches: Dict::default(),
|
||||
params: params_,
|
||||
params_spec: params,
|
||||
decls: Dict::default(),
|
||||
future_defined_locals: Dict::default(),
|
||||
deleted_locals: Dict::default(),
|
||||
|
|
|
@ -5,6 +5,7 @@ use erg_common::traits::{Locational, Runnable, Stream};
|
|||
use erg_common::{enum_unwrap, fn_name, log, set, Str, Triple};
|
||||
|
||||
use erg_parser::ast::{self, AscriptionKind, Identifier, VarName, AST};
|
||||
use erg_parser::desugar::Desugarer;
|
||||
|
||||
use crate::context::instantiate::TyVarCache;
|
||||
use crate::lower::ASTLowerer;
|
||||
|
@ -332,7 +333,7 @@ impl ASTLowerer {
|
|||
fn fake_lower_record(&self, rec: ast::Record) -> LowerResult<hir::Record> {
|
||||
let rec = match rec {
|
||||
ast::Record::Normal(rec) => rec,
|
||||
ast::Record::Mixed(_mixed) => unreachable!(),
|
||||
ast::Record::Mixed(mixed) => Desugarer::desugar_shortened_record_inner(mixed),
|
||||
};
|
||||
let mut elems = Vec::new();
|
||||
for elem in rec.attrs.into_iter() {
|
||||
|
|
|
@ -10,7 +10,7 @@ The name of the operating system dependent module imported. The following names
|
|||
.chdir!: (path: PathLike, ) => NoneType
|
||||
.chmod!: (path: PathLike, mode: Nat) => NoneType
|
||||
.getcwd!: () => Str
|
||||
.getenv!: (key: Str, default: Str or NoneType := NoneType) => Str
|
||||
.getenv!: |D <: Str or NoneType|(key: Str, default: D := NoneType) => Str or D
|
||||
.listdir!: (path := PathLike,) => [Str; _]
|
||||
.mkdir!: (path: PathLike, mode := Nat) => NoneType
|
||||
.putenv!: (key: Str, value: Str) => NoneType
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from _erg_control import then__
|
||||
from _erg_range import Range
|
||||
|
||||
class Array(list):
|
||||
def dedup(self, same_bucket=None):
|
||||
|
@ -24,3 +25,11 @@ class Array(list):
|
|||
|
||||
def __mul__(self, n):
|
||||
return then__(list.__mul__(self, n), Array)
|
||||
|
||||
def __getitem__(self, index_or_slice):
|
||||
if isinstance(index_or_slice, slice):
|
||||
return Array(list.__getitem__(self, index_or_slice))
|
||||
elif isinstance(index_or_slice, Range):
|
||||
return Array(list.__getitem__(self, index_or_slice.into_slice()))
|
||||
else:
|
||||
return list.__getitem__(self, index_or_slice)
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
class Bytes(bytes):
|
||||
def try_new(*b): # -> Result[Nat]
|
||||
return Bytes(bytes(*b))
|
||||
|
||||
def __getitem__(self, index_or_slice):
|
||||
from _erg_range import Range
|
||||
if isinstance(index_or_slice, slice):
|
||||
return Bytes(bytes.__getitem__(self, index_or_slice))
|
||||
elif isinstance(index_or_slice, Range):
|
||||
return Bytes(bytes.__getitem__(self, index_or_slice.into_slice()))
|
||||
else:
|
||||
return bytes.__getitem__(self, index_or_slice)
|
||||
|
|
|
@ -12,6 +12,13 @@ class Range:
|
|||
def __contains__(self, item):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def from_slice(slice):
|
||||
pass
|
||||
|
||||
def into_slice(self):
|
||||
pass
|
||||
|
||||
def __getitem__(self, item):
|
||||
res = self.start + item
|
||||
if res in self:
|
||||
|
@ -56,6 +63,13 @@ class RightOpenRange(Range):
|
|||
def __contains__(self, item):
|
||||
return self.start <= item < self.end
|
||||
|
||||
@staticmethod
|
||||
def from_slice(slice):
|
||||
return Range(slice.start, slice.stop)
|
||||
|
||||
def into_slice(self):
|
||||
return slice(self.start, self.end)
|
||||
|
||||
|
||||
# represents `start<..<end`
|
||||
class OpenRange(Range):
|
||||
|
@ -68,6 +82,13 @@ class ClosedRange(Range):
|
|||
def __contains__(self, item):
|
||||
return self.start <= item <= self.end
|
||||
|
||||
@staticmethod
|
||||
def from_slice(slice):
|
||||
return Range(slice.start, slice.stop - 1)
|
||||
|
||||
def into_slice(self):
|
||||
return slice(self.start, self.end + 1)
|
||||
|
||||
|
||||
class RangeIterator:
|
||||
def __init__(self, rng):
|
||||
|
|
|
@ -2,7 +2,6 @@ from _erg_result import Error
|
|||
from _erg_int import Int
|
||||
from _erg_control import then__
|
||||
|
||||
|
||||
class Str(str):
|
||||
def __instancecheck__(cls, obj):
|
||||
return isinstance(obj, str)
|
||||
|
@ -40,6 +39,15 @@ class Str(str):
|
|||
def __mod__(self, other):
|
||||
return then__(str.__mod__(other, self), Str)
|
||||
|
||||
def __getitem__(self, index_or_slice):
|
||||
from _erg_range import Range
|
||||
if isinstance(index_or_slice, slice):
|
||||
return Str(str.__getitem__(self, index_or_slice))
|
||||
elif isinstance(index_or_slice, Range):
|
||||
return Str(str.__getitem__(self, index_or_slice.into_slice()))
|
||||
else:
|
||||
return str.__getitem__(self, index_or_slice)
|
||||
|
||||
|
||||
class StrMut: # Inherits Str
|
||||
value: Str
|
||||
|
|
|
@ -18,6 +18,7 @@ use erg_common::{fmt_option, fn_name, log, switch_lang, Str};
|
|||
use erg_parser::ast::{self, AscriptionKind, VisModifierSpec};
|
||||
use erg_parser::ast::{OperationKind, TypeSpecWithOp, VarName, AST};
|
||||
use erg_parser::build_ast::ASTBuilder;
|
||||
use erg_parser::desugar::Desugarer;
|
||||
use erg_parser::token::{Token, TokenKind};
|
||||
use erg_parser::Parser;
|
||||
|
||||
|
@ -381,7 +382,9 @@ impl ASTLowerer {
|
|||
log!(info "entered {}({record})", fn_name!());
|
||||
match record {
|
||||
ast::Record::Normal(rec) => self.lower_normal_record(rec),
|
||||
ast::Record::Mixed(_rec) => unreachable!(), // should be desugared
|
||||
ast::Record::Mixed(mixed) => {
|
||||
self.lower_normal_record(Desugarer::desugar_shortened_record_inner(mixed))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ impl SharedCompilerResource {
|
|||
|
||||
pub fn inherit(&self, path: PathBuf) -> Self {
|
||||
let mut _self = self.clone();
|
||||
_self.promises.path = path;
|
||||
_self.promises.path = path.into();
|
||||
_self
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use erg_common::pathutil::NormalizedPathBuf;
|
||||
use erg_common::set;
|
||||
use erg_common::set::Set;
|
||||
use erg_common::shared::{MappedRwLockReadGuard, RwLockReadGuard, Shared};
|
||||
use erg_common::tsort::{tsort, Graph, Node, TopoSortError};
|
||||
use erg_common::{normalize_path, set};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum IncRefError {
|
||||
|
@ -18,7 +19,7 @@ impl IncRefError {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ModuleGraph(Graph<PathBuf, ()>);
|
||||
pub struct ModuleGraph(Graph<NormalizedPathBuf, ()>);
|
||||
|
||||
impl fmt::Display for ModuleGraph {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
|
@ -35,7 +36,7 @@ impl fmt::Display for ModuleGraph {
|
|||
}
|
||||
|
||||
impl IntoIterator for ModuleGraph {
|
||||
type Item = Node<PathBuf, ()>;
|
||||
type Item = Node<NormalizedPathBuf, ()>;
|
||||
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
|
@ -48,20 +49,45 @@ impl ModuleGraph {
|
|||
Self(Graph::new())
|
||||
}
|
||||
|
||||
pub fn get_node(&self, path: &Path) -> Option<&Node<PathBuf, ()>> {
|
||||
let path = normalize_path(path.to_path_buf());
|
||||
pub fn get_node(&self, path: &Path) -> Option<&Node<NormalizedPathBuf, ()>> {
|
||||
let path = NormalizedPathBuf::new(path.to_path_buf());
|
||||
self.0.iter().find(|n| n.id == path)
|
||||
}
|
||||
|
||||
fn parents(&self, path: &Path) -> Option<&Set<PathBuf>> {
|
||||
let path = normalize_path(path.to_path_buf());
|
||||
/// if `path` depends on `target`, returns `true`, else `false`.
|
||||
/// if `path` not found, returns `false`
|
||||
pub fn depends_on(&self, path: &Path, target: &Path) -> bool {
|
||||
let path = NormalizedPathBuf::new(path.to_path_buf());
|
||||
let target = NormalizedPathBuf::new(target.to_path_buf());
|
||||
self.0
|
||||
.iter()
|
||||
.find(|n| n.id == path)
|
||||
.map(|n| n.depends_on.contains(&target))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn children(&self, path: &Path) -> Set<NormalizedPathBuf> {
|
||||
let path = NormalizedPathBuf::new(path.to_path_buf());
|
||||
self.0
|
||||
.iter()
|
||||
.filter(|n| n.depends_on.contains(&path))
|
||||
.map(|n| n.id.clone())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn parents(&self, path: &Path) -> Option<&Set<NormalizedPathBuf>> {
|
||||
let path = NormalizedPathBuf::new(path.to_path_buf());
|
||||
self.0.iter().find(|n| n.id == path).map(|n| &n.depends_on)
|
||||
}
|
||||
|
||||
pub fn ancestors(&self, path: &Path) -> Set<PathBuf> {
|
||||
let path = normalize_path(path.to_path_buf());
|
||||
/// ```erg
|
||||
/// # a.er
|
||||
/// b = import "b"
|
||||
/// ```
|
||||
/// -> a: child, b: parent
|
||||
pub fn ancestors(&self, path: &Path) -> Set<NormalizedPathBuf> {
|
||||
let mut ancestors = set! {};
|
||||
if let Some(parents) = self.parents(&path) {
|
||||
if let Some(parents) = self.parents(path) {
|
||||
for parent in parents.iter() {
|
||||
ancestors.insert(parent.clone());
|
||||
ancestors.extend(self.ancestors(parent));
|
||||
|
@ -71,7 +97,7 @@ impl ModuleGraph {
|
|||
}
|
||||
|
||||
pub fn add_node_if_none(&mut self, path: &Path) {
|
||||
let path = normalize_path(path.to_path_buf());
|
||||
let path = NormalizedPathBuf::new(path.to_path_buf());
|
||||
if self.0.iter().all(|n| n.id != path) {
|
||||
let node = Node::new(path, (), set! {});
|
||||
self.0.push(node);
|
||||
|
@ -80,8 +106,8 @@ impl ModuleGraph {
|
|||
|
||||
/// returns Err (and do nothing) if this operation makes a cycle
|
||||
pub fn inc_ref(&mut self, referrer: &Path, depends_on: PathBuf) -> Result<(), IncRefError> {
|
||||
let referrer = normalize_path(referrer.to_path_buf());
|
||||
let depends_on = normalize_path(depends_on);
|
||||
let referrer = NormalizedPathBuf::new(referrer.to_path_buf());
|
||||
let depends_on = NormalizedPathBuf::new(depends_on);
|
||||
if self.ancestors(&depends_on).contains(&referrer) && referrer != depends_on {
|
||||
return Err(IncRefError::CycleDetected);
|
||||
}
|
||||
|
@ -96,7 +122,7 @@ impl ModuleGraph {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = &Node<PathBuf, ()>> {
|
||||
pub fn iter(&self) -> impl Iterator<Item = &Node<NormalizedPathBuf, ()>> {
|
||||
self.0.iter()
|
||||
}
|
||||
|
||||
|
@ -112,13 +138,13 @@ impl ModuleGraph {
|
|||
}
|
||||
|
||||
pub fn remove(&mut self, path: &Path) {
|
||||
let path = normalize_path(path.to_path_buf());
|
||||
let path = NormalizedPathBuf::new(path.to_path_buf());
|
||||
self.0.retain(|n| n.id != path);
|
||||
}
|
||||
|
||||
pub fn rename_path(&mut self, old: &Path, new: PathBuf) {
|
||||
let old = normalize_path(old.to_path_buf());
|
||||
let new = normalize_path(new);
|
||||
let old = NormalizedPathBuf::new(old.to_path_buf());
|
||||
let new = NormalizedPathBuf::new(new);
|
||||
for node in self.0.iter_mut() {
|
||||
if node.id == old {
|
||||
node.id = new.clone();
|
||||
|
@ -145,7 +171,7 @@ impl fmt::Display for SharedModuleGraph {
|
|||
}
|
||||
|
||||
impl IntoIterator for SharedModuleGraph {
|
||||
type Item = Node<PathBuf, ()>;
|
||||
type Item = Node<NormalizedPathBuf, ()>;
|
||||
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
|
@ -158,7 +184,10 @@ impl SharedModuleGraph {
|
|||
Self(Shared::new(ModuleGraph::new()))
|
||||
}
|
||||
|
||||
pub fn get_node(&self, path: &Path) -> Option<MappedRwLockReadGuard<Node<PathBuf, ()>>> {
|
||||
pub fn get_node(
|
||||
&self,
|
||||
path: &Path,
|
||||
) -> Option<MappedRwLockReadGuard<Node<NormalizedPathBuf, ()>>> {
|
||||
if self.0.borrow().get_node(path).is_some() {
|
||||
Some(RwLockReadGuard::map(self.0.borrow(), |graph| {
|
||||
graph.get_node(path).unwrap()
|
||||
|
@ -168,7 +197,15 @@ impl SharedModuleGraph {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn ancestors(&self, path: &Path) -> Set<PathBuf> {
|
||||
pub fn depends_on(&self, path: &Path, target: &Path) -> bool {
|
||||
self.0.borrow().depends_on(path, target)
|
||||
}
|
||||
|
||||
pub fn children(&self, path: &Path) -> Set<NormalizedPathBuf> {
|
||||
self.0.borrow().children(path)
|
||||
}
|
||||
|
||||
pub fn ancestors(&self, path: &Path) -> Set<NormalizedPathBuf> {
|
||||
self.0.borrow().ancestors(path)
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ impl Promise {
|
|||
#[derive(Debug, Clone, Default)]
|
||||
pub struct SharedPromises {
|
||||
graph: SharedModuleGraph,
|
||||
pub(crate) path: PathBuf,
|
||||
pub(crate) path: NormalizedPathBuf,
|
||||
promises: Shared<Dict<NormalizedPathBuf, Promise>>,
|
||||
}
|
||||
|
||||
|
@ -86,7 +86,7 @@ impl SharedPromises {
|
|||
pub fn new(graph: SharedModuleGraph, path: PathBuf) -> Self {
|
||||
Self {
|
||||
graph,
|
||||
path,
|
||||
path: NormalizedPathBuf::new(path),
|
||||
promises: Shared::new(Dict::new()),
|
||||
}
|
||||
}
|
||||
|
@ -125,6 +125,26 @@ impl SharedPromises {
|
|||
Promise::Running { parent, handle };
|
||||
return Ok(());
|
||||
}
|
||||
// Suppose A depends on B and C, and B depends on C.
|
||||
// In this case, B must join C before A joins C. Otherwise, a deadlock will occur.
|
||||
let children = self.graph.children(path);
|
||||
for child in children.iter() {
|
||||
if child == &self.path {
|
||||
continue;
|
||||
} else if self.graph.depends_on(&self.path, child) {
|
||||
*self.promises.borrow_mut().get_mut(path).unwrap() =
|
||||
Promise::Running { parent, handle };
|
||||
while self
|
||||
.promises
|
||||
.borrow()
|
||||
.get(path)
|
||||
.is_some_and(|p| !p.is_finished())
|
||||
{
|
||||
std::thread::yield_now();
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
let res = handle.join();
|
||||
*self.promises.borrow_mut().get_mut(path).unwrap() = Promise::Finished;
|
||||
res
|
||||
|
|
|
@ -8,7 +8,7 @@ use erg_common::impl_display_from_debug;
|
|||
#[allow(unused_imports)]
|
||||
use erg_common::log;
|
||||
use erg_common::opcode::CommonOpcode;
|
||||
use erg_common::opcode308::Opcode308;
|
||||
use erg_common::opcode309::Opcode309;
|
||||
use erg_common::opcode310::Opcode310;
|
||||
use erg_common::opcode311::{BinOpCode, Opcode311};
|
||||
use erg_common::python_util::{env_magic_number, PythonVersion};
|
||||
|
@ -36,20 +36,20 @@ pub fn consts_into_bytes(consts: Vec<ValueObj>, python_ver: PythonVersion) -> Ve
|
|||
|
||||
pub fn jump_abs_addr(minor_ver: u8, op: u8, idx: usize, arg: usize) -> usize {
|
||||
match minor_ver {
|
||||
7 | 8 => jump_abs_addr_308(Opcode308::from(op), idx, arg),
|
||||
9 | 10 => jump_abs_addr_310(Opcode310::from(op), idx, arg),
|
||||
7 | 8 | 9 => jump_abs_addr_309(Opcode309::from(op), idx, arg),
|
||||
10 => jump_abs_addr_310(Opcode310::from(op), idx, arg),
|
||||
11 => jump_abs_addr_311(Opcode311::from(op), idx, arg),
|
||||
n => todo!("unsupported version: {n}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn jump_abs_addr_308(op: Opcode308, idx: usize, arg: usize) -> usize {
|
||||
fn jump_abs_addr_309(op: Opcode309, idx: usize, arg: usize) -> usize {
|
||||
match op {
|
||||
Opcode308::FOR_ITER | Opcode308::JUMP_FORWARD => idx + arg * 2 + 2,
|
||||
Opcode308::JUMP_ABSOLUTE | Opcode308::POP_JUMP_IF_FALSE | Opcode308::POP_JUMP_IF_TRUE => {
|
||||
arg * 2
|
||||
Opcode309::FOR_ITER | Opcode309::JUMP_FORWARD => idx + arg + 2,
|
||||
Opcode309::JUMP_ABSOLUTE | Opcode309::POP_JUMP_IF_FALSE | Opcode309::POP_JUMP_IF_TRUE => {
|
||||
arg
|
||||
}
|
||||
Opcode308::SETUP_WITH => idx + arg * 2 + 2,
|
||||
Opcode309::SETUP_WITH => idx + arg + 2,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
@ -541,10 +541,8 @@ impl CodeObj {
|
|||
arg
|
||||
};
|
||||
match py_ver.and_then(|pv| pv.minor) {
|
||||
Some(7) => self.read_instr_307(op, arg, idx, &mut instrs),
|
||||
Some(8) => self.read_instr_308(op, arg, idx, &mut instrs),
|
||||
// Some(9) => self.read_instr_309(op, arg, idx, &mut instrs),
|
||||
Some(9 | 10) => self.read_instr_310(op, arg, idx, &mut instrs),
|
||||
Some(7 | 8 | 9) => self.read_instr_309(op, arg, idx, &mut instrs),
|
||||
Some(10) => self.read_instr_310(op, arg, idx, &mut instrs),
|
||||
Some(11) => self.read_instr_311(op, arg, idx, &mut instrs),
|
||||
_ => {}
|
||||
}
|
||||
|
@ -557,78 +555,34 @@ impl CodeObj {
|
|||
instrs
|
||||
}
|
||||
|
||||
fn read_instr_307(&self, op: &u8, arg: usize, idx: usize, instrs: &mut String) {
|
||||
let op307 = Opcode308::from(*op);
|
||||
let s_op = op307.to_string();
|
||||
fn read_instr_309(&self, op: &u8, arg: usize, idx: usize, instrs: &mut String) {
|
||||
let op309 = Opcode309::from(*op);
|
||||
let s_op = op309.to_string();
|
||||
write!(instrs, "{idx:>15} {s_op:<25}").unwrap();
|
||||
if let Ok(op) = CommonOpcode::try_from(*op) {
|
||||
self.dump_additional_info(op, arg, idx, instrs);
|
||||
}
|
||||
match op307 {
|
||||
Opcode308::STORE_DEREF | Opcode308::LOAD_DEREF => {
|
||||
match op309 {
|
||||
Opcode309::STORE_DEREF | Opcode309::LOAD_DEREF => {
|
||||
write!(instrs, "{arg} ({})", self.freevars.get(arg).unwrap()).unwrap();
|
||||
}
|
||||
Opcode308::LOAD_CLOSURE => {
|
||||
Opcode309::LOAD_CLOSURE => {
|
||||
write!(instrs, "{arg} ({})", self.cellvars.get(arg).unwrap()).unwrap();
|
||||
}
|
||||
Opcode308::JUMP_ABSOLUTE => {
|
||||
Opcode309::JUMP_ABSOLUTE => {
|
||||
write!(instrs, "{arg} (to {})", arg).unwrap();
|
||||
}
|
||||
Opcode308::JUMP_FORWARD => {
|
||||
Opcode309::JUMP_FORWARD => {
|
||||
write!(instrs, "{arg} (to {})", idx + arg + 2).unwrap();
|
||||
}
|
||||
Opcode308::POP_JUMP_IF_FALSE | Opcode308::POP_JUMP_IF_TRUE => {
|
||||
// REVIEW: *2?
|
||||
Opcode309::POP_JUMP_IF_FALSE | Opcode309::POP_JUMP_IF_TRUE => {
|
||||
write!(instrs, "{arg} (to {})", arg).unwrap();
|
||||
}
|
||||
Opcode308::CALL_FUNCTION
|
||||
| Opcode308::CALL_FUNCTION_EX
|
||||
| Opcode308::CALL_FUNCTION_KW => {
|
||||
write!(instrs, "{arg}").unwrap();
|
||||
}
|
||||
Opcode308::BINARY_ADD
|
||||
| Opcode308::BINARY_SUBTRACT
|
||||
| Opcode308::BINARY_MULTIPLY
|
||||
| Opcode308::BINARY_TRUE_DIVIDE => {
|
||||
write!(instrs, "{arg} ({:?})", TypePair::from(arg as u8)).unwrap();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
instrs.push('\n');
|
||||
}
|
||||
|
||||
fn read_instr_308(&self, op: &u8, arg: usize, idx: usize, instrs: &mut String) {
|
||||
let op308 = Opcode308::from(*op);
|
||||
let s_op = op308.to_string();
|
||||
write!(instrs, "{idx:>15} {s_op:<25}").unwrap();
|
||||
if let Ok(op) = CommonOpcode::try_from(*op) {
|
||||
self.dump_additional_info(op, arg, idx, instrs);
|
||||
}
|
||||
match op308 {
|
||||
Opcode308::STORE_DEREF | Opcode308::LOAD_DEREF => {
|
||||
write!(instrs, "{arg} ({})", self.freevars.get(arg).unwrap()).unwrap();
|
||||
}
|
||||
Opcode308::LOAD_CLOSURE => {
|
||||
write!(instrs, "{arg} ({})", self.cellvars.get(arg).unwrap()).unwrap();
|
||||
}
|
||||
Opcode308::JUMP_ABSOLUTE => {
|
||||
write!(instrs, "{arg} (to {})", arg * 2).unwrap();
|
||||
}
|
||||
Opcode308::JUMP_FORWARD => {
|
||||
write!(instrs, "{arg} (to {})", idx + arg * 2 + 2).unwrap();
|
||||
}
|
||||
// REVIEW: *2?
|
||||
Opcode308::POP_JUMP_IF_FALSE | Opcode308::POP_JUMP_IF_TRUE => {
|
||||
write!(instrs, "{arg} (to {})", arg * 2).unwrap();
|
||||
}
|
||||
Opcode308::CALL_FUNCTION
|
||||
| Opcode308::CALL_FUNCTION_EX
|
||||
| Opcode308::CALL_FUNCTION_KW => {
|
||||
write!(instrs, "{arg}").unwrap();
|
||||
}
|
||||
Opcode308::BINARY_ADD
|
||||
| Opcode308::BINARY_SUBTRACT
|
||||
| Opcode308::BINARY_MULTIPLY
|
||||
| Opcode308::BINARY_TRUE_DIVIDE => {
|
||||
Opcode309::BINARY_ADD
|
||||
| Opcode309::BINARY_SUBTRACT
|
||||
| Opcode309::BINARY_MULTIPLY
|
||||
| Opcode309::BINARY_TRUE_DIVIDE => {
|
||||
write!(instrs, "{arg} ({:?})", TypePair::from(arg as u8)).unwrap();
|
||||
}
|
||||
_ => {}
|
||||
|
|
|
@ -5,20 +5,20 @@ use erg_common::dict::Dict;
|
|||
use erg_common::log;
|
||||
use erg_common::Str;
|
||||
|
||||
use erg_parser::ast::{ConstBlock, Params};
|
||||
use erg_parser::ast::{Block, ConstBlock, Params};
|
||||
|
||||
use super::constructors::subr_t;
|
||||
use super::value::{EvalValueResult, ValueObj};
|
||||
use super::{Predicate, Type};
|
||||
use super::{ParamTy, Predicate, TyParam, Type};
|
||||
|
||||
use crate::context::Context;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct UserConstSubr {
|
||||
name: Str,
|
||||
params: Params,
|
||||
block: ConstBlock,
|
||||
sig_t: Type,
|
||||
pub name: Str,
|
||||
pub(crate) params: Params,
|
||||
pub(crate) block: ConstBlock,
|
||||
pub(crate) sig_t: Type,
|
||||
}
|
||||
|
||||
impl UserConstSubr {
|
||||
|
@ -30,6 +30,10 @@ impl UserConstSubr {
|
|||
sig_t,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn block(self) -> Block {
|
||||
self.block.downgrade()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -38,6 +42,26 @@ pub struct ValueArgs {
|
|||
pub kw_args: Dict<Str, ValueObj>,
|
||||
}
|
||||
|
||||
impl fmt::Display for ValueArgs {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut args = Vec::new();
|
||||
for arg in &self.pos_args {
|
||||
args.push(arg.to_string());
|
||||
}
|
||||
for (key, arg) in self.kw_args.iter() {
|
||||
args.push(format!("{key} := {arg}"));
|
||||
}
|
||||
write!(f, "({})", args.join(", "))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ValueArgs> for Vec<TyParam> {
|
||||
fn from(args: ValueArgs) -> Self {
|
||||
// TODO: kw_args
|
||||
args.pos_args.into_iter().map(TyParam::Value).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueArgs {
|
||||
pub const fn new(pos_args: Vec<ValueObj>, kw_args: Dict<Str, ValueObj>) -> Self {
|
||||
ValueArgs { pos_args, kw_args }
|
||||
|
@ -54,7 +78,7 @@ impl ValueArgs {
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct BuiltinConstSubr {
|
||||
name: &'static str,
|
||||
name: Str,
|
||||
subr: fn(ValueArgs, &Context) -> EvalValueResult<ValueObj>,
|
||||
sig_t: Type,
|
||||
as_type: Option<Type>,
|
||||
|
@ -91,14 +115,14 @@ impl fmt::Display for BuiltinConstSubr {
|
|||
}
|
||||
|
||||
impl BuiltinConstSubr {
|
||||
pub const fn new(
|
||||
name: &'static str,
|
||||
pub fn new<S: Into<Str>>(
|
||||
name: S,
|
||||
subr: fn(ValueArgs, &Context) -> EvalValueResult<ValueObj>,
|
||||
sig_t: Type,
|
||||
as_type: Option<Type>,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
name: name.into(),
|
||||
subr,
|
||||
sig_t,
|
||||
as_type,
|
||||
|
@ -110,10 +134,90 @@ impl BuiltinConstSubr {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ClosureData {
|
||||
pub(crate) nd_params: Vec<ParamTy>,
|
||||
pub(crate) d_params: Vec<ParamTy>,
|
||||
pub(crate) qual_name: Str,
|
||||
}
|
||||
|
||||
impl ClosureData {
|
||||
pub const fn new(nd_params: Vec<ParamTy>, d_params: Vec<ParamTy>, qual_name: Str) -> Self {
|
||||
Self {
|
||||
nd_params,
|
||||
d_params,
|
||||
qual_name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
#[derive(Clone)]
|
||||
pub struct GenConstSubr {
|
||||
name: Str,
|
||||
data: ClosureData,
|
||||
subr: fn(ClosureData, ValueArgs, &Context) -> EvalValueResult<ValueObj>,
|
||||
sig_t: Type,
|
||||
as_type: Option<Type>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for GenConstSubr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("BuiltinConstSubr")
|
||||
.field("name", &self.name)
|
||||
.field("sig_t", &self.sig_t)
|
||||
.field("as_type", &self.as_type)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for GenConstSubr {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.name == other.name
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for GenConstSubr {}
|
||||
|
||||
impl std::hash::Hash for GenConstSubr {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.name.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for GenConstSubr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "<const subroutine '{}'>", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl GenConstSubr {
|
||||
pub fn new<S: Into<Str>>(
|
||||
name: S,
|
||||
data: ClosureData,
|
||||
subr: fn(ClosureData, ValueArgs, &Context) -> EvalValueResult<ValueObj>,
|
||||
sig_t: Type,
|
||||
as_type: Option<Type>,
|
||||
) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
data,
|
||||
subr,
|
||||
sig_t,
|
||||
as_type,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call(&self, args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
|
||||
(self.subr)(self.data.clone(), args, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum ConstSubr {
|
||||
User(UserConstSubr),
|
||||
Builtin(BuiltinConstSubr),
|
||||
Gen(GenConstSubr),
|
||||
}
|
||||
|
||||
impl fmt::Display for ConstSubr {
|
||||
|
@ -123,6 +227,7 @@ impl fmt::Display for ConstSubr {
|
|||
write!(f, "<user-defined const subroutine '{}'>", subr.name)
|
||||
}
|
||||
ConstSubr::Builtin(subr) => write!(f, "{subr}"),
|
||||
ConstSubr::Gen(subr) => write!(f, "{subr}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -132,10 +237,10 @@ impl ConstSubr {
|
|||
match self {
|
||||
ConstSubr::User(user) => &user.sig_t,
|
||||
ConstSubr::Builtin(builtin) => &builtin.sig_t,
|
||||
ConstSubr::Gen(gen) => &gen.sig_t,
|
||||
}
|
||||
}
|
||||
|
||||
/// ConstSubr{sig_t: Int -> {Int}, ..}.as_type() == Int -> Int
|
||||
pub fn as_type(&self, ctx: &Context) -> Option<Type> {
|
||||
match self {
|
||||
ConstSubr::User(user) => {
|
||||
|
@ -170,6 +275,7 @@ impl ConstSubr {
|
|||
None
|
||||
}
|
||||
ConstSubr::Builtin(builtin) => builtin.as_type.clone(),
|
||||
ConstSubr::Gen(gen) => gen.as_type.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ use self::constructors::subr_t;
|
|||
|
||||
pub const STR_OMIT_THRESHOLD: usize = 16;
|
||||
pub const CONTAINER_OMIT_THRESHOLD: usize = 8;
|
||||
pub const DEFAULT_PARAMS_THRESHOLD: usize = 5;
|
||||
|
||||
/// cloneのコストがあるためなるべく.ref_tを使うようにすること
|
||||
/// いくつかの構造体は直接Typeを保持していないので、その場合は.tを使う
|
||||
|
@ -315,6 +316,10 @@ impl LimitedDisplay for SubrType {
|
|||
var_params.typ().limited_fmt(f, limit - 1)?;
|
||||
}
|
||||
for (i, pt) in self.default_params.iter().enumerate() {
|
||||
if limit.is_positive() && i >= DEFAULT_PARAMS_THRESHOLD {
|
||||
write!(f, ", ...")?;
|
||||
break;
|
||||
}
|
||||
if i > 0 || !self.non_default_params.is_empty() || self.var_params.is_some() {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
|
@ -1712,6 +1717,7 @@ impl Type {
|
|||
Self::Quantified(t) => t.is_procedure(),
|
||||
Self::Subr(subr) if subr.kind == SubrKind::Proc => true,
|
||||
Self::Refinement(refine) => refine.t.is_procedure(),
|
||||
Self::And(lhs, rhs) => lhs.is_procedure() && rhs.is_procedure(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -1814,6 +1820,7 @@ impl Type {
|
|||
match self {
|
||||
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_refinement(),
|
||||
Self::Refinement(_) => true,
|
||||
Self::And(l, r) => l.is_refinement() && r.is_refinement(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -1855,6 +1862,7 @@ impl Type {
|
|||
Self::Refinement(refine) => refine.t.is_method(),
|
||||
Self::Subr(subr) => subr.is_method(),
|
||||
Self::Quantified(quant) => quant.is_method(),
|
||||
Self::And(l, r) => l.is_method() && r.is_method(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -2266,6 +2274,11 @@ impl Type {
|
|||
match self {
|
||||
Type::FreeVar(fv) if fv.is_linked() => fv.crack().intersection_types(),
|
||||
Type::Refinement(refine) => refine.t.intersection_types(),
|
||||
Type::Quantified(tys) => tys
|
||||
.intersection_types()
|
||||
.into_iter()
|
||||
.map(|t| t.quantify())
|
||||
.collect(),
|
||||
Type::And(t1, t2) => {
|
||||
let mut types = t1.intersection_types();
|
||||
types.extend(t2.intersection_types());
|
||||
|
|
|
@ -375,22 +375,7 @@ impl Hash for TypeObj {
|
|||
|
||||
impl fmt::Display for TypeObj {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
TypeObj::Builtin { t, .. } => {
|
||||
if cfg!(feature = "debug") {
|
||||
write!(f, "<type {t}>")
|
||||
} else {
|
||||
write!(f, "{t}")
|
||||
}
|
||||
}
|
||||
TypeObj::Generated(t) => {
|
||||
if cfg!(feature = "debug") {
|
||||
write!(f, "<user type {t}>")
|
||||
} else {
|
||||
write!(f, "{t}")
|
||||
}
|
||||
}
|
||||
}
|
||||
self.limited_fmt(f, 10)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -401,7 +386,7 @@ impl LimitedDisplay for TypeObj {
|
|||
if cfg!(feature = "debug") {
|
||||
write!(f, "<type ")?;
|
||||
t.limited_fmt(f, limit - 1)?;
|
||||
write!(f, "{t}>")
|
||||
write!(f, ">")
|
||||
} else {
|
||||
t.limited_fmt(f, limit - 1)
|
||||
}
|
||||
|
@ -410,7 +395,7 @@ impl LimitedDisplay for TypeObj {
|
|||
if cfg!(feature = "debug") {
|
||||
write!(f, "<user type ")?;
|
||||
t.limited_fmt(f, limit - 1)?;
|
||||
write!(f, "{t}>")
|
||||
write!(f, ">")
|
||||
} else {
|
||||
t.limited_fmt(f, limit - 1)
|
||||
}
|
||||
|
|
|
@ -312,6 +312,24 @@ impl VarInfo {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn d_parameter(t: Type, def_loc: AbsLocation, namespace: Str) -> Self {
|
||||
let kind = VarKind::Parameter {
|
||||
def_id: DefId(0),
|
||||
var: false,
|
||||
default: DefaultInfo::WithDefault,
|
||||
};
|
||||
Self::new(
|
||||
t,
|
||||
Immutable,
|
||||
Visibility::private(namespace),
|
||||
kind,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
def_loc,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn instance_attr(field: Field, t: Type, impl_of: Option<Type>, namespace: Str) -> Self {
|
||||
let muty = if field.is_const() {
|
||||
Mutability::Const
|
||||
|
|
|
@ -4159,6 +4159,13 @@ impl Signature {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn t_spec_op_mut(&mut self) -> Option<&mut TypeSpecWithOp> {
|
||||
match self {
|
||||
Self::Var(v) => v.t_spec.as_mut(),
|
||||
Self::Subr(c) => c.return_t_spec.as_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_const(&self) -> bool {
|
||||
match self {
|
||||
Self::Var(var) => var.is_const(),
|
||||
|
|
|
@ -261,16 +261,11 @@ impl Desugarer {
|
|||
for chunk in def.body.block.into_iter() {
|
||||
chunks.push(desugar(chunk));
|
||||
}
|
||||
if let Signature::Subr(mut subr) = def.sig {
|
||||
let mut defaults = vec![];
|
||||
for default in subr.params.defaults.into_iter() {
|
||||
let default_val = desugar(default.default_val);
|
||||
defaults.push(DefaultParamSignature {
|
||||
default_val,
|
||||
..default
|
||||
});
|
||||
if let Some(t_op) = def.sig.t_spec_op_mut() {
|
||||
*t_op.t_spec_as_expr = desugar(*t_op.t_spec_as_expr.clone());
|
||||
}
|
||||
subr.params.defaults = defaults;
|
||||
if let Signature::Subr(mut subr) = def.sig {
|
||||
subr.params = Self::perform_desugar_params(desugar, subr.params);
|
||||
def.sig = Signature::Subr(subr);
|
||||
}
|
||||
let body = DefBody::new(def.body.op, Block::new(chunks), def.body.id);
|
||||
|
@ -304,15 +299,10 @@ impl Desugarer {
|
|||
for chunk in lambda.body.into_iter() {
|
||||
chunks.push(desugar(chunk));
|
||||
}
|
||||
let mut defaults = vec![];
|
||||
for default in lambda.sig.params.defaults.into_iter() {
|
||||
let default_val = desugar(default.default_val);
|
||||
defaults.push(DefaultParamSignature {
|
||||
default_val,
|
||||
..default
|
||||
});
|
||||
if let Some(t_op) = &mut lambda.sig.return_t_spec {
|
||||
*t_op.t_spec_as_expr = desugar(*t_op.t_spec_as_expr.clone());
|
||||
}
|
||||
lambda.sig.params.defaults = defaults;
|
||||
lambda.sig.params = Self::perform_desugar_params(desugar, lambda.sig.params);
|
||||
let body = Block::new(chunks);
|
||||
Expr::Lambda(Lambda::new(lambda.sig, lambda.op, body, lambda.id))
|
||||
}
|
||||
|
@ -370,6 +360,36 @@ impl Desugarer {
|
|||
}
|
||||
}
|
||||
|
||||
fn perform_desugar_params(mut desugar: impl FnMut(Expr) -> Expr, mut params: Params) -> Params {
|
||||
let mut non_defaults = vec![];
|
||||
for mut non_default in params.non_defaults.into_iter() {
|
||||
non_default.t_spec = non_default.t_spec.map(|t_spec| {
|
||||
TypeSpecWithOp::new(t_spec.op, t_spec.t_spec, desugar(*t_spec.t_spec_as_expr))
|
||||
});
|
||||
non_defaults.push(non_default);
|
||||
}
|
||||
params.var_params = params.var_params.map(|mut var_params| {
|
||||
var_params.t_spec = var_params.t_spec.map(|t_spec| {
|
||||
TypeSpecWithOp::new(t_spec.op, t_spec.t_spec, desugar(*t_spec.t_spec_as_expr))
|
||||
});
|
||||
var_params
|
||||
});
|
||||
let mut defaults = vec![];
|
||||
for mut default in params.defaults.into_iter() {
|
||||
let default_val = desugar(default.default_val);
|
||||
default.sig.t_spec = default.sig.t_spec.map(|t_spec| {
|
||||
TypeSpecWithOp::new(t_spec.op, t_spec.t_spec, desugar(*t_spec.t_spec_as_expr))
|
||||
});
|
||||
defaults.push(DefaultParamSignature {
|
||||
default_val,
|
||||
..default
|
||||
});
|
||||
}
|
||||
params.non_defaults = non_defaults;
|
||||
params.defaults = defaults;
|
||||
params
|
||||
}
|
||||
|
||||
/// `fib 0 = 0; fib 1 = 1; fib n = fib(n-1) + fib(n-2)`
|
||||
/// -> `fib n = match n, (0 -> 0), (1 -> 1), n -> fib(n-1) + fib(n-2)`
|
||||
fn desugar_multiple_pattern_def(&self, module: Module) -> Module {
|
||||
|
@ -856,7 +876,7 @@ impl Desugarer {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn desugar_shortened_record_inner(record: MixedRecord) -> NormalRecord {
|
||||
pub fn desugar_shortened_record_inner(record: MixedRecord) -> NormalRecord {
|
||||
let attrs = record
|
||||
.attrs
|
||||
.into_iter()
|
||||
|
|
12
tests/should_err/array_member.er
Normal file
12
tests/should_err/array_member.er
Normal file
|
@ -0,0 +1,12 @@
|
|||
C = Class { .a = Array(Int) }
|
||||
_ = C.new { .a = [1] } # OK
|
||||
_ = C.new { .a = ["a"] } # ERR
|
||||
|
||||
D = Class { .a = Array(Int, 1) }
|
||||
_ = D.new { .a = [1] } # OK
|
||||
d = D.new { .a = [1, 2] } # OK
|
||||
assert d.a[0] == "a" # ERR
|
||||
|
||||
E = Class { .a = Array(Int, 2) }
|
||||
_ = E.new { .a = [1, 2] } # OK
|
||||
_ = E.new { .a = [1] } # ERR
|
3
tests/should_ok/array_member.er
Normal file
3
tests/should_ok/array_member.er
Normal file
|
@ -0,0 +1,3 @@
|
|||
D = Class { .a = Array(Int, 1) }
|
||||
d = D.new { .a = [1] }
|
||||
assert d.a[0] == 1
|
11
tests/should_ok/index.er
Normal file
11
tests/should_ok/index.er
Normal file
|
@ -0,0 +1,11 @@
|
|||
a = [1, 2, 3]
|
||||
assert a[0] == 1
|
||||
assert a[1..2] == [2, 3]
|
||||
|
||||
s = "abcd"
|
||||
assert s[0] == "a"
|
||||
assert s[1..2] == "bc"
|
||||
|
||||
b = bytes("abcd", "utf-8")
|
||||
assert b[0] == 97
|
||||
assert b[1..2] == bytes("bc", "utf-8")
|
|
@ -17,6 +17,11 @@ fn exec_array() -> Result<(), ()> {
|
|||
expect_success("tests/should_ok/array.er", 0)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_array_member() -> Result<(), ()> {
|
||||
expect_success("tests/should_ok/array_member.er", 0)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_class() -> Result<(), ()> {
|
||||
expect_success("examples/class.er", 0)
|
||||
|
@ -118,6 +123,11 @@ fn exec_import_cyclic() -> Result<(), ()> {
|
|||
expect_success("tests/should_ok/cyclic/import.er", 0)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_index() -> Result<(), ()> {
|
||||
expect_success("tests/should_ok/index.er", 0)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_inherit() -> Result<(), ()> {
|
||||
expect_success("tests/should_ok/inherit.er", 0)
|
||||
|
@ -308,6 +318,11 @@ fn exec_array_err() -> Result<(), ()> {
|
|||
expect_failure("examples/array.er", 0, 1)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_array_member_err() -> Result<(), ()> {
|
||||
expect_failure("tests/should_err/array_member.er", 0, 3)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_as() -> Result<(), ()> {
|
||||
expect_failure("tests/should_err/as.er", 0, 6)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue