/* * ECAL * * Copyright 2020 Matthias Ladkau. All rights reserved. * * This Source Code Form is subject to the terms of the MIT * License, If a copy of the MIT License was not distributed with this * file, You can obtain one at https://opensource.org/licenses/MIT. */ package main import ( "bytes" "flag" "fmt" "go/importer" "go/token" "go/types" "io/ioutil" "os" "path/filepath" "sort" "unicode" ) //go:generate echo Generating ECAL stdlib from Go functions ... //go:generate go run devt.de/krotik/ecal/stdlib/generate $PWD/stdlib /* Stdlib candidates modules: go list std | grep -v internal | grep -v '\.' | grep -v unsafe | grep -v syscall */ var pkgNames = map[string][]string{ "math": {"Pi"}, "fmt": {"Println", "Sprint"}, } var filename = filepath.Join(os.Args[1], "stdlib_gen.go") var stderrPrint = fmt.Println var stdoutPrint = fmt.Println func main() { var err error var outbuf bytes.Buffer flag.Parse() // Make sure pkgNames is sorted var importList []string for pkgName, names := range pkgNames { sort.Strings(names) importList = append(importList, pkgName) } sort.Strings(importList) outbuf.WriteString(` // Code generated by ecal/stdlib/generate; DO NOT EDIT. package stdlib `) outbuf.WriteString("import (\n") for _, pkgName := range importList { outbuf.WriteString(fmt.Sprintf("\t\"%v\"\n", pkgName)) } outbuf.WriteString(` "reflect" ) `) outbuf.WriteString(`/* genStdlib contains all generated stdlib constructs. */ `) outbuf.WriteString("var genStdlib = map[interface{}]interface{}{\n") for _, pkgName := range importList { outbuf.WriteString(fmt.Sprintf("\t\"%v-const\" : %vConstMap,\n", pkgName, pkgName)) outbuf.WriteString(fmt.Sprintf("\t\"%v-func\" : %vFuncMap,\n", pkgName, pkgName)) } outbuf.WriteString("}\n\n") for _, pkgName := range importList { var pkg *types.Package pkgSymbols := pkgNames[pkgName] if err == nil { pkg, err = importer.ForCompiler(token.NewFileSet(), "source", nil).Import(pkgName) if err == nil { stdoutPrint("Generating adapter functions for", pkg) scope := pkg.Scope() // Write constants outbuf.WriteString(fmt.Sprintf(`/* %vConstMap contains the mapping of stdlib %v constants. */ var %vConstMap = map[interface{}]interface{}{ `, pkgName, pkgName, pkgName)) for _, name := range scope.Names() { if !containsSymbol(pkgSymbols, name) { continue } switch obj := scope.Lookup(name).(type) { case *types.Const: if unicode.IsUpper([]rune(name)[0]) { line := fmt.Sprintf(` "%v": %v.%v, `, name, pkgName, obj.Name()) if basicType, ok := obj.Type().(*types.Basic); ok { // Convert number constants so they can be used in calculations switch basicType.Kind() { case types.Int, types.Int8, types.Int16, types.Int32, types.Int64, types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.Uintptr, types.Float32, types.UntypedInt, types.UntypedFloat: line = fmt.Sprintf(` "%v": float64(%v.%v), `, name, pkgName, obj.Name()) } } outbuf.WriteString(line) } } } outbuf.WriteString("}\n\n") // Write functions outbuf.WriteString(fmt.Sprintf(`/* %vFuncMap contains the mapping of stdlib %v functions. */ var %vFuncMap = map[interface{}]interface{}{ `, pkgName, pkgName, pkgName)) for _, name := range scope.Names() { if !containsSymbol(pkgSymbols, name) { continue } switch obj := scope.Lookup(name).(type) { case *types.Func: if unicode.IsUpper([]rune(name)[0]) { outbuf.WriteString( fmt.Sprintf(` "%v": &ECALFunctionAdapter{reflect.ValueOf(%v)}, `, name, obj.FullName())) } } } outbuf.WriteString("}\n\n") } } } if err == nil { err = ioutil.WriteFile(filename, outbuf.Bytes(), 0644) } if err != nil { stderrPrint("Error:", err) } } func containsSymbol(symbols []string, item string) bool { i := sort.SearchStrings(symbols, item) return i < len(symbols) && symbols[i] == item }