]> Cypherpunks repositories - gostls13.git/commitdiff
First cut at a Go pretty printer:
authorRobert Griesemer <gri@golang.org>
Thu, 18 Sep 2008 23:58:37 +0000 (16:58 -0700)
committerRobert Griesemer <gri@golang.org>
Thu, 18 Sep 2008 23:58:37 +0000 (16:58 -0700)
- code scavenged from Go-in-Go front-end (will merge back)
- using "symbol-table" free parsing to build AST
- no printing yet

R=r
OCL=15504
CL=15504

usr/gri/pretty/Makefile [new file with mode: 0644]
usr/gri/pretty/ast.go [new file with mode: 0644]
usr/gri/pretty/parser.go [new file with mode: 0644]
usr/gri/pretty/platform.go [new file with mode: 0644]
usr/gri/pretty/pretty.go [new file with mode: 0644]
usr/gri/pretty/printer.go [new file with mode: 0644]
usr/gri/pretty/scanner.go [new file with mode: 0644]
usr/gri/pretty/utils.go [new file with mode: 0644]

diff --git a/usr/gri/pretty/Makefile b/usr/gri/pretty/Makefile
new file mode 100644 (file)
index 0000000..a1bde84
--- /dev/null
@@ -0,0 +1,39 @@
+# Copyright 2009 The Go Authors.  All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+G=6g
+L=6l
+
+pretty: pretty.6
+       $(L) -o pretty pretty.6
+
+test: all
+       pretty *.go
+       # pretty $(GOROOT)/test/fixedbugs/*.go  # some files legally don't compile
+       pretty $(GOROOT)/test/sieve.go
+       pretty $(GOROOT)/src/pkg/*.go
+       pretty $(GOROOT)/src/lib/flag.go
+       pretty $(GOROOT)/src/lib/fmt.go
+       pretty $(GOROOT)/src/lib/rand.go
+       pretty $(GOROOT)/src/lib/math/*.go
+       pretty $(GOROOT)/src/lib/container/*.go
+       pretty $(GOROOT)/src/syscall/*.go
+       pretty base.go decls.go
+       pretty -token_chan base.go decls.go
+       echo "PASSED"
+
+install: pretty
+       cp pretty $(HOME)/bin/pretty
+
+clean:
+       rm -f pretty *.6  *~
+
+pretty.6:      parser.6 printer.6 platform.6 scanner.6
+
+parser.6:      ast.6 scanner.6 utils.6
+
+scanner.6:     utils.6 platform.6
+
+%.6:   %.go
+       $(G) $(F) $<
diff --git a/usr/gri/pretty/ast.go b/usr/gri/pretty/ast.go
new file mode 100644 (file)
index 0000000..311b91f
--- /dev/null
@@ -0,0 +1,39 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package AST;
+
+
+export type Expr interface {
+       pos() int;
+       print();
+}
+
+
+export type Stat interface {
+       pos() int;
+       print();
+}
+
+
+// ---------------------------------------------------------------------
+// Concrete nodes
+
+export type Ident struct {
+       pos_ int;
+       val_ string;
+}
+
+
+func (p *Ident) pos() int {
+     return p.pos_;
+}
+
+
+func (p *Ident) print() {
+     print("x");  // TODO fix this
+}
+
+
+// TODO: complete this
diff --git a/usr/gri/pretty/parser.go b/usr/gri/pretty/parser.go
new file mode 100644 (file)
index 0000000..f66efd6
--- /dev/null
@@ -0,0 +1,1320 @@
+// Copyright 2009 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package Parser
+
+import Scanner "scanner"
+import AST "ast"
+
+export type Parser struct {
+       verbose bool;
+       indent uint;
+       scanner *Scanner.Scanner;
+       tokchan *<-chan *Scanner.Token;
+       
+       // Token
+       tok int;  // one token look-ahead
+       pos int;  // token source position
+       val string;  // token value (for IDENT, NUMBER, STRING only)
+
+       // Nesting level
+       level int;  // 0 = global scope, -1 = function/struct scope of global functions/structs, etc.
+};
+
+
+// ----------------------------------------------------------------------------
+// Support functions
+
+func (P *Parser) PrintIndent() {
+       for i := P.indent; i > 0; i-- {
+               print(". ");
+       }
+}
+
+
+func (P *Parser) Trace(msg string) {
+       if P.verbose {
+               P.PrintIndent();
+               print(msg, " {\n");
+       }
+       P.indent++;  // always, so proper identation is always checked
+}
+
+
+func (P *Parser) Ecart() {
+       P.indent--;  // always, so proper identation is always checked
+       if P.verbose {
+               P.PrintIndent();
+               print("}\n");
+       }
+}
+
+
+func (P *Parser) Next() {
+       if P.tokchan == nil {
+               P.tok, P.pos, P.val = P.scanner.Scan();
+       } else {
+               t := <-P.tokchan;
+               P.tok, P.pos, P.val = t.tok, t.pos, t.val;
+       }
+       if P.verbose {
+               P.PrintIndent();
+               print("[", P.pos, "] ", Scanner.TokenName(P.tok), "\n");
+       }
+}
+
+
+func (P *Parser) Open(verbose bool, scanner *Scanner.Scanner, tokchan *<-chan *Scanner.Token) {
+       P.verbose = verbose;
+       P.indent = 0;
+       P.scanner = scanner;
+       P.tokchan = tokchan;
+       P.Next();
+       P.level = 0;
+}
+
+
+func (P *Parser) Error(pos int, msg string) {
+       P.scanner.Error(pos, msg);
+}
+
+
+func (P *Parser) Expect(tok int) {
+       if P.tok != tok {
+               P.Error(P.pos, "expected '" + Scanner.TokenName(tok) + "', found '" + Scanner.TokenName(P.tok) + "'");
+       }
+       P.Next();  // make progress in any case
+}
+
+
+func (P *Parser) Optional(tok int) {
+       if P.tok == tok {
+               P.Next();
+       }
+}
+
+
+// ----------------------------------------------------------------------------
+// Scopes
+
+func (P *Parser) OpenScope() {
+}
+
+
+func (P *Parser) CloseScope() {
+}
+
+
+// ----------------------------------------------------------------------------
+// Common productions
+
+func (P *Parser) TryType() bool;
+func (P *Parser) ParseExpression() AST.Expr;
+func (P *Parser) TryStatement() bool;
+func (P *Parser) ParseDeclaration();
+
+
+func (P *Parser) ParseIdent() *AST.Ident {
+       P.Trace("Ident");
+
+       ident := new(AST.Ident);
+       ident.pos_, ident.val_ = P.pos, "";
+       if P.tok == Scanner.IDENT {
+               ident.val_ = P.val;
+               if P.verbose {
+                       P.PrintIndent();
+                       print("Ident = \"", ident.val_, "\"\n");
+               }
+               P.Next();
+       } else {
+               P.Expect(Scanner.IDENT);  // use Expect() error handling
+       }
+       
+       P.Ecart();
+       return ident;
+}
+
+
+func (P *Parser) ParseIdentList() {
+       P.Trace("IdentList");
+
+       P.ParseIdent();
+       for P.tok == Scanner.COMMA {
+               P.Next();
+               P.ParseIdent();
+       }
+
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseQualifiedIdent(ident *AST.Ident) AST.Expr {
+       P.Trace("QualifiedIdent");
+
+       if ident == nil {
+               ident = P.ParseIdent();
+       }
+       
+       P.Ecart();
+       return ident;
+}
+
+
+// ----------------------------------------------------------------------------
+// Types
+
+func (P *Parser) ParseType() {
+       P.Trace("Type");
+       
+       typ := P.TryType();
+       if !typ {
+               P.Error(P.pos, "type expected");
+       }
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseVarType() {
+       P.Trace("VarType");
+       
+       P.ParseType();
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseTypeName() AST.Expr {
+       P.Trace("TypeName");
+       
+       x := P.ParseQualifiedIdent(nil);
+
+       P.Ecart();
+       return x;
+}
+
+
+func (P *Parser) ParseArrayType() {
+       P.Trace("ArrayType");
+       
+       P.Expect(Scanner.LBRACK);
+       if P.tok != Scanner.RBRACK {
+               // TODO set typ.len
+               P.ParseExpression();
+       }
+       P.Expect(Scanner.RBRACK);
+
+       P.Ecart();      
+}
+
+
+func (P *Parser) ParseChannelType() {
+       P.Trace("ChannelType");
+       
+       if P.tok == Scanner.CHAN {
+               P.Next();
+               if P.tok == Scanner.ARROW {
+                       P.Next();
+               }
+       } else {
+               P.Expect(Scanner.ARROW);
+               P.Expect(Scanner.CHAN);
+       }
+       P.ParseVarType();
+
+       P.Ecart();      
+}
+
+
+func (P *Parser) ParseVarDeclList() {
+       P.Trace("VarDeclList");
+       
+       P.ParseIdentList();
+       P.ParseVarType();
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseParameterList() {
+       P.Trace("ParameterList");
+       
+       P.ParseVarDeclList();
+       for P.tok == Scanner.COMMA {
+               P.Next();
+               P.ParseVarDeclList();
+       }
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseParameters() {
+       P.Trace("Parameters");
+       
+       P.Expect(Scanner.LPAREN);
+       if P.tok != Scanner.RPAREN {
+               P.ParseParameterList();
+       }
+       P.Expect(Scanner.RPAREN);
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseResult() {
+       P.Trace("Result");
+       
+       if P.tok == Scanner.LPAREN {
+               // one or more named results
+               // TODO: here we allow empty returns - should proably fix this
+               P.ParseParameters();
+
+       } else {
+               // anonymous result
+               P.TryType();
+       }
+
+       P.Ecart();
+}
+
+
+// Signatures
+//
+// (params)
+// (params) type
+// (params) (results)
+
+func (P *Parser) ParseSignature() {
+       P.Trace("Signature");
+       
+       P.OpenScope();
+       P.level--;
+
+       P.ParseParameters();
+       P.ParseResult();
+
+       P.level++;
+       P.CloseScope();
+       
+       P.Ecart();
+}
+
+
+// Named signatures
+//
+//        ident (params)
+//        ident (params) type
+//        ident (params) (results)
+// (recv) ident (params)
+// (recv) ident (params) type
+// (recv) ident (params) (results)
+
+func (P *Parser) ParseNamedSignature() *AST.Ident {
+       P.Trace("NamedSignature");
+       
+       P.OpenScope();
+       P.level--;
+       p0 := 0;
+
+       if P.tok == Scanner.LPAREN {
+               recv_pos := P.pos;
+               P.ParseParameters();
+               //p0 = sig.entries.len;
+               if p0 != 1 {
+                       print("p0 = ", p0, "\n");
+                       P.Error(recv_pos, "must have exactly one receiver");
+                       panic("UNIMPLEMENTED (ParseNamedSignature)");
+                       // TODO do something useful here
+               }
+       }
+       
+       ident := P.ParseIdent();
+
+       P.ParseParameters();
+       
+       //r0 := sig.entries.len;
+       P.ParseResult();
+       P.level++;
+       P.CloseScope();
+       
+       P.Ecart();
+       return ident;
+}
+
+
+func (P *Parser) ParseFunctionType() {
+       P.Trace("FunctionType");
+       
+       typ := P.ParseSignature();
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseMethodDecl() {
+       P.Trace("MethodDecl");
+       
+       ident := P.ParseIdent();
+       P.OpenScope();
+       P.level--;
+       
+       P.ParseParameters();
+       
+       //r0 := sig.entries.len;
+       P.ParseResult();
+       P.level++;
+       P.CloseScope();
+       P.Optional(Scanner.SEMICOLON);
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseInterfaceType()  {
+       P.Trace("InterfaceType");
+       
+       P.Expect(Scanner.INTERFACE);
+       P.Expect(Scanner.LBRACE);
+       P.OpenScope();
+       P.level--;
+       for P.tok >= Scanner.IDENT {
+               P.ParseMethodDecl();
+       }
+       P.level++;
+       P.CloseScope();
+       P.Expect(Scanner.RBRACE);
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseMapType() {
+       P.Trace("MapType");
+       
+       P.Expect(Scanner.MAP);
+       P.Expect(Scanner.LBRACK);
+       P.ParseVarType();
+       P.Expect(Scanner.RBRACK);
+       P.ParseVarType();
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseStructType() {
+       P.Trace("StructType");
+       
+       P.Expect(Scanner.STRUCT);
+       P.Expect(Scanner.LBRACE);
+       P.OpenScope();
+       P.level--;
+       for P.tok >= Scanner.IDENT {
+               P.ParseVarDeclList();
+               if P.tok != Scanner.RBRACE {
+                       P.Expect(Scanner.SEMICOLON);
+               }
+       }
+       P.Optional(Scanner.SEMICOLON);
+       P.level++;
+       P.CloseScope();
+       P.Expect(Scanner.RBRACE);
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParsePointerType() {
+       P.Trace("PointerType");
+       
+       P.Expect(Scanner.MUL);
+       P.ParseType();
+       
+       P.Ecart();
+}
+
+
+// Returns false if no type was found.
+func (P *Parser) TryType() bool {
+       P.Trace("Type (try)");
+       
+       found := true;
+       switch P.tok {
+       case Scanner.IDENT: P.ParseTypeName();
+       case Scanner.LBRACK: P.ParseArrayType();
+       case Scanner.CHAN, Scanner.ARROW: P.ParseChannelType();
+       case Scanner.INTERFACE: P.ParseInterfaceType();
+       case Scanner.LPAREN: P.ParseFunctionType();
+       case Scanner.MAP: P.ParseMapType();
+       case Scanner.STRUCT: P.ParseStructType();
+       case Scanner.MUL: P.ParsePointerType();
+       default: found = false;
+       }
+
+       P.Ecart();
+       return found;
+}
+
+
+// ----------------------------------------------------------------------------
+// Blocks
+
+func (P *Parser) ParseStatement() {
+       P.Trace("Statement");
+       if !P.TryStatement() {
+               P.Error(P.pos, "statement expected");
+               P.Next();  // make progress
+       }
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseStatementList() {
+       P.Trace("StatementList");
+       for P.TryStatement() {
+               P.Optional(Scanner.SEMICOLON);
+       }
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseBlock() {
+       P.Trace("Block");
+       
+       P.Expect(Scanner.LBRACE);
+       P.OpenScope();
+       if P.tok != Scanner.RBRACE && P.tok != Scanner.SEMICOLON {
+               P.ParseStatementList();
+       }
+       P.Optional(Scanner.SEMICOLON);
+       P.CloseScope();
+       P.Expect(Scanner.RBRACE);
+       
+       P.Ecart();
+}
+
+
+// ----------------------------------------------------------------------------
+// Expressions
+
+func (P *Parser) ParseExpressionList() {
+       P.Trace("ExpressionList");
+       
+       P.ParseExpression();
+       for P.tok == Scanner.COMMA {
+               P.Next();
+               P.ParseExpression();
+       }
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseFunctionLit() AST.Expr {
+       P.Trace("FunctionLit");
+       
+       P.Expect(Scanner.FUNC);
+       P.ParseFunctionType();
+       P.ParseBlock();
+       
+       P.Ecart();
+       var x AST.Expr;
+       return x;
+}
+
+
+func (P *Parser) ParseExpressionPair() {
+       P.Trace("ExpressionPair");
+
+       P.ParseExpression();
+       P.Expect(Scanner.COLON);
+       P.ParseExpression();
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseExpressionPairList() {
+       P.Trace("ExpressionPairList");
+
+       P.ParseExpressionPair();
+       for P.tok == Scanner.COMMA {
+               P.ParseExpressionPair();
+       }
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseCompositeLit() AST.Expr {
+       P.Trace("CompositeLit");
+       
+       P.Expect(Scanner.HASH);
+       P.ParseType();
+       P.Expect(Scanner.LBRACE);
+       // TODO: should allow trailing ','
+       if P.tok != Scanner.RBRACE {
+               P.ParseExpression();
+               if P.tok == Scanner.COMMA {
+                       P.Next();
+                       if P.tok != Scanner.RBRACE {
+                               P.ParseExpressionList();
+                       }
+               } else if P.tok == Scanner.COLON {
+                       P.Next();
+                       P.ParseExpression();
+                       if P.tok == Scanner.COMMA {
+                               P.Next();
+                               if P.tok != Scanner.RBRACE {
+                                       P.ParseExpressionPairList();
+                               }
+                       }
+               }
+       }
+       P.Expect(Scanner.RBRACE);
+
+       P.Ecart();
+       var x AST.Expr;
+       return x;
+}
+
+
+func (P *Parser) ParseOperand(ident *AST.Ident) AST.Expr {
+       P.Trace("Operand");
+
+       if ident == nil && P.tok == Scanner.IDENT {
+               // no look-ahead yet
+               ident = P.ParseIdent();
+       }
+
+       var x AST.Expr;
+
+       if ident != nil {
+               // we have an identifier
+
+       } else {
+       
+               switch P.tok {
+               case Scanner.IDENT:
+                       panic("UNREACHABLE");
+                       
+               case Scanner.LPAREN:
+                       P.Next();
+                       x = P.ParseExpression();
+                       P.Expect(Scanner.RPAREN);
+                       
+               case Scanner.INT:
+                       P.Next();
+
+               case Scanner.FLOAT:
+                       P.Next();
+
+               case Scanner.STRING:
+                       P.Next();
+
+               case Scanner.FUNC:
+                       P.ParseFunctionLit();
+                       
+               case Scanner.HASH:
+                       P.ParseCompositeLit();
+
+               default:
+                       P.Error(P.pos, "operand expected");
+                       P.Next();  // make progress
+               }
+       
+       }
+
+       P.Ecart();
+       return x;
+}
+
+
+func (P *Parser) ParseSelectorOrTypeAssertion(x AST.Expr) AST.Expr {
+       P.Trace("SelectorOrTypeAssertion");
+
+       P.Expect(Scanner.PERIOD);
+       pos := P.pos;
+       
+       if P.tok >= Scanner.IDENT {
+               P.ParseIdent();
+       } else {
+               P.Expect(Scanner.LPAREN);
+               P.ParseType();
+               P.Expect(Scanner.RPAREN);
+       }
+       
+       P.Ecart();
+       return x;
+}
+
+
+func (P *Parser) ParseIndexOrSlice(x AST.Expr) AST.Expr {
+       P.Trace("IndexOrSlice");
+       
+       P.Expect(Scanner.LBRACK);
+       i := P.ParseExpression();
+       if P.tok == Scanner.COLON {
+               P.Next();
+               j := P.ParseExpression();
+       }
+       P.Expect(Scanner.RBRACK);
+               
+       P.Ecart();
+       return x;
+}
+
+
+func (P *Parser) ParseCall(x AST.Expr) AST.Expr {
+       P.Trace("Call");
+
+       P.Expect(Scanner.LPAREN);
+       if P.tok != Scanner.RPAREN {
+               P.ParseExpressionList();
+       }
+       P.Expect(Scanner.RPAREN);
+       
+       P.Ecart();
+       return x;
+}
+
+
+func (P *Parser) ParsePrimaryExpr(ident *AST.Ident) AST.Expr {
+       P.Trace("PrimaryExpr");
+       
+       x := P.ParseOperand(ident);
+       for {
+               switch P.tok {
+               case Scanner.PERIOD: x = P.ParseSelectorOrTypeAssertion(x);
+               case Scanner.LBRACK: x = P.ParseIndexOrSlice(x);
+               case Scanner.LPAREN: x = P.ParseCall(x);
+               default: goto exit;
+               }
+       }
+
+exit:
+       P.Ecart();
+       return x;
+}
+
+
+func (P *Parser) ParseUnaryExpr() AST.Expr {
+       P.Trace("UnaryExpr");
+       
+       switch P.tok {
+       case Scanner.ADD: fallthrough;
+       case Scanner.SUB: fallthrough;
+       case Scanner.NOT: fallthrough;
+       case Scanner.XOR: fallthrough;
+       case Scanner.MUL: fallthrough;
+       case Scanner.ARROW: fallthrough;
+       case Scanner.AND:
+               P.Next();
+               x := P.ParseUnaryExpr();
+               P.Ecart();
+               return x;  // TODO fix this
+       }
+       
+       x := P.ParsePrimaryExpr(nil);
+       P.Ecart();
+       return x;  // TODO fix this
+}
+
+
+func Precedence(tok int) int {
+       // TODO should use a map or array here for lookup
+       switch tok {
+       case Scanner.LOR:
+               return 1;
+       case Scanner.LAND:
+               return 2;
+       case Scanner.ARROW:
+               return 3;
+       case Scanner.EQL, Scanner.NEQ, Scanner.LSS, Scanner.LEQ, Scanner.GTR, Scanner.GEQ:
+               return 4;
+       case Scanner.ADD, Scanner.SUB, Scanner.OR, Scanner.XOR:
+               return 5;
+       case Scanner.MUL, Scanner.QUO, Scanner.REM, Scanner.SHL, Scanner.SHR, Scanner.AND:
+               return 6;
+       }
+       return 0;
+}
+
+
+func (P *Parser) ParseBinaryExpr(ident *AST.Ident, prec1 int) AST.Expr {
+       P.Trace("BinaryExpr");
+       
+       var x AST.Expr;
+       if ident != nil {
+               x = P.ParsePrimaryExpr(ident);
+       } else {
+               x = P.ParseUnaryExpr();
+       }
+
+       for prec := Precedence(P.tok); prec >= prec1; prec-- {
+               for Precedence(P.tok) == prec {
+                       P.Next();
+                       y := P.ParseBinaryExpr(nil, prec + 1);
+               }
+       }
+       
+       P.Ecart();
+       return x;
+}
+
+
+// Expressions where the first token may be an identifier which has already been consumed.
+func (P *Parser) ParseIdentExpression(ident *AST.Ident) AST.Expr {
+       P.Trace("IdentExpression");
+       indent := P.indent;
+       
+       x := P.ParseBinaryExpr(ident, 1);
+       
+       if indent != P.indent {
+               panic("imbalanced tracing code (Expression)");
+       }
+
+       P.Ecart();
+       return x;
+}
+
+
+func (P *Parser) ParseExpression() AST.Expr {
+       P.Trace("Expression");
+       
+       x := P.ParseIdentExpression(nil);
+
+       P.Ecart();
+       return x;
+}
+
+
+// ----------------------------------------------------------------------------
+// Statements
+
+func (P *Parser) ParseSimpleStat() {
+       P.Trace("SimpleStat");
+       
+       P.ParseExpressionList();
+       
+       switch P.tok {
+       case Scanner.COLON:
+               // label declaration
+               P.Next();  // consume ":"
+               
+       case Scanner.DEFINE:
+               // variable declaration
+               P.Next();  // consume ":="
+               P.ParseExpressionList();
+               
+       case Scanner.ASSIGN: fallthrough;
+       case Scanner.ADD_ASSIGN: fallthrough;
+       case Scanner.SUB_ASSIGN: fallthrough;
+       case Scanner.MUL_ASSIGN: fallthrough;
+       case Scanner.QUO_ASSIGN: fallthrough;
+       case Scanner.REM_ASSIGN: fallthrough;
+       case Scanner.AND_ASSIGN: fallthrough;
+       case Scanner.OR_ASSIGN: fallthrough;
+       case Scanner.XOR_ASSIGN: fallthrough;
+       case Scanner.SHL_ASSIGN: fallthrough;
+       case Scanner.SHR_ASSIGN:
+               P.Next();
+               P.ParseExpressionList();
+               
+       default:
+               if P.tok == Scanner.INC || P.tok == Scanner.DEC {
+                       P.Next();
+               }
+       }
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseGoStat() {
+       P.Trace("GoStat");
+       
+       P.Expect(Scanner.GO);
+       P.ParseExpression();
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseReturnStat() {
+       P.Trace("ReturnStat");
+       
+       P.Expect(Scanner.RETURN);
+       if P.tok != Scanner.SEMICOLON && P.tok != Scanner.RBRACE {
+               P.ParseExpressionList();
+       }
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseControlFlowStat(tok int) {
+       P.Trace("ControlFlowStat");
+       
+       P.Expect(tok);
+       if P.tok == Scanner.IDENT {
+               P.ParseIdent();
+       }
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseIfStat() *AST.IfStat {
+       P.Trace("IfStat");
+       
+       P.Expect(Scanner.IF);
+       P.OpenScope();
+       if P.tok != Scanner.LBRACE {
+               if P.tok != Scanner.SEMICOLON {
+                       P.ParseSimpleStat();
+               }
+               if P.tok == Scanner.SEMICOLON {
+                       P.Next();
+                       if P.tok != Scanner.LBRACE {
+                               P.ParseExpression();
+                       }
+               }
+       }
+       P.ParseBlock();
+       if P.tok == Scanner.ELSE {
+               P.Next();
+               if P.tok == Scanner.IF {
+                       P.ParseIfStat();
+               } else {
+                       // TODO should be P.ParseBlock()
+                       P.ParseStatement();
+               }
+       }
+       P.CloseScope();
+       
+       P.Ecart();
+       return nil;
+}
+
+
+func (P *Parser) ParseForStat() {
+       P.Trace("ForStat");
+       
+       P.Expect(Scanner.FOR);
+       P.OpenScope();
+       if P.tok != Scanner.LBRACE {
+               if P.tok != Scanner.SEMICOLON {
+                       P.ParseSimpleStat();
+               }
+               if P.tok == Scanner.SEMICOLON {
+                       P.Next();
+                       if P.tok != Scanner.SEMICOLON {
+                               P.ParseExpression();
+                       }
+                       P.Expect(Scanner.SEMICOLON);
+                       if P.tok != Scanner.LBRACE {
+                               P.ParseSimpleStat();
+                       }
+               }
+       }
+       P.ParseBlock();
+       P.CloseScope();
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseCase() {
+       P.Trace("Case");
+       
+       if P.tok == Scanner.CASE {
+               P.Next();
+               P.ParseExpressionList();
+       } else {
+               P.Expect(Scanner.DEFAULT);
+       }
+       P.Expect(Scanner.COLON);
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseCaseClause() {
+       P.Trace("CaseClause");
+       
+       P.ParseCase();
+       if P.tok != Scanner.FALLTHROUGH && P.tok != Scanner.RBRACE {
+               P.ParseStatementList();
+               P.Optional(Scanner.SEMICOLON);
+       }
+       if P.tok == Scanner.FALLTHROUGH {
+               P.Next();
+               P.Optional(Scanner.SEMICOLON);
+       }
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseSwitchStat() {
+       P.Trace("SwitchStat");
+       
+       P.Expect(Scanner.SWITCH);
+       P.OpenScope();
+       if P.tok != Scanner.LBRACE {
+               if P.tok != Scanner.SEMICOLON {
+                       P.ParseSimpleStat();
+               }
+               if P.tok == Scanner.SEMICOLON {
+                       P.Next();
+                       if P.tok != Scanner.LBRACE {
+                               P.ParseExpression();
+                       }
+               }
+       }
+       P.Expect(Scanner.LBRACE);
+       for P.tok == Scanner.CASE || P.tok == Scanner.DEFAULT {
+               P.ParseCaseClause();
+       }
+       P.Expect(Scanner.RBRACE);
+       P.CloseScope();
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseCommCase() {
+  P.Trace("CommCase");
+  
+  if P.tok == Scanner.CASE {
+       P.Next();
+       if P.tok == Scanner.GTR {
+               // send
+               P.Next();
+               P.ParseExpression();
+               P.Expect(Scanner.EQL);
+               P.ParseExpression();
+       } else {
+               // receive
+               if P.tok != Scanner.LSS {
+                       P.ParseIdent();
+                       P.Expect(Scanner.ASSIGN);
+               }
+               P.Expect(Scanner.LSS);
+               P.ParseExpression();
+       }
+  } else {
+       P.Expect(Scanner.DEFAULT);
+  }
+  P.Expect(Scanner.COLON);
+  
+  P.Ecart();
+}
+
+
+func (P *Parser) ParseCommClause() {
+       P.Trace("CommClause");
+       
+       P.ParseCommCase();
+       if P.tok != Scanner.CASE && P.tok != Scanner.DEFAULT && P.tok != Scanner.RBRACE {
+               P.ParseStatementList();
+               P.Optional(Scanner.SEMICOLON);
+       }
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseRangeStat() {
+       P.Trace("RangeStat");
+       
+       P.Expect(Scanner.RANGE);
+       P.ParseIdentList();
+       P.Expect(Scanner.DEFINE);
+       P.ParseExpression();
+       P.ParseBlock();
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseSelectStat() {
+       P.Trace("SelectStat");
+       
+       P.Expect(Scanner.SELECT);
+       P.Expect(Scanner.LBRACE);
+       for P.tok != Scanner.RBRACE && P.tok != Scanner.EOF {
+               P.ParseCommClause();
+       }
+       P.Next();
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) TryStatement() bool {
+       P.Trace("Statement (try)");
+       indent := P.indent;
+
+       res := true;
+       switch P.tok {
+       case Scanner.CONST: fallthrough;
+       case Scanner.TYPE: fallthrough;
+       case Scanner.VAR:
+               P.ParseDeclaration();
+       case Scanner.FUNC:
+               // for now we do not allow local function declarations
+               fallthrough;
+       case Scanner.MUL, Scanner.ARROW, Scanner.IDENT, Scanner.LPAREN:
+               P.ParseSimpleStat();
+       case Scanner.GO:
+               P.ParseGoStat();
+       case Scanner.RETURN:
+               P.ParseReturnStat();
+       case Scanner.BREAK, Scanner.CONTINUE, Scanner.GOTO:
+               P.ParseControlFlowStat(P.tok);
+       case Scanner.LBRACE:
+               P.ParseBlock();
+       case Scanner.IF:
+               P.ParseIfStat();
+       case Scanner.FOR:
+               P.ParseForStat();
+       case Scanner.SWITCH:
+               P.ParseSwitchStat();
+       case Scanner.RANGE:
+               P.ParseRangeStat();
+       case Scanner.SELECT:
+               P.ParseSelectStat();
+       default:
+               // no statement found
+               res = false;
+       }
+
+       if indent != P.indent {
+               panic("imbalanced tracing code (Statement)");
+       }
+       P.Ecart();
+       return res;
+}
+
+
+// ----------------------------------------------------------------------------
+// Declarations
+
+func (P *Parser) ParseImportSpec() {
+       P.Trace("ImportSpec");
+       
+       if P.tok == Scanner.PERIOD {
+               P.Error(P.pos, `"import ." not yet handled properly`);
+               P.Next();
+       } else if P.tok == Scanner.IDENT {
+               P.ParseIdent();
+       }
+       
+       if P.tok == Scanner.STRING {
+               // TODO eventually the scanner should strip the quotes
+               P.Next();
+       } else {
+               P.Expect(Scanner.STRING);  // use Expect() error handling
+       }
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseConstSpec(exported bool) {
+       P.Trace("ConstSpec");
+       
+       list := P.ParseIdent();
+       P.TryType();
+       
+       if P.tok == Scanner.ASSIGN {
+               P.Next();
+               P.ParseExpressionList();
+       }
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseTypeSpec(exported bool) {
+       P.Trace("TypeSpec");
+
+       ident := P.ParseIdent();
+       P.ParseType();
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseVarSpec(exported bool) {
+       P.Trace("VarSpec");
+       
+       list := P.ParseIdentList();
+       if P.tok == Scanner.ASSIGN {
+               P.Next();
+               P.ParseExpressionList();
+       } else {
+               P.ParseVarType();
+               if P.tok == Scanner.ASSIGN {
+                       P.Next();
+                       P.ParseExpressionList();
+               }
+       }
+       
+       P.Ecart();
+}
+
+
+// TODO With method variables, we wouldn't need this dispatch function.
+func (P *Parser) ParseSpec(exported bool, keyword int) {
+       switch keyword {
+       case Scanner.IMPORT: P.ParseImportSpec();
+       case Scanner.CONST: P.ParseConstSpec(exported);
+       case Scanner.TYPE: P.ParseTypeSpec(exported);
+       case Scanner.VAR: P.ParseVarSpec(exported);
+       default: panic("UNREACHABLE");
+       }
+}
+
+
+func (P *Parser) ParseDecl(exported bool, keyword int) {
+       P.Trace("Decl");
+       
+       P.Expect(keyword);
+       if P.tok == Scanner.LPAREN {
+               P.Next();
+               for P.tok == Scanner.IDENT {
+                       P.ParseSpec(exported, keyword);
+                       if P.tok != Scanner.RPAREN {
+                               // P.Expect(Scanner.SEMICOLON);
+                               P.Optional(Scanner.SEMICOLON);  // TODO this seems wrong! (needed for math.go)
+                       }
+               }
+               P.Next();
+       } else {
+               P.ParseSpec(exported, keyword);
+       }
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseFuncDecl(exported bool) {
+       P.Trace("FuncDecl");
+       
+       P.Expect(Scanner.FUNC);
+       ident := P.ParseNamedSignature();
+       if P.tok == Scanner.SEMICOLON {
+               // forward declaration
+               P.Next();
+       } else {
+               P.ParseBlock();
+       }
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseExportDecl() {
+       P.Trace("ExportDecl");
+       
+       // TODO This is deprecated syntax and should go away eventually.
+       // (Also at the moment the syntax is everything goes...)
+       //P.Expect(Scanner.EXPORT);
+
+       has_paren := false;
+       if P.tok == Scanner.LPAREN {
+               P.Next();
+               has_paren = true;
+       }
+       for P.tok == Scanner.IDENT {
+               ident := P.ParseIdent();
+               P.Optional(Scanner.COMMA);  // TODO this seems wrong
+       }
+       if has_paren {
+               P.Expect(Scanner.RPAREN)
+       }
+       
+       P.Ecart();
+}
+
+
+func (P *Parser) ParseDeclaration() {
+       P.Trace("Declaration");
+       indent := P.indent;
+       
+       exported := false;
+       if P.tok == Scanner.EXPORT {
+               if P.level == 0 {
+                       exported = true;
+               } else {
+                       P.Error(P.pos, "local declarations cannot be exported");
+               }
+               P.Next();
+       }
+       
+       switch P.tok {
+       case Scanner.CONST, Scanner.TYPE, Scanner.VAR:
+               P.ParseDecl(exported, P.tok);
+       case Scanner.FUNC:
+               P.ParseFuncDecl(exported);
+       case Scanner.EXPORT:
+               if exported {
+                       P.Error(P.pos, "cannot mark export declaration for export");
+               }
+               P.Next();
+               P.ParseExportDecl();
+       default:
+               if exported && (P.tok == Scanner.IDENT || P.tok == Scanner.LPAREN) {
+                       P.ParseExportDecl();
+               } else {
+                       P.Error(P.pos, "declaration expected");
+                       P.Next();  // make progress
+               }
+       }
+       
+       if indent != P.indent {
+               panic("imbalanced tracing code (Declaration)");
+       }
+       P.Ecart();
+}
+
+
+// ----------------------------------------------------------------------------
+// Program
+
+func (P *Parser) ParseProgram() {
+       P.Trace("Program");
+       
+       P.OpenScope();
+       P.Expect(Scanner.PACKAGE);
+       obj := P.ParseIdent();
+       P.Optional(Scanner.SEMICOLON);
+       
+       {       P.OpenScope();
+               if P.level != 0 {
+                       panic("incorrect scope level");
+               }
+               
+               for P.tok == Scanner.IMPORT {
+                       P.ParseDecl(false, Scanner.IMPORT);
+                       P.Optional(Scanner.SEMICOLON);
+               }
+               
+               for P.tok != Scanner.EOF {
+                       P.ParseDeclaration();
+                       P.Optional(Scanner.SEMICOLON);
+               }
+               
+               if P.level != 0 {
+                       panic("incorrect scope level");
+               }
+               P.CloseScope();
+       }
+       
+       P.CloseScope();
+       P.Ecart();
+}
diff --git a/usr/gri/pretty/platform.go b/usr/gri/pretty/platform.go
new file mode 100644 (file)
index 0000000..c76d591
--- /dev/null
@@ -0,0 +1,70 @@
+// Copyright 2009 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package Platform
+
+import Utils "utils"
+
+
+// ----------------------------------------------------------------------------
+// Environment
+
+export var
+       GOARCH,
+       GOOS,
+       GOROOT,
+       USER string;
+
+
+func GetEnv(key string) string {
+       n := len(key);
+       for i := 0; i < sys.envc(); i++ {
+               v := sys.envv(i);
+               if v[0 : n] == key {
+                       return v[n + 1 : len(v)];  // +1: trim "="
+               }
+       }
+       return "";
+}
+
+
+func init() {
+       GOARCH = GetEnv("GOARCH");
+       GOOS = GetEnv("GOOS");
+       GOROOT = GetEnv("GOROOT");
+       USER = GetEnv("USER");
+}
+
+
+// ----------------------------------------------------------------------------
+// I/O
+
+export const (
+       MAGIC_obj_file = "@gri-go.7@v0";  // make it clear thar it cannot be a source file
+       src_file_ext = ".go";
+       obj_file_ext = ".7";
+)
+
+
+export func ReadObjectFile(filename string) (data string, ok bool) {
+       data, ok = sys.readfile(filename + obj_file_ext);
+       magic := MAGIC_obj_file;  // TODO remove once len(constant) works
+       if ok && len(data) >= len(magic) && data[0 : len(magic)] == magic {
+               return data, ok;
+       }
+       return "", false;
+}
+
+
+export func ReadSourceFile(name string) (data string, ok bool) {
+       name = Utils.TrimExt(name, src_file_ext) + src_file_ext;
+       data, ok = sys.readfile(name);
+       return data, ok;
+}
+
+
+export func WriteObjectFile(name string, data string) bool {
+       name = Utils.TrimExt(Utils.BaseName(name), src_file_ext) + obj_file_ext;
+       return sys.writefile(name, data);
+}
diff --git a/usr/gri/pretty/pretty.go b/usr/gri/pretty/pretty.go
new file mode 100644 (file)
index 0000000..48f470f
--- /dev/null
@@ -0,0 +1,48 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import Flag "flag"
+import Platform "platform"
+import Scanner "scanner"
+import AST "ast"  // should not be needed
+import Parser "parser"
+import Printer "printer"
+
+
+var (
+    verbose = Flag.Bool("v", false, nil, "verbose mode");
+    sixg = Flag.Bool("6g", false, nil, "6g compatibility mode");
+    tokenchan = Flag.Bool("token_chan", false, nil, "use token channel for scanner-parser connection");
+)
+
+
+func main() {
+       Flag.Parse();
+       
+       // process files
+       for i := 0; i < Flag.NArg(); i++ {
+           src_file := Flag.Arg(i);
+
+           src, ok := Platform.ReadSourceFile(src_file);
+           if !ok {
+                print("cannot open ", src_file, "\n");
+                return;
+            }
+
+           scanner := new(Scanner.Scanner);
+            scanner.Open(src_file, src);
+
+           var tstream *<-chan *Scanner.Token;
+            if tokenchan.BVal() {
+                tstream = scanner.TokenStream();
+           }
+
+           parser := new(Parser.Parser);
+           parser.Open(verbose.BVal(), scanner, tstream);
+
+           parser.ParseProgram();
+       }
+}
diff --git a/usr/gri/pretty/printer.go b/usr/gri/pretty/printer.go
new file mode 100644 (file)
index 0000000..e4aa2ef
--- /dev/null
@@ -0,0 +1,8 @@
+// Copyright 2009 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package Printer;
+
+
+// TODO Fill in the code to print the AST
diff --git a/usr/gri/pretty/scanner.go b/usr/gri/pretty/scanner.go
new file mode 100644 (file)
index 0000000..1e2645c
--- /dev/null
@@ -0,0 +1,792 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package Scanner
+
+import Platform "platform"
+import Utils "utils"
+
+
+export const (
+       ILLEGAL = iota;
+       EOF;
+       INT;
+       FLOAT;
+       STRING;
+
+       COMMA;
+       COLON;
+       SEMICOLON;
+       PERIOD;
+
+       LPAREN;
+       RPAREN;
+       LBRACK;
+       RBRACK;
+       LBRACE;
+       RBRACE;
+       
+       ASSIGN;
+       DEFINE;
+       
+       INC;
+       DEC;
+       NOT;
+       
+       AND;
+       OR;
+       XOR;
+       
+       ADD;
+       SUB;
+       MUL;
+       QUO;
+       REM;
+       
+       EQL;
+       NEQ;
+       LSS;
+       LEQ;
+       GTR;
+       GEQ;
+
+       SHL;
+       SHR;
+       
+       ARROW;
+       HASH;
+
+       ADD_ASSIGN;
+       SUB_ASSIGN;
+       MUL_ASSIGN;
+       QUO_ASSIGN;
+       REM_ASSIGN;
+
+       AND_ASSIGN;
+       OR_ASSIGN;
+       XOR_ASSIGN;
+       
+       SHL_ASSIGN;
+       SHR_ASSIGN;
+
+       LAND;
+       LOR;
+       
+       // IDENT must be immediately before keywords
+       IDENT;
+
+       // keywords
+       KEYWORDS_BEG;
+       BREAK;
+       CASE;
+       CHAN;
+       CONST;
+       CONTINUE;
+       DEFAULT;
+       ELSE;
+       EXPORT;
+       FALLTHROUGH;
+       FOR;
+       FUNC;
+       GO;
+       GOTO;
+       IF;
+       IMPORT;
+       INTERFACE;
+       MAP;
+       PACKAGE;
+       RANGE;
+       RETURN;
+       SELECT;
+       STRUCT;
+       SWITCH;
+       TYPE;
+       VAR;
+       KEYWORDS_END;
+)
+
+
+var Keywords *map [string] int;
+var VerboseMsgs bool;  // error message customization
+
+
+export func TokenName(tok int) string {
+       switch (tok) {
+       case ILLEGAL: return "illegal";
+       case EOF: return "eof";
+       case INT: return "int";
+       case FLOAT: return "float";
+       case STRING: return "string";
+
+       case COMMA: return ",";
+       case COLON: return ":";
+       case SEMICOLON: return ";";
+       case PERIOD: return ".";
+
+       case LPAREN: return "(";
+       case RPAREN: return ")";
+       case LBRACK: return "[";
+       case RBRACK: return "]";
+       case LBRACE: return "LBRACE";
+       case RBRACE: return "RBRACE";
+
+       case ASSIGN: return "=";
+       case DEFINE: return ":=";
+       
+       case INC: return "++";
+       case DEC: return "--";
+       case NOT: return "!";
+
+       case AND: return "&";
+       case OR: return "|";
+       case XOR: return "^";
+       
+       case ADD: return "+";
+       case SUB: return "-";
+       case MUL: return "*";
+       case QUO: return "/";
+       case REM: return "%";
+       
+       case EQL: return "==";
+       case NEQ: return "!=";
+       case LSS: return "<";
+       case LEQ: return "<=";
+       case GTR: return ">";
+       case GEQ: return ">=";
+
+       case SHL: return "<<";
+       case SHR: return ">>";
+       
+       case ARROW: return "<-";
+       case HASH: return "#";
+
+       case ADD_ASSIGN: return "+=";
+       case SUB_ASSIGN: return "-=";
+       case MUL_ASSIGN: return "+=";
+       case QUO_ASSIGN: return "/=";
+       case REM_ASSIGN: return "%=";
+
+       case AND_ASSIGN: return "&=";
+       case OR_ASSIGN: return "|=";
+       case XOR_ASSIGN: return "^=";
+
+       case SHL_ASSIGN: return "<<=";
+       case SHR_ASSIGN: return ">>=";
+
+       case LAND: return "&&";
+       case LOR: return "||";
+
+       case IDENT: return "ident";
+
+       case BREAK: return "break";
+       case CASE: return "case";
+       case CHAN: return "chan";
+       case CONST: return "const";
+       case CONTINUE: return "continue";
+       case DEFAULT: return "default";
+       case ELSE: return "else";
+       case EXPORT: return "export";
+       case FALLTHROUGH: return "fallthrough";
+       case FOR: return "for";
+       case FUNC: return "func";
+       case GO: return "go";
+       case GOTO: return "goto";
+       case IF: return "if";
+       case IMPORT: return "import";
+       case INTERFACE: return "interface";
+       case MAP: return "map";
+       case PACKAGE: return "package";
+       case RANGE: return "range";
+       case RETURN: return "return";
+       case SELECT: return "select";
+       case STRUCT: return "struct";
+       case SWITCH: return "switch";
+       case TYPE: return "type";
+       case VAR: return "var";
+       }
+       
+       return "???";
+}
+
+
+func init() {
+       Keywords = new(map [string] int);
+       
+       for i := KEYWORDS_BEG; i <= KEYWORDS_END; i++ {
+         Keywords[TokenName(i)] = i;
+       }
+       
+       // Provide column information in error messages for gri only...
+       VerboseMsgs = Platform.USER == "gri";
+}
+
+
+func is_whitespace(ch int) bool {
+       return ch == ' ' || ch == '\r' || ch == '\n' || ch == '\t';
+}
+
+
+func is_letter(ch int) bool {
+       return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 128 ;
+}
+
+
+func digit_val(ch int) int {
+       if '0' <= ch && ch <= '9' {
+               return ch - '0';
+       }
+       if 'a' <= ch && ch <= 'f' {
+               return ch - 'a' + 10;
+       }
+       if 'A' <= ch && ch <= 'F' {
+               return ch - 'A' + 10;
+       }
+       return 16;  // larger than any legal digit val
+}
+
+
+export type Scanner struct {
+       filename string;  // error reporting only
+       nerrors int;  // number of errors
+       errpos int;  // last error position
+       
+       src string;  // scanned source
+       pos int;  // current reading position
+       ch int;  // one char look-ahead
+       chpos int;  // position of ch
+}
+
+
+// Read the next Unicode char into S.ch.
+// S.ch < 0 means end-of-file.
+//
+func (S *Scanner) Next() {
+       const (
+               Bit1 = 7;
+               Bitx = 6;
+               Bit2 = 5;
+               Bit3 = 4;
+               Bit4 = 3;
+
+               T1 = (1 << (Bit1 + 1) - 1) ^ 0xFF;  // 0000 0000
+               Tx = (1 << (Bitx + 1) - 1) ^ 0xFF;  // 1000 0000
+               T2 = (1 << (Bit2 + 1) - 1) ^ 0xFF;  // 1100 0000
+               T3 = (1 << (Bit3 + 1) - 1) ^ 0xFF;  // 1110 0000
+               T4 = (1 << (Bit4 + 1) - 1) ^ 0xFF;  // 1111 0000
+
+               Rune1 = 1 << (Bit1 + 0*Bitx) - 1;  // 0000 0000 0111 1111
+               Rune2 = 1 << (Bit2 + 1*Bitx) - 1;  // 0000 0111 1111 1111
+               Rune3 = 1 << (Bit3 + 2*Bitx) - 1;  // 1111 1111 1111 1111
+
+               Maskx = 0x3F;  // 1 << Bitx - 1;  // 0011 1111
+               Testx = 0xC0;  // Maskx ^ 0xFF;  // 1100 0000
+
+               Bad     = 0xFFFD;  // Runeerror
+       );
+
+       src := S.src;
+       lim := len(src);
+       pos := S.pos;
+       
+       // 1-byte sequence
+       // 0000-007F => T1
+       if pos >= lim {
+               S.ch = -1;  // end of file
+               S.chpos = lim;
+               return;
+       }
+       c0 := int(src[pos]);
+       pos++;
+       if c0 < Tx {
+               S.ch = c0;
+               S.chpos = S.pos;
+               S.pos = pos;
+               return;
+       }
+
+       // 2-byte sequence
+       // 0080-07FF => T2 Tx
+       if pos >= lim {
+               goto bad;
+       }
+       c1 := int(src[pos]) ^ Tx;
+       pos++;
+       if c1 & Testx != 0 {
+               goto bad;
+       }
+       if c0 < T3 {
+               if c0 < T2 {
+                       goto bad;
+               }
+               r := (c0 << Bitx | c1) & Rune2;
+               if  r <= Rune1 {
+                       goto bad;
+               }
+               S.ch = r;
+               S.chpos = S.pos;
+               S.pos = pos;
+               return;
+       }
+
+       // 3-byte sequence
+       // 0800-FFFF => T3 Tx Tx
+       if pos >= lim {
+               goto bad;
+       }
+       c2 := int(src[pos]) ^ Tx;
+       pos++;
+       if c2 & Testx != 0 {
+               goto bad;
+       }
+       if c0 < T4 {
+               r := (((c0 << Bitx | c1) << Bitx) | c2) & Rune3;
+               if r <= Rune2 {
+                       goto bad;
+               }
+               S.ch = r;
+               S.chpos = S.pos;
+               S.pos = pos;
+               return;
+       }
+
+       // bad encoding
+bad:
+       S.ch = Bad;
+       S.chpos = S.pos;
+       S.pos += 1;
+       return;
+}
+
+
+// Compute (line, column) information for a given source position.
+func (S *Scanner) LineCol(pos int) (line, col int) {
+       line = 1;
+       lpos := 0;
+       
+       src := S.src;
+       if pos > len(src) {
+               pos = len(src);
+       }
+
+       for i := 0; i < pos; i++ {
+               if src[i] == '\n' {
+                       line++;
+                       lpos = i;
+               }
+       }
+       
+       return line, pos - lpos;
+}
+
+
+func (S *Scanner) Error(pos int, msg string) {
+       const errdist = 10;
+       delta := pos - S.errpos;  // may be negative!
+       if delta < 0 {
+               delta = -delta;
+       }
+       if delta > errdist || S.nerrors == 0 /* always report first error */ {
+               print(S.filename);
+               if pos >= 0 {
+                       // print position
+                       line, col := S.LineCol(pos);
+                       if VerboseMsgs {
+                               print(":", line, ":", col);
+                       } else {
+                               print(":", line);
+                       }
+               }
+               print(": ", msg, "\n");
+               S.nerrors++;
+               S.errpos = pos;
+       }
+       
+       if S.nerrors >= 10 {
+               sys.exit(1);
+       }
+}
+
+
+func (S *Scanner) Open(filename, src string) {
+       S.filename = filename;
+       S.nerrors = 0;
+       S.errpos = 0;
+       
+       S.src = src;
+       S.pos = 0;
+       S.Next();
+}
+
+
+func CharString(ch int) string {
+       s := string(ch);
+       switch ch {
+       case '\a': s = `\a`;
+       case '\b': s = `\b`;
+       case '\f': s = `\f`;
+       case '\n': s = `\n`;
+       case '\r': s = `\r`;
+       case '\t': s = `\t`;
+       case '\v': s = `\v`;
+       case '\\': s = `\\`;
+       case '\'': s = `\'`;
+       }
+       return "'" + s + "' (U+" + Utils.IntToString(ch, 16) + ")";
+}
+
+
+func (S *Scanner) Expect(ch int) {
+       if S.ch != ch {
+               S.Error(S.chpos, "expected " + CharString(ch) + ", found " + CharString(S.ch));
+       }
+       S.Next();  // make always progress
+}
+
+
+func (S *Scanner) SkipWhitespace() {
+       for is_whitespace(S.ch) {
+               S.Next();
+       }
+}
+
+
+func (S *Scanner) SkipComment() {
+       // '/' already consumed
+       if S.ch == '/' {
+               // comment
+               S.Next();
+               for S.ch != '\n' && S.ch >= 0 {
+                       S.Next();
+               }
+               
+       } else {
+               /* comment */
+               pos := S.chpos - 1;
+               S.Expect('*');
+               for S.ch >= 0 {
+                       ch := S.ch;
+                       S.Next();
+                       if ch == '*' && S.ch == '/' {
+                               S.Next();
+                               return;
+                       }
+               }
+               S.Error(pos, "comment not terminated");
+       }
+}
+
+
+func (S *Scanner) ScanIdentifier() (tok int, val string) {
+       pos := S.chpos;
+       for is_letter(S.ch) || digit_val(S.ch) < 10 {
+               S.Next();
+       }
+       val = S.src[pos : S.chpos];
+       
+       var present bool;
+       tok, present = Keywords[val];
+       if !present {
+               tok = IDENT;
+       }
+       
+       return tok, val;
+}
+
+
+func (S *Scanner) ScanMantissa(base int) {
+       for digit_val(S.ch) < base {
+               S.Next();
+       }
+}
+
+
+func (S *Scanner) ScanNumber(seen_decimal_point bool) (tok int, val string) {
+       pos := S.chpos;
+       tok = INT;
+       
+       if seen_decimal_point {
+               tok = FLOAT;
+               pos--;  // '.' is one byte
+               S.ScanMantissa(10);
+               goto exponent;
+       }
+       
+       if S.ch == '0' {
+               // int or float
+               S.Next();
+               if S.ch == 'x' || S.ch == 'X' {
+                       // hexadecimal int
+                       S.Next();
+                       S.ScanMantissa(16);
+               } else {
+                       // octal int or float
+                       S.ScanMantissa(8);
+                       if digit_val(S.ch) < 10 || S.ch == '.' || S.ch == 'e' || S.ch == 'E' {
+                               // float
+                               tok = FLOAT;
+                               goto mantissa;
+                       }
+                       // octal int
+               }
+               goto exit;
+       }
+       
+mantissa:
+       // decimal int or float
+       S.ScanMantissa(10);
+       
+       if S.ch == '.' {
+               // float
+               tok = FLOAT;
+               S.Next();
+               S.ScanMantissa(10)
+       }
+       
+exponent:
+       if S.ch == 'e' || S.ch == 'E' {
+               // float
+               tok = FLOAT;
+               S.Next();
+               if S.ch == '-' || S.ch == '+' {
+                       S.Next();
+               }
+               S.ScanMantissa(10);
+       }
+       
+exit:
+       return tok, S.src[pos : S.chpos];
+}
+
+
+func (S *Scanner) ScanDigits(n int, base int) {
+       for digit_val(S.ch) < base {
+               S.Next();
+               n--;
+       }
+       if n > 0 {
+               S.Error(S.chpos, "illegal char escape");
+       }
+}
+
+
+func (S *Scanner) ScanEscape(quote int) string {
+       // TODO: fix this routine
+       
+       ch := S.ch;
+       pos := S.chpos;
+       S.Next();
+       switch (ch) {
+       case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\':
+               return string(ch);
+               
+       case '0', '1', '2', '3', '4', '5', '6', '7':
+               S.ScanDigits(3 - 1, 8);  // 1 char already read
+               return "";  // TODO fix this
+               
+       case 'x':
+               S.ScanDigits(2, 16);
+               return "";  // TODO fix this
+               
+       case 'u':
+               S.ScanDigits(4, 16);
+               return "";  // TODO fix this
+
+       case 'U':
+               S.ScanDigits(8, 16);
+               return "";  // TODO fix this
+
+       default:
+               // check for quote outside the switch for better generated code (eventually)
+               if ch == quote {
+                       return string(quote);
+               }
+               S.Error(pos, "illegal char escape");
+       }
+       
+       return "";  // TODO fix this
+}
+
+
+func (S *Scanner) ScanChar() string {
+       // '\'' already consumed
+
+       pos := S.chpos - 1;
+       ch := S.ch;
+       S.Next();
+       if ch == '\\' {
+               S.ScanEscape('\'');
+       }
+
+       S.Expect('\'');
+       return S.src[pos : S.chpos];
+}
+
+
+func (S *Scanner) ScanString() string {
+       // '"' already consumed
+
+       pos := S.chpos - 1;
+       for S.ch != '"' {
+               ch := S.ch;
+               S.Next();
+               if ch == '\n' || ch < 0 {
+                       S.Error(pos, "string not terminated");
+                       break;
+               }
+               if ch == '\\' {
+                       S.ScanEscape('"');
+               }
+       }
+       
+       S.Next();
+       return S.src[pos : S.chpos];
+}
+
+
+func (S *Scanner) ScanRawString() string {
+       // '`' already consumed
+
+       pos := S.chpos - 1;
+       for S.ch != '`' {
+               ch := S.ch;
+               S.Next();
+               if ch == '\n' || ch < 0 {
+                       S.Error(pos, "string not terminated");
+                       break;
+               }
+       }
+
+       S.Next();
+       return S.src[pos : S.chpos];
+}
+
+
+func (S *Scanner) Select2(tok0, tok1 int) int {
+       if S.ch == '=' {
+               S.Next();
+               return tok1;
+       }
+       return tok0;
+}
+
+
+func (S *Scanner) Select3(tok0, tok1, ch2, tok2 int) int {
+       if S.ch == '=' {
+               S.Next();
+               return tok1;
+       }
+       if S.ch == ch2 {
+               S.Next();
+               return tok2;
+       }
+       return tok0;
+}
+
+
+func (S *Scanner) Select4(tok0, tok1, ch2, tok2, tok3 int) int {
+       if S.ch == '=' {
+               S.Next();
+               return tok1;
+       }
+       if S.ch == ch2 {
+               S.Next();
+               if S.ch == '=' {
+                       S.Next();
+                       return tok3;
+               }
+               return tok2;
+       }
+       return tok0;
+}
+
+
+func (S *Scanner) Scan() (tok, pos int, val string) {
+       S.SkipWhitespace();
+       
+       ch := S.ch;
+       tok = ILLEGAL;
+       pos = S.chpos;
+       
+       switch {
+       case is_letter(ch): tok, val = S.ScanIdentifier();
+       case digit_val(ch) < 10: tok, val = S.ScanNumber(false);
+       default:
+               S.Next();  // always make progress
+               switch ch {
+               case -1: tok = EOF;
+               case '"': tok, val = STRING, S.ScanString();
+               case '\'': tok, val = INT, S.ScanChar();
+               case '`': tok, val = STRING, S.ScanRawString();
+               case ':': tok = S.Select2(COLON, DEFINE);
+               case '.':
+                       if digit_val(S.ch) < 10 {
+                               tok, val = S.ScanNumber(true);
+                       } else {
+                               tok = PERIOD;
+                       }
+               case ',': tok = COMMA;
+               case ';': tok = SEMICOLON;
+               case '(': tok = LPAREN;
+               case ')': tok = RPAREN;
+               case '[': tok = LBRACK;
+               case ']': tok = RBRACK;
+               case '{': tok = LBRACE;
+               case '}': tok = RBRACE;
+               case '+': tok = S.Select3(ADD, ADD_ASSIGN, '+', INC);
+               case '-': tok = S.Select3(SUB, SUB_ASSIGN, '-', DEC);
+               case '*': tok = S.Select2(MUL, MUL_ASSIGN);
+               case '/':
+                       if S.ch == '/' || S.ch == '*' {
+                               S.SkipComment();
+                               // cannot simply return because of 6g bug
+                               tok, pos, val = S.Scan();
+                               return tok, pos, val;
+                       }
+                       tok = S.Select2(QUO, QUO_ASSIGN);
+               case '%': tok = S.Select2(REM, REM_ASSIGN);
+               case '^': tok = S.Select2(XOR, XOR_ASSIGN);
+               case '<':
+                       if S.ch == '-' {
+                               S.Next();
+                               tok = ARROW;
+                       } else {
+                               tok = S.Select4(LSS, LEQ, '<', SHL, SHL_ASSIGN);
+                       }
+               case '>': tok = S.Select4(GTR, GEQ, '>', SHR, SHR_ASSIGN);
+               case '=': tok = S.Select2(ASSIGN, EQL);
+               case '!': tok = S.Select2(NOT, NEQ);
+               case '&': tok = S.Select3(AND, AND_ASSIGN, '&', LAND);
+               case '|': tok = S.Select3(OR, OR_ASSIGN, '|', LOR);
+               case '#': tok = HASH;
+               default:
+                       S.Error(pos, "illegal character " + CharString(ch));
+                       tok = ILLEGAL;
+               }
+       }
+       
+       return tok, pos, val;
+}
+
+
+export type Token struct {
+       pos int;
+       tok int;
+       val string;
+}
+
+
+func (S *Scanner) TokenStream() *<-chan *Token {
+       ch := new(chan *Token);
+       go func(S *Scanner, ch *chan <- *Token) {
+               for {
+                       t := new(Token);
+                       t.tok, t.pos, t.val = S.Scan();
+                       ch <- t;
+                       if t.tok == EOF {
+                          break;
+                       }
+               }
+       }(S, ch);
+       return ch;
+}
diff --git a/usr/gri/pretty/utils.go b/usr/gri/pretty/utils.go
new file mode 100644 (file)
index 0000000..0764a2f
--- /dev/null
@@ -0,0 +1,63 @@
+// Copyright 2009 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package Utils
+
+
+export func BaseName(s string) string {
+       // TODO this is not correct for non-ASCII strings!
+       i := len(s) - 1;
+       for i >= 0 && s[i] != '/' {
+               if s[i] > 128 {
+                       panic("non-ASCII string");
+               }
+               i--;
+       }
+       return s[i + 1 : len(s)];
+}
+
+
+export func Contains(s, sub string, pos int) bool {
+       end := pos + len(sub);
+       return pos >= 0 && end <= len(s) && s[pos : end] == sub;
+}
+
+
+export func TrimExt(s, ext string) string {
+       i := len(s) - len(ext);
+       if i >= 0 && s[i : len(s)] == ext {
+               s = s[0 : i];
+       }
+       return s;
+}
+
+
+export func IntToString(x, base int) string {
+       x0 := x;
+       if x < 0 {
+               x = -x;
+               if x < 0 {
+                       panic("smallest int not handled");
+               }
+       } else if x == 0 {
+               return "0";
+       }
+
+       // x > 0
+       hex := "0123456789ABCDEF";
+       var buf [32] byte;
+       i := len(buf);
+       for x > 0 {
+               i--;
+               buf[i] = hex[x % base];
+               x /= base;
+       }
+       
+       if x0 < 0 {
+               i--;
+               buf[i] = '-';
+       }
+       
+       return string(buf)[i : len(buf)];
+}