call it a night

This commit is contained in:
Michael Zhang 2020-02-21 03:25:27 -06:00
parent 96df945dbc
commit 0daf13f066
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
4 changed files with 70 additions and 35 deletions

View file

@ -160,6 +160,7 @@ pub enum Rsx {
Elem(Elem<Rsx>),
Code(Expr),
Text(String),
ForLoop(),
#[doc(hidden)]
_Nonexhaustive,
@ -184,15 +185,18 @@ impl ToTokens for Rsx {
fn to_tokens(&self, stream: &mut TokenStream) {
match self {
Rsx::Elem(elem) => {
stream.extend(quote! { #elem });
stream.extend(quote!(#elem));
}
Rsx::Code(expr) => {
let expr = syn::Expr::from_adapter(expr);
stream.extend(quote! { { #expr } });
stream.extend(quote!({ #expr }));
}
Rsx::Text(string) => {
let string = syn::Lit::Str(syn::LitStr::new(string.as_ref(), Span::call_site()));
stream.extend(quote! { #string });
stream.extend(quote!(#string));
}
Rsx::ForLoop() => {
stream.extend(quote!({ for } { / for }));
}
Rsx::_Nonexhaustive => unreachable!("should never be constructed"),
}

View file

@ -2,7 +2,7 @@ use std::collections::BTreeMap;
use std::iter::FromIterator;
use std::iter::Peekable;
use enterprise_compiler::model::{Component, Elem, ModelMap, Rsx};
use enterprise_compiler::model::{Component, Elem, ModelMap, Rsx, TagLhs, TagRhs};
use proc_macro2::{
token_stream::IntoIter, Delimiter, Group, Ident, Punct, Spacing, TokenStream, TokenTree,
};
@ -183,6 +183,11 @@ impl Visitor {
}
fn consume_view(&mut self) -> Result<Vec<Rsx>, ParseError> {
enum Container {
Tag(String, BTreeMap<TagLhs, TagRhs>),
ForLoop(),
}
let mut rsx_parser = RsxParser::new(self.0.clone());
let mut result = Vec::new();
let mut tag_stack = Vec::new();
@ -199,11 +204,13 @@ impl Visitor {
result.push(el);
}
RsxToken::OpeningTag(tag, attrs) => {
tag_stack.push((tag, attrs, result.clone()));
tag_stack.push((Container::Tag(tag.to_string(), attrs), result.clone()));
result.clear();
}
RsxToken::ClosingTag(tag) => {
if let Some((last_tag, attrs, mut last_result)) = tag_stack.pop() {
if let Some((Container::Tag(last_tag, attrs), mut last_result)) =
tag_stack.pop()
{
if tag.as_str() == last_tag.as_str() {
last_result.push(Rsx::Elem(Elem {
tag: tag.to_string(),
@ -227,6 +234,17 @@ impl Visitor {
RsxToken::Str(string) => {
result.push(Rsx::Text(string));
}
RsxToken::OpeningFor() => {
tag_stack.push((Container::ForLoop(), result.clone()));
}
RsxToken::ClosingFor => {
if let Some((Container::ForLoop(), mut last_result)) = tag_stack.pop() {
last_result.push(Rsx::ForLoop());
result = last_result;
} else {
return Err(ParseError::ClosedTooFar);
}
}
unknown => unimplemented!("rsx token: {:?}", unknown),
}
}

View file

@ -5,7 +5,10 @@ use std::iter::Peekable;
use enterprise_compiler::model::{TagLhs, TagRhs};
use proc_macro2::{token_stream::IntoIter, Delimiter, Ident, TokenStream, TokenTree};
use symbol::Symbol;
use syn::{Expr, Lit};
use syn::{
parse::{Parse, ParseStream},
Expr, Lit, Pat, Result as SynResult, Token,
};
use syn_serde::Syn;
use crate::parser::{consume_ident, consume_punct, ParseError};
@ -148,34 +151,43 @@ impl RsxParser {
}
}
TokenTree::Group(group) if group.delimiter() == Delimiter::Brace => {
let mut stream = group.stream().into_iter();
enum GroupType {
Pound,
Slash,
None,
let expr = syn::parse2::<Expr>(group.stream())?;
Ok(Some(RsxToken::Code(expr)))
}
TokenTree::Group(group) if group.delimiter() == Delimiter::Bracket => {
// for loop syntax
#[derive(Debug)]
struct ForLoopHeader {
kw_for: Token![for],
pat: Pat,
kw_in: Token![in],
iter: Expr,
}
// based on svelte for now, we'll probably change up the syntax a bit later
let is_special = match stream.next() {
Some(TokenTree::Punct(punct)) if punct.as_char() == '#' => GroupType::Pound,
Some(TokenTree::Punct(punct)) if punct.as_char() == '/' => GroupType::Slash,
_ => GroupType::None,
};
if let GroupType::None = is_special {
let expr = syn::parse2::<Expr>(group.stream())?;
Ok(Some(RsxToken::Code(expr)))
} else {
match stream.next() {
Some(TokenTree::Ident(ident)) if ident == "for" => {
// syntax: {#for pattern in expr} {/for}
Ok(Some(RsxToken::StartFor()))
}
Some(other) => Err(ParseError::ExpectedIdent(other)),
None => Err(ParseError::UnexpectedEOF),
impl Parse for ForLoopHeader {
fn parse(input: ParseStream) -> SynResult<Self> {
Ok(ForLoopHeader {
kw_for: input.parse()?,
pat: input.parse()?,
kw_in: input.parse()?,
iter: input.parse()?,
})
}
}
let mut stream = group.stream().into_iter().peekable();
match stream.peek() {
Some(TokenTree::Ident(ident)) if &ident.to_string() == "for" => {
let for_loop = syn::parse2::<ForLoopHeader>(group.stream())?;
Ok(Some(RsxToken::OpeningFor()))
}
Some(TokenTree::Punct(punct)) if punct.as_char() == '/' => {
stream.next();
Ok(Some(RsxToken::ClosingFor))
}
Some(token) => Err(ParseError::UnexpectedToken(token.clone())),
None => Err(ParseError::UnexpectedEOF),
}
}
token => Err(ParseError::UnexpectedToken(token)),
}
@ -203,7 +215,8 @@ pub(crate) enum RsxToken {
ClosingTag(Symbol),
Str(String),
Code(Expr),
StartFor(),
OpeningFor(),
ClosingFor,
}
impl Iterator for RsxParser {

View file

@ -10,9 +10,9 @@ component! {
view {
<input on:submit={|evt| { todos.push(evt.name); }} />
<ul>
{#for (key, todo) in todos }
<li>{todo} <a on:click={|_| { todos.remove(key); }}>[x]</a></li>
{/for}
[for (key, todo) in todos]
<li>{todo} <a on:click={|_| { todos.remove(key); }}>"[x]"</a></li>
[/for]
</ul>
}
}