diff options
author | Ting-Wei Lan <tingwei.lan@cobinhood.com> | 2019-01-24 17:46:39 +0800 |
---|---|---|
committer | Jhih-Ming Huang <jm.huang@cobinhood.com> | 2019-05-06 10:44:03 +0800 |
commit | f73b9db5523ec173d738f96a5127061ad8b37164 (patch) | |
tree | 168be62c8632dd70887e2a7a71d23694e12c39f2 /core/vm/sqlvm/parser/parser.go | |
parent | d07af9fe6ba1299bec081b77d575d31a23a2f6e1 (diff) | |
download | dexon-f73b9db5523ec173d738f96a5127061ad8b37164.tar dexon-f73b9db5523ec173d738f96a5127061ad8b37164.tar.gz dexon-f73b9db5523ec173d738f96a5127061ad8b37164.tar.bz2 dexon-f73b9db5523ec173d738f96a5127061ad8b37164.tar.lz dexon-f73b9db5523ec173d738f96a5127061ad8b37164.tar.xz dexon-f73b9db5523ec173d738f96a5127061ad8b37164.tar.zst dexon-f73b9db5523ec173d738f96a5127061ad8b37164.zip |
core: vm: sqlvm: parser: properly handle errors
Instead of ignoring errors, errors returned from external functions are
normalized and reported to users. Errors which should be impossible to
occur are converted to panic calls.
Diffstat (limited to 'core/vm/sqlvm/parser/parser.go')
-rw-r--r-- | core/vm/sqlvm/parser/parser.go | 85 |
1 files changed, 72 insertions, 13 deletions
diff --git a/core/vm/sqlvm/parser/parser.go b/core/vm/sqlvm/parser/parser.go index 19d5e7477..8904c747c 100644 --- a/core/vm/sqlvm/parser/parser.go +++ b/core/vm/sqlvm/parser/parser.go @@ -1,16 +1,21 @@ package parser import ( + "bytes" "encoding/hex" + "fmt" "strconv" "strings" "github.com/dexon-foundation/dexon/core/vm/sqlvm/ast" + "github.com/dexon-foundation/dexon/core/vm/sqlvm/errors" "github.com/shopspring/decimal" ) // Parser was generated with pigeon v1.0.0-99-gbb0192c. //go:generate pigeon -no-recover -o grammar.go grammar.peg +//go:generate sh -c "sed -f grammar.sed grammar.go > grammar_new.go" +//go:generate mv grammar_new.go grammar.go //go:generate goimports -w grammar.go func prepend(x interface{}, xs interface{}) []interface{} { @@ -34,7 +39,10 @@ func hexToInteger(h []byte) interface{} { l := len(h) base := decimal.New(16, 0) for idx, b := range h { - i, _ := strconv.ParseInt(string([]byte{b}), 16, 32) + i, err := strconv.ParseInt(string([]byte{b}), 16, 32) + if err != nil { + panic(fmt.Sprintf("invalid hex digit %s: %v", []byte{b}, err)) + } d = d.Add( decimal.New(i, 0). Mul(base.Pow(decimal.New(int64(l-idx-1), 0))), @@ -44,21 +52,54 @@ func hexToInteger(h []byte) interface{} { } func hexToBytes(h []byte) []byte { - bs, _ := hex.DecodeString(string(h)) + bs := make([]byte, hex.DecodedLen(len(h))) + _, err := hex.Decode(bs, h) + if err != nil { + panic(fmt.Sprintf("invalid hex string %s: %v", h, err)) + } return bs } -func toUint(b []byte) uint32 { - i, _ := strconv.ParseUint(string(b), 10, 32) - return uint32(i) +func convertNumError(err error) errors.ErrorCode { + if err == nil { + return errors.ErrorCodeNil + } + switch err.(*strconv.NumError).Err { + case strconv.ErrSyntax: + return errors.ErrorCodeSyntax + case strconv.ErrRange: + return errors.ErrorCodeIntegerRange + } + panic(fmt.Sprintf("unknown NumError: %v", err)) +} + +func convertDecimalError(err error) errors.ErrorCode { + if err == nil { + return errors.ErrorCodeNil + } + errStr := err.Error() + if strings.HasSuffix(errStr, "decimal: fractional part too long") { + return errors.ErrorCodeFractionalPartTooLong + } else if strings.HasSuffix(errStr, "decimal: exponent is not numeric") { + return errors.ErrorCodeSyntax + } else if strings.HasSuffix(errStr, "decimal: too many .s") { + return errors.ErrorCodeSyntax + } + panic(fmt.Sprintf("unknown decimal error: %v", err)) +} + +func toUint(b []byte) (uint32, errors.ErrorCode) { + i, err := strconv.ParseUint(string(b), 10, 32) + return uint32(i), convertNumError(err) } -func toDecimal(b []byte) decimal.Decimal { - return decimal.RequireFromString(string(b)) +func toDecimal(b []byte) (decimal.Decimal, errors.ErrorCode) { + d, err := decimal.NewFromString(string(b)) + return d, convertDecimalError(err) } func toLower(b []byte) []byte { - return []byte(strings.ToLower(string(b))) + return bytes.ToLower(b) } func joinBytes(x interface{}) []byte { @@ -105,10 +146,7 @@ func rightJoinOperators(o interface{}, x interface{}) interface{} { return o } l := len(xs) - for idx := range xs { - if idx == l-1 { - break - } + for idx := 0; idx < l-1; idx++ { joinOperator(xs[idx+1], xs[idx]) } joinOperator(xs[0], o) @@ -122,5 +160,26 @@ func resolveString(s []byte) []byte { // ParseString parses input string to AST. func ParseString(s string) (interface{}, error) { - return ParseReader("parser", strings.NewReader(s)) + root, pigeonErr := ParseReader("", strings.NewReader(s)) + if pigeonErr == nil { + return root, pigeonErr + } + pigeonErrList := pigeonErr.(errList) + sqlvmErrList := make(errors.ErrorList, len(pigeonErrList)) + for i := range pigeonErrList { + parserErr := pigeonErrList[i].(*parserError) + if sqlvmErr, ok := parserErr.Inner.(errors.Error); ok { + sqlvmErrList[i] = sqlvmErr + } else { + sqlvmErrList[i] = errors.Error{ + Position: uint32(parserErr.pos.offset), + Category: errors.ErrorCategoryGrammar, + Code: errors.ErrorCodeParser, + Token: "", + Prefix: parserErr.prefix, + Message: parserErr.Inner.Error(), + } + } + } + return root, sqlvmErrList } |