Commit 72efdea2 authored by Rob Pike's avatar Rob Pike

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
parent db0e3580
...@@ -18,60 +18,71 @@ type item struct { ...@@ -18,60 +18,71 @@ type item struct {
} }
func (i item) String() string { func (i item) String() string {
switch i.typ { switch {
case itemEOF: case i.typ == itemEOF:
return "EOF" return "EOF"
case itemError: case i.typ == itemError:
return i.val return i.val
} case i.typ > itemKeyword:
if len(i.val) > 10 { return fmt.Sprintf("<%s>", i.val)
case len(i.val) > 10:
return fmt.Sprintf("%.10q...", i.val) return fmt.Sprintf("%.10q...", i.val)
} }
return fmt.Sprintf("%q", 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 type itemType int
const ( const (
itemError itemType = iota // error occurred; value is text of error itemError itemType = iota // error occurred; value is text of error
itemBool // boolean constant itemBool // boolean constant
itemDot // the cursor, spelled '.'. itemComplex // complex constant (1+2i); imaginary is just a number
itemEOF itemEOF
itemElse // else keyword
itemEnd // end keyword
itemField // alphanumeric identifier, starting with '.', possibly chained ('.x.y') itemField // alphanumeric identifier, starting with '.', possibly chained ('.x.y')
itemIdentifier // alphanumeric identifier itemIdentifier // alphanumeric identifier
itemIf // if keyword
itemLeftMeta // left meta-string itemLeftMeta // left meta-string
itemNumber // number itemNumber // simple number, including imaginary
itemPipe // pipe symbol itemPipe // pipe symbol
itemRange // range keyword
itemRawString // raw quoted string (includes quotes) itemRawString // raw quoted string (includes quotes)
itemRightMeta // right meta-string itemRightMeta // right meta-string
itemString // quoted string (includes quotes) itemString // quoted string (includes quotes)
itemText // plain text 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. // Make the types prettyprint.
var itemName = map[itemType]string{ var itemName = map[itemType]string{
itemError: "error", itemError: "error",
itemBool: "bool", itemBool: "bool",
itemDot: ".", itemComplex: "complex",
itemEOF: "EOF", itemEOF: "EOF",
itemElse: "else",
itemEnd: "end",
itemField: "field", itemField: "field",
itemIdentifier: "identifier", itemIdentifier: "identifier",
itemIf: "if",
itemLeftMeta: "left meta", itemLeftMeta: "left meta",
itemNumber: "number", itemNumber: "number",
itemPipe: "pipe", itemPipe: "pipe",
itemRange: "range",
itemRawString: "raw string", itemRawString: "raw string",
itemRightMeta: "rightMeta", itemRightMeta: "rightMeta",
itemString: "string", 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 { func (i itemType) String() string {
...@@ -83,11 +94,14 @@ func (i itemType) String() string { ...@@ -83,11 +94,14 @@ func (i itemType) String() string {
} }
var key = map[string]itemType{ var key = map[string]itemType{
".": itemDot, ".": itemDot,
"else": itemElse, "define": itemDefine,
"end": itemEnd, "else": itemElse,
"if": itemIf, "end": itemEnd,
"range": itemRange, "if": itemIf,
"range": itemRange,
"template": itemTemplate,
"with": itemWith,
} }
const eof = -1 const eof = -1
...@@ -282,7 +296,7 @@ Loop: ...@@ -282,7 +296,7 @@ Loop:
l.backup() l.backup()
word := l.input[l.start:l.pos] word := l.input[l.start:l.pos]
switch { switch {
case key[word] != itemError: case key[word] > itemKeyword:
l.emit(key[word]) l.emit(key[word])
case word[0] == '.': case word[0] == '.':
l.emit(itemField) l.emit(itemField)
...@@ -301,8 +315,23 @@ Loop: ...@@ -301,8 +315,23 @@ Loop:
// isn't a perfect number scanner - for instance it accepts "." and "0x0.2" // 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 // and "089" - but when it's wrong the input is invalid and the parser (via
// strconv) will notice. // strconv) will notice.
// TODO: without expressions you can do imaginary but not complex.
func lexNumber(l *lexer) stateFn { 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. // Optional leading sign.
l.accept("+-") l.accept("+-")
// Is it hex? // Is it hex?
...@@ -323,10 +352,9 @@ func lexNumber(l *lexer) stateFn { ...@@ -323,10 +352,9 @@ func lexNumber(l *lexer) stateFn {
// Next thing mustn't be alphanumeric. // Next thing mustn't be alphanumeric.
if isAlphaNumeric(l.peek()) { if isAlphaNumeric(l.peek()) {
l.next() l.next()
return l.errorf("bad number syntax: %q", l.input[l.start:l.pos]) return false
} }
l.emit(itemNumber) return true
return lexInsideAction
} }
// lexQuote scans a quoted string. // lexQuote scans a quoted string.
......
...@@ -35,7 +35,7 @@ var lexTests = []lexTest{ ...@@ -35,7 +35,7 @@ var lexTests = []lexTest{
{"for", `{{for }}`, []item{tLeft, tFor, tRight, tEOF}}, {"for", `{{for }}`, []item{tLeft, tFor, tRight, tEOF}},
{"quote", `{{"abc \n\t\" "}}`, []item{tLeft, tQuote, tRight, tEOF}}, {"quote", `{{"abc \n\t\" "}}`, []item{tLeft, tQuote, tRight, tEOF}},
{"raw quote", "{{" + raw + "}}", []item{tLeft, tRawQuote, 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, tLeft,
{itemNumber, "1"}, {itemNumber, "1"},
{itemNumber, "02"}, {itemNumber, "02"},
...@@ -43,6 +43,8 @@ var lexTests = []lexTest{ ...@@ -43,6 +43,8 @@ var lexTests = []lexTest{
{itemNumber, "-7.2i"}, {itemNumber, "-7.2i"},
{itemNumber, "1e3"}, {itemNumber, "1e3"},
{itemNumber, "+1.2e-4"}, {itemNumber, "+1.2e-4"},
{itemNumber, "4.2i"},
{itemComplex, "1+2i"},
tRight, tRight,
tEOF, tEOF,
}}, }},
...@@ -68,12 +70,13 @@ var lexTests = []lexTest{ ...@@ -68,12 +70,13 @@ var lexTests = []lexTest{
tRight, tRight,
tEOF, tEOF,
}}, }},
{"keywords", "{{range if else end}}", []item{ {"keywords", "{{range if else end with}}", []item{
tLeft, tLeft,
{itemRange, "range"}, {itemRange, "range"},
{itemIf, "if"}, {itemIf, "if"},
{itemElse, "else"}, {itemElse, "else"},
{itemEnd, "end"}, {itemEnd, "end"},
{itemWith, "with"},
tRight, tRight,
tEOF, tEOF,
}}, }},
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment