1package main
2
3import (
4	"bufio"
5	"bytes"
6	"fmt"
7)
8
9type TypeClass int
10
11const (
12	ClassBoolean TypeClass = iota
13	ClassNumeric
14	ClassComplex
15	ClassString
16	ClassArray
17	ClassSlice
18	ClassStruct
19	ClassPointer
20	ClassFunction
21	ClassInterface
22	ClassMap
23	ClassChan
24
25	TraitAny
26	TraitOrdered
27	TraitComparable
28	TraitIndexable
29	TraitReceivable
30	TraitSendable
31	TraitHashable
32	TraitPrintable
33	TraitLenCapable
34	TraitGlobal
35)
36
37type Type struct {
38	id             string
39	class          TypeClass
40	namedUserType  bool
41	ktyp           *Type   // map key, chan elem, array elem, slice elem, pointee type
42	vtyp           *Type   // map val
43	utyp           *Type   // underlying type
44	styp           []*Type // function arguments
45	rtyp           []*Type // function return values
46	elems          []*Var  // struct fileds and interface methods
47	literal        func() string
48	complexLiteral func() string
49
50	// TODO: cache types
51	// pointerTo *Type
52}
53
54func (smith *Smith) initTypes() {
55	smith.predefinedTypes = []*Type{
56		{id: "string", class: ClassString, literal: func() string { return "\"foo\"" }},
57		{id: "bool", class: ClassBoolean, literal: func() string { return "false" }},
58		{id: "int", class: ClassNumeric, literal: func() string { return "1" }},
59		{id: "byte", class: ClassNumeric, literal: func() string { return "byte(0)" }},
60		{id: "interface{}", class: ClassInterface, literal: func() string { return "interface{}(nil)" }},
61		{id: "rune", class: ClassNumeric, literal: func() string { return "rune(0)" }},
62		{id: "float32", class: ClassNumeric, literal: func() string { return "float32(1.0)" }},
63		{id: "float64", class: ClassNumeric, literal: func() string { return "1.0" }},
64		{id: "complex64", class: ClassComplex, literal: func() string { return "complex64(1i)" }},
65		{id: "complex128", class: ClassComplex, literal: func() string { return "1i" }},
66
67		{id: "uint", class: ClassNumeric, literal: func() string { return "uint(1)" }},
68		{id: "uintptr", class: ClassNumeric, literal: func() string { return "uintptr(0)" }},
69		{id: "int16", class: ClassNumeric, literal: func() string { return "int16(1)" }},
70		{id: "error", class: ClassInterface, literal: func() string { return "error(nil)" }},
71	}
72	for _, t := range smith.predefinedTypes {
73		t.utyp = t
74	}
75
76	smith.stringType = smith.predefinedTypes[0]
77	smith.boolType = smith.predefinedTypes[1]
78	smith.intType = smith.predefinedTypes[2]
79	smith.byteType = smith.predefinedTypes[3]
80	smith.efaceType = smith.predefinedTypes[4]
81	smith.runeType = smith.predefinedTypes[5]
82	smith.float32Type = smith.predefinedTypes[6]
83	smith.float64Type = smith.predefinedTypes[7]
84	smith.complex64Type = smith.predefinedTypes[8]
85	smith.complex128Type = smith.predefinedTypes[9]
86
87	smith.stringType.complexLiteral = func() string {
88		if smith.rndBool() {
89			return `"ab\x0acd"`
90		}
91		return "`abc\\x0acd`"
92	}
93}
94
95func fmtTypeList(list []*Type, parens bool) string {
96	var buf bytes.Buffer
97	if parens || len(list) > 1 {
98		buf.Write([]byte{'('})
99	}
100	for i, t := range list {
101		if i != 0 {
102			buf.Write([]byte{','})
103		}
104		fmt.Fprintf(&buf, "%v", t.id)
105	}
106	if parens || len(list) > 1 {
107		buf.Write([]byte{')'})
108	}
109	return buf.String()
110}
111
112func (smith *Smith) atype(trait TypeClass) *Type {
113	smith.typeDepth++
114	defer func() {
115		smith.typeDepth--
116	}()
117	for {
118		if smith.typeDepth >= 3 || smith.rndBool() {
119			var cand []*Type
120			for _, t := range smith.types() {
121				if smith.satisfiesTrait(t, trait) {
122					cand = append(cand, t)
123				}
124			}
125			if len(cand) > 0 {
126				return cand[smith.rnd(len(cand))]
127			}
128		}
129		t := smith.typeLit()
130		if t != nil && smith.satisfiesTrait(t, trait) {
131			return t
132		}
133	}
134}
135
136func (smith *Smith) typeLit() *Type {
137	switch smith.choice("array", "chan", "struct", "pointer", "interface", "slice", "function", "map") {
138	case "array":
139		return smith.arrayOf(smith.atype(TraitAny))
140	case "chan":
141		return smith.chanOf(smith.atype(TraitAny))
142	case "struct":
143		var elems []*Var
144		var buf bytes.Buffer
145		fmt.Fprintf(&buf, "struct { ")
146		for smith.rndBool() {
147			e := &Var{id: smith.newId("Field"), typ: smith.atype(TraitAny)}
148			elems = append(elems, e)
149			fmt.Fprintf(&buf, "%v %v\n", e.id, e.typ.id)
150		}
151		fmt.Fprintf(&buf, "}")
152		id := buf.String()
153		return &Type{
154			id:    id,
155			class: ClassStruct,
156			elems: elems,
157			literal: func() string {
158				return F("(%v{})", id)
159			},
160			complexLiteral: func() string {
161				if smith.rndBool() {
162					// unnamed
163					var buf bytes.Buffer
164					fmt.Fprintf(&buf, "(%v{", id)
165					for i := 0; i < len(elems); i++ {
166						fmt.Fprintf(&buf, "%v, ", smith.rvalue(elems[i].typ))
167					}
168					fmt.Fprintf(&buf, "})")
169					return buf.String()
170				} else {
171					// named
172					var buf bytes.Buffer
173					fmt.Fprintf(&buf, "(%v{", id)
174					for i := 0; i < len(elems); i++ {
175						if smith.rndBool() {
176							fmt.Fprintf(&buf, "%v: %v, ", elems[i].id, smith.rvalue(elems[i].typ))
177						}
178					}
179					fmt.Fprintf(&buf, "})")
180					return buf.String()
181				}
182			},
183		}
184	case "pointer":
185		return pointerTo(smith.atype(TraitAny))
186	case "interface":
187		var buf bytes.Buffer
188		fmt.Fprintf(&buf, "interface { ")
189		for smith.rndBool() {
190			fmt.Fprintf(&buf, " %v %v %v\n", smith.newId("Method"),
191				fmtTypeList(smith.atypeList(TraitAny), true),
192				fmtTypeList(smith.atypeList(TraitAny), false))
193		}
194		fmt.Fprintf(&buf, "}")
195		return &Type{
196			id:    buf.String(),
197			class: ClassInterface,
198			literal: func() string {
199				return F("%v(nil)", buf.String())
200			},
201		}
202	case "slice":
203		return smith.sliceOf(smith.atype(TraitAny))
204	case "function":
205		return smith.funcOf(smith.atypeList(TraitAny), smith.atypeList(TraitAny))
206	case "map":
207		ktyp := smith.atype(TraitHashable)
208		vtyp := smith.atype(TraitAny)
209		return &Type{
210			id:    F("map[%v]%v", ktyp.id, vtyp.id),
211			class: ClassMap,
212			ktyp:  ktyp,
213			vtyp:  vtyp,
214			literal: func() string {
215				if smith.rndBool() {
216					cap := ""
217					if smith.rndBool() {
218						cap = "," + smith.rvalue(smith.intType)
219					}
220					return F("make(map[%v]%v %v)", ktyp.id, vtyp.id, cap)
221				} else {
222					return F("map[%v]%v{}", ktyp.id, vtyp.id)
223				}
224			},
225		}
226	default:
227		panic("bad")
228	}
229}
230
231func (smith *Smith) satisfiesTrait(t *Type, trait TypeClass) bool {
232	if trait < TraitAny {
233		return t.class == trait
234	}
235
236	switch trait {
237	case TraitAny:
238		return true
239	case TraitOrdered:
240		return t.class == ClassNumeric || t.class == ClassString
241	case TraitComparable:
242		return t.class == ClassBoolean || t.class == ClassNumeric || t.class == ClassString ||
243			t.class == ClassPointer || t.class == ClassChan || t.class == ClassInterface
244	case TraitIndexable:
245		return t.class == ClassArray || t.class == ClassSlice || t.class == ClassString ||
246			t.class == ClassMap
247	case TraitReceivable:
248		return t.class == ClassChan
249	case TraitSendable:
250		return t.class == ClassChan
251	case TraitHashable:
252		if t.class == ClassFunction || t.class == ClassMap || t.class == ClassSlice {
253			return false
254		}
255		if t.class == ClassArray && !smith.satisfiesTrait(t.ktyp, TraitHashable) {
256			return false
257		}
258		if t.class == ClassStruct {
259			for _, e := range t.elems {
260				if !smith.satisfiesTrait(e.typ, TraitHashable) {
261					return false
262				}
263			}
264		}
265		return true
266	case TraitPrintable:
267		return t.class == ClassBoolean || t.class == ClassNumeric || t.class == ClassString ||
268			t.class == ClassPointer || t.class == ClassInterface
269	case TraitLenCapable:
270		return t.class == ClassString || t.class == ClassSlice || t.class == ClassArray ||
271			t.class == ClassMap || t.class == ClassChan
272	case TraitGlobal:
273		for _, t1 := range smith.predefinedTypes {
274			if t == t1 {
275				return true
276			}
277		}
278		return false
279	default:
280		panic("bad")
281	}
282}
283
284func (smith *Smith) atypeList(trait TypeClass) []*Type {
285	n := smith.rnd(4) + 1
286	list := make([]*Type, n)
287	for i := 0; i < n; i++ {
288		list[i] = smith.atype(trait)
289	}
290	return list
291}
292
293func typeList(t *Type, n int) []*Type {
294	list := make([]*Type, n)
295	for i := 0; i < n; i++ {
296		list[i] = t
297	}
298	return list
299}
300
301func pointerTo(elem *Type) *Type {
302	return &Type{
303		id:    F("*%v", elem.id),
304		class: ClassPointer,
305		ktyp:  elem,
306		literal: func() string {
307			return F("(*%v)(nil)", elem.id)
308		}}
309}
310
311func (smith *Smith) chanOf(elem *Type) *Type {
312	return &Type{
313		id:    F("chan %v", elem.id),
314		class: ClassChan,
315		ktyp:  elem,
316		literal: func() string {
317			cap := ""
318			if smith.rndBool() {
319				cap = "," + smith.rvalue(smith.intType)
320			}
321			return F("make(chan %v %v)", elem.id, cap)
322		},
323	}
324}
325
326func (smith *Smith) sliceOf(elem *Type) *Type {
327	return &Type{
328		id:    F("[]%v", elem.id),
329		class: ClassSlice,
330		ktyp:  elem,
331		literal: func() string {
332			return F("[]%v{}", elem.id)
333		},
334		complexLiteral: func() string {
335			switch smith.choice("normal", "keyed") {
336			case "normal":
337				return F("[]%v{%v}", elem.id, smith.fmtRvalueList(typeList(elem, smith.rnd(3))))
338			case "keyed":
339				n := smith.rnd(3)
340				var indexes []int
341			loop:
342				for len(indexes) < n {
343					i := smith.rnd(10)
344					for _, i1 := range indexes {
345						if i1 == i {
346							continue loop
347						}
348					}
349					indexes = append(indexes, i)
350				}
351				var buf bytes.Buffer
352				fmt.Fprintf(&buf, "[]%v{", elem.id)
353				for i, idx := range indexes {
354					if i != 0 {
355						fmt.Fprintf(&buf, ",")
356					}
357					fmt.Fprintf(&buf, "%v: %v", idx, smith.rvalue(elem))
358				}
359				fmt.Fprintf(&buf, "}")
360				return buf.String()
361			default:
362				panic("bad")
363			}
364		},
365	}
366}
367
368func (smith *Smith) arrayOf(elem *Type) *Type {
369	size := smith.rnd(3)
370	return &Type{
371		id:    F("[%v]%v", size, elem.id),
372		class: ClassArray,
373		ktyp:  elem,
374		literal: func() string {
375			return F("[%v]%v{}", size, elem.id)
376		},
377		complexLiteral: func() string {
378			switch smith.choice("normal", "keyed") {
379			case "normal":
380				return F("[%v]%v{%v}", smith.choice(F("%v", size), "..."), elem.id, smith.fmtRvalueList(typeList(elem, size)))
381			case "keyed":
382				var buf bytes.Buffer
383				fmt.Fprintf(&buf, "[%v]%v{", size, elem.id)
384				for i := 0; i < size; i++ {
385					if i != 0 {
386						fmt.Fprintf(&buf, ",")
387					}
388					fmt.Fprintf(&buf, "%v: %v", i, smith.rvalue(elem))
389				}
390				fmt.Fprintf(&buf, "}")
391				return buf.String()
392			default:
393				panic("bad")
394			}
395		},
396	}
397}
398
399func (smith *Smith) funcOf(alist, rlist []*Type) *Type {
400	t := &Type{
401		id:    F("func%v %v", fmtTypeList(alist, true), fmtTypeList(rlist, false)),
402		class: ClassFunction,
403		styp:  alist,
404		rtyp:  rlist,
405	}
406	t.literal = func() string {
407		return F("((func%v %v)(nil))", fmtTypeList(alist, true), fmtTypeList(rlist, false))
408	}
409	t.complexLiteral = func() string {
410		return smith.genFuncLit(t)
411	}
412	return t
413}
414
415func (smith *Smith) genFuncLit(ft *Type) string {
416	//return F("((func%v %v)(nil))", fmtTypeList(ft.styp, true), fmtTypeList(ft.rtyp, false))
417
418	if smith.curBlockPos == -1 {
419		smith.line("")
420	}
421
422	f := &Func{args: ft.styp, rets: ft.rtyp}
423	curFunc0 := smith.curFunc
424	smith.curFunc = f
425	curBlock0 := smith.curBlock
426	curBlockPos0 := smith.curBlockPos
427	curBlockLen0 := len(smith.curBlock.sub)
428	exprDepth0 := smith.exprDepth
429	exprCount0 := smith.exprCount
430	smith.exprDepth = 0
431	smith.exprCount = 0
432	defer func() {
433		smith.curBlock = curBlock0
434		smith.curFunc = curFunc0
435		smith.exprDepth = exprDepth0
436		smith.exprCount = exprCount0
437		smith.curBlockPos = curBlockPos0 + (len(smith.curBlock.sub) - curBlockLen0)
438	}()
439
440	fb := &Block{parent: smith.curBlock, subBlock: smith.curBlock.sub[smith.curBlockPos]}
441	smith.curBlock = fb
442	smith.enterBlock(true)
443	smith.enterBlock(true)
444	argIds := make([]string, len(f.args))
445	argStr := ""
446	for i, a := range f.args {
447		argIds[i] = smith.newId("Param")
448		if i != 0 {
449			argStr += ", "
450		}
451		argStr += argIds[i] + " " + a.id
452	}
453	smith.line("func(%v)%v {", argStr, fmtTypeList(f.rets, false))
454	for i, a := range f.args {
455		smith.defineVar(argIds[i], a)
456	}
457	smith.curBlock.funcBoundary = true
458	smith.genBlock()
459	smith.leaveBlock()
460	smith.stmtReturn()
461	smith.line("}")
462	smith.leaveBlock()
463
464	//b := curBlock.sub[curBlockPos]
465	//copy(curBlock.sub[curBlockPos:], curBlock.sub[curBlockPos+1:])
466	//curBlock.sub = curBlock.sub[:len(curBlock.sub)-1]
467
468	var buf bytes.Buffer
469	w := bufio.NewWriter(&buf)
470	serializeBlock(w, fb, 0)
471	w.Flush()
472	s := buf.String()
473	//fmt.Printf("GEN FUNC:\n%v\n", s)
474	return s[:len(s)-1]
475}
476
477func dependsOn(t, t0 *Type) bool {
478	if t == nil {
479		return false
480	}
481	if t.class == ClassInterface {
482		// We don't know how to walk all types referenced by an interface yet.
483		return true
484	}
485	if t0 == nil && t.namedUserType {
486		return true
487	}
488	if t == t0 {
489		return true
490	}
491	if dependsOn(t.ktyp, t0) {
492		return true
493	}
494	if dependsOn(t.vtyp, t0) {
495		return true
496	}
497	if dependsOn(t.ktyp, t0) {
498		return true
499	}
500	for _, t1 := range t.styp {
501		if dependsOn(t1, t0) {
502			return true
503		}
504	}
505	for _, t1 := range t.rtyp {
506		if dependsOn(t1, t0) {
507			return true
508		}
509	}
510	for _, e := range t.elems {
511		if dependsOn(e.typ, t0) {
512			return true
513		}
514	}
515	return false
516}
517