Skip to content

Commit

Permalink
[MOREL-228] Int structure
Browse files Browse the repository at this point in the history
Implement the `Int` structure from the standard basis
library. (The `Int` structure is an instance of the `INTEGER`
signature in the Standard Basis Library but Morel does not
currently have signatures.)

Fixes #228
  • Loading branch information
julianhyde committed Sep 26, 2024
1 parent 9c3425d commit 9a24fb5
Show file tree
Hide file tree
Showing 4 changed files with 482 additions and 33 deletions.
38 changes: 33 additions & 5 deletions docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,32 @@ Exception:
| General.op o | (β → γ) (α → β) → α → γ | "f o g" is the function composition of `f` and `g`. Thus, `(f o g) a` is equivalent to `f (g a)`. |
| Interact.use | string → unit | "use f" loads source text from the file named `f`. |
| Interact.useSilently | string → unit | "useSilently f" loads source text from the file named `f`, without printing to stdout. |
| Int op * | int * int → int | "i * j" is the product of `i` and `j`. It raises Overflow when the result is not representable. |
| Int op + | int * int → int | "i + j" is the sum of `i` and `j`. It raises Overflow when the result is not representable. |
| Int op - | int * int → int | "i - j" is the difference of `i` and `j`. It raises Overflow when the result is not representable. |
| Int op div | int * int → int | "i div j" returns the greatest integer less than or equal to the quotient of i by j, i.e., `floor(i / j)`. It raises Overflow when the result is not representable, or Div when `j = 0`. Note that rounding is towards negative infinity, not zero. |
| Int op mod | int * int → int | "i mod j" returns the remainder of the division of i by j. It raises Div when `j = 0`. When defined, `(i mod j)` has the same sign as `j`, and `(i div j) * j + (i mod j) = i`. |
| Int op < | int * int → bool | "x < y" returns true if x is less than y. Return false on unordered arguments, i.e., if either argument is NaN, so that the usual reversal of comparison under negation does not hold, e.g., `a < b` is not the same as `not (a >= b)`. |
| Int op <= | int * int → bool | As "<" |
| Int op > | int * int → bool | As "<" |
| Int op >= | int * int → bool | As "<" |
| Int op ~ | int → int | "~ i" returns the negation of `i`. |
| Int.abs | int → int | "abs i" returns the absolute value of `i`. |
| Int.compare | int * int → order | "compare (i, j)" returns `LESS`, `EQUAL`, or `GREATER` according to whether its first argument is less than, equal to, or greater than the second. |
| Int.fromInt, int | int → int | "fromInt i" converts a value from type `int` to the default integer type. Raises Overflow if the value does not fit. |
| Int.fromString | string → int option | "fromString s" scans a `int` value from a string. Returns `SOME(r)` if a `int` value can be scanned from a prefix of `s`, ignoring any initial whitespace; otherwise, it returns `NONE`. Equivalent to `StringCvt.scanString (scan StringCvt.DEC)`. |
| Int.max | int * int → int | "max (i, j)" returns the larger of the arguments. |
| Int.maxInt | int | "maxInt" is the maximal (most positive) integer representable by `int`. If a value is `NONE`, `int` can represent all positive integers, within the limits of the heap size. If `precision` is `SOME(n)`, then we have `maxInt` = 2<sup>(n-1)</sup> - 1. |
| Int.min | int * int &rarr; int | "min (i, j)" returns the smaller of the arguments. |
| Int.minVal | int | "minPos" is the minimal (most negative) integer representable by `int`. If a value is `NONE`, `int` can represent all negative integers, within the limits of the heap size. If `precision` is `SOME(n)`, then we have `minInt` = -2<sup>(n-1)</sup>. |
| Int.mod | int * int &rarr; int | "mod (i, j)" returns the remainder of the division of `i` by `j`. It raises `Div` when `j = 0`. When defined, `(i mod j)` has the same sign as `j, and `(i div j) * j + (i mod j) = i`. |
| Int.precision | int | "precision" is the precision. If `SOME(n)`, this denotes the number `n` of significant bits in type `int`, including the sign bit. If it is `NONE`, int has arbitrary precision. The precision need not necessarily be a power of two. |
| Int.quot | int * int &rarr; int | "quot (i, j)" returns the truncated quotient of the division of `i` by `j`, i.e., it computes `(i / j)` and then drops any fractional part of the quotient. It raises `Overflow` when the result is not representable, or `Div` when `j = 0`. Note that unlike `div`, `quot` rounds towards zero. In addition, unlike `div` and `mod`, neither `quot` nor `rem` are infix by default; an appropriate infix declaration would be `infix 7 quot rem`. This is the semantics of most hardware divide instructions, so `quot` may be faster than `div`. |
| Int.rem | int * int &rarr; int | "rem (i, j)" returns the remainder of the division of `i` by `j`. It raises `Div` when `j = 0`. `(i rem j)` has the same sign as i, and it holds that `(i quot j) * j + (i rem j) = i`. This is the semantics of most hardware divide instructions, so `rem` may be faster than `mod`. |
| Int.sameSign | int * int &rarr; bool | "sameSign (i, j)" returns true if `i` and `j` have the same sign. It is equivalent to `(sign i = sign j)`. |
| Int.sign | int &rarr; int | "sign i" returns ~1, 0, or 1 when `i` is less than, equal to, or greater than 0, respectively. |
| Int.toInt | int &rarr; int | "toInt i" converts a value from the default integer type to type `int`. Raises Overflow if the value does not fit. |
| Int.toString | int &rarr; string | "toString i" converts a `int` into a `string`; equivalent to `(fmt StringCvt.DEC r)`. |
| List.nil | &alpha; list | "nil" is the empty list. |
| List.null | &alpha; list &rarr; bool | "null l" returns `true` if the list `l` is empty. |
| List.length | &alpha; list &rarr; int | "length l" returns the number of elements in the list `l`. |
Expand Down Expand Up @@ -355,7 +381,7 @@ Exception:
| Option.composePartial | (&alpha; &rarr; &beta; option) * (&gamma; &rarr; &alpha; option) &rarr; &gamma; &rarr; &beta; option | "composePartial (f, g) a" returns `NONE` if `g(a)` is `NONE`; otherwise, if `g(a)` is `SOME v`, returns `f(v)`. |
| Option.map | &alpha; &rarr; &beta;) &rarr; &alpha; option &rarr; &beta; option | "map f opt" maps `NONE` to `NONE` and `SOME v` to `SOME (f v)`. |
| Option.mapPartial | &alpha; &rarr; &beta; option) &rarr; &alpha; option &rarr; &beta; option | "mapPartial f opt" maps `NONE` to `NONE` and `SOME v` to `f(v)`. |
| Option.getOpt | &alpha; option * &alpha; &rarr; &alpha; | "getOpt (opt, a)" returns `v` if `opt` is `SOME(v)`; otherwise returns `a`. |
| Option.getOpt | &alpha; option * &alpha; &rarr; &alpha; | "getOpt (opt, a)" returns `v` if `opt` is `SOME (v)`; otherwise returns `a`. |
| Option.isSome | &alpha; option &rarr; bool | "isSome opt" returns `true` if `opt` is `SOME v`; otherwise returns `false`. |
| Option.filter | (&alpha; &rarr; bool) &rarr; &alpha; &rarr; &alpha; option | "filter f a" returns `SOME a` if `f(a)` is `true`, `NONE` otherwise. |
| Option.join | &alpha; option option &rarr; &alpha; option | "join opt" maps `NONE` to `NONE` and `SOME v` to `v`. |
Expand All @@ -377,7 +403,7 @@ Exception:
| Real.floor | real &rarr; int | "floor r" produces `floor(r)`, the largest int not larger than `r`. |
| Real.fromInt, real | int &rarr; real | "fromInt i" converts the integer `i` to a `real` value. If the absolute value of `i` is larger than `maxFinite`, then the appropriate infinity is returned. If `i` cannot be exactly represented as a `real` value, uses current rounding mode to determine the resulting value. |
| Real.fromManExp | {exp:int, man:real} &rarr; real | "fromManExp r" returns `{man, exp}`, where `man` and `exp` are the mantissa and exponent of r, respectively. |
| Real.fromString | string &rarr; real option | "fromString s" scans a `real` value from a string. Returns `SOME(r)` if a `real` value can be scanned from a prefix of `s`, ignoring any initial whitespace; otherwise, it returns `NONE`. This function is equivalent to `StringCvt.scanString scan`. |
| Real.fromString | string &rarr; real option | "fromString s" scans a `real` value from a string. Returns `SOME (r)` if a `real` value can be scanned from a prefix of `s`, ignoring any initial whitespace; otherwise, it returns `NONE`. This function is equivalent to `StringCvt.scanString scan`. |
| Real.isFinite | real &rarr; bool | "isFinite x" returns true if x is neither NaN nor an infinity. |
| Real.isNan | real &rarr; bool | "isNan x" returns true if x NaN. |
| Real.isNormal | real &rarr; bool | "isNormal x" returns true if x is normal, i.e., neither zero, subnormal, infinite nor NaN. |
Expand Down Expand Up @@ -438,8 +464,8 @@ Exception:
| Vector.collate |
| Vector.concat | &alpha; vector list &rarr; &alpha; vector | "concat l" returns the vector that is the concatenation of the vectors in the list `l`. Raises `Size` if the total length of these vectors exceeds `maxLen` |
| Vector.exists |
| Vector.find | (&alpha; &rarr; bool) &rarr; &alpha; vector &rarr; &alpha; option | "find f vec" applies `f` to each element `x` of the vector `vec`, from left to right, until `f(x)` evaluates to `true`. It returns `SOME(x)` if such an `x` exists; otherwise it returns `NONE`. |
| Vector.findi | (int * &alpha; &rarr; bool) &rarr; &alpha; vector &rarr; (int * &alpha;) option | "findi f vec" applies `f` to each element `x` and element index `i` of the vector `vec`, from left to right, until `f(i, x)` evaluates to `true`. It returns `SOME(i, x)` if such an `x` exists; otherwise it returns `NONE`. |
| Vector.find | (&alpha; &rarr; bool) &rarr; &alpha; vector &rarr; &alpha; option | "find f vec" applies `f` to each element `x` of the vector `vec`, from left to right, until `f(x)` evaluates to `true`. It returns `SOME (x)` if such an `x` exists; otherwise it returns `NONE`. |
| Vector.findi | (int * &alpha; &rarr; bool) &rarr; &alpha; vector &rarr; (int * &alpha;) option | "findi f vec" applies `f` to each element `x` and element index `i` of the vector `vec`, from left to right, until `f(i, x)` evaluates to `true`. It returns `SOME (i, x)` if such an `x` exists; otherwise it returns `NONE`. |
| Vector.foldl | (&alpha; * &beta; &rarr; &beta;) &rarr; &beta; &rarr; &alpha; vector &rarr; &beta; | "foldl f init vec" folds the function `f` over all the elements of vector `vec`, left to right, using the initial value `init` |
| Vector.foldli | (int * &alpha; * &beta; &rarr; &beta;) &rarr; &beta; &rarr; &alpha; vector &rarr; &beta; | "foldli f init vec" folds the function `f` over all the (index, element) pairs of vector `vec`, left to right, using the initial value `init` |
| Vector.foldr | (&alpha; * &beta; &rarr; &beta;) &rarr; &beta; &rarr; &alpha; vector &rarr; &beta; | "foldr f init vec" folds the function `f` over all the elements of vector `vec`, right to left, using the initial value `init` |
Expand All @@ -457,6 +483,8 @@ Not yet implemented

| Name | Type | Description |
| ---- | ---- | ----------- |
| Int.fmt | StringCvt.radix &rarr; int &rarr; string | "fmt radix i" returns a string containing a representation of i with #"~" used as the sign for negative numbers. Formats the string according to `radix`; the hexadecimal digits 10 through 15 are represented as #"A" through #"F", respectively. No prefix "0x" is generated for the hexadecimal representation. |
| Int.scan | scan radix getc strm | Returns `SOME (i,rest)` if an integer in the format denoted by `radix` can be parsed from a prefix of the character stream `strm` after skipping initial whitespace, where `i` is the value of the integer parsed and `rest` is the rest of the character stream. `NONE` is returned otherwise. This function raises `Overflow` when an integer can be parsed, but is too large to be represented by type `int`. |
| Real op != | real * real &rarr; bool | "x != y" is equivalent to `not o op ==` and the IEEE `?<>` operator. |
| Real op *+ | real * real * real &rarr; real | "*+ (a, b, c)" returns `a * b + c`. Its behavior on infinities follows from the behaviors derived from addition and multiplication. |
| Real op *- | real * real * real &rarr; real | "*- (a, b, c)" returns `a * b - c`. Its behavior on infinities follows from the behaviors derived from subtraction and multiplication. |
Expand All @@ -469,7 +497,7 @@ Not yet implemented
| Real.fromLarge | IEEEReal.rounding_mode &rarr; real &rarr; real | "toLarge r" converts a value of type `real` to type `LargeReal.real`. If `r` is too small or too large to be represented as a real, converts it to a zero or an infinity. |
| Real.fromLargeInt | IntInf.int &rarr; real | See "fromInt" |
| Real.nextAfter | real * real &rarr; real | "nextAfter (r, t)" returns the next representable real after `r` in the direction of `t`. Thus, if `t` is less than `r`, `nextAfter` returns the largest representable floating-point number less than `r`. |
| Real.scan | (char,'a) StringCvt.reader &rarr; (real,'a) StringCvt.reader | "scan getc strm" scans a `real` value from character source. Reads from ARG/strm/ using reader `getc`, ignoring initial whitespace. It returns `SOME(r, rest)` if successful, where `r` is the scanned `real` value and `rest` is the unused portion of the character stream `strm`. Values of too large a magnitude are represented as infinities; values of too small a magnitude are represented as zeros. |
| Real.scan | (char,'a) StringCvt.reader &rarr; (real,'a) StringCvt.reader | "scan getc strm" scans a `real` value from character source. Reads from ARG/strm/ using reader `getc`, ignoring initial whitespace. It returns `SOME (r, rest)` if successful, where `r` is the scanned `real` value and `rest` is the unused portion of the character stream `strm`. Values of too large a magnitude are represented as infinities; values of too small a magnitude are represented as zeros. |
| Real.toDecimal | real &rarr; IEEEReal.decimal_approx | "toDecimal r" converts a `real` to a decimal approximation |
| Real.toInt | real &rarr; IEEEReal.rounding_mode &rarr; int | "toInt mode x" converts the argument `x` to an integral type using the specified rounding mode. It raises `Overflow` if the result is not representable, in particular, if `x` is an infinity. It raises `Domain` if the input real is NaN. |
| Real.toLarge | real &rarr; real | "toLarge r" convert a value of type `real` to type `LargeReal.real`. |
Expand Down
113 changes: 113 additions & 0 deletions src/main/java/net/hydromatic/morel/compile/BuiltIn.java
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,119 @@ public enum BuiltIn {
ts.fnType(h.get(0), h.get(1))),
ts.fnType(h.get(0), h.get(2))))),

/* TODO:
val ~ : int -> int
val * : int * int -> int
val div : int * int -> int
val mod : int * int -> int
val quot : int * int -> int
val rem : int * int -> int
val + : int * int -> int
val - : int * int -> int
val > : int * int -> bool
val >= : int * int -> bool
val < : int * int -> bool
val <= : int * int -> bool
*/

/** Function "Int.abs" of type "int &rarr; int". */
INT_ABS("Int", "abs", ts -> ts.fnType(INT, INT)),

/** Function "Int.compare", of type "int * int &rarr; order". */
INT_COMPARE("Int", "compare", ts ->
ts.fnType(ts.tupleType(INT, INT), ts.lookup("order"))),

/** Function "Int.div", of type "int * int &rarr; int". */
INT_DIV("Int", "div", ts -> ts.fnType(ts.tupleType(INT, INT), INT)),

/** Function "Int.toInt", of type "int &rarr; int". */
INT_TO_INT("Int", "toInt", ts -> ts.fnType(INT, INT)),

/** Function "Int.fromInt", of type "int &rarr; int". */
INT_FROM_INT("Int", "fromInt", ts -> ts.fnType(INT, INT)),

/** Function "Int.toLarge", of type "int &rarr; int". */
INT_TO_LARGE("Int", "toLarge", ts -> ts.fnType(INT, INT)),

/** Function "Int.fromLarge", of type "int &rarr; int". */
INT_FROM_LARGE("Int", "fromLarge", ts -> ts.fnType(INT, INT)),

/** Function "Int.fromString s", of type "string &rarr; int option",
* scans a {@code int} value from a {@code string}. Returns {@code SOME(r)}
* if a {@code int} value can be scanned from a prefix of {@code s}, ignoring
* any initial whitespace; otherwise, it returns {@code NONE}. This function
* is equivalent to {@code StringCvt.scanString scan}. */
INT_FROM_STRING("Int", "fromString", ts ->
ts.fnType(STRING, ts.option(INT))),

/** Constant "Int.minInt", of type "int option". */
INT_MIN_INT("Int", "minInt", ts -> ts.option(INT)),

/** Constant "Int.maxInt", of type "int option". */
INT_MAX_INT("Int", "maxInt", ts -> ts.option(INT)),

/** Constant "Int.precision", of type "int option". */
INT_PRECISION("Int", "precision", ts -> ts.option(INT)),

/** Function "Int.max", of type "int * int &rarr; int".
*
* <p>Returns the returns the larger of the arguments. If exactly one argument
* is NaN, returns the other argument. If both arguments are NaN, returns
* NaN. */
INT_MAX("Int", "max", ts -> ts.fnType(ts.tupleType(INT, INT), INT)),

/** Function "Int.min", of type "int * int &rarr; int".
*
* <p>Returns the returns the larger of the arguments. If exactly one argument
* is NaN, returns the other argument. If both arguments are NaN, returns
* NaN. */
INT_MIN("Int", "min", ts -> ts.fnType(ts.tupleType(INT, INT), INT)),

/** Function "Int.quot", of type "int * int &rarr; int". */
INT_QUOT("Int", "quot", ts -> ts.fnType(ts.tupleType(INT, INT), INT)),

/** Function "Int.mod", of type "int * int &rarr; int".
*
* <p>Returns the fractional part of r. "intMod" is equivalent to
* "#frac o split". */
INT_MOD("Int", "mod", ts -> ts.fnType(ts.tupleType(INT, INT), INT)),

/** Function "Int.rem", of type "int * int &rarr; int".
*
* <p>Returns the remainder {@code x - n * y}, where
* {@code n = trunc (x / y)}. The result has the same sign as {@code x} and
* has absolute value less than the absolute value of {@code y}. If {@code x}
* is an infinity or {@code y} is 0, returns NaN. If {@code y} is an infinity,
* returns {@code x}. */
INT_REM("Int", "rem", ts -> ts.fnType(ts.tupleType(INT, INT), INT)),

/** Function "Int.sameSign", of type "int * int &rarr; bool".
*
* <p>Returns true if and only if {@code signBit r1} equals
* {@code signBit r2}. */
INT_SAME_SIGN("Int", "sameSign", ts ->
ts.fnType(ts.tupleType(INT, INT), BOOL)),

/** Function "Int.sign", of type "int &rarr; int".
*
* <p>Returns ~1 if r is negative, 0 if r is zero, or 1 if r is positive.
* An infinity returns its sign; a zero returns 0 regardless of its sign.
* It raises
* {@link net.hydromatic.morel.eval.Codes.BuiltInExn#DOMAIN Domain}
* on NaN. */
INT_SIGN("Int", "sign", ts -> ts.fnType(INT, INT)),

/** Function "Int.toString", of type "int &rarr; string".
*
* <p>"toString r" converts ints into strings. The value returned by
* {@code toString t} is equivalent to:
*
* <pre>{@code
* (fmt (StringCvt.GEN NONE) r)
* }</pre>
*/
INT_TO_STRING("Int", "toString", ts -> ts.fnType(INT, STRING)),

/** Function "Interact.use" of type "string &rarr; unit"
*
* <p>"use f" loads source text from the file named `f`. */
Expand Down
Loading

0 comments on commit 9a24fb5

Please sign in to comment.