Skip to content

Commit

Permalink
feat: add WXML AST helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
LastLeaf committed Feb 19, 2025
1 parent 4b4da3d commit ace5ccd
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 81 deletions.
187 changes: 120 additions & 67 deletions glass-easel-template-compiler/src/parse/iter.rs
Original file line number Diff line number Diff line change
@@ -1,83 +1,136 @@
use super::tag::{Element, ElementKind, Node};

pub struct ChildrenIter<'a> {
parent: &'a Element,
cur: usize,
cur_branch: usize,
}

impl<'a> ChildrenIter<'a> {
pub(super) fn new(parent: &'a Element) -> Self {
Self {
parent,
cur: 0,
cur_branch: 0,
macro_rules! iter {
($t:ident, $($mut_t:tt)*) => {
pub struct $t<'a> {
parent: &'a $($mut_t)* Element,
cur: usize,
cur_branch: usize,
}
}
}

impl<'a> Iterator for ChildrenIter<'a> {
type Item = &'a Node;

fn next(&mut self) -> Option<Self::Item> {
match &self.parent.kind {
ElementKind::Normal { children, .. }
| ElementKind::Pure { children, .. }
| ElementKind::For { children, .. } => {
let ret = children.get(self.cur);
if ret.is_some() {
self.cur += 1;

impl<'a> $t<'a> {
pub(super) fn new(parent: &'a $($mut_t)* Element) -> Self {
Self {
parent,
cur: 0,
cur_branch: 0,
}
ret
}
ElementKind::If {
branches,
else_branch,
} => {
let ret = loop {
if self.cur_branch >= branches.len() {
if let Some((_, children)) = else_branch {
let ret = children.get(self.cur);
if ret.is_some() {
}

impl<'a> Iterator for $t<'a> {
type Item = &'a $($mut_t)* Node;

fn next(&mut self) -> Option<Self::Item> {
match & $($mut_t)* self.parent.kind {
ElementKind::Normal { children, .. }
| ElementKind::Pure { children, .. }
| ElementKind::For { children, .. } => {
let ret = children.get(self.cur).map(|x| x as *const _);
if let Some(ret) = ret {
self.cur += 1;
// it is safe because the iterator never returns an item twice
Some(unsafe { & $($mut_t)* *(ret as *mut _) })
} else {
None
}
}
ElementKind::If {
branches,
else_branch,
} => {
let ret = loop {
if self.cur_branch >= branches.len() {
if let Some((_, children)) = else_branch {
let ret = children.get(self.cur).map(|x| x as *const _);
if ret.is_some() {
self.cur += 1;
}
break ret;
}
break None;
}
let (_, _, children) = unsafe { branches.get_unchecked(self.cur_branch) };
let ret = children.get(self.cur).map(|x| x as *const _);
if let Some(ret) = ret {
self.cur += 1;
break Some(ret);
}
break ret;
}
break None;
self.cur_branch += 1;
self.cur = 0;
};
// it is safe because the iterator never returns an item twice
ret.map(|ret| unsafe { & $($mut_t)* *(ret as *mut _) })
}
let (_, _, children) = unsafe { branches.get_unchecked(self.cur_branch) };
let ret = children.get(self.cur);
if ret.is_some() {
self.cur += 1;
break ret;
ElementKind::TemplateRef { .. }
| ElementKind::Include { .. }
| ElementKind::Slot { .. } => None,
}
}

fn size_hint(&self) -> (usize, Option<usize>) {
let count = match &self.parent.kind {
ElementKind::Normal { children, .. }
| ElementKind::Pure { children, .. }
| ElementKind::For { children, .. } => children.len(),
ElementKind::If {
branches,
else_branch,
} => {
branches.iter().map(|x| x.2.len()).sum::<usize>()
+ else_branch.iter().map(|x| x.1.len()).sum::<usize>()
}
self.cur_branch += 1;
self.cur = 0;
ElementKind::TemplateRef { .. }
| ElementKind::Include { .. }
| ElementKind::Slot { .. } => 0,
};
ret
(count, Some(count))
}
ElementKind::TemplateRef { .. }
| ElementKind::Include { .. }
| ElementKind::Slot { .. } => None,
}
}
};
}

iter!(ChildrenIter,);
iter!(ChildrenIterMut, mut);

#[cfg(test)]
mod test {
use crate::parse::tag::{Node, Value};

fn size_hint(&self) -> (usize, Option<usize>) {
let count = match &self.parent.kind {
ElementKind::Normal { children, .. }
| ElementKind::Pure { children, .. }
| ElementKind::For { children, .. } => children.len(),
ElementKind::If {
branches,
else_branch,
} => {
branches.iter().map(|x| x.2.len()).sum::<usize>()
+ else_branch.iter().map(|x| x.1.len()).sum::<usize>()
#[test]
fn iter_children() {
const SRC: &'static str = r#"
<div>
<div>A</div>
<block wx:for="123">B</block>
<block wx:if="1">C</block>
<block wx:elif="2">D</block>
<block wx:elif="3">E</block>
<block>F</block>
</div>
"#;
let (mut template, _) = crate::parse::parse("TEST", SRC);
fn rec(node: &mut Node, visited: &mut String) {
if let Node::Text(v) = node {
let Value::Static { value, .. } = v else { unreachable!() };
visited.push_str(&value);
*value = "".into();
}
ElementKind::TemplateRef { .. }
| ElementKind::Include { .. }
| ElementKind::Slot { .. } => 0,
};
(count, Some(count))
if let Node::Element(elem) = node {
for child in elem.iter_children_mut() {
rec(child, visited);
}
}
}
let mut visited = String::new();
for node in &mut template.content {
rec(node, &mut visited);
}
assert_eq!(visited, "ABCDEF");
let mut visited = String::new();
for node in &mut template.content {
rec(node, &mut visited);
}
assert_eq!(visited, "");
}
}
26 changes: 13 additions & 13 deletions glass-easel-template-compiler/src/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ impl<'s> ParseState<'s> {
}

/// Try parse with `f` , reverting the state if it returns `None` .
pub fn try_parse<T>(&mut self, f: impl FnOnce(&mut Self) -> Option<T>) -> Option<T> {
pub(crate) fn try_parse<T>(&mut self, f: impl FnOnce(&mut Self) -> Option<T>) -> Option<T> {
let prev = self.cur_index;
let prev_line = self.line;
let prev_utf16_col = self.utf16_col;
Expand Down Expand Up @@ -184,7 +184,7 @@ impl<'s> ParseState<'s> {
}
}

pub fn skip_until_before(&mut self, until: &str) -> Option<&'s str> {
pub(crate) fn skip_until_before(&mut self, until: &str) -> Option<&'s str> {
let s = self.cur_str();
if let Some(index) = s.find(until) {
let ret = &s[..index];
Expand All @@ -196,22 +196,22 @@ impl<'s> ParseState<'s> {
}
}

pub fn skip_until_after(&mut self, until: &str) -> Option<&'s str> {
pub(crate) fn skip_until_after(&mut self, until: &str) -> Option<&'s str> {
let ret = self.skip_until_before(until);
if ret.is_some() {
self.skip_bytes(until.len());
}
ret
}

pub fn peek_chars(&mut self) -> impl 's + Iterator<Item = char> {
pub(crate) fn peek_chars(&mut self) -> impl 's + Iterator<Item = char> {
if let Some(f) = self.auto_skip_whitespace.as_ref() {
f(self);
}
self.cur_str().chars()
}

pub fn peek_n<const N: usize>(&mut self) -> Option<[char; N]> {
pub(crate) fn peek_n<const N: usize>(&mut self) -> Option<[char; N]> {
let mut ret: [char; N] = ['\x00'; N];
let mut iter = self.peek_chars();
for i in 0..N {
Expand All @@ -220,15 +220,15 @@ impl<'s> ParseState<'s> {
Some(ret)
}

pub fn peek<const I: usize>(&mut self) -> Option<char> {
pub(crate) fn peek<const I: usize>(&mut self) -> Option<char> {
let mut iter = self.peek_chars();
for _ in 0..I {
iter.next()?;
}
iter.next()
}

pub fn peek_str(&mut self, s: &str) -> bool {
pub(crate) fn peek_str(&mut self, s: &str) -> bool {
if let Some(f) = self.auto_skip_whitespace.as_ref() {
f(self);
}
Expand Down Expand Up @@ -279,7 +279,7 @@ impl<'s> ParseState<'s> {
}

/// Consume the specified string if it matches the peek of the input.
pub fn consume_str(&mut self, s: &str) -> Option<Range<Position>> {
pub(crate) fn consume_str(&mut self, s: &str) -> Option<Range<Position>> {
self.consume_str_except_followed(s, [])
}

Expand All @@ -301,7 +301,7 @@ impl<'s> ParseState<'s> {
}
}

pub fn next(&mut self) -> Option<char> {
pub(crate) fn next(&mut self) -> Option<char> {
if let Some(f) = self.auto_skip_whitespace.as_ref() {
f(self);
}
Expand All @@ -320,7 +320,7 @@ impl<'s> ParseState<'s> {
Some(ret)
}

pub fn skip_whitespace(&mut self) -> Option<Range<Position>> {
pub(crate) fn skip_whitespace(&mut self) -> Option<Range<Position>> {
let mut start_pos = None;
let s = self.cur_str();
let mut i = s.char_indices();
Expand Down Expand Up @@ -370,17 +370,17 @@ impl<'s> ParseState<'s> {
///
/// Panics if the start or the end is not at a character boundary.
///
pub fn code_slice(&self, range: Range<usize>) -> &'s str {
pub(crate) fn code_slice(&self, range: Range<usize>) -> &'s str {
&self.whole_str[range]
}

/// Get the current UTF-8 byte index in the input.
pub fn cur_index(&self) -> usize {
pub(crate) fn cur_index(&self) -> usize {
self.cur_index as usize
}

/// Get the current position.
pub fn position(&self) -> Position {
pub(crate) fn position(&self) -> Position {
Position {
line: self.line,
utf16_col: self.utf16_col,
Expand Down
19 changes: 18 additions & 1 deletion glass-easel-template-compiler/src/parse/tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,12 @@ pub struct Comment {
pub location: Range<Position>,
}

impl Comment {
pub fn new(content: &str, location: Range<Position>) -> Self {
Self { content: content.to_string(), location }
}
}

#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct UnknownMetaTag {
Expand Down Expand Up @@ -492,6 +498,10 @@ impl Element {
super::iter::ChildrenIter::new(self)
}

pub fn iter_children_mut(&mut self) -> super::iter::ChildrenIterMut {
super::iter::ChildrenIterMut::new(self)
}

pub fn slot_value_refs(&self) -> Option<impl Iterator<Item = &StaticAttribute>> {
match &self.kind {
ElementKind::Normal { common, .. } | ElementKind::Slot { common, .. } => {
Expand Down Expand Up @@ -3109,7 +3119,7 @@ impl TemplateStructure for Value {
}

impl Value {
fn new_empty(pos: Position) -> Self {
pub fn new_empty(pos: Position) -> Self {
Self::Static {
value: CompactString::new_inline(""),
location: pos..pos,
Expand All @@ -3124,6 +3134,13 @@ impl Value {
}
}

pub fn new_expression(
expression: Box<Expression>,
double_brace_location: (Range<Position>, Range<Position>),
) -> Self {
Self::Dynamic { expression, double_brace_location, binding_map_keys: None }
}

fn parse_data_binding(ps: &mut ParseState, is_template_data: bool) -> Option<Self> {
let Some(double_brace_left) = ps.consume_str("{{") else {
return None;
Expand Down

0 comments on commit ace5ccd

Please sign in to comment.