use super::element::{parse_element, parse_element_content};
use super::prelude::*;
use super::r#type::{parse_enum_declaration, parse_rustattr, parse_struct_declaration};
#[cfg_attr(test, parser_test)]
pub fn parse_document(p: &mut impl Parser) -> bool {
let mut p = p.start_node(SyntaxKind::Document);
loop {
if p.test(SyntaxKind::Eof) {
return true;
}
if p.peek().kind() == SyntaxKind::Semicolon {
p.error("Extra semicolon. Remove this semicolon");
p.consume();
continue;
}
match p.peek().as_str() {
"export" => {
if !parse_export(&mut *p, None) {
break;
}
}
"import" => {
if !parse_import_specifier(&mut *p) {
break;
}
}
"struct" => {
if !parse_struct_declaration(&mut *p, None) {
break;
}
}
"enum" => {
if !parse_enum_declaration(&mut *p, None) {
break;
}
}
"@" if p.nth(1).as_str() == "rust-attr" => {
let checkpoint = p.checkpoint();
if !parse_rustattr(&mut *p) {
break;
}
while p.peek().as_str() == "@" && p.nth(1).as_str() == "rust-attr" {
parse_rustattr(&mut *p);
}
let is_export = p.peek().as_str() == "export";
let i = if is_export { 1 } else { 0 };
if !matches!(p.nth(i).as_str(), "enum" | "struct") {
p.error("Expected enum or struct after @rust-attr");
continue;
}
let r = if is_export {
parse_export(&mut *p, Some(checkpoint))
} else if p.peek().as_str() == "struct" {
parse_struct_declaration(&mut *p, Some(checkpoint))
} else if p.peek().as_str() == "enum" {
parse_enum_declaration(&mut *p, Some(checkpoint))
} else {
false
};
if !r {
break;
}
}
_ => {
if !parse_component(&mut *p) {
break;
}
}
}
}
while !p.test(SyntaxKind::Eof) {
p.consume()
}
false
}
#[cfg_attr(test, parser_test)]
pub fn parse_component(p: &mut impl Parser) -> bool {
let simple_component = p.nth(1).kind() == SyntaxKind::ColonEqual;
let is_global = !simple_component && p.peek().as_str() == "global";
let is_interface = !simple_component && p.peek().as_str() == "interface";
let is_new_component = !simple_component && p.peek().as_str() == "component";
if !is_global && !simple_component && !is_new_component && !is_interface {
p.error(
"Parse error: expected a top-level item such as a component, a struct, or a global",
);
return false;
}
let mut p = p.start_node(SyntaxKind::Component);
if is_global || is_new_component || is_interface {
p.consume();
}
if !p.start_node(SyntaxKind::DeclaredIdentifier).expect(SyntaxKind::Identifier) {
drop(p.start_node(SyntaxKind::Element));
return false;
}
if p.peek().as_str() == "uses" {
if !is_new_component {
p.error("Only components can have 'uses' clauses");
drop(p.start_node(SyntaxKind::Element));
return false;
}
if !parse_uses_specifier(&mut *p) {
drop(p.start_node(SyntaxKind::Element));
return false;
}
}
if p.peek().as_str() == "implements" {
if is_global {
p.error("Globals cannot implement an interface");
drop(p.start_node(SyntaxKind::Element));
return false;
} else if is_interface {
p.error("Interfaces cannot implement another interface");
drop(p.start_node(SyntaxKind::Element));
return false;
}
if !parse_implements_specifier(&mut *p) {
drop(p.start_node(SyntaxKind::Element));
return false;
}
}
if is_global {
if p.peek().kind() == SyntaxKind::ColonEqual {
p.warning("':=' to declare a global is deprecated. Remove the ':='");
p.consume();
}
} else if is_interface {
if p.peek().kind() == SyntaxKind::ColonEqual {
p.error("':=' to declare an interface is not supported. Remove the ':='");
p.consume();
}
} else if !is_new_component {
if p.peek().kind() == SyntaxKind::ColonEqual {
p.warning("':=' to declare a component is deprecated. The new syntax declare components with 'component MyComponent {'. Read the documentation for more info");
}
if !p.expect(SyntaxKind::ColonEqual) {
drop(p.start_node(SyntaxKind::Element));
return false;
}
} else if p.peek().as_str() == "inherits" {
p.consume();
} else if p.peek().kind() == SyntaxKind::LBrace {
let mut p = p.start_node(SyntaxKind::Element);
p.consume();
parse_element_content(&mut *p);
return p.expect(SyntaxKind::RBrace);
} else {
p.error("Expected '{', keyword 'implements' or keyword 'inherits'");
drop(p.start_node(SyntaxKind::Element));
return false;
}
if (is_global || is_interface) && p.peek().kind() == SyntaxKind::LBrace {
let mut p = p.start_node(SyntaxKind::Element);
p.consume();
parse_element_content(&mut *p);
return p.expect(SyntaxKind::RBrace);
}
parse_element(&mut *p)
}
#[cfg_attr(test, parser_test)]
pub fn parse_qualified_name(p: &mut impl Parser) -> bool {
let mut p = p.start_node(SyntaxKind::QualifiedName);
if !p.expect(SyntaxKind::Identifier) {
return false;
}
loop {
if p.nth(0).kind() != SyntaxKind::Dot {
break;
}
p.consume();
p.expect(SyntaxKind::Identifier);
}
true
}
#[cfg_attr(test, parser_test)]
fn parse_export<P: Parser>(p: &mut P, checkpoint: Option<P::Checkpoint>) -> bool {
debug_assert_eq!(p.peek().as_str(), "export");
let mut p = p.start_node_at(checkpoint.clone(), SyntaxKind::ExportsList);
p.expect(SyntaxKind::Identifier); if p.test(SyntaxKind::LBrace) {
loop {
if p.test(SyntaxKind::RBrace) {
break;
}
parse_export_specifier(&mut *p);
match p.nth(0).kind() {
SyntaxKind::RBrace => {
p.consume();
break;
}
SyntaxKind::Eof => {
p.error("Expected comma");
return false;
}
SyntaxKind::Comma => {
p.consume();
}
_ => {
p.consume();
p.error("Expected comma");
return false;
}
}
}
if p.peek().as_str() == "from" {
let mut p = p.start_node(SyntaxKind::ExportModule);
p.consume(); p.expect(SyntaxKind::StringLiteral);
p.expect(SyntaxKind::Semicolon);
}
true
} else if p.peek().as_str() == "struct" {
parse_struct_declaration(&mut *p, checkpoint)
} else if p.peek().as_str() == "enum" {
parse_enum_declaration(&mut *p, checkpoint)
} else if p.peek().kind == SyntaxKind::Star {
let mut p = p.start_node(SyntaxKind::ExportModule);
p.consume(); if p.peek().as_str() != "from" {
p.error("Expected from keyword for export statement");
return false;
}
p.consume();
let peek = p.peek();
if peek.kind != SyntaxKind::StringLiteral
|| !peek.as_str().starts_with('"')
|| !peek.as_str().ends_with('"')
{
p.error("Expected plain string literal");
return false;
}
p.consume();
p.expect(SyntaxKind::Semicolon)
} else {
parse_component(&mut *p)
}
}
#[cfg_attr(test, parser_test)]
fn parse_export_specifier(p: &mut impl Parser) -> bool {
let mut p = p.start_node(SyntaxKind::ExportSpecifier);
{
let mut p = p.start_node(SyntaxKind::ExportIdentifier);
if !p.expect(SyntaxKind::Identifier) {
return false;
}
}
if p.peek().as_str() == "as" {
p.consume();
let mut p = p.start_node(SyntaxKind::ExportName);
if !p.expect(SyntaxKind::Identifier) {
return false;
}
}
true
}
#[cfg_attr(test, parser_test)]
fn parse_import_specifier(p: &mut impl Parser) -> bool {
debug_assert_eq!(p.peek().as_str(), "import");
let mut p = p.start_node(SyntaxKind::ImportSpecifier);
p.expect(SyntaxKind::Identifier); if p.peek().kind != SyntaxKind::StringLiteral {
if !parse_import_identifier_list(&mut *p) {
return false;
}
if p.peek().as_str() != "from" {
p.error("Expected from keyword for import statement");
return false;
}
if !p.expect(SyntaxKind::Identifier) {
return false;
}
}
let peek = p.peek();
if peek.kind != SyntaxKind::StringLiteral
|| !peek.as_str().starts_with('"')
|| !peek.as_str().ends_with('"')
{
p.error("Expected plain string literal");
return false;
}
p.consume();
p.expect(SyntaxKind::Semicolon)
}
#[cfg_attr(test, parser_test)]
fn parse_import_identifier_list(p: &mut impl Parser) -> bool {
let mut p = p.start_node(SyntaxKind::ImportIdentifierList);
if !p.expect(SyntaxKind::LBrace) {
return false;
}
loop {
if p.test(SyntaxKind::RBrace) {
return true;
}
parse_import_identifier(&mut *p);
if !p.test(SyntaxKind::Comma) && p.nth(0).kind() != SyntaxKind::RBrace {
p.error("Expected comma or brace");
return false;
}
}
}
#[cfg_attr(test, parser_test)]
fn parse_import_identifier(p: &mut impl Parser) -> bool {
let mut p = p.start_node(SyntaxKind::ImportIdentifier);
{
let mut p = p.start_node(SyntaxKind::ExternalName);
if !p.expect(SyntaxKind::Identifier) {
return false;
}
}
if p.nth(0).kind() == SyntaxKind::Identifier && p.peek().as_str() == "as" {
p.consume();
let mut p = p.start_node(SyntaxKind::InternalName);
if !p.expect(SyntaxKind::Identifier) {
return false;
}
}
true
}
#[cfg_attr(test, parser_test)]
fn parse_uses_specifier(p: &mut impl Parser) -> bool {
debug_assert_eq!(p.peek().as_str(), "uses");
let mut p = p.start_node(SyntaxKind::UsesSpecifier);
p.expect(SyntaxKind::Identifier); if !p.expect(SyntaxKind::LBrace) {
return false;
}
loop {
if p.test(SyntaxKind::RBrace) {
return true;
}
if !parse_uses_identifier(&mut *p) {
return false;
}
if !p.test(SyntaxKind::Comma) && p.nth(0).kind() != SyntaxKind::RBrace {
p.error("Expected comma or brace");
return false;
}
}
}
#[cfg_attr(test, parser_test)]
fn parse_uses_identifier(p: &mut impl Parser) -> bool {
let mut p = p.start_node(SyntaxKind::UsesIdentifier);
if !parse_qualified_name(&mut *p) {
drop(p.start_node(SyntaxKind::DeclaredIdentifier));
return false;
}
if !(p.nth(0).kind() == SyntaxKind::Identifier && p.peek().as_str() == "from") {
p.error("Expected 'from' keyword in uses specifier");
drop(p.start_node(SyntaxKind::DeclaredIdentifier));
return false;
}
p.consume();
let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
p.expect(SyntaxKind::Identifier)
}
#[cfg_attr(test, parser_test)]
fn parse_implements_specifier(p: &mut impl Parser) -> bool {
debug_assert_eq!(p.peek().as_str(), "implements");
let mut p = p.start_node(SyntaxKind::ImplementsSpecifier);
p.expect(SyntaxKind::Identifier); parse_qualified_name(&mut *p)
}