Initial commit.

master
Icedream 2017-09-07 11:55:48 +02:00
commit dddef0d558
Signed by: icedream
GPG Key ID: 1573F6D8EFE4D0CF
2 changed files with 238 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
gobfy
*.exe
*.test

214
main.go Normal file
View File

@ -0,0 +1,214 @@
package main
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"log"
"os"
)
const (
InstMoveRight byte = '>'
InstMoveLeft = '<'
InstIncrement = '+'
InstDecrement = '-'
InstOutput = '.'
InstInput = ','
InstLoopStart = '['
InstLoopEnd = ']'
)
const (
DefaultPageSize = 1024
)
type Closure struct {
Skip bool
Root bool
Start int
}
type Processor struct {
Data []byte
DataPointer int
stdin *bufio.Reader
instructionPointer int
instructionBuffer []byte
closures []*Closure
}
func NewProcessor() *Processor {
return &Processor{
Data: make([]byte, DefaultPageSize),
stdin: bufio.NewReader(os.Stdin),
closures: []*Closure{
&Closure{Root: true},
},
instructionBuffer: []byte{},
}
}
func (p *Processor) Stdin(r io.Reader) {
p.stdin = bufio.NewReader(r)
}
func (p *Processor) ensureDataSize() {
if p.DataPointer >= len(p.Data) {
// Increase data array, lock to next page size
nextPagedSize := (1 + (p.DataPointer / DefaultPageSize)) * DefaultPageSize
p.Data = append(p.Data, make([]byte, 1+nextPagedSize-len(p.Data))...)
}
}
func (p *Processor) Load(instructions []byte) {
p.instructionBuffer = instructions
p.instructionPointer = 0
}
func (p *Processor) Execute() {
for p.instructionPointer < len(p.instructionBuffer) {
instruction := p.instructionBuffer[p.instructionPointer]
/*log.Printf("exec 0x%[2]x = %[1]q, data: 0x%[3]x = %[3]q (0x%[4]x), reserved data size: %[5]d B",
instruction,
p.instructionPointer,
p.Data[p.DataPointer],
p.DataPointer,
len(p.Data))*/
switch instruction {
case InstMoveRight:
p.MoveRight()
case InstMoveLeft:
p.MoveLeft()
case InstDecrement:
p.Decrement()
case InstIncrement:
p.Increment()
case InstInput:
p.Input()
case InstOutput:
p.Output()
case InstLoopStart:
p.StartLoop()
case InstLoopEnd:
p.EndLoop()
default:
// Skip
}
p.instructionPointer++
}
}
func (p *Processor) Current() byte {
return p.Data[p.DataPointer]
}
func (p *Processor) Increment() {
if p.closures[0].Skip {
return
}
p.Data[p.DataPointer]++
}
func (p *Processor) Decrement() {
if p.closures[0].Skip {
return
}
p.Data[p.DataPointer]--
}
func (p *Processor) MoveRight() {
if p.closures[0].Skip {
return
}
p.DataPointer++
p.ensureDataSize()
}
func (p *Processor) MoveLeft() {
if p.closures[0].Skip {
return
}
if p.DataPointer == 0 {
log.Fatal("can not move data pointer left, already at beginning of data")
}
p.DataPointer--
}
func (p *Processor) Output() {
if p.closures[0].Skip {
return
}
fmt.Printf("%c", rune(p.Data[p.DataPointer]))
}
func (p *Processor) Input() {
if p.closures[0].Skip {
return
}
input, err := p.stdin.ReadByte()
if err != nil {
log.Fatal(err)
}
p.Data[p.DataPointer] = input
}
func (p *Processor) StartLoop() {
p.closures = append([]*Closure{
&Closure{
Start: p.instructionPointer,
Skip: p.Data[p.DataPointer] == 0,
},
}, p.closures...)
}
func (p *Processor) EndLoop() {
if len(p.closures) <= 1 {
log.Fatal("unexpected end of closure, not in any closure")
}
currentClosure := p.closures[0]
if !currentClosure.Skip {
if p.Data[p.DataPointer] > 0 {
p.instructionPointer = currentClosure.Start
return
}
}
p.closures = p.closures[1:]
}
func (p *Processor) ExpectEnd() {
if len(p.closures) > 1 {
log.Fatal("unexpected end of instructions, still in a closure")
}
}
func main() {
inputFilePath := os.Args[1]
// Open BF source code
input, err := ioutil.ReadFile(inputFilePath)
if err != nil {
log.Fatal(err)
}
p := NewProcessor()
p.Load(input)
p.Execute()
}