pysig.Parse; py/pandas

This commit is contained in:
xushiwei
2024-05-17 05:25:13 +08:00
parent fcc0e1776b
commit c1927c985a
4 changed files with 7520 additions and 27 deletions

View File

@@ -29,6 +29,7 @@ import (
"strings"
"github.com/goplus/gogen"
"github.com/goplus/llgo/chore/llpyg/pysig"
"github.com/goplus/llgo/ssa"
)
@@ -109,12 +110,12 @@ func (ctx *context) genFunc(pkg *gogen.Package, sym *symbol) {
if len(name) == 0 || name[0] == '_' {
return
}
params, variadic, skip := ctx.genParams(pkg, symSig)
if skip {
if symSig == "<NULL>" {
// TODO(xsw): don't skip any func
log.Println("skip func:", name, symSig)
return
}
params, variadic := ctx.genParams(pkg, symSig)
name = genName(name, -1)
sig := types.NewSignatureType(nil, nil, nil, params, ctx.ret, variadic)
fn := pkg.NewFuncDecl(token.NoPos, name, sig)
@@ -125,43 +126,32 @@ func (ctx *context) genFunc(pkg *gogen.Package, sym *symbol) {
// fn.BodyStart(pkg).End()
}
func (ctx *context) genParams(pkg *gogen.Package, sig string) (*types.Tuple, bool, bool) {
if sig == "<NULL>" {
return nil, false, true
func (ctx *context) genParams(pkg *gogen.Package, sig string) (*types.Tuple, bool) {
args := pysig.Parse(sig)
if len(args) == 0 {
return nil, false
}
sig = strings.TrimSuffix(strings.TrimPrefix(sig, "("), ")")
if sig == "" { // empty params
return nil, false, false
}
parts := strings.Split(sig, ",")
n := len(parts)
n := len(args)
objPtr := ctx.objPtr
list := make([]*types.Var, 0, n)
for i := 0; i < n; i++ {
part := strings.TrimSpace(parts[i])
if part == "/" {
name := args[i].Name
if name == "/" {
continue
}
if part == "*" {
if name == "*" {
break
}
if strings.HasPrefix(part, "*") {
if part[1] != '*' {
if strings.HasPrefix(name, "*") {
if name[1] != '*' {
list = append(list, ssa.VArg())
return types.NewTuple(list...), true, false
return types.NewTuple(list...), true
}
return types.NewTuple(list...), false, false
return types.NewTuple(list...), false
}
pos := strings.IndexByte(part, '=')
if pos >= 0 {
if strings.HasPrefix(part[pos+1:], "<") { // skip complex default value
return nil, false, true
}
part = part[:pos]
}
list = append(list, pkg.NewParam(0, genName(part, 0), objPtr))
list = append(list, pkg.NewParam(0, genName(name, 0), objPtr))
}
return types.NewTuple(list...), false, false
return types.NewTuple(list...), false
}
func genName(name string, idxDontTitle int) string {

100
chore/llpyg/pysig/parse.go Normal file
View File

@@ -0,0 +1,100 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package pysig
import (
"strings"
)
type Arg struct {
Name string
Type string
DefVal string
}
// Parse parses a Python function signature.
func Parse(sig string) (args []*Arg) {
sig = strings.TrimPrefix(sig, "(")
for {
pos := strings.IndexAny(sig, ",:=)")
if pos <= 0 {
return
}
arg := &Arg{Name: strings.TrimSpace(sig[:pos])}
args = append(args, arg)
c := sig[pos]
sig = sig[pos+1:]
switch c {
case ',':
continue
case ':':
arg.Type, sig = parseType(sig)
if strings.HasPrefix(sig, "=") {
arg.DefVal, sig = parseDefVal(sig[1:])
}
case '=':
arg.DefVal, sig = parseDefVal(sig)
case ')':
return
}
sig = strings.TrimPrefix(sig, ",")
}
}
const (
allSpecials = "([<'\""
)
var pairStops = map[byte]string{
'(': ")" + allSpecials,
'[': "]" + allSpecials,
'<': ">" + allSpecials,
'\'': "'" + allSpecials,
'"': "\"",
}
func parseText(sig string, stops string) (left string) {
for {
pos := strings.IndexAny(sig, stops)
if pos < 0 {
return sig
}
if c := sig[pos]; c != stops[0] {
if pstop, ok := pairStops[c]; ok {
sig = strings.TrimPrefix(parseText(sig[pos+1:], pstop), pstop[:1])
continue
}
}
return sig[pos:]
}
}
// stops: "=,)"
func parseType(sig string) (string, string) {
left := parseText(sig, "=,)"+allSpecials)
return resultOf(sig, left), left
}
// stops: ",)"
func parseDefVal(sig string) (string, string) {
left := parseText(sig, ",)"+allSpecials)
return resultOf(sig, left), left
}
func resultOf(sig, left string) string {
return strings.TrimSpace(sig[:len(sig)-len(left)])
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package pysig
import "testing"
func TestParse(t *testing.T) {
type testCase struct {
sig string
args []*Arg
}
cases := []testCase{
{"(start=None, *, unit: 'str | None' = None) -> 'TimedeltaIndex'", []*Arg{
{Name: "start", DefVal: "None"},
{Name: "*"},
{Name: "unit", Type: "'str | None'", DefVal: "None"},
}},
{"()", nil},
{"(a =", []*Arg{{Name: "a"}}},
{"(a) -> int", []*Arg{{Name: "a"}}},
{"(a: int)", []*Arg{{Name: "a", Type: "int"}}},
{"(a: int = 1, b: float)", []*Arg{{Name: "a", Type: "int", DefVal: "1"}, {Name: "b", Type: "float"}}},
{"(a = <1>, b = 2.0)", []*Arg{{Name: "a", DefVal: "<1>"}, {Name: "b", DefVal: "2.0"}}},
{"(a: 'Suffixes' = ('_x', '_y'))", []*Arg{{Name: "a", Type: "'Suffixes'", DefVal: "('_x', '_y')"}}},
}
for _, c := range cases {
args := Parse(c.sig)
if len(args) != len(c.args) {
t.Fatalf("%s: len(args) = %v, want %v", c.sig, len(args), len(c.args))
}
for i, arg := range args {
want := c.args[i]
if arg.Name != want.Name || arg.Type != want.Type || arg.DefVal != want.DefVal {
t.Fatalf("%s: args[%v] = %v, want %v", c.sig, i, arg, want)
}
}
}
}