@@ -212,82 +212,101 @@ define("pyret-base/js/js-numbers", function() {
212212
213213
214214 // isPyretNumber: any -> boolean
215- // Returns true if the thing is a pyretnum
215+ // Pure predicate. Raw JS numbers must be integers (the fixnum
216+ // representation) to count as pyretnums; non-integer JS numbers
217+ // are not valid pyretnums and return false.
216218 var isPyretNumber = function ( thing ) {
217- return ( typeof ( thing ) === 'number'
218- || ( thing instanceof Rational ||
219- thing instanceof Roughnum ||
220- thing instanceof BigInteger ) ) ;
219+ if ( typeof ( thing ) === 'number' ) return Number . isInteger ( thing ) ;
220+ return ( thing instanceof Rational ||
221+ thing instanceof Roughnum ||
222+ thing instanceof BigInteger ) ;
223+ } ;
224+
225+ // assertPyretNumber: contract check for entry-point operations that
226+ // require a pyretnum. Throws a domainError for clearly wrong types
227+ // (string, null, etc.). For non-integer JS numbers, logs to
228+ // console.error rather than throwing — we don't yet have full
229+ // coverage of all call sites that may pass these in, and an
230+ // exception in the wild from existing code we haven't audited would
231+ // be worse than the current lax behavior. Flip the non-integer
232+ // branch to errbacks.throwDomainError once we're confident.
233+ var assertPyretNumber = function ( thing , callerName ) {
234+ if ( typeof thing === 'number' ) {
235+ if ( ! Number . isInteger ( thing ) ) {
236+ console . error (
237+ 'js-numbers ' + callerName +
238+ ': non-integer JS number leaked into pyretnum: ' + thing ,
239+ new Error ( 'stack' ) . stack ) ;
240+ }
241+ return ;
242+ }
243+ if ( thing instanceof Rational ||
244+ thing instanceof Roughnum ||
245+ thing instanceof BigInteger ) {
246+ return ;
247+ }
248+ errbacks . throwDomainError (
249+ callerName + ': arg ' + thing + ' is not a number.' ) ;
221250 } ;
222251
223252 // isRational: pyretnum -> boolean
224253 var isRational = function ( n ) {
225- return ( typeof ( n ) === 'number' ||
226- ( isPyretNumber ( n ) && n . isRational ( ) ) ) ;
254+ if ( ! isPyretNumber ( n ) ) return false ;
255+ if ( typeof ( n ) === 'number' ) return true ;
256+ return n . isRational ( ) ;
227257 } ;
228258
229259 var isExact = isRational ;
230260
231261 // isReal: pyretnum -> boolean
232262 var isReal = function ( n ) {
233- return ( typeof ( n ) === 'number' ||
234- ( isPyretNumber ( n ) && n . isReal ( ) ) ) ;
263+ if ( ! isPyretNumber ( n ) ) return false ;
264+ if ( typeof ( n ) === 'number' ) return true ;
265+ return n . isReal ( ) ;
235266 } ;
236267
237268 // isInteger: pyretnum -> boolean
238269 var isInteger = function ( n ) {
239- if ( typeof ( n ) === 'number' ) return Number . isInteger ( n ) ;
240- if ( isPyretNumber ( n ) ) return n . isInteger ( ) ;
241- return false ;
270+ if ( ! isPyretNumber ( n ) ) return false ;
271+ if ( typeof ( n ) === 'number' ) return true ;
272+ return n . isInteger ( ) ;
242273 } ;
243274
244275 var isRoughnum = function ( n ) {
245- if ( typeof ( n ) === 'number' ) {
246- return false ;
247- } else {
248- return ( isPyretNumber ( n ) && n . isRoughnum ( ) ) ;
249- }
276+ if ( ! isPyretNumber ( n ) ) return false ;
277+ if ( typeof ( n ) === 'number' ) return false ;
278+ return n . isRoughnum ( ) ;
250279 } ;
251280
252281 var isPositive = function ( n ) {
253- if ( typeof ( n ) === 'number' ) {
254- return n > 0 ;
255- } else {
256- return ( isPyretNumber ( n ) && n . isPositive ( ) ) ;
257- }
282+ if ( ! isPyretNumber ( n ) ) return false ;
283+ if ( typeof ( n ) === 'number' ) return n > 0 ;
284+ return n . isPositive ( ) ;
258285 } ;
259286
260287 var isNonPositive = function ( n ) {
261- if ( typeof ( n ) === 'number' ) {
262- return n <= 0 ;
263- } else {
264- return ( isPyretNumber ( n ) && n . isNonPositive ( ) ) ;
265- }
288+ if ( ! isPyretNumber ( n ) ) return false ;
289+ if ( typeof ( n ) === 'number' ) return n <= 0 ;
290+ return n . isNonPositive ( ) ;
266291 } ;
267292
268293 var isNegative = function ( n ) {
269- if ( typeof ( n ) === 'number' ) {
270- return n < 0 ;
271- } else {
272- return ( isPyretNumber ( n ) && n . isNegative ( ) ) ;
273- }
294+ if ( ! isPyretNumber ( n ) ) return false ;
295+ if ( typeof ( n ) === 'number' ) return n < 0 ;
296+ return n . isNegative ( ) ;
274297 } ;
275298
276299 var isNonNegative = function ( n ) {
277- if ( typeof ( n ) === 'number' ) {
278- return n >= 0 ;
279- } else {
280- return ( isPyretNumber ( n ) && n . isNonNegative ( ) ) ;
281- }
300+ if ( ! isPyretNumber ( n ) ) return false ;
301+ if ( typeof ( n ) === 'number' ) return n >= 0 ;
302+ return n . isNonNegative ( ) ;
282303 } ;
283304
284305 // toFixnum: pyretnum -> javascript-number
285306 var toFixnum = function ( n ) {
286- if ( typeof ( n ) === 'number' )
287- return n ;
288- if ( isPyretNumber ( n ) )
289- return n . toFixnum ( ) ;
290- errbacks . throwDomainError ( 'toFixnum: arg ' + n + ' is not a number.' ) ;
307+ assertPyretNumber ( n , 'toFixnum' ) ;
308+ if ( typeof ( n ) === 'number' ) return n ;
309+ return n . toFixnum ( ) ;
291310 } ;
292311
293312 // toRational: pyretnum -> pyretnum
@@ -875,14 +894,14 @@ define("pyret-base/js/js-numbers", function() {
875894 return atan ( divide ( y , x ) ) ;
876895 } else { // x > 0, y < 0, 4th qdt
877896 // atan(y/x) is the 4th qdt and negative, so make it positive by adding 2pi
878- return add ( atan ( divide ( y , x ) ) , 2 * Math . PI ) ;
897+ return add ( atan ( divide ( y , x ) ) , Roughnum . makeInstance ( 2 * Math . PI ) ) ;
879898 }
880899 } else { // x < 0
881900 // either x < 0, y >= 0 (2nd qdt), in which case
882901 // atan(y/x) must be reflected from 4th to 2nd qdt, by adding pi
883902 // or x < 0, y < 0 (3rd qdt), in which case
884903 // atan(y/x) must be reflected from 1st to 3rd qdt, again by adding pi
885- return add ( atan ( divide ( y , x ) ) , Math . PI ) ;
904+ return add ( atan ( divide ( y , x ) ) , Roughnum . makeInstance ( Math . PI ) ) ;
886905 }
887906 } ;
888907
0 commit comments