Skip to content

Commit

Permalink
Merge pull request #1978 from goplus/main
Browse files Browse the repository at this point in the history
v1.3.0-pre.2
  • Loading branch information
xushiwei authored Sep 6, 2024
2 parents 43ecd08 + 5f10778 commit 2c4964c
Show file tree
Hide file tree
Showing 13 changed files with 2,056 additions and 42 deletions.
27 changes: 21 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

</div>

Our vision is to **enable everyone to create production-level applications**.
Our vision is to **enable everyone to become a builder of the digital world**.

#### Easy to learn

Expand All @@ -46,13 +46,14 @@ For more details, see [Quick Start](doc/docs.md).

## Key Features of Go+

* Approaching natural language expression and intuitive (see [Command Style Code](#command-style-code)).
* Fully compatible with [Go](https://github.com/golang/go) and can mix Go/Go+ code in the same package (see [Go/Go+ Hybrid Programming](doc/docs.md#gogo-hybrid-programming)).
* Integrating with the C ecosystem including Python and providing limitless possibilities (see [Support for C/C++ and Python](#support-for-cc-and-python)).
* Approaching natural language expression and intuitive (see [How Go+ simplifies Go's expressions](#how-go-simplifies-gos-expressions)).
* Smallest but Turing-complete syntax set in best practices (see [Go+ Specification for STEM Education](doc/spec-mini.md)).
* Fully compatible with [Go](https://github.com/golang/go) and can mix Go/Go+ code in the same package (see [Go+ Full Specification](doc/spec.md) and [Go/Go+ Hybrid Programming](doc/docs.md#gogo-hybrid-programming)).
* Integrating with the C ecosystem including Python and providing limitless possibilities based on [LLGo](https://github.com/goplus/llgo) (see [Support for C/C++ and Python](#support-for-cc-and-python)).
* Does not support DSL (Domain-Specific Languages), but supports SDF (Specific Domain Friendliness) (see [Go+ Classfiles](#go-classfiles)).


## Command Style Code
## How Go+ simplifies Go's expressions

Different from the function call style of most languages, Go+ recommends command style code:

Expand All @@ -68,6 +69,20 @@ echo "Hello world"

For more discussion on coding style, see https://tutorial.goplus.org/hello-world.

Code style is just the first step. We have made many efforts to make the code more intuitive and closer to natural language expression. These include:

| Go code | Go+ code | Note |
| ---- | ---- | ---- |
| package main<br><br>import "fmt"<br><br>func main() {<br>&nbsp;&nbsp;&nbsp;&nbsp;fmt.Println("Hi")<br>} | import "fmt"<br><br>fmt.Println("Hi")<br> | Program structure: Go+ allows omitting `package main` and `func main` |
| fmt.Println("Hi") | echo("Hi") | More builtin functions: It simplifies the expression of the most common tasks |
| fmt.Println("Hi") | echo "Hi" | Command-line style code: It reduces the number of parentheses in the code as much as possible, making it closer to natural language |
| a := []int{1, 2, 3} | a := [1, 2, 3] | List literals |
| a := map[string]int{<br>&nbsp;&nbsp;&nbsp;&nbsp;"Monday": 1,<br>&nbsp;&nbsp;&nbsp;&nbsp;"Tuesday": 2,<br>} | a := {<br>&nbsp;&nbsp;&nbsp;&nbsp;"Monday": 1,<br>&nbsp;&nbsp;&nbsp;&nbsp;"Tuesday": 2,<br>} | Mapping literals |
| OnStart(func() {<br>&nbsp;&nbsp;&nbsp;&nbsp;...<br>}) | onStart => {<br>&nbsp;&nbsp;&nbsp;&nbsp;...<br>} | Lambda expressions |
| type Rect struct {<br>&nbsp;&nbsp;&nbsp;&nbsp;Width&nbsp; float64<br>&nbsp;&nbsp;&nbsp;&nbsp;Height float64<br>}<br><br>func (this *Rect) Area() float64 { <br>&nbsp;&nbsp;&nbsp;&nbsp;return this.Width * this.Height<br>} | var (<br>&nbsp;&nbsp;&nbsp;&nbsp;Width&nbsp; float64<br>&nbsp;&nbsp;&nbsp;&nbsp;Height float64<br>)<br><br>func Area() float64 { <br>&nbsp;&nbsp;&nbsp;&nbsp;return Width * Height<br>} | [Go+ Classfiles](doc/classfile.md): We can express OOP with global variables and functions. |

For more details, see [Go+ Specification for STEM Education](doc/spec-mini.md).


## Support for C/C++ and Python

Expand Down Expand Up @@ -100,7 +115,7 @@ module YourModulePath

go 1.21 // llgo 1.0

require github.com/goplus/llgo v0.9.1
require github.com/goplus/llgo v0.9.7
```

Based on LLGo, Go+ can support importing libraries written in C/C++ and Python.
Expand Down
129 changes: 129 additions & 0 deletions cl/compile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4122,3 +4122,132 @@ func _() {
}
`)
}

func TestSliceLitAssign(t *testing.T) {
gopClTest(t, `
var n = 1
var a []any = [10, 3.14, 200]
n, a = 100, [10, 3.14, 200]
echo a, n
`, `package main
import "fmt"
var n = 1
var a []interface{} = []interface{}{10, 3.14, 200}
func main() {
n, a = 100, []interface{}{10, 3.14, 200}
fmt.Println(a, n)
}
`)
}

func TestSliceLitReturn(t *testing.T) {
gopClTest(t, `
func anyslice() (int, []any) {
return 100, [10, 3.14, 200]
}
n, a := anyslice()
echo n, a
`, `package main
import "fmt"
func anyslice() (int, []interface{}) {
return 100, []interface{}{10, 3.14, 200}
}
func main() {
n, a := anyslice()
fmt.Println(n, a)
}
`)
}

func TestCompositeLitAssign(t *testing.T) {
gopClTest(t, `
var a map[any]any = {10: "A", 3.14: "B", 200: "C"}
var b map[any]string = {10: "A", 3.14: "B", 200: "C"}
echo a
echo b
var n int
n, a = 1, {10: "A", 3.14: "B", 200: "C"}
echo a, n
n, b = 1, {10: "A", 3.14: "B", 200: "C"}
echo b, n
`, `package main
import "fmt"
var a map[interface{}]interface{} = map[interface{}]interface{}{10: "A", 3.14: "B", 200: "C"}
var b map[interface{}]string = map[interface{}]string{10: "A", 3.14: "B", 200: "C"}
func main() {
fmt.Println(a)
fmt.Println(b)
var n int
n, a = 1, map[interface{}]interface{}{10: "A", 3.14: "B", 200: "C"}
fmt.Println(a, n)
n, b = 1, map[interface{}]string{10: "A", 3.14: "B", 200: "C"}
fmt.Println(b, n)
}
`)
}

func TestCompositeLitStruct(t *testing.T) {
gopClTest(t, `
type T struct {
s []any
m map[any]any
fn func(int) int
}
echo &T{[10, 3.14, 200], {10: "A", 3.14: "B", 200: "C"}, (x => x)}
echo &T{s: [10, 3.14, 200], m: {10: "A", 3.14: "B", 200: "C"}, fn: (x => x)}
`, `package main
import "fmt"
type T struct {
s []interface{}
m map[interface{}]interface{}
fn func(int) int
}
func main() {
fmt.Println(&T{[]interface{}{10, 3.14, 200}, map[interface{}]interface{}{10: "A", 3.14: "B", 200: "C"}, func(x int) int {
return x
}})
fmt.Println(&T{s: []interface{}{10, 3.14, 200}, m: map[interface{}]interface{}{10: "A", 3.14: "B", 200: "C"}, fn: func(x int) int {
return x
}})
}
`)
}

func TestCompositeLitEx(t *testing.T) {
gopClTest(t, `
var a [][]any = {[10, 3.14, 200], [100, 200]}
var m map[any][]any = {10: [10, 3.14, 200]}
var f map[any]func(int) int = {10: x => x}
echo a
echo m
echo f
`, `package main
import "fmt"
var a [][]interface{} = [][]interface{}{[]interface{}{10, 3.14, 200}, []interface{}{100, 200}}
var m map[interface{}][]interface{} = map[interface{}][]interface{}{10: []interface{}{10, 3.14, 200}}
var f map[interface{}]func(int) int = map[interface{}]func(int) int{10: func(x int) int {
return x
}}
func main() {
fmt.Println(a)
fmt.Println(m)
fmt.Println(f)
}
`)
}
27 changes: 27 additions & 0 deletions cl/error_msg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1071,3 +1071,30 @@ func (foo).++ = (
)
`)
}

func TestCompositeLitError(t *testing.T) {
codeErrorTest(t, `bar.gop:2:22: cannot use 3.14 (type untyped float) as type int in slice literal`, `
var a [][]int = {[10,3.14,200],[100,200]}
echo a
`)
codeErrorTest(t, `bar.gop:2:17: cannot use lambda literal as type int in assignment`, `
var a []int = {(x => x)}
echo a
`)
codeErrorTest(t, `bar.gop:2:35: cannot use x (type int) as type string in return argument`, `
var a []func(int) string = {(x => x)}
echo a
`)
codeErrorTest(t, `bar.gop:2:27: cannot use lambda literal as type int in assignment to "A"`, `
var a map[any]int = {"A": x => x}
`)
codeErrorTest(t, `bar.gop:2:45: cannot use x (type int) as type string in return argument`, `
var a map[any]func(int) string = {"A": x => x}
`)
codeErrorTest(t, `bar.gop:2:24: cannot use lambda literal as type int in field value`, `
var a = struct{v int}{(x => x)}
`)
codeErrorTest(t, `bar.gop:2:27: cannot use lambda literal as type int in field value to v`, `
var a = struct{v int}{v: (x => x)}
`)
}
92 changes: 67 additions & 25 deletions cl/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -853,8 +853,11 @@ retry:
typ = t.Underlying()
goto retry
}
src := ctx.LoadExpr(toNode)
return nil, ctx.newCodeErrorf(lambda.Pos(), "cannot use lambda literal as type %v in %v to %v", ftyp, flag, src)
var to string
if toNode != nil {
to = " to " + ctx.LoadExpr(toNode)
}
return nil, ctx.newCodeErrorf(lambda.Pos(), "cannot use lambda literal as type %v in %v%v", ftyp, flag, to)
}

func compileLambda(ctx *blockCtx, lambda ast.Expr, sig *types.Signature) {
Expand Down Expand Up @@ -1065,30 +1068,68 @@ func checkCompositeLitElts(elts []ast.Expr) (kind int) {
return compositeLitVal
}

func compileCompositeLitElts(ctx *blockCtx, elts []ast.Expr, kind int, expected *kvType) {
func compileCompositeLitElts(ctx *blockCtx, elts []ast.Expr, kind int, expected *kvType) error {
for _, elt := range elts {
if kv, ok := elt.(*ast.KeyValueExpr); ok {
if key, ok := kv.Key.(*ast.CompositeLit); ok && key.Type == nil {
compileCompositeLit(ctx, key, expected.Key(), false)
} else {
compileExpr(ctx, kv.Key)
}
if val, ok := kv.Value.(*ast.CompositeLit); ok && val.Type == nil {
compileCompositeLit(ctx, val, expected.Elem(), false)
} else {
compileExpr(ctx, kv.Value)
err := compileCompositeLitElt(ctx, kv.Value, expected.Elem(), clLambaAssign, kv.Key)
if err != nil {
return err
}
} else {
if kind == compositeLitKeyVal {
ctx.cb.None()
}
if val, ok := elt.(*ast.CompositeLit); ok && val.Type == nil {
compileCompositeLit(ctx, val, expected.Elem(), false)
} else {
compileExpr(ctx, elt)
err := compileCompositeLitElt(ctx, elt, expected.Elem(), clLambaAssign, nil)
if err != nil {
return err
}
}
}
return nil
}

func compileCompositeLitElt(ctx *blockCtx, e ast.Expr, typ types.Type, flag clLambaFlag, toNode ast.Node) error {
switch v := unparen(e).(type) {
case *ast.LambdaExpr, *ast.LambdaExpr2:
sig, err := checkLambdaFuncType(ctx, v, typ, flag, toNode)
if err != nil {
return err
}
compileLambda(ctx, v, sig)
case *ast.SliceLit:
compileSliceLit(ctx, v, typ)
case *ast.CompositeLit:
compileCompositeLit(ctx, v, typ, false)
default:
compileExpr(ctx, v)
}
return nil
}

func unparen(x ast.Expr) ast.Expr {
if e, ok := x.(*ast.ParenExpr); ok {
return e.X
}
return x
}

func compileStructLit(ctx *blockCtx, elts []ast.Expr, t *types.Struct, typ types.Type, src *ast.CompositeLit) error {
for idx, elt := range elts {
if idx >= t.NumFields() {
return ctx.newCodeErrorf(elt.Pos(), "too many values in %v{...}", typ)
}
err := compileCompositeLitElt(ctx, elt, t.Field(idx).Type(), clLambaField, nil)
if err != nil {
return err
}
}
ctx.cb.StructLit(typ, len(elts), false, src)
return nil
}

func compileStructLitInKeyVal(ctx *blockCtx, elts []ast.Expr, t *types.Struct, typ types.Type, src *ast.CompositeLit) error {
Expand All @@ -1105,15 +1146,9 @@ func compileStructLitInKeyVal(ctx *blockCtx, elts []ast.Expr, t *types.Struct, t
if rec := ctx.recorder(); rec != nil {
rec.Use(name, t.Field(idx))
}
switch expr := kv.Value.(type) {
case *ast.LambdaExpr, *ast.LambdaExpr2:
sig, err := checkLambdaFuncType(ctx, expr, t.Field(idx).Type(), clLambaField, kv.Key)
if err != nil {
return err
}
compileLambda(ctx, expr, sig)
default:
compileExpr(ctx, kv.Value)
err := compileCompositeLitElt(ctx, kv.Value, t.Field(idx).Type(), clLambaField, kv.Key)
if err != nil {
return err
}
}
ctx.cb.StructLit(typ, len(elts)<<1, true, src)
Expand Down Expand Up @@ -1194,12 +1229,21 @@ func compileCompositeLitEx(ctx *blockCtx, v *ast.CompositeLit, expected types.Ty
typ, underlying = expected, tu
}
}
if t, ok := underlying.(*types.Struct); ok && kind == compositeLitKeyVal {
if err := compileStructLitInKeyVal(ctx, v.Elts, t, typ, v); err != nil {
if t, ok := underlying.(*types.Struct); ok {
var err error
if kind == compositeLitKeyVal {
err = compileStructLitInKeyVal(ctx, v.Elts, t, typ, v)
} else {
err = compileStructLit(ctx, v.Elts, t, typ, v)
}
if err != nil {
return err
}
} else {
compileCompositeLitElts(ctx, v.Elts, kind, &kvType{underlying: underlying})
err := compileCompositeLitElts(ctx, v.Elts, kind, &kvType{underlying: underlying})
if err != nil {
return err
}
n := len(v.Elts)
if isMap(underlying) {
if kind == compositeLitVal && n > 0 {
Expand All @@ -1214,8 +1258,6 @@ func compileCompositeLitEx(ctx *blockCtx, v *ast.CompositeLit, expected types.Ty
ctx.cb.SliceLitEx(typ, n<<kind, kind == compositeLitKeyVal, v)
case *types.Array:
ctx.cb.ArrayLitEx(typ, n<<kind, kind == compositeLitKeyVal, v)
case *types.Struct:
ctx.cb.StructLit(typ, n, false, v) // key-val mode handled by compileStructLitInKeyVal
default:
return ctx.newCodeErrorf(v.Pos(), "invalid composite literal type %v", typ)
}
Expand Down
Loading

0 comments on commit 2c4964c

Please sign in to comment.