Skip to content

Instantly share code, notes, and snippets.

@ducc
Created September 28, 2020 08:31
Show Gist options
  • Save ducc/39c631c64e0150b73230e642ac58dc12 to your computer and use it in GitHub Desktop.
Save ducc/39c631c64e0150b73230e642ac58dc12 to your computer and use it in GitHub Desktop.
package main
import (
"bytes"
"fmt"
"regexp"
"sort"
"strconv"
"strings"
)
func main() {
input := `
returnFirst 2 =
| i64(0)
| take
addOne 2 =
| returnFirst
| i64(1)
| add
| is10
is10 1 =
| i64(10)
| equal ? returnFirst : addOne
countTo10 0 =
| i64(1)
| is10
| print
main 0 =
| countTo10
`
run(input)
}
/*
package main
import (
"fmt"
)
func returnFirst(var1 []interface{}) interface{} {
var var2 interface{} = var1[0]
var var3 int64 = var2.(int64)
return var3
}
func addOne(var1 []interface{}) interface{} {
var var2 interface{} = var1[0]
var var3 interface{} = var1[1]
var var4 interface{} = returnFirst([]interface{}{var2, var3})
var var5 int64 = var4.(int64)
var var6 int64 = 1
var var7 int64 = var5 + var6
return is10([]interface{}{var7})
}
func is10(var1 []interface{}) interface{} {
var var2 int64 = var1[0].(int64)
var var3 int64 = 10
if var2 == var3 {
var var4 interface{} = returnFirst([]interface{}{var2, var3})
return var4
} else {
var var5 interface{} = addOne([]interface{}{var2, var3})
return var5
}
}
func countTo10(var1 []interface{}) interface{} {
var var2 int64 = 1
var var3 interface{} = is10([]interface{}{var2})
fmt.Println(var3)
return struct{}{}
}
func main() {
_ = countTo10([]interface{}{})
}
*/
func parseRawBlocks(input string) []string {
var blockRegex = regexp.MustCompile(`(?m)([a-zA-Z0-9]+\s\d{1,100}\s=\n)(?:(?:\|\s.+)\n)+`)
blockMatches := blockRegex.FindAllString(input, -1)
for i, blockMatch := range blockMatches {
fmt.Printf("=== block match %d:\n%s\n", i, blockMatch)
}
return blockMatches
}
func parseBlocks(rawBlocks []string) []Block {
blocks := make([]Block, 0)
for _, blockMatch := range rawBlocks {
var functionName string
var functionArgCount int64
blockInstructions := make([]interface{}, 0)
for i, line := range strings.Split(cleanInput(blockMatch), "\n") {
if i == 0 {
functionDecl := line[:strings.Index(line, "=")-1]
splits := strings.Split(strings.TrimSpace(functionDecl), " ")
functionName = splits[0]
functionArgCount, _ = strconv.ParseInt(splits[1], 10, 64)
continue
} else if strings.HasPrefix(line, "|") {
line = line[2:]
}
var value interface{}
if strings.HasPrefix(line, "i64(") {
content := line[4:strings.LastIndex(line, ")")]
intVal, _ := strconv.ParseInt(content, 10, 64)
value = intValue(intVal)
} else if strings.Contains(line, "?") {
calledFunc := line[:strings.Index(line, "?")-1]
trueFunc := line[strings.Index(line, "?")+2:strings.Index(line, ":")-1]
if strings.Contains(line, ":") {
falseFunc := line[strings.Index(line, ":")+2:]
value = functionCallWithConditionalValue(calledFunc, trueFunc, falseFunc)
} else {
value = functionCallWithConditionalValue(calledFunc, trueFunc, "")
}
} else {
value = functionCallValue(line)
}
blockInstructions = append(blockInstructions, value)
}
for _, blockInstruction := range blockInstructions {
fmt.Println(blockInstruction)
}
blocks = append(blocks, newBlock(functionName, functionArgCount, blockInstructions))
}
return blocks
}
func registerBuiltinFunctions(output map[string]Function, programOutput *[]string) {
output["add"] = func(v []interface{}) interface{} {
fmt.Println("add", v)
return Int64Value{Value: v[0].(Int64Value).Value + v[1].(Int64Value).Value}
}
output["sub"] = func(v []interface{}) interface{} {
fmt.Println("sub", v)
return Int64Value{Value: v[0].(Int64Value).Value - v[1].(Int64Value).Value}
}
output["equal"] = func(v []interface{}) interface{} {
fmt.Println("equal", v)
return BoolValue{Value: v[0] == v[1]}
}
output["more"] = func(v []interface{}) interface{} {
fmt.Println("more", v)
return BoolValue{Value: v[0].(Int64Value).Value > v[1].(Int64Value).Value}
}
output["take"] = func(v []interface{}) interface{} {
fmt.Println("take", v)
index := v[len(v)-1].(Int64Value).Value
return v[index]
}
output["drop"] = func(v []interface{}) interface{} {
fmt.Println("drop")
return None{}
}
output["print"] = func(v []interface{}) interface{} {
values := make([]string, 0)
for _, v := range v {
switch v := v.(type) {
case Int64Value:
values = append(values, fmt.Sprint(v.Value))
case BoolValue:
var text string
if v.Value {
text = "yes"
} else {
text = "no"
}
values = append(values, text)
default:
panic(fmt.Sprintf("unknown type: %s", v))
}
}
*programOutput = append(*programOutput, strings.Join(values, " "))
return nil
}
}
func run(input string) {
/*programOutput := make([]string, 0)
functions := make(map[string]Function)
registerBuiltinFunctions(functions, &programOutput)
rawBlocks := parseRawBlocks(input)
blocks := parseBlocks(rawBlocks)
fmt.Println("============ running =============")
for _, block := range blocks {
block := block
functions[block.FunctionName] = func(input []interface{}) interface{} {
return runInstructions(functions, block.Instructions, input)
}
}
_ = functions["main"]([]interface{}{})
fmt.Printf("========= output =========\n%s\n", strings.Join(programOutput, "\n"))*/
buf := bytes.NewBuffer([]byte{})
write := func(s string, args ...interface{}) {
buf.WriteString(fmt.Sprintf(s, args...))
}
write("package main\n")
write("import \"fmt\"\n")
write("func add(input []interface{}) interface{} {\nreturn input[0].(int64) + input[1].(int64)\n}\n")
write("func sub(input []interface{}) interface{} {\nreturn input[0].(int64) - input[1].(int64)\n}\n")
write("func equal(input []interface{}) interface{} {\nreturn input[0] == input[1]\n}\n")
write("func more(input []interface{}) interface{} {\nreturn input[0].(int64) > input[1].(int64)\n}\n")
write("func take(input []interface{}) interface{} {\nindex := input[len(input)-1].(int64)\nreturn input[index]\n}\n")
write("func print(input []interface{}) interface{} {\nfmt.Println(input)\nreturn struct{}{}\n}\n")
blocks := parseBlocks(parseRawBlocks(input))
getFunctionArgCount := func(name string) int {
switch name {
case "add":
return 2
case "sub":
return 2
case "equal":
return 2
case "more":
return 2
case "take":
return 3
case "drop":
return 0
case "print":
return 1
}
for _, block := range blocks {
if block.FunctionName == name {
return int(block.FunctionArgsCount)
}
}
return 0
}
for _, block := range blocks {
if block.FunctionName == "main" {
write("func main() {\n")
} else {
write("func %s(var1 []interface{}) interface{} {\n", block.FunctionName)
}
i := 1
for idx, instruction := range block.Instructions {
i++
switch v := instruction.(type) {
case Int64Value:
write("var var%d interface{} = %d\n", i, v.Value)
if idx == len(block.Instructions) - 1 {
write("return var%d\n", i)
}
case FunctionCallValue:
args := make([]string, 0)
argCount := getFunctionArgCount(v.Function)
for x := 0; x < argCount; x++ {
args = append(args, fmt.Sprintf("var%d", i-(x+1)))
}
sort.Strings(args)
write("var var%d interface{} = %s([]interface{}{%s})\n", i, v.Function, strings.Join(args, ", "))
if idx == len(block.Instructions) - 1 {
if block.FunctionName == "main" {
write("_ = var%d\n", i)
} else {
write("return var%d\n", i)
}
}
case FunctionCallWithConditionalValue:
args := make([]string, 0)
for x := 0; x < getFunctionArgCount(v.Func.Function); x++ {
args = append(args, fmt.Sprintf("var%d", i-(x+1)))
}
sort.Strings(args)
write("var var%d bool = %s([]interface{}{%s}).(bool)\n", i, v.Func.Function, strings.Join(args, ", "))
write("if var%d {\n", i)
i++
write("var var%d interface{} = %s([]interface{}{%s})\n", i, v.True.Function, strings.Join(args, ", "))
write("return var%d\n", i)
write("} else {\n")
i++
write("var var%d interface{} = %s([]interface{}{%s})\n", i, v.False.Function, strings.Join(args, ", "))
write("return var%d\n", i)
write("}\n")
}
}
write("}\n\n")
//break // todo remove
}
fmt.Println(buf.String())
}
func runInstructions(functions map[string]Function, instructions []interface{}, input []interface{}) interface{} {
values := input
for _, instruction := range instructions {
switch instruction := instruction.(type) {
case Int64Value:
values = append(values, instruction)
case FunctionCallValue:
result := functions[instruction.Function](values)
values = make([]interface{}, 0)
values = append(values, result)
case FunctionCallWithConditionalValue:
result := functions[instruction.Func.Function](values).(BoolValue).Value
if result {
result := functions[instruction.True.Function](values)
values = make([]interface{}, 0)
values = append(values, result)
} else {
result := functions[instruction.False.Function](values)
values = make([]interface{}, 0)
values = append(values, result)
}
}
}
if len(values) == 0 {
return None{}
} else {
return values[0]
}
}
func cleanInput(input string) string {
lines := strings.Split(input, "\n")
instructions := make([]string, 0)
for _, line := range lines {
if line == "" {
continue
}
if strings.HasPrefix(line, "#") {
continue
}
instructions = append(instructions, line)
}
return strings.Join(instructions, "\n")
}
type Int64Value struct {
Value int64
}
func (i Int64Value) String() string {
return fmt.Sprintf("IntValue{Value: %d}", i.Value)
}
func intValue(v int64) Int64Value {
return Int64Value{Value: v}
}
type BoolValue struct {
Value bool
}
func (i BoolValue) String() string {
return fmt.Sprintf("BoolValue{Value: %t}", i.Value)
}
func boolValue(v bool) BoolValue {
return BoolValue{Value: v}
}
type FunctionCallValue struct {
Function string
}
func (i FunctionCallValue) String() string {
return fmt.Sprintf("FunctionCallValue{Function: %s}", i.Function)
}
func functionCallValue(f string) FunctionCallValue {
return FunctionCallValue{Function: f}
}
type FunctionCallWithConditionalValue struct {
Func FunctionCallValue
True FunctionCallValue
False FunctionCallValue
}
func (i FunctionCallWithConditionalValue) String() string {
return fmt.Sprintf("FunctionCallWithCondiitionalValue{Func: %s, True: %s, False: %s}", i.Func, i.True, i.False)
}
func functionCallWithConditionalValue(f, trueF, falseF string) FunctionCallWithConditionalValue {
return FunctionCallWithConditionalValue{
Func: functionCallValue(f),
True: functionCallValue(trueF),
False: functionCallValue(falseF),
}
}
type Function func(v []interface{}) interface{}
type Block struct {
FunctionName string
FunctionArgsCount int64
Instructions []interface{}
}
func newBlock(name string, argsCount int64, instructions []interface{}) Block {
return Block{FunctionName: name, FunctionArgsCount: argsCount, Instructions: instructions}
}
type None struct{}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment