Skip to content

Commit

Permalink
Implement range() function and tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
toggledbits committed Oct 24, 2023
1 parent e762790 commit 3cd1b76
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 10 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

**NOTE:** In order to *build* lexpjs with Unicode-friendly identifiers enabled (if, for some reason, the included pre-built `lexp.js` file doesn't suit your needs), you first need to modify *jison-lex* to allow Unicode property escapes in its *RegExp*s. See `README-lexer.md` for details.

## 1.0.23297

* Add `range()` function with more flexibility than short-cut range operator (`..`).

## 1.0.23296

* Add `asin()`, `acos()`, `atan()` and `atan2()`, which function identically to their JavaScript `Math` library counterparts.
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ The *coalesce operators*, borrowed from C#, are `??`, `?#`, `?.` and `?[`. Coale

The `in` operator is used to establish if an object contains a specified key (e.g. `key in obj`) or an array contains an element at the given index (e.g. `15 in arr`). It is important to note that this operator works on *keys* only, not values, and in particular, cannot be used to search an array for a value (i.e. `4 in [ 4, 5, 6 ]` is *false*). To find an array element, use the `indexOf()` function. The `first` statement can be used to find a value in an object.

The `..` range operator produces an array containing all integers from the left operand to the right, so `3..6` results in `[3,4,5,6]`. A `for`-style counting loop can be implemented using `each` with the range operator as its operand: `each i in 0..9: <statement>` would execute `<statement>` 10 times.
The `..` range operator produces an array containing all integers from the left operand to the right, so `3..6` results in `[3,4,5,6]`. A `for`-style counting loop can be implemented using `each` with the range operator as its operand: `each i in 0..9: <statement>` would execute `<statement>` 10 times. The increment for the range operator is always 1 (or -1 if the ending value is less than the starting value); if you need a different increment, use the `range()` function.

Multiple expressions can be chained together by separating them with a comma. The result of a chained expression is the last expression evaluated.

Expand Down Expand Up @@ -363,7 +363,7 @@ Important notes with respect to date handling (currently; this will evolve):

### Array/Object Handling Functions

* `len( array )` &mdash; returns the number of elements in the array;
* `len( array )` &mdash; returns the number of elements in the array (including any `null` elements; also see `count()`);
* `keys( object )` &mdash; returns, as an array, the keys in the given object;
* `values( object )` &mdash; returns, as an array, the values in the given object;
* `clone( various )` &mdash; returns a (deep) copy of its argument; this is particularly useful for arrays and objects;
Expand All @@ -386,6 +386,7 @@ Important notes with respect to date handling (currently; this will evolve):
* `arrayExclusive( a, b )` &mdash; returns a new array containing all values of the arrays *a* and *b* that appear only in either, but not both (this is often referred to as the *symmetric difference*); for example, `arrayExclusive( [1,2,3], [1,3,5] )` returns `[2,5]`;
* `arrayUnion( a, b )` &mdash; returns a new array containing all values of the arrays *a* and *b*; for example, `arrayUnion( [1,2,3], [1,3,5] )` returns `[1,2,3,5]`;
* `sort( array [, comparison] )` &mdash; sort the given array, returning a new array (the given array is not modified). The array to be sorted may contain data of any type. The default sort is a case-sensitive ascending string sort (so the array is assumed to contain strings, and if it contains any other type the values are coerced to strings prior to comparison). To sort differently (e.g. descending, numeric, etc.), `comparison` can be given as the either the name of a defined function taking two arguments as the values to be compared, or an expression that compares the local variables `$1` and `$2` (defined by the `sort()` function as it runs). In either case, the result *must* be an integer: 0 if the two values are equal; less than 0 (e.g. -1) if the first value sorts before the second; or greater than zero (e.g. 1) if the first value sorts after the second. The comparison must be stable: given two values, it must return the same result every time it runs. Do not apply randomness or other heuristics to the comparison, as this can lead to long runtimes or even infinite loops in the attempt to sort.
* `range( start, end [, increment] )` &mdash; returns an array of integers counting up from `start` to `end` inclusive. If `end` < `start`, the array elements will count down. If `increment` is not specified, it assumed to be 1 (or -1 if `end` < `start`). Examples: `range(0,5)` returns `[0,1,2,3,4,5]`; `range(5,0)` returns `[5,4,3,2,1,0]`; `range(0,5,2)` returns `[0,2,4]`; `range(5,0,-2)` returns `[5,3,1]`; `range(5,0,2)` returns `[]`, an empty array, because a positive increment on a descending range (`end` < `start`) is impossible. A common use of this function is to iterate over the even-numbered items of some other array: `sum=0, each k in range(0,len(otherArray),2): sum = sum + otherArray[k]`.
* `isArray( various )` &mdash; returns *true* if the argument is an array (of any length);
* `isObject( various )` &mdash; returns *true* if the argument is an object;

Expand All @@ -407,4 +408,4 @@ Important notes with respect to date handling (currently; this will evolve):

As a result of the syntax, the following words are reserved and may not be used as identifiers or function names: `true, false, null, each, in, first, of, with, if, then, else, elif, elsif, elseif, endif, case, when, do, done, define, and, or, not, NaN, Infinity, pi`. Note that keywords and identifiers are case-sensitive, so while `each` is not an acceptable identifier, `Each` or `EACH` would be. The names of all defined functions are also reserved.

<small>Updated 2023-Oct-23 (for version 23296)</small>
<small>Updated 2023-Oct-24 (for version 23297)</small>
2 changes: 1 addition & 1 deletion grammar.jison
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@
%start expressions

%{
/* Grammar 22307 */
/* Grammar 23296 */
var buffer = "", qsep = "";
Expand Down
16 changes: 13 additions & 3 deletions lexp.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Version 23296.0858 */
/* Version 23297.0853 */
/** lexpjs - Copyright (C) 2018,2021 Patrick H. Rigney, All Rights Reserved
* See https://github.com/toggledbits/lexpjs
*
Expand All @@ -19,7 +19,7 @@
* SOFTWARE.
*/

const version = 23296;
const version = 23297;

const FEATURE_MONTH_BASE = 1; /* 1 = months 1-12; set to 0 if you prefer JS semantics where 0=Jan,11=Dec */
const MAX_RANGE = 1000; /* Maximum number of elements in a result range op result array */
Expand Down Expand Up @@ -465,7 +465,7 @@ parse: function parse(input) {
return true;
}};

/* Grammar 22307 */
/* Grammar 23296 */

var buffer = "", qsep = "";

Expand Down Expand Up @@ -1226,6 +1226,16 @@ return new Parser;
return null;
}
}
, range : { nargs: 3, impl: (s,e,i) => {
let a = []; s = parseInt(s); e = parseInt(e); e = isNaN(e) ? s : e;
if ( "number" === typeof s && "number" === typeof e ) {
i = parseInt(i);
if ( isNaN(i) ) i = Math.sign(e-s) || 1;
for (let k=s; a.length < MAX_RANGE && (i>0&&k<=e || i<0&&k>=e); k+=i) a.push(k);
}
return a;
}
}
, concat : { nargs: 2, impl: (a,b) => (a||[]).concat(b||[]) }
, slice : { nargs: 2, impl: (a,s,e) => (a||[]).slice( s, e ) }
, insert : { nargs: 2, impl: (a,p,...el) => { a.splice( p, 0, ...el ); return a; } }
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "lexpjs",
"version": "1.0.23296",
"version": "1.0.23297",
"description": "An expression parser written in JavaScript",
"main": "cli.js",
"scripts": {
Expand Down
8 changes: 7 additions & 1 deletion test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const version = 23296;
const version = 23297;

const verbose = false; // If true, all tests and results printed; otherwise just errors.

Expand Down Expand Up @@ -348,6 +348,12 @@ var test_expr = [
, { expr: "t=[9,7,5],s=clone(t),push(s, 3),t", expect: [9,7,5] }
, { expr: "s", expect: [9,7,5,3] }

, { expr: "range(0,5)", expect: [0,1,2,3,4,5] }
, { expr: "range(5,0)", expect: [5,4,3,2,1,0] }
, { expr: "range(0,5,2)", expect: [0,2,4] }
, { expr: "range(5,0,2)", expect: [] }
, { expr: "range(5,0,-2)", expect: [5,3,1] }

, { expr: "t=['dog','cat','rat'],u=['whale','shark','rat'],arrayConcat( t, u )", expect: [ 'dog','cat','rat','whale','shark','rat' ] }

, { expr: "t=['dog','cat','rat'],u=['whale','shark','rat'],arrayIntersection( t, u )", expect: [ 'rat' ] }
Expand Down
12 changes: 11 additions & 1 deletion umd-preamble.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
* SOFTWARE.
*/

const version = 23296;
const version = 23297;

const FEATURE_MONTH_BASE = 1; /* 1 = months 1-12; set to 0 if you prefer JS semantics where 0=Jan,11=Dec */
const MAX_RANGE = 1000; /* Maximum number of elements in a result range op result array */
Expand Down Expand Up @@ -241,6 +241,16 @@ const c_quot = { /* Default quoting */
return null;
}
}
, range : { nargs: 3, impl: (s,e,i) => {
let a = []; s = parseInt(s); e = parseInt(e); e = isNaN(e) ? s : e;
if ( "number" === typeof s && "number" === typeof e ) {
i = parseInt(i);
if ( isNaN(i) ) i = Math.sign(e-s) || 1;
for (let k=s; a.length < MAX_RANGE && (i>0&&k<=e || i<0&&k>=e); k+=i) a.push(k);
}
return a;
}
}
, concat : { nargs: 2, impl: (a,b) => (a||[]).concat(b||[]) }
, slice : { nargs: 2, impl: (a,s,e) => (a||[]).slice( s, e ) }
, insert : { nargs: 2, impl: (a,p,...el) => { a.splice( p, 0, ...el ); return a; } }
Expand Down

0 comments on commit 3cd1b76

Please sign in to comment.