Skip to content

Commit c9b277c

Browse files
authored
Merge pull request #446 from goplus/main
v1.16.2
2 parents a216441 + d741757 commit c9b277c

File tree

7 files changed

+334
-16
lines changed

7 files changed

+334
-16
lines changed

cb_unit.go

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
Copyright 2024 The GoPlus Authors (goplus.org)
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package gogen
15+
16+
import (
17+
"go/ast"
18+
"go/constant"
19+
"go/token"
20+
"go/types"
21+
"log"
22+
"strings"
23+
)
24+
25+
// ----------------------------------------------------------------------------
26+
27+
type objectID struct {
28+
pkg string
29+
name string
30+
}
31+
32+
type typeUnits = map[string]constant.Value
33+
34+
// const Gopu_XXX = "mm=1,cm=10,dm=100,m=1000"
35+
func (p *Package) buildTypeUnits(id objectID, tok token.Token) (ret typeUnits, ok bool) {
36+
v, ok := p.lookupTypeUnitsVal(id)
37+
if ok {
38+
units := strings.Split(v, ",")
39+
ret = make(typeUnits, len(units))
40+
for _, unit := range units {
41+
if pos := strings.Index(unit, "="); pos > 0 {
42+
ret[unit[:pos]] = constant.MakeFromLiteral(unit[pos+1:], tok, 0)
43+
}
44+
}
45+
}
46+
return
47+
}
48+
49+
/*
50+
"ns": time.Nanosecond,
51+
"us": time.Microsecond,
52+
"µs": time.Microsecond,
53+
"ms": time.Millisecond,
54+
"s": time.Second,
55+
"m": time.Minute,
56+
"h": time.Hour,
57+
"d": 24 * time.Hour,
58+
*/
59+
func (p *Package) lookupTypeUnitsVal(id objectID) (string, bool) {
60+
if id.name == "Duration" && id.pkg == "time" { // time.Duration
61+
const tv = "ns=1,us=1000,µs=1000,ms=1000000,s=1000000000,m=60000000000,h=3600000000000,d=86400000000000"
62+
return tv, true
63+
}
64+
imp := p.Import(id.pkg)
65+
if ounits := imp.TryRef("Gopu_" + id.name); ounits != nil {
66+
if v := ounits.(*types.Const).Val(); v.Kind() == constant.String {
67+
return constant.StringVal(v), true
68+
}
69+
}
70+
return "", false
71+
}
72+
73+
// ----------------------------------------------------------------------------
74+
75+
type unitMgr struct {
76+
units map[objectID]typeUnits
77+
}
78+
79+
func (p *unitMgr) init() {
80+
units := make(map[objectID]typeUnits)
81+
p.units = units
82+
}
83+
84+
func (p *Package) getUnits(id objectID, tok token.Token) (units typeUnits, ok bool) {
85+
units, ok = p.units[id]
86+
if !ok {
87+
if units, ok = p.buildTypeUnits(id, tok); ok {
88+
p.units[id] = units
89+
}
90+
}
91+
return
92+
}
93+
94+
// ----------------------------------------------------------------------------
95+
96+
// ValWithUnit func
97+
func (p *CodeBuilder) ValWithUnit(v *ast.BasicLit, t types.Type, unit string) *CodeBuilder {
98+
if debugInstr {
99+
log.Println("ValWithUnit", v.Value, t, unit)
100+
}
101+
named, ok := t.(*types.Named)
102+
if !ok {
103+
log.Panicf("TODO: ValWithUnit: `%v` isn't a named type", t)
104+
}
105+
pkg := p.pkg
106+
e := toExpr(pkg, v, v)
107+
ot := named.Obj()
108+
id := objectID{ot.Pkg().Path(), ot.Name()}
109+
units, ok := pkg.getUnits(id, token.INT) // TODO: INT or FLOAT
110+
if !ok {
111+
log.Panicf("TODO: ValWithUnit: no units of `%s.%s` found", id.pkg, id.name)
112+
}
113+
u, ok := units[unit]
114+
if !ok {
115+
log.Panicf("TODO: ValWithUnit: unknown unit `%s` for `%s.%s`", unit, id.pkg, id.name)
116+
}
117+
val := constant.BinaryOp(e.CVal, token.MUL, u)
118+
e.CVal = val
119+
e.Val = &ast.BasicLit{Kind: token.INT, Value: val.ExactString()}
120+
e.Type = t
121+
p.Val(e, v)
122+
return p
123+
}
124+
125+
// ----------------------------------------------------------------------------

cb_unit_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
Copyright 2024 The GoPlus Authors (goplus.org)
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package gogen
15+
16+
import (
17+
"go/ast"
18+
"go/constant"
19+
"go/token"
20+
"go/types"
21+
"testing"
22+
"time"
23+
)
24+
25+
func TestTimeDurationUnits(t *testing.T) {
26+
timeDurationUnits := map[string]time.Duration{
27+
"ns": time.Nanosecond,
28+
"us": time.Microsecond,
29+
"µs": time.Microsecond,
30+
"ms": time.Millisecond,
31+
"s": time.Second,
32+
"m": time.Minute,
33+
"h": time.Hour,
34+
"d": 24 * time.Hour,
35+
}
36+
pkg := NewPackage("", "foo", nil)
37+
ret, ok := pkg.buildTypeUnits(objectID{"time", "Duration"}, token.INT)
38+
if !ok || len(ret) != len(timeDurationUnits) {
39+
t.Fatal("TestTimeDurationUnits: failed")
40+
}
41+
for k, vex := range timeDurationUnits {
42+
if v := ret[k]; v != constant.MakeInt64(int64(vex)) {
43+
t.Fatal("TestTimeDurationUnits: failed:", k, v)
44+
}
45+
}
46+
}
47+
48+
func TestUserDefinedTypeUnits(t *testing.T) {
49+
pkg := NewPackage("", "foo", nil)
50+
u := pkg.Import("github.com/goplus/gogen/internal/unit")
51+
ut := u.Ref("Distance").Type()
52+
ut2 := u.Ref("NoUnit").Type()
53+
cb := pkg.CB()
54+
cb.ValWithUnit(&ast.BasicLit{Value: "1", Kind: token.INT}, ut, "m")
55+
testValWithUnitPanic(t, "no unit for unit.NoUnit", cb, ut2, "m")
56+
}
57+
58+
func TestValWithUnit(t *testing.T) {
59+
pkg := NewPackage("", "foo", nil)
60+
cb := pkg.CB()
61+
testValWithUnitPanic(t, "no unit for int", cb, types.Typ[types.Int], "m")
62+
testValWithUnitPanic(t, "y is not unit of time.Duration", cb, namedType("time", "Duration"), "y")
63+
testValWithUnitPanic(t, "user defined type: not impl", cb, namedType("foo", "Bar"), "m")
64+
cb.ValWithUnit(&ast.BasicLit{Value: "1", Kind: token.INT}, namedType("time", "Duration"), "m")
65+
}
66+
67+
func namedType(pkgName, tName string) types.Type {
68+
pkg := types.NewPackage(pkgName, "")
69+
return types.NewNamed(types.NewTypeName(0, pkg, tName, nil), types.Typ[types.Int64], nil)
70+
}
71+
72+
func testValWithUnitPanic(t *testing.T, name string, cb *CodeBuilder, typ types.Type, unit string) {
73+
t.Helper()
74+
t.Run(name, func(t *testing.T) {
75+
defer func() {
76+
if e := recover(); e == nil {
77+
t.Fatal("TestErrValWithUnit: no panic?")
78+
}
79+
}()
80+
cb.ValWithUnit(&ast.BasicLit{Value: "1", Kind: token.INT}, typ, unit)
81+
})
82+
}

import.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -585,15 +585,14 @@ func (p *Package) big() PkgRef {
585585
type null struct{}
586586
type autoNames struct {
587587
names map[string]null
588-
reqIdx int
589588
autoIdx int
590589
}
591590

592591
const (
593592
goxAutoPrefix = "_autoGo_"
594593
)
595594

596-
func (p *autoNames) initAutoNames() {
595+
func (p *autoNames) init() {
597596
p.names = make(map[string]null)
598597
}
599598

@@ -611,14 +610,14 @@ func (p *autoNames) hasName(name string) bool {
611610
return ok
612611
}
613612

614-
func (p *autoNames) requireName(name string) (ret string, renamed bool) {
613+
func (p *autoNames) importName(name string) (ret string, renamed bool) {
615614
ret = name
615+
var idx int
616616
for p.hasName(ret) {
617-
p.reqIdx++
618-
ret = name + strconv.Itoa(p.reqIdx)
617+
idx++
618+
ret = name + strconv.Itoa(idx)
619619
renamed = true
620620
}
621-
p.useName(ret)
622621
return
623622
}
624623

internal/go/printer/printer.go

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment
359359
return
360360
}
361361

362-
if pos.Line == p.last.Line && (prev == nil || prev.Text[1] != '/') {
362+
if pos.Line == p.last.Line && (prev == nil || len(prev.Text) > 1 && prev.Text[1] != '/') {
363363
// comment on the same line as last item:
364364
// separate with at least one separator
365365
hasSep := false
@@ -457,7 +457,7 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment
457457

458458
// make sure there is at least one line break
459459
// if the previous comment was a line comment
460-
if n == 0 && prev != nil && prev.Text[1] == '/' {
460+
if n == 0 && prev != nil && len(prev.Text) > 1 && prev.Text[1] == '/' {
461461
n = 1
462462
}
463463

@@ -567,7 +567,7 @@ func stripCommonPrefix(lines []string) {
567567
// for the opening /*, assume up to 3 blanks or a tab. This
568568
// whitespace may be found as suffix in the common prefix.
569569
first := lines[0]
570-
if isBlank(first[2:]) {
570+
if len(first) > 2 && isBlank(first[2:]) {
571571
// no comment text on the first line:
572572
// reduce prefix by up to 3 blanks or a tab
573573
// if present - this keeps comment text indented
@@ -594,8 +594,12 @@ func stripCommonPrefix(lines []string) {
594594
suffix = suffix[2:n]
595595
} else {
596596
// otherwise assume two blanks
597-
suffix[0], suffix[1] = ' ', ' '
598-
suffix = suffix[0:n]
597+
if len(suffix) > 1 {
598+
suffix[0], suffix[1] = ' ', ' '
599+
}
600+
if len(suffix) > n {
601+
suffix = suffix[0:n]
602+
}
599603
}
600604
// Shorten the computed common prefix by the length of
601605
// suffix, if it is found as suffix of the prefix.
@@ -643,7 +647,7 @@ func (p *printer) writeComment(comment *ast.Comment) {
643647
}
644648

645649
// shortcut common case of //-style comments
646-
if text[1] == '/' {
650+
if len(text) > 1 && text[1] == '/' {
647651
p.writeString(pos, trimRight(text), true)
648652
return
649653
}
@@ -756,7 +760,7 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (wro
756760
// to track whether we're inside an expression or statement and
757761
// use that information to decide more directly.
758762
needsLinebreak := false
759-
if p.mode&noExtraBlank == 0 &&
763+
if p.mode&noExtraBlank == 0 && len(last.Text) > 1 &&
760764
last.Text[1] == '*' {
761765
if line := p.lineFor(last.Pos()); (line == 0 || line == next.Line) &&
762766
tok != token.COMMA &&
@@ -771,7 +775,7 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (wro
771775
}
772776
// Ensure that there is a line break after a //-style comment,
773777
// before EOF, and before a closing '}' unless explicitly disabled.
774-
if last.Text[1] == '/' ||
778+
if len(last.Text) > 1 && last.Text[1] == '/' ||
775779
tok == token.EOF ||
776780
tok == token.RBRACE && p.mode&noExtraLinebreak == 0 {
777781
needsLinebreak = true

internal/unit/unit.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
Copyright 2024 The GoPlus Authors (goplus.org)
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package unit
15+
16+
// -----------------------------------------------------------------------------
17+
18+
type NoUnit int
19+
20+
type Distance int
21+
22+
const Gopu_Distance = "mm=1,cm=10,dm=100,m=1000"
23+
24+
// -----------------------------------------------------------------------------

package.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ func (p astVisitor) Visit(node ast.Node) (w ast.Visitor) {
192192
if id, ok := x.(*ast.Ident); ok && id.Obj != nil {
193193
if used, ok := id.Obj.Data.(importUsed); ok && bool(!used) {
194194
id.Obj.Data = importUsed(true)
195-
if name, renamed := p.this.requireName(id.Name); renamed {
195+
if name, renamed := p.this.importName(id.Name); renamed {
196196
id.Name = name
197197
id.Obj.Name = name
198198
}
@@ -317,6 +317,7 @@ type Package struct {
317317
Docs ObjectDocs
318318
Fset *token.FileSet
319319

320+
unitMgr
320321
autoNames
321322
cb CodeBuilder
322323
imp types.Importer
@@ -367,7 +368,8 @@ func NewPackage(pkgPath, name string, conf *Config) *Package {
367368
files: files,
368369
conf: conf,
369370
}
370-
pkg.initAutoNames()
371+
pkg.unitMgr.init()
372+
pkg.autoNames.init()
371373
pkg.imp = imp
372374
pkg.Types = conf.Types
373375
if pkg.Types == nil {

0 commit comments

Comments
 (0)