use crate::common::diagnostics::{Diagnostic, DiagnosticBundle, Severity}; use crate::common::spans::{FileId, Span}; use crate::frontends::pbs::ast::*; use crate::frontends::pbs::lexer::Lexer; use crate::frontends::pbs::token::{Token, TokenKind}; use prometeu_analysis::{NameId, NameInterner, NodeId}; pub struct Parser<'a> { tokens: Vec, pos: usize, file_id: FileId, errors: Vec, interner: &'a mut NameInterner, arena: AstArena, builtin_none: NameId, builtin_some: NameId, builtin_ok: NameId, builtin_err: NameId, builtin_true: NameId, builtin_false: NameId, builtin_void: NameId, builtin_array: NameId, builtin_optional: NameId, builtin_result: NameId, builtin_bounded: NameId, } impl<'a> Parser<'a> { pub fn new(source: &str, file_id: FileId, interner: &'a mut NameInterner) -> Self { let mut lexer = Lexer::new(source, file_id); let mut tokens = Vec::new(); loop { let token = lexer.next_token(); let is_eof = token.kind == TokenKind::Eof; tokens.push(token); if is_eof { break; } } let builtin_none = interner.intern("none"); let builtin_some = interner.intern("some"); let builtin_ok = interner.intern("ok"); let builtin_err = interner.intern("err"); let builtin_true = interner.intern("true"); let builtin_false = interner.intern("false"); let builtin_void = interner.intern("void"); let builtin_array = interner.intern("array"); let builtin_optional = interner.intern("optional"); let builtin_result = interner.intern("result"); let builtin_bounded = interner.intern("bounded"); Self { tokens, pos: 0, file_id, errors: Vec::new(), interner, arena: AstArena::default(), builtin_none, builtin_some, builtin_ok, builtin_err, builtin_true, builtin_false, builtin_void, builtin_array, builtin_optional, builtin_result, builtin_bounded, } } pub fn parse_file(&mut self) -> Result { let start_span = self.peek().span.clone(); let mut imports = Vec::new(); let mut decls = Vec::new(); while self.peek().kind != TokenKind::Eof { if self.peek().kind == TokenKind::Import { match self.parse_import() { Ok(imp) => imports.push(imp), Err(_) => self.recover_to_top_level(), } } else { match self.parse_top_level_decl() { Ok(decl) => decls.push(decl), Err(_) => self.recover_to_top_level(), } } } let end_span = self.peek().span.clone(); if !self.errors.is_empty() { return Err(DiagnosticBundle { diagnostics: self.errors.clone(), }); } let span = Span::new(self.file_id, start_span.start, end_span.end); let root = self.arena.push(NodeKind::File(FileNodeArena { imports, decls }), span); self.arena.roots.push(root); Ok(ParsedAst { arena: std::mem::take(&mut self.arena), root, }) } fn recover_to_top_level(&mut self) { while self.peek().kind != TokenKind::Eof { match self.peek().kind { TokenKind::Import | TokenKind::Fn | TokenKind::Pub | TokenKind::Mod | TokenKind::Declare | TokenKind::Service => break, _ => self.advance(), }; } } fn parse_import(&mut self) -> Result { let start_span = self.consume(TokenKind::Import)?.span; let spec = self.parse_import_spec()?; self.consume(TokenKind::Identifier("from".to_string()))?; let path_tok = match self.peek().kind { TokenKind::StringLit(ref s) => { let s = s.clone(); let span = self.advance().span; (s, span) } _ => return Err(self.error_with_code("Expected string literal after 'from'", Some("E_PARSE_EXPECTED_TOKEN"))), }; if self.peek().kind == TokenKind::Semicolon { self.advance(); } let span = Span::new(self.file_id, start_span.start, path_tok.1.end); Ok(self.arena.push( NodeKind::Import(ImportNodeArena { spec, from: path_tok.0, }), span, )) } fn parse_import_spec(&mut self) -> Result { let mut path = Vec::new(); let start_span = self.peek().span.clone(); if self.peek().kind == TokenKind::OpenBrace { self.advance(); // { loop { let name = match self.peek().kind.clone() { TokenKind::Identifier(name) => name, _ => return Err(self.error("Expected identifier in import spec")), }; self.advance(); path.push(self.interner.intern(&name)); if self.peek().kind == TokenKind::Comma { self.advance(); } else if self.peek().kind == TokenKind::CloseBrace { break; } else { return Err(self.error("Expected ',' or '}' in import spec")); } } self.consume(TokenKind::CloseBrace)?; } else { loop { let name = match self.peek().kind.clone() { TokenKind::Identifier(name) => name, _ => return Err(self.error("Expected identifier in import spec")), }; self.advance(); path.push(self.interner.intern(&name)); if self.peek().kind == TokenKind::Dot { self.advance(); } else { break; } } } let end_span = self.tokens[self.pos - 1].span.clone(); let span = Span::new(self.file_id, start_span.start, end_span.end); Ok(self.arena.push(NodeKind::ImportSpec(ImportSpecNodeArena { path }), span)) } fn parse_top_level_decl(&mut self) -> Result { match self.peek().kind { TokenKind::Fn => self.parse_fn_decl(None), TokenKind::Pub | TokenKind::Mod | TokenKind::Declare | TokenKind::Service => self.parse_decl(), TokenKind::Invalid(ref msg) => { let code = if msg.contains("Unterminated string") { "E_LEX_UNTERMINATED_STRING" } else { "E_LEX_INVALID_CHAR" }; let msg = msg.clone(); Err(self.error_with_code(&msg, Some(code))) } _ => Err(self.error_with_code("Expected top-level declaration", Some("E_PARSE_UNEXPECTED_TOKEN"))), } } fn parse_decl(&mut self) -> Result { let vis = if self.peek().kind == TokenKind::Pub { self.advance(); Some("pub".to_string()) } else if self.peek().kind == TokenKind::Mod { self.advance(); Some("mod".to_string()) } else { None }; match self.peek().kind { TokenKind::Service => self.parse_service_decl(vis), TokenKind::Declare => self.parse_type_decl(vis), TokenKind::Fn => self.parse_fn_decl(vis), _ => Err(self.error("Expected 'service', 'declare', or 'fn'")), } } fn parse_service_decl(&mut self, vis: Option) -> Result { let start_span = self.consume(TokenKind::Service)?.span; let name = self.expect_identifier()?; let mut extends = None; if self.peek().kind == TokenKind::Colon { self.advance(); extends = Some(self.expect_identifier()?); } self.consume(TokenKind::OpenBrace)?; let mut members = Vec::new(); while self.peek().kind != TokenKind::CloseBrace && self.peek().kind != TokenKind::Eof { members.push(self.parse_service_member()?); // Optional semicolon after signature if self.peek().kind == TokenKind::Semicolon { self.advance(); } } let end_span = self.consume(TokenKind::CloseBrace)?.span; let span = Span::new(self.file_id, start_span.start, end_span.end); Ok(self.arena.push( NodeKind::ServiceDecl(ServiceDeclNodeArena { vis, name, extends, members, }), span, )) } fn parse_service_member(&mut self) -> Result { let start_span = self.consume(TokenKind::Fn)?.span; let name = self.expect_identifier()?; let params = self.parse_param_list()?; let ret = if self.peek().kind == TokenKind::Colon { self.advance(); self.parse_type_ref()? } else { self.arena.push( NodeKind::TypeName(TypeNameNodeArena { name: self.builtin_void, }), Span::new(self.file_id, 0, 0), ) }; let span = Span::new(self.file_id, start_span.start, self.arena.span(ret).end); Ok(self.arena.push( NodeKind::ServiceFnSig(ServiceFnSigNodeArena { name, params, ret }), span, )) } fn parse_type_decl(&mut self, vis: Option) -> Result { let start_span = self.consume(TokenKind::Declare)?.span; let type_kind = match self.peek().kind { TokenKind::Struct => { self.advance(); "struct".to_string() } TokenKind::Contract => { self.advance(); "contract".to_string() } TokenKind::Error => { self.advance(); "error".to_string() } _ => return Err(self.error_with_code("Expected 'struct', 'contract', or 'error'", Some("E_PARSE_EXPECTED_TOKEN"))), }; let name = self.expect_identifier()?; let mut params = Vec::new(); if self.peek().kind == TokenKind::OpenParen { params = self.parse_param_list()?; } let mut constructors = Vec::new(); if self.peek().kind == TokenKind::OpenBracket { constructors = self.parse_constructor_list()?; } let mut is_host = false; if self.peek().kind == TokenKind::Host { self.advance(); is_host = true; } let mut constants = Vec::new(); if self.peek().kind == TokenKind::OpenDoubleBracket { self.advance(); while self.peek().kind != TokenKind::CloseDoubleBracket && self.peek().kind != TokenKind::Eof { let c_start = self.peek().span.start; let c_name = self.expect_identifier()?; self.consume(TokenKind::Colon)?; let c_value = self.parse_expr(0)?; let c_span = Span::new(self.file_id, c_start, self.arena.span(c_value).end); constants.push(self.arena.push( NodeKind::ConstantDecl(ConstantDeclNodeArena { name: c_name, value: c_value, }), c_span, )); if self.peek().kind == TokenKind::Comma { self.advance(); } } self.consume(TokenKind::CloseDoubleBracket)?; } let mut body = None; if self.peek().kind == TokenKind::OpenBrace { body = Some(self.parse_type_body()?); } let mut end_pos = start_span.end; if let Some(b) = body { end_pos = self.arena.span(b).end; body = Some(b); } else if !constants.is_empty() { // We should use the CloseDoubleBracket span here, but I don't have it easily // Let's just use the last constant's end end_pos = self.arena.span(*constants.last().unwrap()).end; } else if !params.is_empty() { end_pos = params.last().unwrap().span.end; } let span = Span::new(self.file_id, start_span.start, end_pos); Ok(self.arena.push( NodeKind::TypeDecl(TypeDeclNodeArena { vis, type_kind, name, is_host, params, constructors, constants, body, }), span, )) } fn parse_type_body(&mut self) -> Result { let start_span = self.consume(TokenKind::OpenBrace)?.span; let mut members = Vec::new(); let mut methods = Vec::new(); while self.peek().kind != TokenKind::CloseBrace && self.peek().kind != TokenKind::Eof { if self.peek().kind == TokenKind::Fn { let sig_node = self.parse_service_member()?; methods.push(sig_node); if self.peek().kind == TokenKind::Semicolon { self.advance(); } } else { let m_start = self.peek().span.start; let name = self.expect_identifier()?; self.consume(TokenKind::Colon)?; let ty = self.parse_type_ref()?; let m_end = self.arena.span(ty).end; members.push(TypeMemberNodeArena { span: Span::new(self.file_id, m_start, m_end), name, ty, }); if self.peek().kind == TokenKind::Comma { self.advance(); } else if self.peek().kind == TokenKind::Semicolon { self.advance(); } } } let end_span = self.consume(TokenKind::CloseBrace)?.span; let span = Span::new(self.file_id, start_span.start, end_span.end); Ok(self.arena.push( NodeKind::TypeBody(TypeBodyNodeArena { members, methods }), span, )) } fn parse_fn_decl(&mut self, vis: Option) -> Result { let start_span = self.consume(TokenKind::Fn)?.span; let name = self.expect_identifier()?; let params = self.parse_param_list()?; let _ret = if self.peek().kind == TokenKind::Colon { self.advance(); Some(self.parse_type_ref()?) } else { None }; let mut else_fallback = None; if self.peek().kind == TokenKind::Else { self.advance(); else_fallback = Some(self.parse_block()?); } let body = self.parse_block()?; let body_span = self.arena.span(body); let span = Span::new(self.file_id, start_span.start, body_span.end); Ok(self.arena.push( NodeKind::FnDecl(FnDeclNodeArena { vis, name, params, ret: _ret, else_fallback, body, }), span, )) } fn parse_param_list(&mut self) -> Result, DiagnosticBundle> { self.consume(TokenKind::OpenParen)?; let mut params = Vec::new(); while self.peek().kind != TokenKind::CloseParen && self.peek().kind != TokenKind::Eof { let p_start = self.peek().span.start; let name = self.expect_identifier()?; self.consume(TokenKind::Colon)?; let ty = self.parse_type_ref()?; let p_end = self.arena.span(ty).end; params.push(ParamNodeArena { span: Span::new(self.file_id, p_start, p_end), name, ty, }); if self.peek().kind == TokenKind::Comma { self.advance(); } else { break; } } self.consume(TokenKind::CloseParen)?; Ok(params) } fn parse_type_ref(&mut self) -> Result { let id_tok = self.peek().clone(); let name = match id_tok.kind { TokenKind::Identifier(ref s) => { self.advance(); self.interner.intern(s) } TokenKind::Optional => { self.advance(); self.builtin_optional } TokenKind::Result => { self.advance(); self.builtin_result } TokenKind::Bounded => { self.advance(); self.builtin_bounded } TokenKind::None => { self.advance(); self.builtin_none } TokenKind::Some => { self.advance(); self.builtin_some } TokenKind::Ok => { self.advance(); self.builtin_ok } TokenKind::Err => { self.advance(); self.builtin_err } _ => return Err(self.error_with_code("Expected type name", Some("E_PARSE_EXPECTED_TOKEN"))), }; let mut node = if self.peek().kind == TokenKind::Lt { self.advance(); // < let mut args = Vec::new(); loop { args.push(self.parse_type_ref()?); if self.peek().kind == TokenKind::Comma { self.advance(); } else { break; } } let end_tok = self.consume(TokenKind::Gt)?; self.arena.push( NodeKind::TypeApp(TypeAppNodeArena { base: name, args }), Span::new(self.file_id, id_tok.span.start, end_tok.span.end), ) } else { self.arena.push( NodeKind::TypeName(TypeNameNodeArena { name }), id_tok.span, ) }; if self.peek().kind == TokenKind::OpenBracket { self.advance(); let size_tok = self.peek().clone(); let size = match size_tok.kind { TokenKind::IntLit(v) => { self.advance(); v as u32 } TokenKind::BoundedLit(v) => { self.advance(); v } _ => { return Err(self.error_with_code( "integer or bounded literal for array size", Some("E_PARSE_EXPECTED_TOKEN"), )) } }; let end_tok = self.consume(TokenKind::CloseBracket)?; let size_node = self.arena.push( NodeKind::IntLit(IntLitNodeArena { value: size as i64 }), size_tok.span, ); let span = Span::new(self.file_id, self.arena.span(node).start, end_tok.span.end); let mut wrapped = true; let index = node.0 as usize; if let NodeKind::TypeApp(ta) = &mut self.arena.nodes[index] { if ta.base == self.builtin_array { ta.args.push(size_node); self.arena.spans[index] = span.clone(); wrapped = false; } } if wrapped { node = self.arena.push( NodeKind::TypeApp(TypeAppNodeArena { base: self.builtin_array, args: vec![node, size_node], }), span, ); } } Ok(node) } fn parse_block(&mut self) -> Result { let start_span = self.consume(TokenKind::OpenBrace)?.span; let mut stmts = Vec::new(); let mut tail = None; while self.peek().kind != TokenKind::CloseBrace && self.peek().kind != TokenKind::Eof { if self.peek().kind == TokenKind::Let { stmts.push(self.parse_let_stmt()?); } else if self.peek().kind == TokenKind::Return { stmts.push(self.parse_return_stmt()?); } else { let expr = self.parse_expr(0)?; if self.peek().kind == TokenKind::Semicolon { let semi_span = self.advance().span; let expr_start = self.arena.span(expr).start; let span = Span::new(self.file_id, expr_start, semi_span.end); stmts.push(self.arena.push( NodeKind::ExprStmt(ExprStmtNodeArena { expr }), span, )); } else if self.peek().kind == TokenKind::CloseBrace { tail = Some(expr); } else { // Treat as ExprStmt even without semicolon (e.g. for if/when used as statement) let expr_span = self.arena.span(expr); stmts.push(self.arena.push( NodeKind::ExprStmt(ExprStmtNodeArena { expr }), expr_span, )); } } } let end_span = self.consume(TokenKind::CloseBrace)?.span; let span = Span::new(self.file_id, start_span.start, end_span.end); Ok(self.arena.push(NodeKind::Block(BlockNodeArena { stmts, tail }), span)) } fn parse_let_stmt(&mut self) -> Result { let start_span = self.consume(TokenKind::Let)?.span; let is_mut = if self.peek().kind == TokenKind::Mut { self.advance(); true } else { false }; let name = self.expect_identifier()?; let ty = if self.peek().kind == TokenKind::Colon { self.advance(); Some(self.parse_type_ref()?) } else { None }; self.consume(TokenKind::Assign)?; let init = self.parse_expr(0)?; let end_span = self.consume(TokenKind::Semicolon)?.span; let span = Span::new(self.file_id, start_span.start, end_span.end); Ok(self.arena.push( NodeKind::LetStmt(LetStmtNodeArena { name, is_mut, ty, init, }), span, )) } fn parse_return_stmt(&mut self) -> Result { let start_span = self.consume(TokenKind::Return)?.span; let mut expr = None; if self.peek().kind != TokenKind::Semicolon { expr = Some(self.parse_expr(0)?); } let end_span = self.consume(TokenKind::Semicolon)?.span; let span = Span::new(self.file_id, start_span.start, end_span.end); Ok(self.arena.push(NodeKind::ReturnStmt(ReturnStmtNodeArena { expr }), span)) } fn parse_alloc(&mut self) -> Result { let start_span = self.consume(TokenKind::Alloc)?.span; let ty = self.parse_type_ref()?; let span = Span::new(self.file_id, start_span.start, self.arena.span(ty).end); Ok(self.arena.push(NodeKind::Alloc(AllocNodeArena { ty }), span)) } fn parse_mutate_borrow_peek(&mut self, kind: TokenKind) -> Result { let start_span = self.consume(kind.clone())?.span; let target_expr = self.parse_expr(0)?; let (target, binding) = if let NodeKind::Cast(cast) = self.arena.kind(target_expr) { let binding = match self.arena.kind(cast.ty) { NodeKind::Ident(id) => Some(id.name), NodeKind::TypeName(tn) => Some(tn.name), _ => None, }; if let Some(binding) = binding { (cast.expr, binding) } else { return Err(self.error_with_code( "Expected binding name after 'as'", Some("E_PARSE_EXPECTED_TOKEN"), )); } } else { self.consume(TokenKind::As)?; let binding = self.expect_identifier()?; (target_expr, binding) }; let body = self.parse_block()?; let span = Span::new(self.file_id, start_span.start, self.arena.span(body).end); match kind { TokenKind::Mutate => Ok(self.arena.push( NodeKind::Mutate(MutateNodeArena { target, binding, body }), span, )), TokenKind::Borrow => Ok(self.arena.push( NodeKind::Borrow(BorrowNodeArena { target, binding, body }), span, )), TokenKind::Peek => Ok(self.arena.push( NodeKind::Peek(PeekNodeArena { target, binding, body }), span, )), _ => unreachable!(), } } fn parse_expr(&mut self, min_precedence: u8) -> Result { let mut left = self.parse_primary()?; loop { let (op, precedence) = match self.get_binary_precedence() { Some((op, p)) if p >= min_precedence => (op, p), _ => break, }; self.advance(); let right = self.parse_expr(precedence + 1)?; let span = Span::new( self.file_id, self.arena.span(left).start, self.arena.span(right).end, ); left = self.arena.push( NodeKind::Binary(BinaryNodeArena { op, left, right }), span, ); } Ok(left) } fn parse_primary(&mut self) -> Result { let tok = self.peek().clone(); match tok.kind { TokenKind::IntLit(v) => { self.advance(); Ok(self.arena.push( NodeKind::IntLit(IntLitNodeArena { value: v }), tok.span, )) } TokenKind::FloatLit(v) => { self.advance(); Ok(self.arena.push( NodeKind::FloatLit(FloatLitNodeArena { value: v }), tok.span, )) } TokenKind::BoundedLit(v) => { self.advance(); Ok(self.arena.push( NodeKind::BoundedLit(BoundedLitNodeArena { value: v }), tok.span, )) } TokenKind::StringLit(s) => { self.advance(); Ok(self.arena.push( NodeKind::StringLit(StringLitNodeArena { value: s }), tok.span, )) } TokenKind::Identifier(name) => { self.advance(); let name = self.interner.intern(&name); let mut node = self.arena.push( NodeKind::Ident(IdentNodeArena { name }), tok.span, ); loop { if self.peek().kind == TokenKind::OpenParen { node = self.parse_call(node)?; } else if self.peek().kind == TokenKind::As { node = self.parse_cast(node)?; } else if self.peek().kind == TokenKind::Dot { node = self.parse_member_access(node)?; } else { break; } } Ok(node) } TokenKind::None | TokenKind::Some | TokenKind::Ok | TokenKind::Err => { let name = match tok.kind { TokenKind::None => self.builtin_none, TokenKind::Some => self.builtin_some, TokenKind::Ok => self.builtin_ok, TokenKind::Err => self.builtin_err, _ => unreachable!(), }; self.advance(); let mut node = self.arena.push( NodeKind::Ident(IdentNodeArena { name }), tok.span, ); loop { if self.peek().kind == TokenKind::OpenParen { node = self.parse_call(node)?; } else if self.peek().kind == TokenKind::As { node = self.parse_cast(node)?; } else if self.peek().kind == TokenKind::Dot { node = self.parse_member_access(node)?; } else { break; } } Ok(node) } TokenKind::OpenParen => { self.advance(); let expr = self.parse_expr(0)?; self.consume(TokenKind::CloseParen)?; Ok(expr) } TokenKind::OpenBrace => self.parse_block(), TokenKind::If => self.parse_if_expr(), TokenKind::When => self.parse_when_expr(), TokenKind::Alloc => self.parse_alloc(), TokenKind::Mutate => self.parse_mutate_borrow_peek(TokenKind::Mutate), TokenKind::Borrow => self.parse_mutate_borrow_peek(TokenKind::Borrow), TokenKind::Peek => self.parse_mutate_borrow_peek(TokenKind::Peek), TokenKind::Minus | TokenKind::Not => { self.advance(); let op = match tok.kind { TokenKind::Minus => "-".to_string(), TokenKind::Not => "!".to_string(), _ => unreachable!(), }; let expr = self.parse_expr(11)?; let span = Span::new(self.file_id, tok.span.start, self.arena.span(expr).end); Ok(self.arena.push( NodeKind::Unary(UnaryNodeArena { op, expr }), span, )) } TokenKind::Invalid(msg) => { let code = if msg.contains("Unterminated string") { "E_LEX_UNTERMINATED_STRING" } else { "E_LEX_INVALID_CHAR" }; Err(self.error_with_code(&msg, Some(code))) } _ => Err(self.error_with_code("Expected expression", Some("E_PARSE_UNEXPECTED_TOKEN"))), } } fn parse_member_access(&mut self, object: NodeId) -> Result { self.consume(TokenKind::Dot)?; let member = self.expect_identifier()?; let span = Span::new( self.file_id, self.arena.span(object).start, self.tokens[self.pos - 1].span.end, ); Ok(self.arena.push( NodeKind::MemberAccess(MemberAccessNodeArena { object, member }), span, )) } fn parse_call(&mut self, callee: NodeId) -> Result { self.consume(TokenKind::OpenParen)?; let mut args = Vec::new(); while self.peek().kind != TokenKind::CloseParen && self.peek().kind != TokenKind::Eof { args.push(self.parse_expr(0)?); if self.peek().kind == TokenKind::Comma { self.advance(); } else { break; } } let end_span = self.consume(TokenKind::CloseParen)?.span; let span = Span::new(self.file_id, self.arena.span(callee).start, end_span.end); Ok(self.arena.push( NodeKind::Call(CallNodeArena { callee, args }), span, )) } fn parse_cast(&mut self, expr: NodeId) -> Result { self.consume(TokenKind::As)?; let ty = self.parse_type_ref()?; let span = Span::new(self.file_id, self.arena.span(expr).start, self.arena.span(ty).end); Ok(self.arena.push(NodeKind::Cast(CastNodeArena { expr, ty }), span)) } fn parse_if_expr(&mut self) -> Result { let start_span = self.consume(TokenKind::If)?.span; let cond = self.parse_expr(0)?; let then_block = self.parse_block()?; let mut else_block = None; if self.peek().kind == TokenKind::Else { self.advance(); if self.peek().kind == TokenKind::If { else_block = Some(self.parse_if_expr()?); } else { else_block = Some(self.parse_block()?); } } let end_span = else_block .map(|b| self.arena.span(b).end) .unwrap_or(self.arena.span(then_block).end); let span = Span::new(self.file_id, start_span.start, end_span); Ok(self.arena.push( NodeKind::IfExpr(IfExprNodeArena { cond, then_block, else_block, }), span, )) } fn parse_when_expr(&mut self) -> Result { let start_span = self.consume(TokenKind::When)?.span; self.consume(TokenKind::OpenBrace)?; let mut arms = Vec::new(); while self.peek().kind != TokenKind::CloseBrace && self.peek().kind != TokenKind::Eof { let arm_start = self.peek().span.start; let cond = self.parse_expr(0)?; self.consume(TokenKind::Arrow)?; let body = self.parse_block()?; let arm_span = Span::new(self.file_id, arm_start, self.arena.span(body).end); arms.push(self.arena.push( NodeKind::WhenArm(WhenArmNodeArena { cond, body }), arm_span, )); if self.peek().kind == TokenKind::Comma { self.advance(); } } let end_span = self.consume(TokenKind::CloseBrace)?.span; let span = Span::new(self.file_id, start_span.start, end_span.end); Ok(self.arena.push(NodeKind::WhenExpr(WhenExprNodeArena { arms }), span)) } fn get_binary_precedence(&self) -> Option<(String, u8)> { match self.peek().kind { TokenKind::Plus => Some(("+".to_string(), 5)), TokenKind::Minus => Some(("-".to_string(), 5)), TokenKind::Star => Some(("*".to_string(), 4)), TokenKind::Slash => Some(("/".to_string(), 4)), TokenKind::Percent => Some(("%".to_string(), 4)), TokenKind::Lt => Some(("<".to_string(), 7)), TokenKind::Lte => Some(("<=".to_string(), 7)), TokenKind::Gt => Some((">".to_string(), 7)), TokenKind::Gte => Some((">=".to_string(), 7)), TokenKind::Eq => Some(("==".to_string(), 8)), TokenKind::Neq => Some(("!=".to_string(), 8)), TokenKind::And => Some(("&&".to_string(), 9)), TokenKind::Or => Some(("||".to_string(), 10)), _ => None, } } fn peek(&self) -> &Token { &self.tokens[self.pos] } fn advance(&mut self) -> Token { let tok = self.tokens[self.pos].clone(); if tok.kind != TokenKind::Eof { self.pos += 1; } tok } fn consume(&mut self, kind: TokenKind) -> Result { let peeked_kind = self.peek().kind.clone(); if peeked_kind == kind { Ok(self.advance()) } else { if let TokenKind::Invalid(ref msg) = peeked_kind { let code = if msg.contains("Unterminated string") { "E_LEX_UNTERMINATED_STRING" } else { "E_LEX_INVALID_CHAR" }; let msg = msg.clone(); Err(self.error_with_code(&msg, Some(code))) } else { Err(self.error_with_code(&format!("Expected {:?}, found {:?}", kind, peeked_kind), Some("E_PARSE_EXPECTED_TOKEN"))) } } } fn expect_identifier(&mut self) -> Result { let peeked_kind = self.peek().kind.clone(); match peeked_kind { TokenKind::Identifier(name) => { self.advance(); Ok(self.interner.intern(&name)) } TokenKind::Optional => { self.advance(); Ok(self.builtin_optional) } TokenKind::Result => { self.advance(); Ok(self.builtin_result) } TokenKind::None => { self.advance(); Ok(self.builtin_none) } TokenKind::Some => { self.advance(); Ok(self.builtin_some) } TokenKind::Ok => { self.advance(); Ok(self.builtin_ok) } TokenKind::Err => { self.advance(); Ok(self.builtin_err) } TokenKind::Bounded => { self.advance(); Ok(self.builtin_bounded) } TokenKind::Invalid(msg) => { let code = if msg.contains("Unterminated string") { "E_LEX_UNTERMINATED_STRING" } else { "E_LEX_INVALID_CHAR" }; Err(self.error_with_code(&msg, Some(code))) } _ => Err(self.error_with_code(&format!("Expected identifier, found {:?}", peeked_kind), Some("E_PARSE_EXPECTED_TOKEN"))), } } fn error(&mut self, message: &str) -> DiagnosticBundle { self.error_with_code(message, None) } fn error_with_code(&mut self, message: &str, code: Option<&str>) -> DiagnosticBundle { let diag = Diagnostic { severity: Severity::Error, code: code.unwrap_or("E_PARSE_ERROR").to_string(), message: message.to_string(), span: self.peek().span.clone(), related: Vec::new(), }; self.errors.push(diag.clone()); DiagnosticBundle::from(diag) } fn parse_constructor_list(&mut self) -> Result, DiagnosticBundle> { self.consume(TokenKind::OpenBracket)?; let mut constructors = Vec::new(); while self.peek().kind != TokenKind::CloseBracket && self.peek().kind != TokenKind::Eof { let start_span = self.peek().span.clone(); let params = self.parse_param_list()?; self.consume(TokenKind::Colon)?; let mut initializers = Vec::new(); if self.peek().kind == TokenKind::OpenParen { self.advance(); while self.peek().kind != TokenKind::CloseParen && self.peek().kind != TokenKind::Eof { initializers.push(self.parse_expr(0)?); if self.peek().kind == TokenKind::Comma { self.advance(); } } self.consume(TokenKind::CloseParen)?; } else { initializers.push(self.parse_expr(0)?); } self.consume(TokenKind::As)?; let name = self.expect_identifier()?; let body = self.parse_block()?; let span = Span::new(self.file_id, start_span.start, self.arena.span(body).end); constructors.push(self.arena.push( NodeKind::ConstructorDecl(ConstructorDeclNodeArena { params, initializers, name, body, }), span, )); } self.consume(TokenKind::CloseBracket)?; Ok(constructors) } } #[cfg(test)] mod tests { use super::*; use serde_json; use crate::common::spans::FileId; #[test] fn test_parse_empty_file() { let mut interner = NameInterner::new(); let mut parser = Parser::new("", FileId(0), &mut interner); let parsed = parser.parse_file().unwrap(); let file = match parsed.arena.kind(parsed.root) { NodeKind::File(file) => file, _ => panic!("Expected File"), }; assert_eq!(file.imports.len(), 0); assert_eq!(file.decls.len(), 0); } #[test] fn test_parse_imports() { let source = r#" import std.io from "std"; import math from "./math.pbs"; "#; let mut interner = NameInterner::new(); let mut parser = Parser::new(source, FileId(0), &mut interner); let parsed = parser.parse_file().unwrap(); let file = match parsed.arena.kind(parsed.root) { NodeKind::File(file) => file, _ => panic!("Expected File"), }; assert_eq!(file.imports.len(), 2); let imp = match parsed.arena.kind(file.imports[0]) { NodeKind::Import(imp) => imp, _ => panic!("Expected Import"), }; assert_eq!(imp.from, "std"); let spec = match parsed.arena.kind(imp.spec) { NodeKind::ImportSpec(spec) => spec, _ => panic!("Expected ImportSpec"), }; let path: Vec<&str> = spec.path.iter().map(|id| interner.resolve(*id)).collect(); assert_eq!(path, vec!["std", "io"]); } #[test] fn test_parse_fn_decl() { let source = r#" fn add(a: int, b: int): int { return a + b; } "#; let mut interner = NameInterner::new(); let mut parser = Parser::new(source, FileId(0), &mut interner); let parsed = parser.parse_file().unwrap(); let file = match parsed.arena.kind(parsed.root) { NodeKind::File(file) => file, _ => panic!("Expected File"), }; assert_eq!(file.decls.len(), 1); if let NodeKind::FnDecl(f) = parsed.arena.kind(file.decls[0]) { assert_eq!(interner.resolve(f.name), "add"); assert_eq!(f.params.len(), 2); assert_eq!(interner.resolve(f.params[0].name), "a"); assert_eq!(interner.resolve(f.params[1].name), "b"); } else { panic!("Expected FnDecl"); } } #[test] fn test_parse_type_decl() { let source = r#" pub declare struct Point { x: int, y: int } "#; let mut interner = NameInterner::new(); let mut parser = Parser::new(source, FileId(0), &mut interner); let parsed = parser.parse_file().unwrap(); let file = match parsed.arena.kind(parsed.root) { NodeKind::File(file) => file, _ => panic!("Expected File"), }; assert_eq!(file.decls.len(), 1); if let NodeKind::TypeDecl(t) = parsed.arena.kind(file.decls[0]) { assert_eq!(interner.resolve(t.name), "Point"); assert_eq!(t.type_kind, "struct"); assert_eq!(t.vis, Some("pub".to_string())); } else { panic!("Expected TypeDecl"); } } #[test] fn test_parse_service_decl() { let source = r#" pub service Audio { fn play(sound: Sound); fn stop(): bool; } "#; let mut interner = NameInterner::new(); let mut parser = Parser::new(source, FileId(0), &mut interner); let parsed = parser.parse_file().unwrap(); let file = match parsed.arena.kind(parsed.root) { NodeKind::File(file) => file, _ => panic!("Expected File"), }; assert_eq!(file.decls.len(), 1); if let NodeKind::ServiceDecl(s) = parsed.arena.kind(file.decls[0]) { assert_eq!(interner.resolve(s.name), "Audio"); assert_eq!(s.members.len(), 2); } else { panic!("Expected ServiceDecl"); } } #[test] fn test_parse_expressions() { let source = r#" fn main() { let x = 10 + 20 * 30; let y = (x - 5) / 2; foo(x, y); } "#; let mut interner = NameInterner::new(); let mut parser = Parser::new(source, FileId(0), &mut interner); let parsed = parser.parse_file().unwrap(); let file = match parsed.arena.kind(parsed.root) { NodeKind::File(file) => file, _ => panic!("Expected File"), }; assert_eq!(file.decls.len(), 1); } #[test] fn test_parse_if_when() { let source = r#" fn main(x: int) { if x > 0 { print("positive"); } else { print("non-positive"); } let msg = when { x == 0 -> { return "zero"; }, x == 1 -> { return "one"; } }; } "#; let mut interner = NameInterner::new(); let mut parser = Parser::new(source, FileId(0), &mut interner); let parsed = parser.parse_file().unwrap(); let file = match parsed.arena.kind(parsed.root) { NodeKind::File(file) => file, _ => panic!("Expected File"), }; assert_eq!(file.decls.len(), 1); } #[test] fn test_parse_error_recovery() { let source = r#" fn bad() { let x = ; // Missing init let y = 10; } fn good() {} "#; let mut interner = NameInterner::new(); let mut parser = Parser::new(source, FileId(0), &mut interner); let result = parser.parse_file(); assert!(result.is_err()); } #[test] fn test_parse_mod_fn() { let source = "mod fn test() {}"; let mut interner = NameInterner::new(); let mut parser = Parser::new(source, FileId(0), &mut interner); let parsed = parser.parse_file().expect("mod fn should be allowed"); let file = match parsed.arena.kind(parsed.root) { NodeKind::File(file) => file, _ => panic!("Expected File"), }; if let NodeKind::FnDecl(fn_decl) = parsed.arena.kind(file.decls[0]) { assert_eq!(fn_decl.vis, Some("mod".to_string())); } else { panic!("Expected FnDecl"); } } #[test] fn test_parse_pub_fn() { let source = "pub fn test() {}"; let mut interner = NameInterner::new(); let mut parser = Parser::new(source, FileId(0), &mut interner); let parsed = parser.parse_file().expect("pub fn should be allowed in parser"); let file = match parsed.arena.kind(parsed.root) { NodeKind::File(file) => file, _ => panic!("Expected File"), }; if let NodeKind::FnDecl(fn_decl) = parsed.arena.kind(file.decls[0]) { assert_eq!(fn_decl.vis, Some("pub".to_string())); } else { panic!("Expected FnDecl"); } } #[test] fn test_ast_json_snapshot() { let source = r#" fn main() { return 42; } "#; let mut interner = NameInterner::new(); let mut parser = Parser::new(source, FileId(0), &mut interner); let parsed = parser.parse_file().unwrap(); let file = match parsed.arena.kind(parsed.root) { NodeKind::File(file) => file, _ => panic!("Expected File"), }; let json = serde_json::to_string_pretty(parsed.arena.kind(parsed.root)).unwrap(); assert!(json.contains("\"kind\": \"File\"")); if let NodeKind::FnDecl(fn_decl) = parsed.arena.kind(file.decls[0]) { assert_eq!(interner.resolve(fn_decl.name), "main"); } else { panic!("Expected FnDecl"); } } }