<commit_before>// Copyright 2015 The Serulian Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package es5

import (
	"github.com/serulian/compiler/graphs/typegraph"

	"github.com/cevaris/ordered_map"
)

// generateTypes generates all the types under the  given modules into ES5.
func (gen *es5generator) generateTypes(module typegraph.TGModule) *ordered_map.OrderedMap {
	typeMap := ordered_map.NewOrderedMap()
	types := module.Types()
	for _, typedecl := range types {
		typeMap.Set(typedecl, gen.generateType(typedecl))
	}

	return typeMap
}

// generateType generates the given type into ES5.
func (gen *es5generator) generateType(typedef typegraph.TGTypeDecl) string {
	generating := generatingType{typedef, gen}

	switch typedef.TypeKind() {
	case typegraph.ClassType:
		return gen.templater.Execute("class", classTemplateStr, generating)

	case typegraph.ImplicitInterfaceType:
		return gen.templater.Execute("interface", interfaceTemplateStr, generating)

	case typegraph.NominalType:
		return gen.templater.Execute("nominal", nominalTemplateStr, generating)

	case typegraph.ExternalInternalType:
		return ""

	default:
		panic("Unknown typedef kind")
		return ""
	}
}

// generatingType represents a type being generated.
type generatingType struct {
	Type      typegraph.TGTypeDecl
	Generator *es5generator // The parent generator.
}

// Generics returns a string representing the named generics on this type.
func (gt generatingType) Generics() string {
	return gt.Generator.templater.Execute("generics", genericsTemplateStr, gt.Type)
}

// HasGenerics returns whether the type has generics.
func (gt generatingType) HasGenerics() bool {
	return gt.Type.HasGenerics()
}

// GenerateImplementedMembers generates the source for all the members defined under the type that
// have implementations.
func (gt generatingType) GenerateImplementedMembers() *ordered_map.OrderedMap {
	return gt.Generator.generateImplementedMembers(gt.Type)
}

// GenerateVariables generates the source for all the variables defined under the type.
func (gt generatingType) GenerateVariables() *ordered_map.OrderedMap {
	return gt.Generator.generateVariables(gt.Type)
}

// GenerateComposition generates the source for all the composed types structurually inherited by the type.
func (gt generatingType) GenerateComposition() *ordered_map.OrderedMap {
	typeMap := ordered_map.NewOrderedMap()
	parentTypes := gt.Type.ParentTypes()
	for _, parentTypeRef := range parentTypes {
		data := struct {
			ComposedTypeLocation string
			InnerInstanceName    string
		}{
			gt.Generator.pather.TypeReferenceCall(parentTypeRef),
			gt.Generator.pather.InnerInstanceName(parentTypeRef),
		}

		source := gt.Generator.templater.Execute("composition", compositionTemplateStr, data)
		typeMap.Set(parentTypeRef, source)
	}

	return typeMap
}

// compositionTemplateStr defines a template for instantiating a composed type.
const compositionTemplateStr = `
	({{ .ComposedTypeLocation }}).new().then(function(value) {
	  instance.{{ .InnerInstanceName }} = value;
	})
`

// genericsTemplateStr defines a template for generating generics.
const genericsTemplateStr = `{{ range $index, $generic := .Generics }}{{ if $index }}, {{ end }}{{ $generic.Name }}{{ end }}`

// classTemplateStr defines the template for generating a class type.
const classTemplateStr = `
this.$class('{{ .Type.Name }}', {{ .HasGenerics }}, function({{ .Generics }}) {
	var $static = this;
    var $instance = this.prototype;

    {{ $vars := .GenerateVariables }}
    {{ $composed := .GenerateComposition }}
    {{ if or $vars.Iter $composed.Iter }}
	$static.new = function() {
		var instance = new $static();
		var init = [];
		{{range $idx, $kv := $composed.Iter }}
			init.push({{ $kv.Value }});
  		{{ end }}
		{{ range $idx, $kv := $vars.Iter }}
			init.push({{ $kv.Value }});
		{{ end }}
		return $promise.all(init).then(function() {
			return instance;
		});
	};
	{{ end }}

	{{range $idx, $kv := .GenerateImplementedMembers.Iter }}
  	  {{ $kv.Value }}
  	{{end}}
});
`

// interfaceTemplateStr defines the template for generating an interface type.
const interfaceTemplateStr = `
this.$interface('{{ .Type.Name }}', function({{ .Generics }}) {
	{{range $idx, $kv := .GenerateImplementedMembers.Iter }}
  	  {{ $kv.Value }}
  	{{end}}
});
`

// nominalTemplateStr defines the template for generating a nominal type.
const nominalTemplateStr = `
this.$type('{{ .Type.Name }}', function() {
	var $instance = this;
	{{range $idx, $kv := .GenerateImplementedMembers.Iter }}
  	  {{ $kv.Value }}
  	{{end}}
});<commit_msg>Fix static member generation under nominal types<commit_after>// Copyright 2015 The Serulian Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package es5

import (
	"github.com/serulian/compiler/graphs/typegraph"

	"github.com/cevaris/ordered_map"
)

// generateTypes generates all the types under the  given modules into ES5.
func (gen *es5generator) generateTypes(module typegraph.TGModule) *ordered_map.OrderedMap {
	typeMap := ordered_map.NewOrderedMap()
	types := module.Types()
	for _, typedecl := range types {
		typeMap.Set(typedecl, gen.generateType(typedecl))
	}

	return typeMap
}

// generateType generates the given type into ES5.
func (gen *es5generator) generateType(typedef typegraph.TGTypeDecl) string {
	generating := generatingType{typedef, gen}

	switch typedef.TypeKind() {
	case typegraph.ClassType:
		return gen.templater.Execute("class", classTemplateStr, generating)

	case typegraph.ImplicitInterfaceType:
		return gen.templater.Execute("interface", interfaceTemplateStr, generating)

	case typegraph.NominalType:
		return gen.templater.Execute("nominal", nominalTemplateStr, generating)

	case typegraph.ExternalInternalType:
		return ""

	default:
		panic("Unknown typedef kind")
		return ""
	}
}

// generatingType represents a type being generated.
type generatingType struct {
	Type      typegraph.TGTypeDecl
	Generator *es5generator // The parent generator.
}

// Generics returns a string representing the named generics on this type.
func (gt generatingType) Generics() string {
	return gt.Generator.templater.Execute("generics", genericsTemplateStr, gt.Type)
}

// HasGenerics returns whether the type has generics.
func (gt generatingType) HasGenerics() bool {
	return gt.Type.HasGenerics()
}

// GenerateImplementedMembers generates the source for all the members defined under the type that
// have implementations.
func (gt generatingType) GenerateImplementedMembers() *ordered_map.OrderedMap {
	return gt.Generator.generateImplementedMembers(gt.Type)
}

// GenerateVariables generates the source for all the variables defined under the type.
func (gt generatingType) GenerateVariables() *ordered_map.OrderedMap {
	return gt.Generator.generateVariables(gt.Type)
}

// GenerateComposition generates the source for all the composed types structurually inherited by the type.
func (gt generatingType) GenerateComposition() *ordered_map.OrderedMap {
	typeMap := ordered_map.NewOrderedMap()
	parentTypes := gt.Type.ParentTypes()
	for _, parentTypeRef := range parentTypes {
		data := struct {
			ComposedTypeLocation string
			InnerInstanceName    string
		}{
			gt.Generator.pather.TypeReferenceCall(parentTypeRef),
			gt.Generator.pather.InnerInstanceName(parentTypeRef),
		}

		source := gt.Generator.templater.Execute("composition", compositionTemplateStr, data)
		typeMap.Set(parentTypeRef, source)
	}

	return typeMap
}

// compositionTemplateStr defines a template for instantiating a composed type.
const compositionTemplateStr = `
	({{ .ComposedTypeLocation }}).new().then(function(value) {
	  instance.{{ .InnerInstanceName }} = value;
	})
`

// genericsTemplateStr defines a template for generating generics.
const genericsTemplateStr = `{{ range $index, $generic := .Generics }}{{ if $index }}, {{ end }}{{ $generic.Name }}{{ end }}`

// classTemplateStr defines the template for generating a class type.
const classTemplateStr = `
this.$class('{{ .Type.Name }}', {{ .HasGenerics }}, function({{ .Generics }}) {
	var $static = this;
    var $instance = this.prototype;

    {{ $vars := .GenerateVariables }}
    {{ $composed := .GenerateComposition }}
    {{ if or $vars.Iter $composed.Iter }}
	$static.new = function() {
		var instance = new $static();
		var init = [];
		{{range $idx, $kv := $composed.Iter }}
			init.push({{ $kv.Value }});
  		{{ end }}
		{{ range $idx, $kv := $vars.Iter }}
			init.push({{ $kv.Value }});
		{{ end }}
		return $promise.all(init).then(function() {
			return instance;
		});
	};
	{{ end }}

	{{range $idx, $kv := .GenerateImplementedMembers.Iter }}
  	  {{ $kv.Value }}
  	{{end}}
});
`

// interfaceTemplateStr defines the template for generating an interface type.
const interfaceTemplateStr = `
this.$interface('{{ .Type.Name }}', function({{ .Generics }}) {
	{{range $idx, $kv := .GenerateImplementedMembers.Iter }}
  	  {{ $kv.Value }}
  	{{end}}
});
`

// nominalTemplateStr defines the template for generating a nominal type.
const nominalTemplateStr = `
this.$type('{{ .Type.Name }}', function() {
	var $instance = this;
	var $static = this;
	
	{{range $idx, $kv := .GenerateImplementedMembers.Iter }}
  	  {{ $kv.Value }}
  	{{end}}
});
{'fraction_non_alphanumeric': 0.12737561392269914, 'fraction_numerical': 0.002135383301302584, 'mean_word_length': 4.484192037470726, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2737479', 'n_tokens_mistral': 2896, 'n_tokens_neox': 2758, 'n_words': 771}