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
159 | 1
1
1
1
1
1
494
1
494
494
1
494
1
1939
1
494
494
487
7
7
7
7
7
1
1
7
7
7
2
2
7
2
5
1
487
487
484
3
3
3
3
3
1
484
484
474
10
10
10
10
10
4
6
10
10
1
474
474
474
474
474
474
474
1
508
1
30
30
6
30
1
7
7
23
23
7
1
2
2
7
2
1
492
8
8
492
10
492
| (function(sc) {
"use strict";
require("./lexer");
var charlib = sc.libs.charlib;
var Token = sc.lang.compiler.Token;
var Lexer = sc.lang.compiler.Lexer;
Lexer.addLexMethod("Number", function(source, index) {
return new NumberLexer(source, index).scan();
});
function NumberLexer(source, index) {
this.source = source;
this.index = index;
}
NumberLexer.prototype.scan = function() {
return this.scanNAryNumberLiteral() ||
this.scanHexNumberLiteral() ||
this.scanAccidentalNumberLiteral() ||
this.scanDecimalNumberLiteral();
};
NumberLexer.prototype.match = function(re) {
return re.exec(this.source.slice(this.index));
};
NumberLexer.prototype.scanNAryNumberLiteral = function() {
var items = this.match(
/^(\d+)r((?:[\da-zA-Z](?:_(?=[\da-zA-Z]))?)+)(?:\.((?:[\da-zA-Z](?:_(?=[\da-zA-Z]))?)+))?/
);
if (!items) {
return;
}
var base = removeUnderscore(items[1])|0;
var integer = removeUnderscore(items[2]);
var frac = removeUnderscore(items[3]) || "";
var pi = false;
if (!frac && base < 26 && integer.substr(-2) === "pi") {
integer = integer.slice(0, -2);
pi = true;
}
var type = Token.IntegerLiteral;
var value = calcNBasedInteger(integer, base);
if (frac) {
type = Token.FloatLiteral;
value += calcNBasedFrac(frac, base);
}
if (isNaN(value)) {
return { error: true, value: items[0], length: items[0].length };
}
return makeNumberToken(type, value, pi, items[0].length);
};
NumberLexer.prototype.scanHexNumberLiteral = function() {
var items = this.match(/^(0x(?:[\da-fA-F](?:_(?=[\da-fA-F]))?)+)(pi)?/);
if (!items) {
return;
}
var integer = removeUnderscore(items[1]);
var pi = !!items[2];
var type = Token.IntegerLiteral;
var value = +integer;
return makeNumberToken(type, value, pi, items[0].length);
};
NumberLexer.prototype.scanAccidentalNumberLiteral = function() {
var items = this.match(/^(\d+)([bs]+)(\d*)/);
if (!items) {
return;
}
var integer = removeUnderscore(items[1]);
var accidental = items[2];
var sign = (accidental.charAt(0) === "s") ? +1 : -1;
var cents;
if (items[3] === "") {
cents = Math.min(accidental.length * 0.1, 0.4);
} else {
cents = Math.min(items[3] * 0.001, 0.499);
}
var value = +integer + (sign * cents);
return makeNumberToken(Token.FloatLiteral, value, false, items[0].length);
};
NumberLexer.prototype.scanDecimalNumberLiteral = function() {
var items = this.match(
/^((?:\d(?:_(?=\d))?)+((?:\.(?:\d(?:_(?=\d))?)+)?(?:e[-+]?(?:\d(?:_(?=\d))?)+)?))(pi)?/
);
var integer = +removeUnderscore(items[1]);
var frac = !!items[2];
var pi = !!items[3];
var type = (frac || pi) ? Token.FloatLiteral : Token.IntegerLiteral;
var value = integer;
return makeNumberToken(type, value, pi, items[0].length);
};
function removeUnderscore(str) {
return str && str.replace(/_/g, "");
}
function char2num(ch, base) {
var num = charlib.toNumber(ch);
if (num >= base) {
num = NaN;
}
return num;
}
function calcNBasedInteger(integer, base) {
var value = 0;
for (var i = 0, imax = integer.length; i < imax; ++i) {
value *= base;
value += char2num(integer[i], base);
}
return value;
}
function calcNBasedFrac(frac, base) {
var value = 0;
for (var i = 0, imax = frac.length; i < imax; ++i) {
value += char2num(frac[i], base) * Math.pow(base, -(i + 1));
}
return value;
}
function makeNumberToken(type, value, pi, length) {
if (pi) {
type = Token.FloatLiteral;
value = value * Math.PI;
}
if (type === Token.FloatLiteral && value === (value|0)) {
value = value + ".0";
}
return { type: type, value: String(value), length: length };
}
})(sc);
|