1
\$\begingroup\$

I'm learning Go, so I decided to make a Brain-flak interpreter for practice.

package main

import (
    "fmt"
    "strings"
    "bufio"
    "os"
)

var openToClose map[rune]rune = map[rune]rune {
    '(': ')',
    '[': ']',
    '{': '}',
    '<': '>',
    '&': '^',
}

var bracketNames map[rune]string = map[rune]string {
    '(': "round",
    '[': "square",
    '{': "curly",
    '<': "angle",
    '&': "internal",
}

type bracket struct {
    bracketType rune
    subExprs []bracket
}

func (b bracket) String() string {
  var stringSubs []string
  for _, subExpr := range b.subExprs {
    stringSubs = append(stringSubs, subExpr.String())
  }
  return bracketNames[b.bracketType] + "(" + strings.Join(stringSubs, ", ") + ")"
}

type stack []int

func (s *stack) push(val int) {
    *s = append(*s, val)
}

func (s *stack) pop() int {
    var size int = len(*s)
    if size == 0 {
        return 0
    } else {
        var val int = (*s)[size - 1]
        *s = (*s)[:size - 1]
        return val
    }
}

func (s stack) peek() int {
  var size int = len(s)
  if size == 0 {
    return 0
  } else {
    return s[size - 1]
  }
}

type environment struct {
    active stack    
    inactive stack
}

func (e *environment) swap() {
    e.active, e.inactive = e.inactive, e.active
}

func (e *environment) push(val int) {
    e.active.push(val)
}

func (e *environment) pop() int {
    return e.active.pop()
}

func (e environment) peek() int {
  return e.active.peek()
}

func (e environment) size() int {
    return len(e.active)
}

func (e *environment) EvalArgs(args []bracket) int {
  var result int = 0
  for _, arg := range args {
    result += e.EvalArg(arg)
  }
  return result
}

func (e *environment)EvalArg(arg bracket) int {
  var nilad bool = len(arg.subExprs) == 0
  var name string = bracketNames[arg.bracketType]
  var args = arg.subExprs
  switch name {
  case "round":
    return e.round(args, nilad)
  case "square":
    return e.square(args, nilad)
  case "curly":
    return e.curly(args, nilad)
  case "angle":
    return e.angle(args, nilad)
  case "internal":
    return e.EvalArgs(args)
  default:
    panic("Tried to execute bad command " + name)
  }
}

func (e *environment) round(args []bracket, nilad bool) int {
  if nilad {
    return 1
  } else {
    var result int = e.EvalArgs(args)
    e.push(result)
    return result
  }
}

func (e *environment) square(args []bracket, nilad bool) int {
  if nilad {
    return e.size()
  } else {
    return -e.EvalArgs(args)
  }
}

func (e *environment) curly(args []bracket, nilad bool) int {
  if nilad {
    return e.pop()
  } else {
    var result int = 0
    for e.peek() != 0 {
      result += e.EvalArgs(args)
    }
    return result
  }
}

func (e *environment) angle(args []bracket, nilad bool) int {
  if nilad {
    e.swap()
  } else {
    e.EvalArgs(args)
  }
  return 0
}

func (e *environment) Interpret(code string) int {
  return e.EvalArgs(Parse(code))
}

func parseHelper(code []rune, index int) (bracket, int) {
    var openingBracket rune = code[index]
    var closingBracket rune = openToClose[openingBracket]
    var subExprs []bracket = []bracket{}
    var currInd int = index + 1
    var newExpr bracket
    for code[currInd] != closingBracket {
        newExpr, currInd = parseHelper(code, currInd)
        subExprs = append(subExprs, newExpr)
    }
    return bracket{openingBracket, subExprs}, currInd + 1
}

func Parse(code string) []bracket {
  parsed, _ := parseHelper([]rune("&" + code + "^"), 0)
  return parsed.subExprs
}

func main() {
    var e environment
    codeReader := bufio.NewReader(os.Stdin)
    for {
      code, _ := codeReader.ReadString('\n')
      code = strings.Trim(code, "\n")
      fmt.Println(code)
      fmt.Println(e.Interpret(code))
    }
}

My main questions are:

  • Is there a better way to implement that switch statement? It doesn't really follow DRY.

  • Would it be better to replace the "internal" bracket system with the loop in parseHelper?

  • How could I make this shorter and more readable?

\$\endgroup\$
  • \$\begingroup\$ Generally, you won't see in Go programs var foo Type = value inside a function. Normally this is written foo := value (type is inferred and := doesn't require writing var. This choice is arbitrary, but this is how it works. \$\endgroup\$ – wvxvw Nov 23 '17 at 8:52
  • \$\begingroup\$ You should take a look at golint: it slightly reorganize your code to read more like "expected" go \$\endgroup\$ – oliverpool Nov 24 '17 at 16:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.