use crate::protocol::ast::*; use super::symbol_table::*; use super::{Module, ModuleCompilationPhase, PassCtx}; use super::tokens::*; use super::token_parsing::*; use super::pass_definitions_types::*; use crate::protocol::input_source::{InputSource, InputPosition, InputSpan, ParseError}; use crate::collections::*; /// Parses all the tokenized definitions into actual AST nodes. pub(crate) struct PassDefinitions { // State associated with the definition currently being processed cur_definition: DefinitionId, // Itty bitty parsing machines type_parser: ParserTypeParser, // Temporary buffers of various kinds buffer: String, struct_fields: ScopedBuffer, enum_variants: ScopedBuffer, union_variants: ScopedBuffer, variables: ScopedBuffer, expressions: ScopedBuffer, statements: ScopedBuffer, parser_types: ScopedBuffer, } impl PassDefinitions { pub(crate) fn new() -> Self { Self{ cur_definition: DefinitionId::new_invalid(), type_parser: ParserTypeParser::new(), buffer: String::with_capacity(128), struct_fields: ScopedBuffer::with_capacity(128), enum_variants: ScopedBuffer::with_capacity(128), union_variants: ScopedBuffer::with_capacity(128), variables: ScopedBuffer::with_capacity(128), expressions: ScopedBuffer::with_capacity(128), statements: ScopedBuffer::with_capacity(128), parser_types: ScopedBuffer::with_capacity(128), } } pub(crate) fn parse(&mut self, modules: &mut [Module], module_idx: usize, ctx: &mut PassCtx) -> Result<(), ParseError> { let module = &modules[module_idx]; let module_range = &module.tokens.ranges[0]; debug_assert_eq!(module.phase, ModuleCompilationPhase::ImportsResolved); debug_assert_eq!(module_range.range_kind, TokenRangeKind::Module); // Although we only need to parse the definitions, we want to go through // code ranges as well such that we can throw errors if we get // unexpected tokens at the module level of the source. let mut range_idx = module_range.first_child_idx; loop { let range_idx_usize = range_idx as usize; let cur_range = &module.tokens.ranges[range_idx_usize]; match cur_range.range_kind { TokenRangeKind::Module => unreachable!(), // should not be reachable TokenRangeKind::Pragma | TokenRangeKind::Import => { // Already fully parsed, fall through and go to next range }, TokenRangeKind::Definition | TokenRangeKind::Code => { // Visit range even if it is a "code" range to provide // proper error messages. self.visit_range(modules, module_idx, ctx, range_idx_usize)?; }, } if cur_range.next_sibling_idx == NO_SIBLING { break; } else { range_idx = cur_range.next_sibling_idx; } } modules[module_idx].phase = ModuleCompilationPhase::DefinitionsParsed; Ok(()) } fn visit_range( &mut self, modules: &[Module], module_idx: usize, ctx: &mut PassCtx, range_idx: usize ) -> Result<(), ParseError> { let module = &modules[module_idx]; let cur_range = &module.tokens.ranges[range_idx]; debug_assert!(cur_range.range_kind == TokenRangeKind::Definition || cur_range.range_kind == TokenRangeKind::Code); // Detect which definition we're parsing let mut iter = module.tokens.iter_range(cur_range); loop { let next = iter.next(); if next.is_none() { return Ok(()) } // Token was not None, so peek_ident returns None if not an ident let ident = peek_ident(&module.source, &mut iter); match ident { Some(KW_STRUCT) => self.visit_struct_definition(module, &mut iter, ctx)?, Some(KW_ENUM) => self.visit_enum_definition(module, &mut iter, ctx)?, Some(KW_UNION) => self.visit_union_definition(module, &mut iter, ctx)?, Some(KW_FUNCTION) => self.visit_function_definition(module, &mut iter, ctx)?, Some(KW_PRIMITIVE) | Some(KW_COMPOSITE) => self.visit_component_definition(module, &mut iter, ctx)?, _ => return Err(ParseError::new_error_str_at_pos( &module.source, iter.last_valid_pos(), "unexpected symbol, expected a keyword marking the start of a definition" )), } } } fn visit_struct_definition( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result<(), ParseError> { consume_exact_ident(&module.source, iter, KW_STRUCT)?; let (ident_text, _) = consume_ident(&module.source, iter)?; // Retrieve preallocated DefinitionId let module_scope = SymbolScope::Module(module.root_id); let definition_id = ctx.symbols.get_symbol_by_name_defined_in_scope(module_scope, ident_text) .unwrap().variant.as_definition().definition_id; self.cur_definition = definition_id; // Parse struct definition consume_polymorphic_vars_spilled(&module.source, iter, ctx)?; let mut fields_section = self.struct_fields.start_section(); consume_comma_separated( TokenKind::OpenCurly, TokenKind::CloseCurly, &module.source, iter, ctx, |source, iter, ctx| { let poly_vars = ctx.heap[definition_id].poly_vars(); let start_pos = iter.last_valid_pos(); let parser_type = self.type_parser.consume_parser_type( iter, &ctx.heap, source, &ctx.symbols, poly_vars, definition_id, module_scope, false, None )?; let field = consume_ident_interned(source, iter, ctx)?; Ok(StructFieldDefinition{ span: InputSpan::from_positions(start_pos, field.span.end), field, parser_type }) }, &mut fields_section, "a struct field", "a list of struct fields", None )?; // Transfer to preallocated definition let struct_def = ctx.heap[definition_id].as_struct_mut(); struct_def.fields = fields_section.into_vec(); Ok(()) } fn visit_enum_definition( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result<(), ParseError> { consume_exact_ident(&module.source, iter, KW_ENUM)?; let (ident_text, _) = consume_ident(&module.source, iter)?; // Retrieve preallocated DefinitionId let module_scope = SymbolScope::Module(module.root_id); let definition_id = ctx.symbols.get_symbol_by_name_defined_in_scope(module_scope, ident_text) .unwrap().variant.as_definition().definition_id; self.cur_definition = definition_id; // Parse enum definition consume_polymorphic_vars_spilled(&module.source, iter, ctx)?; let mut enum_section = self.enum_variants.start_section(); consume_comma_separated( TokenKind::OpenCurly, TokenKind::CloseCurly, &module.source, iter, ctx, |source, iter, ctx| { let identifier = consume_ident_interned(source, iter, ctx)?; let value = if iter.next() == Some(TokenKind::Equal) { iter.consume(); let (variant_number, _) = consume_integer_literal(source, iter, &mut self.buffer)?; EnumVariantValue::Integer(variant_number as i64) // TODO: @int } else { EnumVariantValue::None }; Ok(EnumVariantDefinition{ identifier, value }) }, &mut enum_section, "an enum variant", "a list of enum variants", None )?; // Transfer to definition let enum_def = ctx.heap[definition_id].as_enum_mut(); enum_def.variants = enum_section.into_vec(); Ok(()) } fn visit_union_definition( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result<(), ParseError> { consume_exact_ident(&module.source, iter, KW_UNION)?; let (ident_text, _) = consume_ident(&module.source, iter)?; // Retrieve preallocated DefinitionId let module_scope = SymbolScope::Module(module.root_id); let definition_id = ctx.symbols.get_symbol_by_name_defined_in_scope(module_scope, ident_text) .unwrap().variant.as_definition().definition_id; self.cur_definition = definition_id; // Parse union definition consume_polymorphic_vars_spilled(&module.source, iter, ctx)?; let mut variants_section = self.union_variants.start_section(); consume_comma_separated( TokenKind::OpenCurly, TokenKind::CloseCurly, &module.source, iter, ctx, |source, iter, ctx| { let identifier = consume_ident_interned(source, iter, ctx)?; let mut close_pos = identifier.span.end; let mut types_section = self.parser_types.start_section(); let has_embedded = maybe_consume_comma_separated( TokenKind::OpenParen, TokenKind::CloseParen, source, iter, ctx, |source, iter, ctx| { let poly_vars = ctx.heap[definition_id].poly_vars(); self.type_parser.consume_parser_type( iter, &ctx.heap, source, &ctx.symbols, poly_vars, definition_id, module_scope, false, None ) }, &mut types_section, "an embedded type", Some(&mut close_pos) )?; let value = if has_embedded { types_section.into_vec() } else { types_section.forget(); Vec::new() }; Ok(UnionVariantDefinition{ span: InputSpan::from_positions(identifier.span.begin, close_pos), identifier, value }) }, &mut variants_section, "a union variant", "a list of union variants", None )?; // Transfer to AST let union_def = ctx.heap[definition_id].as_union_mut(); union_def.variants = variants_section.into_vec(); Ok(()) } fn visit_function_definition( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result<(), ParseError> { // Retrieve function name consume_exact_ident(&module.source, iter, KW_FUNCTION)?; let (ident_text, _) = consume_ident(&module.source, iter)?; // Retrieve preallocated DefinitionId let module_scope = SymbolScope::Module(module.root_id); let definition_id = ctx.symbols.get_symbol_by_name_defined_in_scope(module_scope, ident_text) .unwrap().variant.as_definition().definition_id; self.cur_definition = definition_id; consume_polymorphic_vars_spilled(&module.source, iter, ctx)?; // Parse function's argument list let mut parameter_section = self.variables.start_section(); consume_parameter_list( &mut self.type_parser, &module.source, iter, ctx, &mut parameter_section, module_scope, definition_id )?; let parameters = parameter_section.into_vec(); // Consume return types consume_token(&module.source, iter, TokenKind::ArrowRight)?; let poly_vars = ctx.heap[definition_id].poly_vars(); let parser_type = self.type_parser.consume_parser_type( iter, &ctx.heap, &module.source, &ctx.symbols, poly_vars, definition_id, module_scope, false, None )?; // Consume body let (body_id, source) = self.consume_procedure_body(module, iter, ctx, definition_id, ProcedureKind::Function)?; let scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::Definition(definition_id))); // Assign everything in the preallocated AST node let function = ctx.heap[definition_id].as_procedure_mut(); function.source = source; function.return_type = Some(parser_type); function.parameters = parameters; function.scope = scope_id; function.body = body_id; Ok(()) } fn visit_component_definition( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result<(), ParseError> { // Consume component variant and name let (_variant_text, _) = consume_any_ident(&module.source, iter)?; debug_assert!(_variant_text == KW_PRIMITIVE || _variant_text == KW_COMPOSITE); let (ident_text, _) = consume_ident(&module.source, iter)?; // Retrieve preallocated definition let module_scope = SymbolScope::Module(module.root_id); let definition_id = ctx.symbols.get_symbol_by_name_defined_in_scope(module_scope, ident_text) .unwrap().variant.as_definition().definition_id; self.cur_definition = definition_id; consume_polymorphic_vars_spilled(&module.source, iter, ctx)?; // Parse component's argument list let mut parameter_section = self.variables.start_section(); consume_parameter_list( &mut self.type_parser, &module.source, iter, ctx, &mut parameter_section, module_scope, definition_id )?; let parameters = parameter_section.into_vec(); // Consume body let procedure_kind = ctx.heap[definition_id].as_procedure().kind; let (body_id, source) = self.consume_procedure_body(module, iter, ctx, definition_id, procedure_kind)?; let scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::Definition(definition_id))); // Assign everything in the AST node let component = ctx.heap[definition_id].as_procedure_mut(); debug_assert!(component.return_type.is_none()); component.source = source; component.parameters = parameters; component.scope = scope_id; component.body = body_id; Ok(()) } /// Consumes a procedure's body: either a user-defined procedure, which we /// parse as normal, or a builtin function, where we'll make sure we expect /// the particular builtin. /// /// We expect that the procedure's name is already stored in the /// preallocated AST node. fn consume_procedure_body( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx, definition_id: DefinitionId, kind: ProcedureKind ) -> Result<(BlockStatementId, ProcedureSource), ParseError> { if iter.peek() == Some(TokenKind::Pragma) { let (pragma, pragma_start, pragma_end) = consume_pragma(&module.source, iter)?; if pragma != b"#builtin" { return Err(ParseError::new_error_str_at_span( &module.source, InputSpan::from_positions(pragma_start, pragma_end), "expected a '#builtin' pragma, or a function body" )); } // Retrieve module and procedure name assert!(module.name.is_some(), "compiler error: builtin procedure body in unnamed module"); let (_, module_name) = module.name.as_ref().unwrap(); let module_name = module_name.as_str(); let definition = ctx.heap[definition_id].as_procedure(); let procedure_name = definition.identifier.value.as_str(); let source = match (module_name, procedure_name) { ("std.global", "get") => ProcedureSource::FuncGet, ("std.global", "put") => ProcedureSource::FuncPut, ("std.global", "fires") => ProcedureSource::FuncFires, ("std.global", "create") => ProcedureSource::FuncCreate, ("std.global", "length") => ProcedureSource::FuncLength, ("std.global", "assert") => ProcedureSource::FuncAssert, ("std.global", "print") => ProcedureSource::FuncPrint, _ => panic!( "compiler error: unknown builtin procedure '{}' in module '{}'", procedure_name, module_name ), }; return Ok((BlockStatementId::new_invalid(), source)); } else { let body_id = self.consume_block_statement(module, iter, ctx)?; let source = match kind { ProcedureKind::Function => ProcedureSource::FuncUserDefined, ProcedureKind::Primitive | ProcedureKind::Composite => ProcedureSource::CompUserDefined, }; return Ok((body_id, source)) } } /// Consumes a statement and returns a boolean indicating whether it was a /// block or not. fn consume_statement(&mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx) -> Result { let next = iter.next().expect("consume_statement has a next token"); if next == TokenKind::OpenCurly { let id = self.consume_block_statement(module, iter, ctx)?; return Ok(id.upcast()); } else if next == TokenKind::Ident { let ident = peek_ident(&module.source, iter).unwrap(); if ident == KW_STMT_IF { // Consume if statement and place end-if statement directly // after it. let id = self.consume_if_statement(module, iter, ctx)?; return Ok(id.upcast()); } else if ident == KW_STMT_WHILE { let id = self.consume_while_statement(module, iter, ctx)?; return Ok(id.upcast()); } else if ident == KW_STMT_BREAK { let id = self.consume_break_statement(module, iter, ctx)?; return Ok(id.upcast()); } else if ident == KW_STMT_CONTINUE { let id = self.consume_continue_statement(module, iter, ctx)?; return Ok(id.upcast()); } else if ident == KW_STMT_SYNC { let id = self.consume_synchronous_statement(module, iter, ctx)?; return Ok(id.upcast()); } else if ident == KW_STMT_FORK { let id = self.consume_fork_statement(module, iter, ctx)?; let end_fork = ctx.heap.alloc_end_fork_statement(|this| EndForkStatement { this, start_fork: id, next: StatementId::new_invalid(), }); let fork_stmt = &mut ctx.heap[id]; fork_stmt.end_fork = end_fork; return Ok(id.upcast()); } else if ident == KW_STMT_SELECT { let id = self.consume_select_statement(module, iter, ctx)?; return Ok(id.upcast()); } else if ident == KW_STMT_RETURN { let id = self.consume_return_statement(module, iter, ctx)?; return Ok(id.upcast()); } else if ident == KW_STMT_GOTO { let id = self.consume_goto_statement(module, iter, ctx)?; return Ok(id.upcast()); } else if ident == KW_STMT_NEW { let id = self.consume_new_statement(module, iter, ctx)?; return Ok(id.upcast()); } else if ident == KW_STMT_CHANNEL { let id = self.consume_channel_statement(module, iter, ctx)?; return Ok(id.upcast().upcast()); } else if iter.peek() == Some(TokenKind::Colon) { let id = self.consume_labeled_statement(module, iter, ctx)?; return Ok(id.upcast()); } else { // Two fallback possibilities: the first one is a memory // declaration, the other one is to parse it as a normal // expression. This is a bit ugly. if let Some(memory_stmt_id) = self.maybe_consume_memory_statement_without_semicolon(module, iter, ctx)? { consume_token(&module.source, iter, TokenKind::SemiColon)?; return Ok(memory_stmt_id.upcast().upcast()); } else { let id = self.consume_expression_statement(module, iter, ctx)?; return Ok(id.upcast()); } } } else if next == TokenKind::OpenParen { // Same as above: memory statement or normal expression if let Some(memory_stmt_id) = self.maybe_consume_memory_statement_without_semicolon(module, iter, ctx)? { consume_token(&module.source, iter, TokenKind::SemiColon)?; return Ok(memory_stmt_id.upcast().upcast()); } else { let id = self.consume_expression_statement(module, iter, ctx)?; return Ok(id.upcast()); } } else { let id = self.consume_expression_statement(module, iter, ctx)?; return Ok(id.upcast()); } } fn consume_block_statement( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { let open_curly_span = consume_token(&module.source, iter, TokenKind::OpenCurly)?; let mut stmt_section = self.statements.start_section(); let mut next = iter.next(); while next != Some(TokenKind::CloseCurly) { if next.is_none() { return Err(ParseError::new_error_str_at_pos( &module.source, iter.last_valid_pos(), "expected a statement or '}'" )); } let stmt_id = self.consume_statement(module, iter, ctx)?; stmt_section.push(stmt_id); next = iter.next(); } let statements = stmt_section.into_vec(); let mut block_span = consume_token(&module.source, iter, TokenKind::CloseCurly)?; block_span.begin = open_curly_span.begin; let block_id = ctx.heap.alloc_block_statement(|this| BlockStatement{ this, span: block_span, statements, end_block: EndBlockStatementId::new_invalid(), scope: ScopeId::new_invalid(), next: StatementId::new_invalid(), }); let scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::Block(block_id))); let end_block_id = ctx.heap.alloc_end_block_statement(|this| EndBlockStatement{ this, start_block: block_id, next: StatementId::new_invalid() }); let block_stmt = &mut ctx.heap[block_id]; block_stmt.end_block = end_block_id; block_stmt.scope = scope_id; Ok(block_id) } fn consume_if_statement( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { let if_span = consume_exact_ident(&module.source, iter, KW_STMT_IF)?; consume_token(&module.source, iter, TokenKind::OpenParen)?; let test = self.consume_expression(module, iter, ctx)?; consume_token(&module.source, iter, TokenKind::CloseParen)?; // Consume bodies of if-statement let true_body = IfStatementCase{ body: self.consume_statement(module, iter, ctx)?, scope: ScopeId::new_invalid(), }; let false_body = if has_ident(&module.source, iter, KW_STMT_ELSE) { iter.consume(); let false_body = IfStatementCase{ body: self.consume_statement(module, iter, ctx)?, scope: ScopeId::new_invalid(), }; Some(false_body) } else { None }; // Construct AST elements let if_stmt_id = ctx.heap.alloc_if_statement(|this| IfStatement{ this, span: if_span, test, true_case: true_body, false_case: false_body, end_if: EndIfStatementId::new_invalid(), }); let end_if_stmt_id = ctx.heap.alloc_end_if_statement(|this| EndIfStatement{ this, start_if: if_stmt_id, next: StatementId::new_invalid(), }); let true_scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::If(if_stmt_id, true))); let false_scope_id = if false_body.is_some() { Some(ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::If(if_stmt_id, false)))) } else { None }; let if_stmt = &mut ctx.heap[if_stmt_id]; if_stmt.end_if = end_if_stmt_id; if_stmt.true_case.scope = true_scope_id; if let Some(false_case) = &mut if_stmt.false_case { false_case.scope = false_scope_id.unwrap(); } return Ok(if_stmt_id); } fn consume_while_statement( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { let while_span = consume_exact_ident(&module.source, iter, KW_STMT_WHILE)?; consume_token(&module.source, iter, TokenKind::OpenParen)?; let test = self.consume_expression(module, iter, ctx)?; consume_token(&module.source, iter, TokenKind::CloseParen)?; let body = self.consume_statement(module, iter, ctx)?; let while_stmt_id = ctx.heap.alloc_while_statement(|this| WhileStatement{ this, span: while_span, test, scope: ScopeId::new_invalid(), body, end_while: EndWhileStatementId::new_invalid(), in_sync: SynchronousStatementId::new_invalid(), }); let end_while_stmt_id = ctx.heap.alloc_end_while_statement(|this| EndWhileStatement{ this, start_while: while_stmt_id, next: StatementId::new_invalid(), }); let scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::While(while_stmt_id))); let while_stmt = &mut ctx.heap[while_stmt_id]; while_stmt.scope = scope_id; while_stmt.end_while = end_while_stmt_id; Ok(while_stmt_id) } fn consume_break_statement( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { let break_span = consume_exact_ident(&module.source, iter, KW_STMT_BREAK)?; let label = if Some(TokenKind::Ident) == iter.next() { let label = consume_ident_interned(&module.source, iter, ctx)?; Some(label) } else { None }; consume_token(&module.source, iter, TokenKind::SemiColon)?; Ok(ctx.heap.alloc_break_statement(|this| BreakStatement{ this, span: break_span, label, target: EndWhileStatementId::new_invalid(), })) } fn consume_continue_statement( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { let continue_span = consume_exact_ident(&module.source, iter, KW_STMT_CONTINUE)?; let label= if Some(TokenKind::Ident) == iter.next() { let label = consume_ident_interned(&module.source, iter, ctx)?; Some(label) } else { None }; consume_token(&module.source, iter, TokenKind::SemiColon)?; Ok(ctx.heap.alloc_continue_statement(|this| ContinueStatement{ this, span: continue_span, label, target: WhileStatementId::new_invalid(), })) } fn consume_synchronous_statement( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { let synchronous_span = consume_exact_ident(&module.source, iter, KW_STMT_SYNC)?; let body = self.consume_statement(module, iter, ctx)?; let sync_stmt_id = ctx.heap.alloc_synchronous_statement(|this| SynchronousStatement{ this, span: synchronous_span, scope: ScopeId::new_invalid(), body, end_sync: EndSynchronousStatementId::new_invalid(), }); let end_sync_stmt_id = ctx.heap.alloc_end_synchronous_statement(|this| EndSynchronousStatement{ this, start_sync: sync_stmt_id, next: StatementId::new_invalid(), }); let scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::Synchronous(sync_stmt_id))); let sync_stmt = &mut ctx.heap[sync_stmt_id]; sync_stmt.scope = scope_id; sync_stmt.end_sync = end_sync_stmt_id; return Ok(sync_stmt_id); } fn consume_fork_statement( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { let fork_span = consume_exact_ident(&module.source, iter, KW_STMT_FORK)?; let left_body = self.consume_statement(module, iter, ctx)?; let right_body = if has_ident(&module.source, iter, KW_STMT_OR) { iter.consume(); let right_body = self.consume_statement(module, iter, ctx)?; Some(right_body) } else { None }; Ok(ctx.heap.alloc_fork_statement(|this| ForkStatement{ this, span: fork_span, left_body, right_body, end_fork: EndForkStatementId::new_invalid(), })) } fn consume_select_statement( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { let select_span = consume_exact_ident(&module.source, iter, KW_STMT_SELECT)?; consume_token(&module.source, iter, TokenKind::OpenCurly)?; let mut cases = Vec::new(); let mut next = iter.next(); while Some(TokenKind::CloseCurly) != next { let guard = match self.maybe_consume_memory_statement_without_semicolon(module, iter, ctx)? { Some(guard_mem_stmt) => guard_mem_stmt.upcast().upcast(), None => { let start_pos = iter.last_valid_pos(); let expr = self.consume_expression(module, iter, ctx)?; let end_pos = iter.last_valid_pos(); let guard_expr_stmt = ctx.heap.alloc_expression_statement(|this| ExpressionStatement{ this, span: InputSpan::from_positions(start_pos, end_pos), expression: expr, next: StatementId::new_invalid(), }); guard_expr_stmt.upcast() }, }; consume_token(&module.source, iter, TokenKind::ArrowRight)?; let block = self.consume_statement(module, iter, ctx)?; cases.push(SelectCase{ guard, body: block, scope: ScopeId::new_invalid(), involved_ports: Vec::with_capacity(1) }); next = iter.next(); } consume_token(&module.source, iter, TokenKind::CloseCurly)?; let num_cases = cases.len(); let select_stmt_id = ctx.heap.alloc_select_statement(|this| SelectStatement{ this, span: select_span, cases, end_select: EndSelectStatementId::new_invalid(), relative_pos_in_parent: -1, next: StatementId::new_invalid(), }); let end_select_stmt_id = ctx.heap.alloc_end_select_statement(|this| EndSelectStatement{ this, start_select: select_stmt_id, next: StatementId::new_invalid(), }); let select_stmt = &mut ctx.heap[select_stmt_id]; select_stmt.end_select = end_select_stmt_id; for case_index in 0..num_cases { let scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::SelectCase(select_stmt_id, case_index as u32))); let select_stmt = &mut ctx.heap[select_stmt_id]; let select_case = &mut select_stmt.cases[case_index]; select_case.scope = scope_id; } return Ok(select_stmt_id) } fn consume_return_statement( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { let return_span = consume_exact_ident(&module.source, iter, KW_STMT_RETURN)?; let mut scoped_section = self.expressions.start_section(); consume_comma_separated_until( TokenKind::SemiColon, &module.source, iter, ctx, |_source, iter, ctx| self.consume_expression(module, iter, ctx), &mut scoped_section, "an expression", None )?; let expressions = scoped_section.into_vec(); if expressions.is_empty() { return Err(ParseError::new_error_str_at_span(&module.source, return_span, "expected at least one return value")); } else if expressions.len() > 1 { return Err(ParseError::new_error_str_at_span(&module.source, return_span, "multiple return values are not (yet) supported")) } Ok(ctx.heap.alloc_return_statement(|this| ReturnStatement{ this, span: return_span, expressions })) } fn consume_goto_statement( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { let goto_span = consume_exact_ident(&module.source, iter, KW_STMT_GOTO)?; let label = consume_ident_interned(&module.source, iter, ctx)?; consume_token(&module.source, iter, TokenKind::SemiColon)?; Ok(ctx.heap.alloc_goto_statement(|this| GotoStatement{ this, span: goto_span, label, target: LabeledStatementId::new_invalid(), })) } fn consume_new_statement( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { let new_span = consume_exact_ident(&module.source, iter, KW_STMT_NEW)?; let start_pos = iter.last_valid_pos(); let expression_id = self.consume_primary_expression(module, iter, ctx)?; let expression = &ctx.heap[expression_id]; let mut valid = false; let mut call_id = CallExpressionId::new_invalid(); if let Expression::Call(expression) = expression { // Allow both components and functions, as it makes more sense to // check their correct use in the validation and linking pass if expression.method == Method::UserComponent || expression.method == Method::UserFunction { call_id = expression.this; valid = true; } } if !valid { return Err(ParseError::new_error_str_at_span( &module.source, InputSpan::from_positions(start_pos, iter.last_valid_pos()), "expected a call expression" )); } consume_token(&module.source, iter, TokenKind::SemiColon)?; debug_assert!(!call_id.is_invalid()); Ok(ctx.heap.alloc_new_statement(|this| NewStatement{ this, span: new_span, expression: call_id, next: StatementId::new_invalid(), })) } fn consume_channel_statement( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { // Consume channel specification let channel_span = consume_exact_ident(&module.source, iter, KW_STMT_CHANNEL)?; let (inner_port_type, end_pos) = if Some(TokenKind::OpenAngle) == iter.next() { // Retrieve the type of the channel, we're cheating a bit here by // consuming the first '<' and setting the initial angle depth to 1 // such that our final '>' will be consumed as well. let angle_start_pos = iter.next_start_position(); iter.consume(); let definition_id = self.cur_definition; let poly_vars = ctx.heap[definition_id].poly_vars(); let parser_type = self.type_parser.consume_parser_type( iter, &ctx.heap, &module.source, &ctx.symbols, poly_vars, definition_id, SymbolScope::Module(module.root_id), true, Some(angle_start_pos) )?; (parser_type.elements, parser_type.full_span.end) } else { // Assume inferred ( vec![ParserTypeElement{ element_span: channel_span, variant: ParserTypeVariant::Inferred }], channel_span.end ) }; let from_identifier = consume_ident_interned(&module.source, iter, ctx)?; consume_token(&module.source, iter, TokenKind::ArrowRight)?; let to_identifier = consume_ident_interned(&module.source, iter, ctx)?; consume_token(&module.source, iter, TokenKind::SemiColon)?; // Construct ports let port_type_span = InputSpan::from_positions(channel_span.begin, end_pos); let port_type_len = inner_port_type.len() + 1; let mut from_port_type = ParserType{ elements: Vec::with_capacity(port_type_len), full_span: port_type_span }; from_port_type.elements.push(ParserTypeElement{ element_span: channel_span, variant: ParserTypeVariant::Output, }); from_port_type.elements.extend_from_slice(&inner_port_type); let from = ctx.heap.alloc_variable(|this| Variable{ this, kind: VariableKind::Local, identifier: from_identifier, parser_type: from_port_type, relative_pos_in_parent: 0, unique_id_in_scope: -1, }); let mut to_port_type = ParserType{ elements: Vec::with_capacity(port_type_len), full_span: port_type_span }; to_port_type.elements.push(ParserTypeElement{ element_span: channel_span, variant: ParserTypeVariant::Input }); to_port_type.elements.extend_from_slice(&inner_port_type); let to = ctx.heap.alloc_variable(|this|Variable{ this, kind: VariableKind::Local, identifier: to_identifier, parser_type: to_port_type, relative_pos_in_parent: 0, unique_id_in_scope: -1, }); // Construct the channel Ok(ctx.heap.alloc_channel_statement(|this| ChannelStatement{ this, span: channel_span, from, to, relative_pos_in_parent: 0, next: StatementId::new_invalid(), })) } fn consume_labeled_statement(&mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx) -> Result { let label = consume_ident_interned(&module.source, iter, ctx)?; consume_token(&module.source, iter, TokenKind::Colon)?; let inner_stmt_id = self.consume_statement(module, iter, ctx)?; let stmt_id = ctx.heap.alloc_labeled_statement(|this| LabeledStatement { this, label, body: inner_stmt_id, relative_pos_in_parent: 0, in_sync: SynchronousStatementId::new_invalid(), }); return Ok(stmt_id); } /// Attempts to consume a memory statement (a statement along the lines of /// `type var_name = initial_expr`). Will return `Ok(None)` if it didn't /// seem like there was a memory statement, `Ok(Some(...))` if there was /// one, and `Err(...)` if its reasonable to assume that there was a memory /// statement, but we failed to parse it. fn maybe_consume_memory_statement_without_semicolon( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result, ParseError> { // This is a bit ugly. It would be nicer if we could somehow // consume the expression with a type hint if we do get a valid // type, but we don't get an identifier following it let iter_state = iter.save(); let definition_id = self.cur_definition; let poly_vars = ctx.heap[definition_id].poly_vars(); let parser_type = self.type_parser.consume_parser_type( iter, &ctx.heap, &module.source, &ctx.symbols, poly_vars, definition_id, SymbolScope::Definition(definition_id), true, None ); if let Ok(parser_type) = parser_type { if Some(TokenKind::Ident) == iter.next() { // Assume this is a proper memory statement let identifier = consume_ident_interned(&module.source, iter, ctx)?; let memory_span = InputSpan::from_positions(parser_type.full_span.begin, identifier.span.end); let assign_span = consume_token(&module.source, iter, TokenKind::Equal)?; let initial_expr_id = self.consume_expression(module, iter, ctx)?; let initial_expr_end_pos = iter.last_valid_pos(); // Create the AST variable let local_id = ctx.heap.alloc_variable(|this| Variable{ this, kind: VariableKind::Local, identifier: identifier.clone(), parser_type, relative_pos_in_parent: 0, unique_id_in_scope: -1, }); // Create the initial assignment expression // Note: we set the initial variable declaration here let variable_expr_id = ctx.heap.alloc_variable_expression(|this| VariableExpression{ this, identifier, declaration: Some(local_id), used_as_binding_target: false, parent: ExpressionParent::None, type_index: -1, }); let assignment_expr_id = ctx.heap.alloc_assignment_expression(|this| AssignmentExpression{ this, operator_span: assign_span, full_span: InputSpan::from_positions(memory_span.begin, initial_expr_end_pos), left: variable_expr_id.upcast(), operation: AssignmentOperator::Set, right: initial_expr_id, parent: ExpressionParent::None, type_index: -1, }); // Put both together in the memory statement let memory_stmt_id = ctx.heap.alloc_memory_statement(|this| MemoryStatement{ this, span: memory_span, variable: local_id, initial_expr: assignment_expr_id, next: StatementId::new_invalid() }); return Ok(Some(memory_stmt_id)); } } // If here then one of the preconditions for a memory statement was not // met. So recover the iterator and return iter.load(iter_state); Ok(None) } fn consume_expression_statement( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { let start_pos = iter.last_valid_pos(); let expression = self.consume_expression(module, iter, ctx)?; let end_pos = iter.last_valid_pos(); consume_token(&module.source, iter, TokenKind::SemiColon)?; Ok(ctx.heap.alloc_expression_statement(|this| ExpressionStatement{ this, span: InputSpan::from_positions(start_pos, end_pos), expression, next: StatementId::new_invalid(), })) } //-------------------------------------------------------------------------- // Expression Parsing //-------------------------------------------------------------------------- // TODO: @Cleanup This is fine for now. But I prefer my stacktraces not to // look like enterprise Java code... fn consume_expression( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { self.consume_assignment_expression(module, iter, ctx) } fn consume_assignment_expression( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { // Utility to convert token into assignment operator fn parse_assignment_operator(token: Option) -> Option { use TokenKind as TK; use AssignmentOperator as AO; if token.is_none() { return None } match token.unwrap() { TK::Equal => Some(AO::Set), TK::AtEquals => Some(AO::Concatenated), TK::StarEquals => Some(AO::Multiplied), TK::SlashEquals => Some(AO::Divided), TK::PercentEquals => Some(AO::Remained), TK::PlusEquals => Some(AO::Added), TK::MinusEquals => Some(AO::Subtracted), TK::ShiftLeftEquals => Some(AO::ShiftedLeft), TK::ShiftRightEquals => Some(AO::ShiftedRight), TK::AndEquals => Some(AO::BitwiseAnded), TK::CaretEquals => Some(AO::BitwiseXored), TK::OrEquals => Some(AO::BitwiseOred), _ => None } } let expr = self.consume_conditional_expression(module, iter, ctx)?; if let Some(operation) = parse_assignment_operator(iter.next()) { let operator_span = iter.next_span(); iter.consume(); let left = expr; let right = self.consume_expression(module, iter, ctx)?; let full_span = InputSpan::from_positions( ctx.heap[left].full_span().begin, ctx.heap[right].full_span().end, ); Ok(ctx.heap.alloc_assignment_expression(|this| AssignmentExpression{ this, operator_span, full_span, left, operation, right, parent: ExpressionParent::None, type_index: -1, }).upcast()) } else { Ok(expr) } } fn consume_conditional_expression( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { let result = self.consume_concat_expression(module, iter, ctx)?; if let Some(TokenKind::Question) = iter.next() { let operator_span = iter.next_span(); iter.consume(); let test = result; let true_expression = self.consume_expression(module, iter, ctx)?; consume_token(&module.source, iter, TokenKind::Colon)?; let false_expression = self.consume_expression(module, iter, ctx)?; let full_span = InputSpan::from_positions( ctx.heap[test].full_span().begin, ctx.heap[false_expression].full_span().end, ); Ok(ctx.heap.alloc_conditional_expression(|this| ConditionalExpression{ this, operator_span, full_span, test, true_expression, false_expression, parent: ExpressionParent::None, type_index: -1, }).upcast()) } else { Ok(result) } } fn consume_concat_expression( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { self.consume_generic_binary_expression( module, iter, ctx, |token| match token { Some(TokenKind::At) => Some(BinaryOperator::Concatenate), _ => None }, Self::consume_logical_or_expression ) } fn consume_logical_or_expression( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { self.consume_generic_binary_expression( module, iter, ctx, |token| match token { Some(TokenKind::OrOr) => Some(BinaryOperator::LogicalOr), _ => None }, Self::consume_logical_and_expression ) } fn consume_logical_and_expression( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { self.consume_generic_binary_expression( module, iter, ctx, |token| match token { Some(TokenKind::AndAnd) => Some(BinaryOperator::LogicalAnd), _ => None }, Self::consume_bitwise_or_expression ) } fn consume_bitwise_or_expression( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { self.consume_generic_binary_expression( module, iter, ctx, |token| match token { Some(TokenKind::Or) => Some(BinaryOperator::BitwiseOr), _ => None }, Self::consume_bitwise_xor_expression ) } fn consume_bitwise_xor_expression( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { self.consume_generic_binary_expression( module, iter, ctx, |token| match token { Some(TokenKind::Caret) => Some(BinaryOperator::BitwiseXor), _ => None }, Self::consume_bitwise_and_expression ) } fn consume_bitwise_and_expression( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { self.consume_generic_binary_expression( module, iter, ctx, |token| match token { Some(TokenKind::And) => Some(BinaryOperator::BitwiseAnd), _ => None }, Self::consume_equality_expression ) } fn consume_equality_expression( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { self.consume_generic_binary_expression( module, iter, ctx, |token| match token { Some(TokenKind::EqualEqual) => Some(BinaryOperator::Equality), Some(TokenKind::NotEqual) => Some(BinaryOperator::Inequality), _ => None }, Self::consume_relational_expression ) } fn consume_relational_expression( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { self.consume_generic_binary_expression( module, iter, ctx, |token| match token { Some(TokenKind::OpenAngle) => Some(BinaryOperator::LessThan), Some(TokenKind::CloseAngle) => Some(BinaryOperator::GreaterThan), Some(TokenKind::LessEquals) => Some(BinaryOperator::LessThanEqual), Some(TokenKind::GreaterEquals) => Some(BinaryOperator::GreaterThanEqual), _ => None }, Self::consume_shift_expression ) } fn consume_shift_expression( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { self.consume_generic_binary_expression( module, iter, ctx, |token| match token { Some(TokenKind::ShiftLeft) => Some(BinaryOperator::ShiftLeft), Some(TokenKind::ShiftRight) => Some(BinaryOperator::ShiftRight), _ => None }, Self::consume_add_or_subtract_expression ) } fn consume_add_or_subtract_expression( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { self.consume_generic_binary_expression( module, iter, ctx, |token| match token { Some(TokenKind::Plus) => Some(BinaryOperator::Add), Some(TokenKind::Minus) => Some(BinaryOperator::Subtract), _ => None, }, Self::consume_multiply_divide_or_modulus_expression ) } fn consume_multiply_divide_or_modulus_expression( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { self.consume_generic_binary_expression( module, iter, ctx, |token| match token { Some(TokenKind::Star) => Some(BinaryOperator::Multiply), Some(TokenKind::Slash) => Some(BinaryOperator::Divide), Some(TokenKind::Percent) => Some(BinaryOperator::Remainder), _ => None }, Self::consume_prefix_expression ) } fn consume_prefix_expression( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { fn parse_prefix_token(token: Option) -> Option { use TokenKind as TK; use UnaryOperator as UO; match token { Some(TK::Plus) => Some(UO::Positive), Some(TK::Minus) => Some(UO::Negative), Some(TK::Tilde) => Some(UO::BitwiseNot), Some(TK::Exclamation) => Some(UO::LogicalNot), _ => None } } let next = iter.next(); if let Some(operation) = parse_prefix_token(next) { let operator_span = iter.next_span(); iter.consume(); let expression = self.consume_prefix_expression(module, iter, ctx)?; let full_span = InputSpan::from_positions( operator_span.begin, ctx.heap[expression].full_span().end, ); Ok(ctx.heap.alloc_unary_expression(|this| UnaryExpression { this, operator_span, full_span, operation, expression, parent: ExpressionParent::None, type_index: -1, }).upcast()) } else if next == Some(TokenKind::PlusPlus) { return Err(ParseError::new_error_str_at_span( &module.source, iter.next_span(), "prefix increment is not supported in the language" )); } else if next == Some(TokenKind::MinusMinus) { return Err(ParseError::new_error_str_at_span( &module.source, iter.next_span(), "prefix decrement is not supported in this language" )); } else { self.consume_postfix_expression(module, iter, ctx) } } fn consume_postfix_expression( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { fn has_matching_postfix_token(token: Option) -> bool { use TokenKind as TK; if token.is_none() { return false; } match token.unwrap() { TK::PlusPlus | TK::MinusMinus | TK::OpenSquare | TK::Dot => true, _ => false } } let mut result = self.consume_primary_expression(module, iter, ctx)?; let mut next = iter.next(); while has_matching_postfix_token(next) { let token = next.unwrap(); let mut operator_span = iter.next_span(); iter.consume(); if token == TokenKind::PlusPlus { return Err(ParseError::new_error_str_at_span( &module.source, operator_span, "postfix increment is not supported in this language" )); } else if token == TokenKind::MinusMinus { return Err(ParseError::new_error_str_at_span( &module.source, operator_span, "prefix increment is not supported in this language" )); } else if token == TokenKind::OpenSquare { let subject = result; let from_index = self.consume_expression(module, iter, ctx)?; // Check if we have an indexing or slicing operation next = iter.next(); if Some(TokenKind::DotDot) == next { iter.consume(); let to_index = self.consume_expression(module, iter, ctx)?; let end_span = consume_token(&module.source, iter, TokenKind::CloseSquare)?; operator_span.end = end_span.end; let full_span = InputSpan::from_positions( ctx.heap[subject].full_span().begin, operator_span.end ); result = ctx.heap.alloc_slicing_expression(|this| SlicingExpression{ this, slicing_span: operator_span, full_span, subject, from_index, to_index, parent: ExpressionParent::None, type_index: -1, }).upcast(); } else if Some(TokenKind::CloseSquare) == next { let end_span = consume_token(&module.source, iter, TokenKind::CloseSquare)?; operator_span.end = end_span.end; let full_span = InputSpan::from_positions( ctx.heap[subject].full_span().begin, operator_span.end ); result = ctx.heap.alloc_indexing_expression(|this| IndexingExpression{ this, operator_span, full_span, subject, index: from_index, parent: ExpressionParent::None, type_index: -1, }).upcast(); } else { return Err(ParseError::new_error_str_at_pos( &module.source, iter.last_valid_pos(), "unexpected token: expected ']' or '..'" )); } } else { // Can be a select expression for struct fields, or a select // for a tuple element. debug_assert_eq!(token, TokenKind::Dot); let subject = result; let next = iter.next(); let (select_kind, full_span) = if Some(TokenKind::Integer) == next { // Tuple member let (index, index_span) = consume_integer_literal(&module.source, iter, &mut self.buffer)?; let full_span = InputSpan::from_positions( ctx.heap[subject].full_span().begin, index_span.end ); (SelectKind::TupleMember(index), full_span) } else if Some(TokenKind::Ident) == next { // Struct field let field_name = consume_ident_interned(&module.source, iter, ctx)?; let full_span = InputSpan::from_positions( ctx.heap[subject].full_span().begin, field_name.span.end ); (SelectKind::StructField(field_name), full_span) } else { return Err(ParseError::new_error_str_at_pos( &module.source, iter.last_valid_pos(), "unexpected token: expected integer or identifier" )); }; result = ctx.heap.alloc_select_expression(|this| SelectExpression{ this, operator_span, full_span, subject, kind: select_kind, parent: ExpressionParent::None, type_index: -1, }).upcast(); } next = iter.next(); } Ok(result) } fn consume_primary_expression( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { let next = iter.next(); let result = if next == Some(TokenKind::OpenParen) { // Something parenthesized. This can mean several things: we have // a parenthesized expression or we have a tuple literal. They are // ambiguous when the tuple has one member. But like the tuple type // parsing we interpret all one-tuples as parenthesized expressions. // // Practically (to prevent unnecessary `consume_expression` calls) // we distinguish the zero-tuple, the parenthesized expression, and // the N-tuple (for N > 1). let open_paren_pos = iter.next_start_position(); iter.consume(); let result = if Some(TokenKind::CloseParen) == iter.next() { // Zero-tuple let (_, close_paren_pos) = iter.next_positions(); iter.consume(); let literal_id = ctx.heap.alloc_literal_expression(|this| LiteralExpression{ this, span: InputSpan::from_positions(open_paren_pos, close_paren_pos), value: Literal::Tuple(Vec::new()), parent: ExpressionParent::None, type_index: -1, }); literal_id.upcast() } else { // Start by consuming one expression, then check for a comma let expr_id = self.consume_expression(module, iter, ctx)?; if Some(TokenKind::Comma) == iter.next() && Some(TokenKind::CloseParen) != iter.peek() { // Must be an N-tuple iter.consume(); // the comma let mut scoped_section = self.expressions.start_section(); scoped_section.push(expr_id); let mut close_paren_pos = open_paren_pos; consume_comma_separated_until( TokenKind::CloseParen, &module.source, iter, ctx, |_source, iter, ctx| self.consume_expression(module, iter, ctx), &mut scoped_section, "an expression", Some(&mut close_paren_pos) )?; debug_assert!(scoped_section.len() > 1); // peeked token wasn't CloseParen, must be expression let literal_id = ctx.heap.alloc_literal_expression(|this| LiteralExpression{ this, span: InputSpan::from_positions(open_paren_pos, close_paren_pos), value: Literal::Tuple(scoped_section.into_vec()), parent: ExpressionParent::None, type_index: -1, }); literal_id.upcast() } else { // Assume we're dealing with a normal expression consume_token(&module.source, iter, TokenKind::CloseParen)?; expr_id } }; result } else if next == Some(TokenKind::OpenCurly) { // Array literal let (start_pos, mut end_pos) = iter.next_positions(); let mut scoped_section = self.expressions.start_section(); consume_comma_separated( TokenKind::OpenCurly, TokenKind::CloseCurly, &module.source, iter, ctx, |_source, iter, ctx| self.consume_expression(module, iter, ctx), &mut scoped_section, "an expression", "a list of expressions", Some(&mut end_pos) )?; ctx.heap.alloc_literal_expression(|this| LiteralExpression{ this, span: InputSpan::from_positions(start_pos, end_pos), value: Literal::Array(scoped_section.into_vec()), parent: ExpressionParent::None, type_index: -1, }).upcast() } else if next == Some(TokenKind::Integer) { let (literal, span) = consume_integer_literal(&module.source, iter, &mut self.buffer)?; ctx.heap.alloc_literal_expression(|this| LiteralExpression{ this, span, value: Literal::Integer(LiteralInteger{ unsigned_value: literal, negated: false }), parent: ExpressionParent::None, type_index: -1, }).upcast() } else if next == Some(TokenKind::String) { let span = consume_string_literal(&module.source, iter, &mut self.buffer)?; let interned = ctx.pool.intern(self.buffer.as_bytes()); ctx.heap.alloc_literal_expression(|this| LiteralExpression{ this, span, value: Literal::String(interned), parent: ExpressionParent::None, type_index: -1, }).upcast() } else if next == Some(TokenKind::Character) { let (character, span) = consume_character_literal(&module.source, iter)?; ctx.heap.alloc_literal_expression(|this| LiteralExpression{ this, span, value: Literal::Character(character), parent: ExpressionParent::None, type_index: -1, }).upcast() } else if next == Some(TokenKind::Ident) { // May be a variable, a type instantiation or a function call. If we // have a single identifier that we cannot find in the type table // then we're going to assume that we're dealing with a variable. let ident_span = iter.next_span(); let ident_text = module.source.section_at_span(ident_span); let symbol = ctx.symbols.get_symbol_by_name(SymbolScope::Module(module.root_id), ident_text); if symbol.is_some() { // The first bit looked like a symbol, so we're going to follow // that all the way through, assume we arrive at some kind of // function call or type instantiation use ParserTypeVariant as PTV; let symbol_scope = SymbolScope::Definition(self.cur_definition); let poly_vars = ctx.heap[self.cur_definition].poly_vars(); let parser_type = self.type_parser.consume_parser_type( iter, &ctx.heap, &module.source, &ctx.symbols, poly_vars, self.cur_definition, symbol_scope, true, None )?; debug_assert!(!parser_type.elements.is_empty()); match parser_type.elements[0].variant { PTV::Definition(target_definition_id, _) => { let definition = &ctx.heap[target_definition_id]; match definition { Definition::Struct(_) => { // Struct literal let mut last_token = iter.last_valid_pos(); let mut struct_fields = Vec::new(); consume_comma_separated( TokenKind::OpenCurly, TokenKind::CloseCurly, &module.source, iter, ctx, |source, iter, ctx| { let identifier = consume_ident_interned(source, iter, ctx)?; consume_token(source, iter, TokenKind::Colon)?; let value = self.consume_expression(module, iter, ctx)?; Ok(LiteralStructField{ identifier, value, field_idx: 0 }) }, &mut struct_fields, "a struct field", "a list of struct fields", Some(&mut last_token) )?; ctx.heap.alloc_literal_expression(|this| LiteralExpression{ this, span: InputSpan::from_positions(ident_span.begin, last_token), value: Literal::Struct(LiteralStruct{ parser_type, fields: struct_fields, definition: target_definition_id, }), parent: ExpressionParent::None, type_index: -1, }).upcast() }, Definition::Enum(_) => { // Enum literal: consume the variant consume_token(&module.source, iter, TokenKind::ColonColon)?; let variant = consume_ident_interned(&module.source, iter, ctx)?; ctx.heap.alloc_literal_expression(|this| LiteralExpression{ this, span: InputSpan::from_positions(ident_span.begin, variant.span.end), value: Literal::Enum(LiteralEnum{ parser_type, variant, definition: target_definition_id, variant_idx: 0 }), parent: ExpressionParent::None, type_index: -1, }).upcast() }, Definition::Union(_) => { // Union literal: consume the variant consume_token(&module.source, iter, TokenKind::ColonColon)?; let variant = consume_ident_interned(&module.source, iter, ctx)?; // Consume any possible embedded values let mut end_pos = variant.span.end; let values = if Some(TokenKind::OpenParen) == iter.next() { self.consume_expression_list(module, iter, ctx, Some(&mut end_pos))? } else { Vec::new() }; ctx.heap.alloc_literal_expression(|this| LiteralExpression{ this, span: InputSpan::from_positions(ident_span.begin, end_pos), value: Literal::Union(LiteralUnion{ parser_type, variant, values, definition: target_definition_id, variant_idx: 0, }), parent: ExpressionParent::None, type_index: -1, }).upcast() }, Definition::Procedure(proc_def) => { // Check whether it is a builtin function // TODO: Once we start generating bytecode this is unnecessary let procedure_id = proc_def.this; let method = match proc_def.source { ProcedureSource::FuncUserDefined => Method::UserFunction, ProcedureSource::CompUserDefined => Method::UserComponent, ProcedureSource::FuncGet => Method::Get, ProcedureSource::FuncPut => Method::Put, ProcedureSource::FuncFires => Method::Fires, ProcedureSource::FuncCreate => Method::Create, ProcedureSource::FuncLength => Method::Length, ProcedureSource::FuncAssert => Method::Assert, ProcedureSource::FuncPrint => Method::Print, _ => todo!("other proc sources") }; // Function call: consume the arguments let func_span = parser_type.full_span; let mut full_span = func_span; let arguments = self.consume_expression_list( module, iter, ctx, Some(&mut full_span.end) )?; ctx.heap.alloc_call_expression(|this| CallExpression{ this, func_span, full_span, parser_type, method, arguments, procedure: procedure_id, parent: ExpressionParent::None, type_index: -1, }).upcast() } } }, _ => { return Err(ParseError::new_error_str_at_span( &module.source, parser_type.full_span, "unexpected type in expression" )) } } } else { // Check for builtin keywords or builtin functions if ident_text == KW_LIT_NULL || ident_text == KW_LIT_TRUE || ident_text == KW_LIT_FALSE { iter.consume(); // Parse builtin literal let value = match ident_text { KW_LIT_NULL => Literal::Null, KW_LIT_TRUE => Literal::True, KW_LIT_FALSE => Literal::False, _ => unreachable!(), }; ctx.heap.alloc_literal_expression(|this| LiteralExpression { this, span: ident_span, value, parent: ExpressionParent::None, type_index: -1, }).upcast() } else if ident_text == KW_LET { // Binding expression let operator_span = iter.next_span(); iter.consume(); let bound_to = self.consume_prefix_expression(module, iter, ctx)?; consume_token(&module.source, iter, TokenKind::Equal)?; let bound_from = self.consume_prefix_expression(module, iter, ctx)?; let full_span = InputSpan::from_positions( operator_span.begin, ctx.heap[bound_from].full_span().end, ); ctx.heap.alloc_binding_expression(|this| BindingExpression{ this, operator_span, full_span, bound_to, bound_from, parent: ExpressionParent::None, type_index: -1, }).upcast() } else if ident_text == KW_CAST { // Casting expression iter.consume(); let to_type = if Some(TokenKind::OpenAngle) == iter.next() { let angle_start_pos = iter.next_start_position(); iter.consume(); let definition_id = self.cur_definition; let poly_vars = ctx.heap[definition_id].poly_vars(); self.type_parser.consume_parser_type( iter, &ctx.heap, &module.source, &ctx.symbols, poly_vars, definition_id, SymbolScope::Module(module.root_id), true, Some(angle_start_pos) )? } else { // Automatic casting with inferred target type ParserType{ elements: vec![ParserTypeElement{ element_span: ident_span, variant: ParserTypeVariant::Inferred, }], full_span: ident_span } }; consume_token(&module.source, iter, TokenKind::OpenParen)?; let subject = self.consume_expression(module, iter, ctx)?; let mut full_span = iter.next_span(); full_span.begin = to_type.full_span.begin; consume_token(&module.source, iter, TokenKind::CloseParen)?; ctx.heap.alloc_cast_expression(|this| CastExpression{ this, cast_span: to_type.full_span, full_span, to_type, subject, parent: ExpressionParent::None, type_index: -1, }).upcast() } else { // Not a builtin literal, but also not a known type. So we // assume it is a variable expression. Although if we do, // then if a programmer mistyped a struct/function name the // error messages will be rather cryptic. For polymorphic // arguments we can't really do anything at all (because it // uses the '<' token). In the other cases we try to provide // a better error message. iter.consume(); let next = iter.next(); if Some(TokenKind::ColonColon) == next { return Err(ParseError::new_error_str_at_span(&module.source, ident_span, "unknown identifier")); } else if Some(TokenKind::OpenParen) == next { return Err(ParseError::new_error_str_at_span( &module.source, ident_span, "unknown identifier, did you mistype a union variant's, component's, or function's name?" )); } else if Some(TokenKind::OpenCurly) == next { return Err(ParseError::new_error_str_at_span( &module.source, ident_span, "unknown identifier, did you mistype a struct type's name?" )) } let ident_text = ctx.pool.intern(ident_text); let identifier = Identifier { span: ident_span, value: ident_text }; ctx.heap.alloc_variable_expression(|this| VariableExpression { this, identifier, declaration: None, used_as_binding_target: false, parent: ExpressionParent::None, type_index: -1, }).upcast() } } } else { return Err(ParseError::new_error_str_at_pos( &module.source, iter.last_valid_pos(), "expected an expression" )); }; Ok(result) } //-------------------------------------------------------------------------- // Expression Utilities //-------------------------------------------------------------------------- #[inline] fn consume_generic_binary_expression< M: Fn(Option) -> Option, F: Fn(&mut PassDefinitions, &Module, &mut TokenIter, &mut PassCtx) -> Result >( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx, match_fn: M, higher_precedence_fn: F ) -> Result { let mut result = higher_precedence_fn(self, module, iter, ctx)?; while let Some(operation) = match_fn(iter.next()) { let operator_span = iter.next_span(); iter.consume(); let left = result; let right = higher_precedence_fn(self, module, iter, ctx)?; let full_span = InputSpan::from_positions( ctx.heap[left].full_span().begin, ctx.heap[right].full_span().end, ); result = ctx.heap.alloc_binary_expression(|this| BinaryExpression{ this, operator_span, full_span, left, operation, right, parent: ExpressionParent::None, type_index: -1, }).upcast(); } Ok(result) } #[inline] fn consume_expression_list( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx, end_pos: Option<&mut InputPosition> ) -> Result, ParseError> { let mut section = self.expressions.start_section(); consume_comma_separated( TokenKind::OpenParen, TokenKind::CloseParen, &module.source, iter, ctx, |_source, iter, ctx| self.consume_expression(module, iter, ctx), &mut section, "an expression", "a list of expressions", end_pos )?; Ok(section.into_vec()) } } /// Consumes polymorphic variables and throws them on the floor. fn consume_polymorphic_vars_spilled(source: &InputSource, iter: &mut TokenIter, _ctx: &mut PassCtx) -> Result<(), ParseError> { maybe_consume_comma_separated_spilled( TokenKind::OpenAngle, TokenKind::CloseAngle, source, iter, _ctx, |source, iter, _ctx| { consume_ident(source, iter)?; Ok(()) }, "a polymorphic variable" )?; Ok(()) } /// Consumes the parameter list to functions/components fn consume_parameter_list( parser: &mut ParserTypeParser, source: &InputSource, iter: &mut TokenIter, ctx: &mut PassCtx, target: &mut ScopedSection, scope: SymbolScope, definition_id: DefinitionId ) -> Result<(), ParseError> { consume_comma_separated( TokenKind::OpenParen, TokenKind::CloseParen, source, iter, ctx, |source, iter, ctx| { let poly_vars = ctx.heap[definition_id].poly_vars(); // Rust being rust, multiple lookups let parser_type = parser.consume_parser_type( iter, &ctx.heap, source, &ctx.symbols, poly_vars, definition_id, scope, false, None )?; let identifier = consume_ident_interned(source, iter, ctx)?; let parameter_id = ctx.heap.alloc_variable(|this| Variable{ this, kind: VariableKind::Parameter, parser_type, identifier, relative_pos_in_parent: 0, unique_id_in_scope: -1, }); Ok(parameter_id) }, target, "a parameter", "a parameter list", None ) }