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.
This commit is contained in:
Dmitry Vyukov 2018-07-09 20:47:07 +02:00
parent f25e577041
commit 710eefe85a
11 changed files with 100 additions and 65 deletions

View File

@ -78,7 +78,8 @@ flagname = "\"" literal "\"" ["," "\"" literal "\""]*
## Ints
`int8`, `int16`, `int32` and `int64` denote an integer of the corresponding size. `intptr` denotes a pointer-sized integer, i.e. C `long` type.
`int8`, `int16`, `int32` and `int64` denote an integer of the corresponding size.
`intptr` denotes a pointer-sized integer, i.e. C `long` type.
By appending `be` suffix (e.g. `int16be`) integers become big-endian.
@ -260,7 +261,7 @@ hex literals, as `'`-surrounded char literals, or as symbolic constants
extracted from kernel headers or defined by `define` directives. For example:
```
foo(a const[10])
foo(a const[10], b const[-10])
foo(a const[0xabcd])
foo(a int8['a':'z'])
foo(a const[PATH_MAX])

View File

@ -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
}

View File

@ -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,
}

View File

@ -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)
}
}

View File

@ -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))

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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,15 +765,31 @@ 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.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
}
if t.Value > t.Value2 {
comp.error(t.Pos, "bad int range [%v:%v]", t.Value, t.Value2)
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)
}
},
}