diff --git a/.cargo/config b/.cargo/config.toml similarity index 100% rename from .cargo/config rename to .cargo/config.toml diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 05385e4a6..1f545163b 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -10,6 +10,8 @@ - [VSC Support](./tutorial/vscsupport.md) - [References](./references/README.md) - [Basic](./references/basic.md) + - [Operator](./references/operator/README.md) + - [Type Operator](./references/operator/tyops.md) - [Array](./references/array.md) - [closure](./references/closure.md) - [Module](./references/module.md) diff --git a/book/src/dev-prepare.md b/book/src/dev-prepare.md index 59a9580b0..70bd09699 100644 --- a/book/src/dev-prepare.md +++ b/book/src/dev-prepare.md @@ -22,7 +22,7 @@ brew install llvm@18 如果你使用的是ubuntu,可以使用[这里](https://github.com/Pivot-Studio/setup-llvm/blob/main/scripts/install_llvm.sh)的脚本进行安装 -# CMake +## CMake 我们的垃圾回收模块使用了LLVM中的StackMap功能,需要使用CMake进行编译。 diff --git a/book/src/references/basic.md b/book/src/references/basic.md index dfe5ab65b..849691d40 100644 --- a/book/src/references/basic.md +++ b/book/src/references/basic.md @@ -90,4 +90,4 @@ 例如: -- `let x = (false && true_with_pnanic()) || (true || !true_with_pnanic());`是一个复杂的逻辑运算 +- `let x = (false && true_with_panic()) || (true || !true_with_panic());`是一个复杂的逻辑运算 diff --git a/book/src/references/operator/README.md b/book/src/references/operator/README.md new file mode 100644 index 000000000..ee01610b3 --- /dev/null +++ b/book/src/references/operator/README.md @@ -0,0 +1,74 @@ +# Operators + +本节收录了所有的运算符。 + +## 算术运算符 + +| 运算符 | 描述 | 示例 | +| --- | --- | --- | +| `+` | 加法 | `1 + 1` | +| `-` | 减法 | `1 - 1` | +| `*` | 乘法 | `1 * 1` | +| `/` | 除法 | `1 / 1` | +| `%` | 取余 | `1 % 1` | + +## 位运算符 + +| 运算符 | 描述 | 示例 | +| --- | --- | --- | +| `&` | 与 | `1 & 1` | +| `\|` | 或 | `1 \| 1` | +| `^` | 异或 | `1 ^ 1` | +| `<<` | 左移 | `1 << 1` | +| `>>` | 右移 | `1 >> 1` | + +## 逻辑运算符 + +| 运算符 | 描述 | 示例 | +| --- | --- | --- | +| `&&` | 逻辑与 | `true && false` | +| `\|\|` | 逻辑或 | `true \|\| false` | +| `!` | 逻辑非 | `!true` | + +## 比较运算符 + +| 运算符 | 描述 | 示例 | +| --- | --- | --- | +| `==` | 等于 | `1 == 1` | +| `!=` | 不等于 | `1 != 1` | +| `>` | 大于 | `1 > 1` | +| `<` | 小于 | `1 < 1` | +| `>=` | 大于等于 | `1 >= 1` | +| `<=` | 小于等于 | `1 <= 1` | + +## 赋值运算符 + +| 运算符 | 描述 | 示例 | +| --- | --- | --- | +| `=` | 赋值 | `a = 1` | + +## 类型运算符 + +详见[类型运算符](./tyops.md)。 + +| 运算符 | 描述 | 示例 | +| --- | --- | --- | +| `as` | 类型转换 | `1 as i32` | +| `is` | 类型判断 | `1 is i32` | +| `impl` | 实现 | `a impl TestTrait?` | + +## 其他运算符 + +| 运算符 | 描述 | 示例 | +| --- | --- | --- | +| `&` | 取地址 | `&a` | +| `*` | 取值 | `*a` | +| `()` | 函数调用 | `test_vm()` | +| `[]` | 索引 | `a[1]` | +| `.` | 成员访问 | `a.b` | +| `:` | 类型标注 | `let a: i32 = 1` | +| `;` | 语句结束 | `let a = 1;` | + + + + diff --git a/book/src/references/operator/tyops.md b/book/src/references/operator/tyops.md new file mode 100644 index 000000000..88fb10ba3 --- /dev/null +++ b/book/src/references/operator/tyops.md @@ -0,0 +1,186 @@ +# Type Operators 类型运算符 + +本章节会对三种特殊的运算符进行介绍:`as`、`is` 和 `impl`。 + +## `as` 类型转换运算符 + +`as` 运算符用于将一个值转换为另一个类型。 + +### 基础类型转换 + +在转换一些基础类型的时候(比如int类型之间的转换),`as` 运算符是不会失败的,例如: + +```plang +let a: i64 = 1; +let b: i32 = a as i32; +``` + +在这个例子中,`a` 是一个 `i64` 类型的变量,我们将其转换为 `i32` 类型的变量 `b`。这种转换永远成功,尽管可能会导致精度丢失或者损失一部分数据。 + +### 和类型转换 + +`as`也可以进行对`和类型`的转换,假设我们有如下类型: + +```plang +struct ST{}; +type Test = T | ST; +``` + +我们可以将任意一个类型用`as`转换为`Option`类型: + +```plang +let a: i64 = 1; +let b = a as Test; +``` + +将子类型转换为和类型是不可能失败的。如果尝试转化为一个不可能的类型,编译器会报错。 + +反之,`as`运算符也可以将和类型转换为子类型: + +```plang +let a: i64 = 1; +let b = a as Test; +let c: i64 = b as i64!; +let d: Option = b as i64?; +``` + +但是,将和类型转换为子类型可能会失败,编译器不会允许你直接使用常规的`as`语句进行转换,你必须使用`?`或`!`来标注转换是非强制的还是强制的。 + +如果使用`?`标注,那么`x as T?`语句会返回一个`Option`类型,如果转换失败,则该返回值是`None`。 + +如果使用`!`标注,那么`x as T!`语句会返回一个`T`类型,如果转换失败,则会导致运行时错误(__cast_panic)。 + +### 泛型类型转换 + +`as`运算符也可以用于泛型类型的转换: + +```plang +fn test(x: T) i64 { + let y = x as i64!; + return x; +} +``` + +如果泛型类型转换失败,会导致运行时错误(__cast_panic)。这种转换是编译期进行的,没有运行时开销。 + +泛型的转换一定是强制的,需要带上`!`标注。 + +#### if let ... as ... 语法 + +`if let ... as ...` 语法可以用于安全的对泛型类型进行转换: + +```plang +fn test(x: T) i64 { + if let y = x as i64 { + return y; + } + return -1; +} +``` + +## `is` 类型判断运算符 + +### 基础类型判断 + +`is` 运算符用于判断一个值是否是某个类型。例如: + +```plang +let a: i64 = 1; +let b = a is i64; +``` + +在这个例子中,`b` 的值是 `true`,因为 `a` 是一个 `i64` 类型的变量。 + +### 和类型判断 + +`is` 运算符也可以用于判断和类型: + +```plang +let a: i64 = 1; +let b = a as Test; +let c = b is i64; +``` + +在这个例子中,`c` 的值是 `true`,因为 `b` 是一个 `i64` 类型的变量。 + +### 泛型类型判断 + +特殊的,`is` 运算符也可以用于判断泛型类型: + +```plang +fn test(x: T) T { + if x is i64 { + doSth(); + } + return x; +} +``` + +## `impl` 判断实现运算符 + +`impl` 运算符用于判断一个泛型是否实现了某个trait。例如: + +```plang +trait TestTrait { + fn test(); +} + +struct TestStruct{}; + +impl TestTrait for TestStruct { + fn test() { + println("test"); + } +} + +fn test(x: T) T { + let y = x impl TestTrait?; + let z = x impl TestTrait!; + z.test(); + return x; +} + +``` + +普通的`impl`语句必须带上`?`或`!`标注,否则编译器会报错。 + +对于`?`标注,如果泛型类型没有实现trait,那么语句会返回`false`,否则返回`true`。 + +对于`!`标注,如果泛型类型没有实现trait,那么语句会导致运行时错误(__impl_panic)。以上方例子举例,如果`x`没有实现`TestTrait`,那么`let z = x impl TestTrait!;`会导致运行时错误。反之,如果`x`实现了`TestTrait`,`z`将会是是一个特殊的`T`类型,但是他的增加了实现`TestTrait`的约束,使得下一行代码可以调用`TestTrait`trait的`test`方法。请注意,虽然`z`的类型和`x`的类型都是`T`,但是他们的约束是不同的,严格来说并不是同一类型。`z`的类型`T`,也不是上下文中的`T`类型。 + +### `if let ... impl ...` 语法 + +`if let ... impl ...` 语法可以用于安全的对泛型类型进行trait实现判断: + +```plang +fn test(x: T) T { + if let y = x impl TestTrait { + y.test(); + } + return x; +} +``` + +他等同于 + +```plang +fn test(x: T) T { + if x impl TestTrait? { + let y = x impl TestTrait!; + y.test(); + } + return x; +} + +``` + + + + + + + + + + + diff --git a/src/ast/compiler.rs b/src/ast/compiler.rs index 84a18cace..df1b9b8b7 100644 --- a/src/ast/compiler.rs +++ b/src/ast/compiler.rs @@ -89,7 +89,7 @@ pub fn compile(db: &dyn Db, docs: MemDocsInput, out: String, op: Options) { } let total_steps = 3; let pb = &COMPILE_PROGRESS; - prepare_prgressbar( + prepare_progressbar( pb, op, format!("{}[{:2}/{:2}]", LOOKING_GLASS, 1, total_steps), @@ -208,7 +208,7 @@ pub fn process_llvm_ir<'a>( let total_steps = 3; let pb = ProgressBar::hidden(); - prepare_prgressbar(&pb, op, format!("{}[{:2}/{:2}]", TRUCK, 2, total_steps)); + prepare_progressbar(&pb, op, format!("{}[{:2}/{:2}]", TRUCK, 2, total_steps)); pb.set_length(mods.len() as u64); let mut set = FxHashSet::default(); @@ -329,7 +329,7 @@ pub fn pl_link(llvmmod: Module, oxbjs: Vec, out: String, op: Options) { let total_steps = 3; let pb = ProgressBar::hidden(); - prepare_prgressbar( + prepare_progressbar( &pb, op, format!("{}[{:2}/{:2}]", CLIP, total_steps, total_steps), diff --git a/src/ast/compiler/progress.rs b/src/ast/compiler/progress.rs index ff4c3fec9..5a8982a9a 100644 --- a/src/ast/compiler/progress.rs +++ b/src/ast/compiler/progress.rs @@ -32,7 +32,7 @@ lazy_static! { } #[cfg(feature = "llvm")] -pub fn prepare_prgressbar(pb: &indicatif::ProgressBar, op: Options, prefix: String) { +pub fn prepare_progressbar(pb: &indicatif::ProgressBar, op: Options, prefix: String) { pb.enable_steady_tick(Duration::from_millis(50)); pb.set_style(PROGRESS_STYLE.clone()); diff --git a/src/ast/ctx/builtins.rs b/src/ast/ctx/builtins.rs index ac9a40ba0..02b335e14 100644 --- a/src/ast/ctx/builtins.rs +++ b/src/ast/ctx/builtins.rs @@ -48,7 +48,8 @@ lazy_static! { mp.insert(usize::MAX - 12, emit_if_arr); mp.insert(usize::MAX - 13, emit_if_union); mp.insert(usize::MAX - 14, emit_arr_slice); - mp.insert(usize::MAX - 16, emit_asm_sp); + mp.insert(usize::MAX - 15, emit_asm_sp); + mp.insert(usize::MAX - 16, emit_is_struct); mp }; pub static ref BUILTIN_FN_SNIPPET_MAP: HashMap = { @@ -91,7 +92,8 @@ lazy_static! { usize::MAX - 14, r#"arr_slice(${1:from}, ${2:start}, ${3:len})$0"#.to_owned(), ); - mp.insert(usize::MAX - 16, r#"asm_sp()$0"#.to_owned()); + mp.insert(usize::MAX - 15, r#"asm_sp()$0"#.to_owned()); + mp.insert(usize::MAX - 16, r#"is_struct<${1:T}>()$0"#.to_owned()); mp }; pub static ref BUILTIN_FN_NAME_MAP: HashMap<&'static str, ValueHandle> = { @@ -110,7 +112,8 @@ lazy_static! { mp.insert("if_arr", usize::MAX - 12); mp.insert("if_union", usize::MAX - 13); mp.insert("arr_slice", usize::MAX - 14); - mp.insert("asm_sp", usize::MAX - 16); + mp.insert("asm_sp", usize::MAX - 15); + mp.insert("is_struct", usize::MAX - 16); mp }; } @@ -219,6 +222,57 @@ fn emit_is_ptr<'a, 'b>( } } +fn emit_is_struct<'a, 'b>( + f: &mut FuncCallNode, + ctx: &'b mut Ctx<'a>, + builder: &'b BuilderEnum<'a, '_>, +) -> NodeResult { + if !f.paralist.is_empty() { + return Err(f + .range + .new_err(crate::ast::diag::ErrorCode::PARAMETER_LENGTH_NOT_MATCH) + .add_to_ctx(ctx)); + } + if f.generic_params.is_none() { + return Err(f + .range + .new_err(crate::ast::diag::ErrorCode::GENERIC_NOT_FOUND) + .add_to_ctx(ctx)); + } + let generic = f.generic_params.as_ref().unwrap(); + generic.emit_highlight(ctx); + if generic.generics.len() != 1 { + return Err(f + .range + .new_err(crate::ast::diag::ErrorCode::GENERIC_NOT_FOUND) + .add_to_ctx(ctx)); + } + if generic.generics[0].is_none() { + return Err(f + .range + .new_err(crate::ast::diag::ErrorCode::GENERIC_NOT_FOUND) + .add_to_ctx(ctx)); + } + let generic = generic.generics[0] + .as_ref() + .unwrap() + .get_type(ctx, builder, true)?; + let binding = get_type_deep(generic); + let binding = binding.borrow(); + match &*binding { + PLType::Struct(_) => { + let b = builder.int_value(&PriType::BOOL, 1, false); + b.new_output(Arc::new(RefCell::new(PLType::Primitive(PriType::BOOL)))) + .to_result() + } + _ => { + let b = builder.int_value(&PriType::BOOL, 0, false); + b.new_output(Arc::new(RefCell::new(PLType::Primitive(PriType::BOOL)))) + .to_result() + } + } +} + fn emit_gc_type<'a, 'b>( f: &mut FuncCallNode, ctx: &'b mut Ctx<'a>, diff --git a/src/ast/diag.rs b/src/ast/diag.rs index 59878fc18..9d2f6dc1d 100644 --- a/src/ast/diag.rs +++ b/src/ast/diag.rs @@ -136,7 +136,7 @@ define_diag!( INVALID_DIRECT_UNION_CAST = "invalid direct union cast", INVALID_DIRECT_TRAIT_CAST = "invalid direct trait cast", UNION_DOES_NOT_CONTAIN_TYPE = "union does not contain type", - INVALID_IS_EXPR = "invalid `is` expression", + INVALID_SRC_TY = "invalid source type", INVALID_CAST = "invalid cast", METHOD_NOT_FOUND = "method not found", DERIVE_TRAIT_NOT_IMPL = "derive trait not impl", @@ -182,6 +182,10 @@ define_diag!( MACRO_EXPANSION_FAILED = "macro expansion failed", SYNTAX_ERROR_FUNC_PARAM = "syntax error: function parameter", ONLY_AWAIT_IN_ASYNC_FN = "await is only expected in async functions", + IF_LET_DOES_NOT_EXPECT_TAIL = "`if let ...` does not expect `?` or `!` tail", + EXPECT_IF_LET_AS = "`if let .. as ...` is expected here", + EXPECT_GENERIC_TYPE = "expect generic type", + EXPECT_TAILING_SYMBOL = "expect tailing symbol `!` or `?`", ); define_diag! { diff --git a/src/ast/expects/test_diag.pi.expect b/src/ast/expects/test_diag.pi.expect index 3209e49cf..af5285018 100644 --- a/src/ast/expects/test_diag.pi.expect +++ b/src/ast/expects/test_diag.pi.expect @@ -43,6 +43,72 @@ }, }, }, + PLDiag { + raw: PLDiagRaw { + code: Err( + REDECLARATION, + ), + help: None, + labels: [], + source: None, + range: Range { + start: Pos { + line: 177, + column: 13, + offset: 1937, + }, + end: Pos { + line: 177, + column: 14, + offset: 1938, + }, + }, + }, + }, + PLDiag { + raw: PLDiagRaw { + code: Err( + REDECLARATION, + ), + help: None, + labels: [], + source: None, + range: Range { + start: Pos { + line: 180, + column: 9, + offset: 1983, + }, + end: Pos { + line: 180, + column: 10, + offset: 1984, + }, + }, + }, + }, + PLDiag { + raw: PLDiagRaw { + code: Err( + REDECLARATION, + ), + help: None, + labels: [], + source: None, + range: Range { + start: Pos { + line: 181, + column: 9, + offset: 2014, + }, + end: Pos { + line: 181, + column: 10, + offset: 2015, + }, + }, + }, + }, PLDiag { raw: PLDiagRaw { code: Err( @@ -87,6 +153,72 @@ }, }, }, + PLDiag { + raw: PLDiagRaw { + code: Err( + TYPE_CANNOT_BE_FULLY_INFERRED, + ), + help: None, + labels: [], + source: None, + range: Range { + start: Pos { + line: 179, + column: 9, + offset: 1958, + }, + end: Pos { + line: 179, + column: 10, + offset: 1959, + }, + }, + }, + }, + PLDiag { + raw: PLDiagRaw { + code: Err( + TYPE_CANNOT_BE_FULLY_INFERRED, + ), + help: None, + labels: [], + source: None, + range: Range { + start: Pos { + line: 180, + column: 9, + offset: 1983, + }, + end: Pos { + line: 180, + column: 10, + offset: 1984, + }, + }, + }, + }, + PLDiag { + raw: PLDiagRaw { + code: Err( + TYPE_CANNOT_BE_FULLY_INFERRED, + ), + help: None, + labels: [], + source: None, + range: Range { + start: Pos { + line: 181, + column: 9, + offset: 2014, + }, + end: Pos { + line: 181, + column: 10, + offset: 2015, + }, + }, + }, + }, PLDiag { raw: PLDiagRaw { code: Err( @@ -473,6 +605,28 @@ }, }, }, + PLDiag { + raw: PLDiagRaw { + code: Err( + EXPECT_TRAIT_TYPE, + ), + help: None, + labels: [], + source: None, + range: Range { + start: Pos { + line: 179, + column: 20, + offset: 1969, + }, + end: Pos { + line: 179, + column: 23, + offset: 1972, + }, + }, + }, + }, PLDiag { raw: PLDiagRaw { code: Err( @@ -730,10 +884,10 @@ PLDiag { raw: PLDiagRaw { code: Err( - INVALID_IS_EXPR, + INVALID_SRC_TY, ), help: Some( - "`is` can only be used on union or trait types", + "Only expect union or generic or trait type", ), labels: [], source: None, @@ -751,6 +905,30 @@ }, }, }, + PLDiag { + raw: PLDiagRaw { + code: Err( + INVALID_SRC_TY, + ), + help: Some( + "Only expect union or generic or trait type", + ), + labels: [], + source: None, + range: Range { + start: Pos { + line: 172, + column: 16, + offset: 1840, + }, + end: Pos { + line: 172, + column: 24, + offset: 1848, + }, + }, + }, + }, PLDiag { raw: PLDiagRaw { code: Err( @@ -1308,6 +1486,98 @@ }, }, }, + PLDiag { + raw: PLDiagRaw { + code: Err( + IF_LET_DOES_NOT_EXPECT_TAIL, + ), + help: Some( + "remove the tailling symbol", + ), + labels: [], + source: None, + range: Range { + start: Pos { + line: 176, + column: 24, + offset: 1921, + }, + end: Pos { + line: 176, + column: 25, + offset: 1922, + }, + }, + }, + }, + PLDiag { + raw: PLDiagRaw { + code: Err( + EXPECT_IF_LET_AS, + ), + help: Some( + "adding as expression might be a solution", + ), + labels: [], + source: None, + range: Range { + start: Pos { + line: 169, + column: 8, + offset: 1798, + }, + end: Pos { + line: 169, + column: 17, + offset: 1807, + }, + }, + }, + }, + PLDiag { + raw: PLDiagRaw { + code: Err( + EXPECT_GENERIC_TYPE, + ), + help: None, + labels: [], + source: None, + range: Range { + start: Pos { + line: 180, + column: 13, + offset: 1987, + }, + end: Pos { + line: 180, + column: 14, + offset: 1988, + }, + }, + }, + }, + PLDiag { + raw: PLDiagRaw { + code: Err( + EXPECT_TAILING_SYMBOL, + ), + help: None, + labels: [], + source: None, + range: Range { + start: Pos { + line: 181, + column: 13, + offset: 2018, + }, + end: Pos { + line: 181, + column: 29, + offset: 2034, + }, + }, + }, + }, PLDiag { raw: PLDiagRaw { code: Warn( @@ -1492,6 +1762,52 @@ }, }, }, + PLDiag { + raw: PLDiagRaw { + code: Warn( + UNUSED_VARIABLE, + ), + help: None, + labels: [ + PLLabel { + file: u!(""), + txt: Some( + ( + u!("Unused variable `{}`"), + [ + u!("a"), + ], + ), + ), + range: Range { + start: Pos { + line: 172, + column: 12, + offset: 1836, + }, + end: Pos { + line: 172, + column: 13, + offset: 1837, + }, + }, + }, + ], + source: None, + range: Range { + start: Pos { + line: 172, + column: 12, + offset: 1836, + }, + end: Pos { + line: 172, + column: 13, + offset: 1837, + }, + }, + }, + }, PLDiag { raw: PLDiagRaw { code: Warn( @@ -1630,6 +1946,52 @@ }, }, }, + PLDiag { + raw: PLDiagRaw { + code: Warn( + UNUSED_VARIABLE, + ), + help: None, + labels: [ + PLLabel { + file: u!(""), + txt: Some( + ( + u!("Unused variable `{}`"), + [ + u!("b"), + ], + ), + ), + range: Range { + start: Pos { + line: 179, + column: 9, + offset: 1958, + }, + end: Pos { + line: 179, + column: 10, + offset: 1959, + }, + }, + }, + ], + source: None, + range: Range { + start: Pos { + line: 179, + column: 9, + offset: 1958, + }, + end: Pos { + line: 179, + column: 10, + offset: 1959, + }, + }, + }, + }, PLDiag { raw: PLDiagRaw { code: Warn( diff --git a/src/ast/fmt.rs b/src/ast/fmt.rs index 3d6e1723a..652d53f79 100644 --- a/src/ast/fmt.rs +++ b/src/ast/fmt.rs @@ -2,7 +2,7 @@ use crate::{ast::node::Num, utils::read_config::enter}; use super::{ node::{ - cast::{AsNode, IsNode}, + cast::{AsNode, ImplCastNode, IsNode}, comment::CommentNode, control::{BreakNode, ContinueNode, ForNode, IfNode, Literal, MatchNode, WhileNode}, error::{ErrorNode, StErrorNode}, @@ -853,6 +853,23 @@ impl FmtBuilder { // 顶层节点加空格 self.enter(); } + + pub fn parse_impl_cast_node(&mut self, node: &ImplCastNode) { + node.expr.format(self); + self.space(); + self.token("impl"); + self.space(); + match node.tail { + Some((TokenType::NOT, _)) => { + self.token("!"); + } + Some((TokenType::QUESTION, _)) => { + self.token("?"); + } + None => {} + _ => unreachable!(), + } + } pub fn parse_match_node(&mut self, node: &MatchNode) { self.token("match"); self.space(); diff --git a/src/ast/node/cast.rs b/src/ast/node/cast.rs index 7e58b9dee..cf5a0cda2 100644 --- a/src/ast/node/cast.rs +++ b/src/ast/node/cast.rs @@ -1,6 +1,6 @@ use std::{cell::RefCell, sync::Arc}; -use super::super::builder::IntPredicate; +use super::{super::builder::IntPredicate, interface::MultiTraitNode}; use internal_macro::node; use ustr::ustr; @@ -88,12 +88,31 @@ impl<'a, 'ctx> Ctx<'a> { node: &AsNode, ) -> Result<(ValueHandle, Arc>), PLDiag> { let target_rc = target_ty.clone(); - match (ty, &*target_ty.clone().borrow()) { - (PLType::Primitive(tyi), PLType::Primitive(target_ty)) => { + match (ty, &*target_ty.clone().borrow(), node.tail) { + (PLType::Generic(g), _, Some((TokenType::NOT, _))) => { + if let Some(tp) = g.curpltype.clone() { + let inner = get_type_deep(tp); + if target_ty == inner { + Ok((val, target_ty)) + } else { + let re = builder.alloc("cast_result", &target_ty.borrow(), self, None); + let cp = builder.get_or_insert_helper_fn_handle("__cast_panic"); + builder.build_call(cp, &[], &PLType::Void, self, None); + Ok((re, target_ty)) + } + } else { + // alloc a result to prevent llvm error + let re = builder.alloc("cast_result", &target_ty.borrow(), self, None); + let cp = builder.get_or_insert_helper_fn_handle("__cast_panic"); + builder.build_call(cp, &[], &PLType::Void, self, None); + Ok((re, target_ty)) + } + } + (PLType::Primitive(tyi), PLType::Primitive(target_ty), _) => { let val = builder.try_load2var(node.expr.range(), val, ty, self)?; Ok((builder.cast_primitives(val, tyi, target_ty), target_rc)) } - (PLType::Union(union), target_ty) => { + (PLType::Union(union), target_ty, _) => { if node.tail.is_none() { let pos = node.target_type.range().end; let end_range = Range { @@ -142,7 +161,7 @@ impl<'a, 'ctx> Ctx<'a> { .add_to_ctx(self)) } } - (PLType::Trait(t), target_ty) if !matches!(target_ty, PLType::Trait(_)) => { + (PLType::Trait(t), target_ty, _) if !matches!(target_ty, PLType::Trait(_)) => { if node.tail.is_none() { let pos = node.target_type.range().end; let end_range = Range { @@ -513,6 +532,39 @@ impl Node for IsNode { let binding = v.get_ty(); let tp = &*binding.borrow(); match tp { + PLType::Generic(g) => { + if let Some(tp) = g.curpltype.clone() { + let inner = get_type_deep(tp); + if target_tp == inner { + builder + .int_value(&PriType::BOOL, 1, false) + .new_output( + ctx.get_type(&"bool".into(), Default::default()) + .unwrap() + .typ, + ) + .to_result() + } else { + builder + .int_value(&PriType::BOOL, 0, false) + .new_output( + ctx.get_type(&"bool".into(), Default::default()) + .unwrap() + .typ, + ) + .to_result() + } + } else { + builder + .int_value(&PriType::BOOL, 0, false) + .new_output( + ctx.get_type(&"bool".into(), Default::default()) + .unwrap() + .typ, + ) + .to_result() + } + } PLType::Union(u) => { if let Some(tag) = u.has_type(&target_tp.borrow(), ctx, builder) { let tag_v = builder.build_struct_gep(val, 0, "tag", tp, ctx).unwrap(); @@ -575,8 +627,8 @@ impl Node for IsNode { } _ => Err(self .range() - .new_err(ErrorCode::INVALID_IS_EXPR) - .add_help("`is` can only be used on union or trait types") + .new_err(ErrorCode::INVALID_SRC_TY) + .add_help("Only expect union or generic or trait type") .add_to_ctx(ctx)), } } @@ -590,3 +642,101 @@ impl PrintTrait for IsNode { self.target_type.print(tabs + 1, true, line.clone()); } } + +#[node] +pub struct ImplCastNode { + /// expr is the expression which calculates a value as output + pub expr: Box, + /// target_type refers the desired type for the expr + pub target_type: Box, + + pub tail: Option<(TokenType, Range)>, +} + +impl Node for ImplCastNode { + fn emit<'a, 'b>( + &mut self, + ctx: &'b mut Ctx<'a>, + builder: &'b BuilderEnum<'a, '_>, + ) -> NodeResult { + let e = self.expr.emit(ctx, builder); + self.target_type.emit_highlight(ctx); + let t = self.target_type.get_types(ctx, builder)?; + let e = e?; + let v = e.get_value().unwrap(); + let ori_ty = v.get_ty(); + let ty = get_type_deep(ori_ty.clone()); + let mut satisfy_bound = true; + for t in &t { + if let PLType::Trait(trait_ty) = &*t.borrow() { + if !ty.borrow().implements_trait(trait_ty, ctx) { + satisfy_bound = false; + break; + } + } else { + return Err(self + .range() + .new_err(ErrorCode::EXPECT_TRAIT_TYPE) + .add_to_ctx(ctx)); + } + } + match self.tail { + Some((TokenType::QUESTION, _)) => builder + .int_value(&PriType::BOOL, satisfy_bound as _, false) + .new_output( + ctx.get_type(&"bool".into(), Default::default()) + .unwrap() + .typ, + ) + .to_result(), + Some((TokenType::NOT, _)) => { + if let PLType::Generic(g) = &*ori_ty.borrow() { + if !satisfy_bound { + let cp = builder.get_or_insert_helper_fn_handle("__cast_panic"); + builder.build_call(cp, &[], &PLType::Void, ctx, None); + } + let mut new_g = g.clone(); + new_g + .trait_impl + .as_mut() + .map(|t| { + t.merge(&self.target_type); + }) + .or_else(|| { + new_g.trait_impl = Some((*self.target_type).clone()); + Some(()) + }); + if let Some(g) = new_g.curpltype.clone() { + if let PLType::PlaceHolder(_) = &*g.borrow() { + new_g.set_type(new_g.set_place_holder(ctx, builder)); + } + } + v.get_value() + .new_output(Arc::new(RefCell::new(PLType::Generic(new_g)))) + .to_result() + } else { + Err(self + .expr + .range() + .new_err(ErrorCode::EXPECT_GENERIC_TYPE) + .add_to_ctx(ctx)) + } + } + None => Err(self + .range() + .new_err(ErrorCode::EXPECT_TAILING_SYMBOL) + .add_to_ctx(ctx)), + _ => unreachable!(), + } + } +} + +impl PrintTrait for ImplCastNode { + fn print(&self, tabs: usize, end: bool, mut line: Vec) { + deal_line(tabs, &mut line, end); + tab(tabs, line.clone(), end); + println!("ImplNode"); + self.expr.print(tabs + 1, false, line.clone()); + // self.target_type.print(tabs + 1, true, line.clone()); + } +} diff --git a/src/ast/node/control.rs b/src/ast/node/control.rs index 3175cb8bf..7229cf539 100644 --- a/src/ast/node/control.rs +++ b/src/ast/node/control.rs @@ -5,6 +5,7 @@ use crate::ast::builder::{BlockHandle, IntPredicate, ValueHandle}; use crate::ast::ctx::Ctx; use crate::ast::diag::ErrorCode; use crate::ast::pltype::{PriType, STType}; +use crate::ast::tokens::TokenType; use crate::ast::traits::CustomType; use crate::format_label; use crate::inference::unknown_arc; @@ -55,30 +56,98 @@ impl Node for IfNode { let merge_block = builder.append_basic_block(ctx.function.unwrap(), "if.after"); builder.build_unconditional_branch(cond_block); ctx.position_at_end(cond_block, builder); - + let mut gen_def = None; let cond_range = self.cond.range(); - _ = self.cond.emit(ctx, builder).and_then(|o| { - let cond_val = o.get_value(); - check_bool( - &cond_val, - ctx, - cond_range, - ErrorCode::IF_CONDITION_MUST_BE_BOOL, - )?; - - let v = cond_val.unwrap(); - let cond = v.get_value(); - let cond = ctx.try_load2var(cond_range, cond, builder, &v.get_ty().borrow())?; - let cond = builder.build_int_truncate(cond, &PriType::BOOL, "trunctemp"); - builder.build_conditional_branch(cond, then_block, else_block); - Ok(()) - }); + if let NodeEnum::Def(def) = &*self.cond { + // check if it is a `let ... = ... as ...` + if let Some(e) = &def.value_expression { + if let NodeEnum::AsNode(a) = &**e { + if let Some((_, r)) = &a.tail { + // tail not allowed in `if let .. as ..` + ctx.add_diag( + r.new_err(ErrorCode::IF_LET_DOES_NOT_EXPECT_TAIL) + .add_help("remove the tailling symbol") + .clone(), + ); + } + let mut transformed_is = NodeEnum::IsNode(IsNode { + expr: a.expr.clone(), + target_type: a.target_type.clone(), + range: a.range(), + }); + build_cond( + &mut transformed_is, + ctx, + builder, + cond_range, + then_block, + else_block, + ); + ctx.position_at_end(then_block, builder); + let transformed_as = NodeEnum::AsNode(AsNode { + expr: a.expr.clone(), + target_type: a.target_type.clone(), + range: a.range(), + tail: Some((TokenType::NOT, Default::default())), + }); + let mut def = def.clone(); + def.value_expression = Some(Box::new(transformed_as)); + gen_def = Some(def); + } else if let NodeEnum::ImplCastNode(a) = &**e { + if let Some((_, r)) = &a.tail { + // tail not allowed in `if let .. impl ..` + ctx.add_diag( + r.new_err(ErrorCode::IF_LET_DOES_NOT_EXPECT_TAIL) + .add_help("remove the tailling symbol") + .clone(), + ); + } + let mut transformed_is = NodeEnum::ImplCastNode(ImplCastNode { + expr: a.expr.clone(), + target_type: a.target_type.clone(), + range: a.range(), + tail: Some((TokenType::QUESTION, Default::default())), + }); + build_cond( + &mut transformed_is, + ctx, + builder, + cond_range, + then_block, + else_block, + ); + ctx.position_at_end(then_block, builder); + let transformed_as = NodeEnum::ImplCastNode(ImplCastNode { + expr: a.expr.clone(), + target_type: a.target_type.clone(), + range: a.range(), + tail: Some((TokenType::NOT, Default::default())), + }); + let mut def = def.clone(); + def.value_expression = Some(Box::new(transformed_as)); + gen_def = Some(def); + } + } + if gen_def.is_none() { + def.range() + .new_err(ErrorCode::EXPECT_IF_LET_AS) + .add_help("adding as expression might be a solution") + .add_to_ctx(ctx); + } + } else { + let cond = &mut *self.cond; + build_cond(cond, ctx, builder, cond_range, then_block, else_block); + } // emit the else logic into the then block ctx.position_at_end(then_block, builder); // emit the code inside a child context because it belongs to a sub-block - let then_terminator = self.then.emit_child(ctx, builder)?.get_term(); + let mut child = ctx.new_child(self.then.range().start, builder); + if let Some(mut def) = gen_def { + def.emit(&mut child, builder)?; + } + let then_terminator = self.then.emit(&mut child, builder)?.get_term(); if then_terminator.is_none() { // there is no terminator(like return, yield and so forth) in the statement // create an unconditional branch to merge block to finish off the "then" block @@ -119,6 +188,33 @@ impl Node for IfNode { // ANCHOR_END: emit } +fn build_cond<'a>( + cond: &mut NodeEnum, + ctx: &mut Ctx<'a>, + builder: &BuilderEnum<'a, '_>, + cond_range: Range, + then_block: usize, + else_block: usize, +) { + _ = cond.emit(ctx, builder).and_then(|o| { + let cond_val = o.get_value(); + check_bool( + &cond_val, + ctx, + cond_range, + ErrorCode::IF_CONDITION_MUST_BE_BOOL, + )?; + + let v = cond_val.unwrap(); + let cond = v.get_value(); + let cond = ctx.try_load2var(cond_range, cond, builder, &v.get_ty().borrow())?; + let cond = builder.build_int_truncate(cond, &PriType::BOOL, "trunctemp"); + + builder.build_conditional_branch(cond, then_block, else_block); + Ok(()) + }); +} + /// # check_bool /// /// it ensures the input NodeValue represents a [PriType::BOOL], diff --git a/src/ast/node/interface.rs b/src/ast/node/interface.rs index 5475520e7..5cf2b9ac8 100644 --- a/src/ast/node/interface.rs +++ b/src/ast/node/interface.rs @@ -17,6 +17,9 @@ pub struct MultiTraitNode { pub traits: Vec>, } impl MultiTraitNode { + pub fn merge(&mut self, other: &MultiTraitNode) { + self.traits.extend(other.traits.clone()); + } pub fn emit_highlight(&self, ctx: &mut Ctx) { for t in &self.traits { t.emit_highlight(ctx); diff --git a/src/ast/node/mod.rs b/src/ast/node/mod.rs index f177e924c..d12bfa0a5 100644 --- a/src/ast/node/mod.rs +++ b/src/ast/node/mod.rs @@ -6,6 +6,7 @@ use crate::ast::ctx::Ctx; use crate::ast::builder::BuilderEnum; use crate::ast::builder::IRBuilder; +use cast::ImplCastNode; use enum_dispatch::enum_dispatch; use lsp_types::SemanticTokenType; @@ -142,6 +143,7 @@ pub enum NodeEnum { UnionDefNode(UnionDefNode), AsNode(AsNode), IsNode(IsNode), + ImplCastNode(ImplCastNode), TupleInitNode(TupleInitNode), ClosureNode(ClosureNode), GlobalConstNode(GlobalConstNode), diff --git a/src/lsp/wasm.rs b/src/lsp/wasm.rs index 99c5c382b..93db10a0a 100644 --- a/src/lsp/wasm.rs +++ b/src/lsp/wasm.rs @@ -284,7 +284,7 @@ pub fn get_legend() -> String { #[wasm_bindgen] pub fn get_completions() -> String { - log::error!("get_completions {:#?}", &*COMPLETIONS.inner.borrow()); + log::trace!("get_completions {:#?}", &*COMPLETIONS.inner.borrow()); return serde_json::to_value(COMPLETIONS.inner.borrow().clone()) .unwrap() .to_string(); diff --git a/src/main.rs b/src/main.rs index b6696d101..f089400b7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -40,7 +40,7 @@ use lsp::mem_docs::{self, MemDocsInput}; #[cfg(not(target_arch = "wasm32"))] use lsp::start_lsp; -use crate::ast::compiler::prepare_prgressbar; +use crate::ast::compiler::prepare_progressbar; fn main() { #[cfg(target_arch = "wasm32")] @@ -224,7 +224,7 @@ impl Cli { None, ); let pb = &CHECK_PROGRESS; - prepare_prgressbar(pb, op, format!("{}[{:2}/{:2}]", CHECK, 1, 1)); + prepare_progressbar(pb, op, format!("{}[{:2}/{:2}]", CHECK, 1, 1)); if let Err(e) = compile_dry(&db, mem) { pb.abandon_with_message("check failed X".to_string().red().to_string()); eprintln!("\n Fatal error: {}", e.to_string().red()); diff --git a/src/nomparser/cast.rs b/src/nomparser/cast.rs index b8a8cddbc..027b08cb7 100644 --- a/src/nomparser/cast.rs +++ b/src/nomparser/cast.rs @@ -1,11 +1,11 @@ use crate::ast::{ - node::cast::{AsNode, IsNode}, + node::cast::{AsNode, ImplCastNode, IsNode}, tokens::TokenType, }; use internal_macro::test_parser; use nom::{ branch::alt, - combinator::{map_res, not, opt, peek}, + combinator::{map, not, opt, peek}, multi::many0, sequence::{pair, terminated, tuple}, IResult, @@ -27,31 +27,43 @@ use super::*; /// ``` #[test_parser("1 as i128 as f32")] #[test_parser("1 as i128 as f32 !")] +#[test_parser("1 as i128! as f32 !")] #[test_parser( "(2.3+10-800*9).add(100)[0] as - i128 as f32" + i128 as f32?" )] pub fn as_exp(input: Span) -> IResult> { - map_res( + map( tuple(( complex_exp, - many0(pair(tag_modifier(TokenType::AS), type_name)), - opt(alt(( - // look the next charactor after "!" to eliminate the "!=" case - terminated( - tag_token_symbol_ex(TokenType::NOT), - peek(not(tag_token(TokenType::ASSIGN))), - ), - tag_token_symbol_ex(TokenType::QUESTION), + many0(tuple(( + tag_modifier(TokenType::AS), + type_name, + opt(alt(( + // look the next charactor after "!" to eliminate the "!=" case + terminated( + tag_token_symbol_ex(TokenType::NOT), + peek(not(tag_token(TokenType::ASSIGN))), + ), + tag_token_symbol_ex(TokenType::QUESTION), + ))), ))), opt(pair(tag_modifier(TokenType::IS), type_name)), + opt(tuple(( + tag_modifier(TokenType::IMPL), + multi_trait, + opt(alt(( + tag_token_symbol_ex(TokenType::NOT), + tag_token_symbol_ex(TokenType::QUESTION), + ))), + ))), )), - |(exp, casts, tail, is)| { + |(exp, casts, is, _impl)| { let mut exp = exp; let start = exp.range().start; // wrap the previous expression into a new 'as' expression - for (_, target_type) in casts { + for (_, target_type, tail) in casts { let range = start.to(target_type.range().end); exp = Box::new(NodeEnum::AsNode(AsNode { @@ -70,7 +82,17 @@ pub fn as_exp(input: Span) -> IResult> { range, })); } - res_box(exp) + if let Some((_impl, traits, tail)) = _impl { + let range = start.to(traits.range().end); + exp = Box::new(NodeEnum::ImplCastNode(ImplCastNode { + expr: exp, + target_type: traits, + range, + tail, + })); + } + + exp }, )(input) } diff --git a/src/nomparser/control.rs b/src/nomparser/control.rs index bcee7649f..ef2c262df 100644 --- a/src/nomparser/control.rs +++ b/src/nomparser/control.rs @@ -58,12 +58,13 @@ pub use _match::*; }" )] pub fn if_statement(input: Span) -> IResult> { - map_res( + map( delspace(tuple(( tag_token_word(TokenType::IF), parse_with_ex( alt(( general_exp, + new_variable, map( match_paired_until( "{", @@ -100,7 +101,7 @@ pub fn if_statement(input: Span) -> IResult> { } else { vec![vec![]] }; - res_enum( + Box::new( IfNode { cond, then: Box::new(then), diff --git a/src/nomparser/expression.rs b/src/nomparser/expression.rs index 421f547f4..e3e720382 100644 --- a/src/nomparser/expression.rs +++ b/src/nomparser/expression.rs @@ -2,7 +2,7 @@ use nom::{ branch::alt, bytes::complete::tag, character::complete::space1, - combinator::{map, map_res, not, opt, peek}, + combinator::{map, not, opt, peek}, multi::{many0, separated_list0}, sequence::{delimited, pair, preceded, terminated, tuple}, IResult, @@ -63,7 +63,7 @@ fn bit_xor(input: Span) -> IResult> { #[test_parser("a&b")] #[test_parser_error("a&&b")] fn bit_and(input: Span) -> IResult> { - delspace(map_res( + delspace(map( tuple(( bit_move, many0(tuple(( @@ -106,7 +106,7 @@ pub fn unary_exp(input: Span) -> IResult> { // todo: consider to aligh the implementation with UnaryExp EBNF delspace(alt(( pointer_exp, - map_res( + map( tuple(( alt(( tag_token_symbol(TokenType::MINUS), @@ -121,7 +121,8 @@ pub fn unary_exp(input: Span) -> IResult> { )), |((op, op_range), exp)| { let range = op_range.start.to(exp.range().end); - res_enum( + + Box::new( UnaryOpNode { op: (op, op_range), exp, @@ -137,7 +138,7 @@ pub fn unary_exp(input: Span) -> IResult> { #[test_parser("&&a{}.d")] #[test_parser("***ad")] pub fn pointer_exp(input: Span) -> IResult> { - map_res( + map( delspace(pair( many0(alt(( tag_token_symbol(TokenType::TAKE_PTR), @@ -166,15 +167,15 @@ pub fn pointer_exp(input: Span) -> IResult> { .into(), ); } - res_box(exp) + exp }, )(input) } #[test_parser("adasda::c!(saddsada)")] fn macro_call_exp(input: Span) -> IResult> { - map_res(pair(extern_identifier, macro_call_op), |(name, op)| { - res_enum( + map(pair(extern_identifier, macro_call_op), |(name, op)| { + Box::new( MacroCallNode { range: name.range(), callee: name, @@ -192,7 +193,7 @@ fn macro_call_exp(input: Span) -> IResult> { #[test_parser("a{}")] #[test_parser("1.xxx()")] pub fn complex_exp(input: Span) -> IResult> { - map_res( + map( pair( primary_exp, many0(alt((take_exp_op, array_element_op, call_function_op))), @@ -251,13 +252,13 @@ pub fn complex_exp(input: Span) -> IResult> { } } } - res_enum(*res) + res }, )(input) } fn primary_exp(input: Span) -> IResult> { - delspace(map_res( + delspace(map( tuple(( many0(comment), alt(( @@ -276,7 +277,8 @@ fn primary_exp(input: Span) -> IResult> { )), |(lcoms, node, rcoms)| { let range = node.range(); - res_enum( + + Box::new( PrimaryNode { value: node, comments: vec![lcoms, rcoms], @@ -290,7 +292,7 @@ fn primary_exp(input: Span) -> IResult> { #[test_parser(".0")] fn take_exp_op(input: Span) -> IResult>)> { - delspace(map_res( + delspace(map( preceded( tag_token_symbol(TokenType::DOT), pair( @@ -298,21 +300,21 @@ fn take_exp_op(input: Span) -> IResult>)> { many0(comment), ), ), - |(idx, coms)| Ok::<_, ()>((ComplexOp::Field(idx), coms)), + |(idx, coms)| (ComplexOp::Field(idx), coms), ))(input) } #[test_parser("(a)")] #[test_parser("(a+a*b/c)")] fn parantheses_exp(input: Span) -> IResult> { - map_res( + map( delimited( tag_token_symbol(TokenType::LPAREN), parse_with_ex(general_exp, false), tag_token_symbol(TokenType::RPAREN), ), |exp| { - res_enum( + Box::new( ParanthesesNode { range: exp.range(), node: exp, @@ -328,7 +330,7 @@ fn parantheses_exp(input: Span) -> IResult> { #[test_parser("|a| =>{return a;}")] #[test_parser("async |a| =>{return a;}")] fn closure(input: Span) -> IResult> { - map_res( + map( tuple(( tuple(( opt(tag_modifier(TokenType::ASYNC_MARKER)), @@ -348,7 +350,8 @@ fn closure(input: Span) -> IResult> { )), |((modi, (_, sr), args, _), _, ret, body)| { let range = sr.start.to(body.range().end); - res_enum( + + Box::new( ClosureNode { range, paralist: args, diff --git a/src/nomparser/helper.rs b/src/nomparser/helper.rs index cc4886a5b..03a734190 100644 --- a/src/nomparser/helper.rs +++ b/src/nomparser/helper.rs @@ -5,7 +5,7 @@ use crate::{ast::range::Range, ast::tokens::TokenType}; use nom::branch::alt; use nom::character::complete::space1; use nom::character::is_alphanumeric; -use nom::combinator::{opt, recognize}; +use nom::combinator::{map, opt, recognize}; use nom::error::FromExternalError; use nom::multi::many0; @@ -146,7 +146,7 @@ pub fn res_box(i: Box) -> Result, ()> { Ok::<_, ()>(i) } -pub fn create_bin((mut left, rights): PLBin) -> Result, ()> { +pub fn create_bin((mut left, rights): PLBin) -> Box { for ((op, op_range), right) in rights { let range = left.range().start.to(right.range().end); @@ -160,7 +160,7 @@ pub fn create_bin((mut left, rights): PLBin) -> Result, ()> { .into(), ); } - res_box(left) + left } type PLBin = (Box, Vec<((TokenType, Range), Box)>); @@ -173,7 +173,7 @@ where { alt(( terminated(p, tag_token_symbol(TokenType::SEMI)), - map_res( + map( tuple(( p2, recognize(many0(alt(( @@ -186,7 +186,7 @@ where |(node, e)| { let range = node.range(); let r = Range::new(&e, &e); - res_enum( + Box::new( StErrorNode { range, st: node, diff --git a/src/nomparser/identifier.rs b/src/nomparser/identifier.rs index 9027cf67f..ef3b35aae 100644 --- a/src/nomparser/identifier.rs +++ b/src/nomparser/identifier.rs @@ -2,7 +2,7 @@ use nom::{ branch::alt, bytes::complete::tag, character::complete::{alpha1, alphanumeric1, one_of}, - combinator::{map_res, opt, recognize}, + combinator::{map, map_res, opt, recognize}, multi::{many0_count, many1, separated_list1}, sequence::{pair, tuple}, IResult, InputTake, @@ -36,7 +36,7 @@ use super::*; #[test_parser("a:")] #[test_parser("a::")] pub fn extern_identifier(input: Span) -> IResult> { - delspace(map_res( + delspace(map( tuple(( separated_list1( tag_token_symbol(TokenType::DOUBLE_COLON), @@ -57,9 +57,8 @@ pub fn extern_identifier(input: Span) -> IResult> { range = range.start.to(opt2.1.end); } identifier_with_namespace.pop(); - res_enum( + Box::new( ExternIdNode { - // after poping, only namespaces are left namespace: identifier_with_namespace, id: lastid, range, diff --git a/src/nomparser/macros.rs b/src/nomparser/macros.rs index 033e934db..eae7697dc 100644 --- a/src/nomparser/macros.rs +++ b/src/nomparser/macros.rs @@ -23,7 +23,7 @@ macro_rules! del_newline_or_space { /// the exp type separated by one of ops. macro_rules! parse_bin_ops { ($exp:ident, $($op:ident),*) => { - delspace(map_res( + delspace(map( tuple(( $exp, many0(tuple(( diff --git a/src/nomparser/types.rs b/src/nomparser/types.rs index e926426d3..a7575e664 100644 --- a/src/nomparser/types.rs +++ b/src/nomparser/types.rs @@ -13,6 +13,7 @@ use crate::{ }, }; use internal_macro::{test_parser, test_parser_error}; +use nom::combinator::map; use nom::multi::separated_list0; use nom::sequence::{preceded, terminated}; use nom::{ @@ -214,8 +215,8 @@ pub fn trait_bound(input: Span) -> IResult> { } pub fn multi_trait(input: Span) -> IResult> { - map_res(type_add, |traits| { - res_box(Box::new(MultiTraitNode { + map(type_add, |traits| { + Box::new(MultiTraitNode { range: traits .first() .unwrap() @@ -223,7 +224,7 @@ pub fn multi_trait(input: Span) -> IResult> { .start .to(traits.last().unwrap().range().end), traits, - })) + }) })(input) } diff --git a/test/lsp_diag/test_diag.pi b/test/lsp_diag/test_diag.pi index 5ee0a697a..2327f321f 100644 --- a/test/lsp_diag/test_diag.pi +++ b/test/lsp_diag/test_diag.pi @@ -165,3 +165,19 @@ fn test_f_diag3(a, b:i64) void { return; } +fn test_if_let_diag() void { + if let a = 1 { + + } + if let a = 1 as i64 { + + } + let d= 1234 as Option; + if let a = d as i64! { + let a = a; + } + let b = d impl i64!; + let b = d impl TestTrait!; + let b = d impl TestTrait; + return; +} diff --git a/test/main.pi b/test/main.pi index b59269808..f80e157a8 100644 --- a/test/main.pi +++ b/test/main.pi @@ -79,7 +79,10 @@ async fn main() Task<()> { await std_test::test_nested_async_closure_in_normal_f2(); await std_test::test_delay(); + return (); } + + diff --git a/test/test/compiletime_reflection.pi b/test/test/compiletime_reflection.pi index 6b119b1a0..6197ca20c 100644 --- a/test/test/compiletime_reflection.pi +++ b/test/test/compiletime_reflection.pi @@ -12,9 +12,48 @@ pub fn test_compile_time_reflection() void { assert(n.dd); // println!(n, "你好", true, 10); io::print_s("\n"); + let a = test_s{}; + + let b = a impl test_t?; + + panic::assert(b); + + test_g(a); return; } +trait test_t { + fn dosth(); +} + +struct test_s { + +} + +impl test_t for test_s { + fn dosth() { + + return ; + } +} + +fn test_g(a:T) void { + if a impl test_t? { + let b = a impl test_t!; + b.dosth(); + } else { + panic::pl_panic(); + } + if let b = a impl test_t { + b.dosth(); + }else { + panic::pl_panic(); + } + + return; +} + + fn name(t:*T) void { let s = fullnameof(); io::print_s(s); diff --git a/test/test/ifel.pi b/test/test/ifel.pi index 57ac49de0..eb8b1e8f9 100644 --- a/test/test/ifel.pi +++ b/test/test/ifel.pi @@ -1,12 +1,36 @@ +use core::panic; + pub fn test_if_else() void { test_if_only(true); test_if_else_ret(true); test_if_ret_only(true); test_else_ret_only(true); test_not_ret(true); + test_if_let(23456); + return; +} +fn test_if_let(t:T) void { + let d= 1234 as Option; + if let a = d as i64 { + panic::assert(a == 1234); + }else { + panic::pl_panic(); + } + + if false { + panic::pl_panic(); + } else if let tt = t as i64 { + panic::assert(tt == 23456); + } else { + panic::pl_panic(); + } + return; } + + + fn test_not_equal() void { if 1 != 2 { return; @@ -52,3 +76,4 @@ pub fn test_not_ret(judge: bool) void { return; } +