#![doc = include_str!("readme.md")]
pub mod element_type;
use crate::{
language::LessLanguage,
lexer::{LessLexer, LessTokenType},
};
pub use element_type::LessElementType;
use oak_core::{
GreenNode, OakError, TextEdit,
parser::{ParseCache, ParseOutput, Parser, ParserState, parse_with_lexer},
source::Source,
};
pub(crate) type State<'a, S> = ParserState<'a, LessLanguage, S>;
pub struct LessParser<'config> {
pub(crate) config: &'config LessLanguage,
}
impl<'config> LessParser<'config> {
pub fn new(config: &'config LessLanguage) -> Self {
Self { config }
}
}
impl<'config> Parser<LessLanguage> for LessParser<'config> {
fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<LessLanguage>) -> ParseOutput<'a, LessLanguage> {
let lexer = LessLexer::new(self.config);
parse_with_lexer(&lexer, text, edits, cache, |state| {
let cp = state.checkpoint();
while state.not_at_end() {
if state.at(LessTokenType::AtRule) || state.at(LessTokenType::AtImport) || state.at(LessTokenType::AtMedia) { self.parse_at_rule(state)? } else { self.parse_ruleset(state)? }
}
Ok(state.finish_at(cp, LessElementType::SourceFile))
})
}
}
impl<'config> LessParser<'config> {
fn parse_at_rule<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
let cp = state.checkpoint();
state.bump();
while state.not_at_end() && !state.at(LessTokenType::Semicolon) && !state.at(LessTokenType::LeftBrace) {
state.bump()
}
if state.at(LessTokenType::LeftBrace) {
state.expect(LessTokenType::LeftBrace).ok();
while state.not_at_end() && !state.at(LessTokenType::RightBrace) {
self.parse_ruleset(state)?
}
state.expect(LessTokenType::RightBrace).ok();
}
else if state.at(LessTokenType::Semicolon) {
state.expect(LessTokenType::Semicolon).ok();
}
state.finish_at(cp, LessElementType::AtRule);
Ok(())
}
fn parse_ruleset<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
let cp = state.checkpoint();
self.parse_selectors(state)?;
let cp_block = state.checkpoint();
state.expect(LessTokenType::LeftBrace).ok();
while state.not_at_end() && !state.at(LessTokenType::RightBrace) {
self.parse_declaration(state)?;
if state.at(LessTokenType::Semicolon) {
state.expect(LessTokenType::Semicolon).ok();
}
else if !state.at(LessTokenType::RightBrace) {
break;
}
}
state.expect(LessTokenType::RightBrace).ok();
state.finish_at(cp_block, LessElementType::DeclarationBlock);
state.finish_at(cp, LessElementType::RuleSet);
Ok(())
}
fn parse_selectors<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
let cp = state.checkpoint();
while state.not_at_end() && !state.at(LessTokenType::LeftBrace) {
state.bump()
}
state.finish_at(cp, LessElementType::SelectorList);
Ok(())
}
fn parse_declaration<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
let cp = state.checkpoint();
let cp_prop = state.checkpoint();
while state.not_at_end() && !state.at(LessTokenType::Colon) && !state.at(LessTokenType::Semicolon) && !state.at(LessTokenType::RightBrace) {
state.bump()
}
state.finish_at(cp_prop, LessElementType::Property);
if state.at(LessTokenType::Colon) {
state.expect(LessTokenType::Colon).ok();
let cp_val = state.checkpoint();
while state.not_at_end() && !state.at(LessTokenType::Semicolon) && !state.at(LessTokenType::RightBrace) {
state.bump()
}
state.finish_at(cp_val, LessElementType::Value);
}
state.finish_at(cp, LessElementType::Declaration);
Ok(())
}
}