-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathword.go
158 lines (127 loc) · 3.95 KB
/
word.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package neng
import (
"strings"
"github.com/Zedran/neng/symbols"
)
// Word represents a single word list entry.
type Word struct {
// Form type
ft FormType
// A slice of irregular forms or nil
irr *[]string
// Word from the list
word string
}
// Returns FormType of the Word.
func (w *Word) FT() FormType {
return w.ft
}
// Returns an irregular form of the Word at index i of the underlying slice
// of irregular forms. Returns an error if called for a non-irregular word
// or if i is out of bounds of the slice.
func (w *Word) Irr(i int) (string, error) {
if w.ft != FT_IRREGULAR {
return "", symbols.ErrNonIrregular
}
if i < 0 || i >= len(*w.irr) {
return "", symbols.ErrOutOfBounds
}
return (*w.irr)[i], nil
}
// Word returns base form of the word.
func (w *Word) Word() string {
return w.word
}
// NewWord parses a single word list line into a new word struct.
// Returns an error if malformed line is encountered.
func NewWord(line string) (*Word, error) {
if len(line) < 2 {
// Line must contain at least two characters:
// - a single digit denoting a type
// - a word at least one character in length
return nil, symbols.ErrBadWordList
}
w := Word{
// Read the FormType by subtracting ASCII zero from the first byte
ft: FormType(line[0] - 48),
}
if w.ft < FT_REGULAR || w.ft > FT_UNCOUNTABLE {
// FormType must be within range of the defined values.
// Coincidentally, this expression returns an error
// if a comma begins the line.
return nil, symbols.ErrBadWordList
}
// Find the first comma
c1 := strings.IndexByte(line, ',')
if w.ft != FT_IRREGULAR {
if c1 != -1 {
// Only irregular words can have irregular forms
return nil, symbols.ErrBadWordList
}
// Assign value to word field - from index 1 to the end of the line
w.word = line[1:]
return &w, nil
}
if c1 <= 1 || c1 == len(line)-1 {
// -1 - No comma, there must be at least one in irregular word's line
// 0 - Checked above already, comma cannot begin the line
// 1 - If the first comma is found at index 1, the word
// has the length of zero
// or - Comma cannot end the line
return nil, symbols.ErrBadWordList
}
// Assign value to word field - from index 1 to the first comma
w.word = line[1:c1]
c1++
// Find a second comma.
// Substring operation - counts from the first comma,
// not from the beginning of a line
c2 := strings.IndexByte(line[c1:], ',')
if c2 == -1 {
// If there is no second comma, the word has only one irregular form
// The condition above ensures it is not zero-length at this point
// (comma does not end the line)
w.irr = &[]string{line[c1:]}
return &w, nil
}
c2 += c1
if c2 == len(line)-1 || strings.IndexByte(line[c2+1:], ',') != -1 {
// Comma at the end of the line means the second word is zero-length
// or - more commas mean more irregular forms - two at most are allowed
return nil, symbols.ErrBadWordList
}
// Assign both irregular forms to a word
w.irr = &[]string{line[c1:c2], line[c2+1:]}
return &w, nil
}
// NewWordFromParams returns a Word struct built from the specified parameters,
// or error, if the following conditions are not met:
//
// - word must be at least 1 character long
// - ft must be in range of the defined FormType values
// - for irregular words, the length of irr must be 1 or 2
// and the elements cannot be empty strings
// - for non-irregular words, irr must be empty
func NewWordFromParams(word string, ft FormType, irr []string) (*Word, error) {
if len(word) == 0 {
return nil, symbols.ErrEmptyWord
}
if ft < FT_REGULAR || ft > FT_UNCOUNTABLE {
return nil, symbols.ErrUndefinedFormType
}
var pIrr *[]string
if ft == FT_IRREGULAR {
if len(irr) != 1 && len(irr) != 2 {
return nil, symbols.ErrMalformedIrr
}
for _, e := range irr {
if len(e) == 0 {
return nil, symbols.ErrMalformedIrr
}
}
pIrr = &irr
} else if len(irr) > 0 {
return nil, symbols.ErrNonIrregular
}
return &Word{ft: ft, irr: pIrr, word: word}, nil
}