Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement greedy name parsing #2

Open
wants to merge 2 commits into
base: syntax-crate
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions vhdl_syntax/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub struct Parser<T: TokenStream> {
builder: builder::NodeBuilder,
diagnostics: Vec<diagnostics::ParserDiagnostic>,
unexpected_eof: bool,
token_index: usize,
}

impl<T: TokenStream> Parser<T> {
Expand All @@ -37,6 +38,7 @@ impl<T: TokenStream> Parser<T> {
builder: builder::NodeBuilder::new(),
diagnostics: Vec::default(),
unexpected_eof: false,
token_index: 0,
}
}

Expand Down
138 changes: 120 additions & 18 deletions vhdl_syntax/src/parser/productions/design.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,34 +41,64 @@ impl<T: TokenStream> Parser<T> {
self.end_node();
}

pub fn context_clause(&mut self) {}
pub fn context_clause(&mut self) {
Schottkyc137 marked this conversation as resolved.
Show resolved Hide resolved
self.start_node(NodeKind::ContextClause);
loop {
match self.tokenizer.peek_next() {
Some(tok) => match tok.kind() {
Keyword(Kw::Use) => self.use_clause(),
Keyword(Kw::Library) => self.library_clause(),
Keyword(Kw::Context) => self.context_reference(),
_ => break,
},
_ => self.eof_err(),
}
}
self.end_node();
}

pub fn library_clause(&mut self) {
self.start_node(NodeKind::LibraryClause);
self.expect_kw(Kw::Library);
self.identifier_list();
self.expect_token(SemiColon);
self.end_node();
}

pub fn use_clause(&mut self) {
self.start_node(NodeKind::UseClause);
self.expect_kw(Kw::Use);
self.name_list();
self.expect_token(SemiColon);
self.end_node();
}

pub fn context_reference(&mut self) {
todo!();
}
}

#[cfg(test)]
mod tests {
use crate::parser::{CanParse, Parser};
use crate::tokens;
use crate::tokens::IntoTokenStream;
use pretty_assertions::assert_eq;
use crate::parser::{test_utils::check, Parser};

#[test]
fn parse_simple_entity() {
let (entity, _) = tokens! {
entity my_ent is
begin
end my_ent;
check(
Parser::design_file,
"\
entity my_ent is
begin
end my_ent;

entity my_ent2 is
begin
end entity;
}
.into_token_stream()
.parse_syntax(Parser::design_file);
assert_eq!(
entity.test_text(),
entity my_ent2 is
begin
end entity;
",
"\
DesignFile
DesignUnit
ContextClause
EntityDeclaration
Keyword(Entity)
Identifier 'my_ent'
Expand All @@ -79,6 +109,7 @@ DesignFile
Identifier 'my_ent'
SemiColon
DesignUnit
ContextClause
EntityDeclaration
Keyword(Entity)
Identifier 'my_ent2'
Expand All @@ -88,7 +119,78 @@ DesignFile
Keyword(End)
Keyword(Entity)
SemiColon
"
",
);
}

#[test]
fn parse_entity_with_context_clause() {
check(
Parser::design_file,
"\
library ieee;
use ieee.std_logic_1164.all;

entity my_ent is
begin
end my_ent;
",
"\
DesignFile
DesignUnit
ContextClause
LibraryClause
Keyword(Library)
IdentifierList
Identifier 'ieee'
SemiColon
UseClause
Keyword(Use)
NameList
Name
Identifier 'ieee'
SelectedName
Dot
Identifier 'std_logic_1164'
SelectedName
Dot
Keyword(All)
SemiColon
EntityDeclaration
Keyword(Entity)
Identifier 'my_ent'
Keyword(Is)
EntityHeader
Keyword(Begin)
Keyword(End)
Identifier 'my_ent'
SemiColon
",
);
}

#[test]
fn parse_use_clause() {
check(
Parser::use_clause,
"use lib1.lib2.lib3.all;",
"\
UseClause
Keyword(Use)
NameList
Name
Identifier 'lib1'
SelectedName
Dot
Identifier 'lib2'
SelectedName
Dot
Identifier 'lib3'
SelectedName
Dot
Keyword(All)
SemiColon
",
);
}
}
20 changes: 19 additions & 1 deletion vhdl_syntax/src/parser/productions/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,29 @@
// Copyright (c) 2025, Lukas Scheller [email protected]

use crate::parser::Parser;
use crate::syntax::node_kind::NodeKind::*;
use crate::tokens::TokenKind::*;
use crate::tokens::TokenStream;

impl<T: TokenStream> Parser<T> {
pub fn expression(&mut self) {
// TODO
self.start_node(Expression);
// TODO: Expecting a simple expression is just a placeholder
self.simple_expression();
self.end_node();
}

pub fn simple_expression(&mut self) {
self.start_node(SimpleExpression);
// TODO: Expecting these literals is just a placeholder
self.expect_one_of_tokens([CharacterLiteral, StringLiteral, Identifier, AbstractLiteral]);
self.end_node();
}

pub fn expression_list(&mut self) {
self.start_node(ExpressionList);
self.separated_list(Parser::expression, Comma);
self.end_node();
}

pub fn condition(&mut self) {
Expand Down
97 changes: 97 additions & 0 deletions vhdl_syntax/src/parser/productions/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,110 @@ impl<T: TokenStream> Parser<T> {
Keyword(Kw::Linkage),
]);
}

pub fn association_list(&mut self) {
self.association_list_bounded(usize::MAX);
}
fn association_list_bounded(&mut self, max_index: usize) {
self.start_node(AssociationList);
self.separated_list(
|parser| {
let end_of_element_idx =
match parser.lookahead_max_token_index(max_index, [Comma, RightPar]) {
Ok((_, idx)) => idx,
Err(idx) => idx,
};
parser.association_element_bounded(end_of_element_idx);
},
Comma,
);
self.end_node();
}

fn association_element_bounded(&mut self, max_index: usize) {
self.start_node(AssociationElement);

// TODO: Error handling is done at a bare minimum.
if let Ok(_) = self.lookahead_max_token_index(max_index, [RightArrow]) {
self.formal_part();
self.expect_token(RightArrow);
}
self.actual_part_bounded(max_index);

self.end_node();
}

pub fn formal_part(&mut self) {
self.start_node(FormalPart);
self.name();
// Note: `self.name()` will already consume any trailing parenthesized names!
self.end_node();
}

fn actual_part_bounded(&mut self, max_index: usize) {
self.start_node(ActualPart);
// Parsing of `actual_part` would boil down to `name | expression | subtype_indication`
self.start_node(RawTokens);
self.skip_to(max_index);
self.end_node();
self.end_node();
}
}

#[cfg(test)]
mod tests {
use crate::parser::test_utils::check;
use crate::parser::Parser;

#[test]
fn association_list() {
check(
Parser::association_list,
"arg1, arg2",
"\
AssociationList
AssociationElement
ActualPart
RawTokens
Identifier 'arg1'
Comma
AssociationElement
ActualPart
RawTokens
Identifier 'arg2'
",
);

check(
Parser::association_list,
"p1 => 1, std_ulogic(p2)=> sl_sig",
"\
AssociationList
AssociationElement
FormalPart
Name
Identifier 'p1'
RightArrow
ActualPart
RawTokens
AbstractLiteral
Comma
AssociationElement
FormalPart
Name
Identifier 'std_ulogic'
RawTokens
LeftPar
Identifier 'p2'
RightPar
RightArrow
ActualPart
RawTokens
Identifier 'sl_sig'
",
);
}

#[test]
fn empty_generic_clause() {
check(
Expand Down
1 change: 1 addition & 0 deletions vhdl_syntax/src/parser/productions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ mod names;
mod signature;
mod statements;
mod subtype;
mod scalar_types;
Loading