mirror of
https://github.com/QuilibriumNetwork/ceremonyclient.git
synced 2026-02-21 18:37:26 +08:00
919 lines
18 KiB
Go
919 lines
18 KiB
Go
//
|
|
// ast.go
|
|
//
|
|
// Copyright (c) 2019-2024 Markku Rossi
|
|
//
|
|
// All rights reserved.
|
|
//
|
|
|
|
package ast
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
"unicode"
|
|
|
|
"source.quilibrium.com/quilibrium/monorepo/bedlam/compiler/mpa"
|
|
"source.quilibrium.com/quilibrium/monorepo/bedlam/compiler/ssa"
|
|
"source.quilibrium.com/quilibrium/monorepo/bedlam/compiler/utils"
|
|
"source.quilibrium.com/quilibrium/monorepo/bedlam/types"
|
|
)
|
|
|
|
var (
|
|
_ AST = &List{}
|
|
_ AST = &Func{}
|
|
_ AST = &VariableDef{}
|
|
_ AST = &Assign{}
|
|
_ AST = &If{}
|
|
_ AST = &Call{}
|
|
_ AST = &ArrayCast{}
|
|
_ AST = &Return{}
|
|
_ AST = &For{}
|
|
_ AST = &ForRange{}
|
|
_ AST = &Binary{}
|
|
_ AST = &Unary{}
|
|
_ AST = &Slice{}
|
|
_ AST = &Index{}
|
|
_ AST = &VariableRef{}
|
|
_ AST = &BasicLit{}
|
|
_ AST = &CompositeLit{}
|
|
_ AST = &Make{}
|
|
_ AST = &Copy{}
|
|
)
|
|
|
|
func indent(w io.Writer, indent int) {
|
|
for i := 0; i < indent; i++ {
|
|
fmt.Fprint(w, " ")
|
|
}
|
|
}
|
|
|
|
// Type specifies AST types.
|
|
type Type int
|
|
|
|
// AST types.
|
|
const (
|
|
TypeName Type = iota
|
|
TypeArray
|
|
TypeSlice
|
|
TypeStruct
|
|
TypePointer
|
|
TypeAlias
|
|
)
|
|
|
|
// TypeInfo contains AST type information.
|
|
type TypeInfo struct {
|
|
utils.Point
|
|
Type Type
|
|
Name Identifier
|
|
ElementType *TypeInfo
|
|
ArrayLength AST
|
|
TypeName string
|
|
StructFields []StructField
|
|
AliasType *TypeInfo
|
|
Methods map[string]*Func
|
|
Annotations Annotations
|
|
}
|
|
|
|
// Equal tests if the argument TypeInfo is equal to this TypeInfo.
|
|
func (ti *TypeInfo) Equal(o *TypeInfo) bool {
|
|
if ti.Type != o.Type {
|
|
return false
|
|
}
|
|
switch ti.Type {
|
|
case TypeName:
|
|
return ti.Name.String() == o.Name.String()
|
|
|
|
case TypeArray:
|
|
return ti.ElementType.Equal(o.ElementType) &&
|
|
ti.ArrayLength == o.ArrayLength
|
|
|
|
case TypeSlice, TypePointer:
|
|
return ti.ElementType.Equal(o.ElementType)
|
|
|
|
case TypeStruct:
|
|
if len(ti.StructFields) != len(o.StructFields) {
|
|
return false
|
|
}
|
|
for idx, f := range ti.StructFields {
|
|
if !f.Type.Equal(o.StructFields[idx].Type) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
|
|
case TypeAlias:
|
|
return ti.AliasType.Equal(o.AliasType)
|
|
|
|
default:
|
|
panic("unsupported type")
|
|
}
|
|
}
|
|
|
|
// StructField contains AST structure field information.
|
|
type StructField struct {
|
|
utils.Point
|
|
Name string
|
|
Type *TypeInfo
|
|
}
|
|
|
|
func (ti *TypeInfo) String() string {
|
|
return ti.format(false)
|
|
}
|
|
|
|
// Format print the type definition of the type info.
|
|
func (ti *TypeInfo) Format() string {
|
|
return ti.format(true)
|
|
}
|
|
|
|
func (ti *TypeInfo) format(pp bool) string {
|
|
var str string
|
|
|
|
if pp {
|
|
str = fmt.Sprintf("type %s ", ti.TypeName)
|
|
}
|
|
|
|
switch ti.Type {
|
|
case TypeName:
|
|
return str + ti.Name.String()
|
|
|
|
case TypeArray:
|
|
return fmt.Sprintf("%s[%s]%s", str, ti.ArrayLength, ti.ElementType)
|
|
|
|
case TypeSlice:
|
|
return fmt.Sprintf("%s[]%s", str, ti.ElementType)
|
|
|
|
case TypeStruct:
|
|
str = fmt.Sprintf("%sstruct {", str)
|
|
if pp {
|
|
var width int
|
|
for _, field := range ti.StructFields {
|
|
if len(field.Name) > width {
|
|
width = len(field.Name)
|
|
}
|
|
}
|
|
for idx, field := range ti.StructFields {
|
|
if idx == 0 {
|
|
str += "\n"
|
|
}
|
|
str += " "
|
|
str += field.Name
|
|
for i := len(field.Name); i < width; i++ {
|
|
str += " "
|
|
}
|
|
str += fmt.Sprintf(" %s\n", field.Type.String())
|
|
}
|
|
} else {
|
|
for idx, field := range ti.StructFields {
|
|
if idx > 0 {
|
|
str += ", "
|
|
}
|
|
str += fmt.Sprintf("%s %s", field.Name, field.Type.String())
|
|
}
|
|
}
|
|
return str + "}"
|
|
|
|
case TypeAlias:
|
|
return fmt.Sprintf("%s= %s", str, ti.AliasType)
|
|
|
|
case TypePointer:
|
|
return fmt.Sprintf("%s*%s", str, ti.ElementType)
|
|
|
|
default:
|
|
return fmt.Sprintf("%s{TypeInfo %d}", str, ti.Type)
|
|
}
|
|
}
|
|
|
|
// IsIdentifier returns true if the type info specifies a type name
|
|
// without package.
|
|
func (ti *TypeInfo) IsIdentifier() bool {
|
|
return ti.Type == TypeName && len(ti.Name.Package) == 0
|
|
}
|
|
|
|
// Resolve resolves the type information in the environment.
|
|
func (ti *TypeInfo) Resolve(env *Env, ctx *Codegen, gen *ssa.Generator) (
|
|
result types.Info, err error) {
|
|
|
|
if ti == nil {
|
|
return
|
|
}
|
|
switch ti.Type {
|
|
case TypeName:
|
|
result, err = types.Parse(ti.Name.Name)
|
|
if err == nil {
|
|
return
|
|
}
|
|
// Check dynamic types from the env.
|
|
var b ssa.Binding
|
|
var pkg *Package
|
|
var ok bool
|
|
|
|
if len(ti.Name.Package) == 0 {
|
|
// Plain indentifiers.
|
|
b, ok = env.Get(ti.Name.Name)
|
|
if !ok {
|
|
// Check dynamic types from the pkg.
|
|
b, ok = ctx.Package.Bindings.Get(ti.Name.Name)
|
|
}
|
|
}
|
|
if !ok {
|
|
// Qualified names and package-local names.
|
|
var pkgName string
|
|
if len(ti.Name.Package) > 0 {
|
|
pkgName = ti.Name.Package
|
|
} else if ti.Name.Defined != ctx.Package.Name {
|
|
pkgName = ti.Name.Defined
|
|
}
|
|
|
|
if len(pkgName) > 0 {
|
|
pkg, ok = ctx.Packages[pkgName]
|
|
if !ok {
|
|
return result, ctx.Errorf(ti, "unknown package: %s",
|
|
pkgName)
|
|
}
|
|
b, ok = pkg.Bindings.Get(ti.Name.Name)
|
|
}
|
|
}
|
|
if ok {
|
|
val, ok := b.Bound.(*ssa.Value)
|
|
if ok && val.TypeRef {
|
|
return val.Type, nil
|
|
}
|
|
}
|
|
return result, ctx.Errorf(ti, "undefined name: %s", ti)
|
|
|
|
case TypeArray:
|
|
// Array length.
|
|
constLength, ok, err := ti.ArrayLength.Eval(env, ctx, gen)
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
if !ok {
|
|
return result, ctx.Errorf(ti.ArrayLength,
|
|
"array length is not constant: %s", ti.ArrayLength)
|
|
}
|
|
length, err := constLength.ConstInt()
|
|
if err != nil {
|
|
return result, ctx.Errorf(ti.ArrayLength,
|
|
"invalid array length: %s", err)
|
|
}
|
|
|
|
// Element type.
|
|
elInfo, err := ti.ElementType.Resolve(env, ctx, gen)
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
return types.Info{
|
|
Type: types.TArray,
|
|
IsConcrete: true,
|
|
Bits: length * elInfo.Bits,
|
|
MinBits: length * elInfo.MinBits,
|
|
ElementType: &elInfo,
|
|
ArraySize: length,
|
|
}, nil
|
|
|
|
case TypeSlice:
|
|
// Element type.
|
|
elInfo, err := ti.ElementType.Resolve(env, ctx, gen)
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
// Bits and ArraySize are left uninitialized and they must be
|
|
// defined when type is instantiated.
|
|
return types.Info{
|
|
Type: types.TSlice,
|
|
ElementType: &elInfo,
|
|
}, nil
|
|
|
|
case TypePointer:
|
|
// Element type.
|
|
elInfo, err := ti.ElementType.Resolve(env, ctx, gen)
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
return types.Info{
|
|
Type: types.TPtr,
|
|
IsConcrete: true,
|
|
Bits: elInfo.Bits,
|
|
MinBits: elInfo.Bits,
|
|
ElementType: &elInfo,
|
|
}, nil
|
|
|
|
default:
|
|
return result, ctx.Errorf(ti, "can't resolve type %s", ti)
|
|
}
|
|
}
|
|
|
|
// ConstantDef implements a constant definition.
|
|
type ConstantDef struct {
|
|
utils.Point
|
|
Name string
|
|
Type *TypeInfo
|
|
Init AST
|
|
Annotations Annotations
|
|
}
|
|
|
|
// Exported describes if the constant is exported from the package.
|
|
func (ast *ConstantDef) Exported() bool {
|
|
return IsExported(ast.Name)
|
|
}
|
|
|
|
// IsExported describes if the name is exported from the package.
|
|
func IsExported(name string) bool {
|
|
if len(name) == 0 {
|
|
return false
|
|
}
|
|
return unicode.IsUpper([]rune(name)[0])
|
|
}
|
|
|
|
func (ast *ConstantDef) String() string {
|
|
result := fmt.Sprintf("const %s", ast.Name)
|
|
if ast.Type != nil {
|
|
result += fmt.Sprintf(" %s", ast.Type)
|
|
}
|
|
if ast.Init != nil {
|
|
result += fmt.Sprintf(" = %s", ast.Init)
|
|
}
|
|
return result
|
|
}
|
|
|
|
// Identifier implements an AST identifier.
|
|
type Identifier struct {
|
|
Defined string
|
|
Package string
|
|
Name string
|
|
}
|
|
|
|
func (i Identifier) String() string {
|
|
if len(i.Package) == 0 {
|
|
return i.Name
|
|
}
|
|
return fmt.Sprintf("%s.%s", i.Package, i.Name)
|
|
}
|
|
|
|
// Qualified tells if the identifier has package part.
|
|
func (i Identifier) Qualified() bool {
|
|
return len(i.Package) > 0
|
|
}
|
|
|
|
// AST implements abstract syntax tree nodes.
|
|
type AST interface {
|
|
utils.Locator
|
|
|
|
String() string
|
|
// SSA generates SSA code from the AST node. The code is appended
|
|
// into the basic block `block'. The function returns the next
|
|
// sequential basic block. The `ssa.Dead' is set to `true' if the
|
|
// code terminates i.e. all following AST nodes are dead code.
|
|
SSA(block *ssa.Block, ctx *Codegen, gen *ssa.Generator) (
|
|
*ssa.Block, []ssa.Value, error)
|
|
// Eval evaluates the AST node during constant propagation.
|
|
Eval(env *Env, ctx *Codegen, gen *ssa.Generator) (
|
|
value ssa.Value, isConstant bool, err error)
|
|
}
|
|
|
|
// NewEnv creates a new environment based on the current environment
|
|
// bindings in the block.
|
|
func NewEnv(block *ssa.Block) *Env {
|
|
return &Env{
|
|
Bindings: block.Bindings.Clone(),
|
|
}
|
|
}
|
|
|
|
// Env implements a value bindings environment.
|
|
type Env struct {
|
|
Bindings *ssa.Bindings
|
|
}
|
|
|
|
// Debug print the environment.
|
|
func (env *Env) Debug() {
|
|
fmt.Print("Env: ")
|
|
env.Bindings.Debug()
|
|
}
|
|
|
|
// Get gets the value binding from the environment.
|
|
func (env *Env) Get(name string) (ssa.Binding, bool) {
|
|
return env.Bindings.Get(name)
|
|
}
|
|
|
|
// Set sets the value binding to the environment.
|
|
func (env *Env) Set(v ssa.Value, val *ssa.Value) {
|
|
env.Bindings.Define(v, val)
|
|
}
|
|
|
|
// List implements an AST list statement.
|
|
type List []AST
|
|
|
|
func (ast List) String() string {
|
|
result := "{\n"
|
|
for _, a := range ast {
|
|
result += fmt.Sprintf("\t%s\n", a)
|
|
}
|
|
return result + "}\n"
|
|
}
|
|
|
|
// Location implements the compiler.utils.Locator interface.
|
|
func (ast List) Location() utils.Point {
|
|
if len(ast) > 0 {
|
|
return ast[0].Location()
|
|
}
|
|
return utils.Point{}
|
|
}
|
|
|
|
// Variable implements an AST variable.
|
|
type Variable struct {
|
|
utils.Point
|
|
Name string
|
|
Type *TypeInfo
|
|
}
|
|
|
|
func (v Variable) String() string {
|
|
if v.Type == nil {
|
|
return v.Name
|
|
}
|
|
return fmt.Sprintf("%s %s", v.Name, v.Type)
|
|
}
|
|
|
|
// Func implements an AST function.
|
|
type Func struct {
|
|
utils.Point
|
|
Name string
|
|
This *Variable
|
|
Args []*Variable
|
|
Return []*Variable
|
|
Returns []*ReturnInfo
|
|
NamedReturn bool
|
|
Body List
|
|
End utils.Point
|
|
NumInstances int
|
|
Annotations Annotations
|
|
}
|
|
|
|
// ReturnInfo provide information about function return values.
|
|
type ReturnInfo struct {
|
|
Return *Return
|
|
Types []types.Info
|
|
}
|
|
|
|
// Annotations specify function annotations.
|
|
type Annotations []string
|
|
|
|
// FirstSentence returns the first sentence from the annotations or an
|
|
// empty string it if annotations are empty.
|
|
func (ann Annotations) FirstSentence() string {
|
|
str := strings.Join(ann, "\n")
|
|
idx := strings.IndexRune(str, '.')
|
|
if idx > 0 {
|
|
return str[:idx+1]
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// NewFunc creates a new function definition.
|
|
func NewFunc(loc utils.Point, name string, args []*Variable, ret []*Variable,
|
|
namedReturn bool, body List, end utils.Point,
|
|
annotations Annotations) *Func {
|
|
|
|
// Skip empty lines from the beginning and end of annotations.
|
|
for i := 0; i < len(annotations); i++ {
|
|
if len(strings.TrimSpace(annotations[i])) > 0 {
|
|
annotations = annotations[i:]
|
|
break
|
|
}
|
|
}
|
|
for i := len(annotations) - 1; i >= 0; i-- {
|
|
if len(strings.TrimSpace(annotations[i])) > 0 {
|
|
annotations = annotations[0 : i+1]
|
|
break
|
|
}
|
|
}
|
|
|
|
return &Func{
|
|
Point: loc,
|
|
Name: name,
|
|
Args: args,
|
|
Return: ret,
|
|
NamedReturn: namedReturn,
|
|
Body: body,
|
|
End: end,
|
|
Annotations: annotations,
|
|
}
|
|
}
|
|
|
|
func (ast *Func) String() string {
|
|
var str string
|
|
if ast.This != nil {
|
|
str = fmt.Sprintf("func (%s %s) %s(",
|
|
ast.This.Name, ast.This.Type, ast.Name)
|
|
} else {
|
|
str = fmt.Sprintf("func %s(", ast.Name)
|
|
}
|
|
for idx, arg := range ast.Args {
|
|
if idx > 0 {
|
|
str += ", "
|
|
}
|
|
if idx+1 < len(ast.Args) && arg.Type.Equal(ast.Args[idx+1].Type) {
|
|
str += arg.Name
|
|
} else {
|
|
str += fmt.Sprintf("%s %s", arg.Name, arg.Type)
|
|
}
|
|
}
|
|
str += ")"
|
|
|
|
if len(ast.Return) > 0 {
|
|
if ast.NamedReturn {
|
|
str += " ("
|
|
for idx, ret := range ast.Return {
|
|
if idx > 0 {
|
|
str += ", "
|
|
}
|
|
if idx+1 < len(ast.Return) &&
|
|
ret.Type.Equal(ast.Return[idx+1].Type) {
|
|
str += ret.Name
|
|
} else {
|
|
str += fmt.Sprintf("%s %s", ret.Name, ret.Type)
|
|
}
|
|
}
|
|
str += ")"
|
|
} else if len(ast.Return) > 1 {
|
|
str += " ("
|
|
for idx, ret := range ast.Return {
|
|
if idx > 0 {
|
|
str += ", "
|
|
}
|
|
str += fmt.Sprintf("%s", ret.Type)
|
|
}
|
|
str += ")"
|
|
} else {
|
|
str += fmt.Sprintf(" %s", ast.Return[0].Type)
|
|
}
|
|
}
|
|
|
|
return str
|
|
}
|
|
|
|
// VariableDef implements an AST variable definition.
|
|
type VariableDef struct {
|
|
utils.Point
|
|
Names []string
|
|
Type *TypeInfo
|
|
Init AST
|
|
Annotations Annotations
|
|
}
|
|
|
|
func (ast *VariableDef) String() string {
|
|
result := fmt.Sprintf("var %v", strings.Join(ast.Names, ", "))
|
|
if ast.Type != nil {
|
|
result += fmt.Sprintf(" %s", ast.Type)
|
|
}
|
|
if ast.Init != nil {
|
|
result += fmt.Sprintf(" = %s", ast.Init)
|
|
}
|
|
return result
|
|
}
|
|
|
|
// Assign implements an AST assignment expression.
|
|
type Assign struct {
|
|
utils.Point
|
|
LValues []AST
|
|
Exprs []AST
|
|
Define bool
|
|
}
|
|
|
|
func (ast *Assign) String() string {
|
|
var op string
|
|
if ast.Define {
|
|
op = ":="
|
|
} else {
|
|
op = "="
|
|
}
|
|
return fmt.Sprintf("%v %s %v", ast.LValues, op, ast.Exprs)
|
|
}
|
|
|
|
// If implements an AST if statement.
|
|
type If struct {
|
|
utils.Point
|
|
Expr AST
|
|
True AST
|
|
False AST
|
|
}
|
|
|
|
func (ast *If) String() string {
|
|
return fmt.Sprintf("if %s", ast.Expr)
|
|
}
|
|
|
|
// Call implements an AST call expression.
|
|
type Call struct {
|
|
utils.Point
|
|
Ref *VariableRef
|
|
Exprs []AST
|
|
}
|
|
|
|
func (ast *Call) String() string {
|
|
str := fmt.Sprintf("%s(", ast.Ref)
|
|
for idx, expr := range ast.Exprs {
|
|
if idx > 0 {
|
|
str += ", "
|
|
}
|
|
str += fmt.Sprintf("%v", expr)
|
|
}
|
|
return str + ")"
|
|
}
|
|
|
|
// ArrayCast implements array cast expressions.
|
|
type ArrayCast struct {
|
|
utils.Point
|
|
TypeInfo *TypeInfo
|
|
Expr AST
|
|
}
|
|
|
|
func (ast *ArrayCast) String() string {
|
|
return fmt.Sprintf("%v(%v)", ast.TypeInfo, ast.Expr)
|
|
}
|
|
|
|
// Return implements an AST return statement.
|
|
type Return struct {
|
|
utils.Point
|
|
Exprs []AST
|
|
AutoGenerated bool
|
|
}
|
|
|
|
func (ast *Return) String() string {
|
|
return fmt.Sprintf("return %v", ast.Exprs)
|
|
}
|
|
|
|
// For implements an AST for statement.
|
|
type For struct {
|
|
utils.Point
|
|
Init AST
|
|
Cond AST
|
|
Inc AST
|
|
Body List
|
|
}
|
|
|
|
func (ast *For) String() string {
|
|
return fmt.Sprintf("for %s; %s; %s %s",
|
|
ast.Init, ast.Cond, ast.Inc, ast.Body)
|
|
}
|
|
|
|
// ForRange implements an AST for for-range statement.
|
|
type ForRange struct {
|
|
utils.Point
|
|
ExprList []AST
|
|
Def bool
|
|
Expr AST
|
|
Body List
|
|
}
|
|
|
|
func (ast *ForRange) String() string {
|
|
result := "for "
|
|
for idx, expr := range ast.ExprList {
|
|
if idx > 0 {
|
|
result += ", "
|
|
}
|
|
result += expr.String()
|
|
}
|
|
if ast.Def {
|
|
result += " := range "
|
|
} else {
|
|
result += " = range "
|
|
}
|
|
result += ast.Expr.String()
|
|
|
|
return result
|
|
}
|
|
|
|
// BinaryType defines binary expression types.
|
|
type BinaryType int
|
|
|
|
// Binary expression types.
|
|
const (
|
|
BinaryMul BinaryType = iota
|
|
BinaryDiv
|
|
BinaryMod
|
|
BinaryLshift
|
|
BinaryRshift
|
|
BinaryBand
|
|
BinaryBclear
|
|
BinaryBor
|
|
BinaryBxor
|
|
BinaryAdd
|
|
BinarySub
|
|
BinaryEq
|
|
BinaryNeq
|
|
BinaryLt
|
|
BinaryLe
|
|
BinaryGt
|
|
BinaryGe
|
|
BinaryAnd
|
|
BinaryOr
|
|
)
|
|
|
|
var binaryTypes = map[BinaryType]string{
|
|
BinaryMul: "*",
|
|
BinaryDiv: "/",
|
|
BinaryMod: "%",
|
|
BinaryLshift: "<<",
|
|
BinaryRshift: ">>",
|
|
BinaryBand: "&",
|
|
BinaryBclear: "&^",
|
|
BinaryBor: "|",
|
|
BinaryBxor: "^",
|
|
BinaryAdd: "+",
|
|
BinarySub: "-",
|
|
BinaryEq: "==",
|
|
BinaryNeq: "!=",
|
|
BinaryLt: "<",
|
|
BinaryLe: "<=",
|
|
BinaryGt: ">",
|
|
BinaryGe: ">=",
|
|
BinaryAnd: "&&",
|
|
BinaryOr: "||",
|
|
}
|
|
|
|
func (t BinaryType) String() string {
|
|
name, ok := binaryTypes[t]
|
|
if ok {
|
|
return name
|
|
}
|
|
return fmt.Sprintf("{BinaryType %d}", t)
|
|
}
|
|
|
|
// Binary implements an AST binary expression.
|
|
type Binary struct {
|
|
utils.Point
|
|
Left AST
|
|
Op BinaryType
|
|
Right AST
|
|
}
|
|
|
|
func (ast *Binary) String() string {
|
|
return fmt.Sprintf("%s %s %s", ast.Left, ast.Op, ast.Right)
|
|
}
|
|
|
|
// UnaryType defines unary expression types.
|
|
type UnaryType int
|
|
|
|
// Unary expression types.
|
|
const (
|
|
UnaryPlus UnaryType = iota
|
|
UnaryMinus
|
|
UnaryNot
|
|
UnaryXor
|
|
UnaryPtr
|
|
UnaryAddr
|
|
UnarySend
|
|
)
|
|
|
|
var unaryTypes = map[UnaryType]string{
|
|
UnaryPlus: "+",
|
|
UnaryMinus: "-",
|
|
UnaryNot: "!",
|
|
UnaryXor: "^",
|
|
UnaryPtr: "*",
|
|
UnaryAddr: "&",
|
|
UnarySend: "<-",
|
|
}
|
|
|
|
func (t UnaryType) String() string {
|
|
name, ok := unaryTypes[t]
|
|
if ok {
|
|
return name
|
|
}
|
|
return fmt.Sprintf("{UnaryType %d}", t)
|
|
}
|
|
|
|
// Unary implements an AST unary expression.
|
|
type Unary struct {
|
|
utils.Point
|
|
Type UnaryType
|
|
Expr AST
|
|
}
|
|
|
|
func (ast *Unary) String() string {
|
|
return fmt.Sprintf("%s%s", ast.Type, ast.Expr)
|
|
}
|
|
|
|
// Slice implements an AST slice expression.
|
|
type Slice struct {
|
|
utils.Point
|
|
Expr AST
|
|
From AST
|
|
To AST
|
|
}
|
|
|
|
func (ast *Slice) String() string {
|
|
var fromStr, toStr string
|
|
if ast.From != nil {
|
|
fromStr = ast.From.String()
|
|
}
|
|
if ast.To != nil {
|
|
toStr = ast.To.String()
|
|
}
|
|
return fmt.Sprintf("%s[%s:%s]", ast.Expr, fromStr, toStr)
|
|
}
|
|
|
|
// Index implements an AST array index expression.
|
|
type Index struct {
|
|
utils.Point
|
|
Expr AST
|
|
Index AST
|
|
}
|
|
|
|
func (ast *Index) String() string {
|
|
return fmt.Sprintf("%s[%s]", ast.Expr, ast.Index)
|
|
}
|
|
|
|
// VariableRef implements an AST variable reference.
|
|
type VariableRef struct {
|
|
utils.Point
|
|
Name Identifier
|
|
}
|
|
|
|
func (ast *VariableRef) String() string {
|
|
return ast.Name.String()
|
|
}
|
|
|
|
// BasicLit implements an AST basic literal value.
|
|
type BasicLit struct {
|
|
utils.Point
|
|
Value interface{}
|
|
}
|
|
|
|
func (ast *BasicLit) String() string {
|
|
return ConstantName(ast.Value)
|
|
}
|
|
|
|
// ConstantName returns the name of the constant value.
|
|
func ConstantName(value interface{}) string {
|
|
switch val := value.(type) {
|
|
case int, uint, int32, uint32, int64, uint64:
|
|
return fmt.Sprintf("%d", val)
|
|
case *mpa.Int:
|
|
return fmt.Sprintf("%s", val)
|
|
case bool:
|
|
return fmt.Sprintf("%v", val)
|
|
case string:
|
|
return fmt.Sprintf("%q", val)
|
|
default:
|
|
return fmt.Sprintf("{undefined constant %v (%T)}", val, val)
|
|
}
|
|
}
|
|
|
|
// CompositeLit implements an AST composite literal value.
|
|
type CompositeLit struct {
|
|
utils.Point
|
|
Type *TypeInfo
|
|
Value []KeyedElement
|
|
}
|
|
|
|
func (ast *CompositeLit) String() string {
|
|
str := ast.Type.String()
|
|
str += "{"
|
|
|
|
for idx, e := range ast.Value {
|
|
if idx > 0 {
|
|
str += ","
|
|
}
|
|
if e.Key != nil {
|
|
str += fmt.Sprintf("%s: %s", e.Key, e.Element)
|
|
} else {
|
|
str += e.Element.String()
|
|
}
|
|
}
|
|
return str + "}"
|
|
}
|
|
|
|
// KeyedElement implements a keyed element of composite literal.
|
|
type KeyedElement struct {
|
|
Key AST
|
|
Element AST
|
|
}
|
|
|
|
// Make implements the builtin function make.
|
|
type Make struct {
|
|
utils.Point
|
|
Type *TypeInfo
|
|
Exprs []AST
|
|
}
|
|
|
|
func (ast *Make) String() string {
|
|
str := fmt.Sprintf("make(%s", ast.Type)
|
|
for _, expr := range ast.Exprs {
|
|
str += ", "
|
|
str += expr.String()
|
|
}
|
|
return str + ")"
|
|
}
|
|
|
|
// Copy implements the builtin function copy.
|
|
type Copy struct {
|
|
utils.Point
|
|
Dst AST
|
|
Src AST
|
|
}
|
|
|
|
func (ast *Copy) String() string {
|
|
return fmt.Sprintf("copy(%v, %v)", ast.Dst, ast.Src)
|
|
}
|