From 08ffde02b3e1736f5ee3d90141df3b530b6f7d74 Mon Sep 17 00:00:00 2001 From: Ken Gorab Date: Thu, 12 Oct 2023 17:40:08 -0400 Subject: [PATCH] @Decorators Add basic support for @Decorators, and use them to implement the @Stub decorator to remove the need for the `stub_mode` in the parser. --- abra_core/src/lexer/lexer.rs | 4 +- abra_core/src/lexer/tokens.rs | 5 +- abra_core/src/lib.rs | 10 - abra_core/src/parser/ast.rs | 12 + abra_core/src/parser/parser.rs | 286 ++++++++++++++++-- abra_core/src/transpile/genc2.rs | 218 ++++++------- abra_core/src/typechecker/prelude.stub.abra | 170 +++++------ abra_core/src/typechecker/typechecker.rs | 4 +- abra_core/src/typechecker/typechecker2.rs | 27 +- .../src/typechecker/typechecker2_tests.rs | 27 +- 10 files changed, 520 insertions(+), 243 deletions(-) diff --git a/abra_core/src/lexer/lexer.rs b/abra_core/src/lexer/lexer.rs index 5b2ffbad..f27ac7af 100644 --- a/abra_core/src/lexer/lexer.rs +++ b/abra_core/src/lexer/lexer.rs @@ -470,6 +470,7 @@ impl<'a> Lexer<'a> { ',' => Ok(Some(Token::Comma(pos))), ':' => Ok(Some(Token::Colon(pos))), '.' => Ok(Some(Token::Dot(pos))), + '@' => Ok(Some(Token::At(pos))), _ => Ok(None) } } @@ -602,7 +603,7 @@ mod tests { #[test] fn test_tokenize_separators() { - let input = "( ) [ ] { } | , : ? #{"; + let input = "( ) [ ] { } | , : ? #{ @"; let tokens = tokenize(input).unwrap(); let expected = vec![ Token::LParen(Position::new(1, 1), false), @@ -616,6 +617,7 @@ mod tests { Token::Colon(Position::new(1, 17)), Token::Question(Position::new(1, 19)), Token::LBraceHash(Position::new(1, 21)), + Token::At(Position::new(1, 24)), ]; assert_eq!(expected, tokens); } diff --git a/abra_core/src/lexer/tokens.rs b/abra_core/src/lexer/tokens.rs index 7f967d55..381cdc40 100644 --- a/abra_core/src/lexer/tokens.rs +++ b/abra_core/src/lexer/tokens.rs @@ -136,6 +136,7 @@ pub enum Token { #[strum(to_string = ".", serialize = "Dot")] Dot(Position), #[strum(to_string = "?.", serialize = "QuestionDot")] QuestionDot(Position), #[strum(to_string = "=>", serialize = "Arrow")] Arrow(Position), + #[strum(to_string = "@", serialize = "At")] At(Position), } impl Token { @@ -212,7 +213,8 @@ impl Token { Token::Question(pos) | Token::Dot(pos) | Token::QuestionDot(pos) | - Token::Arrow(pos) => pos + Token::Arrow(pos) | + Token::At(pos) => pos }; pos.clone() } @@ -296,6 +298,7 @@ impl Token { Token::Dot(pos) => Range::with_length(pos, 0), Token::QuestionDot(pos) => Range::with_length(pos, 1), Token::Arrow(pos) => Range::with_length(pos, 1), + Token::At(pos) => Range::with_length(pos, 0), } } diff --git a/abra_core/src/lib.rs b/abra_core/src/lib.rs index e1c4b66d..75b4bda4 100644 --- a/abra_core/src/lib.rs +++ b/abra_core/src/lib.rs @@ -74,16 +74,6 @@ fn tokenize_and_parse(module_id: &ModuleId, input: &String) -> Result Result { - match lexer::lexer::tokenize(module_id, input) { - Err(e) => Err(Error::LexerError(e)), - Ok(tokens) => match parser::parser::parse_stub(module_id.clone(), tokens) { - Err(e) => Err(Error::ParseError(e)), - Ok(nodes) => Ok(nodes) - } - } -} - pub fn typecheck(module_id: ModuleId, input: &String, loader: &mut ModuleLoader) -> Result where R: ModuleReader { diff --git a/abra_core/src/parser/ast.rs b/abra_core/src/parser/ast.rs index 68fd8067..396348ef 100644 --- a/abra_core/src/parser/ast.rs +++ b/abra_core/src/parser/ast.rs @@ -231,6 +231,7 @@ impl BindingPattern { #[derive(Clone, Debug, PartialEq)] pub struct BindingDeclNode { + pub decorators: Vec, pub export_token: Option, pub binding: BindingPattern, pub type_ann: Option, @@ -253,6 +254,7 @@ pub fn args_to_parameters(raw_arg_tuple: &(Token, Option, bool, #[derive(Clone, Debug, PartialEq)] pub struct FunctionDeclNode { + pub decorators: Vec, pub export_token: Option, // Must be a Token::Ident pub name: Token, @@ -284,6 +286,7 @@ impl LambdaNode { #[derive(Clone, Debug, PartialEq)] pub struct TypeDeclNode { + pub decorators: Vec, pub export_token: Option, // Must be a Token::Ident pub name: Token, @@ -303,6 +306,7 @@ pub struct TypeDeclField { #[derive(Clone, Debug, PartialEq)] pub struct EnumDeclNode { + pub decorators: Vec, pub export_token: Option, // Must be a Token::Ident pub name: Token, @@ -440,6 +444,14 @@ pub struct ImportNode { pub module_id: ModuleId, } +#[derive(Clone, Debug, PartialEq)] +pub struct DecoratorNode { + pub at_token: Token, + // Must be a Token::Ident + pub name: Token, + pub args: Vec<(Option, AstNode)>, +} + #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub enum ModulePathSegment { CurrentDir, diff --git a/abra_core/src/parser/parser.rs b/abra_core/src/parser/parser.rs index 78cfb3a4..92834fb2 100644 --- a/abra_core/src/parser/parser.rs +++ b/abra_core/src/parser/parser.rs @@ -1,7 +1,8 @@ use peekmore::{PeekMore, PeekMoreIterator}; use std::vec::IntoIter; +use itertools::Itertools; use crate::lexer::tokens::{Token, TokenType, Position, Range}; -use crate::parser::ast::{ArrayNode, AssignmentNode, AstLiteralNode, AstNode, BinaryNode, BinaryOp, BindingDeclNode, ForLoopNode, FunctionDeclNode, GroupedNode, IfNode, IndexingMode, IndexingNode, InvocationNode, TypeIdentifier, UnaryNode, UnaryOp, WhileLoopNode, TypeDeclNode, MapNode, AccessorNode, LambdaNode, EnumDeclNode, MatchNode, MatchCase, MatchCaseType, SetNode, BindingPattern, TypeDeclField, ImportNode, ModuleId, MatchCaseArgument, ImportKind, TryNode}; +use crate::parser::ast::{ArrayNode, AssignmentNode, AstLiteralNode, AstNode, BinaryNode, BinaryOp, BindingDeclNode, ForLoopNode, FunctionDeclNode, GroupedNode, IfNode, IndexingMode, IndexingNode, InvocationNode, TypeIdentifier, UnaryNode, UnaryOp, WhileLoopNode, TypeDeclNode, MapNode, AccessorNode, LambdaNode, EnumDeclNode, MatchNode, MatchCase, MatchCaseType, SetNode, BindingPattern, TypeDeclField, ImportNode, ModuleId, MatchCaseArgument, ImportKind, TryNode, DecoratorNode}; use crate::parser::parse_error::{ParseErrorKind, ParseError}; use crate::parser::precedence::Precedence; @@ -11,15 +12,7 @@ pub struct ParseResult { } pub fn parse(module_id: ModuleId, tokens: Vec) -> Result { - parse_impl(module_id, tokens, false) -} - -pub fn parse_stub(module_id: ModuleId, tokens: Vec) -> Result { - parse_impl(module_id, tokens, true) -} - -fn parse_impl(module_id: ModuleId, tokens: Vec, stub_mode: bool) -> Result { - let mut parser = Parser::new(tokens, stub_mode); + let mut parser = Parser::new(tokens); let mut nodes = Vec::new(); let mut imports = Vec::new(); @@ -62,16 +55,16 @@ pub struct Parser { tokens: PeekMoreIterator>, last_token: Token, context: Vec, - stub_mode: bool, + seen_decorators: Vec, } type PrefixFn = dyn Fn(&mut Parser, Token) -> Result; type InfixFn = dyn Fn(&mut Parser, Token, AstNode) -> Result; impl Parser { - fn new(tokens: Vec, stub_mode: bool) -> Self { + fn new(tokens: Vec) -> Self { let tokens = tokens.into_iter().peekmore(); - Parser { tokens, last_token: Token::None(Position::new(0, 0)), context: vec![], stub_mode } + Parser { tokens, last_token: Token::None(Position::new(0, 0)), context: vec![], seen_decorators: vec![] } } fn is_context(&self, ctx: Context) -> bool { @@ -241,6 +234,10 @@ impl Parser { fn parse_stmt(&mut self, export_token: Option) -> Result { match self.expect_peek()? { + Token::At(_) => { + self.parse_decorator()?; + self.parse_stmt(export_token) + } Token::Func(_) => self.parse_func_decl(export_token), Token::Val(_) => self.parse_binding_decl(export_token), Token::Var(_) => self.parse_binding_decl(export_token), @@ -531,7 +528,31 @@ impl Parser { Ok(type_args) } + fn parse_decorator(&mut self) -> Result<(), ParseErrorKind> { + let at_token = self.expect_next_token(TokenType::At)?; + let name = self.expect_next_token(TokenType::Ident)?; + + let next_token = self.expect_peek()?; + let args = match next_token { + Token::Func(_) | Token::Val(_) | Token::Var(_) | Token::Type(_) | Token::Enum(_) | Token::At(_) | Token::Export(_) => vec![], + Token::LParen(_, _) => { + self.expect_next_token(TokenType::LParen)?; + self.parse_invocation_args()? + } + tok => { + let expected = vec![TokenType::Func, TokenType::Val, TokenType::Var, TokenType::Type, TokenType::Enum, TokenType::At, TokenType::Export]; + return Err(ParseErrorKind::ExpectedOneOf(expected, tok.clone())); + } + }; + + self.seen_decorators.push(DecoratorNode { at_token, name, args }); + + Ok(()) + } + fn parse_func_decl(&mut self, export_token: Option) -> Result { + let decorators = self.seen_decorators.drain(..).collect_vec(); + let token = self.expect_next()?; let name = self.expect_next_token(TokenType::Ident)?; @@ -548,7 +569,12 @@ impl Parser { _ => None }; - let stub_mode = self.stub_mode; + let stub_mode = decorators.iter() + .find(|dec| { + let name = Token::get_ident_name(&dec.name); + name == "Stub" || name == "Intrinsic" + }) + .is_some(); let body = match self.peek() { None if stub_mode => Ok(vec![]), None => Err(self.unexpected_eof()), @@ -561,10 +587,12 @@ impl Parser { Some(t) => Err(ParseErrorKind::UnexpectedToken(t.clone())), }?; - Ok(AstNode::FunctionDecl(token, FunctionDeclNode { export_token, name, type_args, args, ret_type, body })) + Ok(AstNode::FunctionDecl(token, FunctionDeclNode { decorators, export_token, name, type_args, args, ret_type, body })) } fn parse_binding_decl(&mut self, export_token: Option) -> Result { + let decorators = self.seen_decorators.drain(..).collect(); + let token = self.expect_next()?; let is_mutable = match &token { Token::Val(_) => false, @@ -601,10 +629,12 @@ impl Parser { Some(Box::new(expr)) } else { None }; - Ok(AstNode::BindingDecl(token, BindingDeclNode { export_token, binding, is_mutable, type_ann, expr })) + Ok(AstNode::BindingDecl(token, BindingDeclNode { decorators, export_token, binding, is_mutable, type_ann, expr })) } fn parse_type_decl(&mut self, export_token: Option) -> Result { + let decorators = self.seen_decorators.drain(..).collect(); + let keyword_tok = self.expect_next()?; let is_enum = if let Token::Enum(_) = &keyword_tok { true } else { false }; @@ -659,6 +689,9 @@ impl Parser { let method = self.parse_func_decl(None)?; methods.push(method); } + Token::At(_) => { + self.parse_decorator()?; + } _ => return Err(ParseErrorKind::UnexpectedToken(token.clone())), } } @@ -666,9 +699,9 @@ impl Parser { self.expect_next_token(TokenType::RBrace)?; if is_enum { - Ok(AstNode::EnumDecl(keyword_tok, EnumDeclNode { export_token, name, variants, methods, type_args })) + Ok(AstNode::EnumDecl(keyword_tok, EnumDeclNode { decorators, export_token, name, variants, methods, type_args })) } else { - Ok(AstNode::TypeDecl(keyword_tok, TypeDeclNode { export_token, name, fields, methods, type_args })) + Ok(AstNode::TypeDecl(keyword_tok, TypeDeclNode { decorators, export_token, name, fields, methods, type_args })) } } @@ -1350,6 +1383,12 @@ impl Parser { fn parse_invocation(&mut self, token: Token, left: AstNode) -> Result { let lparen = token; + let args = self.parse_invocation_args()?; + + Ok(AstNode::Invocation(lparen, InvocationNode { target: Box::new(left), args })) + } + + fn parse_invocation_args(&mut self) -> Result, AstNode)>, ParseErrorKind> { let mut item_expected = true; let mut args = Vec::<(Option, AstNode)>::new(); loop { @@ -1388,7 +1427,7 @@ impl Parser { } } - Ok(AstNode::Invocation(lparen, InvocationNode { target: Box::new(left), args })) + Ok(args) } fn parse_accessor(&mut self, token: Token, left: AstNode) -> Result { @@ -2360,6 +2399,7 @@ mod tests { AstNode::BindingDecl( Token::Val(Position::new(1, 1)), BindingDeclNode { + decorators: vec![], export_token: None, binding: BindingPattern::Variable(ident_token!((1, 5), "abc")), is_mutable: false, @@ -2370,6 +2410,7 @@ mod tests { AstNode::BindingDecl( Token::Var(Position::new(2, 1)), BindingDeclNode { + decorators: vec![], export_token: None, binding: BindingPattern::Variable(ident_token!((2, 5), "abc")), is_mutable: true, @@ -2390,6 +2431,7 @@ mod tests { AstNode::BindingDecl( Token::Val(Position::new(1, 1)), BindingDeclNode { + decorators: vec![], export_token: None, binding: BindingPattern::Variable(ident_token!((1, 5), "abc")), is_mutable: false, @@ -2409,6 +2451,7 @@ mod tests { AstNode::BindingDecl( Token::Var(Position::new(2, 1)), BindingDeclNode { + decorators: vec![], export_token: None, binding: BindingPattern::Variable(ident_token!((2, 5), "abc")), is_mutable: true, @@ -2555,7 +2598,7 @@ mod tests { fn parse_type_identifier(input: &str) -> TypeIdentifier { let module_id = ModuleId::parse_module_path("./test").unwrap(); let tokens = tokenize(&module_id, &input.to_string()).unwrap(); - let mut parser = Parser::new(tokens, false); + let mut parser = Parser::new(tokens); parser.parse_type_identifier(true).unwrap() } @@ -2858,6 +2901,7 @@ mod tests { let expected = AstNode::FunctionDecl( Token::Func(Position::new(1, 1)), FunctionDeclNode { + decorators: vec![], export_token: None, name: Token::Ident(Position::new(1, 6), "abc".to_string()), type_args: vec![], @@ -2874,6 +2918,7 @@ mod tests { let expected = AstNode::FunctionDecl( Token::Func(Position::new(1, 1)), FunctionDeclNode { + decorators: vec![], export_token: None, name: Token::Ident(Position::new(1, 6), "abc".to_string()), type_args: vec![], @@ -2883,6 +2928,7 @@ mod tests { AstNode::BindingDecl( Token::Val(Position::new(1, 14)), BindingDeclNode { + decorators: vec![], export_token: None, binding: BindingPattern::Variable(ident_token!((1, 18), "a")), is_mutable: false, @@ -3027,6 +3073,7 @@ mod tests { AstNode::BindingDecl( Token::Val(Position::new(2, 1)), BindingDeclNode { + decorators: vec![], export_token: None, binding: BindingPattern::Variable(ident_token!((2, 5), "a")), type_ann: None, @@ -3241,6 +3288,7 @@ mod tests { let expected = AstNode::TypeDecl( Token::Type(Position::new(1, 1)), TypeDeclNode { + decorators: vec![], export_token: None, name: ident_token!((1, 6), "Person"), type_args: vec![], @@ -3254,6 +3302,7 @@ mod tests { let expected = AstNode::TypeDecl( Token::Type(Position::new(1, 1)), TypeDeclNode { + decorators: vec![], export_token: None, name: ident_token!((1, 6), "Person"), type_args: vec![], @@ -3276,6 +3325,7 @@ mod tests { let expected = AstNode::TypeDecl( Token::Type(Position::new(1, 1)), TypeDeclNode { + decorators: vec![], export_token: None, name: ident_token!((1, 6), "Person"), type_args: vec![], @@ -3303,6 +3353,7 @@ mod tests { let expected = AstNode::TypeDecl( Token::Type(Position::new(1, 1)), TypeDeclNode { + decorators: vec![], export_token: None, name: ident_token!((1, 6), "Person"), type_args: vec![], @@ -3330,6 +3381,7 @@ mod tests { let expected = AstNode::TypeDecl( Token::Type(Position::new(1, 1)), TypeDeclNode { + decorators: vec![], export_token: None, name: ident_token!((1, 6), "Person"), type_args: vec![], @@ -3356,6 +3408,7 @@ mod tests { let expected = AstNode::TypeDecl( Token::Type(Position::new(1, 1)), TypeDeclNode { + decorators: vec![], export_token: None, name: ident_token!((1, 6), "List"), type_args: vec![ident_token!((1, 11), "T")], @@ -3370,6 +3423,7 @@ mod tests { let expected = AstNode::TypeDecl( Token::Type(Position::new(1, 1)), TypeDeclNode { + decorators: vec![], export_token: None, name: ident_token!((1, 6), "List"), type_args: vec![ @@ -3417,6 +3471,7 @@ mod tests { let expected = AstNode::TypeDecl( Token::Type(Position::new(1, 1)), TypeDeclNode { + decorators: vec![], export_token: None, name: ident_token!((1, 6), "Person"), type_args: vec![], @@ -3425,6 +3480,7 @@ mod tests { AstNode::FunctionDecl( Token::Func(Position::new(2, 1)), FunctionDeclNode { + decorators: vec![], export_token: None, name: Token::Ident(Position::new(2, 6), "hello".to_string()), type_args: vec![], @@ -3486,6 +3542,7 @@ mod tests { let expected = AstNode::EnumDecl( Token::Enum(Position::new(1, 1)), EnumDeclNode { + decorators: vec![], export_token: None, name: ident_token!((1, 6), "Color"), type_args: vec![], @@ -3503,6 +3560,7 @@ mod tests { let expected = AstNode::EnumDecl( Token::Enum(Position::new(1, 1)), EnumDeclNode { + decorators: vec![], export_token: None, name: ident_token!((1, 6), "Direction"), type_args: vec![], @@ -3520,6 +3578,7 @@ mod tests { let expected = AstNode::EnumDecl( Token::Enum(Position::new(1, 1)), EnumDeclNode { + decorators: vec![], export_token: None, name: ident_token!((1, 6), "Direction"), type_args: vec![], @@ -3706,6 +3765,7 @@ mod tests { let ast = parse("val a = 1\n+\na\n[a]\nprintln(a)\n[a]")?; let expected = vec![ AstNode::BindingDecl(Token::Val(Position::new(1, 1)), BindingDeclNode { + decorators: vec![], export_token: None, binding: BindingPattern::Variable(ident_token!((1, 5), "a")), type_ann: None, @@ -3887,6 +3947,7 @@ mod tests { AstNode::BindingDecl( Token::Val(Position::new(1, 12)), BindingDeclNode { + decorators: vec![], export_token: None, binding: BindingPattern::Variable(ident_token!((1, 16), "a")), is_mutable: false, @@ -3918,6 +3979,7 @@ mod tests { let expected = AstNode::BindingDecl( Token::Val(Position::new(1, 1)), BindingDeclNode { + decorators: vec![], export_token: None, binding: BindingPattern::Variable(ident_token!((1, 5), "str")), is_mutable: false, @@ -4350,6 +4412,7 @@ mod tests { AstNode::BindingDecl( Token::Val(Position::new(2, 1)), BindingDeclNode { + decorators: vec![], export_token: None, binding: BindingPattern::Variable(ident_token!((2, 5), "a")), is_mutable: false, @@ -4917,7 +4980,7 @@ mod tests { fn parse_import_path(input: &str) -> Result { let module_id = ModuleId::parse_module_path("./test").unwrap(); let tokens = tokenize(&module_id, &input.to_string()).unwrap(); - let mut parser = Parser::new(tokens, false); + let mut parser = Parser::new(tokens); parser.parse_import_module().map(|(_, module_id)| module_id) } @@ -5127,6 +5190,7 @@ mod tests { let expected = AstNode::BindingDecl( Token::Val(Position::new(1, 8)), BindingDeclNode { + decorators: vec![], export_token: Some(Token::Export(Position::new(1, 1))), binding: BindingPattern::Variable(ident_token!((1, 12), "x")), type_ann: None, @@ -5141,6 +5205,7 @@ mod tests { let expected = AstNode::BindingDecl( Token::Var(Position::new(1, 8)), BindingDeclNode { + decorators: vec![], export_token: Some(Token::Export(Position::new(1, 1))), binding: BindingPattern::Variable(ident_token!((1, 12), "x")), type_ann: None, @@ -5155,6 +5220,7 @@ mod tests { let expected = AstNode::FunctionDecl( Token::Func(Position::new(1, 8)), FunctionDeclNode { + decorators: vec![], export_token: Some(Token::Export(Position::new(1, 1))), name: ident_token!((1, 13), "abc"), type_args: vec![], @@ -5170,6 +5236,7 @@ mod tests { let expected = AstNode::TypeDecl( Token::Type(Position::new(1, 8)), TypeDeclNode { + decorators: vec![], export_token: Some(Token::Export(Position::new(1, 1))), name: ident_token!((1, 13), "Person"), type_args: vec![], @@ -5184,6 +5251,7 @@ mod tests { let expected = AstNode::EnumDecl( Token::Enum(Position::new(1, 8)), EnumDeclNode { + decorators: vec![], export_token: Some(Token::Export(Position::new(1, 1))), name: ident_token!((1, 13), "Direction"), type_args: vec![], @@ -5284,4 +5352,180 @@ mod tests { Ok(()) } + + #[test] + fn parse_decorators() -> TestResult { + // Val binding + let ast = parse("@Foo val x = 14")?; + let expected = AstNode::BindingDecl( + Token::Val(Position::new(1, 6)), + BindingDeclNode { + decorators: vec![DecoratorNode { at_token: Token::At(Position::new(1, 1)), name: ident_token!((1, 2), "Foo"), args: vec![] }], + export_token: None, + binding: BindingPattern::Variable(ident_token!((1, 10), "x")), + type_ann: None, + expr: Some(Box::new(int_literal!((1, 14), 14))), + is_mutable: false, + }, + ); + assert_eq!(expected, ast[0]); + // Exported val binding + let ast = parse("@Foo(\"bar\", \"baz\")\nexport val x = 14")?; + let expected = AstNode::BindingDecl( + Token::Val(Position::new(2, 8)), + BindingDeclNode { + decorators: vec![ + DecoratorNode { + at_token: Token::At(Position::new(1, 1)), + name: ident_token!((1, 2),"Foo"), + args: vec![ + (None, string_literal!((1, 6), "bar")), + (None, string_literal!((1, 13), "baz")), + ], + } + ], + export_token: Some(Token::Export(Position::new(2, 1))), + binding: BindingPattern::Variable(ident_token!((2, 12), "x")), + type_ann: None, + expr: Some(Box::new(int_literal!((2, 16), 14))), + is_mutable: false, + }, + ); + assert_eq!(expected, ast[0]); + // Multiple decorators + let ast = parse("@Foo @Bar val x = 14")?; + let expected = AstNode::BindingDecl( + Token::Val(Position::new(1, 11)), + BindingDeclNode { + decorators: vec![ + DecoratorNode { at_token: Token::At(Position::new(1, 1)), name: ident_token!((1, 2), "Foo"), args: vec![] }, + DecoratorNode { at_token: Token::At(Position::new(1, 6)), name: ident_token!((1, 7), "Bar"), args: vec![] }, + ], + export_token: None, + binding: BindingPattern::Variable(ident_token!((1, 15), "x")), + type_ann: None, + expr: Some(Box::new(int_literal!((1, 19), 14))), + is_mutable: false, + }, + ); + assert_eq!(expected, ast[0]); + + // Function declaration + let ast = parse("@Foo func abc() {}")?; + let expected = AstNode::FunctionDecl( + Token::Func(Position::new(1, 6)), + FunctionDeclNode { + decorators: vec![DecoratorNode { at_token: Token::At(Position::new(1, 1)), name: ident_token!((1, 2), "Foo"), args: vec![] }], + export_token: None, + name: ident_token!((1, 11), "abc"), + type_args: vec![], + args: vec![], + ret_type: None, + body: vec![], + }, + ); + assert_eq!(expected, ast[0]); + // Exported function declaration + let ast = parse("@Foo\nexport func abc() {}")?; + let expected = AstNode::FunctionDecl( + Token::Func(Position::new(2, 8)), + FunctionDeclNode { + decorators: vec![DecoratorNode { at_token: Token::At(Position::new(1, 1)), name: ident_token!((1, 2), "Foo"), args: vec![] }], + export_token: Some(Token::Export(Position::new(2, 1))), + name: ident_token!((2, 13), "abc"), + type_args: vec![], + args: vec![], + ret_type: None, + body: vec![], + }, + ); + assert_eq!(expected, ast[0]); + // Method declaration + let ast = parse("type Foo { @Foo func foo() {} }")?; + let expected = AstNode::TypeDecl( + Token::Type(Position::new(1, 1)), + TypeDeclNode { + decorators: vec![], + export_token: None, + name: ident_token!((1, 6), "Foo"), + type_args: vec![], + fields: vec![], + methods: vec![ + AstNode::FunctionDecl( + Token::Func(Position::new(1, 17)), + FunctionDeclNode { + decorators: vec![DecoratorNode { at_token: Token::At(Position::new(1, 12)), name: ident_token!((1, 13), "Foo"), args: vec![] }], + export_token: None, + name: ident_token!((1, 22), "foo"), + type_args: vec![], + args: vec![], + ret_type: None, + body: vec![], + }, + ), + ], + }, + ); + assert_eq!(expected, ast[0]); + + // Type declaration + let ast = parse("@Foo type Person {}")?; + let expected = AstNode::TypeDecl( + Token::Type(Position::new(1, 6)), + TypeDeclNode { + decorators: vec![DecoratorNode { at_token: Token::At(Position::new(1, 1)), name: ident_token!((1, 2), "Foo"), args: vec![] }], + export_token: None, + name: ident_token!((1, 11), "Person"), + type_args: vec![], + fields: vec![], + methods: vec![], + }, + ); + assert_eq!(expected, ast[0]); + // Exported type declaration + let ast = parse("@Foo\nexport type Person {}")?; + let expected = AstNode::TypeDecl( + Token::Type(Position::new(2, 8)), + TypeDeclNode { + decorators: vec![DecoratorNode { at_token: Token::At(Position::new(1, 1)), name: ident_token!((1, 2), "Foo"), args: vec![] }], + export_token: Some(Token::Export(Position::new(2, 1))), + name: ident_token!((2, 13), "Person"), + type_args: vec![], + fields: vec![], + methods: vec![], + }, + ); + assert_eq!(expected, ast[0]); + + // Enum declaration + let ast = parse("@Foo enum Direction {}")?; + let expected = AstNode::EnumDecl( + Token::Enum(Position::new(1, 6)), + EnumDeclNode { + decorators: vec![DecoratorNode { at_token: Token::At(Position::new(1, 1)), name: ident_token!((1, 2), "Foo"), args: vec![] }], + export_token: None, + name: ident_token!((1, 11), "Direction"), + type_args: vec![], + variants: vec![], + methods: vec![], + }, + ); + assert_eq!(expected, ast[0]); + // Exported enum declaration + let ast = parse("@Foo\nexport enum Direction {}")?; + let expected = AstNode::EnumDecl( + Token::Enum(Position::new(2, 8)), + EnumDeclNode { + decorators: vec![DecoratorNode { at_token: Token::At(Position::new(1, 1)), name: ident_token!((1, 2), "Foo"), args: vec![] }], + export_token: Some(Token::Export(Position::new(2, 1))), + name: ident_token!((2, 13), "Direction"), + type_args: vec![], + variants: vec![], + methods: vec![], + }, + ); + assert_eq!(expected, ast[0]); + + Ok(()) + } } diff --git a/abra_core/src/transpile/genc2.rs b/abra_core/src/transpile/genc2.rs index cefdfe0f..078ae715 100644 --- a/abra_core/src/transpile/genc2.rs +++ b/abra_core/src/transpile/genc2.rs @@ -1125,112 +1125,112 @@ impl CCompiler2 { } } -#[cfg(test)] -mod test { - use std::process::Command; - use assert_cmd::cargo::CommandCargoExt; - use itertools::{EitherOrBoth, Itertools}; - use crate::transpile::get_project_root::get_project_root; - - fn run_test_file(file_name: &str) { - let rust_project_root = get_project_root().unwrap(); - - let test_file_path = rust_project_root.join("abra_core").join("src").join("transpile").join("testv2").join(file_name); - let test_file = std::fs::read_to_string(&test_file_path).unwrap(); - - let mut cmd = Command::cargo_bin("abra").unwrap(); - cmd.arg("compile2").arg(&test_file_path); - let output = cmd.output().unwrap(); - assert!(output.stderr.is_empty(), "Compilation error: {}", String::from_utf8(output.stderr).unwrap()); - - let output = String::from_utf8(output.stdout).unwrap(); - let output = output.lines(); - - let prefix = "/// Expect: "; - let expectations = test_file.lines() - .map(|line| line.trim()) - .enumerate() - .filter(|(_, line)| line.starts_with(prefix)) - .map(|(line_num, line)| (line_num + 1, line.replace(prefix, ""))) - .collect_vec(); - - for pair in expectations.iter().zip_longest(output) { - match pair { - EitherOrBoth::Both((line_num, expected), actual) => { - assert_eq!(expected, actual, "Expectation mismatch at {}:{}", test_file_path.to_str().unwrap(), line_num); - } - EitherOrBoth::Left((line_num, expected)) => { - assert!(false, "Expected: {} (line {}), but reached end of output", expected, line_num); - } - EitherOrBoth::Right(actual) => { - assert!(false, "Received line: {}, but there were no more expectations", actual); - } - } - } - } - - // #[test] - // fn builtin_values() { - // run_test_file("builtinValues.abra"); - // } - // - // #[test] - // fn unary_ops() { - // run_test_file("unaryOps.abra"); - // } - // - // #[test] - // fn binary_ops() { - // run_test_file("binaryOps.abra"); - // } - // - // #[test] - // fn variable_declaration() { - // run_test_file("variableDeclaration.abra"); - // } - // - // #[test] - // fn assignment() { - // run_test_file("assignment.abra"); - // } - // - // #[test] - // fn functions() { - // run_test_file("functions.abra"); - // } - // - // #[test] - // fn lambdas() { - // run_test_file("lambdas.abra"); - // } - // - // #[test] - // fn closures() { - // run_test_file("closures.abra"); - // } - // - // #[test] - // fn strings() { - // run_test_file("strings.abra"); - // } - // - // #[test] - // fn arrays() { - // run_test_file("arrays.abra"); - // } - // - // #[test] - // fn maps() { - // run_test_file("maps.abra"); - // } - // - // #[test] - // fn sets() { - // run_test_file("sets.abra"); - // } - // - // #[test] - // fn types() { - // run_test_file("types.abra"); - // } -} +// #[cfg(test)] +// mod test { +// use std::process::Command; +// use assert_cmd::cargo::CommandCargoExt; +// use itertools::{EitherOrBoth, Itertools}; +// use crate::transpile::get_project_root::get_project_root; +// +// fn run_test_file(file_name: &str) { +// let rust_project_root = get_project_root().unwrap(); +// +// let test_file_path = rust_project_root.join("abra_core").join("src").join("transpile").join("testv2").join(file_name); +// let test_file = std::fs::read_to_string(&test_file_path).unwrap(); +// +// let mut cmd = Command::cargo_bin("abra").unwrap(); +// cmd.arg("compile2").arg(&test_file_path); +// let output = cmd.output().unwrap(); +// assert!(output.stderr.is_empty(), "Compilation error: {}", String::from_utf8(output.stderr).unwrap()); +// +// let output = String::from_utf8(output.stdout).unwrap(); +// let output = output.lines(); +// +// let prefix = "/// Expect: "; +// let expectations = test_file.lines() +// .map(|line| line.trim()) +// .enumerate() +// .filter(|(_, line)| line.starts_with(prefix)) +// .map(|(line_num, line)| (line_num + 1, line.replace(prefix, ""))) +// .collect_vec(); +// +// for pair in expectations.iter().zip_longest(output) { +// match pair { +// EitherOrBoth::Both((line_num, expected), actual) => { +// assert_eq!(expected, actual, "Expectation mismatch at {}:{}", test_file_path.to_str().unwrap(), line_num); +// } +// EitherOrBoth::Left((line_num, expected)) => { +// assert!(false, "Expected: {} (line {}), but reached end of output", expected, line_num); +// } +// EitherOrBoth::Right(actual) => { +// assert!(false, "Received line: {}, but there were no more expectations", actual); +// } +// } +// } +// } +// +// #[test] +// fn builtin_values() { +// run_test_file("builtinValues.abra"); +// } +// +// #[test] +// fn unary_ops() { +// run_test_file("unaryOps.abra"); +// } +// +// #[test] +// fn binary_ops() { +// run_test_file("binaryOps.abra"); +// } +// +// #[test] +// fn variable_declaration() { +// run_test_file("variableDeclaration.abra"); +// } +// +// #[test] +// fn assignment() { +// run_test_file("assignment.abra"); +// } +// +// #[test] +// fn functions() { +// run_test_file("functions.abra"); +// } +// +// #[test] +// fn lambdas() { +// run_test_file("lambdas.abra"); +// } +// +// #[test] +// fn closures() { +// run_test_file("closures.abra"); +// } +// +// #[test] +// fn strings() { +// run_test_file("strings.abra"); +// } +// +// #[test] +// fn arrays() { +// run_test_file("arrays.abra"); +// } +// +// #[test] +// fn maps() { +// run_test_file("maps.abra"); +// } +// +// #[test] +// fn sets() { +// run_test_file("sets.abra"); +// } +// +// #[test] +// fn types() { +// run_test_file("types.abra"); +// } +// } diff --git a/abra_core/src/typechecker/prelude.stub.abra b/abra_core/src/typechecker/prelude.stub.abra index dcdb1a6c..2cbf9208 100644 --- a/abra_core/src/typechecker/prelude.stub.abra +++ b/abra_core/src/typechecker/prelude.stub.abra @@ -1,21 +1,21 @@ -func print(*items: Any[]) -func println(*items: Any[]) -func range(start: Int, end: Int, stepBy = 1): Int[] +@Stub func print(*items: Any[]) +@Stub func println(*items: Any[]) +@Stub func range(start: Int, end: Int, stepBy = 1): Int[] type Int { - func abs(self): Int - func asBase(self, base: Int): String - func isEven(self): Bool - func isOdd(self): Bool - func isBetween(self, lower: Int, upper: Int, inclusive = false): Bool + @Stub func abs(self): Int + @Stub func asBase(self, base: Int): String + @Stub func isEven(self): Bool + @Stub func isOdd(self): Bool + @Stub func isBetween(self, lower: Int, upper: Int, inclusive = false): Bool } type Float { - func abs(self): Float - func floor(self): Int - func ceil(self): Int - func round(self): Int - func withPrecision(self, precision: Int): Float + @Stub func abs(self): Float + @Stub func floor(self): Int + @Stub func ceil(self): Int + @Stub func round(self): Int + @Stub func withPrecision(self, precision: Int): Float } type Bool { @@ -25,39 +25,39 @@ type Bool { type String { length: Int readonly - func isEmpty(self): Bool - func toLower(self): String - func toUpper(self): String - func padLeft(self, totalSize: Int, padding = " "): String - func trim(self): String - func trimStart(self, pattern: String? = None): String - func trimEnd(self, pattern: String? = None): String - func split(self, by = ""): String[] - func splitAt(self, index: Int): (String, String) - func lines(self): String[] - func parseInt(self, radix = 10): Int? - func parseFloat(self): Float? - func concat(self, suffix: Any, *others: Any[]): String - func replaceAll(self, pattern: String, replacement: String): String + @Stub func isEmpty(self): Bool + @Stub func toLower(self): String + @Stub func toUpper(self): String + @Stub func padLeft(self, totalSize: Int, padding = " "): String + @Stub func trim(self): String + @Stub func trimStart(self, pattern: String? = None): String + @Stub func trimEnd(self, pattern: String? = None): String + @Stub func split(self, by = ""): String[] + @Stub func splitAt(self, index: Int): (String, String) + @Stub func lines(self): String[] + @Stub func parseInt(self, radix = 10): Int? + @Stub func parseFloat(self): Float? + @Stub func concat(self, suffix: Any, *others: Any[]): String + @Stub func replaceAll(self, pattern: String, replacement: String): String } //type Pointer { -// //@Intrinsic("pointer_null") +// @Intrinsic("pointer_null") // func null(): Pointer // -// //@Intrinsic("pointer_malloc") +// @Intrinsic("pointer_malloc") // func malloc(size: Int): Pointer // -// //@Intrinsic("pointer_realloc") +// @Intrinsic("pointer_realloc") // func realloc(ptr: Pointer, size: Int): Pointer // -// //@Intrinsic("pointer_offset") +// @Intrinsic("pointer_offset") // func offset(ptr: Pointer, offset: Int): Pointer // -// //@Intrinsic("pointer_load") +// @Intrinsic("pointer_load") // func load(ptr: Pointer): U // -// //@Intrinsic("pointer_store") +// @Intrinsic("pointer_store") // func store(ptr: Pointer, value: U) //} @@ -72,66 +72,66 @@ type Array { // Array(length: 0, _buffer: Pointer.malloc(initialCapacity), _capacity: initialCapacity) // } - func isEmpty(self): Bool - func enumerate(self): (T, Int)[] - func push(self, item: T, *others: T[]) - func pop(self): T? - func popFront(self): T? - func splitAt(self, index: Int): (T[], T[]) - func concat(self, other: T[]): T[] - func map(self, fn: (T) => U): U[] - func filter(self, fn: (T) => Bool): T[] - func reduce(self, initialValue: U, fn: (U, T) => U): U - func forEach(self, fn: (T) => Unit) - func join(self, joiner = ""): String - func contains(self, item: T): Bool - func find(self, fn: (T) => Bool): T? - func findIndex(self, fn: (T) => Bool): (T, Int)? - func any(self, fn: (T) => Bool): Bool - func all(self, fn: (T) => Bool): Bool - func none(self, fn: (T) => Bool): Bool - func sortBy(self, fn: (T) => Int, reverse = false): T[] - func dedupe(self): T[] - func dedupeBy(self, fn: (T) => U): T[] - func partition(self, fn: (T) => U): Map - func tally(self): Map - func tallyBy(self, fn: (T) => U): Map - func asSet(self): Set - func getOr(self, index: Int, default: T): T - func getOrElse(self, index: Int, getDefault: () => T): T - func update(self, index: Int, updater: (T) => T) - func reverse(self): T[] + @Stub func isEmpty(self): Bool + @Stub func enumerate(self): (T, Int)[] + @Stub func push(self, item: T, *others: T[]) + @Stub func pop(self): T? + @Stub func popFront(self): T? + @Stub func splitAt(self, index: Int): (T[], T[]) + @Stub func concat(self, other: T[]): T[] + @Stub func map(self, fn: (T) => U): U[] + @Stub func filter(self, fn: (T) => Bool): T[] + @Stub func reduce(self, initialValue: U, fn: (U, T) => U): U + @Stub func forEach(self, fn: (T) => Unit) + @Stub func join(self, joiner = ""): String + @Stub func contains(self, item: T): Bool + @Stub func find(self, fn: (T) => Bool): T? + @Stub func findIndex(self, fn: (T) => Bool): (T, Int)? + @Stub func any(self, fn: (T) => Bool): Bool + @Stub func all(self, fn: (T) => Bool): Bool + @Stub func none(self, fn: (T) => Bool): Bool + @Stub func sortBy(self, fn: (T) => Int, reverse = false): T[] + @Stub func dedupe(self): T[] + @Stub func dedupeBy(self, fn: (T) => U): T[] + @Stub func partition(self, fn: (T) => U): Map + @Stub func tally(self): Map + @Stub func tallyBy(self, fn: (T) => U): Map + @Stub func asSet(self): Set + @Stub func getOr(self, index: Int, default: T): T + @Stub func getOrElse(self, index: Int, getDefault: () => T): T + @Stub func update(self, index: Int, updater: (T) => T) + @Stub func reverse(self): T[] } type Set { size: Int readonly - func isEmpty(self): Bool - func enumerate(self): (T, Int)[] - func contains(self, item: T): Bool - func insert(self, item: T) - func remove(self, item: T): T? - func map(self, fn: (T) => U): U[] - func filter(self, fn: (T) => Bool): Set - func reduce(self, initialValue: U, fn: (U, T) => U): U - func asArray(self): T[] - func union(self, other: Set): Set - func difference(self, other: Set): Set - func intersection(self, other: Set): Set + @Stub func isEmpty(self): Bool + @Stub func enumerate(self): (T, Int)[] + @Stub func contains(self, item: T): Bool + @Stub func insert(self, item: T) + @Stub func remove(self, item: T): T? + @Stub func map(self, fn: (T) => U): U[] + @Stub func filter(self, fn: (T) => Bool): Set + @Stub func reduce(self, initialValue: U, fn: (U, T) => U): U + @Stub func asArray(self): T[] + @Stub func union(self, other: Set): Set + @Stub func difference(self, other: Set): Set + @Stub func intersection(self, other: Set): Set } type Map { size: Int readonly - func isEmpty(self): Bool - func enumerate(self): (K, V)[] - func keys(self): Set - func values(self): V[] - func entries(self): Set<(K, V)> - func containsKey(self, key: K): Bool - func mapValues(self, fn: (K, V) => U): Map - func getOr(self, key: K, default: V): V - func getOrElse(self, key: K, getDefault: () => V): V - func update(self, key: K, updater: (V) => V) - func remove(self, key: K): V? + @Stub func isEmpty(self): Bool + @Stub func enumerate(self): (K, V)[] + @Stub func keys(self): Set + @Stub func values(self): V[] + @Stub func entries(self): Set<(K, V)> + @Stub func containsKey(self, key: K): Bool + @Stub func mapValues(self, fn: (K, V) => U): Map + @Stub func getOr(self, key: K, default: V): V + @Stub func getOrElse(self, key: K, getDefault: () => V): V + @Stub func update(self, key: K, updater: (V) => V) + @Stub func remove(self, key: K): V? } diff --git a/abra_core/src/typechecker/typechecker.rs b/abra_core/src/typechecker/typechecker.rs index 1f130ff2..eb8cd033 100644 --- a/abra_core/src/typechecker/typechecker.rs +++ b/abra_core/src/typechecker/typechecker.rs @@ -1586,7 +1586,7 @@ impl<'a, R: ModuleReader> AstVisitor for Typ } fn visit_binding_decl(&mut self, token: Token, node: BindingDeclNode) -> Result { - let BindingDeclNode { export_token, is_mutable, mut binding, type_ann, expr } = node; + let BindingDeclNode { export_token, is_mutable, mut binding, type_ann, expr, .. } = node; if !is_mutable && expr.is_none() { if let BindingPattern::Variable(ident) = binding { return Err(TypecheckerErrorKind::MissingRequiredAssignment { ident }); @@ -1938,7 +1938,7 @@ impl<'a, R: ModuleReader> AstVisitor for Typ return Err(TypecheckerErrorKind::InvalidTypeDeclDepth { token }); } - let EnumDeclNode { export_token, name, variants, methods, type_args } = node; + let EnumDeclNode { export_token, name, variants, methods, type_args, .. } = node; let new_enum_name = Token::get_ident_name(&name); let is_exported = if let Some(token) = export_token { if self.scopes.len() != 1 { diff --git a/abra_core/src/typechecker/typechecker2.rs b/abra_core/src/typechecker/typechecker2.rs index 8163396c..22ab4a80 100644 --- a/abra_core/src/typechecker/typechecker2.rs +++ b/abra_core/src/typechecker/typechecker2.rs @@ -3,7 +3,7 @@ use std::hash::{Hash, Hasher}; use std::io::BufRead; use std::path::{Path, PathBuf}; use itertools::{Either, EitherOrBoth, Itertools}; -use crate::{parser, tokenize_and_parse_stub}; +use crate::{parser, tokenize_and_parse}; use crate::common::util::integer_decode; use crate::parser::parser::{ParseResult}; use crate::lexer::lexer_error::LexerError; @@ -772,10 +772,17 @@ impl FuncId { pub const BOGUS: FuncId = FuncId(ScopeId::BOGUS, usize::MAX); } +#[derive(Debug, PartialEq)] +pub struct DecoratorInstance { + pub name: String, + pub args: Vec, +} + #[derive(Debug, PartialEq)] pub struct Function { pub id: FuncId, pub fn_scope_id: ScopeId, + pub decorators: Vec, pub name: String, pub generic_ids: Vec, pub has_self: bool, @@ -2293,7 +2300,7 @@ impl<'a, L: LoadModule> Typechecker2<'a, L> { } let func_id = FuncId(current_scope.id, current_scope.funcs.len()); - let func = Function { id: func_id, fn_scope_id, name: name.clone(), generic_ids, has_self, params, return_type_id, defined_span: Some(span.clone()), body: vec![], captured_vars: vec![] }; + let func = Function { id: func_id, fn_scope_id, decorators: vec![], name: name.clone(), generic_ids, has_self, params, return_type_id, defined_span: Some(span.clone()), body: vec![], captured_vars: vec![] }; current_scope.funcs.push(func); Ok(func_id) @@ -2321,7 +2328,7 @@ impl<'a, L: LoadModule> Typechecker2<'a, L> { let fn_decl_scope = self.project.get_scope_by_id_mut(fn_decl_scope_id); let func_id = FuncId(fn_decl_scope.id, fn_decl_scope.funcs.len()); - let func = Function { id: func_id, fn_scope_id, name, generic_ids: vec![], has_self: false, params, return_type_id: PRELUDE_ANY_TYPE_ID, defined_span: None, body: vec![], captured_vars: vec![] }; + let func = Function { id: func_id, fn_scope_id, decorators: vec![], name, generic_ids: vec![], has_self: false, params, return_type_id: PRELUDE_ANY_TYPE_ID, defined_span: None, body: vec![], captured_vars: vec![] }; fn_decl_scope.funcs.push(func); @@ -2547,7 +2554,7 @@ impl<'a, L: LoadModule> Typechecker2<'a, L> { self.current_scope_id = PRELUDE_SCOPE_ID; let prelude_stub_file = self.module_loader.load_file(&"prelude.stub.abra".to_string()).expect("Internal error: should always be able to load prelude"); - let parse_result = tokenize_and_parse_stub(&parser::ast::ModuleId::External("prelude".to_string()), &prelude_stub_file) + let parse_result = tokenize_and_parse(&parser::ast::ModuleId::External("prelude".to_string()), &prelude_stub_file) .expect("There should not be a problem parsing the prelude file"); self.typecheck_block(parse_result.nodes) .expect("There should not be a problem typechecking the prelude file"); @@ -2953,6 +2960,16 @@ impl<'a, L: LoadModule> Typechecker2<'a, L> { fn typecheck_function_pass_2(&mut self, func_id: FuncId, node: FunctionDeclNode) -> Result<(), TypeError> { debug_assert!(self.function_pass == FunctionPass::Pass2); + let mut decorators = Vec::with_capacity(node.decorators.len()); + for dec in node.decorators { + let mut args = Vec::with_capacity(dec.args.len()); + for (_, arg) in dec.args { + args.push(self.typecheck_expression(arg, None)?) + } + decorators.push(DecoratorInstance { name: Token::get_ident_name(&dec.name), args }); + } + self.project.get_func_by_id_mut(&func_id).decorators = decorators; + let prev_func_id = self.current_function; self.current_function = Some(func_id); @@ -3294,7 +3311,7 @@ impl<'a, L: LoadModule> Typechecker2<'a, L> { match node { AstNode::BindingDecl(token, n) => { - let BindingDeclNode { export_token, mut binding, type_ann, expr, is_mutable } = n; + let BindingDeclNode { export_token, mut binding, type_ann, expr, is_mutable, .. } = n; let is_exported = export_token.is_some(); if let Some(export_token) = export_token { self.verify_export_scope(&export_token)?; } diff --git a/abra_core/src/typechecker/typechecker2_tests.rs b/abra_core/src/typechecker/typechecker2_tests.rs index 4d637d54..40c64c7d 100644 --- a/abra_core/src/typechecker/typechecker2_tests.rs +++ b/abra_core/src/typechecker/typechecker2_tests.rs @@ -3,7 +3,7 @@ use itertools::{Either, Itertools}; use crate::lexer::tokens::{Position, POSITION_BOGUS, Range, Token}; use crate::parser; use crate::parser::ast::{BinaryOp, BindingPattern, UnaryOp}; -use crate::typechecker::typechecker2::{LoadModule, ModuleId, Project, Typechecker2, TypecheckError, PRELUDE_MODULE_ID, Type, PRELUDE_INT_TYPE_ID, PRELUDE_FLOAT_TYPE_ID, PRELUDE_BOOL_TYPE_ID, PRELUDE_STRING_TYPE_ID, TypedNode, TypedLiteral, TypeError, Variable, VarId, ScopeId, Struct, StructId, PRELUDE_UNIT_TYPE_ID, TypeId, Function, FuncId, FunctionParam, StructField, VariableAlias, DuplicateNameKind, AccessorKind, AssignmentKind, ImmutableAssignmentKind, InvalidTupleIndexKind, InvalidAssignmentTargetKind, Enum, EnumId, EnumVariant, EnumVariantKind, Span, UnreachableMatchCaseKind, InvalidLoopTargetKind, ControlFlowTerminator, TerminatorKind, ExportedValue, TypeKind}; +use crate::typechecker::typechecker2::{LoadModule, ModuleId, Project, Typechecker2, TypecheckError, PRELUDE_MODULE_ID, Type, PRELUDE_INT_TYPE_ID, PRELUDE_FLOAT_TYPE_ID, PRELUDE_BOOL_TYPE_ID, PRELUDE_STRING_TYPE_ID, TypedNode, TypedLiteral, TypeError, Variable, VarId, ScopeId, Struct, StructId, PRELUDE_UNIT_TYPE_ID, TypeId, Function, FuncId, FunctionParam, StructField, VariableAlias, DuplicateNameKind, AccessorKind, AssignmentKind, ImmutableAssignmentKind, InvalidTupleIndexKind, InvalidAssignmentTargetKind, Enum, EnumId, EnumVariant, EnumVariantKind, Span, UnreachableMatchCaseKind, InvalidLoopTargetKind, ControlFlowTerminator, TerminatorKind, ExportedValue, TypeKind, DecoratorInstance}; const PRELUDE_STR: &str = include_str!("prelude.stub.abra"); @@ -2115,8 +2115,8 @@ fn typecheck_type_declaration() { let project = test_typecheck("\ type Foo {\n\ a: String readonly\n\ - func foo(self): Int = 12\n\ - func fooStatic(): Int = 24\n\ + @Dec func foo(self): Int = 12\n\ + @Dec(\"foo\") func fooStatic(): Int = 24\n\ }\ ").unwrap(); let module = &project.modules[1]; @@ -2141,6 +2141,7 @@ fn typecheck_type_declaration() { Function { id: FuncId(ScopeId(ModuleId(1), 1), 0), fn_scope_id: ScopeId(ModuleId(1), 2), + decorators: vec![DecoratorInstance { name: "Dec".to_string(), args: vec![] }], name: "foo".to_string(), generic_ids: vec![], has_self: true, @@ -2149,17 +2150,17 @@ fn typecheck_type_declaration() { name: "self".to_string(), type_id: project.find_type_id(&ScopeId(ModuleId(1), 0), &Type::GenericInstance(struct_id, vec![])).unwrap(), var_id: VarId(ScopeId(ModuleId(1), 2), 0), - defined_span: Some(Span::new(ModuleId(1), (3, 10), (3, 13))), + defined_span: Some(Span::new(ModuleId(1), (3, 15), (3, 18))), default_value: None, is_variadic: false, is_incomplete: false, } ], return_type_id: PRELUDE_INT_TYPE_ID, - defined_span: Some(Span::new(ModuleId(1), (3, 6), (3, 8))), + defined_span: Some(Span::new(ModuleId(1), (3, 11), (3, 13))), body: vec![ TypedNode::Literal { - token: Token::Int(Position::new(3, 23), 12), + token: Token::Int(Position::new(3, 28), 12), value: TypedLiteral::Int(12), type_id: PRELUDE_INT_TYPE_ID, } @@ -2169,15 +2170,16 @@ fn typecheck_type_declaration() { Function { id: FuncId(ScopeId(ModuleId(1), 1), 1), fn_scope_id: ScopeId(ModuleId(1), 3), + decorators: vec![DecoratorInstance { name: "Dec".to_string(), args: vec![TypedNode::Literal { type_id: PRELUDE_STRING_TYPE_ID, token: Token::String(Position::new(4, 6), "foo".to_string()), value: TypedLiteral::String("foo".to_string()) }] }], name: "fooStatic".to_string(), generic_ids: vec![], has_self: false, params: vec![], return_type_id: PRELUDE_INT_TYPE_ID, - defined_span: Some(Span::new(ModuleId(1), (4, 6), (4, 14))), + defined_span: Some(Span::new(ModuleId(1), (4, 18), (4, 26))), body: vec![ TypedNode::Literal { - token: Token::Int(Position::new(4, 25), 24), + token: Token::Int(Position::new(4, 37), 24), value: TypedLiteral::Int(24), type_id: PRELUDE_INT_TYPE_ID, } @@ -2445,6 +2447,7 @@ fn typecheck_enum_declaration() { let baz_variant_func = Function { id: baz_func_id, fn_scope_id: ScopeId(ModuleId(1), 2), + decorators: vec![], name: "Baz".to_string(), generic_ids: vec![], has_self: false, @@ -2525,6 +2528,7 @@ fn typecheck_function_declaration() { Function { id: FuncId(ScopeId(ModuleId(1), 0), 0), fn_scope_id: ScopeId(ModuleId(1), 1), + decorators: vec![], name: "foo".to_string(), generic_ids: vec![], has_self: false, @@ -2555,6 +2559,7 @@ fn typecheck_function_declaration() { Function { id: FuncId(ScopeId(ModuleId(1), 0), 0), fn_scope_id: ScopeId(ModuleId(1), 1), + decorators: vec![], name: "foo".to_string(), generic_ids: vec![], has_self: false, @@ -2611,6 +2616,7 @@ fn typecheck_function_declaration() { Function { id: FuncId(ScopeId(ModuleId(1), 0), 0), fn_scope_id: ScopeId(ModuleId(1), 1), + decorators: vec![], name: "foo".to_string(), generic_ids: vec![], has_self: false, @@ -2639,6 +2645,7 @@ fn typecheck_function_declaration() { Function { id: FuncId(ScopeId(ModuleId(1), 0), 0), fn_scope_id: ScopeId(ModuleId(1), 1), + decorators: vec![], name: "foo".to_string(), generic_ids: vec![], has_self: false, @@ -2694,6 +2701,7 @@ fn typecheck_function_declaration() { Function { id: FuncId(ScopeId(ModuleId(1), 0), 0), fn_scope_id, + decorators: vec![], name: "foo".to_string(), generic_ids: vec![t_type_id], has_self: false, @@ -2737,6 +2745,7 @@ fn typecheck_function_declaration() { Function { id: FuncId(ScopeId(ModuleId(1), 0), 0), fn_scope_id, + decorators: vec![], name: "foo".to_string(), generic_ids: vec![], has_self: false, @@ -3142,7 +3151,7 @@ fn typecheck_invocation() { }), ], type_id: PRELUDE_INT_TYPE_ID, - } + }, ]; assert_eq!(expected, module.code);