KimL is an esoteric programming language consisting of invocations on pre-defined pseudo-objects. KimL was created by User:Alexanderdna, who created other languages such as ILYC and TS#.
The current implementation of KimL is written in C++ as both a compiler and a virtual machine that executes the compiled bytecode.
KimL is a case-sensitive language.
- 1 History
- 2 Structure
- 3 Objects
- 3.1 io
- 3.2 var
- 3.3 stack
- 3.4 tape
- 3.5 ctrl
- 4 Expressions
- 5 Examples
- 6 The KimL calling convention
- 7 External resources
KimL was firstly developed and implemented in 2009. As the project was started in December of 2009, the first language specification is Specification 09.12 and the interpreter was written using Visual Basic .NET.
In the later year, another version called Specification 10.07 was implemented in Visual C# in July 2010.
The third version of KimL, born in January 2011 and called Specification 11.01, is the last version till now and is described in this document.
There are three data types in KimL, which are int, real and string. int is 32-bit integer. real is 64-bit floating-point number. string is array of 8-bit character or the std::string type in the C++ Standard Template Library.
Values of type int can be converted to type real or string. Values of type real can be converted to type int or string. Values of type string generally cannot be converted to int or real. However, in some situations, string to numeric conversions are allowed and the runtime will try to parse the string for a valid value or choose 0 if it fails.
A KimL program has an internal stack call the k-stack. This is a supposedly-unbound stack that can store objects of any type.
Besides, there is an array of 128 objects called the tape. The tape also accepts any of the three data types. A tape pointer exists to point to the current item on the tape.
There are pseudo-objects taking care of different tasks in the program. Each object has a set of methods corresponding to the works it represents. Some methods have arguments, some do not.
As the name suggests, io is the object performing input and output tasks. It has two methods, which are in and out.
This method takes data from a text line the user has entered.
One version of io.in uses a type keyword as argument. It then converts the input data to the specified type and pushes it to the k-stack.
io.in int io.in real io.in string
Another version uses a variable name as argument. The input data is converted to the variable’s type and stored in that variable.
io.in a ;where 'a' is a variable
Note: if the type conversion fails, the default value is used.
This method uses an expression a argument, converts the expression result to string (if needed) and prints that on the screen.
io.out "Hello World!"
The var object is used to work on variables.
This method helps declaring a variable for use in the program. It uses a type keyword and a variable name as arguments. The variable should not be declared before unless it has been deleted by var.del (discussed later).
var.decl int n var.decl real r var.decl string s
The variable can have an initial value as showed below:
var.decl int n = 10 var.decl string s = "a simple stupid string" var.decl real r = n * 3.14
The initial value's type should be convertible to the variable type, otherwise an error will occur in compile time or runtime depending on the nature of the expression.
This method deletes an existing variable. If the variable is not declared before, a compile error will occur.
var.del n ;suppose that 'n' exist
Variable declaration and deletion in a KimL program are checked sequentially at compile time. That means, a variable is considered to exist if a former line of code has declared it, and to no longer exist if a former line of code has deleted it. Control flows are not included in the check.
Due to this characteristic of the compiler, if the program, in runtime, jumps to a position where a variable has not been declared and references or deletes it, an error or undefined behaviour will occur.
Programmers are recommended not to have a branching between the declaration and deletion of a variable.
This method assigns a value to a variable.
var.set i = i + 1
A compile error will occur if the variable does not exist.
The stack object have methods to operate on the k-stack.
This method pushes a value to the k-stack.
stack.push n + r / 2
This method pops the top value off the k-stack. If the stack is empty, a runtime error will occur.
If given a variable as argument, this method stores the popped value in the variable.
stack.pop n ;n should exist
This method stores the top value on the stack in the given variable. A runtime error will occur if the stack is empty.
stack.peek n ;n should exist
This method swaps the top two values on the stack. A runtime error will occur if the stack has less than two values.
This method clears the whole stack.
If given a constant integer number as argument, this method clears that number of values on the stack. A runtime error will occur if the stack has less values than specified.
The tape object operates on the program tape and tape pointer.
This method reads the current value on the tape, pointed to by the tape pointer. The read value is then stored in a variable or pushed on the k-stack. Like io.in, the argument is a variable or a type keyword (for pushing on the stack).
tape.read a ;a should exist tape.read string
If the value cannot be converted to the specified type, a runtime error will occur.
If an 'at index' phrase is provided, the value at that index will be read. The index should be a constant integer number in the range from 0 through 127.
tape.read int at 3 tape.read n at 127
This method writes the given value to the current object on the tape, pointed to by the tape pointer. If an index is provided, the value is written to the object at that position.
tape.write 10 ;write to current tape.write a * b + c at 0
This method moves the tape pointer one position forward. If the tape pointer is currently 127, it will be set to 0.
This method moves the tape pointer one position backward. If the tape pointer is currently 0, it will be set to 127.
This method uses an expression as argument. The value of the expression will be converted to integer if needed and assigned to the tape pointer.
If the type conversion fails or the value is out of range (0 to 127), a runtime error will occur.
tape.move i + 3 * j
The ctrl object provides control flow operations.
This method move the program counter to a defined label.
ctrl.goto L1 ;... L1: ;...
If a condition phrase is provided, the branching is done only if the specified condition is met.
ctrl.goto L1 if a > 0
This method works like ctrl.goto but it stores the current program counter in a call stack before branching.
A condition is also allowed.
ctrl.call proc1 if name = "john" and age > 15
This method pops a value from the call stack and sets it to the current program counter. If the call stack is empty, it will set 0 to the program counter.
This method is, by convention, used with ctrl.call.
This method halts the execution of the program.
Valid int literals are 0, 10, 300, 1000000, etc.
Valid real literals are 0.0, 1.23, 0.245, etc.
String literals are enclosed by double-quotes. Allowed escape sequences are \n, \r, \t, \\, \".
The following list are allowed operators in KimL, in descending order of priority.
- # converts to real, @ converts to int
- ^ exponentiation
- unary + and -, not (logic)
- multiplication, / division, \ integral division
- + addition, - subtraction
- & string concatenation
- < less than, <= less than or equal, > greater than, >= greater than or equal
- = equal, <> not equal
- xor (logic)
- and (logic)
- or (logic)
The # and @ conversion operators have a function syntax, that is, they require the converted expression be enclosed in parentheses.
KimL provides a set of built-in functions as listed below:
- abs(numeric): returns the absolute value of a number, the return type depends on the argument type.
- real sqrt(numeric): returns the square root of a number.
- real sin(numeric): returns the sine of an angle (in radians).
- real cos(numeric): returns the cosine of an angle.
- real tan(numeric): returns the tangent of an angle.
- real asin(numeric): returns the arc-sine of a number.
- real acos(numeric): returns the arc-cosine of a number.
- real atan(numeric): returns the arc-tangent of a number.
- string chr(numeric): returns a character from the given number(mod 256).
- int asc(string s): returns the ASCII value of the first character in the given string or 0 if the string is empty.
- int len(string s): returns the length of the given string.
- string left(string s, int n): returns the leading n characters of the given string, or the string itself if n is bigger than its length.
- string mid(string s, int i, int n): returns n characters of the given string, starting from i; or the string itself if i or i+n is bigger than its length.
- string right(string s, int n): returns the trailing n characters of the given string, or the string itself if n is bigger than its length.
- iif(cond, t, f): returns t if cond is true, otherwise f; the return type depends on the type of t or f.
- _tape(int idx): returns a value on the tape, pointed to by idx.
- _stack(int offset): returns a value on the stack, at the given offset from the top of the stack. _stack(1) returns the top value, _stack(2) returns the next to top value, and so on.
- _pop(): pops and returns the top value on the stack.
- _peek(): returns the top value on the stack.
io.out "Hello World!\n"
var.decl int f io.out "n = " io.in int ctrl.call fact var.set f = _tape(0) io.out "n! = " & f ctrl.end ; the following factorial function is recursive fact: ctrl.goto fact_0 if _peek() < 0 ctrl.goto fact_1 if _peek() <= 1 stack.push _peek() - 1 ctrl.call fact tape.write _pop() * _tape(0) at 0 ctrl.return fact_0: stack.pop tape.write 0 at 0 ctrl.return fact_1: stack.pop tape.write 1 at 0 ctrl.return
99 bottles of beer
var.decl int bot = 99 loop: io.out bot & " bottles of beer on the wall.\n" io.out bot & " bottles of beer.\n" var.set bot = bot - 1 ctrl.goto loop if bot > 0 io.out "No bottle of beer on the wall.\nNo bottle of beer.\n" io.out "Go to the store. Buy some more.\n" io.in string
The KimL calling convention
Below is the standard calling convention in KimL:
- Arguments are pushed on the stack, in right-to-left order.
- Return value is stored on the tape at 0.
- The callee is responsible for clearing the stack.
The KimL compiler/virtual machine is written in C++ and distributed under the MIT license. The source code is successfully compiled on Microsoft Windows XP and Ubuntu Linux using Microsoft Visual C++ 2008 Express and GCC 3.4.2.
Please visit http://www.sourceforge.net/projects/kiml to download the package.