summaryrefslogtreecommitdiff
path: root/usr/gri/pretty/parser.go
diff options
context:
space:
mode:
authorRobert Griesemer <gri@golang.org>2008-09-18 16:58:37 -0700
committerRobert Griesemer <gri@golang.org>2008-09-18 16:58:37 -0700
commit9fb7da748396894cb2cb15188f82e124933b8b7a (patch)
tree3f83983bfab51a4a981b8537379eee656016617d /usr/gri/pretty/parser.go
parent2180a3dc9c5c80c097f56350539fbde490845493 (diff)
downloadgolang-9fb7da748396894cb2cb15188f82e124933b8b7a.tar.gz
First cut at a Go pretty printer:
- 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
Diffstat (limited to 'usr/gri/pretty/parser.go')
-rw-r--r--usr/gri/pretty/parser.go1320
1 files changed, 1320 insertions, 0 deletions
diff --git a/usr/gri/pretty/parser.go b/usr/gri/pretty/parser.go
new file mode 100644
index 000000000..f66efd621
--- /dev/null
+++ b/usr/gri/pretty/parser.go
@@ -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();
+}