various annoying things to get macros working

This commit is contained in:
Niko Matsakis 2015-07-15 09:35:24 -04:00
parent bfe72e14fc
commit a1da099572
16 changed files with 4922 additions and 2581 deletions

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,7 @@ extern token {
"+" => Tok::Plus(..),
"*" => Tok::Times(..),
"/" => Tok::Div(..),
"," => Tok::Comma(..),
"Num" => Tok::Num(<i32>)
}
}
@ -21,9 +22,15 @@ pub Expr: &'ast Node<'ast> = {
Factor;
};
Comma<T>: Vec<T> = {
<h:(<T> ",")*> <t:T?> =>
h.into_iter().chain(t).collect();
};
Factor = {
<l:Factor> "*" <r:Term> => arena.alloc(Node::Binary(Op::Mul, l, r));
<l:Factor> "/" <r:Term> => arena.alloc(Node::Binary(Op::Div, l, r));
"*" "(" <Comma<Expr>> ")" => arena.alloc(Node::Reduce(Op::Mul, <>));
Term;
};

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,7 @@ pub enum Op {
pub enum Node<'ast> {
Value(i32),
Binary(Op, &'ast Node<'ast>, &'ast Node<'ast>),
Reduce(Op, Vec<&'ast Node<'ast>>),
}
pub struct Arena<'ast> {

View File

@ -4,10 +4,6 @@ mod expr_arena_ast;
mod sub;
mod util;
fn main() {
println!("Hello, world!");
}
#[test]
fn expr_test1() {
util::test(|v| expr::parse_Expr(1, v), "22 - 3", 22 - 3);
@ -51,7 +47,7 @@ fn sub_test3() {
#[test]
fn expr_arena_test1() {
use expr_arena_ast::*;
let mut arena = Arena::new();
let arena = Arena::new();
let expected =
arena.alloc(Node::Binary(Op::Sub,
arena.alloc(Node::Binary(Op::Mul,
@ -61,3 +57,16 @@ fn expr_arena_test1() {
util::test(|v| expr_arena::parse_Expr(&arena, v), "22 * 3 - 6", expected);
}
#[test]
fn expr_arena_test2() {
use expr_arena_ast::*;
let arena = Arena::new();
let expected =
arena.alloc(Node::Reduce(Op::Mul,
vec![arena.alloc(Node::Value(22)),
arena.alloc(Node::Value(3)),
arena.alloc(Node::Value(6))]));;
util::test(|v| expr_arena::parse_Expr(&arena, v), "*(22, 3, 6)", expected);
util::test(|v| expr_arena::parse_Expr(&arena, v), "*(22, 3, 6,)", expected);
}

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,7 @@ pub enum Tok {
Plus,
Times,
Div,
Comma,
}
// simplest and stupidest possible tokenizer
@ -25,6 +26,7 @@ pub fn tokenize(s: &str) -> Vec<Tok> {
'-' => tokens.push(Tok::Minus),
'+' => tokens.push(Tok::Plus),
'*' => tokens.push(Tok::Times),
',' => tokens.push(Tok::Comma),
'/' => tokens.push(Tok::Div),
_ if c.is_digit(10) => {
let (tmp, next) = take_while(c, &mut chars, |c| c.is_digit(10));

View File

@ -135,6 +135,9 @@ fn emit_recursive_ascent(output_path: &Path, grammar: &r::Grammar) -> io::Result
let output_file = try!(fs::File::create(output_path));
let mut rust = RustWrite::new(output_file);
// often some of the uses are not used here
rust!(rust, "#![allow(unused_imports)]");
try!(emit_uses(grammar, &mut rust));
if grammar.start_nonterminals.is_empty() {

View File

@ -5,10 +5,11 @@ some pre-expansion and so forth before creating the proper AST.
*/
use intern::{InternedString};
use intern::{intern, InternedString};
use grammar::repr::{NominalTypeRepr, TypeRepr};
use grammar::pattern::Pattern;
use std::fmt::{Debug, Display, Formatter, Error};
use std::iter::once;
use util::Sep;
#[derive(Clone, Debug, PartialEq, Eq)]
@ -49,6 +50,12 @@ pub struct Conversion {
pub to: Pattern<TypeRef>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Path {
pub absolute: bool,
pub ids: Vec<InternedString>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TypeRef {
// (T1, T2)
@ -56,7 +63,7 @@ pub enum TypeRef {
// Foo<'a, 'b, T1, T2>, Foo::Bar, etc
Nominal {
path: Vec<InternedString>,
path: Path,
types: Vec<TypeRef>
},
@ -240,6 +247,14 @@ impl Display for TerminalString {
}
}
impl Display for Path {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
write!(fmt, "{}{}",
if self.absolute {"::"} else {""},
Sep("::", &self.ids))
}
}
impl Debug for TerminalString {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
Display::fmt(self, fmt)
@ -346,9 +361,9 @@ impl Display for TypeRef {
TypeRef::Tuple(ref types) =>
write!(fmt, "({})", Sep(", ", types)),
TypeRef::Nominal { ref path, ref types } if types.len() == 0 =>
write!(fmt, "{}", Sep("::", path)),
write!(fmt, "{}", path),
TypeRef::Nominal { ref path, ref types } =>
write!(fmt, "{}<{}>", Sep("::", path), Sep(", ", types)),
write!(fmt, "{}<{}>", path, Sep(", ", types)),
TypeRef::Lifetime(ref s) =>
write!(fmt, "{}", s),
TypeRef::Id(ref s) =>
@ -384,7 +399,7 @@ impl TypeRef {
TypeRepr::Lifetime(id),
TypeRef::Id(id) =>
TypeRepr::Nominal(NominalTypeRepr {
path: vec![id],
path: Path::from_id(id),
types: vec![]
}),
TypeRef::OfSymbol(_) =>
@ -396,3 +411,41 @@ impl TypeRef {
}
}
}
impl Path {
pub fn from_id(id: InternedString) -> Path {
Path {
absolute: false,
ids: vec![id]
}
}
pub fn vec() -> Path {
Path {
absolute: true,
ids: vec![intern("std"), intern("vec"), intern("Vec")]
}
}
pub fn option() -> Path {
Path {
absolute: true,
ids: vec![intern("std"), intern("option"), intern("Option")]
}
}
pub fn as_id(&self) -> Option<InternedString> {
if !self.absolute && self.ids.len() == 1 {
Some(self.ids[0])
} else {
None
}
}
pub fn append(&self, id: InternedString) -> Path {
Path {
absolute: self.absolute,
ids: self.ids.iter().cloned().chain(once(id)).collect()
}
}
}

View File

@ -7,7 +7,7 @@ representations.
*/
use intern::{InternedString};
use grammar::parse_tree::Span;
use grammar::parse_tree::{Path, Span};
use std::fmt::{Display, Formatter, Error};
use util::Sep;
@ -26,9 +26,9 @@ pub struct FieldPattern<T> {
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PatternKind<T> {
Enum(Vec<InternedString>, Vec<Pattern<T>>),
Struct(Vec<InternedString>, Vec<FieldPattern<T>>, /* trailing ..? */ bool),
Path(Vec<InternedString>),
Enum(Path, Vec<Pattern<T>>),
Struct(Path, Vec<FieldPattern<T>>, /* trailing ..? */ bool),
Path(Path),
Tuple(Vec<Pattern<T>>),
Underscore,
DotDot,
@ -96,15 +96,15 @@ impl<T:Display> Display for PatternKind<T> {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
match *self {
PatternKind::Path(ref path) =>
write!(fmt, "{}", Sep("::", path)),
write!(fmt, "{}", path),
PatternKind::Enum(ref path, ref pats) =>
write!(fmt, "{}({})", Sep("::", path), Sep(", ", pats)),
write!(fmt, "{}({})", path, Sep(", ", pats)),
PatternKind::Struct(ref path, ref fields, false) =>
write!(fmt, "{} {{ {} }}", Sep("::", path), Sep(", ", fields)),
write!(fmt, "{} {{ {} }}", path, Sep(", ", fields)),
PatternKind::Struct(ref path, ref fields, true) if fields.len() == 0 =>
write!(fmt, "{} {{ .. }}", Sep("::", path)),
write!(fmt, "{} {{ .. }}", path),
PatternKind::Struct(ref path, ref fields, true) =>
write!(fmt, "{} {{ {}, .. }}", Sep("::", path), Sep(", ", fields)),
write!(fmt, "{} {{ {}, .. }}", path, Sep(", ", fields)),
PatternKind::Tuple(ref paths) =>
write!(fmt, "({})", Sep(", ", paths)),
PatternKind::Underscore =>

View File

@ -6,12 +6,12 @@
use intern::{InternedString};
use grammar::pattern::{Pattern, PatternKind};
use std::iter::once;
use std::fmt::{Debug, Display, Formatter, Error};
use util::{map, Map, Sep};
// These concepts we re-use wholesale
pub use grammar::parse_tree::{NonterminalString,
Path,
Span,
TerminalString, TypeParameter};
@ -92,7 +92,7 @@ pub enum TypeRepr {
#[derive(Clone, PartialEq, Eq)]
pub struct NominalTypeRepr {
pub path: Vec<InternedString>,
pub path: Path,
pub types: Vec<TypeRepr>
}
@ -173,9 +173,9 @@ impl Debug for TypeRepr {
impl Display for NominalTypeRepr {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
if self.types.len() == 0 {
write!(fmt, "{}", Sep("::", &self.path))
write!(fmt, "{}", self.path)
} else {
write!(fmt, "{}<{}>", Sep("::", &self.path), Sep(", ", &self.types))
write!(fmt, "{}<{}>", self.path, Sep(", ", &self.types))
}
}
}
@ -253,11 +253,7 @@ impl ActionFnDefn {
impl Grammar {
pub fn default_pattern(&self, id: InternedString) -> Pattern<TypeRepr> {
let path: Vec<InternedString> =
self.types.terminal_enum_type().path.iter()
.cloned()
.chain(once(id))
.collect();
let path = self.types.terminal_enum_type().path.append(id);
Pattern {
span: self.token_span,
kind: PatternKind::Enum(path, vec![

View File

@ -2,14 +2,11 @@
//!
//! [recursive ascent]: https://en.wikipedia.org/wiki/Recursive_ascent_parser
use intern::{InternedString};
use grammar::repr::{Grammar, NonterminalString, Symbol, TerminalString, Types};
use lr1::{Lookahead, State, StateIndex};
use rust::RustWrite;
use std::io::{self, Write};
use util::Sep;
pub type Path = Vec<InternedString>;
use util::{Escape, Sep};
pub fn compile<'grammar,W:Write>(
grammar: &'grammar Grammar,
@ -61,7 +58,10 @@ impl<'ascent,'grammar,W:Write> RecursiveAscent<'ascent,'grammar,W> {
rust!(self.out, "mod {}parse{} {{",
self.prefix, self.start_symbol);
rust!(self.out, "#![allow(non_snake_case, unused_mut, unused_variables)]");
// these stylistic lints are annoying for the generated code,
// which doesn't follow conventions:
rust!(self.out, "#![allow(non_snake_case, non_camel_case_types, \
unused_mut, unused_variables, unused_imports)]");
rust!(self.out, "");
try!(self.write_uses());
@ -96,7 +96,7 @@ impl<'ascent,'grammar,W:Write> RecursiveAscent<'ascent,'grammar,W> {
// have to unwrap and rewrap as we pass up the stack, which
// seems silly
for &nt in self.grammar.productions.keys() {
rust!(self.out, "{}({}),", nt, self.types.nonterminal_type(nt));
rust!(self.out, "{}({}),", Escape(nt), self.types.nonterminal_type(nt));
}
rust!(self.out, "}}");
@ -122,7 +122,7 @@ impl<'ascent,'grammar,W:Write> RecursiveAscent<'ascent,'grammar,W> {
self.grammar.user_parameter_refs(), self.prefix, self.prefix);
rust!(self.out, "({}lookahead, {}parse{}::{}Nonterminal::{}({}nt)) => \
Ok(({}lookahead, {}nt)),",
self.prefix, self.prefix, self.start_symbol, self.prefix, self.start_symbol,
self.prefix, self.prefix, self.start_symbol, self.prefix, Escape(self.start_symbol),
self.prefix, self.prefix, self.prefix);
rust!(self.out, "_ => unreachable!(),");
rust!(self.out, "}}");
@ -241,11 +241,12 @@ impl<'ascent,'grammar,W:Write> RecursiveAscent<'ascent,'grammar,W> {
if !transfer_syms.is_empty() {
// if we popped anything off of the stack, then this frame is done
rust!(self.out, "return Ok(({}lookahead, {}Nonterminal::{}({}nt)));",
self.prefix, self.prefix, production.nonterminal, self.prefix);
self.prefix, self.prefix, Escape(production.nonterminal), self.prefix);
} else {
// otherwise, pop back
rust!(self.out, "result = ({}lookahead, {}Nonterminal::{}({}nt));",
self.prefix, self.prefix, production.nonterminal, self.prefix);
rust!(self.out, "{}result = ({}lookahead, {}Nonterminal::{}({}nt));",
self.prefix, self.prefix, self.prefix,
Escape(production.nonterminal), self.prefix);
fallthrough = true;
}
@ -272,7 +273,8 @@ impl<'ascent,'grammar,W:Write> RecursiveAscent<'ascent,'grammar,W> {
rust!(self.out, "match {}nt {{", self.prefix);
for (&nt, &next_index) in &this_state.gotos {
rust!(self.out, "{}Nonterminal::{}({}nt) => {{", self.prefix, nt, self.prefix);
rust!(self.out, "{}Nonterminal::{}({}nt) => {{",
self.prefix, Escape(nt), self.prefix);
rust!(self.out, "let {}sym{} = &mut Some({}nt);",
self.prefix, this_prefix.len(), self.prefix);
let transition = self.transition(this_prefix, next_index, "lookahead", "tokens");

View File

@ -1,8 +1,15 @@
use std::collections::{HashMap, HashSet};
use intern::{intern, read, InternedString};
use grammar::parse_tree::{Alternative, Condition, ConditionOp, ExprSymbol, Grammar, GrammarItem,
MacroSymbol, NonterminalData, NonterminalString, RepeatOp, RepeatSymbol,
Span, Symbol, SymbolKind, TypeRef};
use grammar::parse_tree::{Alternative,
Condition, ConditionOp,
ExprSymbol,
Grammar, GrammarItem,
MacroSymbol,
NonterminalData, NonterminalString,
Path,
RepeatOp, RepeatSymbol,
Span, Symbol, SymbolKind,
TypeRef};
use normalize::{NormResult, NormError};
use normalize::norm_util::{self, Symbols};
use regex::Regex;
@ -212,7 +219,8 @@ impl MacroExpander {
TypeRef::Id(id) => {
match args.get(&NonterminalString(id)) {
Some(sym) => TypeRef::OfSymbol(sym.clone()),
None => TypeRef::Nominal { path: vec![id], types: vec![] },
None => TypeRef::Nominal { path: Path::from_id(id),
types: vec![] },
}
}
}
@ -351,7 +359,7 @@ impl MacroExpander {
match repeat.op {
RepeatOp::Star => {
let path = vec![intern("std"), intern("vec"), intern("Vec")];
let path = Path::vec();
let ty_ref = TypeRef::Nominal { path: path, types: vec![base_symbol_ty] };
Ok(GrammarItem::Nonterminal(NonterminalData {
@ -394,7 +402,7 @@ impl MacroExpander {
}
RepeatOp::Plus => {
let path = vec![intern("std"), intern("vec"), intern("Vec")];
let path = Path::vec();
let ty_ref = TypeRef::Nominal { path: path, types: vec![base_symbol_ty] };
Ok(GrammarItem::Nonterminal(NonterminalData {
@ -432,7 +440,7 @@ impl MacroExpander {
}
RepeatOp::Question => {
let path = vec![intern("std"), intern("option"), intern("Option")];
let path = Path::option();
let ty_ref = TypeRef::Nominal { path: path, types: vec![base_symbol_ty] };
Ok(GrammarItem::Nonterminal(NonterminalData {

View File

@ -2,9 +2,13 @@ use super::{NormResult, NormError};
use super::norm_util::{self, AlternativeAction, Symbols};
use std::collections::{HashMap};
use grammar::parse_tree::{Alternative, EnumToken, Grammar, GrammarItem,
use grammar::parse_tree::{Alternative,
EnumToken,
Grammar, GrammarItem,
NonterminalData, NonterminalString,
Span, SymbolKind, TypeRef};
Path,
Span,
SymbolKind, TypeRef};
use grammar::repr::{NominalTypeRepr, Types, TypeRepr};
#[cfg(test)]
@ -199,7 +203,7 @@ impl<'grammar> TypeInferencer<'grammar> {
Ok(TypeRepr::Lifetime(id))
}
TypeRef::Id(id) => {
Ok(TypeRepr::Nominal(NominalTypeRepr { path: vec![id],
Ok(TypeRepr::Nominal(NominalTypeRepr { path: Path::from_id(id),
types: vec![] }))
}
TypeRef::Ref { lifetime, mutable, ref referent } => {

View File

@ -2,16 +2,15 @@ use intern::{intern, InternedString};
use grammar::parse_tree::*;
use grammar::pattern::*;
use rusty_peg;
use std::iter;
#[cfg(test)]
mod test;
fn make_list<T>(head: Vec<(T, &'static str)>,
tail: Option<(T, Option<&'static str>)>)
tail: Option<T>)
-> Vec<T> {
head.into_iter().map(|(a,_)| a)
.chain(tail.map(|(a, _)| a).into_iter())
.chain(tail)
.collect()
}
@ -32,7 +31,7 @@ rusty_peg! {
};
GRAMMAR_TPS: Vec<TypeParameter> =
("<" <h:{TYPE_PARAMETER ","}> <t:[TYPE_PARAMETER [","]]> ">") => make_list(h, t);
("<" <h:{TYPE_PARAMETER ","}> <t:[TYPE_PARAMETER]> ">") => make_list(h, t);
TYPE_PARAMETER: TypeParameter =
(LIFETIME_TYPE_PARAMETER / ID_TYPE_PARAMETER);
@ -44,13 +43,13 @@ rusty_peg! {
(<l:ID>) => TypeParameter::Id(l);
GRAMMAR_PARAMS: Vec<Parameter> =
("(" <h:{GRAMMAR_PARAM ","}> <t:[GRAMMAR_PARAM [","]]> ")") => make_list(h, t);
("(" <h:{GRAMMAR_PARAM ","}> <t:[GRAMMAR_PARAM]> ")") => make_list(h, t);
GRAMMAR_PARAM: Parameter =
(<id:ID> ":" <t:TYPE_REF>) => Parameter { name: id, ty: t };
WHERE_CLAUSES: Vec<String> =
("where" <h:{TYPE ","}> <t:[TYPE [","]]>) => make_list(h, t);
("where" <h:{TYPE ","}> <t:[TYPE]>) => make_list(h, t);
GRAMMAR_ITEM: GrammarItem =
(EXTERN_TOKEN / NONTERMINAL / USE);
@ -83,7 +82,7 @@ rusty_peg! {
(<a:ESCAPE>) => (NonterminalString(a), vec![]);
NONTERMINAL_NAME_MACRO: (NonterminalString, Vec<NonterminalString>) =
(<a:NONTERMINAL_ID> "<" <b:{NONTERMINAL_ID ","}> <c:[NONTERMINAL_ID [","]]> ">") => {
(<a:NONTERMINAL_ID> "<" <b:{NONTERMINAL_ID ","}> <c:[NONTERMINAL_ID]> ">") => {
(a, make_list(b, c))
};
@ -157,7 +156,7 @@ rusty_peg! {
(MACRO_SYMBOL / TERMINAL_SYMBOL / NT_SYMBOL / ESCAPE_SYMBOL / PAREN_SYMBOL);
MACRO_SYMBOL: Symbol =
(<lo:POSL> <l:MACRO_ID> <m:{SYMBOL ","}> <n:[SYMBOL [","]]> ">" <hi:POSR>) => {
(<lo:POSL> <l:MACRO_ID> <m:{SYMBOL ","}> <n:[SYMBOL]> ">" <hi:POSR>) => {
Symbol::new(Span(lo, hi),
SymbolKind::Macro(MacroSymbol { name: l,
args: make_list(m, n) }))
@ -213,14 +212,15 @@ rusty_peg! {
NOMINAL_TYPE_REF: TypeRef =
(<p:PATH> <a:[NOMINAL_TYPE_REF_ARGS]>) => {
if p.len() == 1 && a.is_none() {
// detect something like `Foo` and treat it specially,
// so that macro expansion can pattern match here
TypeRef::Id(p.into_iter().next().unwrap())
} else {
// otherwise, `Vec<..>` or `Foo::Bar` etc expand to
// this full path
TypeRef::Nominal { path: p, types: a.unwrap_or(vec![]) }
match p.as_id() {
Some(id) if a.is_none() =>
// detect something like `Foo` and treat it specially,
// so that macro expansion can pattern match here
TypeRef::Id(id),
_ =>
// otherwise, `Vec<..>` or `Foo::Bar` etc expand to
// this full path
TypeRef::Nominal { path: p, types: a.unwrap_or(vec![]) }
}
};
@ -237,22 +237,18 @@ rusty_peg! {
TYPE_REF_COMMA: TypeRef =
(<t:TYPE_REF> ",") => t;
PATH: Vec<InternedString> =
(<b:{PATH_BASE}> <c:ID>) => {
let mut b = b;
b.push(c);
b
PATH: Path =
(<a:["::"]> <h:{ID "::"}> <t:ID>) => {
Path { absolute: a.is_some(),
ids: make_list(h, Some(t)) }
};
PATH_BASE: InternedString =
(<i:ID> "::") => i;
// TOKEN DEFINITIONS
EXTERN_TOKEN: GrammarItem =
("extern" "token" "{"
"enum" <lo:POSL> <t:TYPE_REF> <hi:POSR> "{"
<c0:{CONVERSION ","}> <c1:[CONVERSION [","]]>
<c0:{CONVERSION ","}> <c1:[CONVERSION]>
"}"
"}") => {
GrammarItem::ExternToken(ExternToken {
@ -274,14 +270,14 @@ rusty_peg! {
DOTDOT_PATTERN / CHOOSE_PATTERN / TUPLE_PATTERN / PATH_PATTERN);
ENUM_PATTERN: Pattern<TypeRef> =
(<lo:POSL> <p:PATH> "(" <s0:{PATTERN ","}> <s1:[PATTERN [","]]> ")" <hi:POSR>) => {
(<lo:POSL> <p:PATH> "(" <s0:{PATTERN ","}> <s1:[PATTERN]> ")" <hi:POSR>) => {
Pattern { span: Span(lo, hi),
kind: PatternKind::Enum(p, make_list(s0, s1)) }
};
STRUCT_PATTERN0: Pattern<TypeRef> =
(<lo:POSL> <p:PATH> "{" <s0:{FIELD_PATTERN ","}>
<s1:[FIELD_PATTERN [","]]> "}" <hi:POSR>) => {
<s1:[FIELD_PATTERN]> "}" <hi:POSR>) => {
Pattern { span: Span(lo, hi),
kind: PatternKind::Struct(p,
make_list(s0, s1),
@ -322,17 +318,14 @@ rusty_peg! {
};
TUPLE_PATTERN: Pattern<TypeRef> =
(<lo:POSL> "(" <p0:{PATTERN ","}> <p1:[PATTERN [","]]> ")" <hi:POSR>) => {
(<lo:POSL> "(" <p0:{PATTERN ","}> <p1:[PATTERN]> ")" <hi:POSR>) => {
Pattern { span: Span(lo, hi),
kind: PatternKind::Tuple(make_list(p0, p1)) }
};
PATH_PATTERN: Pattern<TypeRef> =
(<lo:POSL> <id0:ID> <id1:{"::" ID}> <hi:POSR>) => {
let path = iter::once(id0).chain(id1.into_iter().map(|pair| pair.1))
.collect();
Pattern { span: Span(lo, hi),
kind: PatternKind::Path(path) }
(<lo:POSL> <p:PATH> <hi:POSR>) => {
Pattern { span: Span(lo, hi), kind: PatternKind::Path(p) }
};
// IDENTIFIERS, LIFETIMES

View File

@ -19,6 +19,23 @@ impl<'a,S:Display> Display for Sep<&'a Vec<S>> {
}
}
pub struct Escape<S>(pub S);
impl<S:Display> Display for Escape<S> {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
let tmp = format!("{}", self.0);
for c in tmp.chars() {
match c {
'a' ... 'z' | '_' | '0' ... '9' | 'A' ... 'Z' =>
try!(write!(fmt, "{}", c)),
_ =>
try!(write!(fmt, "_")), // um, obviously not the best escaping :)
}
}
Ok(())
}
}
pub struct Prefix<S>(pub &'static str, pub S);
impl<'a,S:Display> Display for Prefix<&'a [S]> {