| // Copyright 2011 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 parse |
| |
| import ( |
| "reflect" |
| "testing" |
| ) |
| |
| type lexTest struct { |
| name string |
| input string |
| items []item |
| } |
| |
| var ( |
| tEOF = item{itemEOF, ""} |
| tLeft = item{itemLeftDelim, "{{"} |
| tRight = item{itemRightDelim, "}}"} |
| tRange = item{itemRange, "range"} |
| tPipe = item{itemPipe, "|"} |
| tFor = item{itemIdentifier, "for"} |
| tQuote = item{itemString, `"abc \n\t\" "`} |
| raw = "`" + `abc\n\t\" ` + "`" |
| tRawQuote = item{itemRawString, raw} |
| ) |
| |
| var lexTests = []lexTest{ |
| {"empty", "", []item{tEOF}}, |
| {"spaces", " \t\n", []item{{itemText, " \t\n"}, tEOF}}, |
| {"text", `now is the time`, []item{{itemText, "now is the time"}, tEOF}}, |
| {"text with comment", "hello-{{/* this is a comment */}}-world", []item{ |
| {itemText, "hello-"}, |
| {itemText, "-world"}, |
| tEOF, |
| }}, |
| {"punctuation", "{{,@%}}", []item{ |
| tLeft, |
| {itemChar, ","}, |
| {itemChar, "@"}, |
| {itemChar, "%"}, |
| tRight, |
| tEOF, |
| }}, |
| {"empty action", `{{}}`, []item{tLeft, tRight, tEOF}}, |
| {"for", `{{for }}`, []item{tLeft, tFor, tRight, tEOF}}, |
| {"quote", `{{"abc \n\t\" "}}`, []item{tLeft, tQuote, tRight, tEOF}}, |
| {"raw quote", "{{" + raw + "}}", []item{tLeft, tRawQuote, tRight, tEOF}}, |
| {"numbers", "{{1 02 0x14 -7.2i 1e3 +1.2e-4 4.2i 1+2i}}", []item{ |
| tLeft, |
| {itemNumber, "1"}, |
| {itemNumber, "02"}, |
| {itemNumber, "0x14"}, |
| {itemNumber, "-7.2i"}, |
| {itemNumber, "1e3"}, |
| {itemNumber, "+1.2e-4"}, |
| {itemNumber, "4.2i"}, |
| {itemComplex, "1+2i"}, |
| tRight, |
| tEOF, |
| }}, |
| {"characters", `{{'a' '\n' '\'' '\\' '\u00FF' '\xFF' '本'}}`, []item{ |
| tLeft, |
| {itemCharConstant, `'a'`}, |
| {itemCharConstant, `'\n'`}, |
| {itemCharConstant, `'\''`}, |
| {itemCharConstant, `'\\'`}, |
| {itemCharConstant, `'\u00FF'`}, |
| {itemCharConstant, `'\xFF'`}, |
| {itemCharConstant, `'本'`}, |
| tRight, |
| tEOF, |
| }}, |
| {"bools", "{{true false}}", []item{ |
| tLeft, |
| {itemBool, "true"}, |
| {itemBool, "false"}, |
| tRight, |
| tEOF, |
| }}, |
| {"dot", "{{.}}", []item{ |
| tLeft, |
| {itemDot, "."}, |
| tRight, |
| tEOF, |
| }}, |
| {"dots", "{{.x . .2 .x.y}}", []item{ |
| tLeft, |
| {itemField, ".x"}, |
| {itemDot, "."}, |
| {itemNumber, ".2"}, |
| {itemField, ".x.y"}, |
| tRight, |
| tEOF, |
| }}, |
| {"keywords", "{{range if else end with}}", []item{ |
| tLeft, |
| {itemRange, "range"}, |
| {itemIf, "if"}, |
| {itemElse, "else"}, |
| {itemEnd, "end"}, |
| {itemWith, "with"}, |
| tRight, |
| tEOF, |
| }}, |
| {"variables", "{{$c := printf $ $hello $23 $ $var.Field .Method}}", []item{ |
| tLeft, |
| {itemVariable, "$c"}, |
| {itemColonEquals, ":="}, |
| {itemIdentifier, "printf"}, |
| {itemVariable, "$"}, |
| {itemVariable, "$hello"}, |
| {itemVariable, "$23"}, |
| {itemVariable, "$"}, |
| {itemVariable, "$var.Field"}, |
| {itemField, ".Method"}, |
| tRight, |
| tEOF, |
| }}, |
| {"pipeline", `intro {{echo hi 1.2 |noargs|args 1 "hi"}} outro`, []item{ |
| {itemText, "intro "}, |
| tLeft, |
| {itemIdentifier, "echo"}, |
| {itemIdentifier, "hi"}, |
| {itemNumber, "1.2"}, |
| tPipe, |
| {itemIdentifier, "noargs"}, |
| tPipe, |
| {itemIdentifier, "args"}, |
| {itemNumber, "1"}, |
| {itemString, `"hi"`}, |
| tRight, |
| {itemText, " outro"}, |
| tEOF, |
| }}, |
| {"declaration", "{{$v := 3}}", []item{ |
| tLeft, |
| {itemVariable, "$v"}, |
| {itemColonEquals, ":="}, |
| {itemNumber, "3"}, |
| tRight, |
| tEOF, |
| }}, |
| {"2 declarations", "{{$v , $w := 3}}", []item{ |
| tLeft, |
| {itemVariable, "$v"}, |
| {itemChar, ","}, |
| {itemVariable, "$w"}, |
| {itemColonEquals, ":="}, |
| {itemNumber, "3"}, |
| tRight, |
| tEOF, |
| }}, |
| // errors |
| {"badchar", "#{{\x01}}", []item{ |
| {itemText, "#"}, |
| tLeft, |
| {itemError, "unrecognized character in action: U+0001"}, |
| }}, |
| {"unclosed action", "{{\n}}", []item{ |
| tLeft, |
| {itemError, "unclosed action"}, |
| }}, |
| {"EOF in action", "{{range", []item{ |
| tLeft, |
| tRange, |
| {itemError, "unclosed action"}, |
| }}, |
| {"unclosed quote", "{{\"\n\"}}", []item{ |
| tLeft, |
| {itemError, "unterminated quoted string"}, |
| }}, |
| {"unclosed raw quote", "{{`xx\n`}}", []item{ |
| tLeft, |
| {itemError, "unterminated raw quoted string"}, |
| }}, |
| {"unclosed char constant", "{{'\n}}", []item{ |
| tLeft, |
| {itemError, "unterminated character constant"}, |
| }}, |
| {"bad number", "{{3k}}", []item{ |
| tLeft, |
| {itemError, `bad number syntax: "3k"`}, |
| }}, |
| |
| // Fixed bugs |
| // Many elements in an action blew the lookahead until |
| // we made lexInsideAction not loop. |
| {"long pipeline deadlock", "{{|||||}}", []item{ |
| tLeft, |
| tPipe, |
| tPipe, |
| tPipe, |
| tPipe, |
| tPipe, |
| tRight, |
| tEOF, |
| }}, |
| } |
| |
| // collect gathers the emitted items into a slice. |
| func collect(t *lexTest) (items []item) { |
| l := lex(t.name, t.input) |
| for { |
| item := l.nextItem() |
| items = append(items, item) |
| if item.typ == itemEOF || item.typ == itemError { |
| break |
| } |
| } |
| return |
| } |
| |
| func TestLex(t *testing.T) { |
| for _, test := range lexTests { |
| items := collect(&test) |
| if !reflect.DeepEqual(items, test.items) { |
| t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items) |
| } |
| } |
| } |