use crate::ast::*; use crate::parser::Rule; use pest::iterators::{Pair, Pairs}; fn expect_and_use( file_id: usize, pair: Pair, rule: Rule, f: fn(usize, Pair) -> T, ) -> T { if pair.as_rule() != rule { panic!("Expected rule {:?} but found {:?}", rule, pair.as_rule()) } f(file_id, pair) } pub fn build_ast(file_id: usize, compilation_unit_pair: Pair) -> CompilationUnit { build_compilation_unit(file_id, compilation_unit_pair) } fn build_identifier(file_id: usize, identifier_pair: Pair) -> Identifier { let as_span = identifier_pair.as_span(); Identifier::new( as_span.as_str(), file_id, Range { start: as_span.start(), end: as_span.end(), }, ) } fn build_fqn(file_id: usize, fqn_pair: Pair) -> FullyQualifiedName { let as_span = fqn_pair.as_span(); FullyQualifiedName::new( fqn_pair .into_inner() .map(|identifier_pair| { expect_and_use(file_id, identifier_pair, Rule::Identifier, build_identifier) }) .collect(), file_id, Range { start: as_span.start(), end: as_span.end(), }, ) } fn build_type_use(file_id: usize, type_use_pair: Pair) -> TypeUse { let inner_pair = type_use_pair.into_inner().next().unwrap(); match inner_pair.as_rule() { Rule::Void => TypeUse::Void, Rule::InterfaceOrClassTypeUse => { TypeUse::InterfaceOrClass(build_interface_or_class_type_use(file_id, inner_pair)) } Rule::TupleTypeUse => TypeUse::Tuple(build_tuple_type_use(file_id, inner_pair)), Rule::FunctionTypeUse => TypeUse::Function(build_function_type_use(file_id, inner_pair)), _ => unreachable!(), } } fn build_interface_or_class_type_use(file_id: usize, pair: Pair) -> InterfaceOrClassTypeUse { let mut borrow_count = 0; let mut is_mutable = false; let mut fqn = None; let mut generic_arguments = GenericArguments::default(); for inner_pair in pair.into_inner() { match inner_pair.as_rule() { Rule::Borrow => { borrow_count += 1; } Rule::Mut => { is_mutable = true; } Rule::FullyQualifiedName => { fqn = Some(build_fqn(file_id, inner_pair)); } Rule::GenericArguments => { generic_arguments = build_generic_arguments(file_id, inner_pair); } _ => unreachable!(), } } InterfaceOrClassTypeUse { borrow_count, is_mutable, fqn: fqn.unwrap(), generics: generic_arguments, } } fn build_tuple_type_use(file_id: usize, tuple_type_use_pair: Pair) -> TupleTypeUse { let mut borrow_count = 0; let mut is_mutable = false; let mut arguments = None; for inner_pair in tuple_type_use_pair.into_inner() { match inner_pair.as_rule() { Rule::Borrow => { borrow_count += 1; } Rule::Mut => { is_mutable = true; } Rule::TupleArguments => { arguments = Some(build_tuple_arguments(file_id, inner_pair)); } _ => unreachable!(), } } TupleTypeUse { borrow_count, is_mutable, arguments: arguments.unwrap(), } } fn build_function_type_use(file_id: usize, function_pair: Pair) -> FunctionTypeUse { let mut borrow_count = 0; let mut function_modifier: Option = None; let mut generics = GenericParameters::default(); let mut parameters: Option = None; let mut return_type: Option = None; for inner_pair in function_pair.into_inner() { match inner_pair.as_rule() { Rule::Borrow => { borrow_count += 1; } Rule::FunctionTypeModifier => { function_modifier = Some(build_function_type_modifier(file_id, inner_pair)); } Rule::Fn => {} Rule::GenericParameters => { generics = build_generic_parameters(file_id, inner_pair); } Rule::Parameters => { parameters = Some(build_parameters(file_id, inner_pair)); } Rule::ReturnType => { return_type = Some(build_return_type(file_id, inner_pair)); } _ => unreachable!(), } } FunctionTypeUse { borrow_count, function_modifier, generics, parameters: parameters.unwrap(), return_type: return_type.unwrap(), } } fn build_generic_arguments(file_id: usize, generic_arguments_pair: Pair) -> GenericArguments { let type_use_list_pair = generic_arguments_pair.into_inner().next().unwrap(); GenericArguments( type_use_list_pair .into_inner() .map(|type_use_pair| { expect_and_use(file_id, type_use_pair, Rule::TypeUse, build_type_use) }) .collect(), ) } fn build_generic_parameters( file_id: usize, generic_parameters_pair: Pair, ) -> GenericParameters { let identifier_list_pair = generic_parameters_pair.into_inner().next().unwrap(); GenericParameters( identifier_list_pair .into_inner() .map(|identifier_pair| { expect_and_use(file_id, identifier_pair, Rule::Identifier, build_identifier) }) .collect(), ) } fn build_tuple_arguments(file_id: usize, tuple_arguments_pair: Pair) -> TupleArguments { let parentheses_optional_type_use_list_pair = tuple_arguments_pair.into_inner().next().unwrap(); let type_use_list_pair = parentheses_optional_type_use_list_pair .into_inner() .next() .unwrap(); TupleArguments( type_use_list_pair .into_inner() .map(|type_use_pair| { expect_and_use(file_id, type_use_pair, Rule::TypeUse, build_type_use) }) .collect(), ) } fn build_implements_list(file_id: usize, pair: Pair) -> ImplementsList { ImplementsList( pair.into_inner() .map(|type_use_pair| { expect_and_use(file_id, type_use_pair, Rule::TypeUse, build_type_use) }) .collect(), ) } fn build_function_type_modifier(file_id: usize, pair: Pair) -> FunctionTypeModifier { let mut inner = pair.into_inner(); if inner.len() == 2 { FunctionTypeModifier::MutRef } else { match inner.next().unwrap().as_rule() { Rule::Cons => FunctionTypeModifier::Cons, Rule::Mut => FunctionTypeModifier::Mut, Rule::Ref => FunctionTypeModifier::Ref, _ => unreachable!(), } } } fn build_parameters(file_id: usize, parameters_pair: Pair) -> Parameters { Parameters( parameters_pair .into_inner() .map(|parameter_pair| { expect_and_use(file_id, parameter_pair, Rule::Parameter, build_parameter) }) .collect(), ) } fn build_parameter(file_id: usize, parameter_pair: Pair) -> Parameter { let mut inner = parameter_pair.into_inner(); let identifier = expect_and_use( file_id, inner.next().unwrap(), Rule::Identifier, build_identifier, ); let type_use = expect_and_use( file_id, inner.next().unwrap(), Rule::TypeUse, build_type_use, ); Parameter { identifier, type_use, } } fn build_return_type(file_id: usize, return_type_pair: Pair) -> ReturnType { let mut inner = return_type_pair.into_inner(); let declared_type = expect_and_use( file_id, inner.next().unwrap(), Rule::TypeUse, build_type_use, ); let references = inner .next() .map(|ref_list_pair| { expect_and_use(file_id, ref_list_pair, Rule::RefList, build_references) }) .unwrap_or_else(References::default); ReturnType { declared_type: Box::new(declared_type), references, } } fn build_references(file_id: usize, ref_list_pair: Pair) -> References { let mut inner = ref_list_pair.into_inner(); inner.next().unwrap(); // ref References( inner .map(|identifier_pair| { expect_and_use(file_id, identifier_pair, Rule::Identifier, build_identifier) }) .collect(), ) } fn build_compilation_unit(file_id: usize, compilation_unit_pair: Pair) -> CompilationUnit { let mut namespace = None; let mut declarations = vec![]; for inner_pair in compilation_unit_pair.into_inner() { match inner_pair.as_rule() { Rule::Namespace => { namespace = Some(build_namespace(file_id, inner_pair)); } Rule::ModuleLevelDeclaration => { declarations.push(build_module_level_declaration(file_id, inner_pair)); } Rule::EOI => {} _ => unreachable!(), } } CompilationUnit { namespace, declarations, } } fn build_namespace(file_id: usize, namespace_pair: Pair) -> FullyQualifiedName { let mut inner = namespace_pair.into_inner(); inner.next(); // ns expect_and_use( file_id, inner.next().unwrap(), Rule::FullyQualifiedName, build_fqn, ) } fn build_module_level_declaration(file_id: usize, pair: Pair) -> ModuleLevelDeclaration { let inner_pair = pair.into_inner().next().unwrap(); match inner_pair.as_rule() { Rule::Module => { ModuleLevelDeclaration::Module(build_module_declaration(file_id, inner_pair)) } Rule::Interface => { ModuleLevelDeclaration::Interface(build_interface_declaration(file_id, inner_pair)) } Rule::Class => ModuleLevelDeclaration::Class(build_class_declaration(file_id, inner_pair)), Rule::FunctionDefinition => { ModuleLevelDeclaration::Function(build_function_definition(file_id, inner_pair)) } Rule::PlatformFunction => ModuleLevelDeclaration::PlatformFunction( build_platform_function_declaration(file_id, inner_pair), ), _ => unreachable!(), } } fn build_interface_level_declaration( file_id: usize, declaration_pair: Pair, ) -> InterfaceLevelDeclaration { let inner_pair = declaration_pair.into_inner().next().unwrap(); match inner_pair.as_rule() { Rule::Module => { InterfaceLevelDeclaration::Module(build_module_declaration(file_id, inner_pair)) } Rule::Interface => { InterfaceLevelDeclaration::Interface(build_interface_declaration(file_id, inner_pair)) } Rule::Class => { InterfaceLevelDeclaration::Class(build_class_declaration(file_id, inner_pair)) } Rule::InterfaceFunction => InterfaceLevelDeclaration::Function( build_interface_function_declaration(file_id, inner_pair), ), Rule::InterfaceDefaultFunction => InterfaceLevelDeclaration::Function( build_default_interface_function_declaration(file_id, inner_pair), ), Rule::InterfaceOperatorFunction => InterfaceLevelDeclaration::OperatorFunction( build_interface_operator_function_declaration(file_id, inner_pair), ), Rule::InterfaceDefaultOperatorFunction => InterfaceLevelDeclaration::OperatorFunction( build_default_interface_operator_function_declaration(file_id, inner_pair), ), _ => unreachable!(), } } fn build_class_level_declaration( file_id: usize, declaration_pair: Pair, ) -> ClassLevelDeclaration { let inner_pair = declaration_pair.into_inner().next().unwrap(); match inner_pair.as_rule() { Rule::Module => { ClassLevelDeclaration::Module(build_module_declaration(file_id, inner_pair)) } Rule::Interface => { ClassLevelDeclaration::Interface(build_interface_declaration(file_id, inner_pair)) } Rule::Class => ClassLevelDeclaration::Class(build_class_declaration(file_id, inner_pair)), Rule::FunctionDefinition => { ClassLevelDeclaration::Function(build_function_definition(file_id, inner_pair)) } Rule::OperatorFunctionDefinition => ClassLevelDeclaration::OperatorFunction( build_operator_function_declaration(file_id, inner_pair), ), Rule::PlatformFunction => ClassLevelDeclaration::PlatformFunction( build_platform_function_declaration(file_id, inner_pair), ), Rule::Property => { ClassLevelDeclaration::Property(build_property_declaration(file_id, inner_pair)) } Rule::Field => ClassLevelDeclaration::Field(build_field_declaration(file_id, inner_pair)), _ => unreachable!(), } } fn build_module_declaration(file_id: usize, module_pair: Pair) -> ModuleDeclaration { let mut is_public = false; let mut identifier = None; let mut declarations = vec![]; for inner_pair in module_pair.into_inner() { match inner_pair.as_rule() { Rule::Pub => { is_public = true; } Rule::Mod => {} Rule::Identifier => { identifier = Some(build_identifier(file_id, inner_pair)); } Rule::ModuleLevelDeclaration => { declarations.push(build_module_level_declaration(file_id, inner_pair)); } _ => unreachable!(), } } ModuleDeclaration { is_public, identifier: identifier.unwrap(), declarations, } } fn build_interface_declaration(file_id: usize, interface_pair: Pair) -> InterfaceDeclaration { let mut is_public = false; let mut identifier = None; let mut generics = None; let mut implements = None; let mut declarations = vec![]; for inner_pair in interface_pair.into_inner() { match inner_pair.as_rule() { Rule::Pub => { is_public = true; } Rule::Int => {} Rule::Identifier => { identifier = Some(build_identifier(file_id, inner_pair)); } Rule::GenericParameters => { generics = Some(build_generic_parameters(file_id, inner_pair)); } Rule::ImplementsList => { implements = Some(build_implements_list(file_id, inner_pair)); } Rule::InterfaceLevelDeclaration => { declarations.push(build_interface_level_declaration(file_id, inner_pair)); } _ => unreachable!(), } } InterfaceDeclaration { is_public, identifier: identifier.unwrap(), generics: generics.unwrap_or_else(GenericParameters::default), implements: implements.unwrap_or_else(ImplementsList::default), declarations, } } fn build_class_declaration(file_id: usize, class_pair: Pair) -> ClassDeclaration { let mut is_public = false; let mut identifier = None; let mut generics = None; let mut class_constructor = None; let mut implements = None; let mut declarations = vec![]; for inner_pair in class_pair.into_inner() { match inner_pair.as_rule() { Rule::Pub => { is_public = true; } Rule::ClassKw => {} Rule::Identifier => { identifier = Some(build_identifier(file_id, inner_pair)); } Rule::GenericParameters => { generics = Some(build_generic_parameters(file_id, inner_pair)); } Rule::ClassConstructor => { class_constructor = Some(build_class_constructor(file_id, inner_pair)); } Rule::ImplementsList => { implements = Some(build_implements_list(file_id, inner_pair)); } Rule::ClassLevelDeclaration => { declarations.push(build_class_level_declaration(file_id, inner_pair)); } _ => unreachable!(), } } ClassDeclaration { is_public, identifier: identifier.unwrap(), generics: generics.unwrap_or_else(GenericParameters::default), class_constructor, implements: implements.unwrap_or_else(ImplementsList::default), declarations, } } fn build_function_definition( file_id: usize, function_definition_pair: Pair, ) -> FunctionDefinition { let mut is_public = false; let mut modifier = None; let mut generics = None; let mut identifier = None; let mut parameters = None; let mut return_type = None; let mut body = None; for inner_pair in function_definition_pair.into_inner() { match inner_pair.as_rule() { Rule::Pub => { is_public = true; } Rule::FunctionModifier => { modifier = Some(build_function_modifier(file_id, inner_pair)); } Rule::Fn => {} Rule::GenericParameters => { generics = Some(build_generic_parameters(file_id, inner_pair)); } Rule::Identifier => { identifier = Some(build_identifier(file_id, inner_pair)); } Rule::Parameters => { parameters = Some(build_parameters(file_id, inner_pair)); } Rule::ReturnType => { return_type = Some(build_return_type(file_id, inner_pair)); } Rule::FunctionBody => { body = Some(build_function_body(file_id, inner_pair)); } _ => unreachable!(), } } FunctionDefinition { is_public, modifier, generics: generics.unwrap_or_else(GenericParameters::default), identifier: identifier.unwrap(), parameters: parameters.unwrap(), return_type: return_type.unwrap_or_else(ReturnType::void), body: body.unwrap(), } } fn build_operator_function_declaration( file_id: usize, operator_function_pair: Pair, ) -> OperatorFunctionDefinition { todo!() } fn build_platform_function_declaration( file_id: usize, platform_function_pair: Pair, ) -> PlatformFunctionDeclaration { todo!() } fn build_interface_function_declaration( file_id: usize, interface_function_pair: Pair, ) -> InterfaceFunctionDeclaration { todo!() } fn build_interface_operator_function_declaration( file_id: usize, interface_operator_pair: Pair, ) -> InterfaceOperatorFunctionDeclaration { todo!() } fn build_default_interface_function_declaration( file_id: usize, default_interface_function_pair: Pair, ) -> InterfaceFunctionDeclaration { todo!() } fn build_default_interface_operator_function_declaration( file_id: usize, default_interface_operator_pair: Pair, ) -> InterfaceOperatorFunctionDeclaration { todo!() } fn build_class_constructor(file_id: usize, class_constructor_pair: Pair) -> ClassConstructor { ClassConstructor( class_constructor_pair .into_inner() .map(|data_member_pair| { let inner_pair = data_member_pair.into_inner().next().unwrap(); match inner_pair.as_rule() { Rule::Property => { build_property_class_constructor_parameter(file_id, inner_pair) } Rule::Field => build_field_class_constructor_parameter(file_id, inner_pair), _ => unreachable!(), } }) .collect(), ) } fn build_function_modifier(file_id: usize, modifier_pair: Pair) -> FunctionModifier { let mut inner = modifier_pair.into_inner(); if inner.len() == 2 { FunctionModifier::MutRef } else { match inner.next().unwrap().as_rule() { Rule::Static => FunctionModifier::Static, Rule::Cons => FunctionModifier::Cons, Rule::Mut => FunctionModifier::Mut, Rule::Ref => FunctionModifier::Ref, _ => unreachable!(), } } } fn build_function_body(file_id: usize, body_pair: Pair) -> FunctionBody { let inner_pair = body_pair.into_inner().next().unwrap(); match inner_pair.as_rule() { Rule::FunctionEqualsBody => FunctionBody::Equals(expect_and_use( file_id, inner_pair.into_inner().next().unwrap(), Rule::Expression, build_expression, )), Rule::BlockStatement => FunctionBody::Block(build_block_statement(file_id, inner_pair)), Rule::FunctionAliasBody => { let mut alias_body_pairs = inner_pair.into_inner(); alias_body_pairs.next().unwrap(); // Alias FunctionBody::Alias(expect_and_use( file_id, alias_body_pairs.next().unwrap(), Rule::Identifier, build_identifier, )) } _ => unreachable!(), } } fn build_property_class_constructor_parameter( file_id: usize, property_pair: Pair, ) -> ClassConstructorParameter { ClassConstructorParameter::Property(build_property_declaration(file_id, property_pair)) } fn build_field_class_constructor_parameter( file_id: usize, field_pair: Pair, ) -> ClassConstructorParameter { ClassConstructorParameter::Field(build_field_declaration(file_id, field_pair)) } fn build_property_declaration( file_id: usize, property_declaration_pair: Pair, ) -> PropertyDeclaration { let mut is_mutable = false; let mut identifier = None; let mut declared_type = None; for inner_pair in property_declaration_pair.into_inner() { match inner_pair.as_rule() { Rule::Mut => { is_mutable = true; } Rule::Identifier => { identifier = Some(build_identifier(file_id, inner_pair)); } Rule::TypeUse => { declared_type = Some(build_type_use(file_id, inner_pair)); } _ => unreachable!(), } } PropertyDeclaration { is_mutable, identifier: identifier.unwrap(), declared_type: declared_type.unwrap(), } } fn build_field_declaration(file_id: usize, field_pair: Pair) -> FieldDeclaration { let mut is_mutable = false; let mut identifier = None; let mut declared_type = None; for inner_pair in field_pair.into_inner() { match inner_pair.as_rule() { Rule::Mut => { is_mutable = true; } Rule::Fld => {} Rule::Identifier => { identifier = Some(build_identifier(file_id, inner_pair)); } Rule::TypeUse => { declared_type = Some(build_type_use(file_id, inner_pair)); } _ => unreachable!(), } } FieldDeclaration { is_mutable, identifier: identifier.unwrap(), declared_type: declared_type.unwrap(), } } fn build_block_statement(file_id: usize, block_statement_pair: Pair) -> BlockStatement { let mut statements = vec![]; let mut expression = None; for inner_pair in block_statement_pair.into_inner() { match inner_pair.as_rule() { Rule::Statement => { statements.push(build_statement(file_id, inner_pair)); } Rule::Expression => { expression = Some(build_expression(file_id, inner_pair)); } _ => unreachable!(), } } BlockStatement { statements, expression, } } fn build_statement(file_id: usize, statement_pair: Pair) -> Statement { let first = statement_pair.into_inner().next().unwrap(); match first.as_rule() { Rule::BlockStatement => Statement::BlockStatement(build_block_statement(file_id, first)), Rule::VariableDeclaration => { Statement::VariableDeclarationStatement(build_variable_declaration(file_id, first)) } Rule::AssignmentStatement => { Statement::AssignStatement(build_assignment_statement(file_id, first)) } Rule::CallStatement => Statement::CallStatement(build_call_statement(file_id, first)), Rule::ReturnStatement => Statement::ReturnStatement(build_return_statement(file_id, first)), Rule::IfStatement => Statement::IfStatement(build_if_statement(file_id, first)), Rule::IfElseStatement => { Statement::IfElseStatement(build_if_else_statement(file_id, first)) } Rule::WhileStatement => Statement::WhileStatement(build_while_statement(file_id, first)), Rule::ForStatement => Statement::ForStatement(build_for_statement(file_id, first)), _ => unreachable!(), } } fn build_variable_declaration( file_id: usize, variable_declaration_pair: Pair, ) -> VariableDeclarationStatement { let mut is_mutable = false; let mut identifier = None; let mut declared_type = None; let mut initializer = None; for inner_pair in variable_declaration_pair.into_inner() { match inner_pair.as_rule() { Rule::Let => {} Rule::Mut => { is_mutable = true; } Rule::Identifier => { identifier = Some(build_identifier(file_id, inner_pair)); } Rule::TypeUse => { declared_type = Some(build_type_use(file_id, inner_pair)); } Rule::Expression => { initializer = Some(build_expression(file_id, inner_pair)); } _ => unreachable!(), } } VariableDeclarationStatement { is_mutable, identifier: identifier.unwrap(), declared_type, initializer, } } fn build_assignment_statement(file_id: usize, assignment_pair: Pair) -> AssignStatement { let mut inner = assignment_pair.into_inner(); let lhs = build_expression(file_id, inner.next().unwrap()); let rhs = build_expression(file_id, inner.next().unwrap()); AssignStatement { lhs, rhs } } fn build_call_statement(file_id: usize, call_statement_pair: Pair) -> CallStatement { let mut inner = call_statement_pair.into_inner(); let mut result = expect_and_use( file_id, inner.next().unwrap(), Rule::PrimaryExpression, build_primary_expression, ); while let Some(inner_pair) = inner.next() { match inner_pair.as_rule() { Rule::ObjectAccess => { result = Expression::ObjectAccess(build_object_access(file_id, result, inner_pair)); } Rule::ParenthesesCall => { result = Expression::Call(build_call_expression(file_id, result, inner_pair)); } Rule::PlusPlus => { result = Expression::UnarySuffix(SuffixExpression { expression: Box::new(result), operator: SuffixUnaryOperator::PlusPlus, }); } Rule::MinusMinus => { result = Expression::UnarySuffix(SuffixExpression { expression: Box::new(result), operator: SuffixUnaryOperator::MinusMinus, }); } _ => unreachable!(), } } CallStatement(result) } fn build_return_statement(file_id: usize, return_statement_pair: Pair) -> ReturnStatement { todo!() } fn build_if_statement(file_id: usize, if_statement_pair: Pair) -> IfStatement { todo!() } fn build_if_else_statement(file_id: usize, if_else_statement_pair: Pair) -> IfElseStatement { todo!() } fn build_while_statement(file_id: usize, while_statement_pair: Pair) -> WhileStatement { todo!() } fn build_for_statement(file_id: usize, for_statement_pair: Pair) -> ForStatement { todo!() } fn build_expression(file_id: usize, expression_pair: Pair) -> Expression { expect_and_use( file_id, expression_pair.into_inner().next().unwrap(), Rule::TernaryExpression, build_ternary_expression, ) } fn build_ternary_expression(file_id: usize, ternary_pair: Pair) -> Expression { let mut inner = ternary_pair.into_inner(); if inner.len() == 3 { let condition = expect_and_use( file_id, inner.next().unwrap(), Rule::OrExpression, build_or_expression, ); let true_expression = expect_and_use( file_id, inner.next().unwrap(), Rule::Expression, build_expression, ); let false_expression = expect_and_use( file_id, inner.next().unwrap(), Rule::Expression, build_expression, ); Expression::Ternary(TernaryExpression { condition: Box::new(condition), true_expression: Box::new(true_expression), false_expression: Box::new(false_expression), }) } else { expect_and_use( file_id, inner.next().unwrap(), Rule::OrExpression, build_or_expression, ) } } macro_rules! build_binary_expression { ( $file_id:expr, $pair:ident, $left_rule:expr, $left_fn:expr, $( $pat:pat => $arm:expr ),* $(,)? ) => {{ let mut inner: Pairs = $pair.into_inner(); if inner.len() == 3 { let left = expect_and_use($file_id, inner.next().unwrap(), $left_rule, $left_fn); let op = inner.next().unwrap(); // op let right = expect_and_use($file_id, inner.next().unwrap(), Rule::Expression, build_expression); Expression::Binary(BinaryExpression { left: Box::new(left), operator: match op.as_rule() { $( $pat => $arm, )* _ => unreachable!(), }, right: Box::new(right), }) } else { expect_and_use($file_id, inner.next().unwrap(), $left_rule, $left_fn) } }}; } fn build_or_expression(file_id: usize, or_expression_pair: Pair) -> Expression { build_binary_expression!( file_id, or_expression_pair, Rule::AndExpression, build_and_expression, Rule::Or => BinaryOperator::Or, ) } fn build_and_expression(file_id: usize, and_expression_pair: Pair) -> Expression { build_binary_expression!(file_id, and_expression_pair, Rule::ComparisonExpression, build_comparison_expression, Rule::And => BinaryOperator::And, ) } fn build_comparison_expression( file_id: usize, comparison_expression_pair: Pair, ) -> Expression { build_binary_expression!(file_id, comparison_expression_pair, Rule::ShiftExpression, build_shift_expression, Rule::Greater => BinaryOperator::Greater, Rule::Less => BinaryOperator::Less, Rule::GreaterEqual => BinaryOperator::GreaterEqual, Rule::LessEqual => BinaryOperator::LessEqual, Rule::EqualTo => BinaryOperator::EqualTo, Rule::NotEqualTo => BinaryOperator::NotEqualTo, ) } fn build_shift_expression(file_id: usize, shift_expression_pair: Pair) -> Expression { build_binary_expression!(file_id, shift_expression_pair, Rule::AdditiveExpression, build_additive_expression, Rule::LeftShift => BinaryOperator::LeftShift, Rule::RightShift => BinaryOperator::RightShift, ) } fn build_additive_expression(file_id: usize, add_expression_pair: Pair) -> Expression { build_binary_expression!(file_id, add_expression_pair, Rule::MultiplicativeExpression, build_multiplicative_expression, Rule::Add => BinaryOperator::Add, Rule::Subtract => BinaryOperator::Subtract, ) } fn build_multiplicative_expression( file_id: usize, multiplicative_expression_pair: Pair, ) -> Expression { build_binary_expression!(file_id, multiplicative_expression_pair, Rule::PrefixExpression, build_prefix_expression, Rule::Multiply => BinaryOperator::Multiply, Rule::Divide => BinaryOperator::Divide, ) } fn build_prefix_expression(file_id: usize, prefix_pair: Pair) -> Expression { let mut inner_rev = prefix_pair.into_inner().rev(); let mut result = expect_and_use( file_id, inner_rev.next().unwrap(), Rule::SuffixExpression, build_suffix_expression, ); while let Some(prefix_pair) = inner_rev.next() { match prefix_pair.as_rule() { Rule::Spread => { result = Expression::UnaryPrefix(PrefixExpression { operator: PrefixUnaryOperator::Spread, expression: Box::new(result), }); } Rule::BorrowMut => { result = Expression::UnaryPrefix(PrefixExpression { operator: PrefixUnaryOperator::BorrowMut, expression: Box::new(result), }) } Rule::Borrow => { result = Expression::UnaryPrefix(PrefixExpression { operator: PrefixUnaryOperator::Borrow, expression: Box::new(result), }) } Rule::Mut => { result = Expression::UnaryPrefix(PrefixExpression { operator: PrefixUnaryOperator::Mut, expression: Box::new(result), }) } Rule::Not => { result = Expression::UnaryPrefix(PrefixExpression { operator: PrefixUnaryOperator::Not, expression: Box::new(result), }) } Rule::Negative => { result = Expression::UnaryPrefix(PrefixExpression { operator: PrefixUnaryOperator::Negative, expression: Box::new(result), }) } _ => unreachable!(), } } result } fn build_suffix_expression(file_id: usize, suffix_pair: Pair) -> Expression { let mut inner = suffix_pair.into_inner(); let mut result = expect_and_use( file_id, inner.next().unwrap(), Rule::PrimaryExpression, build_primary_expression, ); while let Some(suffix_pair) = inner.next() { match suffix_pair.as_rule() { Rule::ObjectAccess => { result = Expression::ObjectAccess(build_object_access(file_id, result, suffix_pair)) } Rule::ParenthesesCall => { result = Expression::Call(build_call_expression(file_id, result, suffix_pair)) } Rule::PlusPlus => { result = Expression::UnarySuffix(SuffixExpression { expression: Box::new(result), operator: SuffixUnaryOperator::PlusPlus, }) } Rule::MinusMinus => { result = Expression::UnarySuffix(SuffixExpression { expression: Box::new(result), operator: SuffixUnaryOperator::MinusMinus, }) } _ => unreachable!(), } } result } fn build_call_expression( file_id: usize, callee: Expression, parentheses_call_pair: Pair, ) -> CallExpression { let mut turbo_fish = None; let mut arguments = vec![]; for inner_pair in parentheses_call_pair.into_inner() { match inner_pair.as_rule() { Rule::TurboFish => { turbo_fish = Some(build_turbo_fish(file_id, inner_pair)); } Rule::ExpressionList => { for expression_pair in inner_pair.into_inner() { arguments.push(expect_and_use( file_id, expression_pair, Rule::Expression, build_expression, )); } } Rule::Closure => { arguments.push(Expression::Closure(build_closure(file_id, inner_pair))); } _ => unreachable!(), } } CallExpression { callee: Box::new(callee), turbo_fish, arguments: CallArguments( arguments .into_iter() .map(|argument| CallArgument(Box::new(argument))) .collect(), ), } } fn build_turbo_fish(file_id: usize, turbo_fish_pair: Pair) -> TurboFish { TurboFish(build_generic_arguments( file_id, turbo_fish_pair.into_inner().next().unwrap(), )) } fn build_primary_expression(file_id: usize, primary_expression_pair: Pair) -> Expression { let first_pair = primary_expression_pair.into_inner().next().unwrap(); match first_pair.as_rule() { Rule::Literal => Expression::Literal(build_literal(file_id, first_pair)), Rule::FullyQualifiedName => Expression::FullyQualifiedName(build_fqn(file_id, first_pair)), Rule::Closure => Expression::Closure(build_closure(file_id, first_pair)), Rule::ParenthesizedExpression => { let inner_expression = first_pair.into_inner().next().unwrap(); expect_and_use( file_id, inner_expression, Rule::Expression, build_expression, ) } _ => unreachable!(), } } fn build_object_access( file_id: usize, receiver: Expression, object_access_pair: Pair, ) -> ObjectAccess { ObjectAccess { receiver: Box::new(receiver), navigations: ObjectNavigations( object_access_pair .into_inner() .map( |property_or_index_pair| match property_or_index_pair.as_rule() { Rule::ObjectProperty => { build_object_property(file_id, property_or_index_pair) } Rule::ObjectIndex => build_object_index(file_id, property_or_index_pair), _ => unreachable!(), }, ) .collect(), ), } } fn build_object_property(file_id: usize, inner_pair: Pair) -> ObjectNavigation { ObjectNavigation::Identifier(Box::new(expect_and_use( file_id, inner_pair.into_inner().next().unwrap(), Rule::Identifier, build_identifier, ))) } fn build_object_index(file_id: usize, inner_pair: Pair) -> ObjectNavigation { ObjectNavigation::Index(Box::new(expect_and_use( file_id, inner_pair.into_inner().next().unwrap(), Rule::Expression, build_expression, ))) } fn build_closure(file_id: usize, closure_pair: Pair) -> Closure { todo!() } fn build_literal(file_id: usize, literal_pair: Pair) -> Literal { let inner_pair = literal_pair.into_inner().next().unwrap(); match inner_pair.as_rule() { Rule::NumberLiteral => build_number_literal(file_id, inner_pair), Rule::StringLiteral => build_string_literal(file_id, inner_pair), Rule::BooleanLiteral => build_boolean_literal(file_id, inner_pair), _ => unreachable!(), } } fn build_number_literal(file_id: usize, pair: Pair) -> Literal { todo!() } fn build_string_literal(file_id: usize, pair: Pair) -> Literal { let inner_pair = pair.into_inner().next().unwrap(); match inner_pair.as_rule() { Rule::SingleQuoteString => build_single_quote_string(file_id, inner_pair), Rule::DoubleQuoteString => build_double_quote_string(file_id, inner_pair), Rule::BacktickString => build_backtick_string(file_id, inner_pair), _ => unreachable!(), } } fn build_boolean_literal(file_id: usize, pair: Pair) -> Literal { let inner_pair = pair.into_inner().next().unwrap(); match inner_pair.as_rule() { Rule::True => Literal::Boolean(true), Rule::False => Literal::Boolean(false), _ => unreachable!(), } } fn build_single_quote_string(file_id: usize, pair: Pair) -> Literal { let inner_pair = pair.into_inner().next().unwrap(); Literal::String(inner_pair.as_span().as_str().to_string()) } fn build_double_quote_string(file_id: usize, pair: Pair) -> Literal { todo!() } fn build_backtick_string(file_id: usize, pair: Pair) -> Literal { todo!() } #[cfg(test)] mod tests { use super::*; use crate::parser::DeimosParser; use indoc::indoc; fn assert_builds(src: &str) { let parse_result = DeimosParser::parse(Rule::CompilationUnit, src); if let Err(e) = parse_result { panic!("Parsing failed.\n{}", e) } else { build_ast(0, parse_result.unwrap().next().unwrap()); } } #[test] fn index_object() { assert_builds(indoc! {" fn main() { let x = foo[a]; } "}) } #[test] fn object_index_and_call() { assert_builds(indoc! {"\ fn main() { a[b]().c() } "}) } }