Skip to content
This repository has been archived by the owner on Aug 31, 2023. It is now read-only.

Commit

Permalink
feat(rome_js_parser): CSS lexer #4682 (#4724)
Browse files Browse the repository at this point in the history
  • Loading branch information
denbezrukov authored Jul 26, 2023
1 parent ac606e2 commit a3a13d7
Show file tree
Hide file tree
Showing 25 changed files with 661 additions and 66 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 12 additions & 3 deletions crates/rome_css_factory/src/generated/node_factory.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 24 additions & 1 deletion crates/rome_css_factory/src/generated/syntax_factory.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/rome_css_factory/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::generated::CssSyntaxFactory;
pub use crate::generated::CssSyntaxFactory;
use rome_css_syntax::CssLanguage;
use rome_rowan::TreeBuilder;

Expand Down
1 change: 1 addition & 0 deletions crates/rome_css_parser/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ version = "0.0.1"
[dependencies]
rome_console = { workspace = true }
rome_css_syntax = { workspace = true }
rome_css_factory = { workspace = true }
rome_diagnostics = { workspace = true }
rome_js_unicode_table = { workspace = true }
rome_parser = { workspace = true }
Expand Down
11 changes: 9 additions & 2 deletions crates/rome_css_parser/src/lexer/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
//! An extremely fast, lookup table based, СSS lexer which yields SyntaxKind tokens used by the rome-css parser.
#![allow(dead_code)]

#[rustfmt::skip]
mod tests;

use crate::CssParserOptions;
use rome_css_syntax::{CssSyntaxKind, CssSyntaxKind::*, TextLen, TextRange, TextSize, T};
use rome_js_unicode_table::{is_id_continue, is_id_start, lookup_byte, Dispatch::*};
use rome_parser::diagnostic::ParseDiagnostic;
Expand Down Expand Up @@ -35,6 +34,8 @@ pub(crate) struct Lexer<'src> {
position: usize,

diagnostics: Vec<ParseDiagnostic>,

config: CssParserOptions,
}

impl<'src> Lexer<'src> {
Expand All @@ -44,9 +45,15 @@ impl<'src> Lexer<'src> {
source,
position: 0,
diagnostics: vec![],
config: CssParserOptions::default(),
}
}

pub(crate) fn with_config(mut self, config: CssParserOptions) -> Self {
self.config = config;
self
}

/// Returns the source code
pub fn source(&self) -> &'src str {
self.source
Expand Down
116 changes: 116 additions & 0 deletions crates/rome_css_parser/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,120 @@
//! Extremely fast, lossless, and error tolerant CSS Parser.
use crate::parser::CssParser;

use crate::syntax::parse_root;
pub use parser::CssParserOptions;
use rome_css_factory::CssSyntaxFactory;
use rome_css_syntax::{CssLanguage, CssRoot, CssSyntaxNode};
pub use rome_parser::prelude::*;
use rome_parser::tree_sink::LosslessTreeSink;
use rome_rowan::{AstNode, NodeCache};

mod lexer;
mod parser;
mod prelude;
mod syntax;
mod token_source;

pub(crate) type CssLosslessTreeSink<'source> =
LosslessTreeSink<'source, CssLanguage, CssSyntaxFactory>;

pub fn parse_css(source: &str, options: CssParserOptions) -> CssParse {
let mut cache = NodeCache::default();
parse_css_with_cache(source, &mut cache, options)
}

/// Parses the provided string as CSS program using the provided node cache.
pub fn parse_css_with_cache(
source: &str,
cache: &mut NodeCache,
config: CssParserOptions,
) -> CssParse {
tracing::debug_span!("parse").in_scope(move || {
let mut parser = CssParser::new(source, config);

parse_root(&mut parser);

let (events, diagnostics, trivia) = parser.finish();

let mut tree_sink = CssLosslessTreeSink::with_cache(source, &trivia, cache);
rome_parser::event::process(&mut tree_sink, events, diagnostics);
let (green, diagnostics) = tree_sink.finish();

CssParse::new(green, diagnostics)
})
}

/// A utility struct for managing the result of a parser job
#[derive(Debug)]
pub struct CssParse {
root: CssSyntaxNode,
diagnostics: Vec<ParseDiagnostic>,
}

impl CssParse {
pub fn new(root: CssSyntaxNode, diagnostics: Vec<ParseDiagnostic>) -> CssParse {
CssParse { root, diagnostics }
}

/// The syntax node represented by this Parse result
///
/// ```
/// # use rome_css_parser::parse_css;
/// # use rome_css_syntax::CssSyntaxKind;
/// # use rome_rowan::{AstNode, AstNodeList, SyntaxError};
///
/// # fn main() -> Result<(), SyntaxError> {
/// use rome_css_syntax::CssSyntaxKind;
/// use rome_css_parser::CssParserOptions;
/// let parse = parse_css(r#""#, CssParserOptions::default());
///
/// let root_value = parse.tree().rules();
///
/// assert_eq!(root_value.syntax().kind(), CssSyntaxKind::CSS_RULE_LIST);
///
/// # Ok(())
/// # }
/// ```
pub fn syntax(&self) -> CssSyntaxNode {
self.root.clone()
}

/// Get the diagnostics which occurred when parsing
pub fn diagnostics(&self) -> &[ParseDiagnostic] {
&self.diagnostics
}

/// Get the diagnostics which occurred when parsing
pub fn into_diagnostics(self) -> Vec<ParseDiagnostic> {
self.diagnostics
}

/// Returns [true] if the parser encountered some errors during the parsing.
pub fn has_errors(&self) -> bool {
self.diagnostics
.iter()
.any(|diagnostic| diagnostic.is_error())
}

/// Convert this parse result into a typed AST node.
///
/// # Panics
/// Panics if the node represented by this parse result mismatches.
pub fn tree(&self) -> CssRoot {
CssRoot::unwrap_cast(self.syntax())
}
}

#[cfg(test)]
mod tests {
use crate::{parse_css, CssParserOptions};

#[test]
fn parser_smoke_test() {
let src = r#"
"#;

let _css = parse_css(src, CssParserOptions::default());
}
}
63 changes: 63 additions & 0 deletions crates/rome_css_parser/src/parser.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use crate::token_source::CssTokenSource;
use rome_css_syntax::CssSyntaxKind;
use rome_parser::diagnostic::merge_diagnostics;
use rome_parser::event::Event;
use rome_parser::prelude::*;
use rome_parser::token_source::Trivia;
use rome_parser::ParserContext;

pub(crate) struct CssParser<'source> {
context: ParserContext<CssSyntaxKind>,
source: CssTokenSource<'source>,
}

#[derive(Default, Debug, Clone, Copy)]
pub struct CssParserOptions {
pub allow_single_line_comments: bool,
}

impl CssParserOptions {
pub fn with_allow_single_line_comments(mut self) -> Self {
self.allow_single_line_comments = true;
self
}
}

impl<'source> CssParser<'source> {
pub fn new(source: &'source str, config: CssParserOptions) -> Self {
Self {
context: ParserContext::default(),
source: CssTokenSource::from_str(source, config),
}
}

pub fn finish(self) -> (Vec<Event<CssSyntaxKind>>, Vec<ParseDiagnostic>, Vec<Trivia>) {
let (trivia, lexer_diagnostics) = self.source.finish();
let (events, parse_diagnostics) = self.context.finish();

let diagnostics = merge_diagnostics(lexer_diagnostics, parse_diagnostics);

(events, diagnostics, trivia)
}
}

impl<'source> Parser for CssParser<'source> {
type Kind = CssSyntaxKind;
type Source = CssTokenSource<'source>;

fn context(&self) -> &ParserContext<Self::Kind> {
&self.context
}

fn context_mut(&mut self) -> &mut ParserContext<Self::Kind> {
&mut self.context
}

fn source(&self) -> &Self::Source {
&self.source
}

fn source_mut(&mut self) -> &mut Self::Source {
&mut self.source
}
}
13 changes: 13 additions & 0 deletions crates/rome_css_parser/src/syntax.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use crate::parser::CssParser;
use rome_css_syntax::CssSyntaxKind::*;
use rome_parser::Parser;

pub(crate) fn parse_root(p: &mut CssParser) {
let m = p.start();

let rules = p.start();

rules.complete(p, CSS_RULE_LIST);

m.complete(p, CSS_ROOT);
}
Loading

0 comments on commit a3a13d7

Please sign in to comment.