2 // TODO parse following "w"
4 var TengwarParmaite
= require("./tengwar-parmaite");
5 var Parser
= require("./parser");
6 var Notation
= require("./notation");
7 var makeDocumentParser
= require("./document-parser");
8 var normalize
= require("./normalize");
9 var punctuation
= require("./punctuation");
10 var parseNumber
= require("./numbers");
12 exports
.name
= "Mode of Beleriand";
15 exports
.makeOptions
= makeOptions
;
16 function makeOptions(options
) {
17 options
= options
|| defaults
;
19 font
: options
.font
|| TengwarParmaite
,
22 duodecimal
: options
.duodecimal
26 exports
.transcribe
= transcribe
;
27 function transcribe(text
, options
) {
28 options
= makeOptions(options
);
29 var font
= options
.font
;
30 return font
.transcribe(parse(text
, options
), options
);
33 exports
.encode
= encode
;
34 function encode(text
, options
) {
35 options
= makeOptions(options
);
36 return Notation
.encode(parse(text
, options
), options
);
39 var parse
= exports
.parse
= makeDocumentParser(parseNormalWord
, makeOptions
);
41 function parseNormalWord(callback
, options
) {
42 return normalize(parseWord(callback
, options
));
45 function parseWord(callback
, options
, columns
) {
46 columns
= columns
|| [];
47 return parseColumn(function (column
) {
52 columns
.concat([column
])
55 return function (character
) {
56 if (/\d/.test(character
)) {
57 return parseNumber(function (number
) {
58 return parseWord(callback
, options
, columns
.concat(number
));
59 }, options
)(character
);
61 return callback(columns
)(character
);
68 function parseColumn(callback
, options
) {
69 return parseTengwa(function (column
) {
71 return parseFollowingS(callback
, column
);
78 function parseTengwa(callback
, options
) {
79 var font
= options
.font
;
80 var makeColumn
= font
.makeColumn
;
81 return function (character
) {
82 if (character
=== "n") { // n
83 return function (character
) {
84 if (character
=== "t" || character
=== "d") { // n{t,d}
85 return parseTengwa(function (column
) {
86 return callback(column
.addTildeAbove());
87 }, options
)(character
);
88 } else if (character
=== "c" || character
=== "g") { // n{c,g}
89 return parseTengwa(callback
, options
)("ñ")(character
);
90 } else if (character
=== "n") { // nn
91 return callback(makeColumn("numen", {from
: "nn"}));
93 return callback(makeColumn("ore", {from
: "n"}))(character
);
96 } else if (character
=== "t") { // t
97 return function (character
) {
98 if (character
=== "h") { // th
99 return callback(makeColumn("thule", {from
: "th"}));
101 return callback(makeColumn("tinco", {from
: "t"}))(character
);
104 } else if (character
=== "d") { // d
105 return function (character
) {
106 if (character
=== "h") { // dh
107 return callback(makeColumn("anto", {from
: "dh"}));
109 return callback(makeColumn("ando", {from
: "d"}))(character
);
112 } else if (character
=== "m") { // m
113 return function (character
) {
115 character
=== "p" || character
=== "b" ||
116 character
=== "f" || character
=== "v"
118 return parseTengwa(function (column
) {
119 return callback(column
.addTildeAbove({from
: character
}));
120 }, options
)(character
);
121 } else if (character
=== "m") { // mm
122 return callback(makeColumn("malta", {from
: "mm"}));
124 return callback(makeColumn("vala", {from
: "m"}))(character
);
127 } else if (character
=== "p") { // p
128 return callback(makeColumn("parma", {from
: "p"}));
129 } else if (character
=== "b") { // b
130 return callback(makeColumn("umbar", {from
: "b"}));
131 } else if (character
=== "f") { // f
132 return function (character
) {
133 if (Parser
.isFinal(character
)) { // f final
134 return callback(makeColumn("ampa", {from
: "f", final
: true}))(character
);
136 return callback(makeColumn("formen", {from
: "f", medial
: true}))(character
);
139 } else if (character
=== "v") { // v
140 return callback(makeColumn("ampa", {from
: "v"}));
141 } else if (character
=== "ñ") { // ñ
142 return function (character
) {
143 if (character
=== "c" || character
=== "g") {
144 return parseTengwa(function (column
) {
145 if (column
.tengwa
=== "halla") {
146 column
.addError("Lenited G (halla) should not be nasalized with prefix N");
148 return callback(column
.addTildeAbove({from
: characte
}));
149 }, options
)(character
);
151 return callback(makeColumn("noldo", {from
: "ñ"}))(character
);
154 } else if (character
=== "c") { // c
155 return function (character
) {
156 if (character
=== "h") { // ch
157 return function (character
) {
158 if (character
=== "w") { // chw
159 return callback(makeColumn("hwesta", {from
: "chw"}));
161 return callback(makeColumn("harma", {from
: "ch"}))(character
);
164 } else if (character
=== "w") { // cw
165 return callback(makeColumn("quesse", {from
: "cw"}));
167 return callback(makeColumn("calma", {from
: "c"}))(character
);
170 } else if (character
=== "g") {
171 return function (character
) {
172 if (character
=== "h") { // gh
173 return function (character
) {
174 if (character
=== "w") { // ghw
175 return callback(makeColumn("unque", {from
: "ghw"}));
177 return callback(makeColumn("anca", {from
: "gh"}))(character
);
180 } else if (character
=== "w") { // gw
181 return callback(makeColumn("ungwe", {from
: "gw"}));
182 } else if (character
=== "'") { // g'
183 return callback(makeColumn("halla", {from
: "g"})); // gasdil
185 return callback(makeColumn("anga", {from
: "g"}).varies())(character
);
188 } else if (character
=== "r") { // r
189 return function (character
) {
190 if (character
=== "h") { // rh
191 return callback(makeColumn("arda", {from
: "rh"}));
193 return callback(makeColumn("romen", {from
: "r"}))(character
);
196 } else if (character
=== "l") { // l
197 return function (character
) {
198 if (character
=== "h") { // lh
199 return callback(makeColumn("alda", {from
: "lh"}));
201 return callback(makeColumn("lambe", {from
: "l"}))(character
);
204 } else if (character
=== "s") { // s
205 return callback(makeColumn("silme", {from
: "s"}));
206 } else if (character
=== "a") { // a
207 return function (character
) {
208 if (character
=== "i") { // ai
209 return callback(makeColumn("round-carrier", {from
: "a", diphthong
: true}).addAbove("í", {from
: "i"}));
210 } else if (character
=== "u") { // au
211 return callback(makeColumn("round-carrier", {from
: "a", diphthong
: true}).addAbove("w", {from
: "u"}));
212 } else if (character
=== "'") { // a'
213 return callback(makeColumn("round-carrier", {from
: "a"}).addAbove("i", {from
: "a"}));
214 } else if (character
=== "a") { // aa
215 return callback(makeColumn("round-carrier", {from
: "a", long: true}).addAbove("e", {from
: "a"}));
217 return callback(makeColumn("round-carrier", {from
: "a"}).varies())(character
);
220 } else if (character
=== "e" || character
=== "ë") { // e
221 return function (character
) {
222 if (character
=== "i") { // ei
223 return callback(makeColumn("yanta", {from
: "e"}).addAbove("í", {from
: "i"}));
224 } else if (character
=== "e") {
225 return callback(makeColumn("yanta", {from
: "e", long: true}).addAbove("e", {from
: "e"}));
227 return callback(makeColumn("yanta", {from
: "e"}))(character
);
230 } else if (character
=== "i") { // i
231 return function (character
) {
232 if (character
=== "i") { // ii -> í
233 return parseColumn(callback
, options
)("í");
235 return Parser
.countPrimes(function (primes
) {
237 return callback(makeColumn("short-carrier", {from
: "i"}).varies());
238 } else if (primes
=== 1) {
239 return callback(makeColumn("short-carrier", {from
: "i"}).addAbove("i", {from
: ""}).varies());
240 } else if (primes
=== 2) {
241 return callback(makeColumn("long-carrier", {from
: "i", long: true}).addAbove("i", {from
: ""}).varies());
242 } else if (primes
=== 3) {
243 return callback(makeColumn("long-carrier", {from
: "i", long: true}));
245 return callback(makeColumn("long-carrier").addAbove("i").addError("I only has four variants between short or long and dotted or not."));
250 } else if (character
=== "o") {
251 return function (character
) {
252 if (character
=== "o") { // oo
253 return callback(makeColumn("anna", {from
: "o"}).addAbove("e", {from
: "o"}));
255 return callback(makeColumn("anna", {from
: "o"}))(character
);
258 } else if (character
=== "u") {
259 return function (character
) {
260 if (character
=== "i") {
261 return callback(makeColumn("ure", {from
: "u", diphthong
: true}).addAbove("í", {from
: "i"}));
262 } else if (character
=== "u") {
263 return callback(makeColumn("ure", {from
: "u", long: true}).addAbove("e", {from
: "u"}));
265 return callback(makeColumn("ure", {from
: "u"}))(character
);
268 } else if (character
=== "w") { // w
269 return function (character
) {
270 if (character
=== "w") { // ww
271 return callback(makeColumn("wilya", {from
: "w"}).addAbove("e", {from
: "w"}));
273 return callback(makeColumn("wilya", {from
: "w"}))(character
);
276 } else if (character
=== "y") {
277 return function (character
) {
278 if (character
=== "y") { // yy
279 return callback(makeColumn("silme-nuquerna", {from
: "y"}).addAbove("e", {from
: "y"}));
281 return callback(makeColumn("silme-nuquerna", {from
: "y"}))(character
);
284 } else if (character
=== "á") {
285 return callback(makeColumn("round-carrier", {from
: "a", long: true}).addAbove("e", {from
: "a"}));
286 } else if (character
=== "é") {
287 return callback(makeColumn("yanta", {from
: "é"}).addAbove("e"));
288 } else if (character
=== "í") {
289 return Parser
.countPrimes(function (primes
) {
291 return callback(makeColumn("short-carrier", {from
: "i"}).addAbove("e", {from
: "i"}).varies());
292 } else if (primes
=== 1) {
293 return callback(makeColumn("long-carrier", {from
: "í", long: true}).addAbove("e", {from
: ""}));
295 return callback(makeColumn("long-carrier").addAbove("e").addError("Í only has one variant."));
298 } else if (character
=== "ó") {
299 return callback(makeColumn("anna", {from
: "ó", long: true}).addAbove("e", {from
: ""}));
300 } else if (character
=== "ú") {
301 return callback(makeColumn("ure", {from
:"ú", long: true}).addAbove("e", {from
: ""}));
302 } else if (character
=== "h") {
303 return function (character
) {
304 //if (character === "m") { // TODO
305 // return callback(makeColumn("ore-nasalized"));
306 if (character
=== "w") {
307 return callback(makeColumn("hwesta-sindarinwa", {from
: "hw"}));
309 return callback(makeColumn("hyarmen", {from
: "h"}))(character
);
312 } else if (character
=== "z") {
313 return callback(makeColumn("silme", {from
: "z"}).addError("Z does not appear in the mode of Beleriand"));
314 } else if (punctuation
[character
]) {
315 return callback(makeColumn(punctuation
[character
], {from
: character
}));
316 } else if (Parser
.isBreak(character
) || /\d/.test(character
)) {
317 return callback()(character
);
319 return callback(makeColumn("anna", {from
: character
}).addError("Unexpected character: " + JSON
.stringify(character
)));
324 function parseFollowingS(callback
, column
) {
325 return function (character
) {
326 if (character
=== "s") {
327 if (column
.canAddBelow("s")) {
328 return callback(column
.addBelow("s", {from
: "s"}));
330 return Parser
.countPrimes(function (primes
) {
331 return function (character
) {
332 if (Parser
.isFinal(character
)) { // end of word
333 if (column
.canAddFollowing("s-final") && primes
-- === 0) {
334 column
.addFollowing("s-final", {from
: "s"});
335 } else if (column
.canAddFollowing("s-inverse") && primes
-- === 0) {
336 column
.addFollowing("s-inverse");
337 } else if (column
.canAddFollowing("s-extended") && primes
-- === 0) {
338 column
.addFollowing("s-extended", {from
: "s"});
339 } else if (column
.canAddFollowing("s-flourish")) {
340 column
.addFollowing("s-flourish", {from
: "s"});
343 "Following S only has 3 alternate " +
348 return callback(column
)("s", {from
: "s"})(character
);
350 return callback(column
)(character
);
352 return callback(column
)("s", {from
: "s"})(character
);
358 return callback(column
)(character
);