Dialect of Python with explicit variable declaration and block scoping, with a lightweight and easy-to-embed bytecode compiler and interpreter.
Go to file
2020-12-29 18:51:03 +09:00
modules Embed __builtins__ source directly 2020-12-29 18:50:18 +09:00
test switch some type conversions to bound methods 2020-12-29 16:40:42 +09:00
.gitignore Embed __builtins__ source directly 2020-12-29 18:50:18 +09:00
builtins.krk Embed __builtins__ source directly 2020-12-29 18:50:18 +09:00
chunk.c Fix up repl 2020-12-28 10:54:25 +09:00
chunk.h add an exception mechanism 2020-12-29 11:00:12 +09:00
compiler.c fix up some compiler eof stuff to make embedding more reasonable 2020-12-29 18:50:39 +09:00
compiler.h Fix up repl 2020-12-28 10:54:25 +09:00
debug.c add an exception mechanism 2020-12-29 11:00:12 +09:00
debug.h through chapter 19 of Crafting Interpreters 2020-12-26 09:32:21 +09:00
kuroko.c clean up repl highlighter 2020-12-28 21:31:46 +09:00
kuroko.h let's build things for a faster vm by default 2020-12-28 16:31:53 +09:00
LICENSE meta stuff 2020-12-26 16:59:43 +09:00
Makefile Embed __builtins__ source directly 2020-12-29 18:50:18 +09:00
memory.c add an exception mechanism 2020-12-29 11:00:12 +09:00
memory.h Basic garbage collection; had to fix some stuff for stack preallocation 2020-12-27 16:07:27 +09:00
object.c escape sequences in strings 2020-12-29 16:26:00 +09:00
object.h fix upvalue storage being completely broken when we move the stack 2020-12-28 22:02:39 +09:00
README.md reword README 2020-12-29 15:39:34 +09:00
rline.c add an exception mechanism 2020-12-29 11:00:12 +09:00
rline.h add fancy repl 2020-12-28 11:37:38 +09:00
scanner.c fix up some compiler eof stuff to make embedding more reasonable 2020-12-29 18:50:39 +09:00
scanner.h once again, completely redo the parsing for indentation so that we can have pretty syntax errors 2020-12-29 15:26:45 +09:00
table.c Clean up some pedantic warning stuff 2020-12-28 11:11:50 +09:00
table.h Clean up some pedantic warning stuff 2020-12-28 11:11:50 +09:00
value.c add an exception mechanism 2020-12-29 11:00:12 +09:00
value.h add an exception mechanism 2020-12-29 11:00:12 +09:00
vm.c More stuff for embedding 2020-12-29 18:51:03 +09:00
vm.h More stuff for embedding 2020-12-29 18:51:03 +09:00

Kuroko - A bytecode-compiled scripting language

Kuroko is a bytecode-interpreted, dynamic, strongly-typed language with syntax similar to Python.

The bytecode VM / compiler is substantially based on Robert Nystrom's Crafting Interpreters.

At the moment, the intent for this project is to add a proper scripting language to Bim, to which both configuration scripts and syntax highlighting will be ported.

Kuroko, as its name should imply, will also be made available in ToaruOS as a general-purpose user language, and some utilities may end up being written in it.

Features

Kuroko inherits some core features by virtue of following Crafting Interpreters, including its basic type system, classes/methods/functions, and the design of its compiler and bytecode VM.

On top of this, Kuroko has:

  • Python-style indentation-driven block syntax.
  • Importable modules, which run once when first imported and return a value (which should generally be an object).
  • [] as an overloadable operator (calls __get__ and __set__ depending on usage).
  • Built-in types for lists (list) and hashmaps (dict).
  • Exception handling with try/except/raise.
  • Syntax-highlighted repl based on ToaruOS's rline library.
  • Native support for iterators with for VAL in ITER: syntax.

Examples

NOTE: Due to limitations with Github's markdown renderer, these snippets will be highlighted as Python code.

Hello World

Kuroko inherits a print statement from its Lox roots, which is similar to Python's:

print "Hello, world!"
# → Hello, world!

Basic Integer Operations

print 1 + 2 + 3
# → 6

String Concatenation

Strings can be concatenated, and other values can be appended to them.

print "Hello, " + 42 + "!"
# → Hello, 42!

Functions

Function syntax is essentially the same as in Python:

def greet(name):
    print "Hello, " + name + "!"
greet("user")
# → Hello, user!

Variables

In a departure from Python, Kuroko has explicit variable declaration and traditional scoping rules. Variables are declared with the let keyword and take the value None if not defined at declaration time:

let foo
print foo
# → None
foo = 1
print foo
# → 1

Basic Objects

Objects and classes in Kuroko work a lot like Python or similar languages in that they have an arbitrary and mutable set of fields, which may be methods or other values.

To create a basic object without methods, the object class is provided:

let o = object()
o.foo = "bar"
print o.foo
# → bar

To supply methods, define a class:

class Foo():
    def printFoo():
        print self.foo
let o = Foo()
o.foo = "bar"
o.printFoo()
# → bar

The self keyword is implicit in all methods and does not need to be supplied in the argument list. You may optionally include it in the method declaration anyway:

class Foo():
    def printFoo(self):
        print self.foo
let o = Foo()
o.foo = "bar"
o.printFoo()
# → bar

When a class is instantiated, if it has an __init__ method it will be called automatically. __init__ may take arguments as well.

class Foo():
    def __init__(bar):
        self.foo = bar
    def printFoo(self):
        print self.foo
let o = Foo("bar")
o.printFoo()
# → bar

Some other special method names include __get__, __set__, and __str__, which will be explained later.

Collections

Kuroko has built-in classes for flexible arrays (list) and hashmaps (dict):

let l = list()
l.append(1)
l.append(2)
l.append("three")
l.append(False)
print l
# → [1, 2, three, False]
l[1] = 5
print l
# → [1, 5, three, False]
let d = dict()
d["foo"] = "bar"
d[1] = 2
print d
# → {1: 2, foo: bar}

Exceptions

Kuroko provides a mechanism for handling errors at runtime. If an error is not caught, the interpreter will end and print a traceback.

def foo(bar):
    print "I expect an argument! " + bar
foo() # I didn't provide one!
# → Wrong number of arguments (1 expected, got 0)
#   Traceback, most recent first, 1 call frames:
#     File "<stdin>", line 3, in <module>

To catch exceptions, use try/except:

def foo(bar):
    print "I expect an argument! " + bar
try:
    foo() # I didn't provide one!
except:
    print "oh no!"
# → oh no!

Runtime exceptions are passed to the except block as a special variable exception. As of this writing, runtime exceptions from the VM are strings.

def foo(bar):
    print "I expect an argument! " + bar
try:
    foo() # I didn't provide one!
except:
    print "oh no, there was an exception: " + exception
# → oh no, there was an exception: Wrong number of arguments (1 expected, got 0)

Exceptions can also be generated from code:

def login(password):
    if password != "supersecret":
        raise "Wrong password, try again!"
    print "[Hacker voice] I'm in."
login("foo")
# → Wrong password, try again!
#   Traceback, most recent first, 2 call frames:
#     File "<stdin>", line 5, in <module>
#     File "<stdin>", line 3, in login

The except block is optional, and an exception may be caught and ignored.

def login(password):
    if password != "supersecret":
        raise "Wrong password, try again!"
    print "[Hacker voice] I'm in."
try:
    login("foo")
# → Wrong password, try again!
#   Traceback, most recent first, 2 call frames:
#     File "<stdin>", line 6, in <module>
#     File "<stdin>", line 3, in login

Modules

Modules allow scripts to call other scripts.

# modules/demomodule.krk
let module = object()
module.foo = "bar"
return module
# demo.krk
import demomodule
print demomodule.foo
# → bar

When modules are imported, they run in a function local context and variables they declare do not live in the global namespace.

To put variables into the global namespace, use the export keyword:

# modules/demomodule.krk
let module = object()
foo = "bar"
export foo
return module
# demo.krk
import demomodule
print foo
# → bar

Loops

Kuroku supports C-style for loops, while loops, and Python-style iterator for loops.

for i = 1, i < 5, i = i + 1:
    print i
# → 1
#   2
#   3
#   4
let i = 36
while i > 1:
    i = i / 2
    print i
# → 18
#   9
#   4
#   2
#   1
let l = list()
l.append(1)
l.append(2)
l.append(3)
for i in l:
    print i
# → 1
#   2
#   3

Iterators

The special method __iter__ should return an iterator. An iterator should be a function which increments an internal state and returns the next value. If there are no values remaining, return the iterator itself.

An example of an iterator is the range built-in class, which is defined like this:

class range:
    def __init__(self, min, max):
        self.min = min
        self.max = max
    def __iter__(self):
        let me = self
        def makeIter(ind):
            let l = me
            let i = ind
            def iter():
                if i >= l.max:
                    return iter
                let out = i
                i = i + 1
                return out
            return iter
        return makeIter(self.min)

Objects which have an __iter__ method can then be used with the for ... in ... syntax:

for i in range(1,5):
    print i
# → 1
#   2
#   3
#   4

Indexing

Objects with the methods __get__ and __set__ can be used with square brackets []:

class Foo:
    def __get__(ind):
        print "You asked for ind=" + ind
        return ind * 5
    def __set__(ind, val):
        print "You asked to set ind=" + ind + " to " + val
let f = Foo()
print f[4]
f[7] = "bar"
# → You asked for ind=4
#   20
#   You asked to set ind=7 to bar

String Conversion

If an object implements the __str__ method, it will be called to produce string values when concatenating or printing.

class Foo:
    def __str__():
        return "(I am a Foo!)"
let f = Foo()
print f
# → (I am a Foo!)