github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/ast/string.go (about) 1 package ast 2 3 import ( 4 "bytes" 5 "fmt" 6 "regexp" 7 "strconv" 8 9 "github.com/arnodel/golua/luastrings" 10 "github.com/arnodel/golua/token" 11 ) 12 13 // String is a string literal. 14 type String struct { 15 Location 16 Val []byte 17 } 18 19 var _ ExpNode = String{} 20 21 // NewString returns a String from a token containing a Lua short string 22 // literal, or an error if it couln't (although perhaps it should panic instead? 23 // Parsing should ensure well-formed string token). 24 func NewString(id *token.Token) (ss String, err error) { 25 s := luastrings.NormalizeNewLines(id.Lit) 26 defer func() { 27 if r := recover(); r != nil { 28 err2, ok := r.(error) 29 if ok { 30 err = err2 31 } 32 } 33 }() 34 return String{ 35 Location: LocFromToken(id), 36 Val: escapeSeqs.ReplaceAllFunc(s[1:len(s)-1], replaceEscapeSeq), 37 }, nil 38 } 39 40 // NewLongString returns a String computed from a token containing a Lua long 41 // string. 42 func NewLongString(id *token.Token) String { 43 s := luastrings.NormalizeNewLines(id.Lit) 44 idx := bytes.IndexByte(s[1:], '[') + 2 45 contents := s[idx : len(s)-idx] 46 if contents[0] == '\n' { 47 contents = contents[1:] 48 } 49 return String{ 50 Location: LocFromToken(id), 51 Val: contents, 52 } 53 } 54 55 // ProcessExp uses the given ExpProcessor to process the receiver. 56 func (s String) ProcessExp(p ExpProcessor) { 57 p.ProcessStringExp(s) 58 } 59 60 // HWrite prints a tree representation of the node. 61 func (s String) HWrite(w HWriter) { 62 w.Writef("%q", s.Val) 63 } 64 65 // This function replaces escape sequences with the values they escape. 66 func replaceEscapeSeq(e []byte) []byte { 67 switch e[1] { 68 case 'a': 69 return []byte{7} 70 case 'b': 71 return []byte{8} 72 case 't': 73 return []byte{9} 74 case 'n': 75 return []byte{10} 76 case 'v': 77 return []byte{11} 78 case 'f': 79 return []byte{12} 80 case 'r': 81 return []byte{13} 82 case 'z': 83 return []byte{} 84 case 'x', 'X': 85 b, err := strconv.ParseInt(string(e[2:]), 16, 64) 86 if err != nil { 87 panic(err) 88 } 89 return []byte{byte(b)} 90 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 91 b, err := strconv.ParseInt(string(e[1:]), 10, 64) 92 if err != nil { 93 panic(err) 94 } 95 if b >= 256 { 96 panic(fmt.Errorf("decimal escape sequence out of range near '%s'", e)) 97 } 98 return []byte{byte(b)} 99 case 'u', 'U': 100 i, err := strconv.ParseInt(string(e[3:len(e)-1]), 16, 32) 101 if err != nil { 102 panic(fmt.Errorf("unicode escape sequence out of range near '%s'", e)) 103 } 104 var p [6]byte 105 n := luastrings.UTF8EncodeInt32(p[:], int32(i)) 106 return p[:n] 107 default: 108 return e[1:] 109 } 110 } 111 112 // This regex matches all the escape sequences that can be found in a Lua string. 113 var escapeSeqs = regexp.MustCompile(`(?s)\\\d{1,3}|\\[xX][0-9a-fA-F]{2}|\\[abtnvfr\\]|\\z[\s\v]*|\\[uU]{[0-9a-fA-F]+}|\\.`)