fix code according to proptest
This commit is contained in:
parent
f9c478276d
commit
96df945dbc
9 changed files with 158 additions and 52 deletions
|
@ -1,7 +1,6 @@
|
||||||
mod props;
|
mod props;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::BTreeMap;
|
||||||
use std::hash::{BuildHasher, Hash};
|
|
||||||
use std::iter::{self, FromIterator};
|
use std::iter::{self, FromIterator};
|
||||||
|
|
||||||
use proc_macro2::{Span, TokenStream, TokenTree};
|
use proc_macro2::{Span, TokenStream, TokenTree};
|
||||||
|
@ -13,11 +12,9 @@ pub use self::props::*;
|
||||||
|
|
||||||
pub type Id = Symbol;
|
pub type Id = Symbol;
|
||||||
|
|
||||||
pub type ModelMap = HashMap<Symbol, (Type, Expr)>;
|
pub type ModelMap = BTreeMap<Symbol, (Type, Expr)>;
|
||||||
|
|
||||||
pub fn convert_map<T: Hash + Eq, S: BuildHasher>(
|
pub fn convert_map<T: Ord>(map: BTreeMap<T, (syn::Type, syn::Expr)>) -> BTreeMap<T, (Type, Expr)> {
|
||||||
map: HashMap<T, (syn::Type, syn::Expr), S>,
|
|
||||||
) -> HashMap<T, (Type, Expr)> {
|
|
||||||
map.into_iter()
|
map.into_iter()
|
||||||
.map(|(left, (ty, expr))| {
|
.map(|(left, (ty, expr))| {
|
||||||
let ty = ty.to_adapter();
|
let ty = ty.to_adapter();
|
||||||
|
@ -37,6 +34,7 @@ pub struct Component {
|
||||||
|
|
||||||
impl ToTokens for Component {
|
impl ToTokens for Component {
|
||||||
fn to_tokens(&self, stream: &mut TokenStream) {
|
fn to_tokens(&self, stream: &mut TokenStream) {
|
||||||
|
let name = format_ident!("{}", self.name);
|
||||||
let model = TokenStream::from_iter(self.model.iter().map(|(name, (ty, init))| {
|
let model = TokenStream::from_iter(self.model.iter().map(|(name, (ty, init))| {
|
||||||
let name = format_ident!("{}", name.as_str());
|
let name = format_ident!("{}", name.as_str());
|
||||||
let ty = syn::Type::from_adapter(ty);
|
let ty = syn::Type::from_adapter(ty);
|
||||||
|
@ -45,7 +43,7 @@ impl ToTokens for Component {
|
||||||
}));
|
}));
|
||||||
let view = TokenStream::from_iter(self.view.iter().map(|rsx| rsx.to_token_stream()));
|
let view = TokenStream::from_iter(self.view.iter().map(|rsx| rsx.to_token_stream()));
|
||||||
stream.extend(quote! {
|
stream.extend(quote! {
|
||||||
component {
|
component #name {
|
||||||
model { #model }
|
model { #model }
|
||||||
view { #view }
|
view { #view }
|
||||||
}
|
}
|
||||||
|
@ -53,7 +51,7 @@ impl ToTokens for Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialOrd, Ord, PartialEq, Eq)]
|
||||||
pub enum TagLhs {
|
pub enum TagLhs {
|
||||||
Bind(String),
|
Bind(String),
|
||||||
Plain(String),
|
Plain(String),
|
||||||
|
@ -89,6 +87,20 @@ pub enum TagRhs {
|
||||||
Text(String),
|
Text(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq<TagRhs> for TagRhs {
|
||||||
|
fn eq(&self, other: &TagRhs) -> bool {
|
||||||
|
match (self, other) {
|
||||||
|
(TagRhs::Code(expr), TagRhs::Code(other)) => {
|
||||||
|
syn::Expr::from_adapter(expr) == syn::Expr::from_adapter(other)
|
||||||
|
}
|
||||||
|
(TagRhs::Text(string), TagRhs::Text(other)) => string == other,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for TagRhs {}
|
||||||
|
|
||||||
impl Clone for TagRhs {
|
impl Clone for TagRhs {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
|
@ -116,11 +128,11 @@ impl ToTokens for TagRhs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct Elem<T> {
|
pub struct Elem<T> {
|
||||||
pub tag: String,
|
pub tag: String,
|
||||||
#[serde(with = "crate::tuple_map")]
|
#[serde(with = "crate::tuple_map")]
|
||||||
pub attrs: HashMap<TagLhs, TagRhs>,
|
pub attrs: BTreeMap<TagLhs, TagRhs>,
|
||||||
pub inner: Option<Vec<T>>,
|
pub inner: Option<Vec<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,6 +165,21 @@ pub enum Rsx {
|
||||||
_Nonexhaustive,
|
_Nonexhaustive,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq<Rsx> for Rsx {
|
||||||
|
fn eq(&self, other: &Rsx) -> bool {
|
||||||
|
match (self, other) {
|
||||||
|
(Rsx::Elem(this), Rsx::Elem(other)) => this == other,
|
||||||
|
(Rsx::Code(expr), Rsx::Code(other)) => {
|
||||||
|
syn::Expr::from_adapter(expr) == syn::Expr::from_adapter(other)
|
||||||
|
}
|
||||||
|
(Rsx::Text(this), Rsx::Text(other)) => this == other,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Rsx {}
|
||||||
|
|
||||||
impl ToTokens for Rsx {
|
impl ToTokens for Rsx {
|
||||||
fn to_tokens(&self, stream: &mut TokenStream) {
|
fn to_tokens(&self, stream: &mut TokenStream) {
|
||||||
match self {
|
match self {
|
||||||
|
@ -161,7 +188,7 @@ impl ToTokens for Rsx {
|
||||||
}
|
}
|
||||||
Rsx::Code(expr) => {
|
Rsx::Code(expr) => {
|
||||||
let expr = syn::Expr::from_adapter(expr);
|
let expr = syn::Expr::from_adapter(expr);
|
||||||
stream.extend(quote! { #expr });
|
stream.extend(quote! { { #expr } });
|
||||||
}
|
}
|
||||||
Rsx::Text(string) => {
|
Rsx::Text(string) => {
|
||||||
let string = syn::Lit::Str(syn::LitStr::new(string.as_ref(), Span::call_site()));
|
let string = syn::Lit::Str(syn::LitStr::new(string.as_ref(), Span::call_site()));
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use proptest::{
|
use proptest::{
|
||||||
collection::{hash_map, vec, SizeRange},
|
collection::{btree_map, vec, SizeRange},
|
||||||
option::{self, Probability},
|
option::{self, Probability},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
string::string_regex,
|
string::string_regex,
|
||||||
|
@ -15,7 +15,7 @@ use super::{Component, Elem, Rsx};
|
||||||
prop_compose! {
|
prop_compose! {
|
||||||
pub fn arbitrary_component() (
|
pub fn arbitrary_component() (
|
||||||
name in ident_strategy(),
|
name in ident_strategy(),
|
||||||
model in hash_map(
|
model in btree_map(
|
||||||
ident_strategy().prop_map(|ident| Symbol::from(ident)),
|
ident_strategy().prop_map(|ident| Symbol::from(ident)),
|
||||||
// TODO: maybe actually have tests for syn?
|
// TODO: maybe actually have tests for syn?
|
||||||
(Just(syn::parse_str::<Type>("()").unwrap().to_adapter()), Just(syn::parse_str::<Expr>("()").unwrap().to_adapter())),
|
(Just(syn::parse_str::<Type>("()").unwrap().to_adapter()), Just(syn::parse_str::<Expr>("()").unwrap().to_adapter())),
|
||||||
|
@ -35,9 +35,9 @@ pub fn arbitrary_view() -> impl Strategy<Value = Rsx> {
|
||||||
Just(Rsx::Code(
|
Just(Rsx::Code(
|
||||||
syn::parse_str::<Expr>("()").unwrap().to_adapter()
|
syn::parse_str::<Expr>("()").unwrap().to_adapter()
|
||||||
)),
|
)),
|
||||||
any::<String>().prop_map(Rsx::Text),
|
string_regex(r"[:print:]+").unwrap().prop_map(Rsx::Text),
|
||||||
];
|
];
|
||||||
leaf.prop_recursive(4, 16, 5, |inner| {
|
leaf.prop_recursive(2, 4, 5, |inner| {
|
||||||
prop_oneof![(
|
prop_oneof![(
|
||||||
ident_strategy(),
|
ident_strategy(),
|
||||||
option::weighted(Probability::new(0.9), vec(inner, SizeRange::default())),
|
option::weighted(Probability::new(0.9), vec(inner, SizeRange::default())),
|
||||||
|
@ -45,7 +45,7 @@ pub fn arbitrary_view() -> impl Strategy<Value = Rsx> {
|
||||||
.prop_map(|(tag, inner)| Rsx::Elem(Elem {
|
.prop_map(|(tag, inner)| Rsx::Elem(Elem {
|
||||||
tag,
|
tag,
|
||||||
// TODO: ouais
|
// TODO: ouais
|
||||||
attrs: HashMap::new(),
|
attrs: BTreeMap::new(),
|
||||||
inner,
|
inner,
|
||||||
}))]
|
}))]
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
// https://github.com/daboross/serde-tuple-vec-map/blob/master/src/lib.rs
|
// https://github.com/daboross/serde-tuple-vec-map/blob/master/src/lib.rs
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::hash::Hash;
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use serde::{
|
use serde::{
|
||||||
de::{Deserialize, Deserializer, SeqAccess, Visitor},
|
de::{Deserialize, Deserializer, SeqAccess, Visitor},
|
||||||
ser::{Serialize, Serializer},
|
ser::{Serialize, Serializer},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
trait Delegate: Ord {}
|
||||||
|
|
||||||
struct TupleVecMapVisitor<K, V> {
|
struct TupleVecMapVisitor<K, V> {
|
||||||
marker: PhantomData<HashMap<K, V>>,
|
marker: PhantomData<BTreeMap<K, V>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K, V> TupleVecMapVisitor<K, V> {
|
impl<K, V> TupleVecMapVisitor<K, V> {
|
||||||
|
@ -23,12 +25,12 @@ impl<K, V> TupleVecMapVisitor<K, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de, K: Eq + Hash, V> Visitor<'de> for TupleVecMapVisitor<K, V>
|
impl<'de, K: Ord, V> Visitor<'de> for TupleVecMapVisitor<K, V>
|
||||||
where
|
where
|
||||||
K: Deserialize<'de>,
|
K: Deserialize<'de>,
|
||||||
V: Deserialize<'de>,
|
V: Deserialize<'de>,
|
||||||
{
|
{
|
||||||
type Value = HashMap<K, V>;
|
type Value = BTreeMap<K, V>;
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
formatter.write_str("a map")
|
formatter.write_str("a map")
|
||||||
|
@ -36,7 +38,7 @@ where
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn visit_unit<E>(self) -> Result<Self::Value, E> {
|
fn visit_unit<E>(self) -> Result<Self::Value, E> {
|
||||||
Ok(HashMap::new())
|
Ok(BTreeMap::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -44,7 +46,7 @@ where
|
||||||
where
|
where
|
||||||
T: SeqAccess<'de>,
|
T: SeqAccess<'de>,
|
||||||
{
|
{
|
||||||
let mut values = HashMap::new();
|
let mut values = BTreeMap::new();
|
||||||
|
|
||||||
while let Some((key, value)) = seq.next_element()? {
|
while let Some((key, value)) = seq.next_element()? {
|
||||||
values.insert(key, value);
|
values.insert(key, value);
|
||||||
|
@ -54,14 +56,14 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serialize an array of `(K, V)` pairs as if it were a `HashMap<K, V>`.
|
/// Serialize an array of `(K, V)` pairs as if it were a `BTreeMap<K, V>`.
|
||||||
///
|
///
|
||||||
/// In formats where dictionaries are ordered, this maintains the input data's order. Each pair is treated as a single
|
/// In formats where dictionaries are ordered, this maintains the input data's order. Each pair is treated as a single
|
||||||
/// entry into the dictionary.
|
/// entry into the dictionary.
|
||||||
///
|
///
|
||||||
/// Behavior when duplicate keys are present in the data is unspecified and serializer-dependent. This function does
|
/// Behavior when duplicate keys are present in the data is unspecified and serializer-dependent. This function does
|
||||||
/// not check for duplicate keys and will not warn the serializer.
|
/// not check for duplicate keys and will not warn the serializer.
|
||||||
pub fn serialize<K: Eq + Hash, V, S>(data: &HashMap<K, V>, serializer: S) -> Result<S::Ok, S::Error>
|
pub fn serialize<K: Ord, V, S>(data: &BTreeMap<K, V>, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
K: Serialize,
|
K: Serialize,
|
||||||
|
@ -70,12 +72,12 @@ where
|
||||||
serializer.collect_seq(data.iter().map(|x| (x.0, x.1)))
|
serializer.collect_seq(data.iter().map(|x| (x.0, x.1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialize to a `Vec<(K, V)>` as if it were a `HashMap<K, V>`.
|
/// Deserialize to a `Vec<(K, V)>` as if it were a `BTreeMap<K, V>`.
|
||||||
///
|
///
|
||||||
/// This directly deserializes into the returned vec with no intermediate allocation.
|
/// This directly deserializes into the returned vec with no intermediate allocation.
|
||||||
///
|
///
|
||||||
/// In formats where dictionaries are ordered, this maintains the input data's order.
|
/// In formats where dictionaries are ordered, this maintains the input data's order.
|
||||||
pub fn deserialize<'de, K: Eq + Hash, V, D>(deserializer: D) -> Result<HashMap<K, V>, D::Error>
|
pub fn deserialize<'de, K: Ord, V, D>(deserializer: D) -> Result<BTreeMap<K, V>, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
K: Deserialize<'de>,
|
K: Deserialize<'de>,
|
||||||
|
|
|
@ -4,4 +4,4 @@
|
||||||
#
|
#
|
||||||
# It is recommended to check this file in to source control so that
|
# It is recommended to check this file in to source control so that
|
||||||
# everyone who runs the test benefits from these saved cases.
|
# everyone who runs the test benefits from these saved cases.
|
||||||
cc 2a51d2b2a9a6442d273f2835de2bc13bc778cab5d1333e190cc6fb90c5d5e50a # shrinks to tree = Component { name: "A", model: {}, view: [] }
|
cc c87725a641776defa0a321d11950acba1b25c60d510345f7a487df38d08795bc # shrinks to tree = Component { name: "a", model: {}, view: [] }
|
|
@ -1,4 +1,4 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::BTreeMap;
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ use proc_macro2::{
|
||||||
use symbol::Symbol;
|
use symbol::Symbol;
|
||||||
use syn::{
|
use syn::{
|
||||||
parse::{Parse, ParseStream},
|
parse::{Parse, ParseStream},
|
||||||
Error as SynError, Expr, Result as SynResult, Token, Type,
|
Error as SynError, Expr, Lit, Result as SynResult, Token, Type,
|
||||||
};
|
};
|
||||||
use syn_serde::Syn;
|
use syn_serde::Syn;
|
||||||
|
|
||||||
|
@ -26,8 +26,12 @@ pub(crate) enum ParseError {
|
||||||
Syn(SynError),
|
Syn(SynError),
|
||||||
UnexpectedEOF,
|
UnexpectedEOF,
|
||||||
UnexpectedKeyword,
|
UnexpectedKeyword,
|
||||||
|
UnexpectedToken(TokenTree),
|
||||||
MissingModel,
|
MissingModel,
|
||||||
MissingView,
|
MissingView,
|
||||||
|
ClosedTooFar,
|
||||||
|
WrongClosingTag(String, String),
|
||||||
|
UnrecognizedLiteral(Lit),
|
||||||
// InvalidRsx(TokenTree),
|
// InvalidRsx(TokenTree),
|
||||||
UnmatchedOpenTag(TokenTree),
|
UnmatchedOpenTag(TokenTree),
|
||||||
}
|
}
|
||||||
|
@ -148,6 +152,7 @@ impl Visitor {
|
||||||
}
|
}
|
||||||
buf.push(next_token);
|
buf.push(next_token);
|
||||||
}
|
}
|
||||||
|
println!("model buf: {:?}", buf);
|
||||||
|
|
||||||
// probably shouldn't happen?
|
// probably shouldn't happen?
|
||||||
if buf.len() == 0 {
|
if buf.len() == 0 {
|
||||||
|
@ -166,10 +171,11 @@ impl Visitor {
|
||||||
)))
|
)))
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut map = HashMap::new();
|
let mut map = BTreeMap::new();
|
||||||
while let Some((name, ty, init, comma)) = single_def()? {
|
while let Some((name, ty, init, comma)) = single_def()? {
|
||||||
|
println!("single_def => ({}, {:?}, {:?}, {})", name, ty, init, comma);
|
||||||
map.insert(name, (ty, init));
|
map.insert(name, (ty, init));
|
||||||
if comma {
|
if !comma {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,18 +185,42 @@ impl Visitor {
|
||||||
fn consume_view(&mut self) -> Result<Vec<Rsx>, ParseError> {
|
fn consume_view(&mut self) -> Result<Vec<Rsx>, ParseError> {
|
||||||
let mut rsx_parser = RsxParser::new(self.0.clone());
|
let mut rsx_parser = RsxParser::new(self.0.clone());
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
|
let mut tag_stack = Vec::new();
|
||||||
|
|
||||||
while let Some(next_token) = rsx_parser.next() {
|
while let Some(next_token) = rsx_parser.next() {
|
||||||
match next_token? {
|
match next_token? {
|
||||||
RsxToken::EmptyTag(name, attrs) => {
|
RsxToken::EmptyTag(tag, attrs) => {
|
||||||
let elem = Elem {
|
let elem = Elem {
|
||||||
tag: name.to_string(),
|
tag: tag.to_string(),
|
||||||
attrs,
|
attrs,
|
||||||
inner: None,
|
inner: None,
|
||||||
};
|
};
|
||||||
let el = Rsx::Elem(elem);
|
let el = Rsx::Elem(elem);
|
||||||
result.push(el);
|
result.push(el);
|
||||||
}
|
}
|
||||||
|
RsxToken::OpeningTag(tag, attrs) => {
|
||||||
|
tag_stack.push((tag, attrs, result.clone()));
|
||||||
|
result.clear();
|
||||||
|
}
|
||||||
|
RsxToken::ClosingTag(tag) => {
|
||||||
|
if let Some((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(),
|
||||||
|
attrs: attrs.clone(),
|
||||||
|
inner: Some(result),
|
||||||
|
}));
|
||||||
|
result = last_result;
|
||||||
|
} else {
|
||||||
|
return Err(ParseError::WrongClosingTag(
|
||||||
|
last_tag.to_string(),
|
||||||
|
tag.to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(ParseError::ClosedTooFar);
|
||||||
|
}
|
||||||
|
}
|
||||||
RsxToken::Code(expr) => {
|
RsxToken::Code(expr) => {
|
||||||
result.push(Rsx::Code(expr.to_adapter()));
|
result.push(Rsx::Code(expr.to_adapter()));
|
||||||
}
|
}
|
||||||
|
@ -290,20 +320,38 @@ impl Iterator for Visitor {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use enterprise_compiler::model::*;
|
use enterprise_compiler::model::*;
|
||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
|
use syn_serde::Syn;
|
||||||
|
|
||||||
use super::Visitor;
|
use super::Visitor;
|
||||||
|
|
||||||
|
fn convert<K: Clone + Ord>(
|
||||||
|
map: &BTreeMap<K, (syn_serde::Type, syn_serde::Expr)>,
|
||||||
|
) -> BTreeMap<K, (syn::Type, syn::Expr)> {
|
||||||
|
map.iter()
|
||||||
|
.map(|(name, (ty, expr))| {
|
||||||
|
let ty = syn::Type::from_adapter(ty);
|
||||||
|
let expr = syn::Expr::from_adapter(expr);
|
||||||
|
(name.clone(), (ty, expr))
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
proptest! {
|
proptest! {
|
||||||
#[test]
|
#[test]
|
||||||
fn tokens_parse_compatibility(tree in arbitrary_component()) {
|
fn tokens_parse_compatibility(tree in arbitrary_component()) {
|
||||||
let tokens = tree.to_token_stream();
|
let tokens = tree.to_token_stream();
|
||||||
|
|
||||||
let mut visitor = Visitor::from_tokens(tokens.clone());
|
let mut visitor = Visitor::from_tokens(tokens.clone());
|
||||||
let tree2 = visitor.next().unwrap().unwrap();
|
let tree2 = visitor.next().unwrap().unwrap();
|
||||||
|
|
||||||
prop_assert_eq!(format!("{}", tokens), format!("{}", tree2.to_token_stream()));
|
// compare the trees
|
||||||
|
prop_assert_eq!(tree.name, tree2.name, "name");
|
||||||
|
prop_assert_eq!(convert(&tree.model), convert(&tree2.model), "model");
|
||||||
|
prop_assert_eq!(tree.view, tree2.view, "view");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::BTreeMap;
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ use symbol::Symbol;
|
||||||
use syn::{Expr, Lit};
|
use syn::{Expr, Lit};
|
||||||
use syn_serde::Syn;
|
use syn_serde::Syn;
|
||||||
|
|
||||||
use crate::parser::{ParseError, consume_ident, consume_punct};
|
use crate::parser::{consume_ident, consume_punct, ParseError};
|
||||||
|
|
||||||
pub(crate) struct RsxParser(Peekable<IntoIter>);
|
pub(crate) struct RsxParser(Peekable<IntoIter>);
|
||||||
|
|
||||||
|
@ -46,6 +46,8 @@ impl RsxParser {
|
||||||
|
|
||||||
let name = self.consume_ident()?;
|
let name = self.consume_ident()?;
|
||||||
if is_closing {
|
if is_closing {
|
||||||
|
// TODO: assert next is >
|
||||||
|
self.0.next();
|
||||||
return Ok(Some(RsxToken::ClosingTag(Symbol::from(name.to_string()))));
|
return Ok(Some(RsxToken::ClosingTag(Symbol::from(name.to_string()))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +78,7 @@ impl RsxParser {
|
||||||
buf.push(next_token);
|
buf.push(next_token);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut attrs = HashMap::new();
|
let mut attrs = BTreeMap::new();
|
||||||
let mut iter = buf.into_iter().peekable();
|
let mut iter = buf.into_iter().peekable();
|
||||||
loop {
|
loop {
|
||||||
// consume a single attr
|
// consume a single attr
|
||||||
|
@ -133,24 +135,50 @@ impl RsxParser {
|
||||||
} else {
|
} else {
|
||||||
RsxToken::OpeningTag
|
RsxToken::OpeningTag
|
||||||
};
|
};
|
||||||
return Ok(Some(variant(Symbol::from(name.to_string()), attrs)));
|
Ok(Some(variant(Symbol::from(name.to_string()), attrs)))
|
||||||
}
|
}
|
||||||
TokenTree::Literal(lit) => {
|
TokenTree::Literal(lit) => {
|
||||||
let stream = TokenStream::from(TokenTree::Literal(lit));
|
let stream = TokenStream::from(TokenTree::Literal(lit));
|
||||||
let lit = syn::parse2::<Lit>(stream)?;
|
let lit = syn::parse2::<Lit>(stream)?;
|
||||||
|
|
||||||
if let Lit::Str(string) = lit {
|
if let Lit::Str(string) = lit {
|
||||||
return Ok(Some(RsxToken::Str(string.value())));
|
Ok(Some(RsxToken::Str(string.value())))
|
||||||
|
} else {
|
||||||
|
Err(ParseError::UnrecognizedLiteral(lit))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TokenTree::Group(group) if group.delimiter() == Delimiter::Brace => {
|
TokenTree::Group(group) if group.delimiter() == Delimiter::Brace => {
|
||||||
let expr = syn::parse2::<Expr>(group.stream())?;
|
let mut stream = group.stream().into_iter();
|
||||||
return Ok(Some(RsxToken::Code(expr)));
|
|
||||||
}
|
|
||||||
_ => unimplemented!("TOKEN: {:?}", token),
|
|
||||||
};
|
|
||||||
|
|
||||||
unimplemented!("the fuck")
|
enum GroupType {
|
||||||
|
Pound,
|
||||||
|
Slash,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
token => Err(ParseError::UnexpectedToken(token)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consume_ident(&mut self) -> Result<Ident, ParseError> {
|
fn consume_ident(&mut self) -> Result<Ident, ParseError> {
|
||||||
|
@ -170,11 +198,12 @@ impl RsxParser {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum RsxToken {
|
pub(crate) enum RsxToken {
|
||||||
OpeningTag(Symbol, HashMap<TagLhs, TagRhs>),
|
OpeningTag(Symbol, BTreeMap<TagLhs, TagRhs>),
|
||||||
EmptyTag(Symbol, HashMap<TagLhs, TagRhs>),
|
EmptyTag(Symbol, BTreeMap<TagLhs, TagRhs>),
|
||||||
ClosingTag(Symbol),
|
ClosingTag(Symbol),
|
||||||
Str(String),
|
Str(String),
|
||||||
Code(Expr),
|
Code(Expr),
|
||||||
|
StartFor(),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iterator for RsxParser {
|
impl Iterator for RsxParser {
|
||||||
|
|
|
@ -10,7 +10,7 @@ component! {
|
||||||
view {
|
view {
|
||||||
<input on:submit={|evt| { todos.push(evt.name); }} />
|
<input on:submit={|evt| { todos.push(evt.name); }} />
|
||||||
<ul>
|
<ul>
|
||||||
{#for (key, todo) in todos}
|
{#for (key, todo) in todos }
|
||||||
<li>{todo} <a on:click={|_| { todos.remove(key); }}>[x]</a></li>
|
<li>{todo} <a on:click={|_| { todos.remove(key); }}>[x]</a></li>
|
||||||
{/for}
|
{/for}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
0
output.txt
Normal file
0
output.txt
Normal file
|
@ -22,7 +22,7 @@ proc-macro2 = { version = "1.0", default-features = false }
|
||||||
serde = { version = "1.0.99", features = ["derive"] }
|
serde = { version = "1.0.99", features = ["derive"] }
|
||||||
serde_derive = "1.0.99" # This is necessary to make `-Z minimal-versions` build successful.
|
serde_derive = "1.0.99" # This is necessary to make `-Z minimal-versions` build successful.
|
||||||
serde_json = { version = "1.0", optional = true }
|
serde_json = { version = "1.0", optional = true }
|
||||||
syn = { version = "1.0.5", default-features = false, features = ["full"] }
|
syn = { version = "1.0.5", default-features = false, features = ["extra-traits", "full"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
|
|
Loading…
Reference in a new issue