Skip to content

Commit

Permalink
Merge branch 'develop' into feature/data_definition_from_dspf
Browse files Browse the repository at this point in the history
  • Loading branch information
lanarimarco authored Jun 10, 2024
2 parents 4d485c6 + 0dbbe29 commit 29409a0
Show file tree
Hide file tree
Showing 16 changed files with 469 additions and 37 deletions.
2 changes: 1 addition & 1 deletion dspfparser/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlinVersion"
testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlinVersion"
testImplementation 'junit:junit:4.12'
testImplementation 'junit:junit:4.13.1'

implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$serializationVersion"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-cbor:$serializationVersion"
Expand Down
2 changes: 1 addition & 1 deletion examples/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ dependencies {
implementation project(":rpgJavaInterpreter-core")
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlinVersion"
testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlinVersion"
testImplementation 'junit:junit:4.12'
testImplementation 'junit:junit:4.13.1'
}

task javadocJar(type: Jar) {
Expand Down
2 changes: 1 addition & 1 deletion kolasu/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlinVersion"
testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlinVersion"
testImplementation 'junit:junit:4.12'
testImplementation 'junit:junit:4.13.1'

implementation 'com.fifesoft:rsyntaxtextarea:2.5.8'
implementation 'com.fifesoft:autocomplete:2.5.8'
Expand Down
10 changes: 5 additions & 5 deletions rpgJavaInterpreter-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ dependencies {
api project(":dspfparser")

implementation "org.apache.logging.log4j:log4j-api-kotlin:1.0.0"
implementation "org.apache.logging.log4j:log4j-api:2.15.0"
implementation "org.apache.logging.log4j:log4j-core:2.15.0"
implementation "org.apache.logging.log4j:log4j-api:2.23.0"
implementation 'org.apache.logging.log4j:log4j-core:2.23.0'

implementation 'commons-io:commons-io:2.6'
implementation 'commons-io:commons-io:2.7'
implementation 'com.github.ajalt:clikt:2.1.0'

api "io.github.smeup.reload:base:$reloadVersion"
Expand All @@ -76,8 +76,8 @@ dependencies {

testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlinVersion"
testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlinVersion"
testImplementation 'junit:junit:4.12'
testImplementation 'org.hsqldb:hsqldb:2.5.0'
testImplementation 'junit:junit:4.13.1'
testImplementation 'org.hsqldb:hsqldb:2.7.1'
testImplementation 'org.mockito.kotlin:mockito-kotlin:5.3.1'
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ interface Evaluator {
fun eval(expression: MultExpr): Value
fun eval(expression: CharExpr): Value
fun eval(expression: LookupExpr): Value
fun eval(expression: LookupGtExpr): Value
fun eval(expression: LookupGeExpr): Value
fun eval(expression: LookupLtExpr): Value
fun eval(expression: LookupLeExpr): Value
fun eval(expression: ArrayAccessExpr): Value
fun eval(expression: HiValExpr): HiValValue
fun eval(expression: LowValExpr): LowValValue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@ import com.smeup.rpgparser.execution.MainExecutionContext
import com.smeup.rpgparser.logging.ProgramUsageType
import com.smeup.rpgparser.parsing.ast.*
import com.smeup.rpgparser.parsing.parsetreetoast.LogicalCondition
import com.smeup.rpgparser.utils.asBigDecimal
import com.smeup.rpgparser.utils.asLong
import com.smeup.rpgparser.utils.divideAtIndex
import com.smeup.rpgparser.utils.moveEndingString
import com.smeup.rpgparser.utils.*
import com.strumenta.kolasu.model.Position
import com.strumenta.kolasu.model.specificProcess
import java.math.BigDecimal
Expand All @@ -33,10 +30,7 @@ import java.time.LocalDate
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit
import kotlin.math.abs
import kotlin.math.pow
import kotlin.math.roundToLong
import kotlin.math.sqrt
import kotlin.math.*
import kotlin.time.Duration.Companion.nanoseconds

class ExpressionEvaluation(
Expand Down Expand Up @@ -248,25 +242,58 @@ class ExpressionEvaluation(
}

override fun eval(expression: LookupExpr): Value = proxyLogging(expression) {
val searchValued = expression.searchedValued.evalWith(this)
val array = expression.array.evalWith(this) as ArrayValue
var index = array.elements().indexOfFirst {
areEquals(it, searchValued)
}
// If 'start' and/or 'length' specified (both optional)
// Start: is the index of array element to start search
// Length: is the limit number of elements to search forward
var start = expression.start?.evalWith(this)?.asInt()
start = start?.minus(1.asValue()) ?: 0.asValue()
lookup(
array = expression.array.evalWith(this) as ArrayValue,
arrayType = expression.array.type() as ArrayType,
searchedValue = expression.searchedValued.evalWith(this),
start = expression.start?.evalWith(this)?.asInt(),
length = expression.length?.evalWith(this)?.asInt(),
operator = ComparisonOperator.EQ
)
}

val arrayElements = expression.array.type().numberOfElements().asValue()
val length = expression.length?.evalWith(this)?.asInt() ?: arrayElements
override fun eval(expression: LookupGtExpr): Value = proxyLogging(expression) {
lookup(
array = expression.array.evalWith(this) as ArrayValue,
arrayType = expression.array.type() as ArrayType,
searchedValue = expression.searchedValue.evalWith(this),
start = expression.start?.evalWith(this)?.asInt(),
length = expression.length?.evalWith(this)?.asInt(),
operator = ComparisonOperator.GT
)
}

val lowerLimit = start.value
val upperLimit = start.plus(length).value - 1
if (lowerLimit > index || upperLimit < index) index = -1
override fun eval(expression: LookupGeExpr): Value = proxyLogging(expression) {
lookup(
array = expression.array.evalWith(this) as ArrayValue,
arrayType = expression.array.type() as ArrayType,
searchedValue = expression.searchedValue.evalWith(this),
start = expression.start?.evalWith(this)?.asInt(),
length = expression.length?.evalWith(this)?.asInt(),
operator = ComparisonOperator.GE
)
}

return@proxyLogging if (index == -1) 0.asValue() else (index + 1).asValue()
override fun eval(expression: LookupLtExpr): Value = proxyLogging(expression) {
lookup(
array = expression.array.evalWith(this) as ArrayValue,
arrayType = expression.array.type() as ArrayType,
searchedValue = expression.searchedValue.evalWith(this),
start = expression.start?.evalWith(this)?.asInt(),
length = expression.length?.evalWith(this)?.asInt(),
operator = ComparisonOperator.LT
)
}

override fun eval(expression: LookupLeExpr): Value = proxyLogging(expression) {
lookup(
array = expression.array.evalWith(this) as ArrayValue,
arrayType = expression.array.type() as ArrayType,
searchedValue = expression.searchedValue.evalWith(this),
start = expression.start?.evalWith(this)?.asInt(),
length = expression.length?.evalWith(this)?.asInt(),
operator = ComparisonOperator.LE
)
}

override fun eval(expression: ArrayAccessExpr): Value = proxyLogging(expression) {
Expand Down Expand Up @@ -792,6 +819,117 @@ class ExpressionEvaluation(
}
}

private fun lookupLinearSearch(values: List<Value>, target: Value): Int {
for ((index, value) in values.withIndex()) {
if (areEquals(value, target))
return index
}

return -1
}

private inline fun lookupBinarySearchWithComparator(
values: List<Value>,
predicate: (Value) -> Boolean,
isAscending: Boolean,
searchingForLower: Boolean
): Int {
var (left, right) = Pair(0, values.lastIndex)
var bestCandidateIndex = -1

while (left <= right) {
val mid = left + (right - left) / 2
val currentValue = values[mid]
val matchesCondition = predicate(currentValue)
if (matchesCondition) bestCandidateIndex = mid

/*
* Detect if sorting order follows search direction in order to decide which endpoint to move
* - if it follows direction: move left on match and move right else-wise
* - if it does not follow direction: move right on match and move left else-wise
*/
val shouldSearchRight = if (isAscending == searchingForLower) matchesCondition else !matchesCondition
if (shouldSearchRight) left = mid + 1 else right = mid - 1
}

return bestCandidateIndex
}

private inline fun lookupBinarySearchWithCondition(values: List<Value>, predicate: (Value) -> Boolean, target: Value): Int {
var (left, right) = Pair(0, values.lastIndex)

while (left <= right) {
val mid = left + (right - left) / 2
val currentValue = values[mid]

if (predicate(currentValue)) {
return mid
}

if (currentValue < target) {
right = mid - 1
continue
}

// This means currentValue > target
left = mid + 1
}

return -1
}

private fun lookupBinarySearch(
values: List<Value>,
target: Value,
operator: ComparisonOperator,
isAscending: Boolean
): Int {
return when (operator) {
ComparisonOperator.LE ->
lookupBinarySearchWithComparator(values, { it <= target }, isAscending, true)
ComparisonOperator.LT ->
lookupBinarySearchWithComparator(values, { it < target }, isAscending, true)
ComparisonOperator.GE ->
lookupBinarySearchWithComparator(values, { it >= target }, isAscending, false)
ComparisonOperator.GT ->
lookupBinarySearchWithComparator(values, { it > target }, isAscending, false)
ComparisonOperator.EQ -> lookupBinarySearchWithCondition(values, { areEquals(it, target) }, target)
else -> -1
}
}

private fun lookup(
array: ArrayValue,
arrayType: ArrayType,
searchedValue: Value,
start: IntValue?,
length: IntValue?,
operator: ComparisonOperator
): Value {
val arrayLength = arrayType.numberOfElements()
val isSequenced = arrayType.ascend != null

when (operator) {
ComparisonOperator.GE, ComparisonOperator.GT, ComparisonOperator.LE, ComparisonOperator.LT -> {
if (!isSequenced)
throw UnsupportedOperationException("Array must be defined with ASCEND or DESCEND keyword")
}
else -> {}
}

val computedLength = length?.value?.toInt() ?: arrayLength
val lowerBound: Int = start?.value?.let { it - 1 }?.toInt() ?: 0
val upperBound = min(lowerBound + computedLength, arrayLength)
val searchRange = array.elements().slice(lowerBound until upperBound)

val index = if (isSequenced)
lookupBinarySearch(searchRange, searchedValue, operator, arrayType.ascend!!)
else lookupLinearSearch(searchRange, searchedValue)

val offsetIndex = if (index == -1) 0 else index + lowerBound + 1
return offsetIndex.asValue()
}

private fun cleanNumericString(s: String): String {
val result = s.moveEndingString("-")
return when {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,6 @@ import com.strumenta.kolasu.model.Position
import kotlinx.serialization.Serializable

// %LOOKUP
// To be supported:
// * %LOOKUPLT
// * %LOOKUPLE
// * %LOOKUPGT
// * %LOOKUPGE
@Serializable
data class LookupExpr(
var searchedValued: Expression,
Expand All @@ -40,6 +35,62 @@ data class LookupExpr(
override fun evalWith(evaluator: Evaluator): Value = evaluator.eval(this)
}

// %LOOKUPGT
@Serializable
data class LookupGtExpr(
var searchedValue: Expression,
val array: Expression,
val start: Expression? = null,
val length: Expression? = null,
override val position: Position? = null
) : Expression(position) {
override val loggableEntityName: String
get() = "%LOOKUPGT"
override fun evalWith(evaluator: Evaluator): Value = evaluator.eval(this)
}

// %LOOKUPGE
@Serializable
data class LookupGeExpr(
var searchedValue: Expression,
val array: Expression,
val start: Expression? = null,
val length: Expression? = null,
override val position: Position? = null
) : Expression(position) {
override val loggableEntityName: String
get() = "%LOOKUPGE"
override fun evalWith(evaluator: Evaluator): Value = evaluator.eval(this)
}

// %LOOKUPLT
@Serializable
data class LookupLtExpr(
var searchedValue: Expression,
val array: Expression,
val start: Expression? = null,
val length: Expression? = null,
override val position: Position? = null
) : Expression(position) {
override val loggableEntityName: String
get() = "%LOOKUPLT"
override fun evalWith(evaluator: Evaluator): Value = evaluator.eval(this)
}

// %LOOKUPLE
@Serializable
data class LookupLeExpr(
var searchedValue: Expression,
val array: Expression,
val start: Expression? = null,
val length: Expression? = null,
override val position: Position? = null
) : Expression(position) {
override val loggableEntityName: String
get() = "%LOOKUPLE"
override fun evalWith(evaluator: Evaluator): Value = evaluator.eval(this)
}

// %SCAN
@Serializable
data class ScanExpr(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ data class CompilationUnit(

private val inStatementsDataDefinitions = mutableListOf<InStatementDataDefinition>()

fun getInStatementDataDefinitions() = inStatementsDataDefinitions

fun addInStatementDataDefinitions(dataDefinitions: List<InStatementDataDefinition>) {
inStatementsDataDefinitions.addAll(dataDefinitions)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ private val modules = SerializersModule {
subclass(LogicalCondition::class)
subclass(LogicalOrExpr::class)
subclass(LookupExpr::class)
subclass(LookupGtExpr::class)
subclass(LookupGeExpr::class)
subclass(LookupLtExpr::class)
subclass(LookupLeExpr::class)
subclass(LowValExpr::class)
subclass(MinusExpr::class)
subclass(MultExpr::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1188,7 +1188,10 @@ data class DefineStmt(
val containingCU = this.ancestor(CompilationUnit::class.java)
?: return emptyList()

// Search standalone 'D spec' or InStatement definition
val originalDataDefinition = containingCU.dataDefinitions.find { it.name == originalName }
?: containingCU.getInStatementDataDefinitions().find { it.name == originalName }

// If definition was not found as a 'standalone' 'D spec' declaration,
// maybe it can be found as a sub-field of DS in 'D specs' declarations
containingCU.dataDefinitions.forEach {
Expand Down
Loading

0 comments on commit 29409a0

Please sign in to comment.