diff --git a/src/ast/builder/llvmbuilder.rs b/src/ast/builder/llvmbuilder.rs index 33d3187a4..f48d43349 100644 --- a/src/ast/builder/llvmbuilder.rs +++ b/src/ast/builder/llvmbuilder.rs @@ -222,8 +222,8 @@ impl<'a, 'ctx> LLVMBuilder<'a, 'ctx> { size_val, ) .unwrap(); - if let PLType::Struct(_) = pltype { - let f = self.get_or_insert_st_visit_fn_handle(&p); + if let PLType::Struct(tp) = pltype { + let f = self.get_or_insert_st_visit_fn_handle(&p, tp); let i = self.builder.build_ptr_to_int( f.as_global_value().as_pointer_value(), self.context.i64_type(), @@ -526,10 +526,13 @@ impl<'a, 'ctx> LLVMBuilder<'a, 'ctx> { PriType::BOOL => self.context.bool_type().as_basic_type_enum(), } } - fn get_or_insert_st_visit_fn_handle(&self, p: &PointerValue<'ctx>) -> FunctionValue<'ctx> { + fn get_or_insert_st_visit_fn_handle( + &self, + p: &PointerValue<'ctx>, + st: &STType, + ) -> FunctionValue<'ctx> { let ptrtp = p.get_type(); - let ty = ptrtp.get_element_type().into_struct_type(); - let llvmname = ty.get_name().unwrap().to_str().unwrap().to_string() + "@"; + let llvmname = st.get_st_full_name() + "@"; if let Some(v) = self.module.get_function(&llvmname) { return v; } @@ -948,32 +951,37 @@ impl<'a, 'ctx> LLVMBuilder<'a, 'ctx> { self.module .add_function(&llvmname, fn_type, Some(Linkage::External)) } + fn get_fields(&self, pltp: &STType, ctx: &mut Ctx<'a>) -> Vec { + ctx.run_in_type_mod(pltp, |ctx, pltp| { + pltp.get_all_field() + .iter() + .map(|order_field| { + self.get_basic_type_op( + &order_field + .typenode + .get_type(ctx, &self.clone().into()) + .unwrap() + .borrow(), + ctx, + ) + .unwrap() + }) + .collect::>() + }) + } + fn struct_type(&self, pltp: &STType, ctx: &mut Ctx<'a>) -> StructType<'ctx> { let st = self.module.get_struct_type(&pltp.get_st_full_name()); if let Some(st) = st { return st; } + + if pltp.is_tuple { + let fields = &self.get_fields(pltp, ctx); + return self.context.struct_type(fields, false); + } let st = self.context.opaque_struct_type(&pltp.get_st_full_name()); - ctx.run_in_type_mod(pltp, |ctx, pltp| { - st.set_body( - &pltp - .get_all_field() - .iter() - .map(|order_field| { - self.get_basic_type_op( - &order_field - .typenode - .get_type(ctx, &self.clone().into()) - .unwrap() - .borrow(), - ctx, - ) - .unwrap() - }) - .collect::>(), - false, - ); - }); + st.set_body(&self.get_fields(pltp, ctx), false); st } @@ -1716,11 +1724,9 @@ impl<'a, 'ctx> IRBuilder<'a, 'ctx> for LLVMBuilder<'a, 'ctx> { let ptrtp = self.struct_type(v, ctx).ptr_type(AddressSpace::default()); let ty = ptrtp.get_element_type().into_struct_type(); let ftp = self.mark_fn_tp(ptrtp); - let f = self.module.add_function( - &(ty.get_name().unwrap().to_str().unwrap().to_string() + "@"), - ftp, - None, - ); + let f = self + .module + .add_function(&(v.get_st_full_name() + "@"), ftp, None); let bb = self.context.append_basic_block(f, "entry"); self.builder.position_at_end(bb); let fieldn = ty.count_fields(); diff --git a/src/ast/ctx.rs b/src/ast/ctx.rs index 9c359f140..0c2630d2c 100644 --- a/src/ast/ctx.rs +++ b/src/ast/ctx.rs @@ -633,6 +633,9 @@ impl<'a, 'ctx> Ctx<'a> { } pub fn send_if_go_to_def(&self, range: Range, destrange: Range, file: String) { + if range == Default::default() { + return; + } self.plmod.defs.borrow_mut().insert( range, LSPDef::Scalar(Location { diff --git a/src/ast/diag.rs b/src/ast/diag.rs index 490e65c3e..f3fe6c3c4 100644 --- a/src/ast/diag.rs +++ b/src/ast/diag.rs @@ -7,6 +7,7 @@ use rustc_hash::FxHashMap; use std::{ collections::HashMap, fmt::{Display, Formatter}, + process::exit, }; macro_rules! define_error { ($( @@ -476,13 +477,14 @@ pub(crate) fn handle_errors(db: &dyn Db, docs: MemDocsInput) { format!("compile failed: there is {} error", errs_num).bright_red() ); println!("{}", dot::ERROR); - return; + exit(1); } log::error!( "{}", format!("compile failed: there are {} errors", errs_num).bright_red() ); println!("{}", dot::TOOMANYERROR); + exit(1); } } } diff --git a/src/ast/fmt.rs b/src/ast/fmt.rs index f132547e9..f1ac87f76 100644 --- a/src/ast/fmt.rs +++ b/src/ast/fmt.rs @@ -22,6 +22,7 @@ use super::{ ret::RetNode, statement::{AssignNode, DefNode, EmptyNode, StatementsNode}, string_literal::StringNode, + tuple::{TupleInitNode, TupleTypeNode}, types::{ ArrayInitNode, ArrayTypeNameNode, GenericDefNode, GenericParamNode, PointerTypeNode, StructDefNode, StructInitFieldNode, StructInitNode, TypeNameNode, TypedIdentifierNode, @@ -745,4 +746,26 @@ impl FmtBuilder { self.space(); node.ty.format(self); } + pub fn parse_tuple_init_node(&mut self, node: &TupleInitNode) { + self.l_paren(); + for (i, expr) in node.exprs.iter().enumerate() { + if i > 0 { + self.comma(); + self.space(); + } + expr.format(self); + } + self.r_paren(); + } + pub fn parse_tuple_type_node(&mut self, node: &TupleTypeNode) { + self.l_paren(); + for (i, ty) in node.tps.iter().enumerate() { + if i > 0 { + self.comma(); + self.space(); + } + ty.format(self); + } + self.r_paren(); + } } diff --git a/src/ast/node/interface.rs b/src/ast/node/interface.rs index 3a29980ea..5bf048c2e 100644 --- a/src/ast/node/interface.rs +++ b/src/ast/node/interface.rs @@ -41,6 +41,7 @@ impl MultiTraitNode { modifier: None, body_range: Default::default(), is_trait: true, + is_tuple: false, }; builder.opaque_struct_type(&ctx.plmod.get_full_name(&name)); builder.add_body_to_struct_type(&ctx.plmod.get_full_name(&name), &st, ctx); @@ -164,6 +165,7 @@ impl TraitDefNode { modifier: self.modifier, body_range: self.range(), is_trait: true, + is_tuple: false, }))); builder.opaque_struct_type(&ctx.plmod.get_full_name(&self.id.name)); _ = ctx.add_type(self.id.name.clone(), stu, self.id.range); diff --git a/src/ast/node/mod.rs b/src/ast/node/mod.rs index 0db6b7ce1..5fed00349 100644 --- a/src/ast/node/mod.rs +++ b/src/ast/node/mod.rs @@ -32,6 +32,8 @@ use self::primary::*; use self::ret::*; use self::statement::*; use self::string_literal::StringNode; +use self::tuple::TupleInitNode; +use self::tuple::TupleTypeNode; use self::types::*; use self::union::UnionDefNode; @@ -60,6 +62,7 @@ pub mod program; pub mod ret; pub mod statement; pub mod string_literal; +pub mod tuple; pub mod types; pub mod union; @@ -70,6 +73,7 @@ pub enum TypeNodeEnum { Array(ArrayTypeNameNode), Pointer(PointerTypeNode), Func(FuncDefNode), + Tuple(TupleTypeNode), } #[enum_dispatch] pub trait TypeNode: RangeTrait + FmtTrait + PrintTrait { @@ -134,6 +138,7 @@ pub enum NodeEnum { UnionDefNode(UnionDefNode), AsNode(AsNode), IsNode(IsNode), + TupleInitNode(TupleInitNode), } // ANCHOR: range #[enum_dispatch] diff --git a/src/ast/node/tuple.rs b/src/ast/node/tuple.rs new file mode 100644 index 000000000..bf2b41d85 --- /dev/null +++ b/src/ast/node/tuple.rs @@ -0,0 +1,195 @@ +use std::{cell::RefCell, sync::Arc}; + +use crate::ast::builder::IRBuilder; +use crate::ast::node::node_result::NodeResultBuilder; +use crate::ast::node::RangeTrait; +use crate::ast::range::Range; +use internal_macro::node; +use linked_hash_map::LinkedHashMap; + +use crate::ast::pltype::Field; +use crate::ast::{ + node::{deal_line, tab}, + pltype::{PLType, STType}, + tokens::TokenType, +}; + +use super::{Node, NodeEnum, PrintTrait, TypeNode, TypeNodeEnum}; + +#[node] +pub struct TupleInitNode { + pub exprs: Vec>, +} + +impl Node for TupleInitNode { + fn emit<'a, 'ctx, 'b>( + &mut self, + ctx: &'b mut crate::ast::ctx::Ctx<'a>, + builder: &'b crate::ast::builder::BuilderEnum<'a, 'ctx>, + ) -> super::node_result::NodeResult { + let mut expr_values = vec![]; + let mut fields = LinkedHashMap::new(); + let mut field_tps = vec![]; + let mut err = None; + let mut name = String::new(); + for (i, expr) in self.exprs.iter_mut().enumerate() { + let expr = expr.emit(ctx, builder); + match expr { + Ok(value) => { + let ty = value.get_value().unwrap().get_ty(); + field_tps.push(ty.clone()); + let tp = ty.borrow(); + if i != 0 { + name.push_str(", "); + } + name.push_str(&tp.get_name()); + expr_values.push(value); + let f = Field { + index: i as u32 + 1, + typenode: tp.get_typenode(), + name: i.to_string(), + range: Default::default(), + modifier: Some((TokenType::PUB, Default::default())), + }; + fields.insert(i.to_string(), f); + } + Err(diag) => { + err = Some(diag); + } + } + } + name = format!("({})", name); + if let Some(err) = err { + return Err(err); + } + let sttype = new_tuple_type(name, ctx, fields, self.range); + builder.gen_st_visit_function(ctx, &sttype, &field_tps); + let stu = Arc::new(RefCell::new(PLType::Struct(sttype))); + ctx.add_type_without_check(stu.clone()); + let v = builder.alloc("tuple_v", &stu.borrow(), ctx, Some(self.range().start)); + // 初始化赋值 + for (i, value) in expr_values.into_iter().enumerate() { + let field_ptr = builder + .build_struct_gep(v, i as u32 + 1, &i.to_string()) + .unwrap(); + let v = + builder.try_load2var(self.range, value.get_value().unwrap().get_value(), ctx)?; + builder.build_store(field_ptr, v); + } + v.new_output(stu).to_result() + } +} + +fn new_tuple_type( + name: String, + ctx: &mut crate::ast::ctx::Ctx, + fields: LinkedHashMap, + range: Range, +) -> STType { + STType { + name, + path: ctx.plmod.path.clone(), + fields, + range: Default::default(), + doc: vec![], + generic_map: Default::default(), + derives: vec![], + modifier: Some((TokenType::PUB, Default::default())), + body_range: range, + is_trait: false, + is_tuple: true, + } +} +impl PrintTrait for TupleInitNode { + fn print(&self, tabs: usize, end: bool, mut line: Vec) { + deal_line(tabs, &mut line, end); + tab(tabs, line.clone(), end); + println!("TupleInitNode"); + for exp in self.exprs.iter() { + exp.print(tabs + 1, false, line.clone()); + } + } +} + +#[node] +pub struct TupleTypeNode { + pub tps: Vec>, +} + +impl TypeNode for TupleTypeNode { + fn get_type<'a, 'ctx, 'b>( + &self, + ctx: &'b mut crate::ast::ctx::Ctx<'a>, + builder: &'b crate::ast::builder::BuilderEnum<'a, 'ctx>, + ) -> super::TypeNodeResult { + let mut fields = LinkedHashMap::new(); + let mut field_tps = vec![]; + let mut err = None; + let mut name = String::new(); + for (i, tp) in self.tps.iter().enumerate() { + let tp = tp.get_type(ctx, builder); + match tp { + Ok(tp) => { + let arctp = tp.clone(); + let tp = tp.borrow(); + if !name.is_empty() { + name.push_str(", "); + } + name.push_str(&tp.get_name()); + field_tps.push(arctp.clone()); + let f = Field { + index: i as u32 + 1, + typenode: tp.get_typenode(), + name: i.to_string(), + range: Default::default(), + modifier: Some((TokenType::PUB, Default::default())), + }; + fields.insert(i.to_string(), f); + } + Err(diag) => { + err = Some(diag); + } + } + } + name = format!("({})", name); + if let Some(err) = err { + return Err(err); + } + let sttype = new_tuple_type(name, ctx, fields, self.range); + builder.gen_st_visit_function(ctx, &sttype, &field_tps); + let stu = Arc::new(RefCell::new(PLType::Struct(sttype))); + ctx.add_type_without_check(stu.clone()); + Ok(stu) + } + + fn emit_highlight(&self, ctx: &mut crate::ast::ctx::Ctx) { + for tp in &self.tps { + tp.emit_highlight(ctx); + } + } + + fn eq_or_infer<'a, 'ctx, 'b>( + &self, + ctx: &'b mut crate::ast::ctx::Ctx<'a>, + pltype: Arc>, + builder: &'b crate::ast::builder::BuilderEnum<'a, 'ctx>, + ) -> Result { + let left = self.get_type(ctx, builder)?; + let eq = *left.borrow() == *pltype.borrow(); + Ok(crate::ast::ctx::EqRes { + eq, + need_up_cast: false, + }) + } +} + +impl PrintTrait for TupleTypeNode { + fn print(&self, tabs: usize, end: bool, mut line: Vec) { + deal_line(tabs, &mut line, end); + tab(tabs, line.clone(), end); + println!("TupleTypeNode"); + for tp in self.tps.iter() { + tp.print(tabs + 1, false, line.clone()); + } + } +} diff --git a/src/ast/node/types.rs b/src/ast/node/types.rs index a3ee3eb7a..13e0f8a74 100644 --- a/src/ast/node/types.rs +++ b/src/ast/node/types.rs @@ -502,6 +502,7 @@ impl StructDefNode { modifier: self.modifier, body_range: self.range(), is_trait: false, + is_tuple: false, }))); builder.opaque_struct_type(&ctx.plmod.get_full_name(&self.id.name)); _ = ctx.add_type(self.id.name.clone(), stu, self.id.range); diff --git a/src/ast/pltype.rs b/src/ast/pltype.rs index f7541c956..8e8e81b99 100644 --- a/src/ast/pltype.rs +++ b/src/ast/pltype.rs @@ -749,7 +749,7 @@ impl ARRType { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, Eq)] pub struct STType { pub name: String, pub path: String, @@ -761,7 +761,29 @@ pub struct STType { pub modifier: Option<(TokenType, Range)>, pub body_range: Range, pub is_trait: bool, + pub is_tuple: bool, } + +impl PartialEq for STType { + fn eq(&self, other: &Self) -> bool { + if self.is_tuple && other.is_tuple { + self.fields == other.fields + } else { + self.name == other.name + && self.path == other.path + && self.fields == other.fields + && self.range == other.range + && self.doc == other.doc + && self.generic_map == other.generic_map + && self.derives == other.derives + && self.modifier == other.modifier + && self.body_range == other.body_range + && self.is_trait == other.is_trait + && self.is_tuple == other.is_tuple + } + } +} + impl STType { pub fn check_impl_derives(&self, ctx: &Ctx, st: &STType, range: Range) { debug_assert!(self.is_trait); diff --git a/src/nomparser/expression.rs b/src/nomparser/expression.rs index 08b6c9e84..b76157e5e 100644 --- a/src/nomparser/expression.rs +++ b/src/nomparser/expression.rs @@ -216,6 +216,7 @@ fn primary_exp(input: Span) -> IResult> { parantheses_exp, struct_init, array_init, + tuple_init, macro_call_exp, extern_identifier, string_literal, @@ -239,11 +240,15 @@ fn primary_exp(input: Span) -> IResult> { /// ```ebnf /// take_exp_op = ("." identifier?) ; /// ``` +#[test_parser(".0")] fn take_exp_op(input: Span) -> IResult>)> { delspace(map_res( preceded( tag_token_symbol(TokenType::DOT), - pair(opt(identifier), many0(comment)), + pair( + opt(alt((identifier, tuple_field_identifier))), + many0(comment), + ), ), |(idx, coms)| Ok::<_, ()>((ComplexOp::Field(idx), coms)), ))(input) diff --git a/src/nomparser/identifier.rs b/src/nomparser/identifier.rs index 6283eba96..7450bb21f 100644 --- a/src/nomparser/identifier.rs +++ b/src/nomparser/identifier.rs @@ -1,9 +1,9 @@ use nom::{ branch::alt, bytes::complete::tag, - character::complete::{alpha1, alphanumeric1}, + character::complete::{alpha1, alphanumeric1, one_of}, combinator::{map_res, opt, recognize}, - multi::{many0_count, separated_list1}, + multi::{many0_count, many1, separated_list1}, sequence::{pair, tuple}, IResult, InputTake, }; @@ -86,6 +86,18 @@ pub fn identifier(input: Span) -> IResult> { ))(input) } +pub fn tuple_field_identifier(input: Span) -> IResult> { + delspace(map_res( + recognize(many1(one_of("0123456789"))), + |out: Span| { + Ok::<_, ()>(Box::new(VarNode { + name: out.to_string(), + range: Range::new(out, out.take_split(out.len()).0), + })) + }, + ))(input) +} + #[test_parser("myname: int")] pub fn typed_identifier(input: Span) -> IResult> { delspace(map_res( diff --git a/src/nomparser/structure.rs b/src/nomparser/structure.rs index 635ad8bb0..93c98c065 100644 --- a/src/nomparser/structure.rs +++ b/src/nomparser/structure.rs @@ -1,3 +1,4 @@ +use crate::ast::node::tuple::TupleInitNode; use crate::ast::node::types::StructField; use crate::nomparser::Span; use crate::{ @@ -6,6 +7,7 @@ use crate::{ ast::{node::types::StructInitFieldNode, tokens::TokenType}, }; use internal_macro::{test_parser, test_parser_error}; +use nom::multi::separated_list1; use nom::{ branch::alt, combinator::{map_res, opt}, @@ -131,7 +133,7 @@ pub fn struct_init(input: Span) -> IResult> { map_res( tuple(( basic_type, - del_newline_or_space!(tag_token_symbol(TokenType::LBRACE)), + tag_token_symbol_ex(TokenType::LBRACE), alt(( map_res( pair( @@ -164,7 +166,7 @@ pub fn struct_init(input: Span) -> IResult> { }), )), many0(comment), - del_newline_or_space!(tag_token_symbol(TokenType::RBRACE)), + tag_token_symbol(TokenType::RBRACE), )), |(typename, _, (fields, lcomment), rcomment, _)| { let range = if !fields.is_empty() { @@ -189,3 +191,26 @@ pub fn struct_init(input: Span) -> IResult> { }, )(input) } + +#[test_parser("(1,2,a)")] +#[test_parser("(1,2,(a,b, (dd,)))")] +#[test_parser("()")] +#[test_parser("(2,)")] +#[test_parser_error("(,)")] +pub fn tuple_init(input: Span) -> IResult> { + map_res( + tuple(( + tag_token_symbol(TokenType::LPAREN), + opt(terminated( + separated_list1(tag_token_symbol_ex(TokenType::COMMA), general_exp), + opt(tag_token_symbol_ex(TokenType::COMMA)), + )), + tag_token_symbol(TokenType::RPAREN), + )), + |((_, rs), exprs, (_, re))| { + let range = rs.start.to(re.end); + let exprs = exprs.unwrap_or_default(); + res_enum(TupleInitNode { exprs, range }.into()) + }, + )(input) +} diff --git a/src/nomparser/types.rs b/src/nomparser/types.rs index 00978ddb6..1ea1f0b7d 100644 --- a/src/nomparser/types.rs +++ b/src/nomparser/types.rs @@ -1,4 +1,5 @@ use crate::ast::node::interface::{MultiTraitNode, TraitBoundNode}; +use crate::ast::node::tuple::TupleTypeNode; use crate::nomparser::Span; use crate::{ ast::node::types::{ArrayTypeNameNode, TypeNameNode}, @@ -11,7 +12,7 @@ use crate::{ }, }; use internal_macro::{test_parser, test_parser_error}; -use nom::sequence::preceded; +use nom::sequence::{preceded, terminated}; use nom::{ branch::alt, combinator::{map_res, opt}, @@ -26,7 +27,7 @@ pub fn type_name(input: Span) -> IResult> { delspace(map_res( pair( many0(tag_token_symbol(TokenType::TAKE_VAL)), - alt((basic_type, array_type)), + alt((basic_type, array_type, tuple_type)), ), |(pts, n)| { let mut node = n; @@ -233,3 +234,28 @@ pub fn multi_trait(input: Span) -> IResult> { })) })(input) } + +#[test_parser("(i32,i64,(i32,i64))")] +#[test_parser("()")] +#[test_parser_error("(,)")] +fn tuple_type(input: Span) -> IResult> { + map_res( + tuple(( + tag_token_symbol(TokenType::LPAREN), + opt(terminated( + separated_list1( + tag_token_symbol_ex(TokenType::COMMA), + alt((type_name, tuple_type)), + ), + opt(tag_token_symbol_ex(TokenType::COMMA)), + )), + tag_token_symbol(TokenType::RPAREN), + )), + |((_, rs), types, (_, re))| { + let range = rs.start.to(re.end); + let tps = types.unwrap_or_default(); + let node = Box::new(TypeNodeEnum::Tuple(TupleTypeNode { tps, range })); + res_box(node) + }, + )(input) +} diff --git a/test/fmt/test_fmt.pi b/test/fmt/test_fmt.pi index 932d2d7d4..6008676a4 100644 --- a/test/fmt/test_fmt.pi +++ b/test/fmt/test_fmt.pi @@ -185,3 +185,9 @@ trait TC: TA+TB { trait TD: TA { } +fn test_tuple() void { + let a: (i64, i64) = (1, 2); + let b = (1, 2, (8, (9, 0, None{}))); + return; +} + diff --git a/test/main.pi b/test/main.pi index 36caf18d7..04b0856ae 100644 --- a/test/main.pi +++ b/test/main.pi @@ -12,6 +12,7 @@ use project1::test::string; use project1::test::macros; use project1::test::union; use project1::test::multi_trait; +use project1::test::tuple; use pl_test::main; pub fn main() i64 { @@ -30,6 +31,7 @@ pub fn main() i64 { string::test_string(); union::test_union(); multi_trait::test_multi_trait(); + tuple::test_tuple(); return 0; } diff --git a/test/test/tuple.pi b/test/test/tuple.pi new file mode 100644 index 000000000..130a2648a --- /dev/null +++ b/test/test/tuple.pi @@ -0,0 +1,13 @@ +use core::panic; + +pub fn test_tuple() void { + let d:() = (); + let a = (1,);// tuple + let g = (1);// i64 + let b = (1, 2); + let c:(i64,i64,(i64)) = (1, 2, (3,),); + let e = a.0; + let f = c.2; + panic::assert(f.0 == 3); + return; +}