Skip to content

Commit

Permalink
Merge pull request #1816 from goplus/main
Browse files Browse the repository at this point in the history
v1.2.5
  • Loading branch information
xushiwei authored Mar 10, 2024
2 parents 5a40ea9 + 23e5959 commit 4ffe80c
Show file tree
Hide file tree
Showing 22 changed files with 748 additions and 349 deletions.
58 changes: 37 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,44 +92,45 @@ Sound a bit abstract? Let's see some Go+ classfiles.
* STEM Education: [spx: A Go+ 2D Game Engine](https://github.com/goplus/spx)


### gsh: Go+ DevOps Tools
### yap: Yet Another HTTP Web Framework

Yes, now you can write `shell script` in Go+. It supports all shell commands.
This classfile has the file suffix `.yap`.

Let's create a file named `./example.gsh` and write the following code:
Create a file named [get.yap](https://github.com/goplus/yap/blob/main/demo/classfile2_hello/get.yap) with the following content:

```coffee
mkdir "testgsh"
```go
html `<html><body>Hello, YAP!</body></html>`
```

Don't need a `go.mod` file, just enter `gop run ./example.gsh` directly to run.
Execute the following commands:

It's strange to you that the file extension of Go+ source is not `.gop` but `.gsh`. It is only because Go+ register `.gsh` as a builtin [classfile](doc/classfile.md).
```sh
gop mod init hello
gop get github.com/goplus/yap@latest
gop mod tidy
gop run .
```

See [gsh: Go+ DevOps Tools](https://github.com/qiniu/x/tree/main/gsh) for more details.
A simplest web program is running now. At this time, if you visit http://localhost:8080, you will get:

```
Hello, YAP!
```

### yap: Yet Another HTTP Web Framework
YAP uses filenames to define routes. `get.yap`'s route is `get "/"` (GET homepage), and `get_p_#id.yap`'s route is `get "/p/:id"` (In fact, the filename can also be `get_p_:id.yap`, but it is not recommended because `:` is not allowed to exist in filenames under Windows).

Demo of serving static files and ability to handle dynamic GET/POST requests:
Let's create a file named [get_p_#id.yap](https://github.com/goplus/yap/blob/main/demo/classfile2_hello/get_p_%23id.yap) with the following content:

```coffee
static "/foo", FS("public")
static "/" # Equivalent to static "/", FS("static")

get "/p/:id", ctx => {
ctx.json {
"id": ctx.param("id"),
}
json {
"id": ${id},
}

run ":8080"
```

If you run it and visit http://localhost:8080/p/123, you will get:
Execute `gop run .` and visit http://localhost:8080/p/123, you will get:

```
{"id":"123"}
{"id": "123"}
```

See [yap: Yet Another HTTP Web Framework](https://github.com/goplus/yap) for more details.
Expand Down Expand Up @@ -173,6 +174,21 @@ The following procedures are very similar. In this way you can implement dialogu
See [spx: A Go+ 2D Game Engine](https://github.com/goplus/spx) for more details.


### gsh: Go+ DevOps Tools

Yes, now you can write `shell script` in Go+. It supports all shell commands.

Let's create a file named [example.gsh](https://github.com/qiniu/x/blob/main/gsh/demo/hello/example.gsh) and write the following code:

```coffee
mkdir "testgsh"
```

Don't need a `go.mod` file, just enter `gop run ./example.gsh` directly to run.

See [gsh: Go+ DevOps Tools](https://github.com/qiniu/x/tree/main/gsh) for more details.


## Key Features of Go+

* A static typed language.
Expand Down
30 changes: 30 additions & 0 deletions ast/ast_gop.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,36 @@ func NextPartPos(pos token.Pos, part any) (nextPos token.Pos) {

// -----------------------------------------------------------------------------

// A EnvExpr node represents a ${name} expression.
type EnvExpr struct {
TokPos token.Pos // position of "$"
Lbrace token.Pos // position of "{"
Name *Ident // name
Rbrace token.Pos // position of "}"
}

// Pos - position of first character belonging to the node.
func (p *EnvExpr) Pos() token.Pos {
return p.TokPos
}

// End - position of first character immediately after the node.
func (p *EnvExpr) End() token.Pos {
if p.Rbrace != token.NoPos {
return p.Rbrace
}
return p.Name.End()
}

// HasBrace checks is this EnvExpr ${name} or $name.
func (p *EnvExpr) HasBrace() bool {
return p.Rbrace != token.NoPos
}

func (*EnvExpr) exprNode() {}

// -----------------------------------------------------------------------------

// A SliceLit node represents a slice literal.
type SliceLit struct {
Lbrack token.Pos // position of "["
Expand Down
3 changes: 3 additions & 0 deletions ast/walk.go
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,9 @@ func Walk(v Visitor, node Node) {
Walk(v, n.Name)
walkExprList(v, n.Funcs)

case *EnvExpr:
Walk(v, n.Name)

default:
panic(fmt.Sprintf("ast.Walk: unexpected node type %T", n))
}
Expand Down
9 changes: 6 additions & 3 deletions cl/builtin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,12 +252,15 @@ func TestErrParseTypeEmbedName(t *testing.T) {
parseTypeEmbedName(&ast.StructType{})
}

func TestGmxMainFunc(t *testing.T) {
gmxMainFunc(nil, &pkgCtx{
func TestGmxCheckProjs(t *testing.T) {
_, multi := gmxCheckProjs(nil, &pkgCtx{
projs: map[string]*gmxProject{
".a": {}, ".b": {},
},
}, false)
})
if !multi {
t.Fatal("gmxCheckProjs: not multi?")
}
}

func TestNodeInterp(t *testing.T) {
Expand Down
Loading

0 comments on commit 4ffe80c

Please sign in to comment.