BrainForth

From Esolang
Jump to navigation Jump to search

BrainForth is an esoteric programing language created by User:Binarycat in 2020 that combines the tape and minimal instruction set of brainfuck with Forth's word system.

There are 11 predefined words, the eight original brainfuck instructions, ':' and ';' (used for defining words), and '?' (skip next instr if current cell is 0 (I'm not super sure about this one, it may get removed from the language at some point)). The file extention is ".bfth".

Go Implementation

package main

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

var (
	inDefMode bool
	inCondMode bool
	newWordName string
	newWordBody []string

	tape [200]byte
	ptr int = 100
	words map[string]func()

	src = make([]string,0,100)
	srcPos int
	
	stdin = bufio.NewReader(os.Stdin)
	stdout = bufio.NewWriter(os.Stdout)
)

func init() {
	words = map[string]func(){
		"+": inc,
		"-": dec,
		"<": shiftl,
		">": shiftr,
		",": readc,
		".": writec,
		"[": lbracket,
		"]": rbracket,
		"?": enterCondMode,
		":": startNewWord,
		";": endNewWord,
	}
}

func main() {
	srcRdr := openSrcFile()
	for ln, _, err := srcRdr.ReadLine();err == nil;ln, _, err = srcRdr.ReadLine() {
		wds := strings.Fields(string(ln))
		for _, wd := range wds {
			src = append(src,string(wd))
		}
	}
	run(src)
	
	stdout.Flush()
}

func run(src []string) {
	for srcPos < len(src) {
		runWord(src[srcPos])
		srcPos++
	}
}

func runWord(wd string) {
	if inDefMode && wd != ";" {
		if newWordName == "" {
			newWordName = wd
		} else {
			newWordBody = append(newWordBody,wd) 
		}
	} else if inCondMode && tape[ptr] != 0 {
		return
	} else {
		f, ok := words[wd]
		if !ok {panic("unknow word "+wd)}
		f()
	}
}

func lbracket() {
	if tape[ptr] == 0 {
		jumpr()
	}
}

func rbracket() {
	if tape[ptr] != 0 {
		jumpl()
	}
}

func jump(dir int) {
	srcPos += dir
	bDepth := dir
	for bDepth != 0 {
		switch src[srcPos] {
		case "]":
			bDepth--
		case "[":
			bDepth++
		}
		srcPos += dir
	}
}

func jumpr() {
	jump(1)
}

func jumpl() {
	jump(-1)
}

func inc() {
	tape[ptr]++
}

func dec() {
	tape[ptr]--
}

func shiftl() {
	ptr--
}

func shiftr() {
	ptr++
}

func readc() {
	c, err := stdin.ReadByte()
	if err != nil {
		panic(err)
	}
	tape[ptr] = c
}

func writec() {
	err := stdout.WriteByte(tape[ptr])
	if err != nil {
		panic(err)
	}
}

func startNewWord() {
	enterDefMode()
	newWordBody = newWordBody[:0]
	newWordName = ""
}

func endNewWord() {
	exitDefMode()
	words[newWordName] = comWord(newWordBody)
}

func enterDefMode() {
	inDefMode = true
}

func exitDefMode() {
	inDefMode = false
}

func comWord(wds []string) func() {
	wdsCp := make([]string,len(wds))
	copy(wdsCp,wds)
	return func() {
		oldPos := srcPos
		oldSrc := src
		src = wdsCp
		srcPos = 0
		run(wdsCp)
		srcPos = oldPos
		src = oldSrc
	}
}

func enterCondMode() {
	inCondMode = true
}

func openSrcFile() *bufio.Reader {
	if len(os.Args) == 0 || os.Args[0] == "-" {
		return bufio.NewReader(os.Stdin)
	}
	f, err := os.Open(os.Args[1])
	if err != nil {panic(err)}
	
	return bufio.NewReader(f)
}

Example Programs

hello.bfth:

: PRINT [ . > ] ;

: +8 + + + + + + + + ;
: +16 +8 +8 ;
: +32 +16 +16 ;
: +64 +32 +32 ;

: a +64 +32 + ;
: h a +8 - ;
: e a + + + + ;
: l h + + + + ;
: o l + + + ;

h > e > l > l > o [ < ] > PRINT