From 3466908a80e0f730c3f7210d75ea31a609b24882 Mon Sep 17 00:00:00 2001 From: Jesse Brault Date: Mon, 16 Mar 2026 10:21:00 -0500 Subject: [PATCH] Lexing/parsing generic args. --- dmc-lib/src/ast/function.rs | 2 +- dmc-lib/src/ast/type_use.rs | 8 ++++- dmc-lib/src/lexer.rs | 8 ++--- dmc-lib/src/parser.rs | 71 ++++++++++++++++++++++++++++++++++--- dmc-lib/src/token.rs | 4 +-- 5 files changed, 81 insertions(+), 12 deletions(-) diff --git a/dmc-lib/src/ast/function.rs b/dmc-lib/src/ast/function.rs index c799421..4aa46a7 100644 --- a/dmc-lib/src/ast/function.rs +++ b/dmc-lib/src/ast/function.rs @@ -109,7 +109,7 @@ impl Function { Parameter::new( "self", SourceRange::new(0, 0), - TypeUse::new("Self", SourceRange::new(0, 0)), + TypeUse::new("Self", SourceRange::new(0, 0), vec![]), ), ); } diff --git a/dmc-lib/src/ast/type_use.rs b/dmc-lib/src/ast/type_use.rs index 92b77b1..9254019 100644 --- a/dmc-lib/src/ast/type_use.rs +++ b/dmc-lib/src/ast/type_use.rs @@ -9,15 +9,21 @@ use std::rc::Rc; pub struct TypeUse { declared_name: String, declared_name_source_range: SourceRange, + generic_arguments: Vec, scope_id: Option, type_info: Option, } impl TypeUse { - pub fn new(declared_name: &str, declared_name_source_range: SourceRange) -> Self { + pub fn new( + declared_name: &str, + declared_name_source_range: SourceRange, + generic_arguments: Vec, + ) -> Self { Self { declared_name: declared_name.into(), declared_name_source_range, + generic_arguments, scope_id: None, type_info: None, } diff --git a/dmc-lib/src/lexer.rs b/dmc-lib/src/lexer.rs index 423c148..cd1c8c6 100644 --- a/dmc-lib/src/lexer.rs +++ b/dmc-lib/src/lexer.rs @@ -54,10 +54,6 @@ impl<'a> Lexer<'a> { Token::new(self.position, self.position + 1, TokenKind::Modulo) } else if chunk.starts_with("+") { Token::new(self.position, self.position + 1, TokenKind::Plus) - } else if chunk.starts_with("<<") { - Token::new(self.position, self.position + 2, TokenKind::LeftShift) - } else if chunk.starts_with(">>") { - Token::new(self.position, self.position + 2, TokenKind::RightShift) } else if chunk.starts_with("&") { Token::new(self.position, self.position + 1, TokenKind::Ampersand) } else if chunk.starts_with("^") { @@ -76,6 +72,10 @@ impl<'a> Lexer<'a> { Token::new(self.position, self.position + 1, TokenKind::LeftSquare) } else if chunk.starts_with("]") { Token::new(self.position, self.position + 1, TokenKind::RightSquare) + } else if chunk.starts_with("<") { + Token::new(self.position, self.position + 1, TokenKind::Lt) + } else if chunk.starts_with(">") { + Token::new(self.position, self.position + 1, TokenKind::Gt) } else { // more than one char token if chunk.starts_with(|c: char| c.is_ascii_digit()) { diff --git a/dmc-lib/src/parser.rs b/dmc-lib/src/parser.rs index 4b81164..8e0bbb2 100644 --- a/dmc-lib/src/parser.rs +++ b/dmc-lib/src/parser.rs @@ -56,6 +56,12 @@ macro_rules! matches_statement_first { }; } +macro_rules! matches_type_use_first { + ( $token_kind: expr ) => { + matches!($token_kind, TokenKind::LeftSquare | TokenKind::Identifier) + }; +} + struct Parser<'a> { input: &'a str, lexer: Lexer<'a>, @@ -163,6 +169,26 @@ impl<'a> Parser<'a> { } } + fn expect_position_advance( + &mut self, + token_kind: TokenKind, + start_position: usize, + ) -> Result> { + let matched = self.expect_advance(token_kind)?; + if matched.start() == start_position { + Ok(matched) + } else { + Err(vec![ + Diagnostic::new( + &format!("Expected {:?} but found {:?}", token_kind, matched.kind()), + matched.start(), + matched.end(), + ) + .with_reporter(file!(), line!()), + ]) + } + } + fn peek_current(&self, token_kind: TokenKind) -> bool { match &self.current { None => panic!("Unexpected end of input."), @@ -488,9 +514,19 @@ impl<'a> Parser<'a> { } TokenKind::Identifier => { let identifier_token = self.expect_advance(TokenKind::Identifier)?; + let generic_arguments = + if self.current.is_some() && self.peek_current(TokenKind::Lt) { + self.advance(); // < + let generic_arguments = self.generic_arguments_list()?; + self.expect_advance(TokenKind::Gt)?; // > + generic_arguments + } else { + vec![] + }; Ok(TypeUse::new( self.token_text(&identifier_token), SourceRange::new(identifier_token.start(), identifier_token.end()), + generic_arguments, )) } _ => Err(vec![Diagnostic::new( @@ -510,6 +546,19 @@ impl<'a> Parser<'a> { )]) } + fn generic_arguments_list(&mut self) -> Result, Vec> { + let mut generic_arguments: Vec = vec![]; + while self.current.is_some() && matches_type_use_first!(self.get_current().kind()) { + generic_arguments.push(self.type_use()?); + if self.current.is_some() && self.peek_current(TokenKind::Comma) { + self.advance(); // comma + } else { + break; + } + } + Ok(generic_arguments) + } + fn public_class_member( &mut self, fields: &mut Vec, @@ -747,8 +796,10 @@ impl<'a> Parser<'a> { while self.current.is_some() { let current = self.get_current(); match current.kind() { - TokenKind::LeftShift => { - self.advance(); // left shift + TokenKind::Lt => { + let second_lt_start = current.start() + 1; + self.advance(); // first < + self.expect_position_advance(TokenKind::Lt, second_lt_start)?; // second < let rhs = self.additive_expression()?; let source_range = SourceRange::new(result.source_range().start(), rhs.source_range().end()); @@ -759,8 +810,10 @@ impl<'a> Parser<'a> { source_range, )); } - TokenKind::RightShift => { - self.advance(); // right shift + TokenKind::Gt => { + let second_gt_start = current.start() + 1; + self.advance(); // first > + self.expect_position_advance(TokenKind::Gt, second_gt_start)?; // second gt let rhs = self.additive_expression()?; let source_range = SourceRange::new(result.source_range().start(), rhs.source_range().end()); @@ -1170,6 +1223,16 @@ mod smoke_tests { ", ) } + + #[test] + fn array_generic_arg() { + smoke_test("fn main(args: Array) end"); + } + + #[test] + fn nested_generic_args() { + smoke_test("fn main(foo: Array>) end"); + } } #[cfg(test)] diff --git a/dmc-lib/src/token.rs b/dmc-lib/src/token.rs index be7bef6..5783f15 100644 --- a/dmc-lib/src/token.rs +++ b/dmc-lib/src/token.rs @@ -45,8 +45,6 @@ pub enum TokenKind { Star, Slash, Modulo, - LeftShift, - RightShift, Ampersand, Caret, Bar, @@ -58,4 +56,6 @@ pub enum TokenKind { Ctor, LeftSquare, RightSquare, + Lt, + Gt, }