package parser
import (
"reflect"
"github.com/marzeq/qk/symbols"
"github.com/marzeq/qk/types"
)
var (
typeInterface = reflect.TypeOf((*types.Type)(nil)).Elem()
nodeInterface = reflect.TypeOf((*Node)(nil)).Elem()
symbolPointer = reflect.TypeOf((*symbols.Symbol)(nil))
)
// CloneSyntax makes an independent copy of an AST node while discarding all
// semantic information attached by analysis.
func CloneSyntax(node Node) Node {
if node == nil {
return nil
}
return cloneSyntaxValue(reflect.ValueOf(node), make(map[clonePointer]reflect.Value)).Interface().(Node)
}
// CloneSemantic copies an attributed AST while allowing declaration-scoped
// types and symbols to be replaced for a generic instantiation.
func CloneSemantic(
node Node,
substituteType func(types.Type) types.Type,
mapSymbol func(*symbols.Symbol) *symbols.Symbol,
postprocess func(Node),
) Node {
if node == nil {
return nil
}
return cloneSemanticValue(
reflect.ValueOf(node),
make(map[clonePointer]reflect.Value),
substituteType,
mapSymbol,
postprocess,
).Interface().(Node)
}
type clonePointer struct {
typ reflect.Type
ptr uintptr
}
func cloneSyntaxValue(value reflect.Value, pointers map[clonePointer]reflect.Value) reflect.Value {
if !value.IsValid() {
return value
}
if value.Type() == symbolPointer {
return reflect.Zero(value.Type())
}
if value.Type().Implements(typeInterface) {
return reflect.Zero(value.Type())
}
if value.Kind() == reflect.Interface {
if value.IsNil() {
return reflect.Zero(value.Type())
}
if value.Elem().Type().Implements(typeInterface) {
return reflect.Zero(value.Type())
}
cloned := cloneSyntaxValue(value.Elem(), pointers)
result := reflect.New(value.Type()).Elem()
result.Set(cloned)
return result
}
switch value.Kind() {
case reflect.Pointer:
if value.IsNil() {
return reflect.Zero(value.Type())
}
key := clonePointer{typ: value.Type(), ptr: value.Pointer()}
if existing, ok := pointers[key]; ok {
return existing
}
result := reflect.New(value.Type().Elem())
pointers[key] = result
result.Elem().Set(cloneSyntaxValue(value.Elem(), pointers))
return result
case reflect.Struct:
result := reflect.New(value.Type()).Elem()
for i := 0; i < value.NumField(); i++ {
result.Field(i).Set(cloneSyntaxValue(value.Field(i), pointers))
}
return result
case reflect.Slice:
if value.IsNil() {
return reflect.Zero(value.Type())
}
result := reflect.MakeSlice(value.Type(), value.Len(), value.Len())
for i := 0; i < value.Len(); i++ {
result.Index(i).Set(cloneSyntaxValue(value.Index(i), pointers))
}
return result
case reflect.Map:
if value.IsNil() {
return reflect.Zero(value.Type())
}
result := reflect.MakeMapWithSize(value.Type(), value.Len())
iter := value.MapRange()
for iter.Next() {
result.SetMapIndex(cloneSyntaxValue(iter.Key(), pointers), cloneSyntaxValue(iter.Value(), pointers))
}
return result
default:
return value
}
}
func cloneSemanticValue(
value reflect.Value,
pointers map[clonePointer]reflect.Value,
substituteType func(types.Type) types.Type,
mapSymbol func(*symbols.Symbol) *symbols.Symbol,
postprocess func(Node),
) reflect.Value {
if !value.IsValid() {
return value
}
if value.Type() == symbolPointer {
if value.IsNil() {
return reflect.Zero(value.Type())
}
return reflect.ValueOf(mapSymbol(value.Interface().(*symbols.Symbol)))
}
if value.Type().Implements(typeInterface) {
if value.Kind() == reflect.Interface && value.IsNil() {
return reflect.Zero(value.Type())
}
substituted := substituteType(value.Interface().(types.Type))
if substituted == nil {
return reflect.Zero(value.Type())
}
result := reflect.ValueOf(substituted)
if result.Type().AssignableTo(value.Type()) {
return result
}
if value.Type().Kind() == reflect.Interface && result.Type().Implements(value.Type()) {
wrapped := reflect.New(value.Type()).Elem()
wrapped.Set(result)
return wrapped
}
panic("semantic type substitution changed a concrete field's type")
}
if value.Kind() == reflect.Interface {
if value.IsNil() {
return reflect.Zero(value.Type())
}
cloned := cloneSemanticValue(value.Elem(), pointers, substituteType, mapSymbol, postprocess)
result := reflect.New(value.Type()).Elem()
result.Set(cloned)
return result
}
switch value.Kind() {
case reflect.Pointer:
if value.IsNil() {
return reflect.Zero(value.Type())
}
key := clonePointer{typ: value.Type(), ptr: value.Pointer()}
if existing, ok := pointers[key]; ok {
return existing
}
result := reflect.New(value.Type().Elem())
pointers[key] = result
result.Elem().Set(cloneSemanticValue(value.Elem(), pointers, substituteType, mapSymbol, postprocess))
if postprocess != nil && result.Type().Implements(nodeInterface) {
postprocess(result.Interface().(Node))
}
return result
case reflect.Struct:
result := reflect.New(value.Type()).Elem()
for i := 0; i < value.NumField(); i++ {
result.Field(i).Set(cloneSemanticValue(value.Field(i), pointers, substituteType, mapSymbol, postprocess))
}
return result
case reflect.Slice:
if value.IsNil() {
return reflect.Zero(value.Type())
}
result := reflect.MakeSlice(value.Type(), value.Len(), value.Len())
for i := 0; i < value.Len(); i++ {
result.Index(i).Set(cloneSemanticValue(value.Index(i), pointers, substituteType, mapSymbol, postprocess))
}
return result
case reflect.Map:
if value.IsNil() {
return reflect.Zero(value.Type())
}
result := reflect.MakeMapWithSize(value.Type(), value.Len())
iter := value.MapRange()
for iter.Next() {
result.SetMapIndex(
cloneSemanticValue(iter.Key(), pointers, substituteType, mapSymbol, postprocess),
cloneSemanticValue(iter.Value(), pointers, substituteType, mapSymbol, postprocess),
)
}
return result
default:
return value
}
}