github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/ast/number.go (about) 1 package ast 2 3 import ( 4 "bytes" 5 "strconv" 6 "strings" 7 8 "github.com/arnodel/golua/token" 9 ) 10 11 // NewNumber returns an ExpNode that represents the numeric literal in the given 12 // token, or an error if it's not a valid numeric literal (the error should not 13 // happen, perhaps panic instead?). 14 func NewNumber(id *token.Token) (ExpNode, error) { 15 loc := LocFromToken(id) 16 if ft := toFloatToken(id); ft != "" { 17 f, err := strconv.ParseFloat(ft, 64) 18 if err != nil && f == 0 { 19 return nil, err 20 } 21 return Float{Location: loc, Val: f}, nil 22 } 23 var ( 24 nstring = string(id.Lit) 25 n uint64 26 err error 27 ) 28 if strings.HasPrefix(nstring, "0x") || strings.HasPrefix(nstring, "0X") { 29 nstring = nstring[2:] 30 if len(nstring) > 16 { 31 // A hex integral constant is "truncated" if too long (more than 8 bytes) 32 nstring = nstring[len(nstring)-16:] 33 } 34 n, err = strconv.ParseUint(nstring, 16, 64) 35 } else { 36 n, err = strconv.ParseUint(nstring, 10, 64) 37 // If an integer is too big let's make it a float 38 if err != nil { 39 f, err := strconv.ParseFloat(nstring, 64) 40 if err == nil || f != 0 { 41 return Float{Location: loc, Val: f}, nil 42 } 43 } 44 } 45 if err != nil { 46 return nil, err 47 } 48 return Int{Location: loc, Val: n}, nil 49 } 50 51 // IsNumber returns true if the given expression node is a numerical value. 52 func IsNumber(e ExpNode) bool { 53 n, ok := e.(numberOracle) 54 return ok && n.isNumber() 55 } 56 57 // 58 // Int 59 // 60 61 // Int is an expression node representing a non-negative integer literal. 62 type Int struct { 63 Location 64 Val uint64 65 } 66 67 var _ ExpNode = Int{} 68 69 // NewInt returns an Int instance with the given value. 70 func NewInt(val uint64) Int { 71 return Int{Val: val} 72 } 73 74 // HWrite prints a tree representation of the node. 75 func (n Int) HWrite(w HWriter) { 76 w.Writef("%d", n.Val) 77 } 78 79 // ProcessExp uses the given ExpProcessor to process the receiver. 80 func (n Int) ProcessExp(p ExpProcessor) { 81 p.ProcessIntExp(n) 82 } 83 84 func (n Int) isNumber() bool { 85 return true 86 } 87 88 // 89 // Float 90 // 91 92 // Float is an expression node representing a floating point numeric literal. 93 type Float struct { 94 Location 95 Val float64 96 } 97 98 var _ ExpNode = Float{} 99 100 // NewFloat returns a Float instance with the given value. 101 func NewFloat(x float64) Float { 102 return Float{Val: x} 103 } 104 105 // ProcessExp uses the given ExpProcessor to process the receiver. 106 func (f Float) ProcessExp(p ExpProcessor) { 107 p.ProcessFloatExp(f) 108 } 109 110 // HWrite prints a tree representation of the node. 111 func (f Float) HWrite(w HWriter) { 112 w.Writef("%f", f.Val) 113 } 114 115 func (f Float) isNumber() bool { 116 return true 117 } 118 119 type numberOracle interface { 120 isNumber() bool 121 } 122 123 func toFloatToken(tok *token.Token) string { 124 switch tok.Type { 125 case token.NUMDEC: 126 if !bytes.ContainsAny(tok.Lit, ".eE") { 127 return "" 128 } 129 return string(tok.Lit) 130 case token.NUMHEX: 131 if !bytes.ContainsAny(tok.Lit, ".pP") { 132 return "" 133 } 134 if !bytes.ContainsAny(tok.Lit, "pP") { 135 return string(tok.Lit) + "p0" 136 } 137 return string(tok.Lit) 138 default: 139 return "" 140 } 141 }