Implement some new stdlib functions and methods
This commit is contained in:
45
api/src/main/kotlin/io/smnp/ext/HybridModuleProvider.kt
Normal file
45
api/src/main/kotlin/io/smnp/ext/HybridModuleProvider.kt
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package io.smnp.ext
|
||||||
|
|
||||||
|
import io.smnp.callable.function.Function
|
||||||
|
import io.smnp.callable.method.Method
|
||||||
|
import io.smnp.interpreter.LanguageModuleInterpreter
|
||||||
|
import io.smnp.type.module.Module
|
||||||
|
|
||||||
|
abstract class HybridModuleProvider(path: String) : ModuleProvider(path) {
|
||||||
|
open fun functions(): List<Function> = emptyList()
|
||||||
|
open fun methods(): List<Method> = emptyList()
|
||||||
|
open fun files() = listOf("main.mus")
|
||||||
|
|
||||||
|
override fun provideModule(interpreter: LanguageModuleInterpreter): Module {
|
||||||
|
return provideNativeModule(interpreter).merge(provideLanguageModule(interpreter))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun provideNativeModule(interpreter: LanguageModuleInterpreter): Module {
|
||||||
|
return object : NativeModuleProvider(path) {
|
||||||
|
override fun functions() = this@HybridModuleProvider.functions()
|
||||||
|
override fun methods() = this@HybridModuleProvider.methods()
|
||||||
|
}.provideModule(interpreter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disclaimer:
|
||||||
|
// Unfortunately, LanguageModuleProvider cannot be reused here because
|
||||||
|
// getResource() method returns null if is invoked from anonymous class's object instance.
|
||||||
|
// Therefore it is need to have following code here.
|
||||||
|
private fun provideLanguageModule(interpreter: LanguageModuleInterpreter): Module {
|
||||||
|
val segments = path.split(".")
|
||||||
|
val parentNodesChainPath = segments.dropLast(1).joinToString(".")
|
||||||
|
val moduleName = segments.last()
|
||||||
|
|
||||||
|
val module = files()
|
||||||
|
.asSequence()
|
||||||
|
.map { it to javaClass.classLoader.getResource(it) }
|
||||||
|
.map { it.first to (it.second?.readText() ?: throw RuntimeException("Module '$path' does not contain '${it.first}' file")) }
|
||||||
|
.map { interpreter.run(it.second, "module $path::${it.first}") }
|
||||||
|
.map { it.getRootModule() }
|
||||||
|
.reduce { acc, module -> acc.merge(module) }
|
||||||
|
|
||||||
|
module.name = moduleName
|
||||||
|
|
||||||
|
return Module.create(parentNodesChainPath, children = listOf(module))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,16 @@ function _flatten(list: list, output: list) {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function shuffle(list: list) {
|
||||||
|
shuffled = list;
|
||||||
|
list.size-1 as first ^ {
|
||||||
|
second = random(0, list.size);
|
||||||
|
shuffled = shuffled.swap(first, second);
|
||||||
|
}
|
||||||
|
|
||||||
|
return shuffled;
|
||||||
|
}
|
||||||
|
|
||||||
extend list {
|
extend list {
|
||||||
function flatten() {
|
function flatten() {
|
||||||
return flatten(this);
|
return flatten(this);
|
||||||
@@ -52,4 +62,41 @@ extend list {
|
|||||||
function isNotEmpty() {
|
function isNotEmpty() {
|
||||||
return not this.isEmpty();
|
return not this.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function dropIndex(index: int) {
|
||||||
|
output = [];
|
||||||
|
i = 0;
|
||||||
|
this as item ^ {
|
||||||
|
if(index != i) {
|
||||||
|
output = output + [this.get(i)];
|
||||||
|
}
|
||||||
|
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
function put(index: int, value) {
|
||||||
|
return (index as i ^ this.get(i)) + [value] + ((this.size-index) as i ^ this.get(i+index));
|
||||||
|
}
|
||||||
|
|
||||||
|
function replace(index: int, value) {
|
||||||
|
return this
|
||||||
|
.dropIndex(index)
|
||||||
|
.put(index, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function swap(a: int, b: int) {
|
||||||
|
A = this.get(a);
|
||||||
|
B = this.get(b);
|
||||||
|
|
||||||
|
return this
|
||||||
|
.replace(a, B)
|
||||||
|
.replace(b, A);
|
||||||
|
}
|
||||||
|
|
||||||
|
function shuffle() {
|
||||||
|
return shuffle(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -7,10 +7,11 @@ import io.smnp.ext.lang.function.TypeOfFunction
|
|||||||
import io.smnp.ext.lang.method.CharAtMethod
|
import io.smnp.ext.lang.method.CharAtMethod
|
||||||
import io.smnp.ext.lang.method.ListAccessMethod
|
import io.smnp.ext.lang.method.ListAccessMethod
|
||||||
import io.smnp.ext.lang.method.MapAccessMethod
|
import io.smnp.ext.lang.method.MapAccessMethod
|
||||||
|
import io.smnp.ext.lang.method.StringifyMethod
|
||||||
import org.pf4j.Extension
|
import org.pf4j.Extension
|
||||||
|
|
||||||
@Extension
|
@Extension
|
||||||
class LangModule : NativeModuleProvider("smnp.lang") {
|
class LangModule : NativeModuleProvider("smnp.lang") {
|
||||||
override fun functions() = listOf(IntConstructor(), NoteConstructor(), TypeOfFunction())
|
override fun functions() = listOf(IntConstructor(), NoteConstructor(), TypeOfFunction())
|
||||||
override fun methods() = listOf(ListAccessMethod(), MapAccessMethod(), CharAtMethod())
|
override fun methods() = listOf(ListAccessMethod(), MapAccessMethod(), CharAtMethod(), StringifyMethod())
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package io.smnp.ext.lang.method
|
||||||
|
|
||||||
|
import io.smnp.callable.method.Method
|
||||||
|
import io.smnp.callable.method.MethodDefinitionTool
|
||||||
|
import io.smnp.callable.signature.Signature.Companion.simple
|
||||||
|
import io.smnp.type.matcher.Matcher.Companion.anyType
|
||||||
|
import io.smnp.type.model.Value
|
||||||
|
|
||||||
|
class StringifyMethod : Method(anyType(), "toString") {
|
||||||
|
override fun define(new: MethodDefinitionTool) {
|
||||||
|
new method simple() body { _, obj, _ ->
|
||||||
|
Value.string(obj.stringify())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
package io.smnp.ext
|
package io.smnp.ext
|
||||||
|
|
||||||
import io.smnp.ext.function.ModuloFunction
|
import io.smnp.ext.function.ModuloFunction
|
||||||
|
import io.smnp.ext.function.RandomFunction
|
||||||
import io.smnp.ext.function.RangeFunction
|
import io.smnp.ext.function.RangeFunction
|
||||||
import org.pf4j.Extension
|
import org.pf4j.Extension
|
||||||
|
|
||||||
@Extension
|
@Extension
|
||||||
class MathModule : NativeModuleProvider("smnp.math") {
|
class MathModule : HybridModuleProvider("smnp.math") {
|
||||||
override fun functions() = listOf(ModuloFunction(), RangeFunction())
|
override fun functions() = listOf(ModuloFunction(), RangeFunction(), RandomFunction())
|
||||||
|
override fun dependencies() = listOf("smnp.lang", "smnp.collection")
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package io.smnp.ext.function
|
||||||
|
|
||||||
|
import io.smnp.callable.function.Function
|
||||||
|
import io.smnp.callable.function.FunctionDefinitionTool
|
||||||
|
import io.smnp.callable.signature.Signature.Companion.simple
|
||||||
|
import io.smnp.type.model.Value
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
class RandomFunction : Function("random") {
|
||||||
|
override fun define(new: FunctionDefinitionTool) {
|
||||||
|
new function simple() body { _, _ ->
|
||||||
|
Value.float(Random.nextFloat())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
82
modules/math/src/main/resources/main.mus
Normal file
82
modules/math/src/main/resources/main.mus
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
function random(min: int, max: int) {
|
||||||
|
return Int(random() * (max-min) + min)
|
||||||
|
}
|
||||||
|
|
||||||
|
function random(min: float, max: float) {
|
||||||
|
return random() * (max-min) + min
|
||||||
|
}
|
||||||
|
|
||||||
|
function min(numbers: list<int, float>) {
|
||||||
|
if(numbers.isEmpty()) {
|
||||||
|
throw "Empty lists are not supported";
|
||||||
|
}
|
||||||
|
|
||||||
|
min = numbers.get(0);
|
||||||
|
numbers as number ^ {
|
||||||
|
if(number < min) {
|
||||||
|
min = number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
|
||||||
|
function max(numbers: list<int, float>) {
|
||||||
|
if(numbers.isEmpty()) {
|
||||||
|
throw "Empty lists are not supported";
|
||||||
|
}
|
||||||
|
|
||||||
|
max = numbers.get(0);
|
||||||
|
numbers as number ^ {
|
||||||
|
if(number > max) {
|
||||||
|
max = number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sample(list: list) {
|
||||||
|
return list.get(random(0, list.size));
|
||||||
|
}
|
||||||
|
|
||||||
|
function pick(...items: map<string><>) {
|
||||||
|
return pick(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
function pick(items: list<map<string><>>) {
|
||||||
|
acc = 0;
|
||||||
|
|
||||||
|
items as (item, index) ^ {
|
||||||
|
if(item.size != 2) {
|
||||||
|
throw "Expected lists with two items: 'chance' and 'value'";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(not item.containsKey("chance")) {
|
||||||
|
throw "Item " + (index+1) + " does not have 'chance' key";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(not item.containsKey("value")) {
|
||||||
|
throw "Item " + (index+1) + " does not have 'value' key";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(typeOf(item.get("chance")) != "int") {
|
||||||
|
throw "Expected 'chance' to be of int type";
|
||||||
|
}
|
||||||
|
|
||||||
|
acc = acc + item.get("chance");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(acc != 100) {
|
||||||
|
throw "The total sum of each item ('chance' key) should be equal to 100";
|
||||||
|
}
|
||||||
|
|
||||||
|
acc = 0;
|
||||||
|
random = random(0, 100);
|
||||||
|
items as item ^ {
|
||||||
|
acc = acc + item.get("chance");
|
||||||
|
if(random < acc) {
|
||||||
|
return item.get("value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user