Skip to content

Instantly share code, notes, and snippets.

@logicchains
Last active August 29, 2015 13:56
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save logicchains/9061893 to your computer and use it in GitHub Desktop.
Save logicchains/9061893 to your computer and use it in GitHub Desktop.
Simple generics implementation for Go. Run with "go++ -i=myFileName.go".
package main
import(
"io/ioutil"
"strings"
"fmt"
"flag"
"os/exec"
)
type Function struct{
StartLine int
EndLine int
GenParams []string
NumParams int
Name string
Text []string
}
func parseFile(fName string)([]Function, []string){
functions := make([]Function,0,100)
contents, err := ioutil.ReadFile(fName)
if err != nil {
panic(err)
}
fileLines := strings.Split(string(contents),"\n")
for _, line := range fileLines{
if strings.Contains(line,"func") && strings.Contains(line,"<") && strings.Contains(line,">"){
name := strings.TrimSpace(line[strings.Index(line, "func") + 4 : strings.Index(line, "<")])
genParams := strings.Split(strings.TrimSpace(line[strings.Index(line, "<") + 1: strings.Index(line, ">")]),",")
curLine := 0
for i, nLine := range fileLines{
if strings.Contains(nLine,"func") && strings.Contains(nLine,"<") && strings.Contains(nLine,">") && strings.Contains(nLine,name){
curLine = i
}
}
size := findFuncSize(fileLines[curLine:])
text := fileLines[curLine: curLine + size]
functions = append(functions, Function{StartLine: curLine, EndLine: curLine+size, GenParams: genParams, NumParams: len(genParams), Name: name,
Text: text})
fileStr := strings.Join(fileLines, "\n")
fileStr = strings.Replace(fileStr, strings.Join(text,"\n"), "", 1)
fileLines = strings.Split(fileStr, "\n")
}
}
return functions, fileLines
}
func findFuncSize(funcLines []string)int {
numLeftBrackets := 0
numRightBrackets := 0
lineSize := 0
for i, ln := range funcLines{
numLeftBrackets += strings.Count(ln, "{")
numRightBrackets += strings.Count(ln, "}")
if numLeftBrackets == numRightBrackets && numRightBrackets > 0{
lineSize = i
break
}
}
return lineSize + 1
}
func makeGeneric(funcs []Function, srcFile []string)([]string, []string){
src := strings.Join(srcFile,"\n")
mangledFuncs := make([]string,0,20)
for _, fn := range funcs{
for strings.Index(src, fn.Name + "<") >0{
paramStart := strings.Index(src, fn.Name + "<" ) + len(fn.Name) + 1
paramEnd := strings.Index(src[paramStart:], ">") + paramStart
paramList := strings.Split(strings.TrimSpace(src[paramStart:paramEnd]),",")
if len(paramList) != fn.NumParams{
panic("Error: generic function" + fn.Name + "used with wrong number of parameters.")
}
mangledFnName := fn.Name
for _, param := range paramList{
mangledFnName += param
}
mangledFnName = strings.Replace(fn.Text[0], fn.Name, mangledFnName, 1)
mangledFnDec := mangledFnName
for i, param := range paramList{
mangledFnDec = strings.Replace(mangledFnDec,"~" + fn.GenParams[i], param,-1)
mangledFnDec = strings.Replace(mangledFnDec,
mangledFnDec[strings.Index(mangledFnDec, "<") : strings.Index(mangledFnDec, ">") + 1], "", 1)
}
mangledFnBody := strings.Join(fn.Text[1:],"\n")
for i, param := range paramList{
mangledFnBody = strings.Replace(mangledFnBody,"~" + fn.GenParams[i], param,-1)
}
mangledFnWhole := mangledFnDec + "\n" + mangledFnBody
mangledFuncs = append(mangledFuncs, mangledFnWhole)
fnInvocationStart := strings.Index(src, fn.Name + "<")
fnInvocation := src[fnInvocationStart : strings.Index(src[fnInvocationStart:],">") + fnInvocationStart + 1]
newInvocation := mangledFnDec[len("func "): strings.Index(mangledFnDec, "(")]
src = strings.Replace(src, fnInvocation, newInvocation, 1)
}
}
return strings.Split(src,"\n"), mangledFuncs
}
func compileSrc(src, fName string){
err := ioutil.WriteFile(fName, []byte(src), 0644)
if err != nil {
panic(err)
}
cmdOutput, err := exec.Command("go", "build", fName).CombinedOutput()
if err != nil {
fmt.Printf("Compilation of resulting source failed with error %v : %v\n", err, string(cmdOutput))
}
}
var fileNameFlag = flag.String("i", "gen.go", "Filename to compile")
func main(){
flag.Parse()
var fileName string = *fileNameFlag
functions, fileLines := parseFile(fileName)
src, mangledFns := makeGeneric(functions, fileLines)
newSrc := strings.Join(src,"\n") + strings.Join(mangledFns,"\n")
compileSrc(newSrc, fileName[:len(fileName)-3] + "++" + ".go")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment