--- /dev/null
+package main
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+)
+
+type TypeClass int
+
+const (
+ ClassBoolean TypeClass = iota
+ ClassNumeric
+ ClassComplex
+ ClassString
+ ClassArray
+ ClassSlice
+ ClassStruct
+ ClassPointer
+ ClassFunction
+ ClassInterface
+ ClassMap
+ ClassChan
+
+ TraitAny
+ TraitOrdered
+ TraitComparable
+ TraitIndexable
+ TraitReceivable
+ TraitSendable
+ TraitHashable
+ TraitPrintable
+ TraitLenCapable
+ TraitGlobal
+)
+
+type Type struct {
+ id string
+ class TypeClass
+ namedUserType bool
+ ktyp *Type // map key, chan elem, array elem, slice elem, pointee type
+ vtyp *Type // map val
+ utyp *Type // underlying type
+ styp []*Type // function arguments
+ rtyp []*Type // function return values
+ elems []*Var // struct fileds and interface methods
+ literal func() string
+ complexLiteral func() string
+
+ // TODO: cache types
+ // pointerTo *Type
+}
+
+func (smith *Smith) initTypes() {
+ smith.predefinedTypes = []*Type{
+ {id: "string", class: ClassString, literal: func() string { return "\"foo\"" }},
+ {id: "bool", class: ClassBoolean, literal: func() string { return "false" }},
+ {id: "int", class: ClassNumeric, literal: func() string { return "1" }},
+ {id: "byte", class: ClassNumeric, literal: func() string { return "byte(0)" }},
+ {id: "interface{}", class: ClassInterface, literal: func() string { return "interface{}(nil)" }},
+ {id: "rune", class: ClassNumeric, literal: func() string { return "rune(0)" }},
+ {id: "float32", class: ClassNumeric, literal: func() string { return "float32(1.0)" }},
+ {id: "float64", class: ClassNumeric, literal: func() string { return "1.0" }},
+ {id: "complex64", class: ClassComplex, literal: func() string { return "complex64(1i)" }},
+ {id: "complex128", class: ClassComplex, literal: func() string { return "1i" }},
+
+ {id: "uint", class: ClassNumeric, literal: func() string { return "uint(1)" }},
+ {id: "uintptr", class: ClassNumeric, literal: func() string { return "uintptr(0)" }},
+ {id: "int16", class: ClassNumeric, literal: func() string { return "int16(1)" }},
+ {id: "error", class: ClassInterface, literal: func() string { return "error(nil)" }},
+ }
+ for _, t := range smith.predefinedTypes {
+ t.utyp = t
+ }
+
+ smith.stringType = smith.predefinedTypes[0]
+ smith.boolType = smith.predefinedTypes[1]
+ smith.intType = smith.predefinedTypes[2]
+ smith.byteType = smith.predefinedTypes[3]
+ smith.efaceType = smith.predefinedTypes[4]
+ smith.runeType = smith.predefinedTypes[5]
+ smith.float32Type = smith.predefinedTypes[6]
+ smith.float64Type = smith.predefinedTypes[7]
+ smith.complex64Type = smith.predefinedTypes[8]
+ smith.complex128Type = smith.predefinedTypes[9]
+
+ smith.stringType.complexLiteral = func() string {
+ if smith.rndBool() {
+ return `"ab\x0acd"`
+ }
+ return "`abc\\x0acd`"
+ }
+}
+
+func fmtTypeList(list []*Type, parens bool) string {
+ var buf bytes.Buffer
+ if parens || len(list) > 1 {
+ buf.Write([]byte{'('})
+ }
+ for i, t := range list {
+ if i != 0 {
+ buf.Write([]byte{','})
+ }
+ fmt.Fprintf(&buf, "%v", t.id)
+ }
+ if parens || len(list) > 1 {
+ buf.Write([]byte{')'})
+ }
+ return buf.String()
+}
+
+func (smith *Smith) atype(trait TypeClass) *Type {
+ smith.typeDepth++
+ defer func() {
+ smith.typeDepth--
+ }()
+ for {
+ if smith.typeDepth >= 3 || smith.rndBool() {
+ var cand []*Type
+ for _, t := range smith.types() {
+ if smith.satisfiesTrait(t, trait) {
+ cand = append(cand, t)
+ }
+ }
+ if len(cand) > 0 {
+ return cand[smith.rnd(len(cand))]
+ }
+ }
+ t := smith.typeLit()
+ if t != nil && smith.satisfiesTrait(t, trait) {
+ return t
+ }
+ }
+}
+
+func (smith *Smith) typeLit() *Type {
+ switch smith.choice("array", "chan", "struct", "pointer", "interface", "slice", "function", "map") {
+ case "array":
+ return smith.arrayOf(smith.atype(TraitAny))
+ case "chan":
+ return smith.chanOf(smith.atype(TraitAny))
+ case "struct":
+ var elems []*Var
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "struct { ")
+ for smith.rndBool() {
+ e := &Var{id: smith.newId("Field"), typ: smith.atype(TraitAny)}
+ elems = append(elems, e)
+ fmt.Fprintf(&buf, "%v %v\n", e.id, e.typ.id)
+ }
+ fmt.Fprintf(&buf, "}")
+ id := buf.String()
+ return &Type{
+ id: id,
+ class: ClassStruct,
+ elems: elems,
+ literal: func() string {
+ return F("(%v{})", id)
+ },
+ complexLiteral: func() string {
+ if smith.rndBool() {
+ // unnamed
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "(%v{", id)
+ for i := 0; i < len(elems); i++ {
+ fmt.Fprintf(&buf, "%v, ", smith.rvalue(elems[i].typ))
+ }
+ fmt.Fprintf(&buf, "})")
+ return buf.String()
+ } else {
+ // named
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "(%v{", id)
+ for i := 0; i < len(elems); i++ {
+ if smith.rndBool() {
+ fmt.Fprintf(&buf, "%v: %v, ", elems[i].id, smith.rvalue(elems[i].typ))
+ }
+ }
+ fmt.Fprintf(&buf, "})")
+ return buf.String()
+ }
+ },
+ }
+ case "pointer":
+ return pointerTo(smith.atype(TraitAny))
+ case "interface":
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "interface { ")
+ for smith.rndBool() {
+ fmt.Fprintf(&buf, " %v %v %v\n", smith.newId("Method"),
+ fmtTypeList(smith.atypeList(TraitAny), true),
+ fmtTypeList(smith.atypeList(TraitAny), false))
+ }
+ fmt.Fprintf(&buf, "}")
+ return &Type{
+ id: buf.String(),
+ class: ClassInterface,
+ literal: func() string {
+ return F("%v(nil)", buf.String())
+ },
+ }
+ case "slice":
+ return smith.sliceOf(smith.atype(TraitAny))
+ case "function":
+ return smith.funcOf(smith.atypeList(TraitAny), smith.atypeList(TraitAny))
+ case "map":
+ ktyp := smith.atype(TraitHashable)
+ vtyp := smith.atype(TraitAny)
+ return &Type{
+ id: F("map[%v]%v", ktyp.id, vtyp.id),
+ class: ClassMap,
+ ktyp: ktyp,
+ vtyp: vtyp,
+ literal: func() string {
+ if smith.rndBool() {
+ cap := ""
+ if smith.rndBool() {
+ cap = "," + smith.rvalue(smith.intType)
+ }
+ return F("make(map[%v]%v %v)", ktyp.id, vtyp.id, cap)
+ } else {
+ return F("map[%v]%v{}", ktyp.id, vtyp.id)
+ }
+ },
+ }
+ default:
+ panic("bad")
+ }
+}
+
+func (smith *Smith) satisfiesTrait(t *Type, trait TypeClass) bool {
+ if trait < TraitAny {
+ return t.class == trait
+ }
+
+ switch trait {
+ case TraitAny:
+ return true
+ case TraitOrdered:
+ return t.class == ClassNumeric || t.class == ClassString
+ case TraitComparable:
+ return t.class == ClassBoolean || t.class == ClassNumeric || t.class == ClassString ||
+ t.class == ClassPointer || t.class == ClassChan || t.class == ClassInterface
+ case TraitIndexable:
+ return t.class == ClassArray || t.class == ClassSlice || t.class == ClassString ||
+ t.class == ClassMap
+ case TraitReceivable:
+ return t.class == ClassChan
+ case TraitSendable:
+ return t.class == ClassChan
+ case TraitHashable:
+ if t.class == ClassFunction || t.class == ClassMap || t.class == ClassSlice {
+ return false
+ }
+ if t.class == ClassArray && !smith.satisfiesTrait(t.ktyp, TraitHashable) {
+ return false
+ }
+ if t.class == ClassStruct {
+ for _, e := range t.elems {
+ if !smith.satisfiesTrait(e.typ, TraitHashable) {
+ return false
+ }
+ }
+ }
+ return true
+ case TraitPrintable:
+ return t.class == ClassBoolean || t.class == ClassNumeric || t.class == ClassString ||
+ t.class == ClassPointer || t.class == ClassInterface
+ case TraitLenCapable:
+ return t.class == ClassString || t.class == ClassSlice || t.class == ClassArray ||
+ t.class == ClassMap || t.class == ClassChan
+ case TraitGlobal:
+ for _, t1 := range smith.predefinedTypes {
+ if t == t1 {
+ return true
+ }
+ }
+ return false
+ default:
+ panic("bad")
+ }
+}
+
+func (smith *Smith) atypeList(trait TypeClass) []*Type {
+ n := smith.rnd(4) + 1
+ list := make([]*Type, n)
+ for i := 0; i < n; i++ {
+ list[i] = smith.atype(trait)
+ }
+ return list
+}
+
+func typeList(t *Type, n int) []*Type {
+ list := make([]*Type, n)
+ for i := 0; i < n; i++ {
+ list[i] = t
+ }
+ return list
+}
+
+func pointerTo(elem *Type) *Type {
+ return &Type{
+ id: F("*%v", elem.id),
+ class: ClassPointer,
+ ktyp: elem,
+ literal: func() string {
+ return F("(*%v)(nil)", elem.id)
+ }}
+}
+
+func (smith *Smith) chanOf(elem *Type) *Type {
+ return &Type{
+ id: F("chan %v", elem.id),
+ class: ClassChan,
+ ktyp: elem,
+ literal: func() string {
+ cap := ""
+ if smith.rndBool() {
+ cap = "," + smith.rvalue(smith.intType)
+ }
+ return F("make(chan %v %v)", elem.id, cap)
+ },
+ }
+}
+
+func (smith *Smith) sliceOf(elem *Type) *Type {
+ return &Type{
+ id: F("[]%v", elem.id),
+ class: ClassSlice,
+ ktyp: elem,
+ literal: func() string {
+ return F("[]%v{}", elem.id)
+ },
+ complexLiteral: func() string {
+ switch smith.choice("normal", "keyed") {
+ case "normal":
+ return F("[]%v{%v}", elem.id, smith.fmtRvalueList(typeList(elem, smith.rnd(3))))
+ case "keyed":
+ n := smith.rnd(3)
+ var indexes []int
+ loop:
+ for len(indexes) < n {
+ i := smith.rnd(10)
+ for _, i1 := range indexes {
+ if i1 == i {
+ continue loop
+ }
+ }
+ indexes = append(indexes, i)
+ }
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "[]%v{", elem.id)
+ for i, idx := range indexes {
+ if i != 0 {
+ fmt.Fprintf(&buf, ",")
+ }
+ fmt.Fprintf(&buf, "%v: %v", idx, smith.rvalue(elem))
+ }
+ fmt.Fprintf(&buf, "}")
+ return buf.String()
+ default:
+ panic("bad")
+ }
+ },
+ }
+}
+
+func (smith *Smith) arrayOf(elem *Type) *Type {
+ size := smith.rnd(3)
+ return &Type{
+ id: F("[%v]%v", size, elem.id),
+ class: ClassArray,
+ ktyp: elem,
+ literal: func() string {
+ return F("[%v]%v{}", size, elem.id)
+ },
+ complexLiteral: func() string {
+ switch smith.choice("normal", "keyed") {
+ case "normal":
+ return F("[%v]%v{%v}", smith.choice(F("%v", size), "..."), elem.id, smith.fmtRvalueList(typeList(elem, size)))
+ case "keyed":
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "[%v]%v{", size, elem.id)
+ for i := 0; i < size; i++ {
+ if i != 0 {
+ fmt.Fprintf(&buf, ",")
+ }
+ fmt.Fprintf(&buf, "%v: %v", i, smith.rvalue(elem))
+ }
+ fmt.Fprintf(&buf, "}")
+ return buf.String()
+ default:
+ panic("bad")
+ }
+ },
+ }
+}
+
+func (smith *Smith) funcOf(alist, rlist []*Type) *Type {
+ t := &Type{
+ id: F("func%v %v", fmtTypeList(alist, true), fmtTypeList(rlist, false)),
+ class: ClassFunction,
+ styp: alist,
+ rtyp: rlist,
+ }
+ t.literal = func() string {
+ return F("((func%v %v)(nil))", fmtTypeList(alist, true), fmtTypeList(rlist, false))
+ }
+ t.complexLiteral = func() string {
+ return smith.genFuncLit(t)
+ }
+ return t
+}
+
+func (smith *Smith) genFuncLit(ft *Type) string {
+ //return F("((func%v %v)(nil))", fmtTypeList(ft.styp, true), fmtTypeList(ft.rtyp, false))
+
+ if smith.curBlockPos == -1 {
+ smith.line("")
+ }
+
+ f := &Func{args: ft.styp, rets: ft.rtyp}
+ curFunc0 := smith.curFunc
+ smith.curFunc = f
+ curBlock0 := smith.curBlock
+ curBlockPos0 := smith.curBlockPos
+ curBlockLen0 := len(smith.curBlock.sub)
+ exprDepth0 := smith.exprDepth
+ exprCount0 := smith.exprCount
+ smith.exprDepth = 0
+ smith.exprCount = 0
+ defer func() {
+ smith.curBlock = curBlock0
+ smith.curFunc = curFunc0
+ smith.exprDepth = exprDepth0
+ smith.exprCount = exprCount0
+ smith.curBlockPos = curBlockPos0 + (len(smith.curBlock.sub) - curBlockLen0)
+ }()
+
+ fb := &Block{parent: smith.curBlock, subBlock: smith.curBlock.sub[smith.curBlockPos]}
+ smith.curBlock = fb
+ smith.enterBlock(true)
+ smith.enterBlock(true)
+ argIds := make([]string, len(f.args))
+ argStr := ""
+ for i, a := range f.args {
+ argIds[i] = smith.newId("Param")
+ if i != 0 {
+ argStr += ", "
+ }
+ argStr += argIds[i] + " " + a.id
+ }
+ smith.line("func(%v)%v {", argStr, fmtTypeList(f.rets, false))
+ for i, a := range f.args {
+ smith.defineVar(argIds[i], a)
+ }
+ smith.curBlock.funcBoundary = true
+ smith.genBlock()
+ smith.leaveBlock()
+ smith.stmtReturn()
+ smith.line("}")
+ smith.leaveBlock()
+
+ //b := curBlock.sub[curBlockPos]
+ //copy(curBlock.sub[curBlockPos:], curBlock.sub[curBlockPos+1:])
+ //curBlock.sub = curBlock.sub[:len(curBlock.sub)-1]
+
+ var buf bytes.Buffer
+ w := bufio.NewWriter(&buf)
+ serializeBlock(w, fb, 0)
+ w.Flush()
+ s := buf.String()
+ //fmt.Printf("GEN FUNC:\n%v\n", s)
+ return s[:len(s)-1]
+}
+
+func dependsOn(t, t0 *Type) bool {
+ if t == nil {
+ return false
+ }
+ if t.class == ClassInterface {
+ // We don't know how to walk all types referenced by an interface yet.
+ return true
+ }
+ if t0 == nil && t.namedUserType {
+ return true
+ }
+ if t == t0 {
+ return true
+ }
+ if dependsOn(t.ktyp, t0) {
+ return true
+ }
+ if dependsOn(t.vtyp, t0) {
+ return true
+ }
+ if dependsOn(t.ktyp, t0) {
+ return true
+ }
+ for _, t1 := range t.styp {
+ if dependsOn(t1, t0) {
+ return true
+ }
+ }
+ for _, t1 := range t.rtyp {
+ if dependsOn(t1, t0) {
+ return true
+ }
+ }
+ for _, e := range t.elems {
+ if dependsOn(e.typ, t0) {
+ return true
+ }
+ }
+ return false
+}