@ -23,16 +23,16 @@ The project was kicked off in January 2015.
Right now (as of Feb 2015) I am working on the JIT implementation. Please see `Ravi Documentation <http://the-ravi-programming-language.readthedocs.org/en/latest/index.html>`_ for details of this effort.
As of end Jan 2015, the Ravi interpreter allows you to declare local variables as ``int`` or ``double``. This triggers following behaviour:
As of end Jan 2015, the Ravi interpreter allows you to declare local variables as ``integer`` or ``number``. This triggers following behaviour:
* ``int`` and ``double`` variables are initialized to 0
* ``integer`` and ``number`` variables are initialized to 0
* arithmetic operations trigger type specific bytecodes
* values assigned to these variables are checked statically unless the values are results from a function call in which case the there is an attempt to convert values at runtime.
Also initial implementation of arrays is available. So you can declare arrays of integers or doubles.
* The type of an array of integers is denoted as ``int[]``.
* The type of an array of doubles is denoted as ``double[]``.
* The type of an array of integers is denoted as ``integer[]``.
* The type of an array of doubles is denoted as ``number[]``.
* Arrays are implemented using a mix of runtime and compile time checks.
* Specialised operators to get/set from arrays are implemented.
* The standard table operations on arrays are checked to ensure that the type is not subverted.
@ -45,12 +45,12 @@ Example of code that works - you can copy this to the command line input::
local i,j = 5,6
return i,j
end
local i:int, j:int = tryme(); print(i+j)
local i:integer, j:integer = tryme(); print(i+j)
Another::
function tryme()
local j:double
local j:number
for i=1,1000000000 do
j = j+1
end
@ -61,7 +61,7 @@ Another::
An example with arrays::
function tryme()
local a : double[], j:double = {}
local a : number[], j:number = {}
for i=1,10 do
a[i] = i
j = j + a[i]
@ -154,8 +154,8 @@ I hope to enhance the language to variables to be optionally decorated with type
So as of now the only types that seem worth specializing are:
* int (64-bit)
* double
* integer (64-bit)
* number
* array of ints
* array of doubles
@ -190,11 +190,11 @@ If no type is specified then then type will be dynamic - exactly what the Lua de
When a typed function is called the inputs and return value can be validated. Consider the function below::
local function foo(a, b: int, c: string)
local function foo(a, b: integer, c: string)
return
end
When this function is called the compiler can validate that ``b`` is an int and ``c`` is a string. ``a`` on the other hand is dynamic so will behave as regular Lua value. The compiler can also ensure that the types of ``b`` and ``c`` are respected within the function.
When this function is called the compiler can validate that ``b`` is an integer and ``c`` is a string. ``a`` on the other hand is dynamic so will behave as regular Lua value. The compiler can also ensure that the types of ``b`` and ``c`` are respected within the function.
Return statements in typed functions can also be validated.
@ -203,12 +203,12 @@ Array Types
When it comes to complex types such as arrays, tables and functions, at this point in time, I think that Ravi only needs to support explicit specialization for arrays of integers and doubles::
function foo(p1: {}, p2: int[])
function foo(p1: {}, p2: integer[])
-- p1 is a table
-- p2 is an array of integers
local t1 = {} -- t1 is a table
local a1 : int[] = {} -- a1 is an array of integers, specialization of table
local d1 : double[] = {} -- d1 is an array of doubles, specialization of table
local a1 : integer[] = {} -- a1 is an array of integers, specialization of table
local d1 : number[] = {} -- d1 is an array of doubles, specialization of table
end
@ -251,7 +251,7 @@ For now however I am just amending the bit mapping in the 32-bit instruction to
New OpCodes
-----------
The new instructions are specialised for types, and also for register/versus constant. So for example ``OP_RAVI_ADDFI`` means add ``float`` and ``int``. And ``OP_RAVI_ADDFF`` means add ``float`` and ``float``. The existing Lua opcodes that these are based on define which operands are used.
The new instructions are specialised for types, and also for register/versus constant. So for example ``OP_RAVI_ADDFI`` means add ``float`` and ``integer``. And ``OP_RAVI_ADDFF`` means add ``float`` and ``float``. The existing Lua opcodes that these are based on define which operands are used.
Example::
@ -265,7 +265,7 @@ Above standard Lua code compiles to::
@ -112,9 +112,9 @@ The entry point for parsing a local statement is ``localstat()`` in ``lparser.c`
if (testnext(ls, ':')) {
TString *typename = str_checkname(ls); /* we expect a type name */
const char *str = getaddrstr(typename);
if (strcmp(str, "int") == 0)
if (strcmp(str, "integer") == 0)
tt = RAVI_TNUMINT;
else if (strcmp(str, "double") == 0)
else if (strcmp(str, "number") == 0)
tt = RAVI_TNUMFLT;
if (tt == RAVI_TNUMFLT || tt == RAVI_TNUMINT) {
if (testnext(ls, '[')) {
@ -220,7 +220,7 @@ The main changes compared to ``explist()`` are the calls to ``ravi_typecheck()``
* We look for the last bytecode that is OP_NEWTABLE
* and that has the same destination
* register as v->u.info which is our variable
* local a:int[] = { 1 }
* local a:integer[] = { 1 }
* ^ We are just past this and
* about to assign to a
*/
@ -298,13 +298,13 @@ There are several parts to this function.
The simple case is when the type of the expression matches the variable.
Secondly if the expression is a table initializer then we need to generate specialized opcodes if the target variable is supposed to be ``int[]`` or ``double[]``. The specialized opcode sets up some information in the ``Table`` structure. The problem is that this requires us to modify ``OP_NEWTABLE`` instruction which has already been emitted. So we scan the generated instructions to find the last ``OP_NEWTABLE`` instruction that assigns to the register associated with the target variable.
Secondly if the expression is a table initializer then we need to generate specialized opcodes if the target variable is supposed to be ``integer[]`` or ``number[]``. The specialized opcode sets up some information in the ``Table`` structure. The problem is that this requires us to modify ``OP_NEWTABLE`` instruction which has already been emitted. So we scan the generated instructions to find the last ``OP_NEWTABLE`` instruction that assigns to the register associated with the target variable.
Next bit of special handling is for function calls. If the assignment makes a function call then we perform type coercion on return values where these values are being assigned to variables with defined types. This means that if the target variable is ``int`` or ``double`` we issue opcodes ``TOINT`` and ``TOFLT`` respectively. If the target variable is ``int[]`` or ``double[]`` then we issue ``TOARRAYI`` and ``TOARRAYF`` respectively. These opcodes ensure that the values are of required type or can be cast to the required type.
Next bit of special handling is for function calls. If the assignment makes a function call then we perform type coercion on return values where these values are being assigned to variables with defined types. This means that if the target variable is ``integer`` or ``number`` we issue opcodes ``TOINT`` and ``TOFLT`` respectively. If the target variable is ``integer[]`` or ``number[]`` then we issue ``TOARRAYI`` and ``TOARRAYF`` respectively. These opcodes ensure that the values are of required type or can be cast to the required type.
Note that any left over variables that are not assigned values, are set to 0 if they are of int or double type, else they are set to nil as per Lua's default behavior. This is handled in ``localvar_adjust_assign()`` which is described later on.
Note that any left over variables that are not assigned values, are set to 0 if they are of integer or number type, else they are set to nil as per Lua's default behavior. This is handled in ``localvar_adjust_assign()`` which is described later on.
Finally the last case is when the target variable is ``int`` or ``double`` and the expression is a table / array access. In this case we check that the table is of required type.
Finally the last case is when the target variable is ``integer`` or ``number`` and the expression is a table / array access. In this case we check that the table is of required type.
The ``localvar_adjust_assign()`` function referred to above is shown below.
@ -725,7 +725,7 @@ The Lua fornum statements create special variables. In order to allows the loop
/* The fornum sets up its own variables as above.
These are expected to hold numeric values - but from Ravi's
point of view we need to know if the variable is an integer or
double. So we need to check if this can be determined from the
number. So we need to check if this can be determined from the
fornum expressions. If we can then we will set the
fornum variables to the type we discover.
*/
@ -753,7 +753,7 @@ The Lua fornum statements create special variables. In order to allows the loop
@ -23,16 +23,16 @@ The project was kicked off in January 2015.
Right now (as of Feb 2015) I am working on the JIT implementation. Please see `Ravi Documentation <http://the-ravi-programming-language.readthedocs.org/en/latest/index.html>`_ for details of this effort.
As of end Jan 2015, the Ravi interpreter allows you to declare local variables as ``int`` or ``double``. This triggers following behaviour:
As of end Jan 2015, the Ravi interpreter allows you to declare local variables as ``integer`` or ``number``. This triggers following behaviour:
* ``int`` and ``double`` variables are initialized to 0
* ``integer`` and ``number`` variables are initialized to 0
* arithmetic operations trigger type specific bytecodes
* values assigned to these variables are checked statically unless the values are results from a function call in which case the there is an attempt to convert values at runtime.
Also initial implementation of arrays is available. So you can declare arrays of integers or doubles.
* The type of an array of integers is denoted as ``int[]``.
* The type of an array of doubles is denoted as ``double[]``.
* The type of an array of integers is denoted as ``integer[]``.
* The type of an array of doubles is denoted as ``number[]``.
* Arrays are implemented using a mix of runtime and compile time checks.
* Specialised operators to get/set from arrays are implemented.
* The standard table operations on arrays are checked to ensure that the type is not subverted.
@ -45,12 +45,12 @@ Example of code that works - you can copy this to the command line input::
local i,j = 5,6
return i,j
end
local i:int, j:int = tryme(); print(i+j)
local i:integer, j:integer = tryme(); print(i+j)
Another::
function tryme()
local j:double
local j:number
for i=1,1000000000 do
j = j+1
end
@ -61,7 +61,7 @@ Another::
An example with arrays::
function tryme()
local a : double[], j:double = {}
local a : number[], j:number = {}
for i=1,10 do
a[i] = i
j = j + a[i]
@ -154,8 +154,8 @@ I hope to enhance the language to variables to be optionally decorated with type
So as of now the only types that seem worth specializing are:
* int (64-bit)
* double
* integer (64-bit)
* number
* array of ints
* array of doubles
@ -190,11 +190,11 @@ If no type is specified then then type will be dynamic - exactly what the Lua de
When a typed function is called the inputs and return value can be validated. Consider the function below::
local function foo(a, b: int, c: string)
local function foo(a, b: integer, c: string)
return
end
When this function is called the compiler can validate that ``b`` is an int and ``c`` is a string. ``a`` on the other hand is dynamic so will behave as regular Lua value. The compiler can also ensure that the types of ``b`` and ``c`` are respected within the function.
When this function is called the compiler can validate that ``b`` is an integer and ``c`` is a string. ``a`` on the other hand is dynamic so will behave as regular Lua value. The compiler can also ensure that the types of ``b`` and ``c`` are respected within the function.
Return statements in typed functions can also be validated.
@ -203,12 +203,12 @@ Array Types
When it comes to complex types such as arrays, tables and functions, at this point in time, I think that Ravi only needs to support explicit specialization for arrays of integers and doubles::
function foo(p1: {}, p2: int[])
function foo(p1: {}, p2: integer[])
-- p1 is a table
-- p2 is an array of integers
local t1 = {} -- t1 is a table
local a1 : int[] = {} -- a1 is an array of integers, specialization of table
local d1 : double[] = {} -- d1 is an array of doubles, specialization of table
local a1 : integer[] = {} -- a1 is an array of integers, specialization of table
local d1 : number[] = {} -- d1 is an array of doubles, specialization of table
end
@ -251,7 +251,7 @@ For now however I am just amending the bit mapping in the 32-bit instruction to
New OpCodes
-----------
The new instructions are specialised for types, and also for register/versus constant. So for example ``OP_RAVI_ADDFI`` means add ``float`` and ``int``. And ``OP_RAVI_ADDFF`` means add ``float`` and ``float``. The existing Lua opcodes that these are based on define which operands are used.
The new instructions are specialised for types, and also for register/versus constant. So for example ``OP_RAVI_ADDFI`` means add ``float`` and ``integer``. And ``OP_RAVI_ADDFF`` means add ``float`` and ``float``. The existing Lua opcodes that these are based on define which operands are used.
Example::
@ -265,7 +265,7 @@ Above standard Lua code compiles to::
@ -123,13 +123,13 @@ int main(int argc, const char *argv[])
intfailures=0;
//
failures+=test_luacompexec1("function z(x,y) return x<y end; ravi.compile(z); return not z(2,1)",1);
failures+=test_luacompexec1("local function x(); local d:double = 5.0; return d+5 == 5+d and d-5 == 5-d and d*5 == 5*d; end; local y = x(); return y",1);
failures+=test_luacompexec1("function x(f); local i : int, j : int = f(); return i + j; end; return ravi.compile(x)",1);
failures+=test_luacompexec1("local function x(); local d:number = 5.0; return d+5 == 5+d and d-5 == 5-d and d*5 == 5*d; end; local y = x(); return y",1);
failures+=test_luacompexec1("function x(f); local i : integer, j : integer = f(); return i + j; end; return ravi.compile(x)",1);
failures+=test_luacompexec1("local function z(a); print(a); return a+1; end; local function x(yy); local j = 5; j = yy(j); return j; end; local y = x(z); return y",6);
failures+=test_luacompexec1("local function z(a,p); p(a); return 6; end; local function x(yy,p); local j = 5; j = yy(j,p); return j; end; local y = x(z,print); return y",6);
failures+=test_luacompexec1("local function x(yy); local j = 5; yy(j); return j; end; local y = x(print); return y",5);
failures+=test_luacompexec1("local function x(); local i, j:int; j=0; for i=1,1000000000 do; j = j+1; end; return j; end; local y = x(); print(y); return y",1000000000);
failures+=test_luacompexec1("local function x(); local j:double; for i=1,1000000000 do; j = j+1; end; return j; end; local y = x(); print(y); return y",1000000000);
failures+=test_luacompexec1("local function x(); local i, j:integer; j=0; for i=1,1000000000 do; j = j+1; end; return j; end; local y = x(); print(y); return y",1000000000);
failures+=test_luacompexec1("local function x(); local j:number; for i=1,1000000000 do; j = j+1; end; return j; end; local y = x(); print(y); return y",1000000000);
failures+=test_luacompexec1("local function x(); local j = 0; for i=2,6,3 do; j = i; end; return j; end; local y = x(); print(y); return y",5);
failures+=test_luacompexec1("local function x(); local j = 0; for i=2.0,6.0,3.0 do; j = i; end; return j; end; local y = x(); print(y); return y",5);
@ -139,48 +139,48 @@ int main(int argc, const char *argv[])
failures+=test_luacompexec1("local function x(y); if y == 1 then; return 1.0; elseif y == 5 then; return 2.0; else; return 3.0; end; end; local z = x(4); print(z); return z",3);
failures+=test_luacompexec1("local function x(y,z); if y == 1 then; if z == 1 then; return 99.0; else; return z; end; elseif y == 5 then; return 2.0; else; return 3.0; end; end; local z = x(1,1); print(z); return z",99);
failures+=test_luacompexec1("local x:int[] = {1}; local i:int = 1; local d:int = x[i]; x[i] = 5; return d*x[i];",5);
failures+=test_luacompexec1("local function x(); local a:double = 1.0; return a+127 == 128.0; end; local y = x(); return y",1);
failures+=test_luacompexec1("local function x(); local a:double = 1.0; return a+128 == 129.0; end; local y = x(); return y",1);
failures+=test_luacompexec1("local function x(); local a:double = 1.0; return 127+a == 128.0; end; local y = x(); return y",1);
failures+=test_luacompexec1("local function x(); local a:double = 1.0; return 128+a == 129.0; end; local y = x(); return y",1);
failures+=test_luacompexec1("local function x(); local a:double = 1.0; return a+1.0 == 1.0+a; end; local y = x(); return y",1);
failures+=test_luacompexec1("local function x(); local a:int = 1; return a+127 == 128; end; local y = x(); return y",1);
failures+=test_luacompexec1("local function x(); local a:int = 1; return a+128 == 129; end; local y = x(); return y",1);
failures+=test_luacompexec1("local function x(); local a:int = 1; return 127+a == 128; end; local y = x(); return y",1);
failures+=test_luacompexec1("local function x(); local a:int = 1; return 128+a == 129; end; local y = x(); return y",1);
failures+=test_luacompexec1("local function x(); local a:int = 1; return a+1 == 1+a; end; local y = x(); return y",1);
failures+=test_luacomp1("local t = {}; local da : double[] = {}; da=t[1];")==1?0:1;
failures+=test_luacompexec1("local function tryme(x); print(#x); return x; end; local da: double[] = { 5, 6 }; da[1] = 42; da = tryme(da); return da[1];",42);
/* following should fail as x is a double[] */
failures+=test_luacompexec1("local function tryme(x); print(#x); x[1] = 'junk'; return x; end; local da: double[] = {}; da[1] = 42; da = tryme(da); return da[1];",42)==1?0:1;
failures+=test_luacompexec1("local x:integer[] = {1}; local i:integer = 1; local d:integer = x[i]; x[i] = 5; return d*x[i];",5);
failures+=test_luacompexec1("local function x(); local a:number = 1.0; return a+127 == 128.0; end; local y = x(); return y",1);
failures+=test_luacompexec1("local function x(); local a:number = 1.0; return a+128 == 129.0; end; local y = x(); return y",1);
failures+=test_luacompexec1("local function x(); local a:number = 1.0; return 127+a == 128.0; end; local y = x(); return y",1);
failures+=test_luacompexec1("local function x(); local a:number = 1.0; return 128+a == 129.0; end; local y = x(); return y",1);
failures+=test_luacompexec1("local function x(); local a:number = 1.0; return a+1.0 == 1.0+a; end; local y = x(); return y",1);
failures+=test_luacompexec1("local function x(); local a:integer = 1; return a+127 == 128; end; local y = x(); return y",1);
failures+=test_luacompexec1("local function x(); local a:integer = 1; return a+128 == 129; end; local y = x(); return y",1);
failures+=test_luacompexec1("local function x(); local a:integer = 1; return 127+a == 128; end; local y = x(); return y",1);
failures+=test_luacompexec1("local function x(); local a:integer = 1; return 128+a == 129; end; local y = x(); return y",1);
failures+=test_luacompexec1("local function x(); local a:integer = 1; return a+1 == 1+a; end; local y = x(); return y",1);
failures+=test_luacomp1("local t = {}; local da : number[] = {}; da=t[1];")==1?0:1;
failures+=test_luacompexec1("local function tryme(x); print(#x); return x; end; local da: number[] = { 5, 6 }; da[1] = 42; da = tryme(da); return da[1];",42);
/* following should fail as x is a number[] */
failures+=test_luacompexec1("local function tryme(x); print(#x); x[1] = 'junk'; return x; end; local da: number[] = {}; da[1] = 42; da = tryme(da); return da[1];",42)==1?0:1;