tests/testcase.g \
tests/regression.tc
+javatest: edu.berkeley.sbp.jar
+ $(java) -cp $< edu.berkeley.sbp.misc.RegressionTests \
+ tests/meta.g \
+ tests/testcase.g \
+ tests/java.tc
+
boot: edu.berkeley.sbp.jar
cd src; \
$(java) -cp ../$< \
_____________________________________________________________________________
Immediately
+- If a top-level rule has labels but no head-tag, like this
+ Foo = a:Bar b:Baz
+ then infer the name of the rule it belongs to
+
+create( $c:{...}, class ) =
+ return create($c:{...})
+
+create( h:{...}, class ) =
+
+create( , String)
+
+create( _:{...}, String) = treat as char[]
+create( _:{...}, c[] ) = { create(.,c), create(.,c), ... }
+
+create( $c:{...} ) =
+
+
+ - clean up the visualization (?)
+
- I still don't like Atom.Infer and Atom.Invert...
- better ambiguity debugging tools
public void addTree(Tree<T> t);
}
public static class HashSetTreeConsumer<T> extends HashSet<Tree<T>> implements TreeConsumer<T> {
- public void addTree(Tree<T> t) { super.add(t); }
+ public void addTree(Tree<T> t) {
+ super.add(t);
+ }
}
static <T> Forest<T> singleton(Input.Location loc, Position p) {
void expand(int i, TreeMaker<T> h);
}
public abstract void edges(GraphViz.Node n);
+ public boolean ambiguous() { return false; }
protected static class MyBody<T> extends Forest<T> implements Body<T> /* extends PrintableTree<Forest<T>> implements */ {
public boolean isTransparent() { return false; }
GraphViz.Node n = gv.createNode(this);
n.label = headToString()==null?"":headToString();
n.directed = true;
+ n.comment = reduction==null?null:reduction+"";
edges(n);
return n;
}
if (edges) return;
edges = true;
for(int i=0; i<tokens.length; i++) {
- if (i==tokens.length-1 && unwrap) {
+ if (i==tokens.length-1 && unwrap && !tokens[i].ambiguous()) {
tokens[i].edges(n);
} else {
n.edge(tokens[i], labels==null?null:labels[i]);
* viewed, it becomes immutable
*/
static class Ref<T> extends Forest<T> {
+ public boolean ambiguous() {
+ if (hp.size()==0) return false;
+ if (hp.size()==1) return hp.iterator().next().ambiguous();
+ return true;
+ }
private FastSet<Forest<T>> hp = new FastSet<Forest<T>>();
public Ref() { }
public int toInt() {
public boolean isTransparent() { return hp.size()==1; }
public boolean isHidden() { return hp.size()==0; }
- public void edges(GraphViz.Node n) { for(Forest f : hp) f.edges(n); }
+ public void edges(GraphViz.Node n) {
+ if (hp.size()==1) { hp.iterator().next().edges(n); return; }
+ for(Forest f : hp) f.edges(n);
+ }
public GraphViz.Node toGraphViz(GraphViz gv) {
- if (hp.size()==0) return null;
+ //if (hp.size()==0) return null;
if (hp.size()==1) return hp.iterator().next().toGraphViz(gv);
if (gv.hasNode(this)) return gv.createNode(this);
GraphViz.Node n = gv.createNode(this);
}
public <T> Forest<T> postReduce(Input.Location loc, Forest<T>[] args, Position p) {
Forest<T>[] args2 = new Forest[count];
+ Object[] labs2 = new Object[count];
int j = 0;
- for(int i=0; i<args.length; i++) if (!drops[i]) args2[j++] = args[i];
+ for(int i=0; i<args.length; i++) if (!drops[i]) { labs2[j] = labs==null?null:labs[i]; args2[j++] = args[i]; }
//System.out.println("reduce \""+tag+"\"");
- return Forest.create(loc, (T)tag, args2, labs, false, false, p);
+ return Forest.create(loc, (T)tag, args2, labs2, false, false, p);
}
public StringBuffer toString(StringBuffer sb, boolean spacing) {
int len = sb.length();
carets.put(ret, s);
dropAll.add(ret);
return ret;
+ /*
} else if ("^^".equals(head)) {
return new Keep(walk(tree,0));
+ */
} else if ("psx".equals(head)) return ((PreSequence)walk(tree, 0)).buildSequence();
else if ("nonTerminal".equals(head)) return getNonTerminal(string(tree.child(0)));
else if ("::=".equals(head)) return nonTerminal(string(tree.child(0)), (Sequence[][])Reflection.lub(walk(tree, 1)), false, false);
else if ("ps".equals(head)) return new PreSequence((Object[])walk(tree,0), null);
else if (":".equals(head)) {
String s = string(tree.child(0));
- return new Keep("_".equals(s)?walk(tree,1):new PreSequence(new Object[] { walk(tree, 1) }, convertLabel(s)).buildUnion());
+ return new Keep(s,walk(tree,1));
} else if ("::".equals(head)) {
PreSequence p = (PreSequence)walk(tree, 1);
- p.tag = convertLabel(string(tree.child(0)));
+ String s = string(tree.child(0));
+ if (s.equals("[]")) p.unwrap = true;
+ else p.tag = convertLabel(s);
return p;
}
else if ("=>".equals(head)) {
}
static class Keep {
- final Object o;
- public Keep(Object o) { this.o = o; }
+ public final String label;
+ public final Object o;
+ public Keep(String label, Object o) { this.label = label; this.o = o; }
}
- public String convertLabel(String label) { return label; }
+ public Object convertLabel(String label) { return label; }
public Object walk(String tag, Object[] argo) {
if (argo.length==0) return super.walk(tag, argo);
public Element noFollow = null;
public final HashSet<Sequence> and = new HashSet<Sequence>();
public final HashSet<Sequence> not = new HashSet<Sequence>();
- public /*final*/ String tag;
+ public /*final*/ Object tag;
public final Object[] o;
public PreSequence sparse(Object e) {
boolean[] drops = null;
public PreSequence(Object o) { this(new Object[] { o }, null); }
public PreSequence(Object[] o) { this(o, null); }
- public PreSequence(Object[] o, String tag) { this(o, tag, null); }
- public PreSequence(Object[] o, String tag, boolean[] drops) {
+ public PreSequence(Object[] o, Object tag) { this(o, tag, null); }
+ public PreSequence(Object[] o, Object tag, boolean[] drops) {
this.o = o;
this.tag = tag;
this.drops = drops==null ? new boolean[o.length] : drops;
public boolean unwrap = false;
public Sequence buildSequence() { return buildSequence(null, false, false); }
public Sequence buildSequence(Union u) { return buildSequence(u, false, false); }
- public Sequence buildSequence(Union u, boolean lame, String tag) {
+ public Sequence buildSequence(Union u, boolean lame, Object tag) {
this.tag = tag;
return buildSequence(u, lame, false);
}
}
HashSet<Sequence> set = new HashSet<Sequence>();
Element[] o2 = o==null ? new Element[0] : new Element[o.length];
+ Object[] labels = new Object[drops.length];
int nonDrop = 0;
boolean keeping = false;
if (o != null) {
if (!keeping) { for(int k=0; k<i; k++) drops[k]=true; }
drops[i] = false;
keeping = true;
+ labels[i] = ((Keep)oi).label;
oi = ((Keep)oi).o;
} else {
if (keeping) drops[i] = true;
Sequence ret = null;
if (dropAll || lame) ret = Sequence.drop(expansion, lame);
else if (unwrap) ret = new Sequence.Unwrap(expansion, drops);
- else if (tag!=null) ret = Sequence.rewritingSequence(tag, expansion, null, drops);
+ else if (keeping || tag!=null) ret = Sequence.rewritingSequence(tag, expansion, labels, drops);
else {
int idx = -1;
for(int i=0; i<expansion.length; i++)
+
+
+
// DO NOT EDIT STUFF BELOW: IT IS AUTOMATICALLY GENERATED
new edu.berkeley.sbp.Tree(null, "grammar", new edu.berkeley.sbp.Tree[] { new edu.berkeley.sbp.Tree(null, null, new edu.berkeley.sbp.Tree[] { new edu.berkeley.sbp.Tree(null, "=", new edu.berkeley.sbp.Tree[] { new edu.berkeley.sbp.Tree(null, null, new edu.berkeley.sbp.Tree[] { new edu.berkeley.sbp.Tree(null, "s", new edu.berkeley.sbp.Tree[] { })}),
new edu.berkeley.sbp.Tree(null, null, new edu.berkeley.sbp.Tree[] { new edu.berkeley.sbp.Tree(null, null, new edu.berkeley.sbp.Tree[] { new edu.berkeley.sbp.Tree(null, "psx", new edu.berkeley.sbp.Tree[] { new edu.berkeley.sbp.Tree(null, "ps", new edu.berkeley.sbp.Tree[] { new edu.berkeley.sbp.Tree(null, null, new edu.berkeley.sbp.Tree[] { new edu.berkeley.sbp.Tree(null, "!", new edu.berkeley.sbp.Tree[] { new edu.berkeley.sbp.Tree(null, "nonTerminal", new edu.berkeley.sbp.Tree[] { new edu.berkeley.sbp.Tree(null, null, new edu.berkeley.sbp.Tree[] { new edu.berkeley.sbp.Tree(null, "w", new edu.berkeley.sbp.Tree[] { }),
+
+
+
--- /dev/null
+package edu.berkeley.sbp.misc;
+import java.io.*;
+import java.util.*;
+import java.lang.reflect.*;
+import edu.berkeley.sbp.*;
+import edu.berkeley.sbp.misc.*;
+import edu.berkeley.sbp.tib.*;
+import edu.berkeley.sbp.chr.*;
+import edu.berkeley.sbp.util.*;
+
+public class ReflectiveGrammar extends MetaGrammar {
+
+ final Class baseClass;
+ public ReflectiveGrammar(Class baseClass) { this.baseClass = baseClass; }
+
+ public Object convertLabel(String label) { return new ClassMark(label); }
+
+ private static class ClassMark {
+ public final String clazz;
+ public ClassMark(String clazz) { this.clazz = clazz; }
+ public String toString() { return clazz+"$"; }
+ }
+
+ public String stringify(Tree<Object> t) throws Exception {
+ StringBuffer ret = new StringBuffer();
+ for(int i=0; i<t.numChildren(); i++) {
+ Tree<Object> child = t.child(i);
+ Object head = child.numChildren() > 0 ? buildHead(child, String.class) : child.head();
+ if (head!=null) ret.append(head);
+ }
+ return ret.toString();
+ }
+
+ public Object build(Tree<Object> t) throws Exception { return buildHead(t, null); }
+ public Object buildHead(Tree<Object> t, Class c) throws Exception {
+ System.out.println("buildHead " + (c==null?null:c.getName()) + " " + t);
+ Object h = t.head();
+
+ if (h != null && h instanceof ClassMark) return buildBody(t, Class.forName(baseClass.getName()+"$"+((ClassMark)h).clazz));
+ if (c.isArray()) {
+ Object[] ret = new Object[t.numChildren()];
+ for(int i=0; i<ret.length; i++)
+ ret[i] = buildHead(t.child(i), c.getComponentType());
+ return Reflection.lub(ret);
+ }
+ if (h==null) return buildBody(t, c);
+
+ if (c==String.class) return stringify(t);
+ if (c==int.class) return new Integer(stringify(t));
+
+ if (t.numChildren() > 0) throw new RuntimeException("can't buildHead() on a tree with children when the head is of type " + h.getClass().getName());
+ return h;
+ }
+
+ public Object buildBody(Tree<Object> t, Class c) throws Exception {
+ System.out.println("buildBody " + (c==null?null:c.getName()) + " " + t);
+ c = resolveClass(t, c);
+ Object o = c.newInstance();
+ Field[] f = c.getFields();
+ OUTER: for(int i=0; i<t.numChildren(); i++) {
+ Object label = t.label(i);
+ Field field = null;
+ if (label!=null) try { field = c.getField(label+""); } catch (NoSuchFieldException _) { }
+ if (field==null && label != null)
+ for(Method m : c.getMethods())
+ if (m.getName().equals(label)) {
+ m.invoke(o, new Object[] { buildHead(t.child(i), m.getParameterTypes()[0]) });
+ continue OUTER;
+ }
+ if (field==null) System.err.println("warning: skipping field " + label + " ("+i+") on class " + c.getName());
+ else {
+ Object tgt = Reflection.rebuild(buildHead(t.child(i), field.getType()), field.getType());
+ if (tgt instanceof Object[]) tgt = Reflection.lub(tgt);
+ System.err.println("setting field " + field.getName() + " on " + c.getName() + " to " + tgt);
+ try {
+ field.set(o, tgt);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ return o;
+ }
+
+ public Class resolveClass(Tree<Object> t, Class c) throws Exception {
+ if (c==null) return null;
+ System.out.println("resolving " + c.getName());
+ if (Reflection.isConcrete(c)) return c;
+ Class ret = null;
+ Class[] subs = (Class[])c.getField("subclasses").get(null);
+ OUTER: for(int i=0; i<subs.length; i++) {
+ System.err.println("trying " + subs[i].getName());
+ for(int j=0; j<t.numChildren(); j++)
+ if (Reflection.getField(subs[i], t.label(j)+"")==null) {
+ System.err.println("skipping due to " + t.label(j));
+ continue OUTER;
+ }
+ if (ret != null)
+ throw new RuntimeException("couldn't decide between two classes:\n " + subs[i].getName() + "\n " + ret.getName());
+ ret = subs[i];
+ }
+ if (ret==null) throw new RuntimeException("couldn't find a class to match tree: " + t);
+ return ret;
+ }
+
+}
public ReflectiveWalker() { this.target = this; }
public ReflectiveWalker(Object target) { this.target = target; }
private final Object target;
- private String normalize(String s) {
+ public static String mangle(String s) {
StringBuffer ret = new StringBuffer();
for(int i=0; i<s.length(); i++) {
char c = s.charAt(i);
/*
public Object walk(Tree<String> tree) {
if (tree.head()!=null) {
- Member m = member("$"+normalize(tree.head()), 0, false);
+ Member m = member("$"+mangle(tree.head()), 0, false);
if (m!=null) {
if ((m instanceof Method) && ((Method)m).getReturnType()==Void.TYPE) {
Reflection.fuzzyInvoke(target, m, new Object[0]);
*/
public void walk(String tag) {
if (tag==null) return;
- Member m = member(normalize(tag), 0, false);
+ Member m = member(mangle(tag), 0, false);
if (m!=null) Reflection.fuzzyInvoke(target, m);
}
protected Object defaultWalk(String tag, Object[] argo) { return super.walk(tag, argo); }
if (argo.length==0) return super.walk(tag, argo);
if (argo==null) return tag;
if (tag==null || "".equals(tag)) return argo;
- Member m = tag==null ? null : member(normalize(tag), argo.length, false);
+ Member m = tag==null ? null : member(mangle(tag), argo.length, false);
if (m==null) return defaultWalk(tag, argo);
//System.out.println("preparing to invoke method " + (m==null ? "null" : (m.toString())) + " for sequence " + (owner()+"."+tag));
if (m != null) return Reflection.fuzzyInvoke(target, m, argo);
}
public boolean execute() throws Exception {
if (jav) {
- Tree<Object> tree = new CharParser(grammar).parse(new StringReader(input)).expand1();
- System.out.println(tree);
- System.out.println(JavaGrammar.build(tree));
+ Forest<String> tree = new CharParser(grammar).parse(new StringReader(input));
+ FileOutputStream fos = new FileOutputStream("/Users/megacz/Desktop/out.dot");
+ PrintWriter p = new PrintWriter(new OutputStreamWriter(fos));
+ GraphViz gv = new GraphViz();
+ tree.toGraphViz(gv);
+ gv.dump(p);
+ p.flush();
+ p.close();
return true;
}
Forest<String> res = null;
String[] output = tree.numChildren()>2 ? ((String[])walk(tree, 1)) : new String[0];
boolean tib = "tibcase".equals(tree.head());
boolean jav = "javacase".equals(tree.head());
- MetaGrammar gram = jav ? new JavaGrammar() : tib ? new Tib.Grammar() : new MetaGrammar();
+ MetaGrammar gram = jav ? new JavaGrammar() : tib ? /*new Tib.Grammar()*/null : new MetaGrammar();
Union grammar = (Union)((MetaGrammar)(gram.walk(tree, tree.numChildren()-1))).done("s");
return new TestCase(input, output, grammar, tib, jav);
} else if ("ts".equals(tree.head())) return walk(tree, 0);
// Grammar //////////////////////////////////////////////////////////////////////////////
- public static class Grammar extends MetaGrammar {
+ public static class Grammar extends ReflectiveGrammar {
private int anon = 0;
private final Element ws = Repeat.maximal0(getNonTerminal("w"));
- public Grammar() { dropAll.add(ws); }
+ public Grammar(Class c) { super(c); dropAll.add(ws); }
public Object walk(Tree<String> tree) {
String head = tree.head();
if (tree.numChildren()==0) return super.walk(tree);
public class TibDoc {
+ public static class Doc {
+ public Header head;
+ public Body body;
+ }
+ public static class kv { public String key; public Text[] val; }
+ public static class Header {
+ public void attrs(kv[] kvs) {
+ for(int i=0; i<kvs.length; i++)
+ System.out.println("key="+kvs[i].key+" val="+kvs[i].val);
+ }
+ }
+ public static class Body {
+ Section[] sections;
+ }
+ public static class Section { }
+ public static abstract class Text {
+ public static final Class[] subclasses = new Class[] { Chars.class, URL.class, Email.class };
+ }
+ public static class Chars extends Text { String chars; }
+ public static class Symbol extends Text { String chars; }
+ public static class Email extends Text { String user; Host host; }
+ public static interface Host { }
+ public static class DNS implements Host { String[] part; }
+ public static class IP implements Host { int a, b, c, d; }
+ public static class URL extends Text { String method; Host host; int port; String path; }
+ public static class Italic extends Text { Text body; }
+
public static void main(String[] s) throws Exception {
try {
System.out.println("parsing " + s[0]);
Tree<String> res = new CharParser(MetaGrammar.make()).parse(new FileInputStream(s[0])).expand1();
- MetaGrammar gram = (MetaGrammar)new Tib.Grammar().walk(res);
+ MetaGrammar gram = new Tib.Grammar(TibDoc.class);
+ gram = (MetaGrammar)gram.walk(res);
//System.out.println(gram);
Union mg = gram.done();
System.out.println("\nparsing " + s[1]);
Forest f = new CharParser(mg).parse(new Tib(new FileInputStream(s[1])));
-
+ //((Tree)new StringifyWalker().walk(f.expand1())).toPrettyString()
System.out.println();
- System.out.println(f);
- System.out.println();
- System.out.println(((Tree)new StringifyWalker().walk(f.expand1())).toPrettyString());
-
+ Doc doc = (Doc)new ReflectiveGrammar(TibDoc.class).build(f.expand1());
+ System.out.println(doc);
+ /*
String st = new HTMLWalker().walk(f.expand1()).toString();
System.out.println(st);
FileOutputStream fos = new FileOutputStream("out.html");
p.println(st);
p.flush();
p.close();
+ */
} catch (Ambiguous a) {
FileOutputStream fos = new FileOutputStream("/Users/megacz/Desktop/out.dot");
PrintWriter p = new PrintWriter(new OutputStreamWriter(fos));
gv.dump(p);
p.flush();
p.close();
+ a.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
public String ul(String[] li) { return "<ul>"+join(li,"")+"</ul>"; }
public String ol(String[] li) { return "<ol>"+join(li,"")+"</ol>"; }
public String hr() { return "\n<hr/>\n"; }
+ public String br() { return "\n<br/>\n"; }
public String it(Object o) { return "<i>"+o+"</i>"; }
public String tt(Object o) { return "<tt>"+o+"</tt>"; }
public String underline(Object o) { return "<ul>"+o+"</ul>"; }
public class Node {
private final int idx = master_idx++;
public String label;
+ public String comment;
public boolean directed = false;
public String color="black";
public ArrayList<Node> edges = new ArrayList<Node>();
pw.print("\"");
}
pw.print("color="+color);
+ if (comment!=null) pw.print(" comment=\""+StringUtil.escapify(comment,"\\\"")+"\" ");
pw.print("];\n");
}
}
return true;
}
+ public static Field getField(Class c, String s) {
+ try {
+ for(Field f : c.getDeclaredFields())
+ if (f.getName().equals(s))
+ return f;
+ } catch (Exception e) { }
+ return null;
+ }
+
}
header
author = Adam Megacz
myemail = adam@foo.megacz.com
- comment = my homepage is at http://www.megacz.com you should *check* it out
- date = published \today, yep!
+ comment = my homepage is at http://www.megacz.com you should **check** it out
== Introduction ==
- this is the body adam@megacz.com text
+ this is the body adam@megacz.com text \today
You can visit {my website}->adam@megacz.com with a !hyperlink to it!
- The following demonstrates verbatim stuff [[Knu68]], as
- well as a footnote ((like)) because are
+ The following demonstrates->http://www.slashdot.org/ verbatim stuff [[Knu68]], as
+ well \br as \br a \br footnote ((like)) because are
coool in an O(n^^3) way.
"" this is a test of \sc{paragraph of fun}
| ec ^"-" ec
e = (Quoted|Word) ^":" e
- >
- nonTerminal:: Word
+ > nonTerminal:: Word
| literal:: Quoted
| ^"()"
| ^"{" PreSequence "}" /ws
input "aaaaa";
s = top:: z (q::"a"*) z
z = a:: "a"
-}
\ No newline at end of file
+}
+
| ^"testcase" "{" input (grammar::Grammar) "}" /ws
| ^"tibcase" "{" input output +/ ws (grammar::Grammar) "}" /ws
| ^"tibcase" "{" input (grammar::Grammar) "}" /ws
+ | ^"javacase" "{" input output +/ ws (grammar::Grammar) "}" /ws
+ | ^"javacase" "{" input (grammar::Grammar) "}" /ws
output = ^"output" Quoted ";" /ws
input = "input" Quoted ";" /ws
// [1] http://...
//
-// consider ++bold++ and **italic**?
-// \br
// nonbreaking text?
-// ellipsis detection (...)
// degree: 15^o
// Arrows: <- -> => <= <->
// #include
// simple macros (#define) (\define)
-// today's date
// table representation
//
//////////////////////////////////////////////////////////////////////////////
-s = top:: Doc
+s = Doc
-Doc = doc:: {Header} Body /ws
-Header = header:: "header" { kv */ ws } /ws
-Body = body:: Section*/ws
+Doc = Doc:: head:{Header} body:Body /ws
+Header = Header:: "header" attrs:{ kv */ ws } /ws
+Body = sections:Section*/ws
Section = { section:: SectionHeader Paragraph* /ws }
SectionHeader = "==" SectionHeaderBody "=="
SectionHeaderBody = "=" SectionHeaderBody "="
sp = " "**
blank = !sp "\n" !sp "\n" !ws
-kv = kv1:: word "=" text /ws
+kv = kv:: key:word "=" val:text /ws
wp = w++
num = [0-9]++
Paragraph = blockquote:: { "\"\"" !ws text }
onums = nums !(". "|") ")
any = ~[]*
-uli = li:: "* " (!ws text &~ any (oli|uli))
+uli = li:: "* " (!ws text &~ any (oli|uli))
oli = li:: ("# "|onums) (!ws text &~ any (oli|uli))
-//
-
-text = text:: Item
+text = Item
Itemx = !ws Item
| ()
Item = blockquote
> "[]":: { ul:: uli+/ws } Itemx
| "[]":: { ol:: oli+/ws } Itemx
> "[]":: pre Itemx
+ > "[]":: link Itemx
> "[]":: structured Itemx
- > "[]":: structuredx Itemx
> "[]":: styled Itemx
+ > "[]":: (Chars:: alphanum++) Itemx
> "[]":: qtext Itemx
- > "[]":: (stringify:: alphanum++) Itemx
> "[]":: symbol Itemx
- > "[]":: (stringify:: sym++) Itemx
+ > "[]":: (Symbol:: sym++) Itemx
> "[]":: Paragraph Itemx
blockquote = blockquote:: "\"\"" text "\"\""
qtext = quoted:: "\"" text "\""
pre = verbatim:: "[verbatim]" { ~[]+ } /ws // FIXME doesn't work
-styled = underline:: "__" text "__"
- | footnote:: "((" text "))"
- | ( tt:: "[[" text "]]"
- | citation:: "[" text "]"
- )
+styled = underline:: "__" text "__"
+ | footnote:: "((" text "))"
+ | tt:: "[[" text "]]"
+ | citation:: "[" word "]"
| strikethrough:: "!!" text "!!"
- | superscript:: "^^" (word|block)
- | subscript:: ",," (word|block)
- | smallcap:: "\\sc" block
- | bold:: "**" text "**"
- | keyword:: "!" (word|block)
- > it:: "*" text "*"
+ | superscript:: "^^" (word|block)
+ | subscript:: ",," (word|block)
+ | smallcap:: "\\sc" block
+ | bold:: "++" text "++"
+ | keyword:: "!" (word|block)
+ | Italic:: "**" text "**"
-//
+block = { text }
+
+link = link:: text:({ text }) "->" href:(url|email)
+ > link:: text:alphanum++ !ws "->" href:(url|email)
-block = { text }
-structured = link:: { text } "->" (url|email)
- //> alphanum++ "->" (url|email) => link
-structuredx = glyph
+structured = command & "\\" [a-zA-Z0-9]++ block?
+ > glyph
> email
> url
-glyph = "(r)" | "(c)" | "(tm)" | "--" // euro symbol?
- | today:: "\\today" -> ~[a-z]
+glyph = euro:: "(e)" | "(r)" | "(c)" | "(tm)" | "--" | "..."
+command = today:: "\\today"
+ | bre:: "\\br"
// URLs //////////////////////////////////////////////////////////////////////////////
// only gets parsed once
urlpath = urlchar*
-username = stringify:: [a-zA-Z0-9;/?:&=$\-_.+]++
-password = stringify:: [a-zA-Z0-9;/?:&=$\-_.+]++
-urlchar = [a-zA-Z0-9;/?:&=$\-_.+@]
+username = [a-zA-Z0-9;/?:&=$\-_.+]++
+password = [a-zA-Z0-9;/?:&=$\-_.+]++
+urlc = [a-zA-Z0-9;/?:&=$\-_.+@]
+urlv = urlc | [%]
+urlchar = urlc
| "%":: "%" [0-9] [0-9]
-url = "mailto" ":" email
- > url:: method "://" url_login? host (":" nums)? ("/" urlpath)?
+url = "mailto" ":" email -> ~urlv
+ > method:method "://" url_login? host:host port:(":" nums)? path:("/" urlpath)? -> ~urlv
url_login = login:: username (":" password) "@"
-method = stringify:: [+\-.a-z0-9]+
-domain = domain:: (part +/ ".") -> ~"."
-part = stringify:: [a-zA-Z0-9\-]++
+method = [+\-.a-z0-9]+
+domain = (part +/ ".") -> ~"."
+part = [a-zA-Z0-9\-]++
// interesting use of boolean grammars
// &~ ([\-0-9] ~[]* | ~[]* [\-0-9])
-email = emailaddr:: username "@" host -> ~[.]
-nums = stringify:: [0-9]++
-host = ip:: nums "." nums "." nums "." nums
- | domain
+email = user:username "@" host:host -> ~[.]
+nums = [0-9]++
+host = IP:: nums "." nums "." nums "." nums
+ | DNS:: domain
// Tokens ///////////////////////////////////////////////////////////////////
-word = stringify:: alphanum++
+word = alphanum++
| quoted
quoted = "\"" ((~[\"\\] | escaped)+) "\""