Skip to content

Commit

Permalink
A better HTML parser.
Browse files Browse the repository at this point in the history
  • Loading branch information
eholk committed Aug 10, 2012
1 parent b354fe2 commit 9a1a258
Showing 1 changed file with 66 additions and 43 deletions.
109 changes: 66 additions & 43 deletions src/test/run-pass/html-literals.rs
Original file line number Diff line number Diff line change
@@ -1,42 +1,80 @@
// A test of the macro system. Can we do HTML literals?

// xfail-pretty
// xfail-test

/*
This is an HTML parser written as a macro. It's all CPS, and we have
to carry around a bunch of state. The arguments to macros all look like this:
{ tag_stack* # expr* # tokens }
The stack keeps track of where we are in the tree. The expr is a list
of children of the current node. The tokens are everything that's
left.
*/

macro_rules! html {
{ $($body:tt)* } => {
let builder = HTMLBuilder();
build_html!{builder := $($body)*};
builder.getDoc()
}
{ $($body:tt)* } => (
parse_node!( []; []; $($body)* )
)
}

macro_rules! build_html {
{ $builder:expr := </$tag:ident> $($rest:tt)* } => {
$builder.endTag(stringify!($tag));
build_html!{ $builder := $($rest)* };
};
macro_rules! parse_node {
{
[:$head:ident ($(:$head_nodes:expr),*)
$(:$tags:ident ($(:$tag_nodes:expr),*))*];
[$(:$nodes:expr),*];
</$tag:ident> $($rest:tt)*
} => (
parse_node!(
[$(: $tags ($(:$tag_nodes),*))*];
[$(:$head_nodes,)* :tag(stringify!($head), ~[$($nodes),*])];
$($rest)*
)
);

{ $builder:expr := <$tag:ident> $($rest:tt)* } => {
$builder.beginTag(stringify!($tag));
build_html!{ $builder := $($rest)* };
};
{
[$(:$tags:ident ($(:$tag_nodes:expr),*) )*];
[$(:$nodes:expr),*];
<$tag:ident> $($rest:tt)*
} => (
parse_node!(
[:$tag ($(:$nodes)*) $(: $tags ($(:$tag_nodes),*) )*];
[];
$($rest)*
)
);

{ $builder:expr := . $($rest:tt)* } => {
$builder.addText(~".");
build_html!{ $builder := $($rest)* };
};
{
[$(:$tags:ident ($(:$tag_nodes:expr),*) )*];
[$(:$nodes:expr),*];
. $($rest:tt)*
} => (
parse_node!(
[$(: $tags ($(:$tag_nodes),*))*];
[$(:$nodes,)* :text(~".")];
$($rest)*
)
);

{ $builder:expr := $word:ident $($rest:tt)* } => {
$builder.addText(stringify!($word));
build_html!{ $builder := $($rest)* };
};
{
[$(:$tags:ident ($(:$tag_nodes:expr),*) )*];
[$(:$nodes:expr),*];
$word:ident $($rest:tt)*
} => (
parse_node!(
[$(: $tags ($(:$tag_nodes),*))*];
[$(:$nodes,)* :text(stringify!($word))];
$($rest)*
)
);

{ $builder:expr := } => { }
{ []; [:$e:expr]; } => ( $e );
}

fn main() {
let page = html! {
<html>
<head><title>This is the title.</title></head>
Expand All @@ -45,24 +83,9 @@ fn main() {
</body>
</html>
};
// When we can do this, we are successful:
//
//let page = tag(~"html", ~[tag(~"head", ~[...])])

}

enum HTMLFragment {
enum HTMLFragment {
tag(~str, ~[HTMLFragment]),
text(~str),
}

struct HTMLBuilder {
bar: ();
fn getDoc() -> HTMLFragment { fail }
fn beginTag(tag: ~str) { }
fn endTag(tag: ~str) { }
fn addText(test: ~str) { }
}

fn HTMLBuilder() -> HTMLBuilder {
HTMLBuilder { bar: () }
}

0 comments on commit 9a1a258

Please sign in to comment.