2022-05-07 13:48:06 +08:00
|
|
|
# Language Manual
|
|
|
|
|
|
|
|
You can consider pocketlang's syntax as python without indentation. For block statements it uses the ruby way, 'end' keyword
|
|
|
|
to close a block, end a class, function etc. Most of the semantics are simillar to python and if you're know python it fairly
|
|
|
|
easier to grasp pocketlang.
|
|
|
|
|
|
|
|
## Hello World
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
print('Hello World')
|
|
|
|
```
|
|
|
|
|
|
|
|
## Comments and White Spaces
|
|
|
|
|
|
|
|
Comments are marked with `#` and ends at the next new line character.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
# Beautiful is better than ugly.
|
|
|
|
# Explicit is better than implicit.
|
|
|
|
# Simple is better than complex.
|
|
|
|
# Complex is better than complicated.
|
|
|
|
```
|
|
|
|
|
|
|
|
Except for new lines all the white spaces are ignored. New lines are used to end a statement, every statement should
|
|
|
|
ends with a new line or semicollon.
|
|
|
|
|
|
|
|
## Code blocks
|
|
|
|
|
|
|
|
Code blocks are following the same rule of ruby. All the blocks are closed with the `end` keyword,
|
|
|
|
all blocks are stards with either a new line and an optional "block entering keyword". For a single line
|
|
|
|
block these keywords are must. `if` blocks starts with the `then`, for `while` and `for` loops they starts
|
|
|
|
with the `do` keyword.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
## The `do` keyword is a must here.
|
|
|
|
while cond do something() end
|
|
|
|
|
|
|
|
## The `do` keyword is a optional here.
|
|
|
|
for i in 0..10
|
|
|
|
print('$i')
|
|
|
|
end
|
|
|
|
|
|
|
|
## `then` is optional if new line is present.
|
|
|
|
if cond1 then
|
|
|
|
foo()
|
|
|
|
else if cond2
|
|
|
|
bar()
|
|
|
|
else
|
|
|
|
baz()
|
|
|
|
end
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
# Data Types
|
|
|
|
|
|
|
|
Pocketlang has 3 primitive types which are `null`, `boolean` and `number` all the number values are represented as
|
|
|
|
IEEE 754 double precision floats. `null`, `true`, `false` are their literals and number support binary, hex and scientific
|
|
|
|
literals `0b101001`, `0xc0ffee`, `3e8`.
|
|
|
|
|
|
|
|
There are a handfull number of reference types like String, Range, List, Map, Closure (which is the first class functions),
|
|
|
|
Fiber, etc. And you can define you own with the `class` keyword.
|
|
|
|
|
|
|
|
## String
|
|
|
|
|
|
|
|
String sin pocketlang can be either double quoted or single quoted. At the moment pocket lang only supported a limited
|
|
|
|
number of string escape characters and UTF8 is not supported. However it'll be implemented before the first release.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
"Hello there!"; 'I\'m a string.'
|
|
|
|
|
|
|
|
"I'm a multi
|
|
|
|
line string."
|
|
|
|
```
|
|
|
|
|
|
|
|
Additionally pocketlang strings by default support interpolation with the `$` symbol. For a single identifier
|
|
|
|
it can be just written without any enclosing brackets, and for a long expressions they should be inside curly
|
|
|
|
brackets. And they can be nested upto 8 depth.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
n = 25
|
|
|
|
print('sqrt($n) = ${sqrt(n)}')
|
|
|
|
```
|
|
|
|
|
|
|
|
## Range
|
|
|
|
|
|
|
|
A range value represent an interval, they can be created with the `..` operator between two whole numbers.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
for i in 0..10
|
|
|
|
print(i)
|
|
|
|
end
|
|
|
|
```
|
|
|
|
|
|
|
|
!> Unlike ruby we don't have a `...` operator and pocketlang Range objects are exclusive.
|
|
|
|
|
|
|
|
Exclusive ranges are more natural and more usefull when it comes to iterate over the length of something, thus
|
|
|
|
pocketlang's ranges are always exclusive. And having two operator for the same but slightly different operation is
|
|
|
|
a bit redundant in our humble openion.
|
|
|
|
|
|
|
|
## List
|
|
|
|
|
|
|
|
Lists are a collection of ordered objects. Each element can be indexed starting at 0. Internally a list is a
|
|
|
|
dynamically allocated variables buffer they'll grow or shrink as needed to store them.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
[ 'apples', true, 0..5 ]
|
|
|
|
|
|
|
|
[
|
|
|
|
[ 1, 2, 3],
|
|
|
|
[ 4, 5, 6],
|
|
|
|
[ 7, 8, 9],
|
|
|
|
]
|
|
|
|
```
|
|
|
|
|
|
|
|
## Map
|
|
|
|
|
|
|
|
Maps are a collection of un ordered objects that stores the mapping of unique keys to the values. Internally it's
|
|
|
|
implemented as a has table and it require a value to be hashable for it to be a key. In pocketlang only primitive
|
|
|
|
types and immutable objects (`String` and `Range`) are hashable.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
{
|
|
|
|
12 : 34,
|
|
|
|
0..3 : [0, 1, 2],
|
|
|
|
'foo' : 'bar',
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
## Closure
|
|
|
|
|
|
|
|
Closure are the first class citizen of functions that can be created, assigned to variables, passed as an argument,
|
|
|
|
or returned from a function call. A closure can be created with the `fn` keyword, which will crate an
|
|
|
|
anonymous function and wraps itself around it.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
y = fn (x) return x * x end
|
|
|
|
```
|
|
|
|
|
|
|
|
Pocketlang support lexical scoping which allows the closure to capture an external local variable
|
|
|
|
out of it's own scope. They're more like lambda expressions in other langauges.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
def make_function(m, c)
|
|
|
|
return fn(x)
|
|
|
|
return m * x + c
|
|
|
|
end
|
|
|
|
end
|
|
|
|
```
|
|
|
|
|
|
|
|
?> Note that a call of form `f(fn ... end)` is a syntax sugar for `f fn ... end` where `f` is a function
|
|
|
|
or a method that takes a single literal function as argument, like Lua.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
5.times fn(i)
|
|
|
|
print(i)
|
|
|
|
end
|
|
|
|
```
|
|
|
|
|
|
|
|
## Fiber
|
|
|
|
|
|
|
|
Pocketlang support coroutines via [fibers](https://en.wikipedia.org/wiki/Fiber_(computer_science))
|
|
|
|
(light weight threads with cooperative multitask). A fiber object is a wrapper around a closure, contains
|
|
|
|
the execution state (simply the stack and the instruction pointer) of that closure, which can be run
|
|
|
|
and once yielded resumed.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
def foo()
|
|
|
|
print("Hello from fiber!")
|
|
|
|
end
|
|
|
|
|
|
|
|
fb = Fiber(foo)
|
|
|
|
fb.run()
|
|
|
|
```
|
|
|
|
|
|
|
|
When a function is yielded, it's state will be stored in the fiber it's belongs to and will return from
|
|
|
|
the function, to parent fiber it's running from (not the caller of the function). You can pass values
|
|
|
|
between fibers when they yield.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
def foo()
|
|
|
|
print('running')
|
|
|
|
yield()
|
|
|
|
print('resumed')
|
|
|
|
end
|
|
|
|
|
|
|
|
fb = Fiber(foo)
|
|
|
|
fb.run() # Prints 'running'.
|
|
|
|
print('before resumed')
|
|
|
|
fb.resume() # Prints 'resumed'.
|
|
|
|
```
|
|
|
|
|
|
|
|
Yield from the fiber with a value.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
def foo()
|
|
|
|
print('running')
|
|
|
|
yield(42) # Return 42.
|
|
|
|
print('resumed')
|
|
|
|
end
|
|
|
|
|
|
|
|
fb = Fiber(foo)
|
|
|
|
val = fb.run() # Prints 'running'.
|
|
|
|
print(val) # Prints 42.
|
|
|
|
fb.resume() # Prints 'resumed'.
|
|
|
|
```
|
|
|
|
|
|
|
|
Resume the fiber with a value.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
def foo()
|
|
|
|
print('running')
|
|
|
|
val = yield() # Resumed value.
|
|
|
|
print(val) # Prints 42.
|
|
|
|
print('resumed')
|
|
|
|
end
|
|
|
|
|
|
|
|
fb = Fiber(foo)
|
|
|
|
val = fb.run() # Prints 'running'.
|
|
|
|
fb.resume(42) # Resume with 42, Prints 'resumed'.
|
|
|
|
```
|
|
|
|
|
|
|
|
Once a fiber is done execution, trying to resume it will cause a runtime error. To check if the
|
|
|
|
fiber is finished check its attribute `is_done` and use the attribute `function` to get it's function,
|
|
|
|
which could be used to create a new fiber to "re-start" the fiber.
|
|
|
|
|
|
|
|
```ruby
|
2022-05-08 18:02:51 +08:00
|
|
|
fb = Fiber fn()
|
2022-05-07 13:48:06 +08:00
|
|
|
for i in 0..5
|
|
|
|
yield(i)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
fb.run()
|
|
|
|
|
|
|
|
while not fb.is_done
|
|
|
|
fb.resume()
|
|
|
|
end
|
|
|
|
|
|
|
|
# Get the function from the fiber.
|
|
|
|
f = fb.get_func()
|
|
|
|
```
|
|
|
|
|
|
|
|
## Classes
|
|
|
|
|
|
|
|
Classes are the blueprint of objects, contains method definitions and behaviors for its instances.
|
|
|
|
The instance of a class method can be accessed with the `self` keyword.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
class Foo end
|
|
|
|
foo = Foo() ## Create a foo instance of Foo class.
|
|
|
|
```
|
|
|
|
|
|
|
|
To initialize an instance when it's constructed use `_init` method. Pocketlang instance attributes
|
|
|
|
are dynamic (means you can add a new field to an instance on the fly).
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
class Foo
|
|
|
|
def _init(bar, baz)
|
|
|
|
self.bar = bar
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
foo = Foo('bar', 'baz')
|
|
|
|
```
|
|
|
|
|
|
|
|
To override an operator just use the operator symbol as the method name.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
class Vec2
|
|
|
|
def _init(x, y)
|
|
|
|
self.x = x; self.y = y
|
|
|
|
end
|
|
|
|
def _str
|
|
|
|
return "<${self.x}, ${self.y}>"
|
|
|
|
end
|
|
|
|
def + (other)
|
|
|
|
return Vec2(self.x + other.x,
|
|
|
|
self.y + other.y)
|
|
|
|
end
|
|
|
|
def += (other)
|
|
|
|
self.x += other.x
|
|
|
|
self.y += other.y
|
|
|
|
return self
|
|
|
|
end
|
|
|
|
def == (other)
|
|
|
|
return self.x == other.x and self.y == other.y
|
|
|
|
end
|
|
|
|
end
|
|
|
|
```
|
|
|
|
|
|
|
|
To distinguish unary operator with binary operator the `self` keyword should be used.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
class N
|
|
|
|
def _init(n)
|
|
|
|
self.n = n
|
|
|
|
end
|
|
|
|
|
|
|
|
def - (other) ## N(1) - N(2)
|
|
|
|
return N(self.n - other.n)
|
|
|
|
end
|
|
|
|
|
|
|
|
def -self () ## -N(1)
|
|
|
|
return N(-self.n)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
```
|
|
|
|
|
|
|
|
All classes are ultimately inherit an abstract class named `Object` to inherit from any other class
|
|
|
|
use `is` keyword at the class definition. However you cannot inherit from the builtin class like
|
|
|
|
Number, Boolean, Null, String, List, ...
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
class Shape # Implicitly inherit Object class
|
|
|
|
end
|
|
|
|
|
|
|
|
class Circle is Shape # Inherits the Shape class
|
|
|
|
end
|
|
|
|
```
|
|
|
|
|
|
|
|
To override a method just redefine the method in a subclass.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
class Shape
|
|
|
|
def area()
|
|
|
|
assert(false)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class Circle is Shape
|
|
|
|
def _init(r)
|
|
|
|
self.r = r
|
|
|
|
end
|
|
|
|
def area()
|
|
|
|
return math.PI * r ** 2
|
|
|
|
end
|
|
|
|
end
|
|
|
|
```
|
|
|
|
|
|
|
|
To call the a method on the super class use `super` keyword. If the method name is same as the current
|
|
|
|
method `super()` will do otherwise method name should be specified `super.method_name()`.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
class Rectangle is Shape
|
|
|
|
def _init(w, h)
|
|
|
|
self.w = w; self.h = h
|
|
|
|
end
|
|
|
|
def scale(fx, fy)
|
|
|
|
self.w *= fx
|
|
|
|
self.h *= fy
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class Square is Rectangle
|
|
|
|
def _init(s)
|
|
|
|
super(s, s) ## Calls super._init(s, s)
|
|
|
|
end
|
|
|
|
|
|
|
|
def scale(x)
|
|
|
|
super(x, x)
|
|
|
|
end
|
|
|
|
|
|
|
|
def scale2(x, y)
|
|
|
|
super.scale(x, y)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
```
|
|
|
|
|
|
|
|
## Importing Modules
|
|
|
|
|
|
|
|
A module is a collection of functions classes and global variables. Usually a single script file
|
|
|
|
will be compiled to a module. To import a module use `import` statement.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
import math # Import math module.
|
|
|
|
import lang, math # Import multiple modules.
|
|
|
|
from math import sin, tan # Import symbols from a module.
|
|
|
|
|
|
|
|
# Using alias to bind with a different name.
|
|
|
|
import math as foo
|
|
|
|
from lang import clock as now
|
|
|
|
|
|
|
|
# Import inside a directory
|
|
|
|
import foo.bar # Import module bar from foo directory
|
|
|
|
import baz # If baz is a directory it'll import baz/_init.pk
|
|
|
|
|
|
|
|
# I'll only search for foo relatievly.
|
|
|
|
import .foo # ./foo.pk or ./foo/_init.pk or ./foo.dll, ...
|
|
|
|
|
|
|
|
# ^ meaning parent directory relative to this script.
|
|
|
|
import ^^foo.bar.baz # ../../foo/bar/baz.pk
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
?> Note that star import `from foo import *` is not supported but may be in the future.
|