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  }