1846 lines
56 KiB
Rust
1846 lines
56 KiB
Rust
use crate::ast::*;
|
|
use crate::parser::Rule;
|
|
use pest::iterators::{Pair, Pairs};
|
|
use std::str::FromStr;
|
|
|
|
fn expect_and_use<T>(
|
|
file_id: usize,
|
|
pair: Pair<Rule>,
|
|
rule: Rule,
|
|
f: fn(usize, Pair<Rule>) -> 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_name: &str,
|
|
file_id: usize,
|
|
compilation_unit_pair: Pair<Rule>,
|
|
) -> CompilationUnit {
|
|
build_compilation_unit(file_name, file_id, compilation_unit_pair)
|
|
}
|
|
|
|
fn build_identifier(file_id: usize, identifier_pair: Pair<Rule>) -> 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<Rule>) -> 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<Rule>) -> 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<Rule>) -> 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<Rule>) -> 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<Rule>) -> FunctionTypeUse {
|
|
let mut borrow_count = 0;
|
|
let mut function_modifier: Option<FunctionTypeModifier> = None;
|
|
let mut generics = GenericParameters::default();
|
|
let mut parameters: Option<Parameters> = None;
|
|
let mut return_type: Option<ReturnType> = 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<Rule>) -> 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<Rule>,
|
|
) -> 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<Rule>) -> 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<Rule>) -> 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<Rule>) -> 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<Rule>) -> 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<Rule>) -> 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<Rule>) -> 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<Rule>) -> 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_name: &str,
|
|
file_id: usize,
|
|
compilation_unit_pair: Pair<Rule>,
|
|
) -> CompilationUnit {
|
|
let mut namespace = None;
|
|
let mut use_statements = vec![];
|
|
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::UseStatement => {
|
|
use_statements.push(build_use_statement(file_id, inner_pair));
|
|
}
|
|
Rule::ModuleLevelDeclaration => {
|
|
declarations.push(build_module_level_declaration(file_id, inner_pair));
|
|
}
|
|
Rule::Semicolon | Rule::EOI => {}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
CompilationUnit {
|
|
file_name: file_name.to_string(),
|
|
file_id,
|
|
namespace,
|
|
use_statements,
|
|
declarations,
|
|
}
|
|
}
|
|
|
|
fn build_namespace(file_id: usize, namespace_pair: Pair<Rule>) -> 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<Rule>) -> 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<Rule>,
|
|
) -> 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<Rule>,
|
|
) -> 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<Rule>) -> 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<Rule>) -> 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<Rule>) -> 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<Rule>,
|
|
) -> 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<Rule>,
|
|
) -> OperatorFunctionDefinition {
|
|
todo!()
|
|
}
|
|
|
|
fn build_platform_function_declaration(
|
|
file_id: usize,
|
|
platform_function_pair: Pair<Rule>,
|
|
) -> PlatformFunctionDeclaration {
|
|
let mut is_public = false;
|
|
let mut modifier = None;
|
|
let mut generics = GenericParameters::default();
|
|
let mut identifier = None;
|
|
let mut parameters = Parameters::default();
|
|
let mut return_type = None;
|
|
|
|
for inner_pair in platform_function_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::Platform | Rule::Fn => {},
|
|
Rule::GenericParameters => {
|
|
generics = build_generic_parameters(file_id, inner_pair);
|
|
}
|
|
Rule::Identifier => {
|
|
identifier = Some(build_identifier(file_id, inner_pair));
|
|
}
|
|
Rule::Parameters => {
|
|
parameters = build_parameters(file_id, inner_pair);
|
|
}
|
|
Rule::ReturnType => {
|
|
return_type = Some(build_return_type(file_id, inner_pair));
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
PlatformFunctionDeclaration {
|
|
is_public,
|
|
modifier,
|
|
generics,
|
|
identifier: identifier.unwrap(),
|
|
parameters,
|
|
return_type: return_type.unwrap(),
|
|
}
|
|
}
|
|
|
|
fn build_interface_function_declaration(
|
|
file_id: usize,
|
|
interface_function_pair: Pair<Rule>,
|
|
) -> InterfaceFunctionDeclaration {
|
|
todo!()
|
|
}
|
|
|
|
fn build_interface_operator_function_declaration(
|
|
file_id: usize,
|
|
interface_operator_pair: Pair<Rule>,
|
|
) -> InterfaceOperatorFunctionDeclaration {
|
|
todo!()
|
|
}
|
|
|
|
fn build_default_interface_function_declaration(
|
|
file_id: usize,
|
|
default_interface_function_pair: Pair<Rule>,
|
|
) -> InterfaceFunctionDeclaration {
|
|
todo!()
|
|
}
|
|
|
|
fn build_default_interface_operator_function_declaration(
|
|
file_id: usize,
|
|
default_interface_operator_pair: Pair<Rule>,
|
|
) -> InterfaceOperatorFunctionDeclaration {
|
|
todo!()
|
|
}
|
|
|
|
fn build_class_constructor(file_id: usize, class_constructor_pair: Pair<Rule>) -> 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<Rule>) -> 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<Rule>) -> 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<Rule>,
|
|
) -> ClassConstructorParameter {
|
|
ClassConstructorParameter::Property(build_property_declaration(file_id, property_pair))
|
|
}
|
|
|
|
fn build_field_class_constructor_parameter(
|
|
file_id: usize,
|
|
field_pair: Pair<Rule>,
|
|
) -> ClassConstructorParameter {
|
|
ClassConstructorParameter::Field(build_field_declaration(file_id, field_pair))
|
|
}
|
|
|
|
fn build_property_declaration(
|
|
file_id: usize,
|
|
property_declaration_pair: Pair<Rule>,
|
|
) -> 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<Rule>) -> 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_use_statement(file_id: usize, use_statement_pair: Pair<Rule>) -> UseStatement {
|
|
let mut inner = use_statement_pair.into_inner();
|
|
let inner_length = inner.len();
|
|
|
|
inner.next().unwrap(); // use
|
|
|
|
let mut identifiers = vec![];
|
|
let mut last = None;
|
|
|
|
for (i, inner_pair) in inner.into_iter().enumerate() {
|
|
if i != inner_length - 2 {
|
|
identifiers.push(expect_and_use(
|
|
file_id,
|
|
inner_pair,
|
|
Rule::Identifier,
|
|
build_identifier,
|
|
))
|
|
} else {
|
|
last = Some(match inner_pair.as_rule() {
|
|
Rule::Identifier => {
|
|
UseStatementLast::Identifier(build_identifier(file_id, inner_pair))
|
|
}
|
|
Rule::Star => UseStatementLast::Star,
|
|
Rule::UseList => UseStatementLast::Identifiers(
|
|
inner_pair
|
|
.into_inner()
|
|
.map(|identifier_pair| {
|
|
expect_and_use(
|
|
file_id,
|
|
identifier_pair,
|
|
Rule::Identifier,
|
|
build_identifier,
|
|
)
|
|
})
|
|
.collect(),
|
|
),
|
|
_ => unreachable!(),
|
|
})
|
|
}
|
|
}
|
|
|
|
UseStatement {
|
|
identifiers,
|
|
last: last.unwrap(),
|
|
}
|
|
}
|
|
|
|
fn build_block_statement(file_id: usize, block_statement_pair: Pair<Rule>) -> 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<Rule>) -> 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<Rule>,
|
|
) -> 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<Rule>) -> 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<Rule>) -> 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<Rule>) -> ReturnStatement {
|
|
let mut inner = return_statement_pair.into_inner();
|
|
if inner.len() == 2 {
|
|
inner.next().unwrap(); // return
|
|
let expression = expect_and_use(
|
|
file_id,
|
|
inner.next().unwrap(),
|
|
Rule::Expression,
|
|
build_expression,
|
|
);
|
|
ReturnStatement(Some(expression))
|
|
} else {
|
|
ReturnStatement(None)
|
|
}
|
|
}
|
|
|
|
fn build_if_statement(file_id: usize, if_statement_pair: Pair<Rule>) -> IfStatement {
|
|
let mut inner = if_statement_pair.into_inner();
|
|
inner.next().unwrap(); // if
|
|
let condition = expect_and_use(
|
|
file_id,
|
|
inner.next().unwrap(),
|
|
Rule::Expression,
|
|
build_expression,
|
|
);
|
|
let then_block = expect_and_use(
|
|
file_id,
|
|
inner.next().unwrap(),
|
|
Rule::BlockStatement,
|
|
build_block_statement,
|
|
);
|
|
IfStatement {
|
|
condition,
|
|
then_block,
|
|
}
|
|
}
|
|
|
|
fn build_if_else_statement(file_id: usize, if_else_statement_pair: Pair<Rule>) -> IfElseStatement {
|
|
let mut if_statement = None;
|
|
let mut else_ifs = ElseIfs::default();
|
|
let mut else_block = None;
|
|
|
|
for inner_pair in if_else_statement_pair.into_inner() {
|
|
match inner_pair.as_rule() {
|
|
Rule::IfStatement => {
|
|
if_statement = Some(build_if_statement(file_id, inner_pair));
|
|
}
|
|
Rule::ElseIf => {
|
|
let mut else_if_inner = inner_pair.into_inner();
|
|
else_if_inner.next().unwrap(); // else
|
|
else_ifs.0.push(expect_and_use(
|
|
file_id,
|
|
else_if_inner.next().unwrap(),
|
|
Rule::IfStatement,
|
|
build_if_statement,
|
|
));
|
|
}
|
|
Rule::ElseBlock => {
|
|
let mut else_inner = inner_pair.into_inner();
|
|
else_inner.next().unwrap(); // else
|
|
else_block = Some(ElseBlock(expect_and_use(
|
|
file_id,
|
|
else_inner.next().unwrap(),
|
|
Rule::BlockStatement,
|
|
build_block_statement,
|
|
)));
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
IfElseStatement {
|
|
if_statement: if_statement.unwrap(),
|
|
else_ifs,
|
|
else_block,
|
|
}
|
|
}
|
|
|
|
fn build_while_statement(file_id: usize, while_statement_pair: Pair<Rule>) -> WhileStatement {
|
|
let mut inner = while_statement_pair.into_inner();
|
|
inner.next().unwrap(); // while
|
|
let condition = expect_and_use(
|
|
file_id,
|
|
inner.next().unwrap(),
|
|
Rule::Expression,
|
|
build_expression,
|
|
);
|
|
|
|
let body = expect_and_use(
|
|
file_id,
|
|
inner.next().unwrap(),
|
|
Rule::BlockStatement,
|
|
build_block_statement,
|
|
);
|
|
WhileStatement { condition, body }
|
|
}
|
|
|
|
fn build_for_statement(file_id: usize, for_statement_pair: Pair<Rule>) -> ForStatement {
|
|
let mut inner = for_statement_pair.into_inner();
|
|
inner.next().unwrap(); // for
|
|
let variable = expect_and_use(
|
|
file_id,
|
|
inner.next().unwrap(),
|
|
Rule::Identifier,
|
|
build_identifier,
|
|
);
|
|
inner.next().unwrap(); // in
|
|
let iterator = expect_and_use(
|
|
file_id,
|
|
inner.next().unwrap(),
|
|
Rule::Expression,
|
|
build_expression,
|
|
);
|
|
let body = expect_and_use(
|
|
file_id,
|
|
inner.next().unwrap(),
|
|
Rule::BlockStatement,
|
|
build_block_statement,
|
|
);
|
|
ForStatement {
|
|
variable,
|
|
iterator,
|
|
body,
|
|
}
|
|
}
|
|
|
|
fn build_expression(file_id: usize, expression_pair: Pair<Rule>) -> 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<Rule>) -> 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<Rule> = $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<Rule>) -> 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<Rule>) -> 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<Rule>,
|
|
) -> 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<Rule>) -> 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<Rule>) -> 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<Rule>,
|
|
) -> 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<Rule>) -> 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<Rule>) -> 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<Rule>,
|
|
) -> 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<Rule>) -> 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<Rule>) -> 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<Rule>,
|
|
) -> 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<Rule>) -> 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<Rule>) -> 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<Rule>) -> Closure {
|
|
let mut modifier = None;
|
|
let mut is_move = false;
|
|
let mut captures = ClosureCaptures::default();
|
|
let mut parameters = ClosureParameters::default();
|
|
let mut statements = vec![];
|
|
let mut expression = None;
|
|
|
|
for inner_pair in closure_pair.into_inner() {
|
|
match inner_pair.as_rule() {
|
|
Rule::Cons => modifier = Some(ClosureModifier::Cons),
|
|
Rule::Mut => modifier = Some(ClosureModifier::Mut),
|
|
Rule::ClosureCaptures => {
|
|
captures = build_closure_captures(file_id, inner_pair);
|
|
}
|
|
Rule::ClosureParameters => {
|
|
parameters = build_closure_parameters(file_id, inner_pair);
|
|
}
|
|
Rule::Statement => {
|
|
statements.push(build_statement(file_id, inner_pair));
|
|
}
|
|
Rule::Expression => {
|
|
expression = Some(Box::new(build_expression(file_id, inner_pair)));
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
Closure {
|
|
modifier,
|
|
is_move,
|
|
captures,
|
|
parameters,
|
|
statements,
|
|
expression,
|
|
}
|
|
}
|
|
|
|
fn build_closure_captures(file_id: usize, captures_pair: Pair<Rule>) -> ClosureCaptures {
|
|
ClosureCaptures(
|
|
captures_pair
|
|
.into_inner()
|
|
.map(|capture_pair| {
|
|
expect_and_use(
|
|
file_id,
|
|
capture_pair,
|
|
Rule::ClosureCapture,
|
|
build_closure_capture,
|
|
)
|
|
})
|
|
.collect(),
|
|
)
|
|
}
|
|
|
|
fn build_closure_capture(file_id: usize, capture_pair: Pair<Rule>) -> ClosureCapture {
|
|
let mut borrow_count = 0;
|
|
let mut is_mutable = false;
|
|
let mut identifier = None;
|
|
|
|
for inner_pair in capture_pair.into_inner() {
|
|
match inner_pair.as_rule() {
|
|
Rule::Borrow => {
|
|
borrow_count += 1;
|
|
}
|
|
Rule::Mut => {
|
|
is_mutable = true;
|
|
}
|
|
Rule::Identifier => {
|
|
identifier = Some(Box::new(build_identifier(file_id, inner_pair)));
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
ClosureCapture {
|
|
borrow_count,
|
|
is_mutable,
|
|
identifier: identifier.unwrap(),
|
|
}
|
|
}
|
|
|
|
fn build_closure_parameters(file_id: usize, parameters_pair: Pair<Rule>) -> ClosureParameters {
|
|
ClosureParameters(
|
|
parameters_pair
|
|
.into_inner()
|
|
.map(|parameter_pair| {
|
|
expect_and_use(
|
|
file_id,
|
|
parameter_pair,
|
|
Rule::ClosureParameter,
|
|
build_closure_parameter,
|
|
)
|
|
})
|
|
.collect(),
|
|
)
|
|
}
|
|
|
|
fn build_closure_parameter(file_id: usize, parameter_pair: Pair<Rule>) -> ClosureParameter {
|
|
let mut inner = parameter_pair.into_inner();
|
|
let identifier = expect_and_use(
|
|
file_id,
|
|
inner.next().unwrap(),
|
|
Rule::Identifier,
|
|
build_identifier,
|
|
);
|
|
let type_use = if let Some(type_use_pair) = inner.next() {
|
|
Some(expect_and_use(
|
|
file_id,
|
|
type_use_pair,
|
|
Rule::TypeUse,
|
|
build_type_use,
|
|
))
|
|
} else {
|
|
None
|
|
};
|
|
ClosureParameter {
|
|
identifier,
|
|
type_use,
|
|
}
|
|
}
|
|
|
|
fn build_literal(file_id: usize, literal_pair: Pair<Rule>) -> 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<Rule>) -> Literal {
|
|
let inner_pair = pair.into_inner().next().unwrap();
|
|
match inner_pair.as_rule() {
|
|
Rule::DoubleLiteral => build_double_literal(file_id, inner_pair),
|
|
Rule::LongLiteral => build_long_literal(file_id, inner_pair),
|
|
Rule::IntLiteral => build_int_literal(file_id, inner_pair),
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
fn build_double_literal(file_id: usize, inner_pair: Pair<Rule>) -> Literal {
|
|
Literal::Double(f64::from_str(&inner_pair.as_str()).unwrap())
|
|
}
|
|
|
|
fn build_long_literal(file_id: usize, inner_pair: Pair<Rule>) -> Literal {
|
|
let as_string = inner_pair.as_str();
|
|
let without_el = &as_string[0..(as_string.len() - 1)];
|
|
Literal::Long(i64::from_str(without_el).unwrap())
|
|
}
|
|
|
|
fn build_int_literal(file_id: usize, inner_pair: Pair<Rule>) -> Literal {
|
|
Literal::Integer(i32::from_str(&inner_pair.as_str()).unwrap())
|
|
}
|
|
|
|
fn build_string_literal(file_id: usize, pair: Pair<Rule>) -> 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<Rule>) -> 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<Rule>) -> 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<Rule>) -> Literal {
|
|
let mut parts: Vec<DStringPart> = vec![];
|
|
handle_d_backtick_strings_inner(Rule::DStringInner, file_id, pair, &mut parts);
|
|
if parts.len() == 1 && parts[0].is_string() {
|
|
Literal::String(parts.pop().unwrap().unwrap_string())
|
|
} else {
|
|
Literal::DString(DString(parts))
|
|
}
|
|
}
|
|
|
|
fn build_backtick_string(file_id: usize, pair: Pair<Rule>) -> Literal {
|
|
let mut parts: Vec<DStringPart> = vec![];
|
|
handle_d_backtick_strings_inner(Rule::BacktickInner, file_id, pair, &mut parts);
|
|
if parts.len() == 1 && parts[0].is_string() {
|
|
Literal::String(parts.pop().unwrap().unwrap_string())
|
|
} else {
|
|
Literal::BacktickString(DString(parts))
|
|
}
|
|
}
|
|
|
|
fn handle_d_backtick_strings_inner(
|
|
inner_rule: Rule,
|
|
file_id: usize,
|
|
pair: Pair<Rule>,
|
|
parts: &mut Vec<DStringPart>,
|
|
) {
|
|
for inner_pair in pair.into_inner() {
|
|
let as_rule = inner_pair.as_rule();
|
|
if as_rule == inner_rule {
|
|
parts.push(DStringPart::from_string(inner_pair.as_span().as_str()));
|
|
} else if as_rule == Rule::DStringExpression {
|
|
let expression_pair = inner_pair.into_inner().next().unwrap();
|
|
parts.push(DStringPart::from_expression(expect_and_use(
|
|
file_id,
|
|
expression_pair,
|
|
Rule::Expression,
|
|
build_expression,
|
|
)));
|
|
} else {
|
|
unreachable!()
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::ast::pretty_print::PrettyPrint;
|
|
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 {
|
|
let mut pairs = parse_result.unwrap();
|
|
if pairs.as_str().trim() != src.trim() {
|
|
panic!("Parsing did not consume entire input.");
|
|
}
|
|
let ast = build_ast("test.dm", 0, pairs.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()
|
|
}
|
|
"});
|
|
}
|
|
|
|
#[test]
|
|
fn int_literal() {
|
|
assert_builds("fn main() { 0 }");
|
|
}
|
|
|
|
#[test]
|
|
fn long_literal() {
|
|
assert_builds("fn main() { 1L }");
|
|
}
|
|
|
|
#[test]
|
|
fn double_literal() {
|
|
assert_builds("fn main() { 1.0 }");
|
|
}
|
|
|
|
#[test]
|
|
fn d_string_no_expressions() {
|
|
assert_builds("fn main() { \"Hello, World!\" }");
|
|
}
|
|
|
|
#[test]
|
|
fn d_string_one_expression() {
|
|
assert_builds("fn main() { \"${foo}\" }");
|
|
}
|
|
|
|
#[test]
|
|
fn d_string_literal_and_expression() {
|
|
assert_builds("fn main() { \"Hello, ${foo}\" }");
|
|
}
|
|
|
|
#[test]
|
|
fn d_string_literal_expression_literal() {
|
|
assert_builds("fn main() { \"Hello, ${foo}! It is a nice day.\" }");
|
|
}
|
|
|
|
#[test]
|
|
fn if_statement() {
|
|
assert_builds(indoc! {"
|
|
fn main() {
|
|
if (true) {
|
|
foo
|
|
}
|
|
}
|
|
"})
|
|
}
|
|
|
|
#[test]
|
|
fn if_else_statement() {
|
|
assert_builds(indoc! {"
|
|
fn main() {
|
|
if (true) {
|
|
foo
|
|
} else {
|
|
bar
|
|
}
|
|
}
|
|
"})
|
|
}
|
|
|
|
#[test]
|
|
fn if_else_if_statement() {
|
|
assert_builds(indoc! {"
|
|
fn main() {
|
|
if (true) {
|
|
foo
|
|
} else if (false) {
|
|
bar
|
|
}
|
|
}
|
|
"})
|
|
}
|
|
|
|
#[test]
|
|
fn if_else_if_else_statement() {
|
|
assert_builds(indoc! {"
|
|
fn main() {
|
|
if (true) {
|
|
foo
|
|
} else if (false) {
|
|
bar
|
|
} else {
|
|
buzz
|
|
}
|
|
}
|
|
"})
|
|
}
|
|
|
|
#[test]
|
|
fn while_statement() {
|
|
assert_builds(indoc! {"
|
|
fn main() {
|
|
while (true) {
|
|
foo()
|
|
}
|
|
}
|
|
"})
|
|
}
|
|
|
|
#[test]
|
|
fn for_statement() {
|
|
assert_builds(indoc! {"
|
|
fn main(args: Array<String>) {
|
|
for (arg in args) {
|
|
foo(arg);
|
|
}
|
|
}
|
|
"})
|
|
}
|
|
|
|
#[test]
|
|
fn if_with_call_condition() {
|
|
assert_builds(indoc! {"
|
|
fn main() {
|
|
if (foo()) {
|
|
bar()
|
|
}
|
|
}
|
|
"})
|
|
}
|
|
|
|
#[test]
|
|
fn while_with_call_condition() {
|
|
assert_builds(indoc! {"
|
|
fn main() {
|
|
while (foo()) {
|
|
bar()
|
|
}
|
|
}
|
|
"})
|
|
}
|
|
|
|
#[test]
|
|
fn for_with_call_iterator() {
|
|
assert_builds(indoc! {"
|
|
fn main() {
|
|
for (foo in bar()) {
|
|
baz(foo)
|
|
}
|
|
}
|
|
"})
|
|
}
|
|
|
|
#[test]
|
|
fn use_statement() {
|
|
assert_builds("use std::core::println;");
|
|
}
|
|
|
|
#[test]
|
|
fn use_star() {
|
|
assert_builds("use std::core::*;");
|
|
}
|
|
|
|
#[test]
|
|
fn use_list() {
|
|
assert_builds("use std::core::{print, println};");
|
|
}
|
|
|
|
#[test]
|
|
fn platform_function() {
|
|
assert_builds("platform fn println(msg: Any) -> Void;");
|
|
}
|
|
}
|