Initial upload
This commit is contained in:
31
go/forth/.exercism/config.json
Normal file
31
go/forth/.exercism/config.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"blurb": "Implement an evaluator for a very simple subset of Forth",
|
||||
"authors": [
|
||||
"leenipper"
|
||||
],
|
||||
"contributors": [
|
||||
"alebaffa",
|
||||
"bitfield",
|
||||
"da-edra",
|
||||
"ekingery",
|
||||
"ferhatelmas",
|
||||
"hilary",
|
||||
"ilmanzo",
|
||||
"robphoenix",
|
||||
"sebito91"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"forth.go"
|
||||
],
|
||||
"test": [
|
||||
"forth_test.go"
|
||||
],
|
||||
"example": [
|
||||
".meta/example.go"
|
||||
],
|
||||
"editor": [
|
||||
"cases_test.go"
|
||||
]
|
||||
}
|
||||
}
|
1
go/forth/.exercism/metadata.json
Normal file
1
go/forth/.exercism/metadata.json
Normal file
@@ -0,0 +1 @@
|
||||
{"track":"go","exercise":"forth","id":"7fb99fd3f78d43c6a59dd057cac693da","url":"https://exercism.org/tracks/go/exercises/forth","handle":"halfdan","is_requester":true,"auto_approve":false}
|
40
go/forth/HELP.md
Normal file
40
go/forth/HELP.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
To run the tests run the command `go test` from within the exercise directory.
|
||||
|
||||
If the test suite contains benchmarks, you can run these with the `--bench` and `--benchmem`
|
||||
flags:
|
||||
|
||||
go test -v --bench . --benchmem
|
||||
|
||||
Keep in mind that each reviewer will run benchmarks on a different machine, with
|
||||
different specs, so the results from these benchmark tests may vary.
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit forth.go` command.
|
||||
This command will upload your solution to the Exercism website and print the solution page's URL.
|
||||
|
||||
It's possible to submit an incomplete solution which allows you to:
|
||||
|
||||
- See how others have completed the exercise
|
||||
- Request help from a mentor
|
||||
|
||||
## Need to get help?
|
||||
|
||||
If you'd like help solving the exercise, check the following pages:
|
||||
|
||||
- The [Go track's documentation](https://exercism.org/docs/tracks/go)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
To get help if you're having trouble, you can use one of the following resources:
|
||||
|
||||
- [How to Write Go Code](https://golang.org/doc/code.html)
|
||||
- [Effective Go](https://golang.org/doc/effective_go.html)
|
||||
- [Go Resources](http://golang.org/help)
|
||||
- [StackOverflow](http://stackoverflow.com/questions/tagged/go)
|
49
go/forth/README.md
Normal file
49
go/forth/README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Forth
|
||||
|
||||
Welcome to Forth on Exercism's Go Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
|
||||
## Instructions
|
||||
|
||||
Implement an evaluator for a very simple subset of Forth.
|
||||
|
||||
[Forth](https://en.wikipedia.org/wiki/Forth_%28programming_language%29)
|
||||
is a stack-based programming language. Implement a very basic evaluator
|
||||
for a small subset of Forth.
|
||||
|
||||
Your evaluator has to support the following words:
|
||||
|
||||
- `+`, `-`, `*`, `/` (integer arithmetic)
|
||||
- `DUP`, `DROP`, `SWAP`, `OVER` (stack manipulation)
|
||||
|
||||
Your evaluator also has to support defining new words using the
|
||||
customary syntax: `: word-name definition ;`.
|
||||
|
||||
To keep things simple the only data type you need to support is signed
|
||||
integers of at least 16 bits size.
|
||||
|
||||
You should use the following rules for the syntax: a number is a
|
||||
sequence of one or more (ASCII) digits, a word is a sequence of one or
|
||||
more letters, digits, symbols or punctuation that is not a number.
|
||||
(Forth probably uses slightly different rules, but this is close
|
||||
enough.)
|
||||
|
||||
Words are case-insensitive.
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @leenipper
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @alebaffa
|
||||
- @bitfield
|
||||
- @da-edra
|
||||
- @ekingery
|
||||
- @ferhatelmas
|
||||
- @hilary
|
||||
- @ilmanzo
|
||||
- @robphoenix
|
||||
- @sebito91
|
309
go/forth/cases_test.go
Normal file
309
go/forth/cases_test.go
Normal file
@@ -0,0 +1,309 @@
|
||||
package forth
|
||||
|
||||
// Source: exercism/problem-specifications
|
||||
// Commit: 75f4c0a Corrected minor typos in the error msg expectation (doesn't match other similar error patterns and so breaks auto generated tests)
|
||||
// Problem Specifications Version: 1.7.1
|
||||
|
||||
type testGroup struct {
|
||||
group string
|
||||
tests []testCase
|
||||
}
|
||||
|
||||
type testCase struct {
|
||||
description string
|
||||
input []string
|
||||
expected []int // nil slice indicates error expected.
|
||||
}
|
||||
|
||||
var testGroups = []testGroup{
|
||||
{
|
||||
group: "parsing and numbers",
|
||||
tests: []testCase{
|
||||
{
|
||||
"numbers just get pushed onto the stack",
|
||||
[]string{"1 2 3 4 5"},
|
||||
[]int{1, 2, 3, 4, 5},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
group: "addition",
|
||||
tests: []testCase{
|
||||
{
|
||||
"can add two numbers",
|
||||
[]string{"1 2 +"},
|
||||
[]int{3},
|
||||
},
|
||||
{
|
||||
"errors if there is nothing on the stack",
|
||||
[]string{"+"},
|
||||
[]int(nil),
|
||||
},
|
||||
{
|
||||
"errors if there is only one value on the stack",
|
||||
[]string{"1 +"},
|
||||
[]int(nil),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
group: "subtraction",
|
||||
tests: []testCase{
|
||||
{
|
||||
"can subtract two numbers",
|
||||
[]string{"3 4 -"},
|
||||
[]int{-1},
|
||||
},
|
||||
{
|
||||
"errors if there is nothing on the stack",
|
||||
[]string{"-"},
|
||||
[]int(nil),
|
||||
},
|
||||
{
|
||||
"errors if there is only one value on the stack",
|
||||
[]string{"1 -"},
|
||||
[]int(nil),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
group: "multiplication",
|
||||
tests: []testCase{
|
||||
{
|
||||
"can multiply two numbers",
|
||||
[]string{"2 4 *"},
|
||||
[]int{8},
|
||||
},
|
||||
{
|
||||
"errors if there is nothing on the stack",
|
||||
[]string{"*"},
|
||||
[]int(nil),
|
||||
},
|
||||
{
|
||||
"errors if there is only one value on the stack",
|
||||
[]string{"1 *"},
|
||||
[]int(nil),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
group: "division",
|
||||
tests: []testCase{
|
||||
{
|
||||
"can divide two numbers",
|
||||
[]string{"12 3 /"},
|
||||
[]int{4},
|
||||
},
|
||||
{
|
||||
"performs integer division",
|
||||
[]string{"8 3 /"},
|
||||
[]int{2},
|
||||
},
|
||||
{
|
||||
"errors if dividing by zero",
|
||||
[]string{"4 0 /"},
|
||||
[]int(nil),
|
||||
},
|
||||
{
|
||||
"errors if there is nothing on the stack",
|
||||
[]string{"/"},
|
||||
[]int(nil),
|
||||
},
|
||||
{
|
||||
"errors if there is only one value on the stack",
|
||||
[]string{"1 /"},
|
||||
[]int(nil),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
group: "combined arithmetic",
|
||||
tests: []testCase{
|
||||
{
|
||||
"addition and subtraction",
|
||||
[]string{"1 2 + 4 -"},
|
||||
[]int{-1},
|
||||
},
|
||||
{
|
||||
"multiplication and division",
|
||||
[]string{"2 4 * 3 /"},
|
||||
[]int{2},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
group: "dup",
|
||||
tests: []testCase{
|
||||
{
|
||||
"copies a value on the stack",
|
||||
[]string{"1 dup"},
|
||||
[]int{1, 1},
|
||||
},
|
||||
{
|
||||
"copies the top value on the stack",
|
||||
[]string{"1 2 dup"},
|
||||
[]int{1, 2, 2},
|
||||
},
|
||||
{
|
||||
"errors if there is nothing on the stack",
|
||||
[]string{"dup"},
|
||||
[]int(nil),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
group: "drop",
|
||||
tests: []testCase{
|
||||
{
|
||||
"removes the top value on the stack if it is the only one",
|
||||
[]string{"1 drop"},
|
||||
[]int{},
|
||||
},
|
||||
{
|
||||
"removes the top value on the stack if it is not the only one",
|
||||
[]string{"1 2 drop"},
|
||||
[]int{1},
|
||||
},
|
||||
{
|
||||
"errors if there is nothing on the stack",
|
||||
[]string{"drop"},
|
||||
[]int(nil),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
group: "swap",
|
||||
tests: []testCase{
|
||||
{
|
||||
"swaps the top two values on the stack if they are the only ones",
|
||||
[]string{"1 2 swap"},
|
||||
[]int{2, 1},
|
||||
},
|
||||
{
|
||||
"swaps the top two values on the stack if they are not the only ones",
|
||||
[]string{"1 2 3 swap"},
|
||||
[]int{1, 3, 2},
|
||||
},
|
||||
{
|
||||
"errors if there is nothing on the stack",
|
||||
[]string{"swap"},
|
||||
[]int(nil),
|
||||
},
|
||||
{
|
||||
"errors if there is only one value on the stack",
|
||||
[]string{"1 swap"},
|
||||
[]int(nil),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
group: "over",
|
||||
tests: []testCase{
|
||||
{
|
||||
"copies the second element if there are only two",
|
||||
[]string{"1 2 over"},
|
||||
[]int{1, 2, 1},
|
||||
},
|
||||
{
|
||||
"copies the second element if there are more than two",
|
||||
[]string{"1 2 3 over"},
|
||||
[]int{1, 2, 3, 2},
|
||||
},
|
||||
{
|
||||
"errors if there is nothing on the stack",
|
||||
[]string{"over"},
|
||||
[]int(nil),
|
||||
},
|
||||
{
|
||||
"errors if there is only one value on the stack",
|
||||
[]string{"1 over"},
|
||||
[]int(nil),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
group: "user-defined words",
|
||||
tests: []testCase{
|
||||
{
|
||||
"can consist of built-in words",
|
||||
[]string{": dup-twice dup dup ;", "1 dup-twice"},
|
||||
[]int{1, 1, 1},
|
||||
},
|
||||
{
|
||||
"execute in the right order",
|
||||
[]string{": countup 1 2 3 ;", "countup"},
|
||||
[]int{1, 2, 3},
|
||||
},
|
||||
{
|
||||
"can override other user-defined words",
|
||||
[]string{": foo dup ;", ": foo dup dup ;", "1 foo"},
|
||||
[]int{1, 1, 1},
|
||||
},
|
||||
{
|
||||
"can override built-in words",
|
||||
[]string{": swap dup ;", "1 swap"},
|
||||
[]int{1, 1},
|
||||
},
|
||||
{
|
||||
"can override built-in operators",
|
||||
[]string{": + * ;", "3 4 +"},
|
||||
[]int{12},
|
||||
},
|
||||
{
|
||||
"can use different words with the same name",
|
||||
[]string{": foo 5 ;", ": bar foo ;", ": foo 6 ;", "bar foo"},
|
||||
[]int{5, 6},
|
||||
},
|
||||
{
|
||||
"can define word that uses word with the same name",
|
||||
[]string{": foo 10 ;", ": foo foo 1 + ;", "foo"},
|
||||
[]int{11},
|
||||
},
|
||||
{
|
||||
"cannot redefine numbers",
|
||||
[]string{": 1 2 ;"},
|
||||
[]int(nil),
|
||||
},
|
||||
{
|
||||
"errors if executing a non-existent word",
|
||||
[]string{"foo"},
|
||||
[]int(nil),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
group: "case-insensitivity",
|
||||
tests: []testCase{
|
||||
{
|
||||
"DUP is case-insensitive",
|
||||
[]string{"1 DUP Dup dup"},
|
||||
[]int{1, 1, 1, 1},
|
||||
},
|
||||
{
|
||||
"DROP is case-insensitive",
|
||||
[]string{"1 2 3 4 DROP Drop drop"},
|
||||
[]int{1},
|
||||
},
|
||||
{
|
||||
"SWAP is case-insensitive",
|
||||
[]string{"1 2 SWAP 3 Swap 4 swap"},
|
||||
[]int{2, 3, 4, 1},
|
||||
},
|
||||
{
|
||||
"OVER is case-insensitive",
|
||||
[]string{"1 2 OVER Over over"},
|
||||
[]int{1, 2, 1, 2, 1},
|
||||
},
|
||||
{
|
||||
"user-defined words are case-insensitive",
|
||||
[]string{": foo dup ;", "1 FOO Foo foo"},
|
||||
[]int{1, 1, 1, 1},
|
||||
},
|
||||
{
|
||||
"definitions are case-insensitive",
|
||||
[]string{": SWAP DUP Dup dup ;", "1 swap"},
|
||||
[]int{1, 1, 1, 1},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
5
go/forth/forth.go
Normal file
5
go/forth/forth.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package forth
|
||||
|
||||
func Forth(input []string) ([]int, error) {
|
||||
panic("Please implement the Forth function")
|
||||
}
|
40
go/forth/forth_test.go
Normal file
40
go/forth/forth_test.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package forth
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestForth(t *testing.T) {
|
||||
for _, tg := range testGroups {
|
||||
for _, tc := range tg.tests {
|
||||
if v, err := Forth(tc.input); err == nil {
|
||||
var _ error = err
|
||||
if tc.expected == nil {
|
||||
t.Fatalf("FAIL: %s | %s\n\tForth(%#v) expected an error, got %v",
|
||||
tg.group, tc.description, tc.input, v)
|
||||
} else if !reflect.DeepEqual(v, tc.expected) {
|
||||
t.Fatalf("FAIL: %s | %s\n\tForth(%#v) expected %v, got %v",
|
||||
tg.group, tc.description, tc.input, tc.expected, v)
|
||||
}
|
||||
} else if tc.expected != nil {
|
||||
t.Fatalf("FAIL: %s | %s\n\tForth(%#v) expected %v, got an error: %q",
|
||||
tg.group, tc.description, tc.input, tc.expected, err)
|
||||
}
|
||||
t.Logf("PASS: %s | %s", tg.group, tc.description)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkForth(b *testing.B) {
|
||||
if testing.Short() {
|
||||
b.Skip("skipping benchmark in short mode.")
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, tg := range testGroups {
|
||||
for _, tc := range tg.tests {
|
||||
Forth(tc.input)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
go/forth/go.mod
Normal file
3
go/forth/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module forth
|
||||
|
||||
go 1.16
|
24
go/forth/stack.go
Normal file
24
go/forth/stack.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package forth
|
||||
|
||||
import "fmt"
|
||||
|
||||
|
||||
|
||||
type Stack struct {
|
||||
stack []int
|
||||
ptr int
|
||||
}
|
||||
|
||||
func (s *Stack) Push(k int) {
|
||||
s.stack = append(s.stack, k)
|
||||
}
|
||||
|
||||
func (s *Stack) Pop() (int, error) {
|
||||
if len(s.stack) == 0 {
|
||||
return 0, fmt.Errorf("cannot pop empty stack")
|
||||
}
|
||||
|
||||
el := s.stack[len(s.stack)-1]
|
||||
s.stack = s.stack[:len(s.stack)-1]
|
||||
return el, nil
|
||||
}
|
Reference in New Issue
Block a user