librustc: Implement |A| -> B syntax for closures and make bare fn

work
This commit is contained in:
Patrick Walton 2013-10-29 15:06:13 -07:00
parent e976de32dc
commit f27272d60f
10 changed files with 320 additions and 57 deletions

View file

@ -308,10 +308,11 @@ fn bare_fn_to_str(cx: ctxt,
ident: Option<ast::Ident>,
sig: &ty::FnSig)
-> ~str {
let mut s = ~"extern ";
s.push_str(abis.to_str());
s.push_char(' ');
let mut s = if abis.is_rust() {
~""
} else {
format!("extern {} ", abis.to_str())
};
match purity {
ast::impure_fn => {}
@ -331,16 +332,16 @@ fn bare_fn_to_str(cx: ctxt,
_ => { }
}
push_sig_to_str(cx, &mut s, sig);
push_sig_to_str(cx, &mut s, '(', ')', sig);
return s;
}
fn closure_to_str(cx: ctxt, cty: &ty::ClosureTy) -> ~str
{
fn closure_to_str(cx: ctxt, cty: &ty::ClosureTy) -> ~str {
let is_proc =
(cty.sigil, cty.onceness) == (ast::OwnedSigil, ast::Once);
let is_borrowed_closure = cty.sigil == ast::BorrowedSigil;
let mut s = if is_proc {
let mut s = if is_proc || is_borrowed_closure {
~""
} else {
cty.sigil.to_str()
@ -374,23 +375,42 @@ fn closure_to_str(cx: ctxt, cty: &ty::ClosureTy) -> ~str
}
};
s.push_str("fn");
if !is_borrowed_closure {
s.push_str("fn");
}
}
if !cty.bounds.is_empty() {
s.push_str(":");
}
s.push_str(cty.bounds.repr(cx));
if !is_borrowed_closure {
// Print bounds before `fn` if this is not a borrowed closure.
if !cty.bounds.is_empty() {
s.push_str(":");
s.push_str(cty.bounds.repr(cx));
}
push_sig_to_str(cx, &mut s, &cty.sig);
push_sig_to_str(cx, &mut s, '(', ')', &cty.sig);
} else {
// Print bounds after the signature if this is a borrowed closure.
push_sig_to_str(cx, &mut s, '|', '|', &cty.sig);
if is_borrowed_closure {
if !cty.bounds.is_empty() {
s.push_str(":");
s.push_str(cty.bounds.repr(cx));
}
}
}
return s;
}
fn push_sig_to_str(cx: ctxt, s: &mut ~str, sig: &ty::FnSig) {
s.push_char('(');
fn push_sig_to_str(cx: ctxt,
s: &mut ~str,
bra: char,
ket: char,
sig: &ty::FnSig) {
s.push_char(bra);
let strs = sig.inputs.map(|a| fn_input_to_str(cx, *a));
s.push_str(strs.connect(", "));
s.push_char(')');
s.push_char(ket);
if ty::get(sig.output).sty != ty_nil {
s.push_str(" -> ");
if ty::type_is_bot(sig.output) {

View file

@ -561,6 +561,45 @@ pub fn check_reserved_keywords(&self) {
}
}
// Expect and consume a `|`. If `||` is seen, replace it with a single
// `|` and continue. If a `|` is not seen, signal an error.
fn expect_or(&self) {
match *self.token {
token::BINOP(token::OR) => self.bump(),
token::OROR => {
self.replace_token(token::BINOP(token::OR),
self.span.lo + BytePos(1),
self.span.hi)
}
_ => {
let found_token = self.token_to_str(&token::BINOP(token::OR));
self.fatal(format!("expected `{}`, found `{}`",
found_token,
self.this_token_to_str()))
}
}
}
// Parse a sequence bracketed by `|` and `|`, stopping before the `|`.
fn parse_seq_to_before_or<T>(&self,
sep: &token::Token,
f: &fn(&Parser) -> T)
-> ~[T] {
let mut first = true;
let mut vector = ~[];
while *self.token != token::BINOP(token::OR) &&
*self.token != token::OROR {
if first {
first = false
} else {
self.expect(sep)
}
vector.push(f(self))
}
vector
}
// expect and consume a GT. if a >> is seen, replace it
// with a single > and continue. If a GT is not seen,
// signal an error.
@ -761,11 +800,33 @@ pub fn id_to_str(&self, id: Ident) -> @str {
get_ident_interner().get(id.name)
}
// is this one of the keywords that signals a closure type?
pub fn token_is_closure_keyword(&self, tok: &token::Token) -> bool {
token::is_keyword(keywords::Unsafe, tok) ||
token::is_keyword(keywords::Once, tok) ||
token::is_keyword(keywords::Fn, tok)
// Is the current token one of the keywords that signals a bare function
// type?
pub fn token_is_bare_fn_keyword(&self) -> bool {
if token::is_keyword(keywords::Fn, self.token) {
return true
}
if token::is_keyword(keywords::Unsafe, self.token) ||
token::is_keyword(keywords::Once, self.token) {
return self.look_ahead(1, |t| token::is_keyword(keywords::Fn, t))
}
false
}
// Is the current token one of the keywords that signals a closure type?
pub fn token_is_closure_keyword(&self) -> bool {
token::is_keyword(keywords::Unsafe, self.token) ||
token::is_keyword(keywords::Once, self.token)
}
// Is the current token one of the keywords that signals an old-style
// closure type (with explicit sigil)?
pub fn token_is_old_style_closure_keyword(&self) -> bool {
token::is_keyword(keywords::Unsafe, self.token) ||
token::is_keyword(keywords::Once, self.token) ||
token::is_keyword(keywords::Fn, self.token)
}
pub fn token_is_lifetime(&self, tok: &token::Token) -> bool {
@ -786,15 +847,15 @@ pub fn get_lifetime(&self, tok: &token::Token) -> ast::Ident {
pub fn parse_ty_bare_fn(&self) -> ty_ {
/*
extern "ABI" [unsafe] fn <'lt> (S) -> T
^~~~^ ^~~~~~~^ ^~~~^ ^~^ ^
| | | | |
| | | | Return type
| | | Argument types
| | Lifetimes
| |
| Purity
ABI
[extern "ABI"] [unsafe] fn <'lt> (S) -> T
^~~~^ ^~~~~~~^ ^~~~^ ^~^ ^
| | | | |
| | | | Return type
| | | Argument types
| | Lifetimes
| |
| Purity
ABI
*/
@ -828,8 +889,8 @@ pub fn parse_proc_type(&self) -> ty_ {
// parse a ty_closure type
pub fn parse_ty_closure(&self,
sigil: ast::Sigil,
region: Option<ast::Lifetime>)
opt_sigil: Option<ast::Sigil>,
mut region: Option<ast::Lifetime>)
-> ty_ {
/*
@ -852,10 +913,58 @@ pub fn parse_ty_closure(&self,
let purity = self.parse_unsafety();
let onceness = parse_onceness(self);
self.expect_keyword(keywords::Fn);
let bounds = self.parse_optional_ty_param_bounds();
let (decl, lifetimes) = self.parse_ty_fn_decl();
let (sigil, decl, lifetimes, bounds) = match opt_sigil {
Some(sigil) => {
// Old-style closure syntax (`fn(A)->B`).
self.expect_keyword(keywords::Fn);
let bounds = self.parse_optional_ty_param_bounds();
let (decl, lifetimes) = self.parse_ty_fn_decl();
(sigil, decl, lifetimes, bounds)
}
None => {
// New-style closure syntax (`<'lt>|A|:K -> B`).
let lifetimes = if self.eat(&token::LT) {
let lifetimes = self.parse_lifetimes();
self.expect_gt();
// Re-parse the region here. What a hack.
if region.is_some() {
self.span_err(*self.last_span,
"lifetime declarations must precede \
the lifetime associated with a \
closure");
}
region = self.parse_opt_lifetime();
lifetimes
} else {
opt_vec::Empty
};
let inputs = if self.eat(&token::OROR) {
~[]
} else {
self.expect_or();
let inputs = self.parse_seq_to_before_or(
&token::COMMA,
|p| p.parse_arg_general(false));
self.expect_or();
inputs
};
let bounds = self.parse_optional_ty_param_bounds();
let (return_style, output) = self.parse_ret_ty();
let decl = ast::fn_decl {
inputs: inputs,
output: output,
cf: return_style,
};
(BorrowedSigil, decl, lifetimes, bounds)
}
};
return ty_closure(@TyClosure {
sigil: sigil,
@ -1120,13 +1229,23 @@ pub fn parse_ty(&self, _: bool) -> Ty {
// BORROWED POINTER
self.bump();
self.parse_borrowed_pointee()
} else if self.eat_keyword(keywords::Extern) {
// EXTERN FUNCTION
} else if self.is_keyword(keywords::Extern) ||
self.token_is_bare_fn_keyword() {
// BARE FUNCTION
self.parse_ty_bare_fn()
} else if self.token_is_closure_keyword(self.token) {
} else if self.token_is_closure_keyword() ||
*self.token == token::BINOP(token::OR) ||
*self.token == token::OROR ||
*self.token == token::LT ||
self.token_is_lifetime(self.token) {
// CLOSURE
let result = self.parse_ty_closure(ast::BorrowedSigil, None);
self.obsolete(*self.last_span, ObsoleteBareFnType);
//
// XXX(pcwalton): Eventually `token::LT` will not unambiguously
// introduce a closure, once procs can have lifetime bounds. We
// will need to refactor the grammar a little bit at that point.
let lifetime = self.parse_opt_lifetime();
let result = self.parse_ty_closure(None, lifetime);
result
} else if self.eat_keyword(keywords::Typeof) {
// TYPEOF
@ -1161,12 +1280,12 @@ pub fn parse_box_or_uniq_pointee(&self,
match *self.token {
token::LIFETIME(*) => {
let lifetime = self.parse_lifetime();
return self.parse_ty_closure(sigil, Some(lifetime));
return self.parse_ty_closure(Some(sigil), Some(lifetime));
}
token::IDENT(*) => {
if self.token_is_closure_keyword(self.token) {
return self.parse_ty_closure(sigil, None);
if self.token_is_old_style_closure_keyword() {
return self.parse_ty_closure(Some(sigil), None);
}
}
_ => {}
@ -1187,8 +1306,8 @@ pub fn parse_borrowed_pointee(&self) -> ty_ {
// look for `&'lt` or `&'foo ` and interpret `foo` as the region name:
let opt_lifetime = self.parse_opt_lifetime();
if self.token_is_closure_keyword(self.token) {
return self.parse_ty_closure(BorrowedSigil, opt_lifetime);
if self.token_is_old_style_closure_keyword() {
return self.parse_ty_closure(Some(BorrowedSigil), opt_lifetime);
}
let mt = self.parse_mt();
@ -4390,8 +4509,13 @@ fn fn_expr_lookahead(&self, tok: &token::Token) -> bool {
}
}
// parse a string as an ABI spec on an extern type or module
// Parses a string as an ABI spec on an extern type or module. Consumes
// the `extern` keyword, if one is found.
fn parse_opt_abis(&self) -> Option<AbiSet> {
if !self.eat_keyword(keywords::Extern) {
return None
}
match *self.token {
token::LIT_STR(s)
| token::LIT_STR_RAW(s, _) => {
@ -4467,7 +4591,7 @@ fn parse_item_or_view_item(&self,
});
}
// either a view item or an item:
if self.eat_keyword(keywords::Extern) {
if self.is_keyword(keywords::Extern) {
let opt_abis = self.parse_opt_abis();
if self.eat_keyword(keywords::Fn) {

View file

@ -2015,20 +2015,39 @@ pub fn print_ty_fn(s: @ps,
// function prints the sigil in the wrong place. That should be fixed.
if opt_sigil == Some(ast::OwnedSigil) && onceness == ast::Once {
word(s.s, "proc");
} else {
} else if opt_sigil == Some(ast::BorrowedSigil) {
print_extern_opt_abis(s, opt_abis);
for lifetime in opt_region.iter() {
print_lifetime(s, lifetime);
}
print_purity(s, purity);
print_onceness(s, onceness);
} else {
print_opt_abis_and_extern_if_nondefault(s, opt_abis);
print_opt_sigil(s, opt_sigil);
print_opt_lifetime(s, opt_region);
print_purity(s, purity);
print_onceness(s, onceness);
word(s.s, "fn");
}
match id { Some(id) => { word(s.s, " "); print_ident(s, id); } _ => () }
do opt_bounds.as_ref().map |bounds| { print_bounds(s, bounds, true); };
if opt_sigil != Some(ast::BorrowedSigil) {
do opt_bounds.as_ref().map |bounds| {
print_bounds(s, bounds, true);
};
}
match generics { Some(g) => print_generics(s, g), _ => () }
zerobreak(s.s);
popen(s);
if opt_sigil == Some(ast::BorrowedSigil) {
word(s.s, "|");
} else {
popen(s);
}
// It is unfortunate to duplicate the commasep logic, but we want the
// self type and the args all in the same box.
box(s, 0u, inconsistent);
@ -2041,7 +2060,14 @@ pub fn print_ty_fn(s: @ps,
print_arg(s, arg);
}
end(s);
pclose(s);
if opt_sigil == Some(ast::BorrowedSigil) {
word(s.s, "|");
opt_bounds.as_ref().map(|bounds| print_bounds(s, bounds, true));
} else {
pclose(s);
}
maybe_print_comment(s, decl.output.span.lo);
@ -2274,6 +2300,17 @@ pub fn print_opt_purity(s: @ps, opt_purity: Option<ast::purity>) {
}
}
pub fn print_opt_abis_and_extern_if_nondefault(s: @ps,
opt_abis: Option<AbiSet>) {
match opt_abis {
Some(abis) if !abis.is_rust() => {
word_nbsp(s, "extern");
word_nbsp(s, abis.to_str());
}
Some(_) | None => {}
};
}
pub fn print_extern_opt_abis(s: @ps, opt_abis: Option<AbiSet>) {
match opt_abis {
Some(abis) => {

View file

@ -19,5 +19,5 @@ fn g(f: extern fn(&fn())) {
}
f(g);
//~^ ERROR mismatched types: expected `extern "Rust" fn(extern "Rust" fn(extern "Rust" fn()))`
//~^ ERROR mismatched types: expected `fn(fn(fn()))`
}

View file

@ -0,0 +1,13 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
fn call_bare(f: fn(&str)) {
f("Hello ");
}
fn main() {
let string = "world!";
let f: |&str| = |s| println(s + string);
call_bare(f) //~ ERROR mismatched types
}

View file

@ -0,0 +1,17 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/publicdomain/zero/1.0/
// pp-exact
fn call_it(f: proc(~str) -> ~str) { }
fn call_this(f: |&str|: Send) { }
fn call_that(f: <'a>|&'a int, &'a int|: -> int) { }
fn call_extern(f: fn() -> int) { }
fn call_abid_extern(f: extern "C" fn() -> int) { }
pub fn main() { }

View file

@ -14,7 +14,7 @@
// preserved. They are needed to disambiguate `{return n+1}; - 0` from
// `({return n+1}-0)`.
fn id(f: &fn() -> int) -> int { f() }
fn id(f: || -> int) -> int { f() }
fn wsucc(_n: int) -> int { (do id || { 1 }) - 0 }
fn main() { }

View file

@ -10,6 +10,6 @@
// pp-exact
fn f(f: &fn(int)) { f(10) }
fn f(f: |int|) { f(10) }
fn main() { do f |i| { assert!(i == 10) } }

View file

@ -10,7 +10,7 @@
// pp-exact
fn from_foreign_fn(_x: extern "Rust" fn()) { }
fn from_stack_closure(_x: &fn()) { }
fn from_foreign_fn(_x: fn()) { }
fn from_stack_closure(_x: ||) { }
fn from_unique_closure(_x: ~fn()) { }
fn main() { }

View file

@ -1,11 +1,42 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
use std::cast;
fn call_it(f: proc(~str) -> ~str) {
println(f(~"Fred"))
}
fn call_a_thunk(f: ||) {
f();
}
fn call_this(f: |&str|:Send) {
f("Hello!");
}
fn call_that(f: <'a>|&'a int, &'a int|: -> int) {
let (ten, forty_two) = (10, 42);
println!("Your lucky number is {}", f(&ten, &forty_two));
}
fn call_cramped(f:||->uint,g:<'a>||->&'a uint) {
let number = f();
let other_number = *g();
println!("Ticket {} wins an all-expenses-paid trip to Mountain View", number + other_number);
}
fn call_bare(f: fn(&str)) {
f("Hello world!")
}
fn call_bare_again(f: extern "Rust" fn(&str)) {
f("Goodbye world!")
}
pub fn main() {
// Procs
let greeting = ~"Hi ";
do call_it |s| {
greeting + s
@ -23,5 +54,26 @@ pub fn main() {
call_it(proc(s: ~str) -> ~str {
greeting + s
});
// Closures
call_a_thunk(|| println("Hello world!"));
call_this(|s| println(s));
call_that(|x, y| *x + *y);
let z = 100;
call_that(|x, y| *x + *y - z);
call_cramped(|| 1, || unsafe {
cast::transmute(&100)
});
// External functions
call_bare(println);
call_bare_again(println);
}