Add dot syntax for associated functions
This commit is contained in:
		
							parent
							
								
									7e3a13cf55
								
							
						
					
					
						commit
						1c83ca44ab
					
				
							
								
								
									
										27
									
								
								examples/associated_functions_shorthand.reid
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								examples/associated_functions_shorthand.reid
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | ||||
| import std::print; | ||||
| import std::from_str; | ||||
| import std::String; | ||||
| 
 | ||||
| struct Otus { | ||||
|     field: u32, | ||||
| } | ||||
| 
 | ||||
| impl Otus { | ||||
|     fn test(self) -> u32 { | ||||
|         self.field | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl i32 { | ||||
|     fn test(self) -> u32 { | ||||
|         43 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn main() -> u32 { | ||||
|     let otus = Otus { field: 17 }; | ||||
|     print(from_str("otus: ") + otus.test() as u64); | ||||
| 
 | ||||
| 
 | ||||
|     return otus.test(); | ||||
| } | ||||
| @ -89,6 +89,8 @@ pub enum ExpressionKind { | ||||
|     Indexed(Box<Expression>, Box<Expression>), | ||||
|     /// Struct-accessed, e.g. <expr>.<expr>
 | ||||
|     Accessed(Box<Expression>, String), | ||||
|     /// Associated function call, but with a shorthand
 | ||||
|     AccessCall(Box<Expression>, Box<FunctionCallExpression>), | ||||
|     Binop(BinaryOperator, Box<Expression>, Box<Expression>), | ||||
|     FunctionCall(Box<FunctionCallExpression>), | ||||
|     AssociatedFunctionCall(Type, Box<FunctionCallExpression>), | ||||
|  | ||||
| @ -358,12 +358,20 @@ impl Parse for PrimaryExpression { | ||||
|                         stream.get_range().unwrap(), | ||||
|                     ); | ||||
|                 } | ||||
|                 ValueIndex::Struct(StructValueIndex(name)) => { | ||||
|                 ValueIndex::Dot(val) => match val { | ||||
|                     DotIndexKind::StructValueIndex(name) => { | ||||
|                         expr = Expression( | ||||
|                             ExpressionKind::Accessed(Box::new(expr), name), | ||||
|                             stream.get_range().unwrap(), | ||||
|                         ); | ||||
|                     } | ||||
|                     DotIndexKind::FunctionCall(function_call_expression) => { | ||||
|                         expr = Expression( | ||||
|                             ExpressionKind::AccessCall(Box::new(expr), Box::new(function_call_expression)), | ||||
|                             stream.get_range().unwrap(), | ||||
|                         ); | ||||
|                     } | ||||
|                 }, | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| @ -473,27 +481,35 @@ impl Parse for BinaryOperator { | ||||
| impl Parse for FunctionCallExpression { | ||||
|     fn parse(mut stream: TokenStream) -> Result<Self, Error> { | ||||
|         if let Some(Token::Identifier(name)) = stream.next() { | ||||
|             stream.expect(Token::ParenOpen)?; | ||||
| 
 | ||||
|             let mut args = Vec::new(); | ||||
| 
 | ||||
|             if let Ok(exp) = stream.parse() { | ||||
|                 args.push(exp); | ||||
| 
 | ||||
|                 while stream.expect(Token::Comma).is_ok() { | ||||
|                     args.push(stream.parse()?); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             stream.expect(Token::ParenClose)?; | ||||
| 
 | ||||
|             Ok(FunctionCallExpression(name, args, stream.get_range().unwrap())) | ||||
|             let args = stream.parse::<FunctionArgs>()?; | ||||
|             Ok(FunctionCallExpression(name, args.0, stream.get_range().unwrap())) | ||||
|         } else { | ||||
|             Err(stream.expected_err("identifier")?) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct FunctionArgs(Vec<Expression>); | ||||
| 
 | ||||
| impl Parse for FunctionArgs { | ||||
|     fn parse(mut stream: TokenStream) -> Result<Self, Error> { | ||||
|         stream.expect(Token::ParenOpen)?; | ||||
| 
 | ||||
|         let mut params = Vec::new(); | ||||
|         if let Ok(exp) = stream.parse() { | ||||
|             params.push(exp); | ||||
| 
 | ||||
|             while stream.expect(Token::Comma).is_ok() { | ||||
|                 params.push(stream.parse()?); | ||||
|             } | ||||
|         } | ||||
|         stream.expect(Token::ParenClose)?; | ||||
| 
 | ||||
|         Ok(FunctionArgs(params)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Parse for IfExpression { | ||||
|     fn parse(mut stream: TokenStream) -> Result<Self, Error> { | ||||
|         stream.expect(Token::If)?; | ||||
| @ -766,14 +782,14 @@ impl<T: Parse + std::fmt::Debug> Parse for NamedField<T> { | ||||
| #[derive(Debug, Clone)] | ||||
| pub enum ValueIndex { | ||||
|     Array(ArrayValueIndex), | ||||
|     Struct(StructValueIndex), | ||||
|     Dot(DotIndexKind), | ||||
| } | ||||
| 
 | ||||
| impl Parse for ValueIndex { | ||||
|     fn parse(mut stream: TokenStream) -> Result<Self, Error> { | ||||
|         match stream.peek() { | ||||
|             Some(Token::BracketOpen) => Ok(ValueIndex::Array(stream.parse()?)), | ||||
|             Some(Token::Dot) => Ok(ValueIndex::Struct(stream.parse()?)), | ||||
|             Some(Token::Dot) => Ok(ValueIndex::Dot(stream.parse()?)), | ||||
|             _ => Err(stream.expecting_err("value or struct index")?), | ||||
|         } | ||||
|     } | ||||
| @ -792,13 +808,24 @@ impl Parse for ArrayValueIndex { | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct StructValueIndex(String); | ||||
| pub enum DotIndexKind { | ||||
|     StructValueIndex(String), | ||||
|     FunctionCall(FunctionCallExpression), | ||||
| } | ||||
| 
 | ||||
| impl Parse for StructValueIndex { | ||||
| impl Parse for DotIndexKind { | ||||
|     fn parse(mut stream: TokenStream) -> Result<Self, Error> { | ||||
|         stream.expect(Token::Dot)?; | ||||
|         if let Some(Token::Identifier(name)) = stream.next() { | ||||
|             Ok(StructValueIndex(name)) | ||||
|             if let Ok(args) = stream.parse::<FunctionArgs>() { | ||||
|                 Ok(Self::FunctionCall(FunctionCallExpression( | ||||
|                     name, | ||||
|                     args.0, | ||||
|                     stream.get_range_prev().unwrap(), | ||||
|                 ))) | ||||
|             } else { | ||||
|                 Ok(Self::StructValueIndex(name)) | ||||
|             } | ||||
|         } else { | ||||
|             return Err(stream.expected_err("struct index (number)")?); | ||||
|         } | ||||
|  | ||||
| @ -422,6 +422,19 @@ impl ast::Expression { | ||||
|                     meta: fn_call_expr.2.as_meta(module_id), | ||||
|                 }, | ||||
|             ), | ||||
|             ast::ExpressionKind::AccessCall(expression, fn_call_expr) => { | ||||
|                 let mut params: Vec<_> = fn_call_expr.1.iter().map(|e| e.process(module_id)).collect(); | ||||
|                 params.insert(0, expression.process(module_id)); | ||||
|                 mir::ExprKind::AssociatedFunctionCall( | ||||
|                     mir::TypeKind::Vague(mir::VagueType::Unknown), | ||||
|                     mir::FunctionCall { | ||||
|                         name: fn_call_expr.0.clone(), | ||||
|                         return_type: mir::TypeKind::Vague(mir::VagueType::Unknown), | ||||
|                         parameters: params, | ||||
|                         meta: fn_call_expr.2.as_meta(module_id), | ||||
|                     }, | ||||
|                 ) | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         mir::Expression(kind, self.1.as_meta(module_id)) | ||||
|  | ||||
| @ -82,6 +82,8 @@ pub enum ErrorKind { | ||||
|     BinaryOpAlreadyDefined(BinaryOperator, TypeKind, TypeKind), | ||||
|     #[error("Binary operation {0} between {1} and {2} is not defined")] | ||||
|     InvalidBinop(BinaryOperator, TypeKind, TypeKind), | ||||
|     #[error("Could not infer type for {0:?}. Try adding type annotations.")] | ||||
|     CouldNotInferType(String), | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, PartialEq, Eq)] | ||||
| @ -294,13 +296,13 @@ impl TypeKind { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub(super) fn assert_known(&self, refs: &TypeRefs, state: &TypecheckPassState) -> Result<TypeKind, ErrorKind> { | ||||
|         self.is_known(refs, state).map(|_| self.clone()) | ||||
|     pub(super) fn assert_known(&self, state: &TypecheckPassState) -> Result<TypeKind, ErrorKind> { | ||||
|         self.is_known(state).map(|_| self.clone()) | ||||
|     } | ||||
| 
 | ||||
|     pub(super) fn is_known(&self, refs: &TypeRefs, state: &TypecheckPassState) -> Result<(), ErrorKind> { | ||||
|     pub(super) fn is_known(&self, state: &TypecheckPassState) -> Result<(), ErrorKind> { | ||||
|         match &self { | ||||
|             TypeKind::Array(type_kind, _) => type_kind.as_ref().is_known(refs, state), | ||||
|             TypeKind::Array(type_kind, _) => type_kind.as_ref().is_known(state), | ||||
|             TypeKind::CustomType(custom_type_key) => { | ||||
|                 state | ||||
|                     .scope | ||||
| @ -311,9 +313,9 @@ impl TypeKind { | ||||
|                         state.module_id.unwrap(), | ||||
|                     )) | ||||
|             } | ||||
|             TypeKind::Borrow(type_kind, _) => type_kind.is_known(refs, state), | ||||
|             TypeKind::UserPtr(type_kind) => type_kind.is_known(refs, state), | ||||
|             TypeKind::CodegenPtr(type_kind) => type_kind.is_known(refs, state), | ||||
|             TypeKind::Borrow(type_kind, _) => type_kind.is_known(state), | ||||
|             TypeKind::UserPtr(type_kind) => type_kind.is_known(state), | ||||
|             TypeKind::CodegenPtr(type_kind) => type_kind.is_known(state), | ||||
|             TypeKind::Vague(vague_type) => Err(ErrorKind::TypeIsVague(*vague_type)), | ||||
|             _ => Ok(()), | ||||
|         } | ||||
|  | ||||
| @ -112,7 +112,7 @@ impl BinopDefinition { | ||||
|     fn typecheck(&mut self, typerefs: &TypeRefs, state: &mut TypecheckPassState) -> Result<TypeKind, ErrorKind> { | ||||
|         for param in vec![&self.lhs, &self.rhs] { | ||||
|             let param_t = state.or_else( | ||||
|                 param.1.assert_known(typerefs, state), | ||||
|                 param.1.assert_known(state), | ||||
|                 TypeKind::Vague(Vague::Unknown), | ||||
|                 self.signature(), | ||||
|             ); | ||||
| @ -130,7 +130,7 @@ impl BinopDefinition { | ||||
|             state.ok(res, self.signature()); | ||||
|         } | ||||
| 
 | ||||
|         let return_type = self.return_type.clone().assert_known(typerefs, state)?; | ||||
|         let return_type = self.return_type.clone().assert_known(state)?; | ||||
| 
 | ||||
|         state.scope.return_type_hint = Some(self.return_type.clone()); | ||||
|         let inferred = self | ||||
| @ -150,7 +150,7 @@ impl FunctionDefinition { | ||||
|     fn typecheck(&mut self, typerefs: &TypeRefs, state: &mut TypecheckPassState) -> Result<TypeKind, ErrorKind> { | ||||
|         for param in &self.parameters { | ||||
|             let param_t = state.or_else( | ||||
|                 param.1.assert_known(typerefs, state), | ||||
|                 param.1.assert_known(state), | ||||
|                 TypeKind::Vague(Vague::Unknown), | ||||
|                 self.signature(), | ||||
|             ); | ||||
| @ -168,7 +168,7 @@ impl FunctionDefinition { | ||||
|             state.ok(res, self.signature()); | ||||
|         } | ||||
| 
 | ||||
|         let return_type = self.return_type.clone().assert_known(typerefs, state)?; | ||||
|         let return_type = self.return_type.clone().assert_known(state)?; | ||||
|         let inferred = self.kind.typecheck(typerefs, state, Some(self.return_type.clone())); | ||||
| 
 | ||||
|         match inferred { | ||||
| @ -327,7 +327,7 @@ impl Block { | ||||
|                 } | ||||
|                 StmtKind::While(WhileStatement { condition, block, meta }) => { | ||||
|                     let condition_ty = condition.typecheck(&mut state, typerefs, HintKind::Coerce(TypeKind::Bool))?; | ||||
|                     if condition_ty.assert_known(typerefs, &state)? != TypeKind::Bool { | ||||
|                     if condition_ty.assert_known(&state)? != TypeKind::Bool { | ||||
|                         state.note_errors(&vec![ErrorKind::TypesIncompatible(condition_ty, TypeKind::Bool)], *meta); | ||||
|                     } | ||||
| 
 | ||||
|  | ||||
| @ -595,6 +595,19 @@ impl Expression { | ||||
|                 Ok(type_refs.from_type(type_kind).unwrap()) | ||||
|             } | ||||
|             ExprKind::AssociatedFunctionCall(type_kind, function_call) => { | ||||
|                 if type_kind.is_known(state).is_err() { | ||||
|                     let first_param = function_call | ||||
|                         .parameters | ||||
|                         .get_mut(0) | ||||
|                         .expect("Unknown-type associated function NEEDS to always have at least one parameter!"); | ||||
|                     let param_ty = first_param.infer_types(state, type_refs).unwrap().resolve_deep(); | ||||
|                     *type_kind = state.or_else( | ||||
|                         param_ty.ok_or(ErrorKind::CouldNotInferType(format!("{}", first_param))), | ||||
|                         Void, | ||||
|                         first_param.1, | ||||
|                     ); | ||||
|                 } | ||||
| 
 | ||||
|                 // Get function definition and types
 | ||||
|                 let fn_call = state | ||||
|                     .scope | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user