diff options
Diffstat (limited to 'src/pkg/go/scanner/scanner_test.go')
-rw-r--r-- | src/pkg/go/scanner/scanner_test.go | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/src/pkg/go/scanner/scanner_test.go b/src/pkg/go/scanner/scanner_test.go new file mode 100644 index 000000000..0defece8b --- /dev/null +++ b/src/pkg/go/scanner/scanner_test.go @@ -0,0 +1,276 @@ +// 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 ( + "go/scanner"; + "go/token"; + "io"; + "testing"; +) + + +const /* class */ ( + special = iota; + literal; + operator; + keyword; +) + + +func tokenclass(tok token.Token) int { + switch { + case tok.IsLiteral(): return literal; + case tok.IsOperator(): return operator; + case tok.IsKeyword(): return keyword; + } + return special; +} + + +type elt struct { + tok token.Token; + lit string; + class int; +} + + +var tokens = [...]elt{ + // Special tokens + elt{ token.COMMENT, "/* a comment */", special }, + elt{ token.COMMENT, "// a comment \n", special }, + + // Identifiers and basic type literals + elt{ token.IDENT, "foobar", literal }, + elt{ token.IDENT, "a۰۱۸", literal }, + elt{ token.IDENT, "foo६४", literal }, + elt{ token.IDENT, "bar9876", literal }, + elt{ token.INT, "0", literal }, + elt{ token.INT, "01234567", literal }, + elt{ token.INT, "0xcafebabe", literal }, + elt{ token.FLOAT, "0.", literal }, + elt{ token.FLOAT, ".0", literal }, + elt{ token.FLOAT, "3.14159265", literal }, + elt{ token.FLOAT, "1e0", literal }, + elt{ token.FLOAT, "1e+100", literal }, + elt{ token.FLOAT, "1e-100", literal }, + elt{ token.FLOAT, "2.71828e-1000", literal }, + elt{ token.CHAR, "'a'", literal }, + elt{ token.CHAR, "'\\000'", literal }, + elt{ token.CHAR, "'\\xFF'", literal }, + elt{ token.CHAR, "'\\uff16'", literal }, + elt{ token.CHAR, "'\\U0000ff16'", literal }, + elt{ token.STRING, "`foobar`", literal }, + + // Operators and delimitors + elt{ token.ADD, "+", operator }, + elt{ token.SUB, "-", operator }, + elt{ token.MUL, "*", operator }, + elt{ token.QUO, "/", operator }, + elt{ token.REM, "%", operator }, + + elt{ token.AND, "&", operator }, + elt{ token.OR, "|", operator }, + elt{ token.XOR, "^", operator }, + elt{ token.SHL, "<<", operator }, + elt{ token.SHR, ">>", operator }, + elt{ token.AND_NOT, "&^", operator }, + + elt{ token.ADD_ASSIGN, "+=", operator }, + elt{ token.SUB_ASSIGN, "-=", operator }, + elt{ token.MUL_ASSIGN, "*=", operator }, + elt{ token.QUO_ASSIGN, "/=", operator }, + elt{ token.REM_ASSIGN, "%=", operator }, + + elt{ token.AND_ASSIGN, "&=", operator }, + elt{ token.OR_ASSIGN, "|=", operator }, + elt{ token.XOR_ASSIGN, "^=", operator }, + elt{ token.SHL_ASSIGN, "<<=", operator }, + elt{ token.SHR_ASSIGN, ">>=", operator }, + elt{ token.AND_NOT_ASSIGN, "&^=", operator }, + + elt{ token.LAND, "&&", operator }, + elt{ token.LOR, "||", operator }, + elt{ token.ARROW, "<-", operator }, + elt{ token.INC, "++", operator }, + elt{ token.DEC, "--", operator }, + + elt{ token.EQL, "==", operator }, + elt{ token.LSS, "<", operator }, + elt{ token.GTR, ">", operator }, + elt{ token.ASSIGN, "=", operator }, + elt{ token.NOT, "!", operator }, + + elt{ token.NEQ, "!=", operator }, + elt{ token.LEQ, "<=", operator }, + elt{ token.GEQ, ">=", operator }, + elt{ token.DEFINE, ":=", operator }, + elt{ token.ELLIPSIS, "...", operator }, + + elt{ token.LPAREN, "(", operator }, + elt{ token.LBRACK, "[", operator }, + elt{ token.LBRACE, "{", operator }, + elt{ token.COMMA, ",", operator }, + elt{ token.PERIOD, ".", operator }, + + elt{ token.RPAREN, ")", operator }, + elt{ token.RBRACK, "]", operator }, + elt{ token.RBRACE, "}", operator }, + elt{ token.SEMICOLON, ";", operator }, + elt{ token.COLON, ":", operator }, + + // Keywords + elt{ token.BREAK, "break", keyword }, + elt{ token.CASE, "case", keyword }, + elt{ token.CHAN, "chan", keyword }, + elt{ token.CONST, "const", keyword }, + elt{ token.CONTINUE, "continue", keyword }, + + elt{ token.DEFAULT, "default", keyword }, + elt{ token.DEFER, "defer", keyword }, + elt{ token.ELSE, "else", keyword }, + elt{ token.FALLTHROUGH, "fallthrough", keyword }, + elt{ token.FOR, "for", keyword }, + + elt{ token.FUNC, "func", keyword }, + elt{ token.GO, "go", keyword }, + elt{ token.GOTO, "goto", keyword }, + elt{ token.IF, "if", keyword }, + elt{ token.IMPORT, "import", keyword }, + + elt{ token.INTERFACE, "interface", keyword }, + elt{ token.MAP, "map", keyword }, + elt{ token.PACKAGE, "package", keyword }, + elt{ token.RANGE, "range", keyword }, + elt{ token.RETURN, "return", keyword }, + + elt{ token.SELECT, "select", keyword }, + elt{ token.STRUCT, "struct", keyword }, + elt{ token.SWITCH, "switch", keyword }, + elt{ token.TYPE, "type", keyword }, + elt{ token.VAR, "var", keyword }, +} + + +const whitespace = " \t \n\n\n"; // to separate tokens + +type TestErrorHandler struct { + t *testing.T +} + +func (h *TestErrorHandler) Error(pos token.Position, msg string) { + h.t.Errorf("Error() called (msg = %s)", msg); +} + + +func NewlineCount(s string) int { + n := 0; + for i := 0; i < len(s); i++ { + if s[i] == '\n' { + n++; + } + } + return n; +} + + +// Verify that calling Scan() provides the correct results. +func TestScan(t *testing.T) { + // make source + var src string; + for i, e := range tokens { + src += e.lit + whitespace; + } + whitespace_linecount := NewlineCount(whitespace); + + // verify scan + index := 0; + eloc := token.Position{0, 1, 1}; + nerrors := scanner.Tokenize(io.StringBytes(src), &TestErrorHandler{t}, scanner.ScanComments, + func (pos token.Position, tok token.Token, litb []byte) bool { + e := elt{token.EOF, "", special}; + if index < len(tokens) { + e = tokens[index]; + } + lit := string(litb); + if tok == token.EOF { + lit = "<EOF>"; + eloc.Column = 0; + } + if pos.Offset != eloc.Offset { + t.Errorf("bad position for %s: got %d, expected %d", lit, pos.Offset, eloc.Offset); + } + if pos.Line != eloc.Line { + t.Errorf("bad line for %s: got %d, expected %d", lit, pos.Line, eloc.Line); + } + if pos.Column!= eloc.Column { + t.Errorf("bad column for %s: got %d, expected %d", lit, pos.Column, eloc.Column); + } + if tok != e.tok { + t.Errorf("bad token for %s: got %s, expected %s", lit, tok.String(), e.tok.String()); + } + if e.tok.IsLiteral() && lit != e.lit { + t.Errorf("bad literal for %s: got %s, expected %s", lit, lit, e.lit); + } + if tokenclass(tok) != e.class { + t.Errorf("bad class for %s: got %d, expected %d", lit, tokenclass(tok), e.class); + } + eloc.Offset += len(lit) + len(whitespace); + eloc.Line += NewlineCount(lit) + whitespace_linecount; + index++; + return tok != token.EOF; + } + ); + if nerrors != 0 { + t.Errorf("found %d errors", nerrors); + } +} + + +// Verify that initializing the same scanner more then once works correctly. +func TestInit(t *testing.T) { + var s scanner.Scanner; + + // 1st init + s.Init(io.StringBytes("if true { }"), nil, 0); + s.Scan(); // if + s.Scan(); // true + pos, tok, lit := s.Scan(); // { + if tok != token.LBRACE { + t.Errorf("bad token: got %s, expected %s", tok.String(), token.LBRACE); + } + + // 2nd init + s.Init(io.StringBytes("go true { ]"), nil, 0); + pos, tok, lit = s.Scan(); // go + if tok != token.GO { + t.Errorf("bad token: got %s, expected %s", tok.String(), token.GO); + } + + if s.ErrorCount != 0 { + t.Errorf("found %d errors", s.ErrorCount); + } +} + + +func TestIllegalChars(t *testing.T) { + var s scanner.Scanner; + + const src = "*?*$*@*"; + s.Init(io.StringBytes(src), &TestErrorHandler{t}, scanner.AllowIllegalChars); + for offs, ch := range src { + pos, tok, lit := s.Scan(); + if pos.Offset != offs { + t.Errorf("bad position for %s: got %d, expected %d", string(lit), pos.Offset, offs); + } + if tok == token.ILLEGAL && string(lit) != string(ch) { + t.Errorf("bad token: got %s, expected %s", string(lit), string(ch)); + } + } + + if s.ErrorCount != 0 { + t.Errorf("found %d errors", s.ErrorCount); + } +} |