diff --git a/compiler/erg_compiler/codegen.rs b/compiler/erg_compiler/codegen.rs index 17877638..97b18950 100644 --- a/compiler/erg_compiler/codegen.rs +++ b/compiler/erg_compiler/codegen.rs @@ -133,6 +133,7 @@ pub struct PyCodeGenerator { pub(crate) py_version: PythonVersion, str_cache: CacheSet, prelude_loaded: bool, + mutate_op_loaded: bool, in_op_loaded: bool, record_type_loaded: bool, module_type_loaded: bool, @@ -149,6 +150,7 @@ impl PyCodeGenerator { cfg, str_cache: CacheSet::new(), prelude_loaded: false, + mutate_op_loaded: false, in_op_loaded: false, record_type_loaded: false, module_type_loaded: false, @@ -426,7 +428,8 @@ impl PyCodeGenerator { } fn emit_load_const>(&mut self, cons: C) { - let value = cons.into(); + let value: ValueObj = cons.into(); + let is_int = value.is_int(); let is_nat = value.is_nat(); let is_bool = value.is_bool(); if !self.cfg.no_std { @@ -436,6 +439,9 @@ impl PyCodeGenerator { } else if is_nat { self.emit_push_null(); self.emit_load_name_instr(Identifier::public("Nat")); + } else if is_int { + self.emit_push_null(); + self.emit_load_name_instr(Identifier::public("Int")); } } let idx = self @@ -450,7 +456,8 @@ impl PyCodeGenerator { self.write_instr(LOAD_CONST); self.write_arg(idx); self.stack_inc(); - if !self.cfg.no_std && is_nat { + if !self.cfg.no_std && is_int { + // is_int => is_nat and is_bool self.emit_call_instr(1, Name); self.stack_dec(); } @@ -1258,12 +1265,18 @@ impl PyCodeGenerator { fn emit_unaryop(&mut self, unary: UnaryOp) { log!(info "entered {} ({unary})", fn_name!()); let tycode = TypeCode::from(unary.lhs_t()); - self.emit_expr(*unary.expr); let instr = match &unary.op.kind { // TODO: TokenKind::PrePlus => UNARY_POSITIVE, TokenKind::PreMinus => UNARY_NEGATIVE, - TokenKind::Mutate => NOP, // ERG_MUTATE, + TokenKind::Mutate => { + if !self.mutate_op_loaded { + self.load_mutate_op(); + } + self.emit_push_null(); + self.emit_load_name_instr(Identifier::private("#mutate_operator")); + NOP // ERG_MUTATE, + } _ => { CompileError::feature_error( self.cfg.input.clone(), @@ -1275,8 +1288,14 @@ impl PyCodeGenerator { NOT_IMPLEMENTED } }; - self.write_instr(instr); - self.write_arg(tycode as usize); + self.emit_expr(*unary.expr); + if instr != NOP { + self.write_instr(instr); + self.write_arg(tycode as usize); + } else { + self.emit_precall_and_call(1); + self.stack_dec(); + } } fn emit_binop(&mut self, bin: BinOp) { @@ -1792,7 +1811,7 @@ impl PyCodeGenerator { if i != 0 && i != len - 1 { self.dup_top(); } - self.emit_load_const(i as i32); + self.emit_load_const(i); self.write_instr(Opcode311::BINARY_SUBSCR); self.write_arg(0); self.stack_dec(); @@ -1806,7 +1825,7 @@ impl PyCodeGenerator { if i != 0 && i != len - 1 { self.dup_top(); } - self.emit_load_const(i as i32); + self.emit_load_const(i); self.write_instr(Opcode311::BINARY_SUBSCR); self.write_arg(0); self.stack_dec(); @@ -2695,10 +2714,15 @@ impl PyCodeGenerator { } fn load_prelude(&mut self) { + // NOTE: Integers need to be used in IMPORT_NAME + // but `Int` are called before importing it, so they need to be no_std mode + let no_std = self.cfg.no_std; + self.cfg.no_std = true; self.load_record_type(); self.load_prelude_py(); self.prelude_loaded = true; self.record_type_loaded = true; + self.cfg.no_std = no_std; } fn load_in_op(&mut self) { @@ -2717,6 +2741,22 @@ impl PyCodeGenerator { self.in_op_loaded = true; } + fn load_mutate_op(&mut self) { + let mod_name = if self.py_version.minor >= Some(10) { + Identifier::public("_erg_std_prelude") + } else { + Identifier::public("_erg_std_prelude_old") + }; + self.emit_global_import_items( + mod_name, + vec![( + Identifier::public("mutate_operator"), + Some(Identifier::private("#mutate_operator")), + )], + ); + self.mutate_op_loaded = true; + } + fn load_control(&mut self) { let mod_name = Identifier::public("_erg_control"); self.emit_import_all_instr(mod_name); diff --git a/compiler/erg_compiler/context/initialize/mod.rs b/compiler/erg_compiler/context/initialize/mod.rs index 99804ddf..29e7b4fe 100644 --- a/compiler/erg_compiler/context/initialize/mod.rs +++ b/compiler/erg_compiler/context/initialize/mod.rs @@ -773,6 +773,8 @@ impl Context { // class("Rational"), // class("Integral"), int.register_builtin_impl("abs", fn0_met(Int, Nat), Immutable, Public); + int.register_builtin_py_impl("succ", fn0_met(Int, Int), Immutable, Public, Some("succ")); + int.register_builtin_py_impl("pred", fn0_met(Int, Int), Immutable, Public, Some("pred")); let mut int_ord = Self::builtin_methods(Some(mono("Ord")), 2); int_ord.register_builtin_impl( "__partial_cmp__", @@ -1328,6 +1330,9 @@ impl Context { let mut int_mut = Self::builtin_mono_class("Int!", 2); int_mut.register_superclass(Int, &int); int_mut.register_superclass(mono("Float!"), &float_mut); + let t = pr_met(mono("Int!"), vec![], None, vec![kw("i", Int)], NoneType); + int_mut.register_builtin_py_impl("inc!", t.clone(), Immutable, Public, Some("inc")); + int_mut.register_builtin_py_impl("dec!", t, Immutable, Public, Some("dec")); let mut int_mut_mutable = Self::builtin_methods(Some(mono("Mutable")), 2); int_mut_mutable.register_builtin_const("ImmutType", Public, ValueObj::builtin_t(Int)); let f_t = kw("func", func(vec![kw("old", Int)], None, vec![], Int)); diff --git a/compiler/erg_compiler/lib/std/_erg_int.py b/compiler/erg_compiler/lib/std/_erg_int.py new file mode 100644 index 00000000..78b0e413 --- /dev/null +++ b/compiler/erg_compiler/lib/std/_erg_int.py @@ -0,0 +1,85 @@ +from _erg_result import Error + +class Int(int): + def try_new(i): # -> Result[Nat] + if isinstance(i, int): + Int(i) + else: + Error("not an integer") + def succ(self): + return Int(self + 1) + def pred(self): + return Int(self - 1) + def mutate(self): + return IntMut(self) + +class IntMut(): # inherits Int + value: Int + + def __init__(self, i): + self.value = Int(i) + def __repr__(self): + return self.value.__repr__() + def __eq__(self, other): + if isinstance(other, Int): + return self.value == other + else: + return self.value == other.value + def __ne__(self, other): + if isinstance(other, Int): + return self.value != other + else: + return self.value != other.value + def __le__(self, other): + if isinstance(other, Int): + return self.value <= other + else: + return self.value <= other.value + def __ge__(self, other): + if isinstance(other, Int): + return self.value >= other + else: + return self.value >= other.value + def __lt__(self, other): + if isinstance(other, Int): + return self.value < other + else: + return self.value < other.value + def __gt__(self, other): + if isinstance(other, Int): + return self.value > other + else: + return self.value > other.value + def __add__(self, other): + if isinstance(other, Int): + return IntMut(self.value + other) + else: + return IntMut(self.value + other.value) + def __sub__(self, other): + if isinstance(other, Int): + return IntMut(self.value - other) + else: + return IntMut(self.value - other.value) + def __mul__(self, other): + if isinstance(other, Int): + return IntMut(self.value * other) + else: + return IntMut(self.value * other.value) + def __floordiv__(self, other): + if isinstance(other, Int): + return IntMut(self.value // other) + else: + return IntMut(self.value // other.value) + def __pow__(self, other): + if isinstance(other, Int): + return IntMut(self.value ** other) + else: + return IntMut(self.value ** other.value) + def inc(self, i=1): + self.value = Int(self.value + i) + def dec(self, i=1): + self.value = Int(self.value - i) + def succ(self): + return self.value.succ() + def pred(self): + return self.value.pred() diff --git a/compiler/erg_compiler/lib/std/_erg_mutate_operator.py b/compiler/erg_compiler/lib/std/_erg_mutate_operator.py new file mode 100644 index 00000000..e5753140 --- /dev/null +++ b/compiler/erg_compiler/lib/std/_erg_mutate_operator.py @@ -0,0 +1,5 @@ +def mutate_operator(x): + if hasattr(x, 'mutate'): + return x.mutate() + else: + return x diff --git a/compiler/erg_compiler/lib/std/_erg_nat.py b/compiler/erg_compiler/lib/std/_erg_nat.py index f13e0820..4cadd671 100644 --- a/compiler/erg_compiler/lib/std/_erg_nat.py +++ b/compiler/erg_compiler/lib/std/_erg_nat.py @@ -1,6 +1,8 @@ from _erg_result import Error +from _erg_int import Int +from _erg_int import IntMut -class Nat(int): +class Nat(Int): def try_new(i): # -> Result[Nat] if i >= 0: return Nat(i) @@ -16,3 +18,67 @@ class Nat(int): return self - other else: return 0 + def mutate(self): + return NatMut(self) + +class NatMut(IntMut): # and Nat + value: Nat + + def __init__(self, n): + self.value = n + def __repr__(self): + return self.value.__repr__() + def __eq__(self, other): + if isinstance(other, Int): + return self.value == other + else: + return self.value == other.value + def __ne__(self, other): + if isinstance(other, Int): + return self.value != other + else: + return self.value != other.value + def __le__(self, other): + if isinstance(other, Int): + return self.value <= other + else: + return self.value <= other.value + def __ge__(self, other): + if isinstance(other, Int): + return self.value >= other + else: + return self.value >= other.value + def __lt__(self, other): + if isinstance(other, Int): + return self.value < other + else: + return self.value < other.value + def __gt__(self, other): + if isinstance(other, Int): + return self.value > other + else: + return self.value > other.value + def __add__(self, other): + if isinstance(other, Nat): + return NatMut(self.value + other) + else: + return NatMut(self.value + other.value) + def __mul__(self, other): + if isinstance(other, Nat): + return NatMut(self.value * other) + else: + return NatMut(self.value * other.value) + def __pow__(self, other): + if isinstance(other, Nat): + return NatMut(self.value ** other) + else: + return NatMut(self.value ** other.value) + def try_new(i): # -> Result[Nat] + if i >= 0: + return NatMut(i) + else: + return Error("Nat can't be negative") + + def times(self, f): + for _ in range(self.value): + f() diff --git a/compiler/erg_compiler/lib/std/_erg_range.py b/compiler/erg_compiler/lib/std/_erg_range.py index 6a0914d5..933958dd 100644 --- a/compiler/erg_compiler/lib/std/_erg_range.py +++ b/compiler/erg_compiler/lib/std/_erg_range.py @@ -69,7 +69,7 @@ class RangeIterator: self.needle = chr(ord(self.needle) + 1) else: if not(self.needle in self.rng): - self.needle = self.needle.incremented() + self.needle = self.needle.succ() def __iter__(self): return self @@ -88,7 +88,7 @@ class RangeIterator: else: if self.needle in self.rng: result = self.needle - self.needle = self.needle.incremented() + self.needle = self.needle.succ() return result raise StopIteration diff --git a/compiler/erg_compiler/lib/std/_erg_std_prelude.py b/compiler/erg_compiler/lib/std/_erg_std_prelude.py index c7664816..47c65d1c 100644 --- a/compiler/erg_compiler/lib/std/_erg_std_prelude.py +++ b/compiler/erg_compiler/lib/std/_erg_std_prelude.py @@ -1,7 +1,9 @@ from _erg_range import Range, LeftOpenRange, RightOpenRange, OpenRange, ClosedRange, RangeIterator from _erg_result import Result, Error, is_ok -from _erg_nat import Nat +from _erg_int import Int, IntMut +from _erg_nat import Nat, NatMut from _erg_bool import Bool -from _erg_str import Str +from _erg_str import Str, StrMut from _erg_array import Array from _erg_in_operator import in_operator +from _erg_mutate_operator import mutate_operator diff --git a/compiler/erg_compiler/lib/std/_erg_std_prelude_old.py b/compiler/erg_compiler/lib/std/_erg_std_prelude_old.py index ec67f54a..b8a17aff 100644 --- a/compiler/erg_compiler/lib/std/_erg_std_prelude_old.py +++ b/compiler/erg_compiler/lib/std/_erg_std_prelude_old.py @@ -54,7 +54,6 @@ class Bool(Nat): class Str(str): def __instancecheck__(cls, obj): - print(cls, obj) return obj == Str or obj == str def try_new(s: str): # -> Result[Nat] diff --git a/compiler/erg_compiler/lib/std/_erg_str.py b/compiler/erg_compiler/lib/std/_erg_str.py index 6d278376..183015eb 100644 --- a/compiler/erg_compiler/lib/std/_erg_str.py +++ b/compiler/erg_compiler/lib/std/_erg_str.py @@ -13,27 +13,47 @@ class Str(str): return Str(self[i]) else: return None + def mutate(self): + return StrMut(self) -class StrMut(Str): +class StrMut(): # Inherits Str + value: Str + + def __init__(self, s: str): + self.value = s + def __repr__(self): + return self.value.__repr__() + def __eq__(self, other): + if isinstance(other, Str): + return self.value == other + else: + return self.value == other.value + def __ne__(self, other): + if isinstance(other, Str): + return self.value != other + else: + return self.value != other.value def try_new(s: str): if isinstance(s, str): - return StrMut(s) + self = StrMut() + self.value = s + return self else: return Error("Str! can't be other than str") def clear(self): - self = "" + self.value = "" def pop(self): - if len(self) > 0: - last = self[-1] - self = self[:-1] + if len(self.value) > 0: + last = self.value[-1] + self.value = self.value[:-1] return last else: return Error("Can't pop from empty `Str!`") def push(self, c: str): - self += c + self.value += c def remove(self, idx: int): - char = self[idx] - self = self[:idx] + self[idx+1:] + char = self.value[idx] + self.value = self.value[:idx] + self.value[idx+1:] return char def insert(self, idx: int, c: str): - self = self[:idx] + c + self[idx:] + self.value = self.value[:idx] + c + self.value[idx:] diff --git a/compiler/erg_compiler/transpile.rs b/compiler/erg_compiler/transpile.rs index a3255ea6..a104414a 100644 --- a/compiler/erg_compiler/transpile.rs +++ b/compiler/erg_compiler/transpile.rs @@ -185,6 +185,7 @@ pub struct ScriptGenerator { level: usize, fresh_var_n: usize, namedtuple_loaded: bool, + mutate_op_loaded: bool, in_op_loaded: bool, range_ops_loaded: bool, builtin_types_loaded: bool, @@ -197,6 +198,7 @@ impl ScriptGenerator { level: 0, fresh_var_n: 0, namedtuple_loaded: false, + mutate_op_loaded: false, in_op_loaded: false, range_ops_loaded: false, builtin_types_loaded: false, @@ -220,12 +222,14 @@ impl ScriptGenerator { // TODO: more smart way fn replace_import(src: &str) -> String { src.replace("from _erg_nat import Nat", "") - .replace("from _erg_result import Error", "") - .replace("from _erg_result import is_ok", "") + .replace("from _erg_int import IntMut", "") + .replace("from _erg_int import Int", "") .replace("from _erg_bool import Bool", "") .replace("from _erg_str import Str", "") .replace("from _erg_array import Array", "") .replace("from _erg_range import Range", "") + .replace("from _erg_result import Error", "") + .replace("from _erg_result import is_ok", "") } fn load_namedtuple(&mut self) { @@ -235,6 +239,7 @@ impl ScriptGenerator { // TODO: name escaping fn load_range_ops(&mut self) { self.prelude += &Self::replace_import(include_str!("lib/std/_erg_result.py")); + self.prelude += &Self::replace_import(include_str!("lib/std/_erg_int.py")); self.prelude += &Self::replace_import(include_str!("lib/std/_erg_nat.py")); self.prelude += &Self::replace_import(include_str!("lib/std/_erg_str.py")); self.prelude += &Self::replace_import(include_str!("lib/std/_erg_range.py")); @@ -246,16 +251,22 @@ impl ScriptGenerator { self.prelude += &Self::replace_import(include_str!("lib/std/_erg_in_operator.py")); } + fn load_mutate_op(&mut self) { + self.prelude += &Self::replace_import(include_str!("lib/std/_erg_mutate_operator.py")); + } + fn load_builtin_types(&mut self) { if self.range_ops_loaded { self.prelude += &Self::replace_import(include_str!("lib/std/_erg_array.py")); } else if self.in_op_loaded { + self.prelude += &Self::replace_import(include_str!("lib/std/_erg_int.py")); self.prelude += &Self::replace_import(include_str!("lib/std/_erg_nat.py")); self.prelude += &Self::replace_import(include_str!("lib/std/_erg_bool.py")); self.prelude += &Self::replace_import(include_str!("lib/std/_erg_str.py")); self.prelude += &Self::replace_import(include_str!("lib/std/_erg_array.py")); } else { self.prelude += &Self::replace_import(include_str!("lib/std/_erg_result.py")); + self.prelude += &Self::replace_import(include_str!("lib/std/_erg_int.py")); self.prelude += &Self::replace_import(include_str!("lib/std/_erg_nat.py")); self.prelude += &Self::replace_import(include_str!("lib/std/_erg_bool.py")); self.prelude += &Self::replace_import(include_str!("lib/std/_erg_str.py")); @@ -265,7 +276,20 @@ impl ScriptGenerator { fn transpile_expr(&mut self, expr: Expr) -> String { match expr { - Expr::Lit(lit) => lit.token.content.to_string(), + Expr::Lit(lit) => { + if matches!( + &lit.value, + ValueObj::Bool(_) | ValueObj::Int(_) | ValueObj::Nat(_) | ValueObj::Str(_) + ) { + if !self.builtin_types_loaded { + self.load_builtin_types(); + self.builtin_types_loaded = true; + } + format!("{}({})", lit.value.class(), lit.token.content) + } else { + lit.token.content.to_string() + } + } Expr::Call(call) => self.transpile_call(call), Expr::BinOp(bin) => match bin.op.kind { TokenKind::Closed @@ -314,8 +338,15 @@ impl ScriptGenerator { } }, Expr::UnaryOp(unary) => { - let mut code = "(".to_string(); - if unary.op.kind != TokenKind::Mutate { + let mut code = "".to_string(); + if unary.op.kind == TokenKind::Mutate { + if !self.mutate_op_loaded { + self.load_mutate_op(); + self.mutate_op_loaded = true; + } + code += "mutate_operator("; + } else { + code += "("; code += &unary.op.content; } code += &self.transpile_expr(*unary.expr); diff --git a/compiler/erg_compiler/ty/value.rs b/compiler/erg_compiler/ty/value.rs index 3e8f8c2b..22bba80c 100644 --- a/compiler/erg_compiler/ty/value.rs +++ b/compiler/erg_compiler/ty/value.rs @@ -624,6 +624,14 @@ impl ValueObj { } } + pub fn is_int(&self) -> bool { + match self { + Self::Int(_) | Self::Nat(_) | Self::Bool(_) => true, + Self::Mut(n) => n.borrow().is_nat(), + _ => false, + } + } + pub fn is_nat(&self) -> bool { match self { Self::Nat(_) | Self::Bool(_) => true, diff --git a/examples/mut.er b/examples/mut.er new file mode 100644 index 00000000..55468984 --- /dev/null +++ b/examples/mut.er @@ -0,0 +1,8 @@ +i = !1 +i.update! i -> i * 3 +assert i == 3 + +i.inc!() +assert i == 4 +i.dec!() +assert i == 3 diff --git a/tests/test.rs b/tests/test.rs index c441f8c4..e47897fb 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -84,6 +84,11 @@ fn exec_infer_trait() -> Result<(), ()> { expect_success("tests/should_ok/infer_trait.er") } +#[test] +fn exec_mut() -> Result<(), ()> { + expect_success("examples/mut.er") +} + #[test] fn exec_pattern() -> Result<(), ()> { expect_success("tests/should_ok/pattern.er")