]> Cypherpunks repositories - gostls13.git/commitdiff
daily snapshot:
authorRobert Griesemer <gri@golang.org>
Sat, 25 Apr 2009 00:22:58 +0000 (17:22 -0700)
committerRobert Griesemer <gri@golang.org>
Sat, 25 Apr 2009 00:22:58 +0000 (17:22 -0700)
- more work on template-driven ast printing

R=r
OCL=27851
CL=27851

usr/gri/pretty/ast.txt
usr/gri/pretty/format.go

index a4f62f3b8dd3b81cbefaa6162ef5143fa03900b0..998352ccaa3ea77e9f68764024c68afef0243154 100644 (file)
@@ -1,14 +1,40 @@
-// TODO prefix decl doesn't work
-//ast .
+//string =
+//     "%s" ;
 
-ast.Ident =
-       Value .
+pointer =
+       ^ ;
 
-ast.Program =
-       "package " Name "\n" { Decls "\n\n" } .
+array =
+       ^ ;
 
-ast.GenDecl =
-       "def " .
+//token.Token =
+//     "token<%d>" ;  // this should be a Go-installed formatter
 
-ast.FuncDecl =
-       "func " .
\ No newline at end of file
+ast
+       ;
+
+Comments =
+       "comments\n" ;
+
+Ident =
+       Value ;
+
+Program =
+       "package " Name "\n\n" { Decls "\n\n" } ;
+
+GenDecl =
+       Doc
+       Tok " (\n"
+       ")\n";
+
+FuncType =
+       "(" { Params } ")" ;
+
+BlockStmt =
+       "{\n" "}\n" ;
+
+FuncDecl =
+       "func " Name Type [ " " Body ] ;
+       
+Decl =
+       ^ ;
\ No newline at end of file
index 35c3bc78ae2096669cb82645b7ffebf861527923..d069ef4c8bea12617984664603ad06e476bf1806 100644 (file)
@@ -17,39 +17,88 @@ import (
 // -----------------------------------------------------------------------------
 // Format
 
-// node kind
-const (
-       self = iota;
-       alternative;
-       sequence;
-       field;
-       literal;
-       option;
-       repetition;
+// A production expression is built from the following nodes.
+//
+type (
+       expr interface {
+               implements_expr();
+       };
+
+       empty struct {
+       };
+
+       alternative struct {
+               x, y expr;
+       };
+
+       sequence struct {
+               x, y expr;
+       };
+
+       field struct {
+               name string;  // including "^", "*"
+               format expr;  // nil if no format specified
+       };
+       
+       literal struct {
+               // TODO should there be other types or should it all be string literals?
+               value []byte;
+       };
+
+       option struct {
+               x expr
+       };
+
+       repetition struct {
+               x expr
+       };
+
+       // TODO custom formats are not yet used
+       custom struct {
+               name string;
+               f func(w io.Write, value interface{}, name string) bool
+       };
 )
 
 
-type node struct {
-       kind int;
-       name string;  // field name
-       value []byte;  // literal value
-       x, y *node;
-}
+// These methods are used to enforce the "implements" relationship for
+// better compile-time type checking.
+//
+// TODO If we had a basic accessor mechanism in the language (a field
+// "f T" automatically implements a corresponding accessor "f() T", this
+// could be expressed more easily by simply providing the field.
+//
+func (x *empty) implements_expr()  {}
+func (x *alternative) implements_expr()  {}
+func (x *sequence) implements_expr()  {}
+func (x *field) implements_expr()  {}
+func (x *literal) implements_expr()  {}
+func (x *option) implements_expr()  {}
+func (x *repetition) implements_expr()  {}
+func (x *custom) implements_expr()  {}
 
 
-// A Format is a set of production nodes.
-type Format map [string] *node;
+// A Format is a set of production expressions.
+type Format map [string] expr;
 
 
 // -----------------------------------------------------------------------------
 // Parsing
 
+/*     TODO
+       - EBNF vs Kleene notation
+       - default formatters for basic types (may imply scopes so we can override)
+       - installable custom formatters (like for template.go)
+       - format strings
+*/
+
 /*     Format      = { Production } .
-       Production  = DottedName [ "=" Expression ] "." .
-       DottedName  = name { "." name } .
+       Production  = Name [ "=" [ Expression ] ] ";" .
+       Name        = identifier { "." identifier } .
        Expression  = Term { "|" Term } .
        Term        = Factor { Factor } .
-       Factor      = "*" | name | string_literal | Group | Option | Repetition .
+       Factor      = string_literal | Field | Group | Option | Repetition .
+       Field           = ( "^" | "*" | Name ) [ ":" Expression ] .
        Group       = "(" Expression ")" .
        Option      = "[" Expression "]" .
        Repetition  = "{" Expression "}" .
@@ -109,30 +158,30 @@ func (p *parser) expect(tok token.Token) token.Position {
 }
 
 
-func (p *parser) parseName() string {
+func (p *parser) parseIdentifier() string {
        name := string(p.lit);
        p.expect(token.IDENT);
        return name;
 }
 
 
-func (p *parser) parseDottedName() string {
-       name := p.parseName();
+func (p *parser) parseName() string {
+       name := p.parseIdentifier();
        for p.tok == token.PERIOD {
                p.next();
-               name = name + "." + p.parseName();
+               name = name + "." + p.parseIdentifier();
        }
        return name;
 }
 
 
-// TODO should have WriteByte in ByteBuffer instead!
-var (
-       newlineByte = []byte{'\n'};
-       tabByte = []byte{'\t'};
-)
+// TODO WriteByte should be a ByteBuffer method
+func writeByte(buf *io.ByteBuffer, b byte) {
+       buf.Write([]byte{b});
+}
 
 
+// TODO make this complete
 func escapeString(s []byte) []byte {
        // the string syntax is correct since it comes from the scannner
        var buf io.ByteBuffer;
@@ -141,14 +190,13 @@ func escapeString(s []byte) []byte {
                if s[i] == '\\' {
                        buf.Write(s[i0 : i]);
                        i++;
+                       var esc byte;
                        switch s[i] {
-                       case 'n':
-                               buf.Write(newlineByte);
-                       case 't':
-                               buf.Write(tabByte);
-                       default:
-                               panic("unhandled escape:", string(s[i]));
+                       case 'n': esc = '\n';
+                       case 't': esc = '\t';
+                       default: panic("unhandled escape:", string(s[i]));
                        }
+                       writeByte(&buf, esc);
                        i++;
                        i0 = i;
                } else {
@@ -182,32 +230,54 @@ func (p *parser) parseValue() []byte {
 }
 
 
-func (p *parser) parseExpression() *node
+func (p *parser) parseExpr() expr
 
-func (p *parser) parseFactor() (x *node) {
+func (p *parser) parseField() expr {
+       var name string;
        switch p.tok {
+       case token.XOR:
+               name = "^";
+               p.next();
        case token.MUL:
-               x = &node{self, "", nil, nil, nil};
-
+               name = "*";
+               p.next();
        case token.IDENT:
-               x = &node{field, p.parseName(), nil, nil, nil};
+               name = p.parseName();
+       default:
+               panic("unreachable");
+       }
+
+       var format expr;
+       if p.tok == token.COLON {
+               p.next();
+               format = p.parseExpr();
+       }
+       
+       return &field{name, format};
+}
+
+
+func (p *parser) parseFactor() (x expr) {
+       switch p.tok {
+       case token.XOR, token.MUL, token.IDENT:
+               x = p.parseField();
 
        case token.STRING:
-               x = &node{literal, "", p.parseValue(), nil, nil};
+               x = &literal{p.parseValue()};
 
        case token.LPAREN:
                p.next();
-               x = p.parseExpression();
+               x = p.parseExpr();
                p.expect(token.RPAREN);
 
        case token.LBRACK:
                p.next();
-               x = &node{option, "", nil, p.parseExpression(), nil};
+               x = &option{p.parseExpr()};
                p.expect(token.RBRACK);
 
        case token.LBRACE:
                p.next();
-               x = &node{repetition, "", nil, p.parseExpression(), nil};
+               x = &repetition{p.parseExpr()};
                p.expect(token.RBRACE);
 
        default:
@@ -219,46 +289,52 @@ func (p *parser) parseFactor() (x *node) {
 }
 
 
-func (p *parser) parseTerm() *node {
+func (p *parser) parseTerm() expr {
        x := p.parseFactor();
 
-       for     p.tok == token.IDENT ||
+       for     p.tok == token.XOR ||
+               p.tok == token.MUL ||
+               p.tok == token.IDENT ||
                p.tok == token.STRING ||
                p.tok == token.LPAREN ||
                p.tok == token.LBRACK ||
                p.tok == token.LBRACE
        {
                y := p.parseFactor();
-               x = &node{sequence, "", nil, x, y};
+               x = &sequence{x, y};
        }
 
        return x;
 }
 
 
-func (p *parser) parseExpression() *node {
+func (p *parser) parseExpr() expr {
        x := p.parseTerm();
 
        for p.tok == token.OR {
                p.next();
                y := p.parseTerm();
-               x = &node{alternative, "", nil, x, y};
+               x = &alternative{x, y};
        }
 
        return x;
 }
 
 
-func (p *parser) parseProduction() (string, *node) {
-       name := p.parseDottedName();
+func (p *parser) parseProduction() (string, expr) {
+       name := p.parseName();
        
-       var x *node;
+       var x expr;
        if p.tok == token.ASSIGN {
                p.next();
-               x = p.parseExpression();
+               if p.tok == token.SEMICOLON {
+                       x = &empty{};
+               } else {
+                       x = p.parseExpr();
+               }
        }
 
-       p.expect(token.PERIOD);
+       p.expect(token.SEMICOLON);
 
        return name, x;
 }
@@ -365,118 +441,181 @@ func getField(v reflect.StructValue, fieldname string) reflect.Value {
 }
 
 
-func (f Format) apply(w io.Write, v reflect.Value) bool
+func typename(value reflect.Value) string {
+       name := value.Type().Name();
 
-// Returns true if a non-empty field value was found.
-func (f Format) print(w io.Write, x *node, v reflect.Value, index int) bool {
-       switch x.kind {
-       case self:
-               panic("self");
+       if name != "" {
+               return name;
+       }
 
-       case alternative:
-               // print the contents of the first alternative with a non-empty field
-               var buf io.ByteBuffer;
-               if !f.print(&buf, x.x, v, -1) {
-                       f.print(&buf, x.y, v, -1);
-               }
-               w.Write(buf.Data());
+       switch value.Kind() {
+       case reflect.ArrayKind: name = "array";
+       case reflect.BoolKind: name = "bool";
+       case reflect.ChanKind: name = "chan";
+       case reflect.DotDotDotKind: name = "...";
+       case reflect.FloatKind: name = "float";
+       case reflect.Float32Kind: name = "float32";
+       case reflect.Float64Kind: name = "float64";
+       case reflect.FuncKind: name = "func";
+       case reflect.IntKind: name = "int";
+       case reflect.Int16Kind: name = "int16";
+       case reflect.Int32Kind: name = "int32";
+       case reflect.Int64Kind: name = "int64";
+       case reflect.Int8Kind: name = "int8";
+       case reflect.InterfaceKind: name = "interface";
+       case reflect.MapKind: name = "map";
+       case reflect.PtrKind: name = "pointer";
+       case reflect.StringKind: name = "string";
+       case reflect.StructKind: name = "struct";
+       case reflect.UintKind: name = "uint";
+       case reflect.Uint16Kind: name = "uint16";
+       case reflect.Uint32Kind: name = "uint32";
+       case reflect.Uint64Kind: name = "uint64";
+       case reflect.Uint8Kind: name = "uint8";
+       case reflect.UintptrKind: name = "uintptr";
+       }
+       
+       return name;
+}
 
-       case sequence:
-               f.print(w, x.x, v, -1);
-               f.print(w, x.y, v, -1);
 
-       case field:
-               if sv, is_struct := v.(reflect.StructValue); is_struct {
-                       return f.apply(w, getField(sv, x.name));
-               } else {
-                       panicln("not in a struct - field:", x.name);
-               }
+var defaultFormat = &literal{io.StringBytes("%v")};
 
-       case literal:
-               w.Write(x.value);
+func (f Format) getFormat(value reflect.Value) expr {
+       if format, found := f[typename(value)]; found {
+               return format;
+       }
+       // no format found
+       return defaultFormat;
+}
 
-       case option:
-               // print the contents of the option if there is a non-empty field
-               var buf io.ByteBuffer;
-               if f.print(&buf, x.x, v, -1) {
-                       w.Write(buf.Data());
-               }
 
-       case repetition:
-               // print the contents of the repetition while there is a non-empty field
-               for i := 0; ; i++ {
-                       var buf io.ByteBuffer;
-                       if f.print(&buf, x.x, v, i) {
-                               w.Write(buf.Data());
-                       } else {
-                               break;
-                       }
+// Count the number of printf-style '%' formatters in s.
+// The result is 0, 1, or 2 (where 2 stands for 2 or more).
+//
+func percentCount(s []byte) int {
+       n := 0;
+       for i := 0; n < 2 && i < len(s); i++ {
+               // TODO should not count "%%"'s
+               if s[i] == '%' {
+                       n++;
                }
-
-       default:
-               panic("unreachable");
        }
-
-       return false;
+       return n;
 }
 
 
-func (f Format) Dump() {
-       for name, x := range f {
-               println(name, x);
+func printf(w io.Write, format []byte, value reflect.Value) {
+       // TODO this seems a bit of a hack
+       if percentCount(format) == 1 {
+               // exactly one '%' format specifier - try to use it
+               fmt.Fprintf(w, string(format), value.Interface());
+       } else {
+               // 0 or more then 1 '%' format specifier - ignore them
+               w.Write(format);
        }
 }
 
 
-func (f Format) apply(w io.Write, v reflect.Value) bool {
-       println("apply typename:", v.Type().Name());
+// Returns true if a non-empty field value was found.
+func (f Format) print(w io.Write, format expr, value reflect.Value, index int) bool {
+       switch t := format.(type) {
+       case *empty:
+               return true;
 
-       if x, found := f[v.Type().Name()]; found {
-               // format using corresponding production
-               f.print(w, x, v, -1);
-               
-       } else {
-               // format using default formats
-               switch x := v.(type) {
-               case reflect.ArrayValue:
-                       if x.Len() == 0 {
-                               return false;
-                       }
-                       for i := 0; i < x.Len(); i++ {
-                               f.apply(w, x.Elem(i));
+       case *alternative:
+               // print the contents of the first alternative with a non-empty field
+               var buf io.ByteBuffer;
+               b := f.print(&buf, t.x, value, index);
+               if !b {
+                       b = f.print(&buf, t.y, value, index);
+               }
+               if b {
+                       w.Write(buf.Data());
+               }
+               return index < 0 || b;
+
+       case *sequence:
+               b1 := f.print(w, t.x, value, index);
+               b2 := f.print(w, t.y, value, index);
+               return index < 0 || b1 && b2;
+
+       case *field:
+               var x reflect.Value;
+               switch t.name {
+               case "^":
+                       if v, is_ptr := value.(reflect.PtrValue); is_ptr {
+                               if v.Get() == nil {
+                                       return false;
+                               }
+                               x = v.Sub();
+                       } else if v, is_array := value.(reflect.ArrayValue); is_array {
+                               if index < 0 || v.Len() <= index {
+                                       return false;
+                               }
+                               x = v.Elem(index);
+                       } else if v, is_interface := value.(reflect.InterfaceValue); is_interface {
+                               if v.Get() == nil {
+                                       return false;
+                               }
+                               x = v.Value();
+                       } else {
+                               panic("not a ptr, array, or interface");  // TODO fix this
                        }
-
-               case reflect.StringValue:
-                       w.Write(io.StringBytes(x.Get()));
-
-               case reflect.IntValue:
-                       // TODO is this the correct way to check the right type?
-                       // or should it be t, ok := x.Interface().(token.Token) instead?
-                       if x.Type().Name() == "token.Token" {
-                               fmt.Fprintf(w, "%s", token.Token(x.Get()).String());
+               case "*":
+                       x = value;
+               default:
+                       if v, is_struct := value.(reflect.StructValue); is_struct {
+                               x = getField(v, t.name);
                        } else {
-                               fmt.Fprintf(w, "%d", x.Get());
+                               panic ("not a struct");  // TODO fix this
                        }
+               }
+               format = t.format;
+               if format == nil {
+                       format = f.getFormat(x);
+               }
+               b := f.print(w, format, x, index);
+               return index < 0 || b;
 
-               case reflect.InterfaceValue:
-                       f.apply(w, x.Value());
+       case *literal:
+               printf(w, t.value, value);
+               return true;
 
-               case reflect.PtrValue:
-                       // TODO is this the correct way to check nil ptr?
-                       if x.Get() == nil {
-                               return false;
-                       }
-                       return f.apply(w, x.Sub());
+       case *option:
+               // print the contents of the option if there is a non-empty field
+               var buf io.ByteBuffer;
+               b := f.print(&buf, t.x, value, -1);
+               if b {
+                       w.Write(buf.Data());
+               }
+               return index < 0 || b;
 
-               default:
-                       panicln("unsupported kind:", v.Kind());
+       case *repetition:
+               // print the contents of the repetition while there is a non-empty field
+               b := false;
+               for i := 0; ; i++ {
+                       var buf io.ByteBuffer;
+                       if f.print(&buf, t.x, value, i) {
+                               w.Write(buf.Data());
+                               b = true;
+                       } else {
+                               break;
+                       }
                }
+               return index < 0 || b;
+               
+       case *custom:
+               b := t.f(w, value.Interface(), t.name);
+               return index < 0 || b;
        }
-
-       return true;
+       
+       panic("unreachable");
+       return false;
 }
 
 
 func (f Format) Apply(w io.Write, data interface{}) {
-       f.apply(w, reflect.NewValue(data));
+       value := reflect.NewValue(data);
+       f.print(w, f.getFormat(value), value, -1);
 }