Change note's duration type from int to Fraction and enable + operator support for note and int
This commit is contained in:
@@ -4,73 +4,70 @@ import io.smnp.data.entity.Note
|
||||
import io.smnp.data.enumeration.Pitch
|
||||
import io.smnp.dsl.token.model.entity.TokenizerOutput
|
||||
import io.smnp.dsl.token.model.enumeration.TokenType
|
||||
import io.smnp.math.Fraction
|
||||
|
||||
class NoteTokenizer : Tokenizer {
|
||||
override fun tokenize(input: String, current: Int, line: Int): TokenizerOutput {
|
||||
var consumedChars = 0
|
||||
var notePitch: String
|
||||
var octave: Int? = null
|
||||
var duration: String? = null
|
||||
var dot = false
|
||||
var rawValue = ""
|
||||
override fun tokenize(input: String, current: Int, line: Int): TokenizerOutput {
|
||||
var consumedChars = 0
|
||||
var notePitch: String
|
||||
var octave: Int? = null
|
||||
var duration: String? = null
|
||||
var dot = false
|
||||
var rawValue = ""
|
||||
|
||||
// Note literal start symbol
|
||||
if(input[current] == '@') {
|
||||
rawValue += input[current+consumedChars]
|
||||
// Note literal start symbol
|
||||
if (input[current] == '@') {
|
||||
rawValue += input[current + consumedChars]
|
||||
consumedChars += 1
|
||||
|
||||
// Note basic pitch
|
||||
if (listOf('c', 'd', 'e', 'f', 'g', 'a', 'h', 'b').contains(input[current + consumedChars].toLowerCase())) {
|
||||
rawValue += input[current + consumedChars]
|
||||
notePitch = input[current + consumedChars].toString()
|
||||
consumedChars += 1
|
||||
|
||||
// Note basic pitch
|
||||
if(listOf('c', 'd', 'e', 'f', 'g', 'a', 'h', 'b').contains(input[current+consumedChars].toLowerCase())) {
|
||||
rawValue += input[current+consumedChars]
|
||||
notePitch = input[current+consumedChars].toString()
|
||||
consumedChars += 1
|
||||
|
||||
// Flat or sharp
|
||||
if(current+consumedChars < input.length && listOf('b', '#').contains(input[current+consumedChars])) {
|
||||
rawValue += input[current+consumedChars]
|
||||
notePitch += input[current+consumedChars]
|
||||
consumedChars += 1
|
||||
}
|
||||
|
||||
// Octave
|
||||
if(current+consumedChars < input.length && "\\d".toRegex().matches(input[current+consumedChars].toString())) {
|
||||
rawValue += input[current+consumedChars]
|
||||
octave = input[current+consumedChars].toString().toInt()
|
||||
consumedChars += 1
|
||||
}
|
||||
|
||||
// Duration start symbol
|
||||
if(current+consumedChars < input.length && input[current+consumedChars] == ':') {
|
||||
rawValue += input[current+consumedChars]
|
||||
duration = ""
|
||||
consumedChars += 1
|
||||
while(current+consumedChars < input.length && "\\d".toRegex().matches(input[current+consumedChars].toString())) {
|
||||
rawValue += input[current+consumedChars]
|
||||
duration += input[current+consumedChars]
|
||||
consumedChars += 1
|
||||
}
|
||||
|
||||
if(duration.isEmpty()) {
|
||||
return TokenizerOutput.NONE
|
||||
}
|
||||
dot = (current+consumedChars < input.length && input[current+consumedChars] == 'd')
|
||||
|
||||
if(dot) {
|
||||
rawValue += input[current+consumedChars]
|
||||
consumedChars += 1
|
||||
}
|
||||
}
|
||||
|
||||
val note = Note.Builder()
|
||||
.pitch(Pitch.parse(notePitch))
|
||||
.dot(dot)
|
||||
octave?.let { note.octave(it) }
|
||||
duration?.let { note.duration(it.toInt()) }
|
||||
|
||||
return TokenizerOutput.produce(consumedChars, note.build(), rawValue, TokenType.NOTE, line, current)
|
||||
// Flat or sharp
|
||||
if (current + consumedChars < input.length && listOf('b', '#').contains(input[current + consumedChars])) {
|
||||
rawValue += input[current + consumedChars]
|
||||
notePitch += input[current + consumedChars]
|
||||
consumedChars += 1
|
||||
}
|
||||
}
|
||||
|
||||
return TokenizerOutput.NONE
|
||||
}
|
||||
// Octave
|
||||
if (current + consumedChars < input.length && "\\d".toRegex().matches(input[current + consumedChars].toString())) {
|
||||
rawValue += input[current + consumedChars]
|
||||
octave = input[current + consumedChars].toString().toInt()
|
||||
consumedChars += 1
|
||||
}
|
||||
|
||||
// Duration start symbol
|
||||
if (current + consumedChars < input.length && input[current + consumedChars] == ':') {
|
||||
rawValue += input[current + consumedChars]
|
||||
duration = ""
|
||||
consumedChars += 1
|
||||
while (current + consumedChars < input.length && "\\d".toRegex().matches(input[current + consumedChars].toString())) {
|
||||
rawValue += input[current + consumedChars]
|
||||
duration += input[current + consumedChars]
|
||||
consumedChars += 1
|
||||
}
|
||||
|
||||
if (duration.isEmpty()) {
|
||||
return TokenizerOutput.NONE
|
||||
}
|
||||
dot = (current + consumedChars < input.length && input[current + consumedChars] == 'd')
|
||||
|
||||
if (dot) {
|
||||
rawValue += input[current + consumedChars]
|
||||
consumedChars += 1
|
||||
}
|
||||
}
|
||||
|
||||
val note = Note(Pitch.parse(notePitch), octave ?: 4, Fraction(1, duration?.toInt() ?: 4), dot)
|
||||
|
||||
return TokenizerOutput.produce(consumedChars, note, rawValue, TokenType.NOTE, line, current)
|
||||
}
|
||||
}
|
||||
|
||||
return TokenizerOutput.NONE
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.smnp.evaluation.evaluator
|
||||
|
||||
import io.smnp.data.entity.Note
|
||||
import io.smnp.dsl.ast.model.node.Node
|
||||
import io.smnp.dsl.ast.model.node.SumOperatorNode
|
||||
import io.smnp.dsl.ast.model.node.TokenNode
|
||||
@@ -11,8 +12,12 @@ import io.smnp.error.PositionException
|
||||
import io.smnp.error.ShouldNeverReachThisLineException
|
||||
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
||||
import io.smnp.evaluation.util.NumberUnification.unify
|
||||
import io.smnp.type.enumeration.DataType
|
||||
import io.smnp.math.Fraction
|
||||
import io.smnp.type.enumeration.DataType.*
|
||||
import io.smnp.type.model.Value
|
||||
import io.smnp.type.model.Value.Companion.list
|
||||
import io.smnp.type.model.Value.Companion.note
|
||||
import io.smnp.type.model.Value.Companion.string
|
||||
|
||||
class SumOperatorEvaluator : Evaluator() {
|
||||
override fun supportedNodes() = listOf(SumOperatorNode::class)
|
||||
@@ -34,20 +39,20 @@ class SumOperatorEvaluator : Evaluator() {
|
||||
}
|
||||
|
||||
private fun plus(lhs: Value, plusNode: Node, rhs: Value, environment: Environment): Value {
|
||||
return if (areNumeric(lhs, rhs))
|
||||
unify(lhs, rhs, int = { (l, r) -> Value.int(l + r) }, float = { (l, r) -> Value.float(l + r) })
|
||||
else if (lhs.type == DataType.STRING)
|
||||
Value.string(lhs.value as String + rhs.value.toString())
|
||||
else if (areLists(lhs, rhs))
|
||||
Value.list(lhs.value as List<Value> + rhs.value as List<Value>)
|
||||
else throw PositionException(
|
||||
EnvironmentException(
|
||||
EvaluationException(
|
||||
"The ${lhs.typeName} and ${rhs.typeName} are not supported by + operator"
|
||||
),
|
||||
environment
|
||||
), plusNode.position
|
||||
)
|
||||
return when {
|
||||
areNumeric(lhs, rhs) -> unify(lhs, rhs, int = { (l, r) -> Value.int(l + r) }, float = { (l, r) -> Value.float(l + r) })
|
||||
lhs.type == STRING -> string(lhs.value as String + rhs.value.toString())
|
||||
areLists(lhs, rhs) -> list(lhs.value as List<Value> + rhs.value as List<Value>)
|
||||
lhs.type == NOTE && rhs.type == INT -> note(lhs.value as Note + Fraction(1, rhs.value as Int))
|
||||
else -> throw PositionException(
|
||||
EnvironmentException(
|
||||
EvaluationException(
|
||||
"The ${lhs.typeName} and ${rhs.typeName} are not supported by + operator"
|
||||
),
|
||||
environment
|
||||
), plusNode.position
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun minus(lhs: Value, minusNode: Node, rhs: Value, environment: Environment): Value {
|
||||
@@ -68,5 +73,5 @@ class SumOperatorEvaluator : Evaluator() {
|
||||
}
|
||||
|
||||
private fun areNumeric(lhs: Value, rhs: Value) = lhs.type.isNumeric() && rhs.type.isNumeric()
|
||||
private fun areLists(lhs: Value, rhs: Value) = lhs.type == DataType.LIST && rhs.type == DataType.LIST
|
||||
private fun areLists(lhs: Value, rhs: Value) = lhs.type == LIST && rhs.type == LIST
|
||||
}
|
||||
Reference in New Issue
Block a user