Lexing/parsing generic args.

This commit is contained in:
Jesse Brault 2026-03-16 10:21:00 -05:00
parent b4094bf570
commit 3466908a80
5 changed files with 81 additions and 12 deletions

View File

@ -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![]),
),
);
}

View File

@ -9,15 +9,21 @@ use std::rc::Rc;
pub struct TypeUse {
declared_name: String,
declared_name_source_range: SourceRange,
generic_arguments: Vec<TypeUse>,
scope_id: Option<usize>,
type_info: Option<TypeInfo>,
}
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<TypeUse>,
) -> Self {
Self {
declared_name: declared_name.into(),
declared_name_source_range,
generic_arguments,
scope_id: None,
type_info: None,
}

View File

@ -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()) {

View File

@ -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<Token, Vec<Diagnostic>> {
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<TypeUse>, Vec<Diagnostic>> {
let mut generic_arguments: Vec<TypeUse> = 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<Field>,
@ -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<String>) end");
}
#[test]
fn nested_generic_args() {
smoke_test("fn main(foo: Array<Bar<Foo>>) end");
}
}
#[cfg(test)]

View File

@ -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,
}