Koskript
Koskript is a simple, embeddable, and lightweight scripting language designed to be used as a DSL inside Python applications. It features dynamic typing, lexical scoping, and native Python interop — letting you expose any Python function or object directly to your scripts.
NOTE: Koskript is currently in early development. Features like module imports, more types, and performance improvements are on the way.
Features
- Dynamic typing.
- Block-level lexical scoping with
localdeclarations - Constants with
const - Classes with inheritance, visibility, static methods and constructors
- Native Python interop via
KoskriptObject if,elseif,elsewhile,for,foreachloops withbreak/continue- Member access —
map.key.subkey - Index access —
array[0],map["key"] - First-class functions and lambda expressions
- Arithmetic (
+,-,*,/,%), comparison and logical operators - Embeddable in any Python application
Installation
Clone the repository and import it directly into your project:
git clone https://github.com/alesisce/koskript.git
or
pip install -U koskript
Quick Start
from koskript import KoskriptRuntime
runtime = KoskriptRuntime({
"print": print
})
runtime.execute("""
local x = 10
local y = 26
print(x+y)
""")
Any Python value or callable you pass in is wrapped automatically — no need to build KoskriptObject yourself.
Example
// Student grade checker
local students = {
"Aleix": {
"age": 17,
"grade": 95
},
"Maria": {
"age": 15,
"grade": 72
},
"Juan": {
"age": 18,
"grade": 88
}
}
local passing_grade = 75
foreach (name, data in students) {
local grade = data.grade
if (grade >= passing_grade) {
print("PASS:", name, "->", grade) // Depends on how you implement it.
} elseif (grade >= 60) {
print("NEAR PASS:", name, "->", grade)
} else {
print("FAIL:", name, "->", grade)
}
}
Output:
PASS: Aleix -> 95
NEAR PASS: Maria -> 72
PASS: Juan -> 88
Language Reference
Types
| Type | Description |
|---|---|
int |
Integer number |
float |
Floating-point number |
string |
Text string |
bool |
true or false |
null |
The absence of a value |
array |
Ordered list |
map |
Key-value store |
Variables
local x = 10
local pi = 3.14
local name = "Koskript"
local active = true
local missing = null
local items = [1, 2, 3]
local empty = []
local config = { "debug": true, "version": 1 }
Variables declared with local are scoped to the block they are declared in — including if, while, for and foreach bodies.
Constants
const declares a read-only binding in the current scope. Reassigning it raises a ProtectedObject error, but the contents of an array or map can still be modified:
const PI = 3.14
const CONFIG = { "debug": true }
CONFIG.debug = false // ok, the binding is constant, not the contents
PI = 3.0 // error: cannot modify a constant value
Functions
fn add(a, b) {
return a + b
}
local result = add(10, 20)
Operators
local a = 2 + 3 * 4 // 14 (precedence: * / % before + -)
local b = 10 % 3 // 1
local c = -a // unary minus
local d = (a + b) * 2 // grouping with parentheses
if (x >= 10 and not done or retry) {
// ...
}
| Operators | Description |
|---|---|
+ - * / % |
Arithmetic |
-x |
Unary minus |
== != > < >= <= |
Comparison |
and or not |
Logical (short-circuiting) |
( ) |
Grouping |
Control Flow
if (x > 10) {
print("big")
} elseif (x == 10) {
print("exact")
} else {
print("small")
}
Lambda Functions
local greet = () {
print("Hello world")
}
greet()
// lambdas can take parameters and return values
local add = (a, b) {
return a + b
}
print(add(1, 2))
// invoke a lambda literal directly
print((() { return 42 })())
Classes
Classes support single inheritance, visibility modifiers, static methods and constructors:
class Animal {
public name = "generic" // fields can be public or private
private energy = 100
constructor(name) {
this.name = name
}
public fn speak() {
return "..."
}
public fn describe() {
return ::speak() + " " + this.name // :: calls an instance method
}
private fn drain() {
this.energy = this.energy - 10
}
static fn kingdom() {
return "animalia" // static methods have no `this`
}
}
class Dog extends Animal {
constructor(name) {
super::constructor(name) // parent constructor
}
public fn speak() {
return "woof" // overrides Animal.speak
}
public fn parent_speak() {
return super::speak() // parent implementation
}
}
const rex = new Dog("Rex")
print(rex.describe()) // woof Rex (dynamic dispatch)
print(rex::describe()) // same, called with `::` from outside
print(rex.parent_speak()) // ...
print(Dog.kingdom()) // animalia (statics always use `.`)
Rules:
| Syntax | Meaning |
|---|---|
public fn / private fn |
Instance method. public is the default. |
static fn |
Static method. Called as .Method() inside the class or Class.Method() outside. |
public x = value / private x = value |
Instance field with a default value. public is the default. |
constructor(params) { } |
Runs on new Class(args). |
this.field |
Field access inside instance methods. |
::Method() |
Calls an instance method with the current this (dynamic dispatch). |
.Method() |
Calls a static method of the current class. |
instance::Method() |
Calls an instance method from outside, binding this to that instance. |
Class.Method() |
Calls a static method from outside (statics never use ::). |
super::Method() |
Calls the parent implementation. |
super::constructor(args) |
Calls the parent constructor (only inside a constructor). |
new Class(args) |
Expression that creates an instance. |
- Private members are only accessible from methods of the class that defines them; they are not inherited.
- A subclass cannot redeclare an inherited field, or change a method between static and instance.
- If a class defines no constructor, the nearest inherited constructor is used.
- Fields are initialized (parent first) before the constructor runs.
Loops
// while
while (x > 0) {
x = x - 1
}
// for — iterate array
for (item in items) {
print(item)
}
// foreach — iterate map
foreach (key, value in config) {
print(key, value)
}
break exits the nearest loop and continue skips to the next iteration:
for (item in items) {
if (item == 2) {
continue // skip this item
}
if (item == 5) {
break // stop looping
}
print(item)
}
Member Access
local user = { "name": "Aleix", "age": 17 }
print(user.name)
print(user.age)
Index Access
local items = [10, 20, 30]
print(items[0]) // 10
print(items[-1]) // 30
local config = { "debug": true }
print(config["debug"]) // true
// member access and indexing can be chained
local data = { "nums": [1, 2, 3] }
print(data.nums[1]) // 2
Strings
Strings support single or double quotes and the escapes \n, \t, \r, \0, \\, \" and \':
print("line one\nline two")
Comments
// line comments start with two slashes
Reserved Keywords
The following words cannot be used as identifiers:
if elseif else while for foreach fn return local const true false null and or not in break continue class extends new static public private this super constructor
Python Interop
Any Python value or callable can be exposed to Koskript. They are wrapped in a KoskriptObject automatically:
from koskript import KoskriptRuntime
runtime = KoskriptRuntime({
"print": print,
"len": len,
})
# add more later — register() is chainable
runtime.register("sqrt", math.sqrt)
runtime["now"] = time.time
Embedding API
execute() returns the value of the last evaluated expression, or the value of a top-level return:
runtime = KoskriptRuntime({"print": print})
result = runtime.execute("local x = 10\nx * 2") # 20
result = runtime.execute("return 1 + 2") # 3
For a quick one-off script, use the run() helper:
from koskript import run
run("print(1 + 2)", print=print) # 3
Errors raised by scripts are available under koskript.Errors:
from koskript import Errors
try:
runtime.execute("local a = [1]\nprint(a[5])")
except Errors.RuntimeError as e:
print(e)
Roadmap
- Index access (
array[0],map["key"]) -
floattype -
nulltype -
break/continuestatements - Module imports (
import "mymodule") - Performance improvements
- Standard library
- PyPI package
- Custom parser (remove Lark dependency)
- VM-based execution
License
Koskript is licensed under the Mozilla Public License 2.0 (MPL-2.0).
You are free to use, modify, and redistribute Koskript. However, you may not redistribute this project under a different name or claim authorship of the Koskript language.
Koskript is a trademark of Alesis.
Built with ❤️ by Alesis
Metadata
Release files for koskript 1.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| koskript-1.3.1.tar.gz | 33.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| koskript-1.3.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 62.6 kB
Release files / koskript-1.3.1.tar.gz
| Download URL | koskript-1.3.1.tar.gz |
|---|---|
| Size | 33.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
92b756a2035868304fbda76cb993bc497fff18e651a5db627d46be2e202fc894
|
|
BLAKE2b-256 checksum How to use checksums |
895161f3492a76ffb04ae549d883ba021a3b22a55ed5089bd79a5e278abb92f5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|
Release files / koskript-1.3.1-py3-none-any.whl
| Download URL | koskript-1.3.1-py3-none-any.whl |
|---|---|
| Size | 29.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a989eb48339004953ab3b615b7fbd34b1b81d9f2b357270eb700cf1ede2a1a7a
|
|
BLAKE2b-256 checksum How to use checksums |
2953a5c8ef1f498077ad7acde09fc09c0d74000cc6466c6a46b7683b01127d66
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|