]> Cypherpunks repositories - gostls13.git/commitdiff
exp/template: allow complex numbers, add 'with', 'define', and 'template' keywords.
authorRob Pike <r@golang.org>
Mon, 4 Jul 2011 01:50:35 +0000 (11:50 +1000)
committerRob Pike <r@golang.org>
Mon, 4 Jul 2011 01:50:35 +0000 (11:50 +1000)
Also simplify the handling of keywords.

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/4639096

src/pkg/exp/template/lex.go
src/pkg/exp/template/lex_test.go

index 1919cf4715ad0272760e3102b556c5a58738fef9..d22d825a1df04a41c8f0747198ef9eee8c2e7d24 100644 (file)
@@ -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.
index 62bce6daa01dfc680854264a30bacef5e38eff3f..e13a7247a9b53eadf506935a358e75290f9c2a60 100644 (file)
@@ -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,
        }},