From: Robert Griesemer Date: Thu, 18 Sep 2008 23:58:37 +0000 (-0700) Subject: First cut at a Go pretty printer: X-Git-Tag: weekly.2009-11-06~3176 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=81d7c5183722af90f66a989a68c617484eb1388f;p=gostls13.git 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 --- diff --git a/usr/gri/pretty/Makefile b/usr/gri/pretty/Makefile new file mode 100644 index 0000000000..a1bde84f1e --- /dev/null +++ b/usr/gri/pretty/Makefile @@ -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 index 0000000000..311b91f517 --- /dev/null +++ b/usr/gri/pretty/ast.go @@ -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 index 0000000000..f66efd6216 --- /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(); +} diff --git a/usr/gri/pretty/platform.go b/usr/gri/pretty/platform.go new file mode 100644 index 0000000000..c76d591505 --- /dev/null +++ b/usr/gri/pretty/platform.go @@ -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 index 0000000000..48f470f185 --- /dev/null +++ b/usr/gri/pretty/pretty.go @@ -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 index 0000000000..e4aa2ef8f1 --- /dev/null +++ b/usr/gri/pretty/printer.go @@ -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 index 0000000000..1e2645cb26 --- /dev/null +++ b/usr/gri/pretty/scanner.go @@ -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 index 0000000000..0764a2f8ca --- /dev/null +++ b/usr/gri/pretty/utils.go @@ -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)]; +}