diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2018-07-09 20:47:07 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2018-07-09 20:47:07 +0200 |
| commit | 710eefe85a976c438da255499fbefd1a6c989ef6 (patch) | |
| tree | 3467ac95a3c574bdf41105e012df2e4c540ed859 /pkg | |
| parent | f25e57704183544b0d540ef0035acfa6fb9071d7 (diff) | |
pkg/compiler: support negative integers
Currently we have to use 0xffffffffffffffff to represent -1,
and we can't express e.g. -20:20 int range.
Support negative consts to fix both problems.
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/ast/ast.go | 15 | ||||
| -rw-r--r-- | pkg/ast/clone.go | 6 | ||||
| -rw-r--r-- | pkg/ast/format.go | 34 | ||||
| -rw-r--r-- | pkg/ast/parser.go | 17 | ||||
| -rw-r--r-- | pkg/ast/scanner.go | 29 | ||||
| -rw-r--r-- | pkg/ast/testdata/all.txt | 1 | ||||
| -rw-r--r-- | pkg/compiler/testdata/all.txt | 6 | ||||
| -rw-r--r-- | pkg/compiler/testdata/errors.txt | 1 | ||||
| -rw-r--r-- | pkg/compiler/testdata/errors2.txt | 5 | ||||
| -rw-r--r-- | pkg/compiler/types.go | 44 |
10 files changed, 96 insertions, 62 deletions
diff --git a/pkg/ast/ast.go b/pkg/ast/ast.go index 610d22f30..1e1e97221 100644 --- a/pkg/ast/ast.go +++ b/pkg/ast/ast.go @@ -169,19 +169,20 @@ func (n *String) Info() (Pos, string, string) { return n.Pos, tok2str[tokString], "" } -type intFmt int +type IntFmt int const ( - intFmtDec intFmt = iota - intFmtHex - intFmtChar + IntFmtDec IntFmt = iota + IntFmtNeg + IntFmtHex + IntFmtChar ) type Int struct { Pos Pos // Only one of Value, Ident, CExpr is filled. Value uint64 - valueFmt intFmt + ValueFmt IntFmt Ident string CExpr string } @@ -194,7 +195,7 @@ type Type struct { Pos Pos // Only one of Value, Ident, String is filled. Value uint64 - valueFmt intFmt + ValueFmt IntFmt Ident string String string HasString bool @@ -202,7 +203,7 @@ type Type struct { HasColon bool Pos2 Pos Value2 uint64 - value2Fmt intFmt + Value2Fmt IntFmt Ident2 string Args []*Type } diff --git a/pkg/ast/clone.go b/pkg/ast/clone.go index 9ccc3ea76..072425115 100644 --- a/pkg/ast/clone.go +++ b/pkg/ast/clone.go @@ -165,7 +165,7 @@ func (n *Int) Clone() Node { return &Int{ Pos: n.Pos, Value: n.Value, - valueFmt: n.valueFmt, + ValueFmt: n.ValueFmt, Ident: n.Ident, CExpr: n.CExpr, } @@ -179,14 +179,14 @@ func (n *Type) Clone() Node { return &Type{ Pos: n.Pos, Value: n.Value, - valueFmt: n.valueFmt, + ValueFmt: n.ValueFmt, Ident: n.Ident, String: n.String, HasString: n.HasString, HasColon: n.HasColon, Pos2: n.Pos2, Value2: n.Value2, - value2Fmt: n.value2Fmt, + Value2Fmt: n.Value2Fmt, Ident2: n.Ident2, Args: args, } diff --git a/pkg/ast/format.go b/pkg/ast/format.go index 5022d5da1..13cd26e2c 100644 --- a/pkg/ast/format.go +++ b/pkg/ast/format.go @@ -35,6 +35,21 @@ func SerializeNode(n Node) string { return buf.String() } +func FormatInt(v uint64, format IntFmt) string { + switch format { + case IntFmtDec: + return fmt.Sprint(v) + case IntFmtNeg: + return fmt.Sprint(int64(v)) + case IntFmtHex: + return fmt.Sprintf("0x%x", v) + case IntFmtChar: + return fmt.Sprintf("'%c'", v) + default: + panic(fmt.Sprintf("unknown int format %v", format)) + } +} + type serializer interface { serialize(w io.Writer) } @@ -159,14 +174,14 @@ func fmtType(t *Type) string { case t.HasString: v = fmt.Sprintf("\"%v\"", t.String) default: - v = fmtIntValue(t.Value, t.valueFmt) + v = FormatInt(t.Value, t.ValueFmt) } if t.HasColon { switch { case t.Ident2 != "": v += fmt.Sprintf(":%v", t.Ident2) default: - v += fmt.Sprintf(":%v", fmtIntValue(t.Value2, t.value2Fmt)) + v += fmt.Sprintf(":%v", FormatInt(t.Value2, t.Value2Fmt)) } } v += fmtTypeList(t.Args) @@ -206,20 +221,7 @@ func fmtInt(i *Int) string { case i.CExpr != "": return fmt.Sprintf("%v", i.CExpr) default: - return fmtIntValue(i.Value, i.valueFmt) - } -} - -func fmtIntValue(v uint64, format intFmt) string { - switch format { - case intFmtDec: - return fmt.Sprint(v) - case intFmtHex: - return fmt.Sprintf("0x%x", v) - case intFmtChar: - return fmt.Sprintf("'%c'", v) - default: - panic(fmt.Sprintf("unknown int format %v", format)) + return FormatInt(i.Value, i.ValueFmt) } } diff --git a/pkg/ast/parser.go b/pkg/ast/parser.go index e4c5d8742..d43011796 100644 --- a/pkg/ast/parser.go +++ b/pkg/ast/parser.go @@ -413,7 +413,7 @@ func (p *parser) parseType() *Type { switch p.tok { case tokInt: allowColon = true - arg.Value, arg.valueFmt = p.parseIntValue() + arg.Value, arg.ValueFmt = p.parseIntValue() case tokIdent: allowColon = true arg.Ident = p.lit @@ -429,7 +429,7 @@ func (p *parser) parseType() *Type { arg.Pos2 = p.pos switch p.tok { case tokInt: - arg.Value2, arg.value2Fmt = p.parseIntValue() + arg.Value2, arg.Value2Fmt = p.parseIntValue() case tokIdent: arg.Ident2 = p.lit default: @@ -479,7 +479,7 @@ func (p *parser) parseInt() *Int { } switch p.tok { case tokInt: - i.Value, i.valueFmt = p.parseIntValue() + i.Value, i.ValueFmt = p.parseIntValue() case tokIdent: i.Ident = p.lit default: @@ -489,16 +489,19 @@ func (p *parser) parseInt() *Int { return i } -func (p *parser) parseIntValue() (uint64, intFmt) { +func (p *parser) parseIntValue() (uint64, IntFmt) { if p.lit[0] == '\'' { - return uint64(p.lit[1]), intFmtChar + return uint64(p.lit[1]), IntFmtChar } if v, err := strconv.ParseUint(p.lit, 10, 64); err == nil { - return v, intFmtDec + return v, IntFmtDec + } + if v, err := strconv.ParseInt(p.lit, 10, 64); err == nil { + return uint64(v), IntFmtNeg } if len(p.lit) > 2 && p.lit[0] == '0' && p.lit[1] == 'x' { if v, err := strconv.ParseUint(p.lit[2:], 16, 64); err == nil { - return v, intFmtHex + return v, IntFmtHex } } panic(fmt.Sprintf("scanner returned bad integer %q", p.lit)) diff --git a/pkg/ast/scanner.go b/pkg/ast/scanner.go index 67e7f50bd..af9ac562f 100644 --- a/pkg/ast/scanner.go +++ b/pkg/ast/scanner.go @@ -150,7 +150,7 @@ func (s *scanner) Scan() (tok token, lit string, pos Pos) { case s.ch == '"' || s.ch == '<': tok = tokString lit = s.scanStr(pos) - case s.ch >= '0' && s.ch <= '9': + case s.ch >= '0' && s.ch <= '9' || s.ch == '-': tok = tokInt lit = s.scanInt(pos) case s.ch == '\'': @@ -211,25 +211,26 @@ func (s *scanner) scanStr(pos Pos) string { func (s *scanner) scanInt(pos Pos) string { for s.ch >= '0' && s.ch <= '9' || s.ch >= 'a' && s.ch <= 'f' || - s.ch >= 'A' && s.ch <= 'F' || s.ch == 'x' { + s.ch >= 'A' && s.ch <= 'F' || + s.ch == 'x' || s.ch == '-' { s.next() } lit := string(s.data[pos.Off:s.off]) - bad := false - if _, err := strconv.ParseUint(lit, 10, 64); err != nil { - if len(lit) > 2 && lit[0] == '0' && lit[1] == 'x' { - if _, err := strconv.ParseUint(lit[2:], 16, 64); err != nil { - bad = true - } - } else { - bad = true + if _, err := strconv.ParseUint(lit, 10, 64); err == nil { + return lit + } + if len(lit) > 1 && lit[0] == '-' { + if _, err := strconv.ParseInt(lit, 10, 64); err == nil { + return lit } } - if bad { - s.Error(pos, fmt.Sprintf("bad integer %q", lit)) - lit = "0" + if len(lit) > 2 && lit[0] == '0' && lit[1] == 'x' { + if _, err := strconv.ParseUint(lit[2:], 16, 64); err == nil { + return lit + } } - return lit + s.Error(pos, fmt.Sprintf("bad integer %q", lit)) + return "0" } func (s *scanner) scanChar(pos Pos) string { diff --git a/pkg/ast/testdata/all.txt b/pkg/ast/testdata/all.txt index 6c818eca6..6e4e72d2c 100644 --- a/pkg/ast/testdata/all.txt +++ b/pkg/ast/testdata/all.txt @@ -12,6 +12,7 @@ int_flags = 0, "foo" ### unexpected string, expecting int, identifier int_flags2 = ' ### char literal is not terminated int_flags3 = 'a ### char literal is not terminated int_flags3 = 'a, 1 ### char literal is not terminated +int_flags4 = 1, -2- ### bad integer "-2-" str_flags0 = "foo", "bar" str_flags1 = "non terminated ### string literal is not terminated diff --git a/pkg/compiler/testdata/all.txt b/pkg/compiler/testdata/all.txt index 573fb6412..6fff34284 100644 --- a/pkg/compiler/testdata/all.txt +++ b/pkg/compiler/testdata/all.txt @@ -7,7 +7,9 @@ foo$2(a ptr[out, array[int32]]) foo$3(a union_arg) foo$4() r0 foo$5(a int8['a':'z']) -foo$6(a ptr[in, strings]) +foo$6(a int8[-20:-10]) +foo$7(a int8[-20:20]) +foo$8(a ptr[in, strings]) resource r0[intptr] @@ -38,7 +40,7 @@ strings { string_flags1 = "foo", "barbaz" string_flags2 = "" -int_flags = 0, 1 +int_flags = 0, 1, 0xabc, 'x', -11 _ = 1, 2 _ = C1, C2 diff --git a/pkg/compiler/testdata/errors.txt b/pkg/compiler/testdata/errors.txt index cbfebbfd7..3c2cb57ae 100644 --- a/pkg/compiler/testdata/errors.txt +++ b/pkg/compiler/testdata/errors.txt @@ -329,4 +329,5 @@ foo$fmt4(a ptr[in, fmt[dec, int8:3]]) ### unexpected ':', only struct fields ca struct$fmt0 { f0 fmt[dec, int8:3] ### unexpected ':', only struct fields can be bitfields + f1 int32:-1 ### bitfield of size 18446744073709551615 is too large for base type of size 32 } diff --git a/pkg/compiler/testdata/errors2.txt b/pkg/compiler/testdata/errors2.txt index 1b1adefa5..77db29197 100644 --- a/pkg/compiler/testdata/errors2.txt +++ b/pkg/compiler/testdata/errors2.txt @@ -217,6 +217,11 @@ foo$507(a ptr[in, array[int32, 0:0]]) ### arrays of size 0 are not supported foo$508(a ptr[in, string["foo", 3]]) ### string value "foo\x00" exceeds buffer length 3 foo$509(a int8['b':'a']) ### bad int range [98:97] foo$510(a type500) +foo$511(a int32[-10:-20]) ### bad int range [18446744073709551606:18446744073709551596] +foo$512(a ptr[in, array[int8, -2:-1]]) ### bad size range [18446744073709551614:18446744073709551615] +foo$513(a ptr[in, array[int8, -2:2]]) ### bad size range [18446744073709551614:2] +foo$514(a vma[-2:2]) ### bad size range [18446744073709551614:2] +foo$515(a ptr[in, proc[1, -10, int64]]) ### values starting from 1 with step 18446744073709551606 overflow base type for 32 procs type type500 proc[C1, 8, int8] ### values starting from 1 with step 8 overflow base type for 32 procs type type501 int8 ### unused type type501 diff --git a/pkg/compiler/types.go b/pkg/compiler/types.go index 156b1505a..293496a9c 100644 --- a/pkg/compiler/types.go +++ b/pkg/compiler/types.go @@ -68,7 +68,7 @@ var typeInt = &typeDesc{ AllowColon: true, ResourceBase: true, OptArgs: 1, - Args: []namedArg{{Name: "range", Type: typeArgRange}}, + Args: []namedArg{{Name: "range", Type: typeArgIntRange}}, Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { typeArgBase.Type.Check(comp, t) }, @@ -128,7 +128,7 @@ var typeArray = &typeDesc{ CanBeTypedef: true, CantBeOpt: true, OptArgs: 1, - Args: []namedArg{{Name: "type", Type: typeArgType}, {Name: "size", Type: typeArgRange}}, + Args: []namedArg{{Name: "type", Type: typeArgType}, {Name: "size", Type: typeArgSizeRange}}, CheckConsts: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { if len(args) > 1 && args[1].Value == 0 && args[1].Value2 == 0 { comp.error(args[1].Pos, "arrays of size 0 are not supported") @@ -290,7 +290,7 @@ var typeVMA = &typeDesc{ Names: []string{"vma"}, CanBeArgRet: canBeArg, OptArgs: 1, - Args: []namedArg{{Name: "size range", Type: typeArgRange}}, + Args: []namedArg{{Name: "size range", Type: typeArgSizeRange}}, Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { begin, end := uint64(0), uint64(0) if len(args) > 0 { @@ -367,14 +367,16 @@ var typeProc = &typeDesc{ return } size := base.TypeSize * 8 - if size != 64 { - const maxPids = 32 // executor knows about this constant (MAX_PIDS) - if start >= 1<<size { - comp.error(args[0].Pos, "values starting from %v overflow base type", start) - } else if start+maxPids*perProc > 1<<size { - comp.error(args[0].Pos, "values starting from %v with step %v overflow base type for %v procs", - start, perProc, maxPids) - } + max := uint64(1) << size + if size == 64 { + max = ^uint64(0) + } + const maxPids = 32 // executor knows about this constant (MAX_PIDS) + if start >= max { + comp.error(args[0].Pos, "values starting from %v overflow base type", start) + } else if perProc > (max-start)/maxPids { + comp.error(args[0].Pos, "values starting from %v with step %v overflow base type for %v procs", + start, perProc, maxPids) } }, Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { @@ -763,19 +765,35 @@ var typeArgInt = &typeArg{ Kind: kindInt, } -var typeArgRange = &typeArg{ +var typeArgIntRange = &typeArg{ Kind: kindInt, AllowColon: true, CheckConsts: func(comp *compiler, t *ast.Type) { if !t.HasColon { t.Value2 = t.Value + t.Value2Fmt = t.ValueFmt } - if t.Value > t.Value2 { + if t.Value2-t.Value > 1<<64-1<<32 { comp.error(t.Pos, "bad int range [%v:%v]", t.Value, t.Value2) } }, } +// Size of array and vma's. +var typeArgSizeRange = &typeArg{ + Kind: kindInt, + AllowColon: true, + CheckConsts: func(comp *compiler, t *ast.Type) { + if !t.HasColon { + t.Value2 = t.Value + } + const maxVal = 1e6 + if t.Value > t.Value2 || t.Value > maxVal || t.Value2 > maxVal { + comp.error(t.Pos, "bad size range [%v:%v]", t.Value, t.Value2) + } + }, +} + // Base type of const/len/etc. Same as typeInt, but can't have range. var typeArgBase = namedArg{ Name: "base type", |
