Initial commit.
commit
dddef0d558
|
@ -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
|
|
@ -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()
|
||||
}
|
Loading…
Reference in New Issue