Skip to content

Commit

Permalink
merge: pull request #2 from raklaptudirm/vm
Browse files Browse the repository at this point in the history
`core`: implement working virtual machine
  • Loading branch information
raklaptudirm authored Mar 29, 2022
2 parents fc90bc5 + 02cbb26 commit 7cff91c
Show file tree
Hide file tree
Showing 8 changed files with 240 additions and 10 deletions.
67 changes: 67 additions & 0 deletions cmd/brainfuck/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright © 2021 Rak Laptudirm <raklaptudirm@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"fmt"
"os"

"laptudirm.com/x/brainfuck/pkg/lexer"
"laptudirm.com/x/brainfuck/pkg/parser"
"laptudirm.com/x/brainfuck/pkg/token"
"laptudirm.com/x/brainfuck/pkg/vm"
)

const Memory = 30000

func main() {
if len(os.Args) != 2 {
fmt.Fprintln(os.Stderr, "usage: brainfuck [filename]")
os.Exit(1)
}

filename := os.Args[1]
file, err := os.ReadFile(filename)
if err != nil {
fmt.Fprintln(os.Stderr, err)
}

var l lexer.Lexer
l.Init(string(file), handleErr)

var p parser.Parser
p.Init(&l, handleErr)

program := p.ParseProgram()

if l.ErrorCount > 0 || p.ErrorCount > 0 {
os.Exit(1)
}

m := vm.New(Memory)
err = m.Execute(program)
if err != nil {
printErr(err.Error())
os.Exit(1)
}
}

func handleErr(p token.Position, e string) {
printErr("%s: %v\n", p, e)
}

func printErr(format string, a ...interface{}) error {
_, err := fmt.Fprintf(os.Stderr, format, a...)
return err
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module github.com/raklaptudirm/brainfuck
module laptudirm.com/x/brainfuck

go 1.16
19 changes: 16 additions & 3 deletions pkg/ast/ast.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
// Copyright © 2021 Rak Laptudirm <raklaptudirm@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ast

import "github.com/raklaptudirm/brainfuck/pkg/token"
import "laptudirm.com/x/brainfuck/pkg/token"

type Node interface {
TokenLiteral() string
Expand Down Expand Up @@ -45,12 +58,12 @@ func (o *Operator) TokenLiteral() string {
func (o *Operator) operationNode() {}

type Loop struct {
Operators []Operation
Operations []Operation
}

func (l *Loop) TokenLiteral() string {
s := "["
for _, op := range l.Operators {
for _, op := range l.Operations {
s += op.TokenLiteral()
}

Expand Down
15 changes: 14 additions & 1 deletion pkg/lexer/lexer.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
// Copyright © 2021 Rak Laptudirm <raklaptudirm@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package lexer

import (
"unicode/utf8"

"github.com/raklaptudirm/brainfuck/pkg/token"
"laptudirm.com/x/brainfuck/pkg/token"
)

type Lexer struct {
Expand Down
21 changes: 17 additions & 4 deletions pkg/parser/parser.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
// Copyright © 2021 Rak Laptudirm <raklaptudirm@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package parser

import (
"github.com/raklaptudirm/brainfuck/pkg/ast"
"github.com/raklaptudirm/brainfuck/pkg/lexer"
"github.com/raklaptudirm/brainfuck/pkg/token"
"laptudirm.com/x/brainfuck/pkg/ast"
"laptudirm.com/x/brainfuck/pkg/lexer"
"laptudirm.com/x/brainfuck/pkg/token"
)

type Parser struct {
Expand Down Expand Up @@ -54,7 +67,7 @@ func (p *Parser) parseLoop() *ast.Loop {

for p.next(); p.tok != token.ELOOP && p.tok != token.EOF; p.next() {
op := p.parseOperation()
loop.Operators = append(loop.Operators, op)
loop.Operations = append(loop.Operations, op)
}

if p.tok == token.EOF {
Expand Down
15 changes: 14 additions & 1 deletion pkg/token/position.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
// Copyright © 2021 Rak Laptudirm <raklaptudirm@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package token

import "fmt"
Expand All @@ -7,7 +20,7 @@ type Position struct {
Col int
}

func (p *Position) String() string {
func (p Position) String() string {
return fmt.Sprintf("%v:%v", p.Line, p.Col)
}

Expand Down
13 changes: 13 additions & 0 deletions pkg/token/token.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
// Copyright © 2021 Rak Laptudirm <raklaptudirm@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package token

import "strconv"
Expand Down
98 changes: 98 additions & 0 deletions pkg/vm/vm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright © 2021 Rak Laptudirm <raklaptudirm@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package vm

import (
"errors"
"fmt"

"laptudirm.com/x/brainfuck/pkg/ast"
"laptudirm.com/x/brainfuck/pkg/token"
)

type Machine struct {
Length int
Memory []byte
Pointer int
}

func New(l int) *Machine {
return &Machine{
Length: l,
Memory: make([]byte, l),
}
}

var ErrAst = errors.New("invalid ast structure")

func (m *Machine) Execute(n ast.Node) error {
switch v := n.(type) {
case *ast.Program:
return m.executeOperations(v.Operations)
case *ast.Comment:
return nil // ignore comments
case *ast.Loop:
if m.Memory[m.Pointer] == 0 {
return nil
}

for {
err := m.executeOperations(v.Operations)
if err != nil {
return err
}

if m.Memory[m.Pointer] == 0 {
return nil
}
}
case *ast.Operator:
return m.executeOperator(v)
default:
return ErrAst
}
}

func (m *Machine) executeOperations(operations []ast.Operation) error {
for _, operation := range operations {
err := m.Execute(operation)
if err != nil {
return err
}
}

return nil
}

func (m *Machine) executeOperator(o *ast.Operator) error {
switch o.Token {
case token.INC_VAL:
m.Memory[m.Pointer]++
case token.DEC_VAL:
m.Memory[m.Pointer]--
case token.INC_PTR:
m.Pointer++
case token.DEC_PTR:
m.Pointer--
case token.INPUT:
_, err := fmt.Scanf("%c", &m.Memory[m.Pointer])
return err
case token.PRINT:
fmt.Print(string(m.Memory[m.Pointer]))
default:
return fmt.Errorf("invalid operator %s", o.Token)
}

return nil
}

0 comments on commit 7cff91c

Please sign in to comment.