trackler 2.0.6.36 → 2.0.6.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/trackler/version.rb +1 -1
- data/tracks/fsharp/exercises/isogram/IsogramTest.fs +70 -12
- data/tracks/fsharp/exercises/luhn/LuhnTest.fs +103 -9
- data/tracks/fsharp/exercises/nth-prime/NthPrimeTest.fs +53 -14
- data/tracks/go/config.json +5 -0
- data/tracks/go/exercises/forth/cases_test.go +246 -0
- data/tracks/go/exercises/forth/example.go +238 -0
- data/tracks/go/exercises/forth/example_gen.go +99 -0
- data/tracks/go/exercises/forth/forth_test.go +64 -0
- data/tracks/java/exercises/phone-number/src/example/java/PhoneNumber.java +19 -25
- data/tracks/java/exercises/phone-number/src/test/java/PhoneNumberTest.java +44 -30
- data/tracks/ocaml/README.md +11 -0
- data/tracks/ocaml/tools/test-generator/README.md +39 -1
- data/tracks/perl6/.travis.yml +1 -1
- data/tracks/perl6/exercises/allergies/allergies.t +118 -3
- data/tracks/perl6/exercises/atbash-cipher/atbash-cipher.t +91 -3
- data/tracks/perl6/exercises/phone-number/phone-number.t +58 -5
- data/tracks/perl6/exercises/space-age/space-age.t +51 -4
- data/tracks/perl6/exercises/wordy/wordy.t +91 -4
- metadata +6 -7
- data/tracks/perl6/exercises/allergies/cases.json +0 -109
- data/tracks/perl6/exercises/atbash-cipher/cases.json +0 -82
- data/tracks/perl6/exercises/phone-number/cases.json +0 -47
- data/tracks/perl6/exercises/space-age/cases.json +0 -46
- data/tracks/perl6/exercises/wordy/cases.json +0 -89
@@ -0,0 +1,238 @@
|
|
1
|
+
// Package forth implements a tiny subset of the Forth language.
|
2
|
+
package forth
|
3
|
+
|
4
|
+
import (
|
5
|
+
"errors"
|
6
|
+
"strconv"
|
7
|
+
"strings"
|
8
|
+
"unicode"
|
9
|
+
)
|
10
|
+
|
11
|
+
type operatorFn func(stack *[]int) error
|
12
|
+
|
13
|
+
type operatorId byte
|
14
|
+
|
15
|
+
const (
|
16
|
+
addOp operatorId = iota
|
17
|
+
subOp
|
18
|
+
mulOp
|
19
|
+
divOp
|
20
|
+
dropOp
|
21
|
+
dupOp
|
22
|
+
swapOp
|
23
|
+
overOp
|
24
|
+
constOp
|
25
|
+
userDefOp
|
26
|
+
endDefOp
|
27
|
+
)
|
28
|
+
|
29
|
+
type operatorTyp struct {
|
30
|
+
fn operatorFn
|
31
|
+
id operatorId
|
32
|
+
}
|
33
|
+
|
34
|
+
func Forth(input []string) (result []int, err error) {
|
35
|
+
if len(input) == 0 {
|
36
|
+
return []int{}, nil
|
37
|
+
}
|
38
|
+
|
39
|
+
// Allocate an initially empty stack, with arbitrary starting capacity of 8.
|
40
|
+
stack := make([]int, 0, 8)
|
41
|
+
// Allocate a map for user defined words.
|
42
|
+
userDefs := make(map[string][]operatorTyp, 8)
|
43
|
+
for _, phrase := range input {
|
44
|
+
// Parse one phrase of input, building up an operator list.
|
45
|
+
opList, err := parse(phrase, userDefs)
|
46
|
+
if err != nil {
|
47
|
+
return nil, err
|
48
|
+
}
|
49
|
+
// Perform any operators from that phrase, updating stack.
|
50
|
+
for _, opr := range opList {
|
51
|
+
err = opr.fn(&stack)
|
52
|
+
if err != nil {
|
53
|
+
return nil, err
|
54
|
+
}
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
return stack[:len(stack)], nil
|
59
|
+
}
|
60
|
+
|
61
|
+
// parse given phrase, returning an operator list, and updating
|
62
|
+
// a userDefs map for any user definifion of words in phrase.
|
63
|
+
func parse(phrase string, userDefs map[string][]operatorTyp) (oplist []operatorTyp, err error) {
|
64
|
+
words := strings.FieldsFunc(phrase,
|
65
|
+
func(r rune) bool {
|
66
|
+
return unicode.IsSpace(r) || unicode.IsControl(r)
|
67
|
+
})
|
68
|
+
|
69
|
+
// t is token index into words[]
|
70
|
+
for t := 0; t < len(words); t++ {
|
71
|
+
w := strings.ToUpper(words[t])
|
72
|
+
// Handle reference to user defined word.
|
73
|
+
if udef, ok := userDefs[w]; ok {
|
74
|
+
oplist = append(oplist, udef...)
|
75
|
+
} else if op, ok := builtinOps[w]; ok {
|
76
|
+
if op.id == userDefOp {
|
77
|
+
// Handle user defined word definition.
|
78
|
+
t++
|
79
|
+
if t >= len(words)-2 {
|
80
|
+
return nil, emptyUserDefErr
|
81
|
+
}
|
82
|
+
userword := strings.ToUpper(words[t])
|
83
|
+
if _, numerr := strconv.Atoi(userword); numerr == nil {
|
84
|
+
return nil, invalidUserDefErr
|
85
|
+
}
|
86
|
+
t++
|
87
|
+
var userops []operatorTyp
|
88
|
+
for t < len(words) {
|
89
|
+
oneOp, err := parse(words[t], userDefs)
|
90
|
+
if err != nil {
|
91
|
+
return nil, err
|
92
|
+
}
|
93
|
+
if oneOp[0].id == endDefOp {
|
94
|
+
break
|
95
|
+
}
|
96
|
+
userops = append(userops, oneOp...)
|
97
|
+
t++
|
98
|
+
}
|
99
|
+
if len(userops) == 0 {
|
100
|
+
return nil, emptyUserDefErr
|
101
|
+
} else {
|
102
|
+
userDefs[userword] = userops
|
103
|
+
}
|
104
|
+
} else {
|
105
|
+
// Normal builtin operator.
|
106
|
+
oplist = append(oplist, op)
|
107
|
+
}
|
108
|
+
} else {
|
109
|
+
// Handle constant literal.
|
110
|
+
var x int
|
111
|
+
x, err = strconv.Atoi(w)
|
112
|
+
if err != nil {
|
113
|
+
return nil, err
|
114
|
+
}
|
115
|
+
oplist = append(oplist,
|
116
|
+
operatorTyp{id: constOp,
|
117
|
+
fn: func(stack *[]int) error {
|
118
|
+
push(stack, x)
|
119
|
+
return nil
|
120
|
+
},
|
121
|
+
})
|
122
|
+
}
|
123
|
+
}
|
124
|
+
return oplist, nil
|
125
|
+
}
|
126
|
+
|
127
|
+
// builtinOps are the pre-defined operators to support.
|
128
|
+
var builtinOps = map[string]operatorTyp{
|
129
|
+
"+": {add, addOp},
|
130
|
+
"-": {subtract, subOp},
|
131
|
+
"*": {multiply, mulOp},
|
132
|
+
"/": {divide, divOp},
|
133
|
+
"DUP": {dup, dropOp},
|
134
|
+
"DROP": {drop, dupOp},
|
135
|
+
"SWAP": {swap, swapOp},
|
136
|
+
"OVER": {over, overOp},
|
137
|
+
":": {nil, userDefOp},
|
138
|
+
";": {nil, endDefOp},
|
139
|
+
}
|
140
|
+
|
141
|
+
func pop(stack *[]int) (v int, err error) {
|
142
|
+
slen := len(*stack)
|
143
|
+
if slen >= 1 {
|
144
|
+
v = (*stack)[slen-1]
|
145
|
+
*stack = (*stack)[:slen-1]
|
146
|
+
return v, nil
|
147
|
+
}
|
148
|
+
return 0, notEnoughOperands
|
149
|
+
}
|
150
|
+
|
151
|
+
func pop2(stack *[]int) (v1, v2 int, err error) {
|
152
|
+
v1, err = pop(stack)
|
153
|
+
if err != nil {
|
154
|
+
return 0, 0, err
|
155
|
+
}
|
156
|
+
v2, err = pop(stack)
|
157
|
+
return v1, v2, err
|
158
|
+
}
|
159
|
+
|
160
|
+
func push(stack *[]int, v int) {
|
161
|
+
*stack = append(*stack, v)
|
162
|
+
}
|
163
|
+
|
164
|
+
func binaryOp(stack *[]int, op func(a, b int) int) error {
|
165
|
+
v1, v2, err := pop2(stack)
|
166
|
+
if err != nil {
|
167
|
+
return err
|
168
|
+
}
|
169
|
+
push(stack, op(v2, v1))
|
170
|
+
return nil
|
171
|
+
}
|
172
|
+
|
173
|
+
func add(stack *[]int) (err error) {
|
174
|
+
return binaryOp(stack, func(a, b int) int { return a + b })
|
175
|
+
}
|
176
|
+
|
177
|
+
func subtract(stack *[]int) (err error) {
|
178
|
+
return binaryOp(stack, func(a, b int) int { return a - b })
|
179
|
+
}
|
180
|
+
|
181
|
+
func multiply(stack *[]int) error {
|
182
|
+
return binaryOp(stack, func(a, b int) int { return a * b })
|
183
|
+
}
|
184
|
+
|
185
|
+
func divide(stack *[]int) error {
|
186
|
+
v1, v2, err := pop2(stack)
|
187
|
+
if err != nil {
|
188
|
+
return err
|
189
|
+
}
|
190
|
+
if v1 == 0 {
|
191
|
+
return divideByZero
|
192
|
+
}
|
193
|
+
push(stack, v2/v1)
|
194
|
+
return nil
|
195
|
+
}
|
196
|
+
|
197
|
+
func dup(stack *[]int) error {
|
198
|
+
v1, err := pop(stack)
|
199
|
+
if err != nil {
|
200
|
+
return err
|
201
|
+
}
|
202
|
+
push(stack, v1)
|
203
|
+
push(stack, v1)
|
204
|
+
return nil
|
205
|
+
}
|
206
|
+
|
207
|
+
func drop(stack *[]int) error {
|
208
|
+
_, err := pop(stack)
|
209
|
+
return err
|
210
|
+
}
|
211
|
+
|
212
|
+
func over(stack *[]int) error {
|
213
|
+
v1, v2, err := pop2(stack)
|
214
|
+
if err != nil {
|
215
|
+
return err
|
216
|
+
}
|
217
|
+
push(stack, v2)
|
218
|
+
push(stack, v1)
|
219
|
+
push(stack, v2)
|
220
|
+
return nil
|
221
|
+
}
|
222
|
+
|
223
|
+
func swap(stack *[]int) error {
|
224
|
+
v1, v2, err := pop2(stack)
|
225
|
+
if err != nil {
|
226
|
+
return err
|
227
|
+
}
|
228
|
+
push(stack, v1)
|
229
|
+
push(stack, v2)
|
230
|
+
return nil
|
231
|
+
}
|
232
|
+
|
233
|
+
var notEnoughOperands error = errors.New("not enough operands")
|
234
|
+
var divideByZero error = errors.New("attempt to divide by zero")
|
235
|
+
var emptyUserDefErr error = errors.New("empty user definition")
|
236
|
+
var invalidUserDefErr error = errors.New("invalid user def word")
|
237
|
+
|
238
|
+
const testVersion = 1
|
@@ -0,0 +1,99 @@
|
|
1
|
+
// +build ignore
|
2
|
+
|
3
|
+
package main
|
4
|
+
|
5
|
+
import (
|
6
|
+
"log"
|
7
|
+
"text/template"
|
8
|
+
|
9
|
+
"../../gen"
|
10
|
+
)
|
11
|
+
|
12
|
+
func main() {
|
13
|
+
t, err := template.New("").Parse(tmpl)
|
14
|
+
if err != nil {
|
15
|
+
log.Fatal(err)
|
16
|
+
}
|
17
|
+
var j js
|
18
|
+
if err := gen.Gen("forth", &j, t); err != nil {
|
19
|
+
log.Fatal(err)
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
// The JSON structure we expect to be able to unmarshal into
|
24
|
+
type js struct {
|
25
|
+
ParsingAndNumbers section `json:"parsing and numbers"`
|
26
|
+
Addition section
|
27
|
+
Subtraction section
|
28
|
+
Multiplication section
|
29
|
+
Division section
|
30
|
+
CombinedArithmetic section `json:"combined arithmetic"`
|
31
|
+
Dup section
|
32
|
+
Drop section
|
33
|
+
Swap section
|
34
|
+
Over section
|
35
|
+
UserDefinedWords section `json:"user-defined words"`
|
36
|
+
}
|
37
|
+
|
38
|
+
// A grouped section of test cases.
|
39
|
+
type section struct {
|
40
|
+
Cases []struct {
|
41
|
+
Description string
|
42
|
+
Input []string
|
43
|
+
Expected []int
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
// template applied to above data structure generates the Go test cases
|
48
|
+
var tmpl = `package forth
|
49
|
+
|
50
|
+
// Source: {{.Ori}}
|
51
|
+
{{if .Commit}}// Commit: {{.Commit}}
|
52
|
+
{{end}}
|
53
|
+
|
54
|
+
{{/* template for repeated caseSection */}}
|
55
|
+
|
56
|
+
{{define "caseSection"}} = []testCase{
|
57
|
+
{{range $y, $c := .}}{
|
58
|
+
{{printf "%q" $c.Description}},
|
59
|
+
{{printf "%#v" $c.Input}},
|
60
|
+
{{printf "%#v" $c.Expected}},
|
61
|
+
},
|
62
|
+
{{end}}}{{end}}
|
63
|
+
|
64
|
+
var parsingGroup{{template "caseSection" .J.ParsingAndNumbers.Cases}}
|
65
|
+
|
66
|
+
var additionGroup{{template "caseSection" .J.Addition.Cases}}
|
67
|
+
|
68
|
+
var subtractionGroup{{template "caseSection" .J.Subtraction.Cases}}
|
69
|
+
|
70
|
+
var multiplicationGroup{{template "caseSection" .J.Multiplication.Cases}}
|
71
|
+
|
72
|
+
var divisionGroup{{template "caseSection" .J.Division.Cases}}
|
73
|
+
|
74
|
+
var arithmeticGroup{{template "caseSection" .J.CombinedArithmetic.Cases}}
|
75
|
+
|
76
|
+
var dupGroup{{template "caseSection" .J.Dup.Cases}}
|
77
|
+
|
78
|
+
var dropGroup{{template "caseSection" .J.Drop.Cases}}
|
79
|
+
|
80
|
+
var swapGroup{{template "caseSection" .J.Swap.Cases}}
|
81
|
+
|
82
|
+
var overGroup{{template "caseSection" .J.Over.Cases}}
|
83
|
+
|
84
|
+
var userdefinedGroup{{template "caseSection" .J.UserDefinedWords.Cases}}
|
85
|
+
|
86
|
+
var testSections = []testcaseSection{
|
87
|
+
{"parsing", parsingGroup},
|
88
|
+
{"addition(+)", additionGroup},
|
89
|
+
{"subtraction(-)", subtractionGroup},
|
90
|
+
{"multiplication(*)", multiplicationGroup},
|
91
|
+
{"division(/)", divisionGroup},
|
92
|
+
{"arithmetic", arithmeticGroup},
|
93
|
+
{"dup", dupGroup},
|
94
|
+
{"drop", dropGroup},
|
95
|
+
{"swap", swapGroup},
|
96
|
+
{"over", overGroup},
|
97
|
+
{"user-defined", userdefinedGroup},
|
98
|
+
}
|
99
|
+
`
|
@@ -0,0 +1,64 @@
|
|
1
|
+
package forth
|
2
|
+
|
3
|
+
// API:
|
4
|
+
// func Forth([]string) ([]int, error)
|
5
|
+
//
|
6
|
+
|
7
|
+
import (
|
8
|
+
"reflect"
|
9
|
+
"testing"
|
10
|
+
)
|
11
|
+
|
12
|
+
const targetTestVersion = 1
|
13
|
+
|
14
|
+
type testCase struct {
|
15
|
+
description string
|
16
|
+
input []string
|
17
|
+
expected []int // nil slice indicates error expected.
|
18
|
+
}
|
19
|
+
|
20
|
+
type testcaseSection struct {
|
21
|
+
name string
|
22
|
+
tests []testCase
|
23
|
+
}
|
24
|
+
|
25
|
+
func TestTestVersion(t *testing.T) {
|
26
|
+
if testVersion != targetTestVersion {
|
27
|
+
t.Fatalf("Found testVersion = %v, want %v", testVersion, targetTestVersion)
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
func runTestCases(t *testing.T, sectionName string, testCases []testCase) {
|
32
|
+
for _, tc := range testCases {
|
33
|
+
description := sectionName + " - " + tc.description
|
34
|
+
if v, err := Forth(tc.input); err == nil {
|
35
|
+
var _ error = err
|
36
|
+
if tc.expected == nil {
|
37
|
+
t.Fatalf("%s\n\tForth(%#v) expected an error, got %v",
|
38
|
+
description, tc.input, v)
|
39
|
+
} else if !reflect.DeepEqual(v, tc.expected) {
|
40
|
+
t.Fatalf("%s\n\tForth(%#v) expected %v, got %v",
|
41
|
+
description, tc.input, tc.expected, v)
|
42
|
+
}
|
43
|
+
} else if tc.expected != nil {
|
44
|
+
t.Fatalf("%s\n\tForth(%#v) expected %v, got an error: %q",
|
45
|
+
description, tc.input, tc.expected, err)
|
46
|
+
}
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
func TestForth(t *testing.T) {
|
51
|
+
for _, testSection := range testSections {
|
52
|
+
runTestCases(t, testSection.name, testSection.tests)
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
func BenchmarkForth(b *testing.B) {
|
57
|
+
for i := 0; i < b.N; i++ {
|
58
|
+
for _, testSection := range testSections {
|
59
|
+
for _, test := range testSection.tests {
|
60
|
+
Forth(test.input)
|
61
|
+
}
|
62
|
+
}
|
63
|
+
}
|
64
|
+
}
|
@@ -7,44 +7,38 @@ public class PhoneNumber {
|
|
7
7
|
}
|
8
8
|
|
9
9
|
private String extractDigits(String dirtyNumber) {
|
10
|
-
|
10
|
+
StringBuilder stringBuilder = new StringBuilder();
|
11
|
+
for (char c : dirtyNumber.toCharArray()) {
|
12
|
+
if (c == ' ' || c == '.' || c == '(' || c == ')' || c == '-') {
|
13
|
+
// Remove spaces, dots, parentheses and hyphens
|
14
|
+
continue;
|
15
|
+
}
|
16
|
+
if (!Character.isDigit(c)) {
|
17
|
+
throw new IllegalArgumentException("Illegal character in phone number. "
|
18
|
+
+ "Only digits, spaces, parentheses, hyphens or dots accepted.");
|
19
|
+
}
|
20
|
+
stringBuilder.append(c);
|
21
|
+
}
|
22
|
+
return stringBuilder.toString();
|
11
23
|
}
|
12
24
|
|
13
25
|
private String normalize(String number) {
|
14
|
-
if(number.length() > 11 || number.length() < 10){
|
26
|
+
if (number.length() > 11 || number.length() < 10) {
|
15
27
|
throw new IllegalArgumentException("Number must be 10 or 11 digits");
|
16
28
|
}
|
17
29
|
|
18
|
-
if(number.length() == 11){
|
19
|
-
if(number.startsWith("1")){
|
30
|
+
if (number.length() == 11) {
|
31
|
+
if (number.startsWith("1")) {
|
20
32
|
number = number.substring(1, number.length());
|
21
|
-
}
|
22
|
-
else{
|
33
|
+
} else {
|
23
34
|
throw new IllegalArgumentException("Can only have 11 digits if number starts with '1'");
|
24
35
|
}
|
25
|
-
}
|
26
|
-
|
36
|
+
}
|
37
|
+
|
27
38
|
return number;
|
28
39
|
}
|
29
40
|
|
30
41
|
public String getNumber() {
|
31
42
|
return number;
|
32
43
|
}
|
33
|
-
|
34
|
-
public String getAreaCode() {
|
35
|
-
return number.substring(0, 3);
|
36
|
-
}
|
37
|
-
|
38
|
-
public String getExchangeCode() {
|
39
|
-
return number.substring(3, 6);
|
40
|
-
}
|
41
|
-
|
42
|
-
public String getSubscriberNumber() {
|
43
|
-
return number.substring(6, 10);
|
44
|
-
}
|
45
|
-
|
46
|
-
public String pretty() {
|
47
|
-
return "(" + getAreaCode() + ") " + getExchangeCode() + "-" + getSubscriberNumber();
|
48
|
-
}
|
49
|
-
|
50
44
|
}
|