--- /dev/null
+// 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();
+}
--- /dev/null
+// 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;
+}