github.com/gopherd/gonum@v0.0.4/cmplxs/cscalar/parse.go (about)

     1  // Copyright ©2020 The Gonum Authors. All rights reserved.
     2  // Use of this code is governed by a BSD-style
     3  // license that can be found in the LICENSE file
     4  
     5  package cscalar
     6  
     7  import (
     8  	"fmt"
     9  	"math/cmplx"
    10  	"strconv"
    11  	"strings"
    12  )
    13  
    14  // parse converts the string s to a complex128. The string may be parenthesized and
    15  // has the format [±]N±Ni. The order of the components is not strict.
    16  func parse(s string) (complex128, error) {
    17  	if len(s) == 0 {
    18  		return 0, parseError{state: -1}
    19  	}
    20  	orig := s
    21  
    22  	wantClose := s[0] == '('
    23  	if wantClose {
    24  		if s[len(s)-1] != ')' {
    25  			return 0, parseError{string: orig, state: -1}
    26  		}
    27  		s = s[1 : len(s)-1]
    28  	}
    29  	if len(s) == 0 {
    30  		return 0, parseError{string: orig, state: -1}
    31  	}
    32  	switch s[0] {
    33  	case 'n', 'N':
    34  		if strings.ToLower(s) == "nan" {
    35  			return cmplx.NaN(), nil
    36  		}
    37  	case 'i', 'I':
    38  		if strings.ToLower(s) == "inf" {
    39  			return cmplx.Inf(), nil
    40  		}
    41  	}
    42  
    43  	var q complex128
    44  	var parts byte
    45  	for i := 0; i < 4; i++ {
    46  		beg, end, p, err := floatPart(s)
    47  		if err != nil {
    48  			return q, parseError{string: orig, state: -1}
    49  		}
    50  		if parts&(1<<p) != 0 {
    51  			return q, parseError{string: orig, state: -1}
    52  		}
    53  		parts |= 1 << p
    54  		var v float64
    55  		switch s[:end] {
    56  		case "-":
    57  			if len(s[end:]) == 0 {
    58  				return q, parseError{string: orig, state: -1}
    59  			}
    60  			v = -1
    61  		case "+":
    62  			if len(s[end:]) == 0 {
    63  				return q, parseError{string: orig, state: -1}
    64  			}
    65  			v = 1
    66  		default:
    67  			v, err = strconv.ParseFloat(s[beg:end], 64)
    68  			if err != nil {
    69  				return q, err
    70  			}
    71  		}
    72  		s = s[end:]
    73  		switch p {
    74  		case 0:
    75  			q += complex(v, 0)
    76  		case 1:
    77  			q += complex(0, v)
    78  			s = s[1:]
    79  		}
    80  		if len(s) == 0 {
    81  			return q, nil
    82  		}
    83  		if !isSign(rune(s[0])) {
    84  			return q, parseError{string: orig, state: -1}
    85  		}
    86  	}
    87  
    88  	return q, parseError{string: orig, state: -1}
    89  }
    90  
    91  func floatPart(s string) (beg, end int, part uint, err error) {
    92  	const (
    93  		wantMantSign = iota
    94  		wantMantIntInit
    95  		wantMantInt
    96  		wantMantFrac
    97  		wantExpSign
    98  		wantExpInt
    99  
   100  		wantInfN
   101  		wantInfF
   102  		wantCloseInf
   103  
   104  		wantNaNA
   105  		wantNaNN
   106  		wantCloseNaN
   107  	)
   108  	var i, state int
   109  	var r rune
   110  	for i, r = range s {
   111  		switch state {
   112  		case wantMantSign:
   113  			switch {
   114  			default:
   115  				return 0, i, 0, parseError{string: s, state: state, rune: r}
   116  			case isSign(r):
   117  				state = wantMantIntInit
   118  			case isDigit(r):
   119  				state = wantMantInt
   120  			case isDot(r):
   121  				state = wantMantFrac
   122  			case r == 'i', r == 'I':
   123  				state = wantInfN
   124  			case r == 'n', r == 'N':
   125  				state = wantNaNA
   126  			}
   127  
   128  		case wantMantIntInit:
   129  			switch {
   130  			default:
   131  				return 0, i, 0, parseError{string: s, state: state, rune: r}
   132  			case isDigit(r):
   133  				state = wantMantInt
   134  			case isDot(r):
   135  				state = wantMantFrac
   136  			case r == 'i':
   137  				// We need to sneak a look-ahead here.
   138  				if i == len(s)-1 || s[i+1] == '-' || s[i+1] == '+' {
   139  					return 0, i, 1, nil
   140  				}
   141  				fallthrough
   142  			case r == 'I':
   143  				state = wantInfN
   144  			case r == 'n', r == 'N':
   145  				state = wantNaNA
   146  			}
   147  
   148  		case wantMantInt:
   149  			switch {
   150  			default:
   151  				return 0, i, 0, parseError{string: s, state: state, rune: r}
   152  			case isDigit(r):
   153  				// Do nothing
   154  			case isDot(r):
   155  				state = wantMantFrac
   156  			case isExponent(r):
   157  				state = wantExpSign
   158  			case isSign(r):
   159  				return 0, i, 0, nil
   160  			case r == 'i':
   161  				return 0, i, 1, nil
   162  			}
   163  
   164  		case wantMantFrac:
   165  			switch {
   166  			default:
   167  				return 0, i, 0, parseError{string: s, state: state, rune: r}
   168  			case isDigit(r):
   169  				// Do nothing
   170  			case isExponent(r):
   171  				state = wantExpSign
   172  			case isSign(r):
   173  				return 0, i, 0, nil
   174  			case r == 'i':
   175  				return 0, i, 1, nil
   176  			}
   177  
   178  		case wantExpSign:
   179  			switch {
   180  			default:
   181  				return 0, i, 0, parseError{string: s, state: state, rune: r}
   182  			case isSign(r) || isDigit(r):
   183  				state = wantExpInt
   184  			}
   185  
   186  		case wantExpInt:
   187  			switch {
   188  			default:
   189  				return 0, i, 0, parseError{string: s, state: state, rune: r}
   190  			case isDigit(r):
   191  				// Do nothing
   192  			case isSign(r):
   193  				return 0, i, 0, nil
   194  			case r == 'i':
   195  				return 0, i, 1, nil
   196  			}
   197  
   198  		case wantInfN:
   199  			if r != 'n' && r != 'N' {
   200  				return 0, i, 0, parseError{string: s, state: state, rune: r}
   201  			}
   202  			state = wantInfF
   203  		case wantInfF:
   204  			if r != 'f' && r != 'F' {
   205  				return 0, i, 0, parseError{string: s, state: state, rune: r}
   206  			}
   207  			state = wantCloseInf
   208  		case wantCloseInf:
   209  			switch {
   210  			default:
   211  				return 0, i, 0, parseError{string: s, state: state, rune: r}
   212  			case isSign(r):
   213  				return 0, i, 0, nil
   214  			case r == 'i':
   215  				return 0, i, 1, nil
   216  			}
   217  
   218  		case wantNaNA:
   219  			if r != 'a' && r != 'A' {
   220  				return 0, i, 0, parseError{string: s, state: state, rune: r}
   221  			}
   222  			state = wantNaNN
   223  		case wantNaNN:
   224  			if r != 'n' && r != 'N' {
   225  				return 0, i, 0, parseError{string: s, state: state, rune: r}
   226  			}
   227  			state = wantCloseNaN
   228  		case wantCloseNaN:
   229  			if isSign(rune(s[0])) {
   230  				beg = 1
   231  			}
   232  			switch {
   233  			default:
   234  				return beg, i, 0, parseError{string: s, state: state, rune: r}
   235  			case isSign(r):
   236  				return beg, i, 0, nil
   237  			case r == 'i':
   238  				return beg, i, 1, nil
   239  			}
   240  		}
   241  	}
   242  	switch state {
   243  	case wantMantSign, wantExpSign, wantExpInt:
   244  		if state == wantExpInt && isDigit(r) {
   245  			break
   246  		}
   247  		return 0, i, 0, parseError{string: s, state: state, rune: r}
   248  	}
   249  	return 0, len(s), 0, nil
   250  }
   251  
   252  func isSign(r rune) bool {
   253  	return r == '+' || r == '-'
   254  }
   255  
   256  func isDigit(r rune) bool {
   257  	return '0' <= r && r <= '9'
   258  }
   259  
   260  func isExponent(r rune) bool {
   261  	return r == 'e' || r == 'E'
   262  }
   263  
   264  func isDot(r rune) bool {
   265  	return r == '.'
   266  }
   267  
   268  type parseError struct {
   269  	string string
   270  	state  int
   271  	rune   rune
   272  }
   273  
   274  func (e parseError) Error() string {
   275  	if e.state < 0 {
   276  		return fmt.Sprintf("quat: failed to parse: %q", e.string)
   277  	}
   278  	return fmt.Sprintf("quat: failed to parse in state %d with %q: %q", e.state, e.rune, e.string)
   279  }