lexer.go (3062B)
1 package lexer 2 3 import ( 4 "bytes" 5 "fmt" 6 oLexer "github.com/alecthomas/participle/lexer" 7 "io" 8 "strconv" 9 "strings" 10 "text/scanner" 11 "unicode/utf8" 12 ) 13 14 var MatrixDefinition = &matrixDefinition{} 15 16 type matrixDefinition struct{} 17 18 func (d *matrixDefinition) Lex(r io.Reader) oLexer.Lexer { 19 return Lex(r) 20 } 21 22 func (d *matrixDefinition) Symbols() map[string]rune { 23 return map[string]rune{ 24 "EOF": scanner.EOF, 25 "Char": scanner.Char, 26 "Ident": scanner.Ident, 27 "Int": scanner.Int, 28 "Float": scanner.Float, 29 "String": scanner.String, 30 "RawString": scanner.RawString, 31 "Comment": scanner.Comment, 32 } 33 } 34 35 // textScannerLexer is a Lexer based on text/scanner.Scanner 36 type textScannerLexer struct { 37 scanner *scanner.Scanner 38 filename string 39 } 40 41 // Lex an io.Reader with text/scanner.Scanner. 42 // 43 // This provides very fast lexing of source code compatibile with Go tokens. 44 // 45 // Note that this differs from text/scanner.Scanner in that string tokens will be unquoted. 46 func Lex(r io.Reader) oLexer.Lexer { 47 lexer := lexWithScanner(r, &scanner.Scanner{}) 48 lexer.scanner.Error = func(s *scanner.Scanner, msg string) { 49 // This is to support single quoted strings. Hacky. 50 if msg != "illegal char literal" { 51 oLexer.Panic(oLexer.Position(lexer.scanner.Pos()), msg) 52 } 53 } 54 return oLexer.Upgrade(lexer) 55 } 56 57 // LexWithScanner creates a Lexer from a user-provided scanner.Scanner. 58 // 59 // Useful if you need to customise the Scanner. 60 // 61 // Note that if this function is used, single-quoted strings are not supported. See the source for 62 // Lex() for how to achieve this. 63 64 func LexWithScanner(r io.Reader, scan *scanner.Scanner) oLexer.Lexer { 65 return oLexer.Upgrade(lexWithScanner(r, scan)) 66 } 67 68 func lexWithScanner(r io.Reader, scan *scanner.Scanner) *textScannerLexer { 69 lexer := &textScannerLexer{ 70 filename: oLexer.NameOfReader(r), 71 scanner: scan, 72 } 73 lexer.scanner.Init(r) 74 return lexer 75 } 76 77 // LexBytes returns a new default lexer over bytes. 78 func LexBytes(b []byte) oLexer.Lexer { 79 return Lex(bytes.NewReader(b)) 80 } 81 82 // LexString returns a new default lexer over a string. 83 func LexString(s string) oLexer.Lexer { 84 return Lex(strings.NewReader(s)) 85 } 86 87 func (t *textScannerLexer) Next() oLexer.Token { 88 typ := t.scanner.Scan() 89 text := t.scanner.TokenText() 90 pos := oLexer.Position(t.scanner.Position) 91 pos.Filename = t.filename 92 out := oLexer.Token{ 93 Type: typ, 94 Value: text, 95 Pos: pos, 96 } 97 out.Pos.Filename = t.filename 98 // Unquote strings. 99 switch out.Type { 100 case scanner.Char: 101 // FIXME(alec): This is pretty hacky...we convert a single quoted char into a double 102 // quoted string in order to support single quoted strings. 103 out.Value = fmt.Sprintf("\"%s\"", out.Value[1:len(out.Value)-1]) 104 fallthrough 105 case scanner.String: 106 s, err := strconv.Unquote(out.Value) 107 if err != nil { 108 oLexer.Panic(out.Pos, err.Error()) 109 } 110 out.Value = s 111 if out.Type == scanner.Char && utf8.RuneCountInString(s) > 1 { 112 out.Type = scanner.String 113 } 114 case scanner.RawString: 115 out.Value = out.Value[1 : len(out.Value)-1] 116 } 117 return out 118 }