Skip to content

Instantly share code, notes, and snippets.

@ducc
Last active September 24, 2020 16:19
Show Gist options
  • Save ducc/cf97e182c98259222ada9ec2fd2f70ad to your computer and use it in GitHub Desktop.
Save ducc/cf97e182c98259222ada9ec2fd2f70ad to your computer and use it in GitHub Desktop.
package main
import (
"fmt"
"regexp"
"strconv"
"strings"
)
func main() {
input := `
returnFirst =
| i64(0)
| take
addOne =
| returnFirst
| i64(1)
| add
| is10
is10 =
| i64(10)
| equal ? returnFirst : addOne
countTo10 =
| i64(1)
| is10
| print
main =
| countTo10
`
run(input)
}
func parseRawBlocks(input string) []string {
var blockRegex = regexp.MustCompile(`(?m)([a-zA-Z0-9]+\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
blockInstructions := make([]interface{}, 0)
for i, line := range strings.Split(cleanInput(blockMatch), "\n") {
if i == 0 {
functionName = line[:strings.Index(line, "=")-1]
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, 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"))
}
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
Instructions []interface{}
}
func newBlock(name string, instructions []interface{}) Block {
return Block{FunctionName: name, Instructions: instructions}
}
type None struct{}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment