diff --git a/Cargo.lock b/Cargo.lock index cb8d890d6..b25a9f5ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,7 +15,7 @@ dependencies = [ [[package]] name = "erg" -version = "0.3.0" +version = "0.3.1" dependencies = [ "erg_common", "erg_compiler", @@ -32,7 +32,7 @@ dependencies = [ [[package]] name = "erg_compiler" -version = "0.3.0" +version = "0.3.1" dependencies = [ "erg_common", "erg_parser", @@ -41,7 +41,7 @@ dependencies = [ [[package]] name = "erg_parser" -version = "0.3.0" +version = "0.3.1" dependencies = [ "erg_common", ] diff --git a/Cargo.toml b/Cargo.toml index f0a10fd04..0bfdad309 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "erg" -version = "0.3.0" +version = "0.3.1" description = "The Erg programming language" authors = ["Shunsuke Shibayama "] license = "MIT OR Apache-2.0" @@ -47,8 +47,8 @@ traditional_chinese = [ [dependencies] erg_common = { version = "0.3.0", path = "./compiler/erg_common" } -erg_parser = { version = "0.3.0", path = "./compiler/erg_parser" } -erg_compiler = { version = "0.3.0", path = "./compiler/erg_compiler" } +erg_parser = { version = "0.3.1", path = "./compiler/erg_parser" } +erg_compiler = { version = "0.3.1", path = "./compiler/erg_compiler" } erg_type = { version = "0.3.0", path = "./compiler/erg_type" } # [workspace] diff --git a/FUNDING.yml b/FUNDING.yml index 0ce2019da..82f5e246b 100644 --- a/FUNDING.yml +++ b/FUNDING.yml @@ -1,6 +1,6 @@ # These are supported funding model platforms -github: mtshiba # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +github: erg-lang # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username diff --git a/compiler/erg_compiler/Cargo.toml b/compiler/erg_compiler/Cargo.toml index 8e9162c13..e9aacdfbc 100644 --- a/compiler/erg_compiler/Cargo.toml +++ b/compiler/erg_compiler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "erg_compiler" -version = "0.3.0" +version = "0.3.1" description = "Centimetre: the Erg compiler" authors = ["Shunsuke Shibayama "] license = "MIT OR Apache-2.0" @@ -18,7 +18,7 @@ traditional_chinese = [ "erg_common/traditional_chinese", "erg_parser/traditiona [dependencies] erg_common = { version = "0.3.0", path = "../erg_common" } -erg_parser = { version = "0.3.0", path = "../erg_parser" } +erg_parser = { version = "0.3.1", path = "../erg_parser" } erg_type = { version = "0.3.0", path = "../erg_type" } [lib] diff --git a/compiler/erg_compiler/codegen.rs b/compiler/erg_compiler/codegen.rs index bcc2bbd59..0b05f6210 100644 --- a/compiler/erg_compiler/codegen.rs +++ b/compiler/erg_compiler/codegen.rs @@ -811,6 +811,8 @@ impl CodeGenerator { // no else block let idx_end = self.cur_block().lasti; self.edit_code(idx_pop_jump_if_false + 1, idx_end / 2); + self.emit_load_const(ValueObj::None); + self.stack_dec(); self.stack_dec(); } Ok(()) diff --git a/compiler/erg_parser/Cargo.toml b/compiler/erg_parser/Cargo.toml index b67b2247f..fbf789f7a 100644 --- a/compiler/erg_parser/Cargo.toml +++ b/compiler/erg_parser/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "erg_parser" -version = "0.3.0" +version = "0.3.1" description = "The Erg parser" authors = ["mtshiba "] license = "MIT OR Apache-2.0" diff --git a/compiler/erg_parser/ast.rs b/compiler/erg_parser/ast.rs index 1dcb14ff0..6f74c7e2e 100644 --- a/compiler/erg_parser/ast.rs +++ b/compiler/erg_parser/ast.rs @@ -2269,6 +2269,15 @@ impl LambdaSignature { bounds, } } + + pub fn do_sig(do_symbol: &Token) -> Self { + let parens = Some((do_symbol.clone(), do_symbol.clone())); + Self::new( + Params::new(vec![], vec![], parens), + None, + TypeBoundSpecs::empty(), + ) + } } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] diff --git a/compiler/erg_parser/parse.rs b/compiler/erg_parser/parse.rs index 90b49d61e..96949107c 100644 --- a/compiler/erg_parser/parse.rs +++ b/compiler/erg_parser/parse.rs @@ -53,6 +53,7 @@ enum PosOrKwArg { pub enum Side { LhsAssign, LhsLambda, + Do, Rhs, } @@ -156,6 +157,15 @@ impl Parser { /// `(Rhs) , (LhsLambda) ->` /// `(Rhs) (LhsLambda) -> (Rhs);` fn cur_side(&self) -> Side { + match self.peek() { + Some(t) => { + let name = &t.inspect()[..]; + if name == "do" || name == "do!" { + return Side::Do; + } + } + _ => {} + } // 以降に=, ->などがないならすべて右辺値 let opt_equal_pos = self.tokens.iter().skip(1).position(|t| t.is(Equal)); let opt_arrow_pos = self @@ -1531,6 +1541,11 @@ impl Parser { debug_call_info!(self); match self.peek() { Some(t) if t.is(Symbol) => { + if &t.inspect()[..] == "do" || &t.inspect()[..] == "do!" { + let lambda = self.try_reduce_do_block().map_err(|_| self.stack_dec())?; + self.level -= 1; + return Ok(PosOrKwArg::Pos(PosArg::new(Expr::Lambda(lambda)))); + } if self.nth_is(1, Colon) { let acc = self.try_reduce_acc().map_err(|_| self.stack_dec())?; debug_power_assert!(self.cur_is(Colon)); @@ -1651,6 +1666,29 @@ impl Parser { Ok(Lambda::new(sig, op, body, self.counter)) } + fn try_reduce_do_block(&mut self) -> ParseResult { + debug_call_info!(self); + let do_symbol = self.lpop(); + let sig = LambdaSignature::do_sig(&do_symbol); + let op = match &do_symbol.inspect()[..] { + "do" => Token::from_str(FuncArrow, "->"), + "do!" => Token::from_str(ProcArrow, "=>"), + _ => todo!(), + }; + if self.cur_is(Colon) { + self.lpop(); + let body = self.try_reduce_block().map_err(|_| self.stack_dec())?; + self.counter.inc(); + self.level -= 1; + Ok(Lambda::new(sig, op, body, self.counter)) + } else { + let expr = self.try_reduce_expr().map_err(|_| self.stack_dec())?; + let block = Block::new(vec![expr]); + self.level -= 1; + Ok(Lambda::new(sig, op, block, self.counter)) + } + } + fn try_reduce_expr(&mut self) -> ParseResult { debug_call_info!(self); let mut stack = Vec::::new(); @@ -1665,6 +1703,11 @@ impl Parser { self.level -= 1; Ok(Expr::Lambda(lambda)) } + Side::Do => { + let lambda = self.try_reduce_do_block().map_err(|_| self.stack_dec())?; + self.level -= 1; + Ok(Expr::Lambda(lambda)) + } Side::Rhs => { stack.push(ExprOrOp::Expr( self.try_reduce_bin_lhs().map_err(|_| self.stack_dec())?, diff --git a/doc/EN/syntax/10_array.md b/doc/EN/syntax/10_array.md index 207db65e6..a3c8c20cc 100644 --- a/doc/EN/syntax/10_array.md +++ b/doc/EN/syntax/10_array.md @@ -48,5 +48,5 @@ print! Typeof l[1..2] # [Int; 4] ```

- Previous | Next + Previous | Next

diff --git a/doc/EN/syntax/17_mutability.md b/doc/EN/syntax/17_mutability.md index 2a5f6e630..e88b87b14 100644 --- a/doc/EN/syntax/17_mutability.md +++ b/doc/EN/syntax/17_mutability.md @@ -99,5 +99,5 @@ In C, types and functions cannot be assigned to variables; int and main are iden However, in Erg, "everything is an object". Not only functions and types, but even operators can be assigned to variables.

- Previous | Next + Previous | Next

diff --git a/doc/EN/syntax/19_visibility.md b/doc/EN/syntax/19_visibility.md index f036b9f98..4c1fef533 100644 --- a/doc/EN/syntax/19_visibility.md +++ b/doc/EN/syntax/19_visibility.md @@ -187,5 +187,5 @@ _ = foo.record.a.z # OK ```

- Previous | Next + Previous | Next

diff --git a/doc/EN/syntax/22_subroutine.md b/doc/EN/syntax/22_subroutine.md index 7793c65ea..0128c059d 100644 --- a/doc/EN/syntax/22_subroutine.md +++ b/doc/EN/syntax/22_subroutine.md @@ -58,5 +58,5 @@ and(x, y, z) = x and y and z ```

- Previous | Next + Previous | Next

diff --git a/doc/EN/syntax/25_object_sysyem.md b/doc/EN/syntax/25_object_system.md similarity index 100% rename from doc/EN/syntax/25_object_sysyem.md rename to doc/EN/syntax/25_object_system.md diff --git a/doc/EN/syntax/type/06_nst_vs_sst.md b/doc/EN/syntax/type/06_nst_vs_sst.md index e69de29bb..097ce7804 100644 --- a/doc/EN/syntax/type/06_nst_vs_sst.md +++ b/doc/EN/syntax/type/06_nst_vs_sst.md @@ -0,0 +1,42 @@ +# Nominal Subtyping vs. Structural Subtyping + +```erg +# NST +MonthsClass = Class Months +MonthsClass. + name self = + match self: + 1 -> "january" + 2 -> "february" + 3 -> "march" + ... + +# SST +Months = 0..12 +MonthsImpl = Patch Months +MonthsImpl. + name self = + match self: + 1 -> "January" + 2 -> "February" + 3 -> "March" + ... + +assert 12 in Months +assert 2.name() == "February" +assert not 12 in MonthsClass +assert MonthsClass.new(12) in MonthsClass +# It can use structural types, even though wrapped in a class. +assert MonthsClass.new(12) in Months +# If both exist, class methods take priority. +assert MonthsClass.new(2).name() == "february" +``` + +## In The End, Which Should I Use, NST or SST? + +If you cannot decide which one to use, our recommendation is NST. +SST requires abstraction skills to write code that does not break down in any use case. Good abstraction can lead to high productivity, but wrong abstraction (commonality by appearances) can lead to counterproductive results. (NSTs can reduce this risk by deliberately keeping abstraction to a minimum. If you are not a library implementor, it is not a bad idea to code only with NSTs. + +

+ Previous | Next +

\ No newline at end of file diff --git a/doc/EN/syntax/type/09_attributive.md b/doc/EN/syntax/type/09_attributive.md index e69de29bb..3098d6713 100644 --- a/doc/EN/syntax/type/09_attributive.md +++ b/doc/EN/syntax/type/09_attributive.md @@ -0,0 +1,9 @@ +# Attributive Type + +Attribute types are types that contain Record and Dataclass, Patch, Module, etc. +Types belonging to attribute types are not value types. + +## Record Type Composite + +It is possible to flatten Record types composited. +For example, `{... {.name = Str; .age = Nat}; ... {.name = Str; .id = Nat}}` becomes `{.name = Str; .age = Nat; .id = Nat}`. \ No newline at end of file diff --git a/doc/EN/syntax/type/10_interval.md b/doc/EN/syntax/type/10_interval.md index e69de29bb..4c71ed4f9 100644 --- a/doc/EN/syntax/type/10_interval.md +++ b/doc/EN/syntax/type/10_interval.md @@ -0,0 +1,36 @@ +# Interval Type + +The most basic use of `Range` objects is as iterator. + +```erg +for! 0..9, i => + print! i +``` + +Note that unlike Python, it includes a end number. + +However, this is not only use for the `Range` objects. It can also be used the type. Such a type is called the Interval type. + +```erg +i: 0..10 = 2 +``` + +The `Nat` type is equivalent to `0.. ExtraStatus, and elements of Status can use methods of ExtraStatus. +Status = Trait {"Ok", "Error"} + # ... +ExtraStatus = Trait {"Ok", "Error", "Unknown"} + # ... +``` + +Methods can also be added by patching. + +Use the `or` operator to explicitly indicate inclusion or to add a choice to an existing Enum type. + +```erg +ExtraStatus = Status or {"Unknown"} +``` + +An enumerated type in which all classes to which an element belongs are identical is called a homogenous enumerated type. + +By default, a class whose requirement type is an homogeneous enumerated type can be treated as a subclass of the class to which the element belongs. + +If you do not wish to do so, you can make it a wrapper class. + +```erg +Abc = Class {"A", "B", "C"} +Abc.new("A").is_uppercase() + +OpaqueAbc = Class {inner = {"A", "B", "C"}}. + new inner: {"A", "B", "C"} = Self.new {inner;} +OpaqueAbc.new("A").is_uppercase() # TypeError +``` diff --git a/doc/EN/syntax/type/13_algebraic.md b/doc/EN/syntax/type/13_algebraic.md index e69de29bb..f910147aa 100644 --- a/doc/EN/syntax/type/13_algebraic.md +++ b/doc/EN/syntax/type/13_algebraic.md @@ -0,0 +1,85 @@ +# Algebraic type + +Algebraic types are types that are generated by operating types by treating them like algebra. +Operations handled by them include Union, Intersection, Diff, Complement, and so on. +Normal classes can only perform Union, and other operations will result in a type error. + +## Union + +Union types can give multiple possibilities for types. As the name suggests, they are generated by the `or` operator. +A typical Union is the `Option` type. The `Option` type is a `T or NoneType` patch type, primarily representing values that may fail. + + +```erg +IntOrStr = Int or Str +assert dict.get("some key") in (Int or NoneType) + +# Implicitly become `T != NoneType` +Option T = T or NoneType +``` + +## Intersection + +Intersection types are got by combining types with the `and` operation. + +```erg +Num = Add and Sub and Mul and Eq +``` + +As mentioned above, normal classes cannot be combined with the `and` operation. This is because instances belong to only one class. + +## Diff + +Diff types are got by `not` operation. +It is better to use `and not` as a closer notation to English text, but it is recommended to use just `not` because it fits better alongside `and` and `or`. + +```erg +CompleteNum = Add and Sub and Mul and Div and Eq and Ord +Num = CompleteNum not Div not Ord + +True = Bool not {False} +OneTwoThree = {1, 2, 3, 4, 5, 6} - {4, 5, 6, 7, 8, 9, 10} +``` + +## Complement + +Complement types is got by the `not` operation, which is a unary operation. The `not T` type is a shorthand for `{=} not T`. +Intersection with type `not T` is equivalent to Diff, and Diff with type `not T` is equivalent to Intersection. +However, this way of writing is not recommended. + +```erg +# the simplest definition of the non-zero number type +NonZero = Not {0} +# deprecated styles +{True} == Bool and not {False} # 1 == 2 + - 1 +Bool == {True} not not {False} # 2 == 1 - -1 +``` + +## True Algebraic type + +There are two algebraic types: apparent algebraic types that can be simplified and true algebraic types that cannot be further simplified. +The "apparent algebraic types" include `or` and `and` of Enum, Interval, and the Record types. +These are not true algebraic types because they are simplified, and using them as type specifiers will result in a Warning; to eliminate the Warning, you must either simplify them or define their types. + +```erg +assert {1, 2, 3} or {2, 3} == {1, 2, 3} +assert {1, 2, 3} and {2, 3} == {2, 3} +assert -2..-1 or 1..2 == {-2, -1, 1, 2} + +i: {1, 2} or {3, 4} = 1 # TypeWarning: {1, 2} or {3, 4} can be simplified to {1, 2, 3, 4} +p: {x = Int, ...} and {y = Int; ...} = {x = 1; y = 2; z = 3} +# TypeWaring: {x = Int, ...} and {y = Int; ...} can be simplified to {x = Int; y = Int; ...} + +Point1D = {x = Int; ...} +Point2D = Point1D and {y = Int; ...} # == {x = Int; y = Int; ...} +q: Point2D = {x = 1; y = 2; z = 3} +``` + +True algebraic types include the types `Or` and `And`. Classes such as `or` between classes are of type `Or`. + +```erg +assert Int or Str == Or(Int, Str) +assert Int and Marker == And(Int, Marker) +``` + +Diff, Complement types are not true algebraic types because they can always be simplified. diff --git a/doc/EN/syntax/type/advanced/quiantified_dependent.md b/doc/EN/syntax/type/advanced/quiantified_dependent.md index 4eda565c9..5a5476f02 100644 --- a/doc/EN/syntax/type/advanced/quiantified_dependent.md +++ b/doc/EN/syntax/type/advanced/quiantified_dependent.md @@ -1,15 +1,15 @@ # Quantified Dependent Type -Erg has quantified and dependent types. Then, naturally, it is possible to create a type that combines the two. That is the quantified dependent type. +Erg has quantified and dependent types. Then naturally, it is possible to create a type that combines the two. That is the quantified dependent type. ```erg NonNullStr = |N: Nat| StrWithLen N | N ! = 0 # same as {S | N: Nat; S: StrWithLen N; N ! = 0} NonEmptyArray = |N: Nat| [_; N | N > 0] # same as {A | N: Nat; A: Array(_, N); N > 0} ``` -The standard form of a quantified dependent type is `K(A, ... | Pred)`. ``K`` is a type constructor, `A, B` are type arguments, and `Pred` is a conditional expression. +The standard form of a quantified dependent types are `K(A, ... | Pred)`. ``K`` is a type constructor, `A, B` are type arguments, and `Pred` is a conditional expression. -A quantified dependent type as a left-hand side value can only define methods in the same module as the original type. +Quantified dependent types as a left-hand side value can only define methods in the same module as the original type. ```erg K A: Nat = Class ... @@ -19,7 +19,7 @@ K(A | A >= 1). method ref! self(A ~> A+1) = ... ``` -A quantified dependent type as a right-hand side value requires that the type variable to be used be declared in the type variable list (`||`). +Quantified dependent types as a right-hand side value require that the type variable to be used be declared in the type variable list (`||`). ```erg # T is a concrete type diff --git a/doc/EN/syntax/type/advanced/shared.md b/doc/EN/syntax/type/advanced/shared.md index e69de29bb..ccc907ae8 100644 --- a/doc/EN/syntax/type/advanced/shared.md +++ b/doc/EN/syntax/type/advanced/shared.md @@ -0,0 +1,72 @@ +# Shared Reference + +Shared references are one of those language features that must be handled with care. +In TypeScript, for example, the following code will pass type checking. + +```typescript +class NormalMember {} +class VIPMember extends NormalMember {} + +let vip_area: VIPMember[] = [] +let normal_area: NormalMember[] = vip_area + +normal_area.push(new NormalMember()) +console.log(vip_area) # [NormalMember] +``` + +A NormalMember has entered the vip_area. It is an obvious bug, however what went wrong? +The cause is the shared reference [denatured](./variance.md). The `normal_area` is created by copying the `vip_area`, but in doing so the type has changed. +But `VIPMember` inherits from `NormalMember`, so `VIPMember[] <: NormalMember[]`, and this is not a problem. +The relation `VIPMember[] <: NormalMember[]` is fine for immutable objects. However, if you perform a destructive operation like the one above, there will be a breakdown. + +In Erg, such code is played back due to the ownership system. + +```erg +NormalMember = Class() +VIPMember = Class() + +vip_area = [].into [VIPMember; !_] +normal_area: [NormalMember; !_] = vip_area + +normal_area.push!(NormalMember.new()) +log vip_area # OwnershipError: `vip_room` was moved to `normal_room` +``` + +However, it can be inconvenient for an object to be owned by only one place. +For this reason, Erg has a type `SharedCell!T!`, which represents a shared state. + +```erg +$p1 = SharedCell!.new(!1) +$p2 = $p1.mirror!() +$p3 = SharedCell!.new(!1) +# If $p1 == $p2, a comparison of the content type Int! +assert $p1 == $p2 +assert $p1 == $p3 +# Check if $p1 and $p2 point to the same thing with `.addr!`. +assert $p1.addr!() == $p2.addr!() +assert $p1.addr!() != $p3.addr!() +$p1.add! 1 +assert $p1 == 2 +assert $p2 == 2 +assert $p3 == 1 +``` + +Objects of type `SharedCell!` must be prefixed with `$`. Also, by their nature, they cannot be constants. + +The `SharedCell! T!` type is also a subtype of `T!` and can call methods of type `T!`. The only methods specific to the `SharedCell!T!` type are `.addr!`, `.mirror!` and `.try_take`. + +An important fact is that `SharedCell! T!` is non-variant, i.e., no inclusions are defined for different type arguments. + +```erg +$vip_area = SharedCell!.new([].into [VIPMember; !_]) +$normal_area: SharedCell!([NormalMember; !_]) = $vip_area.mirror!() # TypeError: expected SharedCell!([NormalMember; !_]), but got SharedCell!([VIPMember; !_]) +# hint: SharedCell!(T) is non-variant, which means it cannot have a supertype or a subtype. +``` + +However, the following code have not problem. In the last line, it's the `VIPMember` argument that has been typed converted. + +```erg +$normal_area = SharedCell!.new([].into [NormalMember; !_]) +$normal_area.push!(NormalMember.new()) # OK +$normal_area.push!(VIPMember.new()) # OK +``` diff --git a/doc/EN/syntax/type/advanced/special.md b/doc/EN/syntax/type/advanced/special.md index e69de29bb..118f234cd 100644 --- a/doc/EN/syntax/type/advanced/special.md +++ b/doc/EN/syntax/type/advanced/special.md @@ -0,0 +1,51 @@ +# Special Type(Self, Super) + +`Self` represents itself types. It can be used simply as an alias, however note that its meaning changes in derived types (it refers to the derived own type). + +```erg +@Inheritable +C = Class() +C. + new_self() = Self.new() + new_c() = C.new() +D = Inherit C + +classof D.new_self() # D +classof D.new_c() # C +``` + +`Super` represents types of base classes. The method itself refers to the base class, however the instance uses its own type. + +```erg +@Inheritable +C = Class() + +D = Inherit(C) +D. + new_super() = Super.new() + new_c() = C.new() + +classof D.new_super() # D +classof D.new_c() # C +``` + +## Special Type Variable + +`Self` and `Super` can be used as type variables in structural types and traits. It refers to a class, which is a subtype of that type. That is, `Self` in type `T` means `Self <: T`. + +```erg +Add R = Trait { + .AddO = Type + .`_+_`: Self, R -> Self.AddO +} +ClosedAdd = Subsume Add(Self) + +ClosedAddForInt = Patch(Int, Impl: ClosedAdd) +ClosedAddForInt. + AddO = Int + +assert 1 in Add(Int, Int) +assert 1 in ClosedAdd +assert Int < Add(Int, Int) +assert Int < ClosedAdd +``` diff --git a/doc/JA/syntax/00_basic.md b/doc/JA/syntax/00_basic.md index 63ac026be..700f63907 100644 --- a/doc/JA/syntax/00_basic.md +++ b/doc/JA/syntax/00_basic.md @@ -114,5 +114,5 @@ x \ ```

- Previous | Next + Previous | Next

diff --git a/doc/JA/syntax/01_literal.md b/doc/JA/syntax/01_literal.md index 57a2e388e..456b3ff32 100644 --- a/doc/JA/syntax/01_literal.md +++ b/doc/JA/syntax/01_literal.md @@ -146,5 +146,5 @@ assert 1m / 1s == 1 (m/s) ```

- Previous | Next + Previous | Next

diff --git a/doc/JA/syntax/10_array.md b/doc/JA/syntax/10_array.md index 01e1400ea..d8d16bb2e 100644 --- a/doc/JA/syntax/10_array.md +++ b/doc/JA/syntax/10_array.md @@ -48,5 +48,5 @@ print! Typeof l[1..2] # Ref [Int; 4] ```

- Previous | Next + Previous | Next

diff --git a/doc/JA/syntax/13_record.md b/doc/JA/syntax/13_record.md index 6bf2ac41d..2ab07f4a7 100644 --- a/doc/JA/syntax/13_record.md +++ b/doc/JA/syntax/13_record.md @@ -195,5 +195,5 @@ john + 1 ```

- Previous | Next + Previous | Next

diff --git a/doc/JA/syntax/17_mutability.md b/doc/JA/syntax/17_mutability.md index ef3eccb96..901fdd7ce 100644 --- a/doc/JA/syntax/17_mutability.md +++ b/doc/JA/syntax/17_mutability.md @@ -97,5 +97,5 @@ match! x: しかし、Ergでは「全てがオブジェクト」です。関数や型は勿論、演算子でさえ変数に代入可能です。

- Previous | Next + Previous | Next

diff --git a/doc/JA/syntax/19_visibility.md b/doc/JA/syntax/19_visibility.md index ab6c7f86b..1209eaeee 100644 --- a/doc/JA/syntax/19_visibility.md +++ b/doc/JA/syntax/19_visibility.md @@ -187,5 +187,5 @@ _ = foo.record.a.z # OK ```

- Previous | Next + Previous | Next

diff --git a/doc/JA/syntax/22_subroutine.md b/doc/JA/syntax/22_subroutine.md index 486d95f03..b13a57d4e 100644 --- a/doc/JA/syntax/22_subroutine.md +++ b/doc/JA/syntax/22_subroutine.md @@ -57,5 +57,5 @@ and(x, y, z) = x and y and z ```

- Previous | Next + Previous | Next

diff --git a/doc/JA/syntax/type/06_nst_vs_sst.md b/doc/JA/syntax/type/06_nst_vs_sst.md index ad59ebe2d..006f71095 100644 --- a/doc/JA/syntax/type/06_nst_vs_sst.md +++ b/doc/JA/syntax/type/06_nst_vs_sst.md @@ -1,6 +1,16 @@ # 記名的部分型 vs. 構造的部分型 ```erg +# NST +MonthsClass = Class Months +MonthsClass. + name self = + match self: + 1 -> "January" + 2 -> "February" + 3 -> "March" + ... + # SST Months = 0..12 MonthsImpl = Patch Months @@ -11,15 +21,6 @@ MonthsImpl. 2 -> "February" 3 -> "March" ... -# NST -MonthsClass = Class Months -MonthsClass. - name self = - match self: - 1 -> "january" - 2 -> "february" - 3 -> "march" - ... assert 12 in Months assert 2.name() == "February" diff --git a/doc/JA/syntax/type/10_interval.md b/doc/JA/syntax/type/10_interval.md index 77d7db1f7..38d308ba3 100644 --- a/doc/JA/syntax/type/10_interval.md +++ b/doc/JA/syntax/type/10_interval.md @@ -15,13 +15,19 @@ Pythonと違い、末尾の数字は含まれることに注意してくださ i: 0..10 = 2 ``` -`Nat`型は`0.. log "hello" if! True, () => print! "hello" # if True, () => print! "hello" # this should cause a type error -if True, () -> +if True, do: _x = "aaa" + input!() # this should cause an effect error print! "hello" # this should cause an effect error diff --git a/src/dummy.rs b/src/dummy.rs index 1bab864be..bbf9e60fb 100644 --- a/src/dummy.rs +++ b/src/dummy.rs @@ -1,6 +1,6 @@ use std::fs::remove_file; use std::io::{Read, Write}; -use std::net::TcpStream; +use std::net::{Ipv4Addr, SocketAddrV4, TcpListener, TcpStream}; use std::thread::sleep; use std::time::Duration; @@ -30,12 +30,12 @@ impl Runnable for DummyVM { fn new(cfg: ErgConfig) -> Self { let stream = if cfg.input.is_repl() { println!("Starting the REPL server..."); - let code = include_str!("scripts/repl_server.py"); - exec_py(code); + let port = find_available_port(); + let code = include_str!("scripts/repl_server.py") + .replace("__PORT__", port.to_string().as_str()); + exec_py(&code); + let addr = SocketAddrV4::new(Ipv4Addr::LOCALHOST, port); println!("Connecting to the REPL server..."); - let repl_server_ip = "127.0.0.1"; - let repl_server_port = 8736; - let addr = format!("{repl_server_ip}:{repl_server_port}"); loop { match TcpStream::connect(&addr) { Ok(stream) => { @@ -128,3 +128,17 @@ impl Runnable for DummyVM { Ok(res) } } + +fn find_available_port() -> u16 { + const DEFAULT_PORT: u16 = 8736; + TcpListener::bind(SocketAddrV4::new(Ipv4Addr::LOCALHOST, DEFAULT_PORT)) + .is_ok() + .then_some(DEFAULT_PORT) + .unwrap_or_else(|| { + let socket = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0); + TcpListener::bind(socket) + .and_then(|listener| listener.local_addr()) + .map(|sock_addr| sock_addr.port()) + .expect("No free port found.") + }) +} diff --git a/src/scripts/repl_server.py b/src/scripts/repl_server.py index dcbbd2847..481950a4c 100644 --- a/src/scripts/repl_server.py +++ b/src/scripts/repl_server.py @@ -6,7 +6,8 @@ import io as __io __server_socket = __socket.socket() -__server_socket.bind(('0.0.0.0', 8736)) +# DummyVM will replace this __PORT__ with free port +__server_socket.bind(('127.0.0.1', __PORT__)) __server_socket.listen(1) (__client_socket, __client_address) = __server_socket.accept()