kuroko/README.md

362 lines
8.0 KiB
Markdown
Raw Normal View History

2020-12-26 10:59:43 +03:00
# Kuroko - A bytecode-compiled scripting language
2020-12-29 09:39:34 +03:00
Kuroko is a bytecode-interpreted, dynamic, strongly-typed language with syntax similar to Python.
2020-12-28 10:08:06 +03:00
2020-12-29 09:39:34 +03:00
The bytecode VM / compiler is substantially based on Robert Nystrom's [_Crafting Interpreters_](https://craftinginterpreters.com/).
2020-12-26 10:59:43 +03:00
2020-12-29 09:39:34 +03:00
At the moment, the intent for this project is to add a proper scripting language to [Bim](https://github.com/klange/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](https://github.com/klange/toaruos) as a general-purpose user language, and some utilities may end up being written in it.
## Features
2020-12-29 09:39:34 +03:00
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:
```py
print "Hello, world!"
# → Hello, world!
```
### Basic Integer Operations
```py
print 1 + 2 + 3
# → 6
```
### String Concatenation
Strings can be concatenated, and other values can be appended to them.
```py
print "Hello, " + 42 + "!"
# → Hello, 42!
```
### Functions
Function syntax is essentially the same as in Python:
```py
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:
```py
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:
```py
let o = object()
o.foo = "bar"
print o.foo
# → bar
```
To supply methods, define a class:
```py
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:
```py
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.
```py
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`):
```py
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.
```py
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`:
```py
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.
```py
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:
```py
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.
```py
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.
```py
# modules/demomodule.krk
let module = object()
module.foo = "bar"
return module
```
```py
# 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:
```py
# modules/demomodule.krk
let module = object()
foo = "bar"
export foo
return module
```
```py
# demo.krk
import demomodule
print foo
# → bar
```
### Loops
Kuroku supports C-style for loops, while loops, and Python-style iterator for loops.
```py
for i = 1, i < 5, i = i + 1:
print i
# → 1
# 2
# 3
# 4
```
```py
let i = 36
while i > 1:
i = i / 2
print i
# → 18
# 9
# 4
# 2
# 1
```
```py
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:
```py
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:
```py
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 `[]`:
```py
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.
```py
class Foo:
def __str__():
return "(I am a Foo!)"
let f = Foo()
print f
# → (I am a Foo!)
```