From 9528c34774ff27b112c9e66afff6e10fa7021635 Mon Sep 17 00:00:00 2001
From: Brian Anderson <andersrb@gmail.com>
Date: Wed, 23 Feb 2011 23:48:01 -0500
Subject: [PATCH] Begin implementing #fmt in rustc

---
 src/comp/front/ast.rs                     | 13 +++-
 src/comp/front/extfmt.rs                  | 84 +++++++++++++++++++++++
 src/comp/front/parser.rs                  | 35 +++++++++-
 src/comp/rustc.rc                         |  1 +
 src/test/run-pass/syntax-extension-fmt.rs | 14 +++-
 5 files changed, 142 insertions(+), 5 deletions(-)
 create mode 100644 src/comp/front/extfmt.rs

diff --git a/src/comp/front/ast.rs b/src/comp/front/ast.rs
index 4ed513a3e06..18add3bdfbd 100644
--- a/src/comp/front/ast.rs
+++ b/src/comp/front/ast.rs
@@ -185,7 +185,7 @@ tag expr_ {
     expr_field(@expr, ident, ann);
     expr_index(@expr, @expr, ann);
     expr_path(path, option.t[def], ann);
-    expr_ext(path, vec[@expr], option.t[@expr], ann);
+    expr_ext(path, vec[@expr], option.t[@expr], option.t[@expr], ann);
     expr_fail;
     expr_ret(option.t[@expr]);
     expr_put(option.t[@expr]);
@@ -363,6 +363,17 @@ fn is_call_expr(@expr e) -> bool {
     }
 }
 
+fn is_ext_expr(@expr e) -> bool {
+    alt (e.node) {
+        case (expr_ext(_, _, _, _, _)) {
+            ret true;
+        }
+        case (_) {
+            ret false;
+        }
+    }
+}
+
 //
 // Local Variables:
 // mode: rust
diff --git a/src/comp/front/extfmt.rs b/src/comp/front/extfmt.rs
new file mode 100644
index 00000000000..cb70805a335
--- /dev/null
+++ b/src/comp/front/extfmt.rs
@@ -0,0 +1,84 @@
+/* The 'fmt' extension is modeled on the posix printf system.
+ * 
+ * A posix conversion ostensibly looks like this:
+ * 
+ * %[parameter][flags][width][.precision][length]type
+ * 
+ * Given the different numeric type bestiary we have, we omit the 'length'
+ * parameter and support slightly different conversions for 'type':
+ * 
+ * %[parameter][flags][width][.precision]type
+ * 
+ * we also only support translating-to-rust a tiny subset of the possible
+ * combinations at the moment.
+ */
+
+use std;
+
+import std.option;
+
+tag signedness {
+    signed;
+    unsigned;
+}
+
+tag caseness {
+    case_upper;
+    case_lower;
+}
+
+tag ty {
+    ty_bool;
+    ty_str;
+    ty_char;
+    ty_int(signedness);
+    ty_bits;
+    ty_hex(caseness);
+    // FIXME: More types
+}
+
+tag flag {
+    flag_left_justify;
+    flag_left_zero_pad;
+    flag_left_space_pad;
+    flag_plus_if_positive;
+    flag_alternate;
+}
+
+tag count {
+    count_is(int);
+    count_is_param(int);
+    count_is_next_param;
+    count_implied;
+}
+
+// A formatted conversion from an expression to a string
+tag conv {
+    conv_param(option.t[int]);
+    conv_flags(vec[flag]);
+    conv_width(count);
+    conv_precision(count);
+    conv_ty(ty);
+}
+
+// A fragment of the output sequence
+tag piece {
+    piece_string(str);
+    piece_conv(str);
+}
+
+fn expand_syntax_ext(vec[@ast.expr] args,
+                     option.t[@ast.expr] body) -> @ast.expr {
+    fail;
+}
+
+//
+// Local Variables:
+// mode: rust
+// fill-column: 78;
+// indent-tabs-mode: nil
+// c-basic-offset: 4
+// buffer-file-coding-system: utf-8-unix
+// compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
+// End:
+//
diff --git a/src/comp/front/parser.rs b/src/comp/front/parser.rs
index f747c084b5b..8f6db17d88d 100644
--- a/src/comp/front/parser.rs
+++ b/src/comp/front/parser.rs
@@ -656,7 +656,10 @@ impure fn parse_bottom_expr(parser p) -> @ast.expr {
                                            some(token.COMMA),
                                            pf, p);
             hi = es.span;
-            ex = ast.expr_ext(pth, es.node, none[@ast.expr], ast.ann_none);
+            ex = ast.expr_ext(pth, es.node, none[@ast.expr],
+                              none[@ast.expr], ast.ann_none);
+            // FIXME: Here is probably not the right place for this
+            ex = expand_syntax_ext(p, @spanned(lo, hi, ex)).node;
         }
 
         case (token.FAIL) {
@@ -736,6 +739,36 @@ impure fn parse_bottom_expr(parser p) -> @ast.expr {
     ret @spanned(lo, hi, ex);
 }
 
+/* 
+ * FIXME: This is a crude approximation of the syntax-extension system,
+ * for purposes of prototyping and/or hard-wiring any extensions we
+ * wish to use while bootstrapping. The eventual aim is to permit
+ * loading rust crates to process extensions, but this will likely
+ * require a rust-based frontend, or an ocaml-FFI-based connection to
+ * rust crates. At the moment we have neither.
+ */
+
+impure fn expand_syntax_ext(parser p, @ast.expr ext) -> @ast.expr {
+    check (ast.is_ext_expr(ext));
+    alt (ext.node) {
+        case (ast.expr_ext(?path, ?args, ?body, _, ?ann)) {
+            check (_vec.len[ast.ident](path.node.idents) > 0u);
+            auto extname = path.node.idents.(0);
+            if (_str.eq(extname, "fmt")) {
+                auto expanded = extfmt.expand_syntax_ext(args, body);
+                check (ast.is_ext_expr(expanded));
+                auto newexpr = ast.expr_ext(path, args, body,
+                                            some[@ast.expr](expanded), ann);
+
+                ret @spanned(ext.span, ext.span, newexpr);
+            } else {
+                p.err("unknown syntax extension");
+            }
+        }
+    }
+    fail;
+}
+
 impure fn extend_expr_by_ident(parser p, span lo, span hi,
                                @ast.expr e, ast.ident i) -> @ast.expr {
     auto e_ = e.node;
diff --git a/src/comp/rustc.rc b/src/comp/rustc.rc
index bc4aaa52c1a..16d24e9b277 100644
--- a/src/comp/rustc.rc
+++ b/src/comp/rustc.rc
@@ -5,6 +5,7 @@ use std;
 
 mod front {
     mod ast;
+    mod extfmt;
     mod lexer;
     mod parser;
     mod token;
diff --git a/src/test/run-pass/syntax-extension-fmt.rs b/src/test/run-pass/syntax-extension-fmt.rs
index 65e7647ee8b..66fe4fd7ce0 100644
--- a/src/test/run-pass/syntax-extension-fmt.rs
+++ b/src/test/run-pass/syntax-extension-fmt.rs
@@ -1,5 +1,13 @@
 use std;
-fn main() {
-  auto s = #fmt("hello %d friends and %s things", 10, "formatted");
-  log s;
+import std._str;
+
+fn test(str actual, str expected) {
+  log actual;
+  log expected;
+  check (_str.eq(actual, expected));
+}
+
+fn main() {
+  test(#fmt("hello %d friends and %s things", 10, "formatted"),
+       "hello 10 friends and formatted things");
 }