From d2b5f0bdad771cbb1a680a84e79bd75edc6baead Mon Sep 17 00:00:00 2001 From: Rak Laptudirm Date: Sat, 12 Mar 2022 12:04:49 +0530 Subject: [PATCH 1/4] feat: added working vm Tree walk vm is now working. --- pkg/ast/ast.go | 17 +++++++- pkg/lexer/lexer.go | 13 ++++++ pkg/parser/parser.go | 15 ++++++- pkg/token/position.go | 15 ++++++- pkg/token/token.go | 13 ++++++ pkg/vm/vm.go | 98 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 167 insertions(+), 4 deletions(-) create mode 100644 pkg/vm/vm.go diff --git a/pkg/ast/ast.go b/pkg/ast/ast.go index 0461d10..c253e7f 100644 --- a/pkg/ast/ast.go +++ b/pkg/ast/ast.go @@ -1,3 +1,16 @@ +// Copyright © 2021 Rak Laptudirm +// +// 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" @@ -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() } diff --git a/pkg/lexer/lexer.go b/pkg/lexer/lexer.go index 7decfdb..bc86c6e 100644 --- a/pkg/lexer/lexer.go +++ b/pkg/lexer/lexer.go @@ -1,3 +1,16 @@ +// Copyright © 2021 Rak Laptudirm +// +// 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 ( diff --git a/pkg/parser/parser.go b/pkg/parser/parser.go index eda6644..0e9b5de 100644 --- a/pkg/parser/parser.go +++ b/pkg/parser/parser.go @@ -1,3 +1,16 @@ +// Copyright © 2021 Rak Laptudirm +// +// 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 ( @@ -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 { diff --git a/pkg/token/position.go b/pkg/token/position.go index 75a6685..f88ae62 100644 --- a/pkg/token/position.go +++ b/pkg/token/position.go @@ -1,3 +1,16 @@ +// Copyright © 2021 Rak Laptudirm +// +// 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" @@ -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) } diff --git a/pkg/token/token.go b/pkg/token/token.go index e2a7170..fe6d925 100644 --- a/pkg/token/token.go +++ b/pkg/token/token.go @@ -1,3 +1,16 @@ +// Copyright © 2021 Rak Laptudirm +// +// 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" diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go new file mode 100644 index 0000000..cb580b6 --- /dev/null +++ b/pkg/vm/vm.go @@ -0,0 +1,98 @@ +// Copyright © 2021 Rak Laptudirm +// +// 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" + + "github.com/raklaptudirm/brainfuck/pkg/ast" + "github.com/raklaptudirm/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 ErrAst + } + + return nil +} From 20f7c90e19f39928f4bf225bee3c3d9347295f34 Mon Sep 17 00:00:00 2001 From: Rak Laptudirm Date: Sat, 12 Mar 2022 15:20:38 +0530 Subject: [PATCH 2/4] chore: use separate error for invalid operator --- pkg/vm/vm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index cb580b6..d694cbf 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -91,7 +91,7 @@ func (m *Machine) executeOperator(o *ast.Operator) error { case token.PRINT: fmt.Print(string(m.Memory[m.Pointer])) default: - return ErrAst + return fmt.Errorf("invalid operator %s", o.Token) } return nil From e7304e26a484721eeb2045854fedc21810f5f7b9 Mon Sep 17 00:00:00 2001 From: Rak Laptudirm Date: Tue, 29 Mar 2022 13:21:32 +0530 Subject: [PATCH 3/4] feat: change module path to `laptudirm.com/x/` --- go.mod | 2 +- pkg/ast/ast.go | 2 +- pkg/lexer/lexer.go | 2 +- pkg/parser/parser.go | 6 +++--- pkg/vm/vm.go | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 0406944..d539a3c 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ -module github.com/raklaptudirm/brainfuck +module laptudirm.com/x/brainfuck go 1.16 diff --git a/pkg/ast/ast.go b/pkg/ast/ast.go index c253e7f..11bbcf5 100644 --- a/pkg/ast/ast.go +++ b/pkg/ast/ast.go @@ -13,7 +13,7 @@ package ast -import "github.com/raklaptudirm/brainfuck/pkg/token" +import "laptudirm.com/x/brainfuck/pkg/token" type Node interface { TokenLiteral() string diff --git a/pkg/lexer/lexer.go b/pkg/lexer/lexer.go index bc86c6e..4c862b1 100644 --- a/pkg/lexer/lexer.go +++ b/pkg/lexer/lexer.go @@ -16,7 +16,7 @@ package lexer import ( "unicode/utf8" - "github.com/raklaptudirm/brainfuck/pkg/token" + "laptudirm.com/x/brainfuck/pkg/token" ) type Lexer struct { diff --git a/pkg/parser/parser.go b/pkg/parser/parser.go index 0e9b5de..18c7a0b 100644 --- a/pkg/parser/parser.go +++ b/pkg/parser/parser.go @@ -14,9 +14,9 @@ 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 { diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index d694cbf..3e91c15 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -17,8 +17,8 @@ import ( "errors" "fmt" - "github.com/raklaptudirm/brainfuck/pkg/ast" - "github.com/raklaptudirm/brainfuck/pkg/token" + "laptudirm.com/x/brainfuck/pkg/ast" + "laptudirm.com/x/brainfuck/pkg/token" ) type Machine struct { From 02cbb269ec9909a9324fd70bab54a943c08bf785 Mon Sep 17 00:00:00 2001 From: Rak Laptudirm Date: Tue, 29 Mar 2022 13:26:24 +0530 Subject: [PATCH 4/4] feat: add main method --- cmd/brainfuck/main.go | 67 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 cmd/brainfuck/main.go diff --git a/cmd/brainfuck/main.go b/cmd/brainfuck/main.go new file mode 100644 index 0000000..90a7026 --- /dev/null +++ b/cmd/brainfuck/main.go @@ -0,0 +1,67 @@ +// Copyright © 2021 Rak Laptudirm +// +// 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 +}