Skip to content

Commit

Permalink
refactor(interval): ♻️ extract parse into IntervalNotation
Browse files Browse the repository at this point in the history
  • Loading branch information
albertms10 committed Mar 2, 2024
1 parent 0d58906 commit 627050a
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 72 deletions.
109 changes: 96 additions & 13 deletions lib/src/interval/interval.dart
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,6 @@ final class Interval implements Comparable<Interval> {
static const A13 =
Interval.imperfect(Size.thirteenth, ImperfectQuality.augmented);

static final _regExp = RegExp(r'(\w+?)(-?\d+)');

/// Creates a new [Interval] allowing only perfect quality [size]s.
const Interval.perfect(this.size, PerfectQuality this.quality)
// Copied from [Size.isPerfect] to allow const.
Expand Down Expand Up @@ -201,16 +199,11 @@ final class Interval implements Comparable<Interval> {
/// Interval.parse('P-5') == -Interval.P5
/// Interval.parse('z') // throws a FormatException
/// ```
factory Interval.parse(String source) {
final match = _regExp.firstMatch(source);
if (match == null) throw FormatException('Invalid Interval', source);

final size = Size(int.parse(match[2]!));
final parseFactory =
size.isPerfect ? PerfectQuality.parse : ImperfectQuality.parse;

return Interval._(size, parseFactory(match[1]!));
}
factory Interval.parse(
String source, {
IntervalNotation system = IntervalNotation.standard,
}) =>
system.parseInterval(source);

/// The number of semitones of this [Interval].
///
Expand Down Expand Up @@ -447,18 +440,54 @@ abstract class IntervalNotation {
/// The string notation for [interval].
String interval(Interval interval);

/// Parse [source] as an [Interval].
Interval parseInterval(String source);

/// The string notation for [size].
String size(Size size);

/// Parse [source] as a [Size].
Size parseSize(String source);

/// The string notation for [quality].
String quality(Quality quality);

/// Parse [source] as a [PerfectQuality].
PerfectQuality parsePerfectQuality(String source);

/// Parse [source] as an [ImperfectQuality].
ImperfectQuality parseImperfectQuality(String source);
}

/// The standard [Interval] notation system.
final class StandardIntervalNotation extends IntervalNotation {
/// Creates a new [StandardIntervalNotation].
const StandardIntervalNotation();

static final _intervalRegExp = RegExp(r'(\w+?)(-?\d+)');

/// The symbol for a diminished [Quality].
static const diminishedSymbol = 'd';

/// The symbol for a [PerfectQuality].
static const perfectSymbol = 'P';

/// The symbol for an augmented [Quality].
static const augmentedSymbol = 'A';

/// The symbol for a minor [ImperfectQuality].
static const minorSymbol = 'm';

/// The symbol for a major [ImperfectQuality].
static const majorSymbol = 'M';

static final _perfectQualityRegExp =
RegExp('^($diminishedSymbol+|$perfectSymbol|$augmentedSymbol+)\$');

static final _imperfectQualityRegExp = RegExp(
'^($diminishedSymbol+|$minorSymbol|$majorSymbol|$augmentedSymbol+)\$',
);

@override
String interval(Interval interval) {
final quality = interval.quality.toString(system: this);
Expand All @@ -468,9 +497,63 @@ final class StandardIntervalNotation extends IntervalNotation {
return '$naming ($quality${interval.simple.size.format(system: this)})';
}

@override
Interval parseInterval(String source) {
final match = _intervalRegExp.firstMatch(source);
if (match == null) throw FormatException('Invalid Interval', source);

final size = Size(int.parse(match[2]!));
final parseFactory =
size.isPerfect ? PerfectQuality.parse : ImperfectQuality.parse;

return Interval._(size, parseFactory(match[1]!));
}

@override
String size(Size size) => '$size';

@override
String quality(Quality quality) => quality.symbol;
Size parseSize(String source) => Size(int.parse(source));

@override
String quality(Quality quality) => switch (quality) {
PerfectQuality() => switch (quality.semitones) {
< 0 => diminishedSymbol * quality.semitones.abs(),
0 => perfectSymbol,
_ => augmentedSymbol * quality.semitones,
},
ImperfectQuality() => switch (quality.semitones) {
< 0 => diminishedSymbol * quality.semitones.abs(),
0 => minorSymbol,
1 => majorSymbol,
_ => augmentedSymbol * (quality.semitones - 1),
},
};

@override
PerfectQuality parsePerfectQuality(String source) {
if (!_perfectQualityRegExp.hasMatch(source)) {
throw FormatException('Invalid PerfectQuality', source);
}

return switch (source[0]) {
diminishedSymbol => PerfectQuality(-source.length),
perfectSymbol => PerfectQuality.perfect,
_ /* augmentedSymbol */ => PerfectQuality(source.length),
};
}

@override
ImperfectQuality parseImperfectQuality(String source) {
if (!_imperfectQualityRegExp.hasMatch(source)) {
throw FormatException('Invalid PerfectQuality', source);
}

return switch (source[0]) {
diminishedSymbol => ImperfectQuality(-source.length),
minorSymbol => ImperfectQuality.minor,
majorSymbol => ImperfectQuality.major,
_ /* augmentedSymbol */ => ImperfectQuality(source.length + 1),
};
}
}
69 changes: 10 additions & 59 deletions lib/src/interval/quality.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,6 @@ sealed class Quality implements Comparable<Quality> {
/// Creates a new [Quality] from [semitones].
const Quality(this.semitones);

static const _diminishedSymbol = 'd';
static const _augmentedSymbol = 'A';

/// The symbol of this [Quality].
String get symbol;

/// The inverted version of this [Quality].
Quality get inverted;

Expand Down Expand Up @@ -78,13 +72,6 @@ final class PerfectQuality extends Quality {
/// A triply augmented [PerfectQuality].
static const triplyAugmented = PerfectQuality(3);

static const _perfectSymbol = 'P';

static final _regExp = RegExp(
'^(${Quality._diminishedSymbol}+|$_perfectSymbol|'
'${Quality._augmentedSymbol}+)\$',
);

/// Parse [source] as a [PerfectQuality] and return its value.
///
/// If the [source] string does not contain a valid [PerfectQuality], a
Expand All @@ -96,24 +83,11 @@ final class PerfectQuality extends Quality {
/// PerfectQuality.parse('dd') == PerfectQuality.doublyDiminished
/// PerfectQuality.parse('z') // throws a FormatException
/// ```
factory PerfectQuality.parse(String source) {
if (!_regExp.hasMatch(source)) {
throw FormatException('Invalid PerfectQuality', source);
}

return switch (source[0]) {
Quality._diminishedSymbol => PerfectQuality(-source.length),
_perfectSymbol => PerfectQuality.perfect,
_ /* Quality._augmentedSymbol */ => PerfectQuality(source.length),
};
}

@override
String get symbol => switch (semitones) {
< 0 => Quality._diminishedSymbol * semitones.abs(),
0 => _perfectSymbol,
_ => Quality._augmentedSymbol * semitones,
};
factory PerfectQuality.parse(
String source, {
IntervalNotation system = IntervalNotation.standard,
}) =>
system.parsePerfectQuality(source);

/// The inverted version of this [PerfectQuality].
///
Expand Down Expand Up @@ -165,14 +139,6 @@ final class ImperfectQuality extends Quality {
/// A triply augmented [ImperfectQuality].
static const triplyAugmented = ImperfectQuality(4);

static const _minorSymbol = 'm';
static const _majorSymbol = 'M';

static final _regExp = RegExp(
'^(${Quality._diminishedSymbol}+|$_minorSymbol|$_majorSymbol|'
'${Quality._augmentedSymbol}+)\$',
);

/// Parse [source] as a [ImperfectQuality] and return its value.
///
/// If the [source] string does not contain a valid [ImperfectQuality], a
Expand All @@ -184,26 +150,11 @@ final class ImperfectQuality extends Quality {
/// ImperfectQuality.parse('A') == ImperfectQuality.augmented
/// ImperfectQuality.parse('z') // throws a FormatException
/// ```
factory ImperfectQuality.parse(String source) {
if (!_regExp.hasMatch(source)) {
throw FormatException('Invalid PerfectQuality', source);
}

return switch (source[0]) {
Quality._diminishedSymbol => ImperfectQuality(-source.length),
_minorSymbol => ImperfectQuality.minor,
_majorSymbol => ImperfectQuality.major,
_ /* Quality._augmentedSymbol */ => ImperfectQuality(source.length + 1),
};
}

@override
String get symbol => switch (semitones) {
< 0 => Quality._diminishedSymbol * semitones.abs(),
0 => _minorSymbol,
1 => _majorSymbol,
_ => Quality._augmentedSymbol * (semitones - 1),
};
factory ImperfectQuality.parse(
String source, {
IntervalNotation system = IntervalNotation.standard,
}) =>
system.parseImperfectQuality(source);

/// The inverted version of this [ImperfectQuality].
///
Expand Down

0 comments on commit 627050a

Please sign in to comment.