Created
January 15, 2014 15:09
-
-
Save danking/8437957 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/////////////////////////////////////////////////////////////////////////////// | |
// data | |
function throw2 (arg) { | |
throw arg; | |
} | |
function numberDatum() { } | |
function makeDefaultNumber() { | |
var n = new numberDatum() | |
n.isExact = false | |
n.base = 10 | |
n.value = NaN | |
return n | |
} | |
/////////////////////////////////////////////////////////////////////////////// | |
// parser | |
// we only do inexact reals. no exacts, no complex | |
// we also don't allow '#' to represent zero's whose value is unknown. | |
function read_number(str, i) { | |
var p = str.charAt(i); | |
var datum = makeDefaultNumber() | |
if(p === '#') { | |
i = read_number_exactness_and_radix(str, i, datum) | |
} | |
p = str.charAt(i) | |
// signed inexact or inf.0, nan.0 | |
if(/[+-]/.test(p)) { | |
i = read_signed_number_or_special(str, i, datum) | |
} else { | |
i = read_unsigned_number(str, i, datum) | |
} | |
var sexp = datum | |
sexp.location = i | |
// TODO: actually get location right | |
// sexp.location = new Location(sCol, sLine, iStart, i-iStart) | |
return sexp | |
} | |
function read_number_exactness_and_radix(str, i, datum) { | |
var p = str.charAt(i) | |
// read exactness and raidx | |
if(p === '#') { | |
if(/[ei]/.test(str.charAt(i+1))) { | |
// exactness first | |
i = read_number_exactness(str, i+1, datum) | |
if (str.charAt(i) === '#') { | |
i = read_number_radix(str, i+1, datum) | |
} | |
} else { | |
// radix first | |
i = read_number_radix(str, i+1, datum) | |
if (str.charAt(i) === '#') { | |
i = read_number_exactness(str, i+1, datum) | |
} | |
} | |
} | |
return i | |
} | |
function read_number_exactness(str, i, datum) { | |
datum.isExact = (str.charAt(i) === 'e') ? true : false | |
return i+1 | |
} | |
function read_number_radix(str, i, datum) { | |
if (i >= str.length) { | |
throw "read: end of string while reading a radix: string \"" + | |
str + "\", position " + i | |
} | |
datum.base = | |
(str.charAt(i) === 'b') ? 2 : | |
(str.charAt(i) === 'o') ? 8 : | |
(str.charAt(i) === 'd') ? 10 : | |
(str.charAt(i) === 'x') ? 16 : | |
(throw2("read_number_radix: invalid radix: " + str.charAt(i))) | |
return i+1 | |
} | |
// special refers to nan and infinity | |
function read_signed_number_or_special(str, i, datum) { | |
var sign = str.charAt(i) === '+' ? 1 : -1 | |
var digits = digits_for_radix(datum.base) | |
i = i + 1 | |
if(digits.test(str.charAt(i))) { | |
// a number | |
i = read_unsigned_number(str, i, datum) | |
datum.value = sign * datum.value | |
} else { | |
// nan or inf | |
var nanOrInf = str.substr(i, 5) | |
i = i+5 | |
if(nanOrInf === "inf.0" || nanOrInf === "inf.f") { | |
datum.value = Infinity | |
} else if (nanOrInf === "nan.0" || nanOrInf === "nan.f") { | |
datum.value = NaN | |
} else { | |
throw2("read: error, expected nan or inf: str \"" + | |
str + "\", position " + i) | |
} | |
} | |
return i | |
} | |
function read_unsigned_number(str, i, datum) { | |
var digits = digits_for_radix(datum.base) | |
i = read_digits(str, i, datum, digits) | |
i = maybe_read_slash_and_denominator(str, i, datum, digits) | |
i = maybe_read_exponent(str, i, datum, digits) | |
return i | |
} | |
function read_digits(str, i, datum, digits) { | |
var valueAndI = read_digits_to_js_num(str, i, datum.base, digits) | |
datum.value = valueAndI[0] | |
i = valueAndI[1] | |
if (isNaN(datum.value)) { | |
throw2("read: expected a base " + datum.base + " number") | |
} | |
return i | |
} | |
// in this function, datum contains the value of the numerator | |
// we'll read the denominator and then divide by it | |
function maybe_read_slash_and_denominator(str, i, datum, digits) { | |
if (str.charAt(i) === '/') { | |
i = i + 1 | |
var denominator = makeDefaultNumber() | |
denominator.base = datum.base | |
denominator.isExact = datum.base | |
i = read_digits(str, i, denominator, digits) | |
datum.value = datum.value / denominator.value | |
} | |
return i | |
} | |
function maybe_read_exponent(str, i, datum, digits) { | |
if(exp_mark_for_radix(datum.base).test(str.charAt(i))) { | |
// we ignore the single versus double precision distinction | |
i = i + 1 | |
var sign = 1 | |
if(/[-+]/.test(str.charAt(i))) { | |
sign = str.charAt(i) === '+' ? 1 : -1 | |
i = i + 1 | |
} | |
var exponentAndI = read_digits_to_js_num(str, i, datum.base, digits) | |
if (isNaN(exponentAndI[0])) { | |
throw2("read: expected an exponent in base " + datum.base) | |
} | |
var exponent = sign * exponentAndI[0] | |
i = exponentAndI[1] | |
datum.value = datum.value * Math.pow(10, exponent) | |
} | |
return i | |
} | |
function read_digits_to_js_num(str, i, base, digits) { | |
var len = 0 | |
while(i < str.length && | |
digits.test(str.charAt(i+len))) { | |
len++ | |
} | |
var digitString = str.substr(i, len) | |
// var intParser = Number.parseInt | |
var intParser = parseInt | |
return [intParser(digitString, base), i+len] | |
} | |
function digits_for_radix(radix) { | |
return radix === 2 ? /[01]/ : | |
radix === 8 ? /[0123]/ : | |
radix === 10 ? /[0123456789]/ : | |
radix === 16 ? /[0123456789abcdef]/ : | |
(throw2("digits_for_radix: invalid radix: " + radix)) | |
} | |
function exp_mark_for_radix(radix) { | |
return (radix === 2 || radix === 8 || radix === 10) ? /[sldef]/ : | |
radix === 16 ? /[sl]/ : | |
(throw2("digits_for_radix: invalid radix: " + radix)) | |
} | |
/////////////////////////////////////////////////////////////////////////////// | |
// tests | |
function testThis(str) { | |
return read_number(str, 0) | |
} | |
console.log(testThis("-1").value == -1) | |
console.log(testThis("1/2").value == 0.5) | |
console.log(testThis("1.0").value == 1.0) | |
// console.log(parse(lex("1+2i")).real == 1 ; parse(lex("1+2i")).imag == 2) | |
// console.log(parse(lex("1/2+3/4i")).real == 0.5; parse(lex("1/2+3/4i")).imag == 0.75) | |
console.log(testThis("2e5").value == 200000) | |
console.log(testThis("#i5").value == 5) | |
console.log(testThis("#e2e5").value == 200000) | |
console.log(testThis("#x2e5").value == 741) | |
console.log(testThis("#b101").value == 5) | |
// -1 reads equal to -1 | |
// 1/2 reads equal to (/ 1 2) | |
// 1.0 reads equal to (exact->inexact 1) | |
// 1+2i reads equal to (make-complex 1 2) | |
// 1/2+3/4i reads equal to (make-complex (/ 1 2) (/ 3 4)) | |
// 1.0+3.0e7i reads equal to (exact->inexact (make-complex 1 30000000)) | |
// 2e5 reads equal to (exact->inexact 200000) | |
// #i5 reads equal to (exact->inexact 5) | |
// #e2e5 reads equal to 200000 | |
// #x2e5 reads equal to 741 | |
// #b101 reads equal to 5 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment