Last active
August 29, 2015 13:56
-
-
Save logicchains/9061893 to your computer and use it in GitHub Desktop.
Simple generics implementation for Go. Run with "go++ -i=myFileName.go".
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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