summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Pike <r@golang.org>2011-07-04 11:50:35 +1000
committerRob Pike <r@golang.org>2011-07-04 11:50:35 +1000
commit72efdea20e96cba1b28352c1b0343b510caa8402 (patch)
treeab746d54a7600248019cef7a1dc98dfd28f868d1
parentdb0e3580225ef11a3a8be0de608cb56ddd4aee55 (diff)
downloadgo-git-72efdea20e96cba1b28352c1b0343b510caa8402.tar.gz
exp/template: allow complex numbers, add 'with', 'define', and 'template' keywords.
Also simplify the handling of keywords. R=golang-dev, dsymonds CC=golang-dev https://golang.org/cl/4639096
-rw-r--r--src/pkg/exp/template/lex.go88
-rw-r--r--src/pkg/exp/template/lex_test.go7
2 files changed, 63 insertions, 32 deletions
diff --git a/src/pkg/exp/template/lex.go b/src/pkg/exp/template/lex.go
index 1919cf4715..d22d825a1d 100644
--- a/src/pkg/exp/template/lex.go
+++ b/src/pkg/exp/template/lex.go
@@ -18,60 +18,71 @@ type item struct {
}
func (i item) String() string {
- switch i.typ {
- case itemEOF:
+ switch {
+ case i.typ == itemEOF:
return "EOF"
- case itemError:
+ case i.typ == itemError:
return i.val
- }
- if len(i.val) > 10 {
+ case i.typ > itemKeyword:
+ return fmt.Sprintf("<%s>", i.val)
+ case len(i.val) > 10:
return fmt.Sprintf("%.10q...", i.val)
}
return fmt.Sprintf("%q", i.val)
}
-// itemType identifies the type of lex item.
+// itemType identifies the type of lex items.
type itemType int
const (
- itemError itemType = iota // error occurred; value is text of error
- itemBool // boolean constant
- itemDot // the cursor, spelled '.'.
+ itemError itemType = iota // error occurred; value is text of error
+ itemBool // boolean constant
+ itemComplex // complex constant (1+2i); imaginary is just a number
itemEOF
- itemElse // else keyword
- itemEnd // end keyword
itemField // alphanumeric identifier, starting with '.', possibly chained ('.x.y')
itemIdentifier // alphanumeric identifier
- itemIf // if keyword
itemLeftMeta // left meta-string
- itemNumber // number
+ itemNumber // simple number, including imaginary
itemPipe // pipe symbol
- itemRange // range keyword
itemRawString // raw quoted string (includes quotes)
itemRightMeta // right meta-string
itemString // quoted string (includes quotes)
itemText // plain text
+ // Keywords appear after all the rest.
+ itemKeyword // used only to delimit the keywords
+ itemDot // the cursor, spelled '.'.
+ itemDefine // define keyword
+ itemElse // else keyword
+ itemEnd // end keyword
+ itemIf // if keyword
+ itemRange // range keyword
+ itemTemplate // template keyword
+ itemWith // with keyword
)
// Make the types prettyprint.
var itemName = map[itemType]string{
itemError: "error",
itemBool: "bool",
- itemDot: ".",
+ itemComplex: "complex",
itemEOF: "EOF",
- itemElse: "else",
- itemEnd: "end",
itemField: "field",
itemIdentifier: "identifier",
- itemIf: "if",
itemLeftMeta: "left meta",
itemNumber: "number",
itemPipe: "pipe",
- itemRange: "range",
itemRawString: "raw string",
itemRightMeta: "rightMeta",
itemString: "string",
- itemText: "text",
+ // keywords
+ itemDot: ".",
+ itemDefine: "define",
+ itemElse: "else",
+ itemIf: "if",
+ itemEnd: "end",
+ itemRange: "range",
+ itemTemplate: "template",
+ itemWith: "with",
}
func (i itemType) String() string {
@@ -83,11 +94,14 @@ func (i itemType) String() string {
}
var key = map[string]itemType{
- ".": itemDot,
- "else": itemElse,
- "end": itemEnd,
- "if": itemIf,
- "range": itemRange,
+ ".": itemDot,
+ "define": itemDefine,
+ "else": itemElse,
+ "end": itemEnd,
+ "if": itemIf,
+ "range": itemRange,
+ "template": itemTemplate,
+ "with": itemWith,
}
const eof = -1
@@ -282,7 +296,7 @@ Loop:
l.backup()
word := l.input[l.start:l.pos]
switch {
- case key[word] != itemError:
+ case key[word] > itemKeyword:
l.emit(key[word])
case word[0] == '.':
l.emit(itemField)
@@ -301,8 +315,23 @@ Loop:
// isn't a perfect number scanner - for instance it accepts "." and "0x0.2"
// and "089" - but when it's wrong the input is invalid and the parser (via
// strconv) will notice.
-// TODO: without expressions you can do imaginary but not complex.
func lexNumber(l *lexer) stateFn {
+ if !l.scanNumber() {
+ return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
+ }
+ if sign := l.peek(); sign == '+' || sign == '-' {
+ // Complex: 1+2i. No spaces, must end in 'i'.
+ if !l.scanNumber() || l.input[l.pos-1] != 'i' {
+ return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
+ }
+ l.emit(itemComplex)
+ } else {
+ l.emit(itemNumber)
+ }
+ return lexInsideAction
+}
+
+func (l *lexer) scanNumber() bool {
// Optional leading sign.
l.accept("+-")
// Is it hex?
@@ -323,10 +352,9 @@ func lexNumber(l *lexer) stateFn {
// Next thing mustn't be alphanumeric.
if isAlphaNumeric(l.peek()) {
l.next()
- return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
+ return false
}
- l.emit(itemNumber)
- return lexInsideAction
+ return true
}
// lexQuote scans a quoted string.
diff --git a/src/pkg/exp/template/lex_test.go b/src/pkg/exp/template/lex_test.go
index 62bce6daa0..e13a7247a9 100644
--- a/src/pkg/exp/template/lex_test.go
+++ b/src/pkg/exp/template/lex_test.go
@@ -35,7 +35,7 @@ var lexTests = []lexTest{
{"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}}", []item{
+ {"numbers", "{{1 02 0x14 -7.2i 1e3 +1.2e-4 4.2i 1+2i}}", []item{
tLeft,
{itemNumber, "1"},
{itemNumber, "02"},
@@ -43,6 +43,8 @@ var lexTests = []lexTest{
{itemNumber, "-7.2i"},
{itemNumber, "1e3"},
{itemNumber, "+1.2e-4"},
+ {itemNumber, "4.2i"},
+ {itemComplex, "1+2i"},
tRight,
tEOF,
}},
@@ -68,12 +70,13 @@ var lexTests = []lexTest{
tRight,
tEOF,
}},
- {"keywords", "{{range if else end}}", []item{
+ {"keywords", "{{range if else end with}}", []item{
tLeft,
{itemRange, "range"},
{itemIf, "if"},
{itemElse, "else"},
{itemEnd, "end"},
+ {itemWith, "with"},
tRight,
tEOF,
}},