Skip to content

Commit

Permalink
Reserve nan and inf as keywords
Browse files Browse the repository at this point in the history
  • Loading branch information
frostburn committed May 13, 2024
1 parent 7409e6b commit 12deb49
Show file tree
Hide file tree
Showing 12 changed files with 70 additions and 37 deletions.
2 changes: 1 addition & 1 deletion documentation/BUILTIN.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ Obtain a copy of the given/current scale containing values that evaluate to `tru
Convert interval to (relative) FJS.

### flatten(*array*, *depth*)
Returns a new array with all sub-array elements concatenated into it recursively up to the specified depth (default `Infinity`).
Returns a new array with all sub-array elements concatenated into it recursively up to the specified depth (default `inf`).

### floor(*interval*)
Round value down to the nearest integer.
Expand Down
2 changes: 2 additions & 0 deletions documentation/advanced-dsl.md
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,8 @@ It's legal to declare `let Hz = 'whatever'`, but the grammar prevents the `Hz` v
### Obscure types
| Type | Literal | Meaning |
| ----------------- | ---------- | ---------------------------------------------------------- |
| Infinity | `inf` | Linear relative infinity |
| Not-a-number | `nan` | Generic invalid real value |
| Second | `1s` | Inverse of `1Hz` i.e. `1s * 1Hz` evaluates to `1` |
| Jorp | `` | Geometric inverse of `c` i.e. `` is equal to `<1200]` |
| Pilcrowspoob | `` | Geometric inverse of `logarithmic(1Hz)` |
Expand Down
22 changes: 11 additions & 11 deletions src/__tests__/cli.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ describe('Interchange format', () => {
});

it('has representation for infinity', () => {
const result = toSonicWeaveInterchange('Infinity');
expect(result).toContain('Infinity');
const result = toSonicWeaveInterchange('inf');
expect(result).toContain('inf');
});

it('has representation for NaN', () => {
const result = toSonicWeaveInterchange('NaN');
expect(result).toContain('NaN');
it('has representation for nan', () => {
const result = toSonicWeaveInterchange('nan');
expect(result).toContain('nan');
});

it('has representation for Infinity Hz', () => {
const result = toSonicWeaveInterchange('Infinity * 1 Hz');
expect(result).toContain('Infinity * 1Hz');
it('has representation for infinity Hz', () => {
const result = toSonicWeaveInterchange('inf * 1 Hz');
expect(result).toContain('inf * 1Hz');
});

it('has representation for NaN Hz (normalizes)', () => {
const result = toSonicWeaveInterchange('NaN * 1 Hz');
expect(result).toContain('NaN');
it('has representation for nan Hz (normalizes)', () => {
const result = toSonicWeaveInterchange('nan * 1 Hz');
expect(result).toContain('nan');
});
});
8 changes: 4 additions & 4 deletions src/__tests__/monzo.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ describe('Real value with time', () => {
it('can represent NaN', () => {
const nan = TimeReal.fromValue(NaN);
expect(nan.value).toBeNaN();
expect(nan.toString()).toBe('NaN');
expect(nan.toString()).toBe('nan');
});

it('can represent Infinity', () => {
const inf = TimeReal.fromValue(Infinity);
expect(inf.value).toBe(Infinity);
expect(inf.toString()).toBe('Infinity');
expect(inf.toString()).toBe('inf');
});
});

Expand Down Expand Up @@ -466,13 +466,13 @@ describe('JSON serialization', () => {
];
const serialized = JSON.stringify(data);
expect(serialized).toBe(
'["Hello, world!",{"n":10,"d":7},{"type":"TimeReal","timeExponent":-1,"value":777},3.5,{"type":"TimeMonzo","timeExponent":{"n":0,"d":1},"primeExponents":[{"n":-4,"d":1},{"n":4,"d":1},{"n":-1,"d":1},{"n":0,"d":1},{"n":0,"d":1},{"n":0,"d":1},{"n":0,"d":1},{"n":0,"d":1},{"n":0,"d":1}],"residual":{"n":1,"d":1}},null,{"type":"TimeReal","timeExponent":0,"value":"NaN"},{"type":"TimeReal","timeExponent":1,"value":"Infinity"}]'
'["Hello, world!",{"n":10,"d":7},{"type":"TimeReal","timeExponent":-1,"value":777},3.5,{"type":"TimeMonzo","timeExponent":{"n":0,"d":1},"primeExponents":[{"n":-4,"d":1},{"n":4,"d":1},{"n":-1,"d":1},{"n":0,"d":1},{"n":0,"d":1},{"n":0,"d":1},{"n":0,"d":1},{"n":0,"d":1},{"n":0,"d":1}],"residual":{"n":1,"d":1}},null,{"type":"TimeReal","timeExponent":0,"value":"nan"},{"type":"TimeReal","timeExponent":1,"value":"inf"}]'
);
});

it('can deserialize an array of primitives, fractions and monzos', () => {
const serialized =
'["Hello, world!",{"n":10,"d":7},{"type":"TimeReal","timeExponent":-1,"value":777},3.5,{"type":"TimeMonzo","timeExponent":{"n":0,"d":1},"primeExponents":[{"n":-4,"d":1},{"n":4,"d":1},{"n":-1,"d":1},{"n":0,"d":1},{"n":0,"d":1},{"n":0,"d":1},{"n":0,"d":1},{"n":0,"d":1},{"n":0,"d":1}],"residual":{"n":1,"d":1}},null,{"type":"TimeReal","timeExponent":0,"value":"NaN"},{"type":"TimeReal","timeExponent":1,"value":"Infinity"}]';
'["Hello, world!",{"n":10,"d":7},{"type":"TimeReal","timeExponent":-1,"value":777},3.5,{"type":"TimeMonzo","timeExponent":{"n":0,"d":1},"primeExponents":[{"n":-4,"d":1},{"n":4,"d":1},{"n":-1,"d":1},{"n":0,"d":1},{"n":0,"d":1},{"n":0,"d":1},{"n":0,"d":1},{"n":0,"d":1},{"n":0,"d":1}],"residual":{"n":1,"d":1}},null,{"type":"TimeReal","timeExponent":0,"value":"nan"},{"type":"TimeReal","timeExponent":1,"value":"inf"}]';
function reviver(key: string, value: any) {
return TimeMonzo.reviver(
key,
Expand Down
19 changes: 19 additions & 0 deletions src/expression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,14 @@ export type ReciprocalLogarithmicHertzLiteral = {
type: 'ReciprocalLogarithmicHertzLiteral';
};

export type NotANumberLiteral = {
type: 'NotANumberLiteral';
};

export type InfinityLiteral = {
type: 'InfinityLiteral';
};

export type FJS = {
type: 'FJS';
ups: number;
Expand Down Expand Up @@ -257,6 +265,8 @@ export type IntervalLiteral =
| CentsLiteral
| CentLiteral
| ReciprocalCentLiteral
| NotANumberLiteral
| InfinityLiteral
| FJS
| AspiringFJS
| AbsoluteFJS
Expand Down Expand Up @@ -1051,6 +1061,10 @@ export function literalToString(literal: IntervalLiteral) {
return '€';
case 'ReciprocalLogarithmicHertzLiteral':
return '¶';
case 'NotANumberLiteral':
return 'nan';
case 'InfinityLiteral':
return 'inf';
case 'FJS':
return formatFJS(literal);
case 'AbsoluteFJS':
Expand All @@ -1076,6 +1090,7 @@ export function literalToString(literal: IntervalLiteral) {
case 'MosStepLiteral':
return formatMosStepLiteral(literal);
default:
literal satisfies AspiringFJS | AspiringAbsoluteFJS;
throw new Error(`Cannot format ${literal.type}`);
}
}
Expand Down Expand Up @@ -1172,6 +1187,8 @@ export function literalToJSON(literal?: IntervalLiteral): any {
case 'NedjiLiteral':
case 'CentLiteral':
case 'ReciprocalCentLiteral':
case 'NotANumberLiteral':
case 'InfinityLiteral':
case 'FJS':
case 'AspiringFJS':
case 'AbsoluteFJS':
Expand Down Expand Up @@ -1222,6 +1239,8 @@ export function literalFromJSON(object: any): IntervalLiteral | undefined {
case 'NedjiLiteral':
case 'CentLiteral':
case 'ReciprocalCentLiteral':
case 'NotANumberLiteral':
case 'InfinityLiteral':
case 'FJS':
case 'AspiringFJS':
case 'AbsoluteFJS':
Expand Down
12 changes: 12 additions & 0 deletions src/grammars/sonic-weave.pegjs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
'if',
'import',
'in',
'inf',
'labs',
'lest',
'let',
Expand All @@ -42,6 +43,7 @@
'modc',
'module',
'MOS',
'nan',
'niente',
'not',
'of',
Expand Down Expand Up @@ -158,6 +160,7 @@ FromToken = @'from' !IdentifierPart
IfToken = @'if' !IdentifierPart
ImportToken = @'import' !IdentifierPart
InToken = @'in' !IdentifierPart
InfinityToken = @'inf' !IdentifierPart
LogAbsToken = @'labs' !IdentifierPart
LestToken = @'lest' !IdentifierPart
LetToken = @'let' !IdentifierPart
Expand All @@ -168,6 +171,7 @@ MinToken = @'min' !IdentifierPart
ModToken = @'mod' !IdentifierPart
ModCeilingToken = @'modc' !IdentifierPart
ModuleToken = @'module' !IdentifierPart
NotANumberToken = @'nan' !IdentifierPart
NoneToken = @'niente' !IdentifierPart
NotToken = @'not' !IdentifierPart
OfToken = @'of' !IdentifierPart
Expand Down Expand Up @@ -995,6 +999,8 @@ Primary
/ NoneLiteral
/ TrueLiteral
/ FalseLiteral
/ NotANumberLiteral
/ InfinityLiteral
/ NedjiLiteral
/ StepLiteral
/ ScalarMultiple
Expand Down Expand Up @@ -1247,6 +1253,12 @@ TrueLiteral
FalseLiteral
= FalseToken { return { type: 'FalseLiteral' }; }
NotANumberLiteral
= NotANumberToken { return { type: 'NotANumberLiteral' }; }
InfinityLiteral
= InfinityToken { return { type: 'InfinityLiteral' }; }
ColorLiteral
= value: (@RGB8 / @RGB4) {
return {
Expand Down
18 changes: 9 additions & 9 deletions src/monzo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,11 +238,11 @@ export class TimeReal {
value.type === 'TimeReal'
) {
let v: number | string = value.value;
if (v === 'NaN') {
if (v === 'nan') {
v = NaN;
} else if (v === 'Infinity') {
} else if (v === 'inf') {
v = Infinity;
} else if (v === '-Infinity') {
} else if (v === '-inf') {
v = -Infinity;
}
return new TimeReal(value.timeExponent, v as number);
Expand All @@ -258,11 +258,11 @@ export class TimeReal {
let value: number | string = this.value;
// JSON sure is a standard
if (isNaN(value)) {
value = 'NaN';
value = 'nan';
} else if (value === Infinity) {
value = 'Infinity';
value = 'inf';
} else if (value === -Infinity) {
value = '-Infinity';
value = 'inf';
}

return {
Expand Down Expand Up @@ -851,13 +851,13 @@ export class TimeReal {
if (isNaN(this.value)) {
switch (domain) {
case 'linear':
return 'NaN';
return 'nan';
case 'logarithmic':
return 'logarithmic(NaN)';
return 'logarithmic(nan)';
}
}
if (!isFinite(this.value)) {
let value = this.value < 0 ? '-Infinity' : 'Infinity';
let value = this.value < 0 ? '-inf' : 'inf';
if (this.timeExponent) {
value = `${value} * 1${this.formatTimeExponent()}`;
}
Expand Down
10 changes: 5 additions & 5 deletions src/parser/__tests__/expression.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1218,15 +1218,15 @@ describe('SonicWeave expression evaluator', () => {
});

it('has not-a-number', () => {
const nan = evaluate('NaN') as Interval;
const nan = evaluate('nan') as Interval;
expect(nan.valueOf()).toBeNaN();
expect(nan.toString()).toBe('NaN');
expect(nan.toString()).toBe('nan');
});

it('has negative infinity', () => {
const inf = evaluate('-Infinity') as Interval;
const inf = evaluate('-inf') as Interval;
expect(inf.valueOf()).toBe(-Infinity);
expect(inf.toString()).toBe('-Infinity');
expect(inf.toString()).toBe('-inf');
});

it('has root half', () => {
Expand All @@ -1250,7 +1250,7 @@ describe('SonicWeave expression evaluator', () => {

it('produces nan from asin', () => {
const wishIwasAcomplexNumber = evaluate('asin(2)') as Interval;
expect(wishIwasAcomplexNumber.toString()).toBe('NaN');
expect(wishIwasAcomplexNumber.toString()).toBe('nan');
});

it('has domain-crossing acos', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/parser/__tests__/vector-broadcasting.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ describe('SonicWeave vector broadcasting', () => {
const polarity = sw1D`[-1/2, PI] sign`;
expect(polarity).toEqual([-1, 1]);

const circleOfNaN = sw2D`sign([[0, LN2], [-1, NaN]])`;
const circleOfNaN = sw2D`sign([[0, LN2], [-1, nan]])`;
expect(circleOfNaN).toEqual([
[0, 1],
[-1, NaN],
Expand Down
4 changes: 4 additions & 0 deletions src/parser/expression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,10 @@ export class ExpressionVisitor {
return node.value;
case 'NoneLiteral':
return undefined;
case 'NotANumberLiteral':
return new Interval(new TimeReal(0, NaN), 'linear', 0, node);
case 'InfinityLiteral':
return new Interval(new TimeReal(0, Infinity), 'linear', 0, node);
case 'DownExpression':
return this.visitDownExpression(node);
case 'StepLiteral':
Expand Down
6 changes: 1 addition & 5 deletions src/stdlib/builtin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,6 @@ const PI = new Interval(TimeReal.fromValue(Math.PI), 'linear');
const SQRT1_2 = new Interval(TimeMonzo.fromEqualTemperament('-1/2'), 'linear');
const SQRT2 = new Interval(TimeMonzo.fromEqualTemperament('1/2'), 'linear');
const TAU = new Interval(TimeReal.fromValue(2 * Math.PI), 'linear');
const NAN = new Interval(TimeReal.fromValue(NaN), 'linear');
const INFINITY = new Interval(TimeReal.fromValue(Infinity), 'linear');

// == Real-valued Math wrappers ==
const MATH_WRAPPERS: Record<string, SonicWeaveFunction> = {};
Expand Down Expand Up @@ -1221,7 +1219,7 @@ function flatten(array: SonicWeaveValue, depth?: Interval) {
return [array];
}
flatten.__doc__ =
'Returns a new array with all sub-array elements concatenated into it recursively up to the specified depth (default `Infinity`).';
'Returns a new array with all sub-array elements concatenated into it recursively up to the specified depth (default `inf`).';
flatten.__node__ = builtinNode(flatten);

function clear(this: ExpressionVisitor, scale?: Interval[]) {
Expand Down Expand Up @@ -2487,8 +2485,6 @@ export const BUILTIN_CONTEXT: Record<string, Interval | SonicWeaveFunction> = {
SQRT1_2,
SQRT2,
TAU,
NaN: NAN,
Infinity: INFINITY,
VERSION,
// First-party wrappers
numComponents,
Expand Down
2 changes: 1 addition & 1 deletion src/stdlib/prelude.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ riff denominator(x) {
}
riff sign(x) {
"Calculate the sign of x.";
return 1 where x > 0 else -1 where x < 0 else 0 where x == 0 else NaN;
return 1 where x > 0 else -1 where x < 0 else 0 where x == 0 else nan;
}
riff oddLimitOf(x, equave = 2) {
"Calculate the odd limit of x. Here 'odd' means not divisible by the equave.";
Expand Down

0 comments on commit 12deb49

Please sign in to comment.