Skip to content

Commit

Permalink
Implement binary prefixes for no reason
Browse files Browse the repository at this point in the history
ref #211
  • Loading branch information
frostburn committed Apr 10, 2024
1 parent 38f29ae commit 665b72b
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 8 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ Records are accessed with the same syntax as arrays but using string indices e.g
Nullish access is supported e.g. `{}~["nothing here"]` evaluates to `niente`.

## Metric prefixes
Frequency literals support [metric prefixes](https://en.wikipedia.org/wiki/Metric_prefix) e.g. `1.2 kHz` is the same as `1200 Hz`.
Frequency literals support [metric prefixes](https://en.wikipedia.org/wiki/Metric_prefix) e.g. `1.2 kHz` is the same as `1200 Hz`. [Binary prefixes](https://en.wikipedia.org/wiki/Binary_prefix) are also supported for no particular reason.

## Numeric frequency flavor
The space is mandatory in frequency literals like `123 hz` but there is a numeric *flavor* 'z' for quick input i.e. `123z`.
Expand Down
6 changes: 6 additions & 0 deletions src/__tests__/parser/expression.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1751,4 +1751,10 @@ describe('SonicWeave expression evaluator', () => {
const thirdFourth = parseSingle('⅓M2_7l');
expect(thirdFourth.toFraction().toFraction()).toBe('11/10');
});

it('has binary prefixes for mild amusement', () => {
const tooLong = parseSingle('3 Yis');
expect(tooLong.isAbsolute()).toBe(true);
expect(tooLong.valueOf()).toBeCloseTo(3 * 1024 ** 8);
});
});
5 changes: 3 additions & 2 deletions src/expression.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
ABSURD_EXPONENT,
BinaryPrefix,
MetricPrefix,
Sign,
bigAbs,
Expand Down Expand Up @@ -104,12 +105,12 @@ export type ReciprocalCentLiteral = {

export type HertzLiteral = {
type: 'HertzLiteral';
prefix: MetricPrefix;
prefix: MetricPrefix | BinaryPrefix;
};

export type SecondLiteral = {
type: 'SecondLiteral';
prefix: MetricPrefix;
prefix: MetricPrefix | BinaryPrefix;
};

export type FJS = {
Expand Down
18 changes: 16 additions & 2 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ import {
NEGATIVE_ONE,
TWO,
hasOwn,
binaryExponent,
MetricPrefix,
BinaryPrefix,
} from './utils';
import {pythagoreanMonzo, absoluteMonzo} from './pythagorean';
import {inflect} from './fjs';
Expand Down Expand Up @@ -1016,6 +1019,7 @@ export class StatementVisitor {

const TWO_MONZO = new TimeMonzo(ZERO, [ONE]);
const TEN_MONZO = new TimeMonzo(ZERO, [ONE, ZERO, ONE]);
const KIBI_MONZO = new TimeMonzo(ZERO, [new Fraction(10)]);
const CENT_MONZO = new TimeMonzo(ZERO, [new Fraction(1, 1200)]);
const RECIPROCAL_CENT_MONZO = new TimeMonzo(ZERO, [new Fraction(1200)]);

Expand Down Expand Up @@ -2273,13 +2277,23 @@ export class ExpressionVisitor {
}

visitHertzLiteral(node: HertzLiteral): Interval {
const value = TEN_MONZO.pow(metricExponent(node.prefix));
let value: TimeMonzo;
if (node.prefix.endsWith('i')) {
value = KIBI_MONZO.pow(binaryExponent(node.prefix as BinaryPrefix));
} else {
value = TEN_MONZO.pow(metricExponent(node.prefix as MetricPrefix));
}
value.timeExponent = NEGATIVE_ONE;
return new Interval(value, 'linear', node);
}

visitSecondLiteral(node: SecondLiteral): Interval {
const value = TEN_MONZO.pow(metricExponent(node.prefix));
let value: TimeMonzo;
if (node.prefix.endsWith('i')) {
value = KIBI_MONZO.pow(binaryExponent(node.prefix as BinaryPrefix));
} else {
value = TEN_MONZO.pow(metricExponent(node.prefix as MetricPrefix));
}
value.timeExponent = ONE;
return new Interval(value, 'linear', node);
}
Expand Down
8 changes: 6 additions & 2 deletions src/sonic-weave.pegjs
Original file line number Diff line number Diff line change
Expand Up @@ -1097,15 +1097,15 @@ HertzLiteral
prefix: '',
}
}
/ prefix: MetricPrefix? (HertzToken / LowHertzToken) {
/ prefix: (BinaryPrefix / MetricPrefix)? (HertzToken / LowHertzToken) {
return {
type: 'HertzLiteral',
prefix,
};
}

SecondLiteral
= prefix: MetricPrefix? SecondToken {
= prefix: (BinaryPrefix / MetricPrefix)? SecondToken {
return {
type: 'SecondLiteral',
prefix,
Expand Down Expand Up @@ -1428,6 +1428,10 @@ ParenthesizedExpression
MetricPrefix
= $([QRYZEPTGMkhdcmµnpfazyrq] / 'da' / '')

// Note: According to Wikipedia Ri and Qi are still under review.
BinaryPrefix
= $([KMGTPEZYRQ] 'i')

StringLiteral
= '"' chars: DoubleStringCharacter* '"' { return { type: 'StringLiteral', value: chars.join('') }; }
/ "'" chars: SingleStringCharacter* "'" { return { type: 'StringLiteral', value: chars.join('') }; }
41 changes: 40 additions & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,46 @@ export function metricExponent(prefix: MetricPrefix): number {
case 'q':
return -30;
default:
throw new Error(`Unrecognized prefix ${prefix}`);
throw new Error(`Unrecognized prefix ${prefix}.`);
}
}

export type BinaryPrefix =
| 'Ki'
| 'Mi'
| 'Gi'
| 'Ti'
| 'Pi'
| 'Ei'
| 'Zi'
| 'Yi'
| 'Ri'
| 'Qi';

export function binaryExponent(prefix: BinaryPrefix): number {
switch (prefix) {
case 'Ki':
return 1;
case 'Mi':
return 2;
case 'Gi':
return 3;
case 'Ti':
return 4;
case 'Pi':
return 5;
case 'Ei':
return 6;
case 'Zi':
return 7;
case 'Yi':
return 8;
case 'Ri':
return 9;
case 'Qi':
return 10;
default:
throw new Error(`Unrecognized prefix ${prefix}.`);
}
}

Expand Down

0 comments on commit 665b72b

Please sign in to comment.