1
Fork 0

Beginnings of front-end support for classes

Added class support to the parser, prettyprinter, fold, and visit.
(See Issue 1726.)

This is WIP -- the test case is xfailed, and attempting to compile
it will error out in resolve.
This commit is contained in:
Tim Chevalier 2012-01-31 19:30:40 -08:00
parent d65eabd5de
commit f3343b3571
10 changed files with 294 additions and 10 deletions

View file

@ -119,6 +119,9 @@ fn encode_module_item_paths(ebml_w: ebml::writer, module: _mod, path: [str],
encode_def_id(ebml_w, local_def(it.id));
ebml::end_tag(ebml_w);
}
item_class(_,_,_,_) {
fail "encode: implement item_class";
}
item_enum(variants, tps) {
add_to_index(ebml_w, path, index, it.ident);
ebml::start_tag(ebml_w, tag_paths_data_item);
@ -340,6 +343,9 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item,
ebml::end_tag(ebml_w);
encode_enum_variant_info(ecx, ebml_w, item.id, variants, index, tps);
}
item_class(_,_,_,_) {
fail "encode: implement item_class";
}
item_res(_, tps, _, _, ctor_id) {
let fn_ty = node_id_to_type(tcx, ctor_id);

View file

@ -1421,6 +1421,9 @@ fn index_mod(md: ast::_mod) -> mod_index {
variant_idx += 1u;
}
}
ast::item_class(_, items, ctor_decl, _) {
fail "resolve::index_mod: item_class";
}
}
}
ret index;

View file

@ -60,6 +60,9 @@ fn find_pre_post_item(ccx: crate_ctxt, i: item) {
ccx: ccx};
find_pre_post_fn(fcx, body);
}
item_class(_,_,_,_) {
fail "find_pre_post_item: implement item_class";
}
item_impl(_, _, _, ms) { for m in ms { find_pre_post_method(ccx, m); } }
}
}

View file

@ -426,6 +426,9 @@ fn ty_of_item(tcx: ty::ctxt, mode: mode, it: @ast::item)
tcx.tcache.insert(local_def(it.id), tpt);
ret tpt;
}
ast::item_class(_,_,_,_) {
fail "ty_of_item: implement item_class";
}
ast::item_impl(_, _, _, _) | ast::item_mod(_) |
ast::item_native_mod(_) { fail; }
}
@ -1232,7 +1235,7 @@ fn check_pat(fcx: @fn_ctxt, map: pat_util::pat_id_map, pat: @ast::pat,
if subpats_len == 1u { "" } else { "s" },
arg_len,
if arg_len == 1u { "" } else { "s" }];
tcx.sess.span_fatal(pat.span, s);
tcx.sess.span_err(pat.span, s);
}
vec::iter2(subpats, arg_types) {|subpat, arg_ty|
@ -1240,7 +1243,7 @@ fn check_pat(fcx: @fn_ctxt, map: pat_util::pat_id_map, pat: @ast::pat,
}
} else if subpats_len > 0u {
// TODO: note definition of enum variant
tcx.sess.span_fatal
tcx.sess.span_err
(pat.span, #fmt["this pattern has %u field%s, \
but the corresponding \
variant has no fields",
@ -1252,7 +1255,7 @@ fn check_pat(fcx: @fn_ctxt, map: pat_util::pat_id_map, pat: @ast::pat,
_ {
// FIXME: Switch expected and actual in this message? I
// can never tell.
tcx.sess.span_fatal
tcx.sess.span_err
(pat.span,
#fmt["mismatched types: expected `%s` but found enum",
ty_to_str(tcx, expected)]);

View file

@ -472,11 +472,29 @@ enum item_ {
item_enum([variant], [ty_param]),
item_res(fn_decl /* dtor */, [ty_param], blk,
node_id /* dtor id */, node_id /* ctor id */),
item_class([ty_param], /* ty params for class */
[@class_item], /* methods, etc. */
/* (not including ctor) */
fn_decl, /* ctor decl */
blk /* ctor body */
),
item_iface([ty_param], [ty_method]),
item_impl([ty_param], option<@ty> /* iface */,
@ty /* self */, [@method]),
}
type class_item_ = {privacy: privacy, decl: @class_member};
type class_item = spanned<class_item_>;
enum class_member {
instance_var(ident, @ty, class_mutability, node_id),
class_method(@item)
}
enum class_mutability { class_mutable, class_immutable }
enum privacy { priv, pub }
type native_item =
{ident: ident,
attrs: [attribute],

View file

@ -27,6 +27,7 @@ type ast_fold_precursor =
fold_view_item: fn@(view_item_, ast_fold) -> view_item_,
fold_native_item: fn@(&&@native_item, ast_fold) -> @native_item,
fold_item: fn@(&&@item, ast_fold) -> @item,
fold_class_item: fn@(&&@class_item, ast_fold) -> @class_item,
fold_item_underscore: fn@(item_, ast_fold) -> item_,
fold_method: fn@(&&@method, ast_fold) -> @method,
fold_block: fn@(blk_, span, ast_fold) -> (blk_, span),
@ -53,6 +54,7 @@ type a_f =
fold_view_item: fn@(&&@view_item) -> @view_item,
fold_native_item: fn@(&&@native_item) -> @native_item,
fold_item: fn@(&&@item) -> @item,
fold_class_item: fn@(&&@class_item) -> @class_item,
fold_item_underscore: fn@(item_) -> item_,
fold_method: fn@(&&@method) -> @method,
fold_block: fn@(blk) -> blk,
@ -82,6 +84,7 @@ fn nf_crate_directive_dummy(&&_c: @crate_directive) -> @crate_directive {
fn nf_view_item_dummy(&&_v: @view_item) -> @view_item { fail; }
fn nf_native_item_dummy(&&_n: @native_item) -> @native_item { fail; }
fn nf_item_dummy(&&_i: @item) -> @item { fail; }
fn nf_class_item_dummy(&&_ci: @class_item) -> @class_item { fail; }
fn nf_item_underscore_dummy(_i: item_) -> item_ { fail; }
fn nf_method_dummy(&&_m: @method) -> @method { fail; }
fn nf_blk_dummy(_b: blk) -> blk { fail; }
@ -220,6 +223,20 @@ fn noop_fold_item(&&i: @item, fld: ast_fold) -> @item {
span: i.span};
}
fn noop_fold_class_item(&&ci: @class_item, fld: ast_fold)
-> @class_item {
@{node: {
privacy:ci.node.privacy,
decl:
@alt *ci.node.decl {
instance_var(ident, t, cm, id) {
instance_var(ident, fld.fold_ty(t), cm, id)
}
class_method(i) { class_method(fld.fold_item(i)) }
}},
span: ci.span}
}
fn noop_fold_item_underscore(i: item_, fld: ast_fold) -> item_ {
ret alt i {
item_const(t, e) { item_const(fld.fold_ty(t), fld.fold_expr(e)) }
@ -233,6 +250,12 @@ fn noop_fold_item_underscore(i: item_, fld: ast_fold) -> item_ {
item_enum(variants, typms) {
item_enum(vec::map(variants, fld.fold_variant), typms)
}
item_class(typms, items, ctor_decl, ctor_body) {
item_class(typms,
vec::map(items, fld.fold_class_item),
fold_fn_decl(ctor_decl, fld),
fld.fold_block(ctor_body))
}
item_impl(tps, ifce, ty, methods) {
item_impl(tps, option::map(ifce, fld.fold_ty), fld.fold_ty(ty),
vec::map(methods, fld.fold_method))
@ -518,6 +541,7 @@ fn default_ast_fold() -> @ast_fold_precursor {
fold_view_item: noop_fold_view_item,
fold_native_item: noop_fold_native_item,
fold_item: noop_fold_item,
fold_class_item: noop_fold_class_item,
fold_item_underscore: noop_fold_item_underscore,
fold_method: noop_fold_method,
fold_block: wrap(noop_fold_block),
@ -548,6 +572,7 @@ fn make_fold(afp: ast_fold_precursor) -> ast_fold {
fold_view_item: bind nf_view_item_dummy(_),
fold_native_item: bind nf_native_item_dummy(_),
fold_item: bind nf_item_dummy(_),
fold_class_item: bind nf_class_item_dummy(_),
fold_item_underscore: bind nf_item_underscore_dummy(_),
fold_method: bind nf_method_dummy(_),
fold_block: bind nf_blk_dummy(_),
@ -591,6 +616,21 @@ fn make_fold(afp: ast_fold_precursor) -> ast_fold {
fn f_item(afp: ast_fold_precursor, f: ast_fold, &&i: @item) -> @item {
ret afp.fold_item(i, f);
}
fn f_class_item(afp: ast_fold_precursor, f: ast_fold,
&&ci: @class_item) -> @class_item {
@{node:
{privacy:ci.node.privacy,
decl:
@alt *ci.node.decl {
instance_var(nm, t, mt, id) {
instance_var(nm, f_ty(afp, f, t),
mt, id)
}
class_method(i) {
class_method(afp.fold_item(i, f))
}
}}, span: ci.span}
}
fn f_item_underscore(afp: ast_fold_precursor, f: ast_fold, i: item_) ->
item_ {
ret afp.fold_item_underscore(i, f);
@ -665,6 +705,7 @@ fn make_fold(afp: ast_fold_precursor) -> ast_fold {
fold_view_item: bind f_view_item(afp, result, _),
fold_native_item: bind f_native_item(afp, result, _),
fold_item: bind f_item(afp, result, _),
fold_class_item: bind f_class_item(afp, result, _),
fold_item_underscore: bind f_item_underscore(afp, result, _),
fold_method: bind f_method(afp, result, _),
fold_block: bind f_block(afp, result, _),

View file

@ -1616,6 +1616,21 @@ fn parse_let(p: parser) -> @ast::decl {
ret @spanned(lo, p.last_span.hi, ast::decl_local(locals));
}
fn parse_instance_var(p:parser) -> @ast::class_member {
let is_mut = ast::class_immutable;
expect_word(p, "let");
if eat_word(p, "mutable") {
is_mut = ast::class_mutable;
}
if !is_plain_ident(p) {
p.fatal("expecting ident");
}
let name = parse_ident(p);
expect(p, token::COLON);
let ty = parse_ty(p, false);
ret @ast::instance_var(name, ty, is_mut, p.get_id());
}
fn parse_stmt(p: parser, first_item_attrs: [ast::attribute]) -> @ast::stmt {
fn check_expected_item(p: parser, current_attrs: [ast::attribute]) {
// If we have attributes then we should have an item
@ -1980,6 +1995,92 @@ fn parse_item_res(p: parser, attrs: [ast::attribute]) -> @ast::item {
attrs);
}
fn parse_item_class(p: parser, attrs: [ast::attribute]) -> @ast::item {
let lo = p.last_span.lo;
let class_name = parse_value_ident(p);
let ty_params = parse_ty_params(p);
expect(p, token::LBRACE);
let items: [@ast::class_item] = [];
let the_ctor : option<(ast::fn_decl, ast::blk)> = none;
while p.token != token::RBRACE {
alt parse_class_item(p) {
ctor_decl(a_fn_decl, blk) {
the_ctor = some((a_fn_decl, blk));
}
plain_decl(a_decl) {
items += [@{node: {privacy: ast::pub, decl: a_decl},
span: p.last_span}];
}
priv_decls(some_decls) {
items += vec::map(some_decls, {|d|
@{node: {privacy: ast::priv, decl: d},
span: p.last_span}});
}
}
}
p.bump();
alt the_ctor {
some((ct_d, ct_b)) { ret mk_item(p, lo, p.last_span.hi, class_name,
ast::item_class(ty_params, items, ct_d, ct_b), attrs); }
/*
Is it strange for the parser to check this?
*/
none { /* parse error */ fail "Class with no ctor"; }
}
}
// lets us identify the constructor declaration at
// parse time
// we don't really want just the fn_decl...
enum class_contents { ctor_decl(ast::fn_decl, ast::blk),
// assumed to be public
plain_decl(@ast::class_member),
// contents of a priv section --
// parse_class_item ensures that
// none of these are a ctor decl
priv_decls([@ast::class_member])}
fn parse_class_item(p:parser) -> class_contents {
if eat_word(p, "new") {
// Can ctors have attrs?
let decl = parse_fn_decl(p, ast::impure_fn);
let body = parse_block(p);
ret ctor_decl(decl, body);
}
// TODO: refactor
else if eat_word(p, "priv") {
expect(p, token::LBRACE);
let results = [];
while p.token != token::RBRACE {
alt parse_item(p, []) {
some(i) {
results += [@ast::class_method(i)];
}
_ {
let a_var = parse_instance_var(p);
expect(p, token::SEMI);
results += [a_var];
}
}
}
p.bump();
ret priv_decls(results);
}
else {
// Probably need to parse attrs
alt parse_item(p, []) {
some(i) {
ret plain_decl(@ast::class_method(i));
}
_ {
let a_var = parse_instance_var(p);
expect(p, token::SEMI);
ret plain_decl(a_var);
}
}
}
}
fn parse_mod_items(p: parser, term: token::token,
first_item_attrs: [ast::attribute]) -> ast::_mod {
// Shouldn't be any view items since we've already parsed an item attr
@ -2222,7 +2323,10 @@ fn parse_item(p: parser, attrs: [ast::attribute]) -> option<@ast::item> {
ret some(parse_item_impl(p, attrs));
} else if eat_word(p, "resource") {
ret some(parse_item_res(p, attrs));
} else { ret none; }
} else if eat_word(p, "class") {
ret some(parse_item_class(p, attrs));
}
else { ret none; }
}
// A type to distingush between the parsing of item attributes or syntax

View file

@ -479,6 +479,52 @@ fn print_item(s: ps, &&item: @ast::item) {
bclose(s, item.span);
}
}
ast::item_class(tps,items,ctor_decl,ctor_body) {
head(s, "class");
word_nbsp(s, item.ident);
print_type_params(s, tps);
bopen(s);
hardbreak_if_not_bol(s);
head(s, "new");
print_fn_args_and_ret(s, ctor_decl);
space(s.s);
print_block(s, ctor_body);
for ci in items {
/*
TODO: collect all private items and print them
in a single "priv" section
*/
hardbreak_if_not_bol(s);
alt ci.node.privacy {
ast::priv {
head(s, "priv");
bopen(s);
hardbreak_if_not_bol(s);
}
_ {}
}
alt *ci.node.decl {
ast::instance_var(nm, t, mt, _) {
word_nbsp(s, "let");
alt mt {
ast::class_mutable { word_nbsp(s, "mutable"); }
_ {}
}
word(s.s, nm);
word_nbsp(s, ":");
print_type(s, t);
word(s.s, ";");
}
ast::class_method(i) {
print_item(s, i);
}
}
alt ci.node.privacy {
ast::priv { bclose(s, ci.span); }
_ {}
}
}
}
ast::item_impl(tps, ifce, ty, methods) {
head(s, "impl");
word(s.s, item.ident);
@ -641,7 +687,6 @@ fn print_possibly_embedded_block_(s: ps, blk: ast::blk, embedded: embed_type,
ast::unsafe_blk { word(s.s, "unsafe"); }
ast::default_blk { }
}
maybe_print_comment(s, blk.span.lo);
let ann_node = node_block(s, blk);
s.ann.pre(ann_node);

View file

@ -54,7 +54,8 @@ type visitor<E> =
visit_ty: fn@(@ty, E, vt<E>),
visit_ty_params: fn@([ty_param], E, vt<E>),
visit_constr: fn@(@path, span, node_id, E, vt<E>),
visit_fn: fn@(fn_kind, fn_decl, blk, span, node_id, E, vt<E>)};
visit_fn: fn@(fn_kind, fn_decl, blk, span, node_id, E, vt<E>),
visit_class_item: fn@(span, privacy, @class_member, E, vt<E>)};
fn default_visitor<E>() -> visitor<E> {
ret @{visit_mod: bind visit_mod::<E>(_, _, _, _, _),
@ -71,7 +72,8 @@ fn default_visitor<E>() -> visitor<E> {
visit_ty: bind skip_ty::<E>(_, _, _),
visit_ty_params: bind visit_ty_params::<E>(_, _, _),
visit_constr: bind visit_constr::<E>(_, _, _, _, _),
visit_fn: bind visit_fn::<E>(_, _, _, _, _, _, _)};
visit_fn: bind visit_fn::<E>(_, _, _, _, _, _, _),
visit_class_item: bind visit_class_item::<E>(_,_,_,_,_)};
}
fn visit_crate<E>(c: crate, e: E, v: vt<E>) {
@ -135,6 +137,14 @@ fn visit_item<E>(i: @item, e: E, v: vt<E>) {
m.id, e, v);
}
}
item_class(tps, members, ctor_decl, ctor_blk) {
v.visit_ty_params(tps, e, v);
for m in members {
v.visit_class_item(m.span, m.node.privacy, m.node.decl, e, v);
}
visit_fn_decl(ctor_decl, e, v);
v.visit_block(ctor_blk, e, v);
}
item_iface(tps, methods) {
v.visit_ty_params(tps, e, v);
for m in methods {
@ -145,6 +155,18 @@ fn visit_item<E>(i: @item, e: E, v: vt<E>) {
}
}
fn visit_class_item<E>(_s: span, _p: privacy, cm: @class_member,
e:E, v:vt<E>) {
alt *cm {
instance_var(ident, t, mt, id) {
v.visit_ty(t, e, v);
}
class_method(i) {
v.visit_item(i, e, v);
}
}
}
fn skip_ty<E>(_t: @ty, _e: E, _v: vt<E>) {}
fn visit_ty<E>(t: @ty, e: E, v: vt<E>) {
@ -387,7 +409,8 @@ type simple_visitor =
visit_ty: fn@(@ty),
visit_ty_params: fn@([ty_param]),
visit_constr: fn@(@path, span, node_id),
visit_fn: fn@(fn_kind, fn_decl, blk, span, node_id)};
visit_fn: fn@(fn_kind, fn_decl, blk, span, node_id),
visit_class_item: fn@(span, privacy, @class_member)};
fn simple_ignore_ty(_t: @ty) {}
@ -407,7 +430,8 @@ fn default_simple_visitor() -> simple_visitor {
visit_ty_params: fn@(_ps: [ty_param]) {},
visit_constr: fn@(_p: @path, _sp: span, _id: node_id) { },
visit_fn: fn@(_fk: fn_kind, _d: fn_decl, _b: blk, _sp: span,
_id: node_id) { }
_id: node_id) { },
visit_class_item: fn@(_s: span, _p: privacy, _c: @class_member) {}
};
}
@ -482,6 +506,12 @@ fn mk_simple_visitor(v: simple_visitor) -> vt<()> {
} else {
bind v_ty(v.visit_ty, _, _, _)
};
fn v_class_item(f: fn@(span, privacy, @class_member),
s:span, p:privacy, cm: @class_member, &&e: (),
v: vt<()>) {
f(s, p, cm);
visit_class_item(s, p, cm, e, v);
}
ret mk_vt(@{visit_mod: bind v_mod(v.visit_mod, _, _, _, _, _),
visit_view_item: bind v_view_item(v.visit_view_item, _, _, _),
visit_native_item:
@ -497,7 +527,9 @@ fn mk_simple_visitor(v: simple_visitor) -> vt<()> {
visit_ty: visit_ty,
visit_ty_params: bind v_ty_params(v.visit_ty_params, _, _, _),
visit_constr: bind v_constr(v.visit_constr, _, _, _, _, _),
visit_fn: bind v_fn(v.visit_fn, _, _, _, _, _, _, _)
visit_fn: bind v_fn(v.visit_fn, _, _, _, _, _, _, _),
visit_class_item: bind v_class_item(v.visit_class_item, _, _,
_, _, _)
});
}

View file

@ -0,0 +1,29 @@
// xfail-test
class cat {
priv {
let mutable meows : uint;
fn meow() {
#error("Meow");
meows += 1;
if meows % 5 == 0 {
how_hungry += 1;
}
}
}
let how_hungry : int;
new(in_x : uint, in_y : int) { meows = in_x; how_hungry = in_y; }
fn speak() { meow(); }
fn eat() {
if how_hungry > 0 {
#error("OM NOM NOM");
how_hungry -= 2;
}
else {
#error("Not hungry!");
}
}
}