use super::expressions::parse_expression;
use super::prelude::*;
use super::r#type::parse_type;
use super::statements::parse_statement;
#[cfg_attr(test, parser_test)]
pub fn parse_document(p: &mut impl Parser) -> bool {
let mut p = p.start_node(SyntaxKind::Document);
loop {
match p.peek().as_str() {
"export" => {
if !parse_export(&mut *p) {
return false;
}
}
"import" => {
if !parse_import_specifier(&mut *p) {
return false;
}
}
_ => {
if p.peek().as_str() == "component" && p.nth(1).kind() != SyntaxKind::ColonEqual {
p.expect(SyntaxKind::Identifier);
}
if !parse_component(&mut *p) {
return false;
}
}
}
if p.nth(0).kind() == SyntaxKind::Eof {
return true;
}
}
}
#[cfg_attr(test, parser_test)]
pub fn parse_component(p: &mut impl Parser) -> bool {
let mut p = p.start_node(SyntaxKind::Component);
if !(p.expect(SyntaxKind::Identifier) && p.expect(SyntaxKind::ColonEqual)) {
return false;
}
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);
}
parse_element(&mut *p)
}
#[cfg_attr(test, parser_test)]
pub fn parse_element(p: &mut impl Parser) -> bool {
let mut p = p.start_node(SyntaxKind::Element);
if !(parse_qualified_name(&mut *p) && p.expect(SyntaxKind::LBrace)) {
return false;
}
parse_element_content(&mut *p);
p.expect(SyntaxKind::RBrace)
}
#[cfg_attr(test, parser_test)]
fn parse_element_content(p: &mut impl Parser) {
let mut had_parse_error = false;
loop {
match p.nth(0).kind() {
SyntaxKind::RBrace => return,
SyntaxKind::Eof => return,
SyntaxKind::Identifier => match p.nth(1).kind() {
SyntaxKind::Colon => parse_property_binding(&mut *p),
SyntaxKind::ColonEqual | SyntaxKind::LBrace => parse_sub_element(&mut *p),
SyntaxKind::FatArrow | SyntaxKind::LParent if p.peek().as_str() != "if" => {
parse_signal_connection(&mut *p)
}
SyntaxKind::DoubleArrow => parse_two_way_binding(&mut *p),
SyntaxKind::Identifier if p.peek().as_str() == "for" => {
parse_repeated_element(&mut *p);
}
SyntaxKind::Identifier if p.peek().as_str() == "signal" => {
parse_signal_declaration(&mut *p);
}
SyntaxKind::Identifier | SyntaxKind::Star if p.peek().as_str() == "animate" => {
parse_property_animation(&mut *p);
}
SyntaxKind::LAngle if p.peek().as_str() == "property" => {
parse_property_declaration(&mut *p);
}
SyntaxKind::LParent if p.peek().as_str() == "if" => {
parse_if_element(&mut *p);
}
SyntaxKind::LBracket if p.peek().as_str() == "states" => {
parse_states(&mut *p);
}
SyntaxKind::LBracket if p.peek().as_str() == "transitions" => {
parse_transitions(&mut *p);
}
_ => {
p.consume();
if !had_parse_error {
p.error("Parse error");
had_parse_error = true;
}
}
},
SyntaxKind::Dollar => {
let checkpoint = p.checkpoint();
p.consume();
if p.peek().as_str() == "children" {
{
let _ =
p.start_node_at(checkpoint.clone(), SyntaxKind::ChildrenPlaceholder);
}
p.consume()
} else {
p.consume();
p.error("Unexpected identifier. Expected $children.")
}
}
_ => {
if !had_parse_error {
p.error("Parse error");
had_parse_error = true;
}
p.consume();
}
}
}
}
#[cfg_attr(test, parser_test)]
fn parse_sub_element(p: &mut impl Parser) {
let mut p = p.start_node(SyntaxKind::SubElement);
if p.nth(1).kind() == SyntaxKind::ColonEqual {
assert!(p.expect(SyntaxKind::Identifier));
p.expect(SyntaxKind::ColonEqual);
}
parse_element(&mut *p);
}
#[cfg_attr(test, parser_test)]
fn parse_repeated_element(p: &mut impl Parser) {
debug_assert_eq!(p.peek().as_str(), "for");
let mut p = p.start_node(SyntaxKind::RepeatedElement);
p.consume(); if p.nth(0).kind() == SyntaxKind::Identifier {
let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
p.expect(SyntaxKind::Identifier);
}
if p.nth(0).kind() == SyntaxKind::LBracket {
let mut p = p.start_node(SyntaxKind::RepeatedIndex);
p.expect(SyntaxKind::LBracket);
p.expect(SyntaxKind::Identifier);
p.expect(SyntaxKind::RBracket);
}
if p.peek().as_str() != "in" {
p.error("Invalid 'for' syntax: there should be a 'in' token");
return;
}
p.consume(); parse_expression(&mut *p);
p.expect(SyntaxKind::Colon);
parse_element(&mut *p);
}
#[cfg_attr(test, parser_test)]
fn parse_if_element(p: &mut impl Parser) {
debug_assert_eq!(p.peek().as_str(), "if");
let mut p = p.start_node(SyntaxKind::ConditionalElement);
p.consume(); if !p.expect(SyntaxKind::LParent) {
return;
}
parse_expression(&mut *p);
if !p.expect(SyntaxKind::RParent) || !p.expect(SyntaxKind::Colon) {
return;
}
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);
}
return true;
}
#[cfg_attr(test, parser_test)]
fn parse_property_binding(p: &mut impl Parser) {
let mut p = p.start_node(SyntaxKind::Binding);
p.consume();
p.expect(SyntaxKind::Colon);
parse_binding_expression(&mut *p);
}
#[cfg_attr(test, parser_test)]
fn parse_binding_expression(p: &mut impl Parser) {
let mut p = p.start_node(SyntaxKind::BindingExpression);
if p.nth(0).kind() == SyntaxKind::LBrace && p.nth(2).kind() != SyntaxKind::Colon {
parse_code_block(&mut *p);
p.test(SyntaxKind::Semicolon);
} else {
parse_expression(&mut *p);
p.expect(SyntaxKind::Semicolon);
}
}
#[cfg_attr(test, parser_test)]
pub fn parse_code_block(p: &mut impl Parser) {
let mut p = p.start_node(SyntaxKind::CodeBlock);
p.expect(SyntaxKind::LBrace);
while p.nth(0).kind() != SyntaxKind::RBrace {
if !parse_statement(&mut *p) {
break;
}
}
p.expect(SyntaxKind::RBrace);
}
#[cfg_attr(test, parser_test)]
fn parse_signal_connection(p: &mut impl Parser) {
let mut p = p.start_node(SyntaxKind::SignalConnection);
p.consume(); if p.test(SyntaxKind::LParent) {
while p.peek().kind() != SyntaxKind::RParent {
{
let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
p.expect(SyntaxKind::Identifier);
}
if !p.test(SyntaxKind::Comma) {
break;
}
}
p.expect(SyntaxKind::RParent);
}
p.expect(SyntaxKind::FatArrow);
parse_code_block(&mut *p);
}
#[cfg_attr(test, parser_test)]
fn parse_two_way_binding(p: &mut impl Parser) {
let mut p = p.start_node(SyntaxKind::TwoWayBinding);
p.consume(); p.expect(SyntaxKind::DoubleArrow);
parse_expression(&mut *p);
p.expect(SyntaxKind::Semicolon);
}
#[cfg_attr(test, parser_test)]
fn parse_signal_declaration(p: &mut impl Parser) {
debug_assert_eq!(p.peek().as_str(), "signal");
let mut p = p.start_node(SyntaxKind::SignalDeclaration);
p.consume(); {
let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
p.expect(SyntaxKind::Identifier);
}
if p.test(SyntaxKind::LParent) {
while p.peek().kind() != SyntaxKind::RParent {
parse_type(&mut *p);
if !p.test(SyntaxKind::Comma) {
break;
}
}
p.expect(SyntaxKind::RParent);
}
p.expect(SyntaxKind::Semicolon);
}
#[cfg_attr(test, parser_test)]
fn parse_property_declaration(p: &mut impl Parser) {
debug_assert_eq!(p.peek().as_str(), "property");
let mut p = p.start_node(SyntaxKind::PropertyDeclaration);
p.consume(); p.expect(SyntaxKind::LAngle);
parse_type(&mut *p);
p.expect(SyntaxKind::RAngle);
{
let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
p.expect(SyntaxKind::Identifier);
}
match p.nth(0).kind() {
SyntaxKind::Colon => {
p.consume();
parse_binding_expression(&mut *p);
}
SyntaxKind::DoubleArrow => {
let mut p = p.start_node(SyntaxKind::TwoWayBinding);
p.consume();
parse_expression(&mut *p);
p.expect(SyntaxKind::Semicolon);
}
_ => {
p.expect(SyntaxKind::Semicolon);
}
}
}
#[cfg_attr(test, parser_test)]
fn parse_property_animation(p: &mut impl Parser) {
debug_assert_eq!(p.peek().as_str(), "animate");
let mut p = p.start_node(SyntaxKind::PropertyAnimation);
p.consume(); if p.nth(0).kind() == SyntaxKind::Star {
p.consume();
} else {
parse_qualified_name(&mut *p);
while p.nth(0).kind() == SyntaxKind::Comma {
p.consume();
parse_qualified_name(&mut *p);
}
};
p.expect(SyntaxKind::LBrace);
loop {
match p.nth(0).kind() {
SyntaxKind::RBrace => {
p.consume();
return;
}
SyntaxKind::Eof => return,
SyntaxKind::Identifier => match p.nth(1).kind() {
SyntaxKind::Colon => parse_property_binding(&mut *p),
_ => {
p.consume();
p.error("Only bindings are allowed in animations");
}
},
_ => {
p.consume();
p.error("Only bindings are allowed in animations");
}
}
}
}
#[cfg_attr(test, parser_test)]
fn parse_states(p: &mut impl Parser) {
debug_assert_eq!(p.peek().as_str(), "states");
let mut p = p.start_node(SyntaxKind::States);
p.consume(); p.expect(SyntaxKind::LBracket);
while parse_state(&mut *p) {}
p.expect(SyntaxKind::RBracket);
}
#[cfg_attr(test, parser_test)]
fn parse_state(p: &mut impl Parser) -> bool {
if p.nth(0).kind() != SyntaxKind::Identifier {
return false;
}
let mut p = p.start_node(SyntaxKind::State);
{
let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
p.expect(SyntaxKind::Identifier);
}
if p.peek().as_str() == "when" {
p.consume();
parse_expression(&mut *p)
}
p.expect(SyntaxKind::Colon);
if !p.expect(SyntaxKind::LBrace) {
return false;
}
loop {
match p.nth(0).kind() {
SyntaxKind::RBrace => {
p.consume();
return true;
}
SyntaxKind::Eof => return false,
_ => {
let mut p = p.start_node(SyntaxKind::StatePropertyChange);
parse_qualified_name(&mut *p);
p.expect(SyntaxKind::Colon);
parse_binding_expression(&mut *p);
}
}
}
}
#[cfg_attr(test, parser_test)]
fn parse_transitions(p: &mut impl Parser) {
debug_assert_eq!(p.peek().as_str(), "transitions");
let mut p = p.start_node(SyntaxKind::Transitions);
p.consume(); p.expect(SyntaxKind::LBracket);
while p.nth(0).kind() != SyntaxKind::RBracket && parse_transition(&mut *p) {}
p.expect(SyntaxKind::RBracket);
}
#[cfg_attr(test, parser_test)]
fn parse_transition(p: &mut impl Parser) -> bool {
if !matches!(p.peek().as_str(), "to" | "out") {
p.error("Expected 'to' or 'out' to declare a transition");
return false;
}
let mut p = p.start_node(SyntaxKind::Transition);
p.consume(); {
let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
p.expect(SyntaxKind::Identifier);
}
p.expect(SyntaxKind::Colon);
if !p.expect(SyntaxKind::LBrace) {
return false;
}
loop {
match p.nth(0).kind() {
SyntaxKind::RBrace => {
p.consume();
return true;
}
SyntaxKind::Eof => return false,
SyntaxKind::Identifier if p.peek().as_str() == "animate" => {
parse_property_animation(&mut *p);
}
_ => {
p.consume();
p.error("Expected 'animate'");
}
}
}
}
#[cfg_attr(test, parser_test)]
fn parse_export(p: &mut impl Parser) -> bool {
debug_assert_eq!(p.peek().as_str(), "export");
let mut p = p.start_node(SyntaxKind::ExportsList);
p.consume(); if p.test(SyntaxKind::LBrace) {
loop {
parse_export_specifier(&mut *p);
match p.nth(0).kind() {
SyntaxKind::RBrace => {
p.consume();
return true;
}
SyntaxKind::Eof => return false,
SyntaxKind::Comma => {
p.consume();
}
_ => {
p.consume();
p.error("Expected comma")
}
}
}
} else {
return 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;
}
}
return 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.consume(); 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;
}
if !p.expect(SyntaxKind::StringLiteral) {
return false;
}
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;
}
if p.test(SyntaxKind::RBrace) {
return true;
}
loop {
parse_import_identifier(&mut *p);
match p.nth(0).kind() {
SyntaxKind::RBrace => {
p.consume();
return true;
}
SyntaxKind::Eof => return false,
SyntaxKind::Comma => {
p.consume();
}
_ => {
p.consume();
p.error("Expected comma")
}
}
}
}
#[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;
}
}
return true;
}