chore: clangpp, clangast

This commit is contained in:
xushiwei
2024-05-06 08:11:54 +08:00
parent df333fb144
commit 7223ff004a
14 changed files with 1986 additions and 2 deletions

View File

@@ -0,0 +1,251 @@
/*
* Copyright (c) 2022 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 parser
import (
"go/token"
"go/types"
"testing"
ctypes "github.com/goplus/llgo/x/clang/types"
)
// -----------------------------------------------------------------------------
var (
pkg = types.NewPackage("", "foo")
scope = pkg.Scope()
)
var (
nameInt128 = types.NewTypeName(token.NoPos, pkg, "__int128", nil)
nameUint128 = types.NewTypeName(token.NoPos, pkg, "__uint128", nil)
tyInt128 = types.NewNamed(nameInt128, types.Typ[types.String], nil)
tyUint128 = types.NewNamed(nameUint128, types.Typ[types.Rune], nil)
)
func init() {
aliasType(scope, pkg, "char", types.Typ[types.Int8])
aliasType(scope, pkg, "void", ctypes.Void)
aliasType(scope, pkg, "float", types.Typ[types.Float32])
aliasType(scope, pkg, "double", types.Typ[types.Float64])
aliasType(scope, pkg, "uint", types.Typ[types.Uint32])
aliasType(scope, pkg, ctypes.MangledName("struct", "ConstantString"), tyConstantString)
aliasType(scope, pkg, ctypes.MangledName("union", "arg"), tyArg)
aliasType(scope, pkg, "va_list", ctypes.Valist)
scope.Insert(nameInt128)
}
func aliasType(scope *types.Scope, pkg *types.Package, name string, typ types.Type) {
o := types.NewTypeName(token.NoPos, pkg, name, typ)
scope.Insert(o)
}
var (
tnameConstantString = types.NewTypeName(token.NoPos, pkg, "ConstantString", nil)
tnameArg = types.NewTypeName(token.NoPos, pkg, "UnionArg", nil)
)
var (
tyChar = types.Typ[types.Int8]
tyUchar = types.Typ[types.Uint8]
tyInt16 = types.Typ[types.Int16]
tyUint16 = types.Typ[types.Uint16]
tyInt32 = types.Typ[types.Int32]
tyUint32 = types.Typ[types.Uint32]
tyInt64 = types.Typ[types.Int64]
tyUint64 = types.Typ[types.Uint64]
tyInt = ctypes.Int
tyInt100 = types.NewArray(tyInt, 100)
tyInt3 = types.NewArray(tyInt, 3)
tyInt3_100 = types.NewArray(tyInt3, 100)
tyPInt3_100 = types.NewPointer(tyInt3_100)
tyPInt100 = types.NewPointer(tyInt100)
tyUint = ctypes.Uint
tyString = types.Typ[types.String]
tyCharPtr = types.NewPointer(tyChar)
tyCharPtrPtr = types.NewPointer(tyCharPtr)
tyConstantString = types.NewNamed(tnameConstantString, tyString, nil)
tyArg = types.NewNamed(tnameArg, tyString, nil)
tyEmptyInterface = types.NewInterfaceType(nil, nil)
)
var (
paramInt = types.NewParam(token.NoPos, pkg, "", tyInt)
paramVoidPtr = types.NewParam(token.NoPos, pkg, "", ctypes.UnsafePointer)
paramCharPtrPtr = types.NewParam(token.NoPos, pkg, "", tyCharPtrPtr)
paramAnySlice = types.NewParam(token.NoPos, pkg, "", types.NewSlice(tyEmptyInterface))
paramPAnySlice = types.NewParam(token.NoPos, pkg, "", types.NewPointer(types.NewSlice(tyEmptyInterface)))
)
var (
typesInt = types.NewTuple(paramInt)
typesIntVA = types.NewTuple(paramInt, paramAnySlice)
typesIntPVA = types.NewTuple(paramInt, paramPAnySlice)
typesVoidPtr = types.NewTuple(paramVoidPtr)
typesPICC = types.NewTuple(paramVoidPtr, paramInt, paramCharPtrPtr, paramCharPtrPtr)
)
func newFn(in, out *types.Tuple) types.Type {
return types.NewSignature(nil, in, out, false)
}
func newFnv(in, out *types.Tuple) types.Type {
return types.NewSignature(nil, in, out, true)
}
func newFnProto(in, out *types.Tuple, variadic bool) types.Type {
return ctypes.NewFunc(in, out, variadic)
}
var (
tyFnHandle = newFn(typesInt, nil)
paramFnHandle = types.NewParam(token.NoPos, pkg, "", tyFnHandle)
typesIF = types.NewTuple(paramInt, paramFnHandle)
typesF = types.NewTuple(paramFnHandle)
)
// -----------------------------------------------------------------------------
type testCase struct {
qualType string
flags int
anonym types.Type
typ types.Type
err string
}
var cases = []testCase{
{qualType: "int", typ: tyInt},
{qualType: "unsigned int", typ: tyUint},
{qualType: "struct ConstantString", typ: tyConstantString},
{qualType: "union arg", typ: tyArg},
{qualType: "volatile signed int", typ: tyInt},
{qualType: "__int128", typ: tyInt128},
{qualType: "signed", typ: tyInt},
{qualType: "signed short", typ: tyInt16},
{qualType: "signed long", typ: ctypes.Long},
{qualType: "unsigned", typ: tyUint},
{qualType: "uint", typ: tyUint32},
{qualType: "unsigned char", typ: tyUchar},
{qualType: "unsigned __int128", typ: tyUint128},
{qualType: "unsigned long", typ: ctypes.Ulong},
{qualType: "unsigned long long", typ: tyUint64},
{qualType: "long double", typ: ctypes.LongDouble},
{qualType: "_Complex float", typ: types.Typ[types.Complex64]},
{qualType: "_Complex double", typ: types.Typ[types.Complex128]},
{qualType: "_Complex long double", typ: types.Typ[types.Complex128]},
{qualType: "int (*)(void)", typ: newFn(nil, typesInt)},
{qualType: "int (void)", typ: newFnProto(nil, typesInt, false)},
{qualType: "void (void) __attribute__((noreturn))", typ: newFnProto(nil, nil, false)},
{qualType: "void (*)(void *)", typ: newFn(typesVoidPtr, nil)},
{qualType: "void (^ _Nonnull)(void)", typ: newFn(nil, nil)},
{qualType: "void (int, ...)", typ: newFnProto(typesIntVA, nil, true)},
{qualType: "void (int, va_list*)", typ: newFn(typesIntPVA, nil)},
{qualType: "va_list *", typ: types.NewPointer(types.NewSlice(tyEmptyInterface))},
{qualType: "int (*)()", typ: newFn(nil, typesInt)},
{qualType: "int (*)(int, ...)", typ: newFnv(typesIntVA, typesInt)},
{qualType: "int (*)(int, struct __va_list_tag*)", typ: newFn(typesIntVA, typesInt)},
{qualType: "int (*volatile)(int, struct __va_list_tag* restrict)", typ: newFn(typesIntVA, typesInt)},
{qualType: "int (const char *, const char *, unsigned int)", flags: FlagGetRetType, typ: tyInt},
{qualType: "const char *restrict", typ: tyCharPtr},
{qualType: "const char [7]", typ: types.NewArray(tyChar, 7)},
{qualType: "const char [7]", flags: FlagIsParam, typ: tyCharPtr},
{qualType: "char []", flags: FlagIsStructField, typ: types.NewArray(tyChar, 0)},
{qualType: "char []", flags: FlagIsExtern, typ: types.NewArray(tyChar, -1)},
{qualType: "char []", flags: 0, err: emsgDefArrWithoutLen},
{qualType: "char []", flags: FlagIsTypedef, typ: types.NewArray(tyChar, -1)},
{qualType: "char []", flags: FlagIsParam, typ: tyCharPtr},
{qualType: "int [100][3]", typ: tyInt3_100},
{qualType: "int (*)[100][3]", typ: tyPInt3_100},
{qualType: "int (*)[100]", typ: tyPInt100},
{qualType: "int (*const [2])(void *)", typ: types.NewArray(newFn(typesVoidPtr, typesInt), 2)},
{qualType: "char *", typ: tyCharPtr},
{qualType: "void", typ: ctypes.Void},
{qualType: "void *", typ: ctypes.UnsafePointer},
{qualType: "int (*_Nullable)(void *, int, char **, char **)", typ: newFn(typesPICC, typesInt)},
{qualType: "void (*(*)(int, void (*)(int)))(int)", typ: newFn(typesIF, typesF)},
{qualType: "void (*(int, void (*)(int)))(int)", typ: newFnProto(typesIF, typesF, false)},
{qualType: "void (*(int, void (*)(int)))(int)", flags: FlagGetRetType, typ: tyFnHandle},
{qualType: "int (*)(void *, int, const char *, void (**)(void *, int, void **), void **)"},
{qualType: "struct (anonymous) [2]", anonym: tyInt, typ: types.NewArray(tyInt, 2)},
{qualType: "enum a", typ: ctypes.Int},
}
type baseEnv struct {
pkg *types.Package
tyInt128 types.Type
tyUint128 types.Type
}
func (p *baseEnv) Pkg() *types.Package {
return p.pkg
}
func (p *baseEnv) Int128() types.Type {
return p.tyInt128
}
func (p *baseEnv) Uint128() types.Type {
return p.tyUint128
}
func TestCases(t *testing.T) {
sel := ""
for _, c := range cases {
if sel != "" && c.qualType != sel {
continue
}
t.Run(c.qualType, func(t *testing.T) {
conf := &Config{
Scope: scope, Flags: c.flags, Anonym: c.anonym,
ParseEnv: &baseEnv{pkg: pkg, tyInt128: tyInt128, tyUint128: tyUint128},
}
typ, _, err := ParseType(c.qualType, conf)
if err != nil {
if errMsgOf(err) != c.err {
t.Fatal("ParseType:", err, ", expected:", c.err)
}
} else if c.typ != nil && !ctypes.Identical(typ, c.typ) {
t.Fatal("ParseType:", typ, ", expected:", c.typ)
}
})
}
}
func errMsgOf(err error) string {
if e, ok := err.(*ParseTypeError); ok {
return e.ErrMsg
}
return err.Error()
}
// -----------------------------------------------------------------------------
func TestIsArrayWithoutLen(t *testing.T) {
_, _, err := ParseType("byte[]", &Config{Scope: types.Universe})
if !IsArrayWithoutLen(err) {
t.Fatal("ParseType:", err)
}
_, _, err = ParseType("byte[]", &Config{Scope: types.Universe, Flags: FlagIsExtern})
if IsArrayWithoutLen(err) {
t.Fatal("ParseType:", err)
}
}
// -----------------------------------------------------------------------------

View File

@@ -0,0 +1,595 @@
/*
* Copyright (c) 2022 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 parser
import (
"errors"
"fmt"
"go/token"
"go/types"
"io"
"log"
"strconv"
"github.com/goplus/gogen"
"github.com/goplus/llgo/x/clang/types/scanner"
ctypes "github.com/goplus/llgo/x/clang/types"
)
const (
emsgDefArrWithoutLen = "define array without length"
)
var (
ErrInvalidType = errors.New("invalid type")
)
type TypeNotFound struct {
Literal string
StructOrUnion bool
}
func (p *TypeNotFound) Error() string {
return fmt.Sprintf("type %s not found", p.Literal)
}
type ParseTypeError struct {
QualType string
ErrMsg string
}
func (p *ParseTypeError) Error() string {
return p.ErrMsg // TODO
}
func IsArrayWithoutLen(err error) bool {
if e, ok := err.(*ParseTypeError); ok {
return e.ErrMsg == emsgDefArrWithoutLen
}
return false
}
// -----------------------------------------------------------------------------
const (
FlagIsParam = 1 << iota
FlagIsStructField
FlagIsExtern
FlagIsTypedef
FlagGetRetType
)
func getRetType(flags int) bool {
return (flags & FlagGetRetType) != 0
}
type ParseEnv interface {
Pkg() *types.Package
Int128() types.Type
Uint128() types.Type
}
type Config struct {
ParseEnv
Scope *types.Scope
Anonym types.Type
Flags int
}
const (
KindFConst = 1 << iota
KindFVolatile
KindFAnonymous
KindFVariadic
)
// qualType can be:
// - unsigned int
// - struct ConstantString
// - volatile uint32_t
// - int (*)(void *, int, char **, char **)
// - int (*)(const char *, ...)
// - int (*)(void)
// - void (*(int, void (*)(int)))(int)
// - const char *restrict
// - const char [7]
// - char *
// - void
// - ...
func ParseType(qualType string, conf *Config) (t types.Type, kind int, err error) {
p := newParser(qualType, conf)
if t, kind, err = p.parse(conf.Flags); err != nil {
return
}
if p.tok != token.EOF {
err = p.newError("unexpect token " + p.tok.String())
}
return
}
// -----------------------------------------------------------------------------
type parser struct {
s scanner.Scanner
scope *types.Scope
conf *Config
tok token.Token
lit string
old struct {
tok token.Token
lit string
}
}
const (
invalidTok token.Token = -1
)
func newParser(qualType string, conf *Config) *parser {
p := &parser{scope: conf.Scope, conf: conf}
p.old.tok = invalidTok
p.s.Init(qualType)
return p
}
func (p *parser) peek() token.Token {
if p.old.tok == invalidTok {
p.old.tok, p.old.lit = p.s.Scan()
}
return p.old.tok
}
func (p *parser) next() {
if p.old.tok != invalidTok { // support unget
p.tok, p.lit = p.old.tok, p.old.lit
p.old.tok = invalidTok
return
}
p.tok, p.lit = p.s.Scan()
}
func (p *parser) unget(tok token.Token, lit string) {
p.old.tok, p.old.lit = p.tok, p.lit
p.tok, p.lit = tok, lit
}
func (p *parser) skipUntil(tok token.Token) bool {
for {
p.next()
switch p.tok {
case tok:
return true
case token.EOF:
return false
}
}
}
func (p *parser) newErrorf(format string, args ...interface{}) *ParseTypeError {
return p.newError(fmt.Sprintf(format, args...))
}
func (p *parser) newError(errMsg string) *ParseTypeError {
return &ParseTypeError{QualType: p.s.Source(), ErrMsg: errMsg}
}
func (p *parser) expect(tokExp token.Token) error {
p.next()
if p.tok != tokExp {
return p.newErrorf("expect %v, got %v", tokExp, p.tok)
}
return nil
}
const (
flagShort = 1 << iota
flagLong
flagLongLong
flagUnsigned
flagSigned
flagComplex
flagStructOrUnion
)
func (p *parser) lookupType(tylit string, flags int) (t types.Type, err error) {
structOrUnion := (flags & flagStructOrUnion) != 0
_, o := gogen.LookupParent(p.scope, tylit, token.NoPos)
if o == nil {
return nil, &TypeNotFound{Literal: tylit, StructOrUnion: structOrUnion}
}
t = o.Type()
if !structOrUnion && flags != 0 {
tt, ok := t.(*types.Basic)
if !ok {
tyInt128 := p.conf.Int128()
if t == tyInt128 {
switch flags {
case flagSigned:
return tyInt128, nil
case flagUnsigned:
return p.conf.Uint128(), nil
}
}
} else if (flags & flagComplex) != 0 {
switch tt.Kind() {
case types.Float32:
return types.Typ[types.Complex64], nil
case types.Float64:
return types.Typ[types.Complex128], nil
case types.Int:
return types.Typ[types.Complex128], nil
}
} else {
switch tt.Kind() {
case types.Int:
if t = intTypes[flags&^flagSigned]; t != nil {
return
}
case types.Int8:
switch flags {
case flagUnsigned:
return types.Typ[types.Uint8], nil
case flagSigned:
return types.Typ[types.Int8], nil
}
case types.Float64:
switch flags {
case flagLong:
return ctypes.LongDouble, nil
}
}
}
log.Panicln("lookupType: TODO - invalid type")
return nil, ErrInvalidType
}
if t == types.Typ[types.Int] {
return ctypes.Int, nil
}
return
}
var intTypes = [...]types.Type{
0: ctypes.Int,
flagShort: types.Typ[types.Int16],
flagLong: ctypes.Long,
flagLong | flagLongLong: types.Typ[types.Int64],
flagUnsigned: ctypes.Uint,
flagShort | flagUnsigned: types.Typ[types.Uint16],
flagLong | flagUnsigned: ctypes.Ulong,
flagLong | flagLongLong | flagUnsigned: types.Typ[types.Uint64],
flagShort | flagLong | flagLongLong | flagUnsigned: nil,
}
func (p *parser) parseArray(t types.Type, inFlags int) (types.Type, error) {
var n int64
var err error
p.next()
switch p.tok {
case token.RBRACK: // ]
if (inFlags & FlagIsStructField) != 0 {
n = 0
} else {
n = -1
}
case token.INT:
if n, err = strconv.ParseInt(p.lit, 10, 64); err != nil {
return nil, p.newError(err.Error())
}
if err = p.expect(token.RBRACK); err != nil { // ]
return nil, err
}
default:
return nil, p.newError("array length not an integer")
}
if n >= 0 || (inFlags&(FlagIsExtern|FlagIsTypedef|FlagIsParam)) != 0 {
t = types.NewArray(t, n)
} else {
return nil, p.newError(emsgDefArrWithoutLen)
}
return t, nil
}
func (p *parser) parseArrays(t types.Type, inFlags int) (ret types.Type, err error) {
if t == nil {
return nil, p.newError("array to nil")
}
var tyArr types.Type
for {
if tyArr, err = p.parseArray(tyArr, inFlags); err != nil {
return
}
if p.peek() != token.LBRACK {
return newArraysEx(t, tyArr, inFlags), nil
}
p.next()
}
}
func (p *parser) parseFunc(pkg *types.Package, t types.Type, inFlags int) (ret types.Type, err error) {
var results *types.Tuple
if ctypes.NotVoid(t) {
results = types.NewTuple(types.NewParam(token.NoPos, pkg, "", t))
}
args, variadic, err := p.parseArgs(pkg)
if err != nil {
return
}
return ctypes.NewFunc(types.NewTuple(args...), results, variadic), nil
}
func (p *parser) parseArgs(pkg *types.Package) (args []*types.Var, variadic bool, err error) {
for {
arg, kind, e := p.parse(FlagIsParam)
if e != nil {
return nil, false, e
}
if ctypes.NotVoid(arg) {
args = append(args, types.NewParam(token.NoPos, pkg, "", arg))
}
if p.tok != token.COMMA {
variadic = (kind & KindFVariadic) != 0
break
}
}
if p.tok != token.RPAREN { // )
return nil, false, p.newError("expect )")
}
return
}
func (p *parser) parseStars() (nstar int) {
for isPtr(p.peek()) {
p.next()
nstar++
}
return
}
func (p *parser) parse(inFlags int) (t types.Type, kind int, err error) {
flags := 0
for {
p.next()
retry:
switch p.tok {
case token.IDENT:
ident:
switch lit := p.lit; lit {
case "unsigned":
flags |= flagUnsigned
case "short":
flags |= flagShort
case "long":
if (flags & flagLong) != 0 {
flags |= flagLongLong
} else {
flags |= flagLong
}
case "signed":
flags |= flagSigned
case "const":
kind |= KindFConst
case "volatile":
kind |= KindFVolatile
case "_Complex":
flags |= flagComplex
case "restrict", "_Nullable", "_Nonnull":
case "enum":
if err = p.expect(token.IDENT); err != nil {
return
}
if t != nil {
return nil, 0, p.newError("illegal syntax: multiple types?")
}
t = ctypes.Int
continue
case "struct", "union":
p.next()
switch p.tok {
case token.IDENT:
case token.LPAREN:
if t == nil && p.conf.Anonym != nil {
p.skipUntil(token.RPAREN)
t = p.conf.Anonym
kind |= KindFAnonymous
continue
}
fallthrough
default:
log.Panicln("c.types.ParseType: struct/union - TODO:", p.lit)
}
lit = ctypes.MangledName(lit, p.lit)
flags |= flagStructOrUnion
fallthrough
default:
if t != nil {
return nil, 0, p.newError("illegal syntax: multiple types?")
}
if t, err = p.lookupType(lit, flags); err != nil {
return
}
flags = 0
}
if flags != 0 {
p.next()
if p.tok == token.IDENT {
goto ident
}
if t != nil {
return nil, 0, p.newError("illegal syntax: multiple types?")
}
if t, err = p.lookupType("int", flags); err != nil {
return
}
flags = 0
goto retry
}
case token.MUL: // *
if t == nil {
return nil, 0, p.newError("pointer to nil")
}
t = ctypes.NewPointer(t)
case token.LBRACK: // [
if t, err = p.parseArrays(t, inFlags); err != nil {
return
}
case token.LPAREN: // (
if t == nil {
log.Panicln("TODO")
return nil, 0, p.newError("no function return type")
}
var nstar = p.parseStars()
var nstarRet int
var tyArr types.Type
var pkg, isFn = p.conf.Pkg(), false
var args []*types.Var
var variadic bool
if nstar == 0 {
if getRetType(inFlags) {
err = nil
p.tok = token.EOF
return
}
if args, variadic, err = p.parseArgs(pkg); err != nil {
return
}
isFn = true
} else {
nextTok:
p.next()
switch p.tok {
case token.RPAREN: // )
case token.LPAREN: // (
if !isFn {
nstar, nstarRet = p.parseStars(), nstar
if nstar != 0 {
p.expect(token.RPAREN) // )
p.expect(token.LPAREN) // (
}
if args, variadic, err = p.parseArgs(pkg); err != nil {
return
}
isFn = true
goto nextTok
}
return nil, 0, p.newError("expect )")
case token.LBRACK:
if tyArr, err = p.parseArrays(ctypes.Void, 0); err != nil {
return
}
p.expect(token.RPAREN) // )
case token.IDENT:
switch p.lit {
case "_Nullable", "_Nonnull", "const", "volatile":
goto nextTok
}
fallthrough
default:
return nil, 0, p.newError("expect )")
}
}
p.next()
switch p.tok {
case token.LPAREN: // (
if t, err = p.parseFunc(pkg, t, inFlags); err != nil {
return
}
case token.LBRACK: // [
if t, err = p.parseArrays(t, 0); err != nil {
return
}
case token.EOF:
case token.IDENT:
if p.lit == "__attribute__" {
p.tok, p.lit = token.EOF, ""
p.unget(token.EOF, "")
break
}
fallthrough
default:
return nil, 0, p.newError("unexpected " + p.tok.String())
}
t = newPointers(t, nstarRet)
if isFn {
if getRetType(inFlags) {
p.tok = token.EOF
return
}
var results *types.Tuple
if ctypes.NotVoid(t) {
results = types.NewTuple(types.NewParam(token.NoPos, pkg, "", t))
}
t = ctypes.NewFunc(types.NewTuple(args...), results, variadic)
}
t = newPointers(t, nstar)
t = newArrays(t, tyArr)
case token.RPAREN:
if t == nil {
t = ctypes.Void
}
return
case token.COMMA, token.EOF:
if t == nil {
err = io.ErrUnexpectedEOF
}
return
case token.ELLIPSIS:
if t != nil {
return nil, 0, p.newError("illegal syntax: multiple types?")
}
t = ctypes.Valist
kind |= KindFVariadic
default:
log.Panicln("c.types.ParseType: unknown -", p.tok, p.lit)
}
}
}
func newPointers(t types.Type, nstar int) types.Type {
for nstar > 0 {
t = ctypes.NewPointer(t)
nstar--
}
return t
}
func isPtr(tok token.Token) bool {
return tok == token.MUL || tok == token.XOR // * or ^
}
func newArrays(t types.Type, tyArr types.Type) types.Type {
retry:
if arr, ok := tyArr.(*types.Array); ok {
t = types.NewArray(t, arr.Len())
tyArr = arr.Elem()
goto retry
}
return t
}
func newArraysEx(t types.Type, tyArr types.Type, inFlags int) types.Type {
t = newArrays(t, tyArr)
if arr, ok := t.(*types.Array); ok {
if (inFlags & FlagIsParam) != 0 {
t = ctypes.NewPointer(arr.Elem())
}
}
return t
}
// -----------------------------------------------------------------------------

View File

@@ -0,0 +1,303 @@
/*
* Copyright (c) 2022 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 scanner
import (
"fmt"
"go/token"
"unicode"
"unicode/utf8"
)
// An ErrorHandler may be provided to Scanner.Init. If a syntax error is
// encountered and a handler was installed, the handler is called with a
// position and an error message. The position points to the beginning of
// the offending token.
type ErrorHandler func(pos token.Position, msg string)
// A Scanner holds the scanner's internal state while processing
// a given text. It can be allocated as part of another data
// structure but must be initialized via Init before use.
type Scanner struct {
// immutable state
src string
// scanning state
ch rune // current character
offset int // character offset
rdOffset int // reading offset (position after current character)
// public state - ok to modify
ErrorCount int // number of errors encountered
OnErr func(msg string)
}
const (
bom = 0xFEFF // byte order mark, only permitted as very first character
eof = -1 // end of file
)
// Read the next Unicode char into s.ch.
// s.ch < 0 means end-of-file.
//
// For optimization, there is some overlap between this method and
// s.scanIdentifier.
func (s *Scanner) next() {
if s.rdOffset < len(s.src) {
s.offset = s.rdOffset
r, w := rune(s.src[s.rdOffset]), 1
switch {
case r == 0:
s.error("illegal character NUL")
case r >= utf8.RuneSelf:
// not ASCII
r, w = utf8.DecodeRuneInString(s.src[s.rdOffset:])
if r == utf8.RuneError && w == 1 {
s.error("illegal UTF-8 encoding")
} else if r == bom && s.offset > 0 {
s.error("illegal byte order mark")
}
}
s.rdOffset += w
s.ch = r
} else {
s.offset = len(s.src)
s.ch = eof
}
}
// peek returns the byte following the most recently read character without
// advancing the scanner. If the scanner is at EOF, peek returns 0.
func (s *Scanner) peek() byte {
if s.rdOffset < len(s.src) {
return s.src[s.rdOffset]
}
return 0
}
func (s *Scanner) Init(src string) {
s.src = src
s.ch = ' '
s.offset = 0
s.rdOffset = 0
s.ErrorCount = 0
s.next()
if s.ch == bom {
s.next() // ignore BOM at file beginning
}
}
func (s *Scanner) Source() string {
return s.src
}
func (s *Scanner) error(msg string) {
if s.OnErr != nil {
s.OnErr(msg)
}
s.ErrorCount++
}
func (s *Scanner) errorf(format string, args ...interface{}) {
s.error(fmt.Sprintf(format, args...))
}
func isLetter(ch rune) bool {
return 'a' <= lower(ch) && lower(ch) <= 'z' || ch == '_' || ch >= utf8.RuneSelf && unicode.IsLetter(ch)
}
func isDigit(ch rune) bool {
return isDecimal(ch) || ch >= utf8.RuneSelf && unicode.IsDigit(ch)
}
// scanIdentifier reads the string of valid identifier characters at s.offset.
// It must only be called when s.ch is known to be a valid letter.
//
// Be careful when making changes to this function: it is optimized and affects
// scanning performance significantly.
func (s *Scanner) scanIdentifier() string {
offs := s.offset
// Optimize for the common case of an ASCII identifier.
//
// Ranging over s.src[s.rdOffset:] lets us avoid some bounds checks, and
// avoids conversions to runes.
//
// In case we encounter a non-ASCII character, fall back on the slower path
// of calling into s.next().
for rdOffset, b := range s.src[s.rdOffset:] {
if 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z' || b == '_' || '0' <= b && b <= '9' {
// Avoid assigning a rune for the common case of an ascii character.
continue
}
s.rdOffset += rdOffset
if 0 < b && b < utf8.RuneSelf {
// Optimization: we've encountered an ASCII character that's not a letter
// or number. Avoid the call into s.next() and corresponding set up.
//
// Note that s.next() does some line accounting if s.ch is '\n', so this
// shortcut is only possible because we know that the preceding character
// is not '\n'.
s.ch = rune(b)
s.offset = s.rdOffset
s.rdOffset++
goto exit
}
// We know that the preceding character is valid for an identifier because
// scanIdentifier is only called when s.ch is a letter, so calling s.next()
// at s.rdOffset resets the scanner state.
s.next()
for isLetter(s.ch) || isDigit(s.ch) {
s.next()
}
goto exit
}
s.offset = len(s.src)
s.rdOffset = len(s.src)
s.ch = eof
exit:
return string(s.src[offs:s.offset])
}
func lower(ch rune) rune { return ('a' - 'A') | ch } // returns lower-case ch iff ch is ASCII letter
func isDecimal(ch rune) bool { return '0' <= ch && ch <= '9' }
func isHex(ch rune) bool { return '0' <= ch && ch <= '9' || 'a' <= lower(ch) && lower(ch) <= 'f' }
func (s *Scanner) digits(base int, invalid *int) (digsep int) {
if base <= 10 {
max := rune('0' + base)
for isDecimal(s.ch) {
if s.ch >= max && *invalid < 0 {
*invalid = s.offset // record invalid rune offset
}
digsep = 1
s.next()
}
} else {
for isHex(s.ch) {
digsep = 1
s.next()
}
}
return
}
func (s *Scanner) scanNumber() (token.Token, string) {
offs := s.offset
base := 10 // number base
prefix := rune(0) // one of 0 (decimal), '0' (0-octal), 'x', 'o', or 'b'
digsep := 0 // bit 0: digit present, bit 1: '_' present
invalid := -1 // index of invalid digit in literal, or < 0
if s.ch == '0' {
s.next()
switch lower(s.ch) {
case 'x':
s.next()
base, prefix = 16, 'x'
case 'o':
s.next()
base, prefix = 8, 'o'
case 'b':
s.next()
base, prefix = 2, 'b'
default:
base, prefix = 8, '0'
digsep = 1 // leading 0
}
}
digsep |= s.digits(base, &invalid)
if digsep&1 == 0 {
s.error(litname(prefix) + " has no digits")
}
lit := string(s.src[offs:s.offset])
if invalid >= 0 {
s.errorf("invalid digit %q in %s", lit[invalid-offs], litname(prefix))
}
return token.INT, lit
}
func litname(prefix rune) string {
switch prefix {
case 'x':
return "hexadecimal literal"
case 'o', '0':
return "octal literal"
case 'b':
return "binary literal"
}
return "decimal literal"
}
func (s *Scanner) skipWhitespace() {
for s.ch == ' ' || s.ch == '\t' || s.ch == '\n' || s.ch == '\r' {
s.next()
}
}
func (s *Scanner) Scan() (tok token.Token, lit string) {
s.skipWhitespace()
// determine token value
switch ch := s.ch; {
case isLetter(ch):
lit = s.scanIdentifier()
tok = token.IDENT
case isDecimal(ch):
tok, lit = s.scanNumber()
default:
s.next() // always make progress
switch ch {
case -1:
tok = token.EOF
case '.':
// fractions starting with a '.' are handled by outer switch
tok = token.PERIOD
if s.ch == '.' && s.peek() == '.' {
s.next()
s.next() // consume last '.'
tok = token.ELLIPSIS
}
case ',':
tok = token.COMMA
case '(':
tok = token.LPAREN
case ')':
tok = token.RPAREN
case '[':
tok = token.LBRACK
case ']':
tok = token.RBRACK
case '*':
tok = token.MUL
case '^':
tok = token.XOR
default:
// next reports unexpected BOMs - don't repeat
if ch != bom {
s.errorf("illegal character %#U", ch)
}
tok = token.ILLEGAL
lit = string(ch)
}
}
return
}

99
x/clang/types/types.go Normal file
View File

@@ -0,0 +1,99 @@
/*
* Copyright (c) 2022 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 types
import (
"go/token"
"go/types"
"unsafe"
"github.com/goplus/gogen"
)
// -----------------------------------------------------------------------------
var (
Void = types.Typ[types.UntypedNil]
UnsafePointer = types.Typ[types.UnsafePointer]
Int = types.Typ[types.Int32]
Uint = types.Typ[types.Uint32]
Long = types.Typ[uintptr(types.Int32)+unsafe.Sizeof(0)>>3] // int32/int64
Ulong = types.Typ[uintptr(types.Uint32)+unsafe.Sizeof(0)>>3] // uint32/uint64
NotImpl = UnsafePointer
LongDouble = types.Typ[types.Float64]
)
func NotVoid(t types.Type) bool {
return t != Void
}
func MangledName(tag, name string) string {
return tag + "_" + name // TODO: use sth to replace _
}
// -----------------------------------------------------------------------------
var (
ValistTag types.Type
Valist types.Type = types.NewSlice(gogen.TyEmptyInterface)
)
func init() {
vaTag := types.NewTypeName(token.NoPos, types.Unsafe, MangledName("struct", "__va_list_tag"), nil)
ValistTag = types.NewNamed(vaTag, types.NewStruct(nil, nil), nil)
types.Universe.Insert(vaTag)
}
// -----------------------------------------------------------------------------
func NewFunc(params, results *types.Tuple, variadic bool) *types.Signature {
return gogen.NewCSignature(params, results, variadic)
}
func NewPointer(typ types.Type) types.Type {
switch t := typ.(type) {
case *types.Basic:
if t == Void {
return types.Typ[types.UnsafePointer]
}
case *types.Signature:
if gogen.IsCSignature(t) {
return types.NewSignature(nil, t.Params(), t.Results(), t.Variadic())
}
case *types.Named:
if typ == ValistTag {
return Valist
}
}
return types.NewPointer(typ)
}
func IsFunc(typ types.Type) bool {
sig, ok := typ.(*types.Signature)
if ok {
ok = gogen.IsCSignature(sig)
}
return ok
}
func Identical(typ1, typ2 types.Type) bool {
return types.Identical(typ1, typ2)
}
// -----------------------------------------------------------------------------