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

262
x/clang/ast/ast.go Normal file
View File

@@ -0,0 +1,262 @@
/*
* 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 ast
// -----------------------------------------------------------------------------
type IncludedFrom struct {
File string `json:"file"`
}
type Loc struct {
Offset int64 `json:"offset,omitempty"` // 432
File string `json:"file,omitempty"` // "sqlite3.i"
Line int `json:"line,omitempty"`
PresumedFile string `json:"presumedFile,omitempty"`
PresumedLine int `json:"presumedLine,omitempty"`
Col int `json:"col,omitempty"`
TokLen int `json:"tokLen,omitempty"`
IncludedFrom *IncludedFrom `json:"includedFrom,omitempty"` // "sqlite3.c"
}
type Pos struct {
Offset int64 `json:"offset,omitempty"`
Col int `json:"col,omitempty"`
TokLen int `json:"tokLen,omitempty"`
IncludedFrom *IncludedFrom `json:"includedFrom,omitempty"` // "sqlite3.c"
SpellingLoc *Loc `json:"spellingLoc,omitempty"`
ExpansionLoc *Loc `json:"expansionLoc,omitempty"`
}
type Range struct {
Begin Pos `json:"begin"`
End Pos `json:"end"`
}
// -----------------------------------------------------------------------------
type ID string
type Kind string
const (
TranslationUnitDecl Kind = "TranslationUnitDecl"
TypedefType Kind = "TypedefType"
TypedefDecl Kind = "TypedefDecl"
ElaboratedType Kind = "ElaboratedType"
BuiltinType Kind = "BuiltinType"
ConstantArrayType Kind = "ConstantArrayType"
IncompleteArrayType Kind = "IncompleteArrayType"
PointerType Kind = "PointerType"
RecordType Kind = "RecordType"
RecordDecl Kind = "RecordDecl"
FieldDecl Kind = "FieldDecl"
IndirectFieldDecl Kind = "IndirectFieldDecl"
VarDecl Kind = "VarDecl"
EmptyDecl Kind = "EmptyDecl"
EnumDecl Kind = "EnumDecl"
EnumConstantDecl Kind = "EnumConstantDecl"
AlwaysInlineAttr Kind = "AlwaysInlineAttr"
AsmLabelAttr Kind = "AsmLabelAttr"
AvailabilityAttr Kind = "AvailabilityAttr"
DeprecatedAttr Kind = "DeprecatedAttr"
BuiltinAttr Kind = "BuiltinAttr"
FormatAttr Kind = "FormatAttr"
FormatArgAttr Kind = "FormatArgAttr"
ColdAttr Kind = "ColdAttr"
ConstAttr Kind = "ConstAttr"
PureAttr Kind = "PureAttr"
PackedAttr Kind = "PackedAttr"
GNUInlineAttr Kind = "GNUInlineAttr"
StrictFPAttr Kind = "StrictFPAttr"
ReturnsTwiceAttr Kind = "ReturnsTwiceAttr"
RestrictAttr Kind = "RestrictAttr"
NoThrowAttr Kind = "NoThrowAttr"
NoInlineAttr Kind = "NoInlineAttr"
NoSanitizeAttr Kind = "NoSanitizeAttr"
NonNullAttr Kind = "NonNullAttr"
MayAliasAttr Kind = "MayAliasAttr"
MSAllocatorAttr Kind = "MSAllocatorAttr"
MaxFieldAlignmentAttr Kind = "MaxFieldAlignmentAttr"
WarnUnusedResultAttr Kind = "WarnUnusedResultAttr"
AllocSizeAttr Kind = "AllocSizeAttr"
AlignedAttr Kind = "AlignedAttr"
VisibilityAttr Kind = "VisibilityAttr"
C11NoReturnAttr Kind = "C11NoReturnAttr"
FunctionProtoType Kind = "FunctionProtoType"
FunctionDecl Kind = "FunctionDecl"
ParmVarDecl Kind = "ParmVarDecl"
ParenType Kind = "ParenType"
DeclStmt Kind = "DeclStmt"
CompoundStmt Kind = "CompoundStmt"
NullStmt Kind = "NullStmt"
ForStmt Kind = "ForStmt"
WhileStmt Kind = "WhileStmt"
DoStmt Kind = "DoStmt"
GotoStmt Kind = "GotoStmt"
BreakStmt Kind = "BreakStmt"
ContinueStmt Kind = "ContinueStmt"
LabelStmt Kind = "LabelStmt"
IfStmt Kind = "IfStmt"
SwitchStmt Kind = "SwitchStmt"
CaseStmt Kind = "CaseStmt"
DefaultStmt Kind = "DefaultStmt"
ReturnStmt Kind = "ReturnStmt"
GCCAsmStmt Kind = "GCCAsmStmt"
ParenExpr Kind = "ParenExpr"
CallExpr Kind = "CallExpr"
ConstantExpr Kind = "ConstantExpr"
InitListExpr Kind = "InitListExpr"
CStyleCastExpr Kind = "CStyleCastExpr"
DeclRefExpr Kind = "DeclRefExpr"
MemberExpr Kind = "MemberExpr"
ImplicitCastExpr Kind = "ImplicitCastExpr"
ImplicitValueInitExpr Kind = "ImplicitValueInitExpr"
UnaryExprOrTypeTraitExpr Kind = "UnaryExprOrTypeTraitExpr"
OffsetOfExpr Kind = "OffsetOfExpr"
ArraySubscriptExpr Kind = "ArraySubscriptExpr"
AtomicExpr Kind = "AtomicExpr"
VAArgExpr Kind = "VAArgExpr"
CompoundAssignOperator Kind = "CompoundAssignOperator"
BinaryOperator Kind = "BinaryOperator"
UnaryOperator Kind = "UnaryOperator"
ConditionalOperator Kind = "ConditionalOperator"
CompoundLiteralExpr Kind = "CompoundLiteralExpr"
PredefinedExpr Kind = "PredefinedExpr"
CharacterLiteral Kind = "CharacterLiteral"
IntegerLiteral Kind = "IntegerLiteral"
StringLiteral Kind = "StringLiteral"
FloatingLiteral Kind = "FloatingLiteral"
ImaginaryLiteral Kind = "ImaginaryLiteral"
AllocAlignAttr Kind = "AllocAlignAttr"
DisableTailCallsAttr Kind = "DisableTailCallsAttr"
StaticAssertDecl Kind = "StaticAssertDecl"
)
type ValueCategory string
const (
RValue ValueCategory = "rvalue"
PRValue ValueCategory = "prvalue"
LValue ValueCategory = "lvalue"
)
type CC string
const (
CDecl CC = "cdecl"
)
type StorageClass string
const (
Static StorageClass = "static"
Extern StorageClass = "extern"
)
type CastKind string
const (
LValueToRValue CastKind = "LValueToRValue"
BitCast CastKind = "BitCast"
FloatingToIntegral CastKind = "FloatingToIntegral"
FloatingComplexCast CastKind = "FloatingComplexCast"
FloatingRealToComplex CastKind = "FloatingRealToComplex"
IntegralRealToComplex CastKind = "IntegralRealToComplex"
FloatingCast CastKind = "FloatingCast"
IntegralCast CastKind = "IntegralCast"
IntegralToPointer CastKind = "IntegralToPointer"
IntegralToFloating CastKind = "IntegralToFloating"
IntegralToBoolean CastKind = "IntegralToBoolean"
FloatingToBoolean CastKind = "FloatingToBoolean"
IntegralComplexToBoolean CastKind = "IntegralComplexToBoolean"
FloatingComplexToBoolean CastKind = "FloatingComplexToBoolean"
PointerToBoolean CastKind = "PointerToBoolean"
PointerToIntegral CastKind = "PointerToIntegral"
FunctionToPointerDecay CastKind = "FunctionToPointerDecay"
ArrayToPointerDecay CastKind = "ArrayToPointerDecay"
BuiltinFnToFnPtr CastKind = "BuiltinFnToFnPtr"
ToVoid CastKind = "ToVoid"
NullToPointer CastKind = "NullToPointer"
NoOp CastKind = "NoOp"
)
type (
// OpCode can be:
// + - * / || >= -- ++ etc
OpCode string
)
type Type struct {
// QualType can be:
// unsigned int
// struct ConstantString
// volatile uint32_t
// int (*)(void *, int, char **, char **)
// int (*)(const char *, ...)
// int (*)(void)
// const char *restrict
// const char [7]
// char *
// void
// ...
QualType string `json:"qualType"`
DesugaredQualType string `json:"desugaredQualType,omitempty"`
TypeAliasDeclID ID `json:"typeAliasDeclId,omitempty"`
}
type Node struct {
ID ID `json:"id,omitempty"`
Kind Kind `json:"kind,omitempty"`
Loc *Loc `json:"loc,omitempty"`
Range *Range `json:"range,omitempty"`
ReferencedMemberDecl ID `json:"referencedMemberDecl,omitempty"`
PreviousDecl ID `json:"previousDecl,omitempty"`
ParentDeclContextID ID `json:"parentDeclContextId,omitempty"`
IsImplicit bool `json:"isImplicit,omitempty"` // is this type implicit defined
IsReferenced bool `json:"isReferenced,omitempty"` // is this type refered or not
IsUsed bool `json:"isUsed,omitempty"` // is this variable used or not
IsArrow bool `json:"isArrow,omitempty"` // is ptr->member not obj.member
IsPostfix bool `json:"isPostfix,omitempty"`
IsPartOfExplicitCast bool `json:"isPartOfExplicitCast,omitempty"`
IsBitfield bool `json:"isBitfield,omitempty"`
Inline bool `json:"inline,omitempty"`
StorageClass StorageClass `json:"storageClass,omitempty"`
TagUsed string `json:"tagUsed,omitempty"` // struct | union
HasElse bool `json:"hasElse,omitempty"`
CompleteDefinition bool `json:"completeDefinition,omitempty"`
Complicated bool `json:"-"` // complicated statement
Variadic bool `json:"variadic,omitempty"`
Name string `json:"name,omitempty"`
MangledName string `json:"mangledName,omitempty"`
Type *Type `json:"type,omitempty"`
CC CC `json:"cc,omitempty"`
Field *Node `json:"field,omitempty"`
Decl *Node `json:"decl,omitempty"`
OwnedTagDecl *Node `json:"ownedTagDecl,omitempty"`
ReferencedDecl *Node `json:"referencedDecl,omitempty"`
OpCode OpCode `json:"opcode,omitempty"`
Init string `json:"init,omitempty"`
ValueCategory ValueCategory `json:"valueCategory,omitempty"`
Value interface{} `json:"value,omitempty"`
CastKind CastKind `json:"castKind,omitempty"`
Size int `json:"size,omitempty"` // array size
Inner []*Node `json:"inner,omitempty"`
ArrayFiller []*Node `json:"array_filler,omitempty"`
}
// -----------------------------------------------------------------------------

View File

@@ -24,7 +24,7 @@ import (
// -----------------------------------------------------------------------------
// Cmd represents a nm command.
// Cmd represents a clang command.
type Cmd struct {
app string
@@ -32,7 +32,7 @@ type Cmd struct {
Stderr io.Writer
}
// New creates a new nm command.
// New creates a new clang command.
func New(app string) *Cmd {
if app == "" {
app = "clang"

122
x/clang/parser/pages.go Normal file
View File

@@ -0,0 +1,122 @@
/*
* 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
// -----------------------------------------------------------------------------
const pageSize = 1024 * 1024
type PagedWriter struct {
pages []*[pageSize]byte
last *[pageSize]byte
off int
}
func NewPagedWriter() *PagedWriter {
return &PagedWriter{last: new([pageSize]byte)}
}
func (p *PagedWriter) Write(buf []byte) (written int, err error) {
for {
n := copy(p.last[p.off:], buf[written:])
written += n
if written >= len(buf) {
p.off += n
return
}
p.pages = append(p.pages, p.last)
p.last, p.off = new([pageSize]byte), 0
}
}
func (p *PagedWriter) Len() int {
return len(p.pages)*pageSize + p.off
}
func (p *PagedWriter) Bytes() []byte {
out, n := make([]byte, p.Len()), 0
for _, page := range p.pages {
n += copy(out[n:], page[:])
}
copy(out[n:], p.last[:p.off])
return out
}
/*
func (p *PagedWriter) ToReader() *PagedReader {
return &PagedReader{src: p, curr: p.getPage(0)}
}
func (p *PagedWriter) getPage(ipage int) []byte {
if ipage == len(p.pages) { // last page
return p.last[:p.off]
}
return p.pages[ipage][:]
}
// -----------------------------------------------------------------------------
type PagedReader struct {
src *PagedWriter
curr []byte
off int
ipage int
}
func (p *PagedReader) WriteTo(w io.Writer) (written int64, err error) {
n, err := w.Write(p.curr[p.off:])
written = int64(n)
if err != nil {
return
}
src, ipage := p.src, p.ipage
for {
if ipage == len(src.pages) { // last page
p.ipage, p.off = ipage, len(p.curr)
return
}
ipage++
page := src.getPage(ipage)
n, err = w.Write(page)
written += int64(n)
if err != nil {
p.ipage, p.curr, p.off = ipage, page, n
return
}
}
}
func (p *PagedReader) Read(buf []byte) (nread int, err error) {
for {
n := copy(buf[nread:], p.curr[p.off:])
nread += n
p.off += n
if nread >= len(buf) {
return
}
src := p.src
if p.ipage == len(src.pages) { // last page
err = io.EOF
return
}
p.ipage++
p.curr, p.off = src.getPage(p.ipage), 0
}
}
*/
// -----------------------------------------------------------------------------

104
x/clang/parser/parse.go Normal file
View File

@@ -0,0 +1,104 @@
/*
* 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 (
"bytes"
"os"
"os/exec"
"strings"
"github.com/goplus/llgo/x/clang/ast"
jsoniter "github.com/json-iterator/go"
)
type Mode uint
// -----------------------------------------------------------------------------
type ParseError struct {
Err error
Stderr []byte
}
func (p *ParseError) Error() string {
if len(p.Stderr) > 0 {
return string(p.Stderr)
}
return p.Err.Error()
}
// -----------------------------------------------------------------------------
type Config struct {
Json *[]byte
Flags []string
Stderr bool
}
func DumpAST(filename string, conf *Config) (result []byte, warning []byte, err error) {
if conf == nil {
conf = new(Config)
}
skiperr := strings.HasSuffix(filename, "vfprintf.c.i")
stdout := NewPagedWriter()
stderr := new(bytes.Buffer)
args := []string{"-Xclang", "-ast-dump=json", "-fsyntax-only", filename}
if len(conf.Flags) != 0 {
args = append(conf.Flags, args...)
}
cmd := exec.Command("clang", args...)
cmd.Stdin = os.Stdin
cmd.Stdout = stdout
if conf.Stderr && !skiperr {
cmd.Stderr = os.Stderr
} else {
cmd.Stderr = stderr
}
err = cmd.Run()
errmsg := stderr.Bytes()
if err != nil && !skiperr {
return nil, nil, &ParseError{Err: err, Stderr: errmsg}
}
return stdout.Bytes(), errmsg, nil
}
// -----------------------------------------------------------------------------
var json = jsoniter.ConfigCompatibleWithStandardLibrary
func ParseFileEx(filename string, mode Mode, conf *Config) (file *ast.Node, warning []byte, err error) {
out, warning, err := DumpAST(filename, conf)
if err != nil {
return
}
if conf != nil && conf.Json != nil {
*conf.Json = out
}
file = new(ast.Node)
err = json.Unmarshal(out, file)
if err != nil {
err = &ParseError{Err: err}
}
return
}
func ParseFile(filename string, mode Mode) (file *ast.Node, warning []byte, err error) {
return ParseFileEx(filename, mode, nil)
}
// -----------------------------------------------------------------------------

View File

@@ -0,0 +1,28 @@
/*
* 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 pathutil
import (
"path/filepath"
)
func Canonical(baseDir string, uri string) string {
if filepath.IsAbs(uri) {
return filepath.Clean(uri)
}
return filepath.Join(baseDir, uri)
}

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 preprocessor
import (
"log"
"os"
"os/exec"
"path/filepath"
"github.com/goplus/llgo/x/clang/pathutil"
)
const (
DbgFlagExecCmd = 1 << iota
DbgFlagAll = DbgFlagExecCmd
)
var (
debugExecCmd bool
)
func SetDebug(flags int) {
debugExecCmd = (flags & DbgFlagExecCmd) != 0
}
// -----------------------------------------------------------------------------
type Config struct {
Compiler string // default: clang
PPFlag string // default: -E
BaseDir string // base of include searching directory, should be absolute path
IncludeDirs []string
Defines []string
Flags []string
}
func Do(infile, outfile string, conf *Config) (err error) {
if infile, err = filepath.Abs(infile); err != nil {
return
}
if outfile, err = filepath.Abs(outfile); err != nil {
return
}
if conf == nil {
conf = new(Config)
}
base := conf.BaseDir
if base == "" {
if base, err = os.Getwd(); err != nil {
return
}
}
compiler := conf.Compiler
if compiler == "" {
compiler = "clang"
}
ppflag := conf.PPFlag
if ppflag == "" {
ppflag = "-E"
}
n := 4 + len(conf.Flags) + len(conf.IncludeDirs) + len(conf.Defines)
args := make([]string, 3, n)
args[0] = ppflag
args[1], args[2] = "-o", outfile
args = append(args, conf.Flags...)
for _, def := range conf.Defines {
args = append(args, "-D"+def)
}
for _, inc := range conf.IncludeDirs {
args = append(args, "-I"+pathutil.Canonical(base, inc))
}
args = append(args, infile)
if debugExecCmd {
log.Println("==> runCmd:", compiler, args)
}
cmd := exec.Command(compiler, args...)
cmd.Dir = filepath.Dir(infile)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
// -----------------------------------------------------------------------------

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)
}
// -----------------------------------------------------------------------------