function keyword changed back to fn
9.3 KiB
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
print('Hello World')
Comments and White Spaces
Comments are marked with #
and ends at the next new line character.
# 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.
## 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.
"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.
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.
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.
[ '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.
{
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.
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.
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.
5.times fn(i)
print(i)
end
Fiber
Pocketlang support coroutines via fibers (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.
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.
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.
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.
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.
fb = Fiber foo()
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.
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).
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.
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.
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, ...
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.
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()
.
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.
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.