Skip to content

Commit

Permalink
Shorten '===' to '=='
Browse files Browse the repository at this point in the history
Replace '!==' with '<>'.
Replace '==' with '~='.
Get rid of '!='.

refs #282, #283
  • Loading branch information
frostburn committed May 3, 2024
1 parent 29052fa commit 5d3fc4b
Show file tree
Hide file tree
Showing 13 changed files with 96 additions and 95 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ SonicWeave looks like Javascript with Python semantics, has Haskell ranges and o
* NumPy - Travis Oliphant et. al.
* Scala - Manuel Op de Coul
* Scale Workshop 1 - Sean Archibald et. al.
* SQL - Donald D. Chamberlin et. al.
* FJS - "misotanni"
* NFJS - Matthew Yacavone
* Xen-calc - Matthew Yacavone
Expand Down
12 changes: 6 additions & 6 deletions documentation/advanced-dsl.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ Defer is used to execute a statement while exiting the current block.
let x = 5;
{
defer x += 2;
assert(x === 5);
assert(x == 5);
}
assert(x === 7);
assert(x == 7);
```
When there are multiple defers in a single block, they are executed in reverse order.
Expand All @@ -47,7 +47,7 @@ let x = 5;
defer x += 2;
defer x /= 2;
}
assert(x === 4.5e);
assert(x == 4.5e);
```

Defer is useful for pushing implicit tempering and general housekeeping to the top of the source instead of having to dangle everything at the end while editing the scale.
Expand Down Expand Up @@ -109,7 +109,7 @@ The `else` branch of a "while" or "for" loop is executed if no `break` statement
```c
for (const i of [2..12]) {
for (const j of [2..i-1]) {
if (i mod j === 0) break
if (i mod j == 0) break
} else {
i
}
Expand All @@ -127,11 +127,11 @@ results in `$ = [2^1/7, 2^2/7, 2^3/7, 2^4/7, 2^5/7, 2^6/7, 2]`.
#### If clause
To conditionally include elements use an `if` clause e.g.
```c
[i for i of [1..9] if i mod 3 !== 0]
[i for i of [1..9] if i mod 3 <> 0]
```
results in `$ = [1, 2, 4, 5, 7, 8]` i.e. all [throdd](https://en.xen.wiki/w/Threeven) numbers below 10.

Above the `!== 0` part is unnecessary. `[i for i of [1..9] if i mod 3]` works the same because `0` is falsy while `1` and `2` are truthy.
Above the `<> 0` part is unnecessary. `[i for i of [1..9] if i mod 3]` works the same because `0` is falsy while `1` and `2` are truthy.

## If...else
Conditional statements are evaluated if the test expression evaluates to `true` otherwise the `else` branch is taken.
Expand Down
19 changes: 9 additions & 10 deletions documentation/intermediate-dsl.md
Original file line number Diff line number Diff line change
Expand Up @@ -503,16 +503,15 @@ Binary operation is vectorized elementwise:
Vectorized versions of logical operators work on plain values too and do not short-circuit. `P8 white vor pop()` is a handy expression to swap out the last interval for a white-colored octave because the `pop()` command executes without further effects.

#### Boolean
| Strict equality | `===` |
| Strict inequality | `!==` |
| Equality | `==` |
| Inequality | `!=` |
| Greater than | `>` |
| Greater than or equal | `>=` |
| Less than | `<` |
| Less than or equal | `<=` |
| Strict equality | `==` |
| Strict inequality | `<>` |
| Size equality | `~=` |
| Greater than | `>` |
| Greater than or equal | `>=` |
| Less than | `<` |
| Less than or equal | `<=` |

All boolean operators vectorize over arrays. `[1, 2] === [1, 3]` evaluates to `[true, false]`.
All boolean operators vectorize over arrays. `[1, 2] == [1, 3]` evaluates to `[true, false]`.

Absolute quantities are converted to relative before comparison so `440 Hz > 1` evaluates to `true` if `1 = 432Hz` was declared as the unison frequency. This conversion has no impact on the relative ordering between absolute quantities `1 ms > 440 Hz` always evaluates to `true` because `1 ms` represents `1000 Hz` as a frequency.

Expand Down Expand Up @@ -738,7 +737,7 @@ The Hertz unit may be spelled with a lowercase 'h' and without spaces `123hz` bu
## S-expressions
SonicWeave uses the logarithmic domain for [S-expressions](https://en.xen.wiki/w/Square_superparticular) in order to make them compatible with FJS.

So a linear fact like S9 = S6/S8 is expressed as `S9 === S6-S8` in SonicWeave.
So a linear fact like S9 = S6/S8 is expressed as `S9 == S6-S8` in SonicWeave.

Sums of consecutive S-expressions use the range syntax. E.g. `logarithmic(10/9)` is equivalent to `S5..8` i.e. `logarithmic(25/24 * 36/35 * 49/48 * 64/63)`

Expand Down
2 changes: 1 addition & 1 deletion documentation/technical.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ All operations are left-associative except exponentiation, recipropower, and log
| `max`, `min` | Maximum, minimum |
| `x:y::z`, `/x::y:x` | Chord enumeration, reflected chord enumeration |
| `to`, `by` | Linear rounding, logarithmic rounding |
| `===`, `!==`, `==`, `!=`, `<=`, `>=`, `<`, `>`, `of`, `not of`, `~of`, `not ~of`, `in`, `not in`, `~in`, `not ~in` | Strict equality, size equality, comparisons, strict/non-strict value inclusion, strict/non-strict index/key inclusion |
| `==`, `<>`, `~=`, `<=`, `>=`, `<`, `>`, `of`, `not of`, `~of`, `not ~of`, `in`, `not in`, `~in`, `not ~in` | Strict equality, size equality, comparisons, strict/non-strict value inclusion, strict/non-strict index/key inclusion |
| `not x`, `vnot x` | Boolean not, vector not |
| `and`, `vand` | Boolean and, vector and |
| `or`, `vor`, `??` | Boolean or, vector or, niente coalescing |
Expand Down
5 changes: 2 additions & 3 deletions src/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@ export type BinaryOperator =
| 'vor'
| 'and'
| 'vand'
| '==='
| '!=='
| '=='
| '!='
| '<>'
| '~='
| '<='
| '>='
| '<'
Expand Down
12 changes: 8 additions & 4 deletions src/grammars/sonic-weave.pegjs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,11 @@ VariableManipulationStatement
};
} else if (operator) {
throw new Error('Left-hand-side expression expected.');
} else if (preferLeft) {
return {
type: 'ExpressionStatement',
expression: BinaryExpression('~=', name, value, false, false)
};
}
return {
type: 'PitchDeclaration',
Expand Down Expand Up @@ -625,10 +630,9 @@ NotExpression
}

RelationalOperator 'relational operator'
= '==='
/ '!=='
/ '=='
/ '!='
= '=='
/ '<>'
/ '~='
/ '<='
/ '>='
/ '<'
Expand Down
2 changes: 1 addition & 1 deletion src/parser/__tests__/expression.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,7 @@ describe('SonicWeave expression evaluator', () => {
});

it('can add vals', () => {
const yes = evaluate('[email protected] + [email protected] === [email protected]');
const yes = evaluate('[email protected] + [email protected] == [email protected]');
expect(yes).toBe(true);
});

Expand Down
4 changes: 2 additions & 2 deletions src/parser/__tests__/sonic-weave-ast.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1009,7 +1009,7 @@ describe('SonicWeave Abstract Syntax Tree parser', () => {
});

it('has a pythonic precedence between logical and relational', () => {
const ast = parseSingle('not a < b and c > d or not e === f');
const ast = parseSingle('not a < b and c > d or not e == f');
const e = ast.expression;
expect(e.operator).toBe('or');
{
Expand All @@ -1028,7 +1028,7 @@ describe('SonicWeave Abstract Syntax Tree parser', () => {
}
}
expect(e.right.operator).toBe('not');
expect(e.right.operand.operator).toBe('===');
expect(e.right.operand.operator).toBe('==');
{
expect(e.right.operand.left.id).toBe('e');
expect(e.right.operand.right.id).toBe('f');
Expand Down
39 changes: 24 additions & 15 deletions src/parser/__tests__/source.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1064,7 +1064,7 @@ describe('SonicWeave parser', () => {
for (const i of [2..12]) {
let j = 1;
while (++j < i) {
if (i mod j === 0) break;
if (i mod j == 0) break;
} else {
i;
}
Expand All @@ -1077,7 +1077,7 @@ describe('SonicWeave parser', () => {
const scale = expand(`
for (const i of [2..12]) {
for (const j of [2..i-1]) {
if (i mod j === 0) break;
if (i mod j == 0) break;
} else {
i;
}
Expand Down Expand Up @@ -1404,41 +1404,50 @@ describe('SonicWeave parser', () => {
});

it('has defer similar to Zig (single)', () => {
evaluateSource(`
evaluateSource(
`
let x = 5;
{
defer x += 2;
if (x !== 5) {
if (x <> 5) {
throw 'Defer executed early!';
}
}
if (x !== 7) {
if (x <> 7) {
throw 'Defer did not execute!';
}
`);
`,
false
);
});

it('has defer similar to Zig (multi)', () => {
evaluateSource(`
evaluateSource(
`
let x = 5;
{
defer x += 2;
defer x /= 2;
}
if (x !== 4.5e) {
if (x <> 4.5e) {
throw 'Deferred statements executed in the wrong order!';
}
`);
`,
false
);
});

it('rejects confusing defer', () => {
expect(() =>
evaluateSource(`
for (const i of [3, 5]) {
defer break;
i / (i - 1);
}
`)
evaluateSource(
`
for (const i of [3, 5]) {
defer break;
i / (i - 1);
}
`,
false
)
).toThrow('Illegal BreakStatement inside a deferred block.');
});
});
10 changes: 4 additions & 6 deletions src/parser/__tests__/stdlib.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1448,16 +1448,14 @@ describe('SonicWeave standard library', () => {
let x = 5;
{
defer x += 2;
assert(x === 5);
assert(x == 5);
}
assert(x === 7);
assert(x == 7);
`);
});

it('has assert (failure)', () => {
expect(() => evaluateSource('assert(1 === 2)')).toThrow(
'Assertion failed.'
);
expect(() => evaluateSource('assert(1 == 2)')).toThrow('Assertion failed.');
});

it('executes deferred actions before interrupting', () => {
Expand All @@ -1469,7 +1467,7 @@ describe('SonicWeave standard library', () => {
x = "Unreachable code executed!";
throw "Execution shouldn't reach here!";
}
assert(doStuff() === 311);
assert(doStuff() == 311);
x;
`);
expect(result).toBe('Deferred action triggered.');
Expand Down
5 changes: 2 additions & 3 deletions src/parser/__tests__/vector-broadcasting.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,9 @@ describe('SonicWeave vector broadcasting', () => {
it.each([
'vor',
'vand',
'===',
'!==',
'==',
'!=',
'<>',
'~=',
'<=',
'>=',
'<',
Expand Down
30 changes: 11 additions & 19 deletions src/parser/expression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1220,10 +1220,9 @@ export class ExpressionVisitor {
case '??':
case 'or':
case 'and':
case '===':
case '!==':
case '==':
case '!=':
case '<>':
case '~=':
case '<=':
case '>=':
case '<':
Expand Down Expand Up @@ -1270,14 +1269,12 @@ export class ExpressionVisitor {
return result;
}
switch (operator) {
case '===':
case '==':
return left.strictEquals(right);
case '!==':
case '<>':
return !left.strictEquals(right);
case '==':
case '~=':
return left.equals(right);
case '!=':
return !left.equals(right);
case '<=':
return compare.bind(this)(left, right) <= 0;
case '>=':
Expand Down Expand Up @@ -1381,14 +1378,12 @@ export class ExpressionVisitor {
} else if (left instanceof Val) {
if (right instanceof Val) {
switch (operator) {
case '===':
case '==':
return left.strictEquals(right);
case '!==':
case '<>':
return !left.strictEquals(right);
case '==':
case '~=':
return left.equals(right);
case '!=':
return !left.equals(right);
case '+':
return left.add(right);
case '-':
Expand Down Expand Up @@ -1430,16 +1425,13 @@ export class ExpressionVisitor {
}
}
switch (operator) {
case '===':
case '==':
return left === right;
case '!==':
case '<>':
return left !== right;
case '==':
case '~=':
// eslint-disable-next-line eqeqeq
return left == right;
case '!=':
// eslint-disable-next-line eqeqeq
return left != right;
case '<=':
return (left as any) <= (right as any);
case '<':
Expand Down
Loading

0 comments on commit 5d3fc4b

Please sign in to comment.