==Domain specific languages== DSL allows to express various concepts in very lapidary and concise form. Xreate recognizes and acknowledges very successful and beneficial DSL usage in certain areas, primarily to express //queries// and //configs//, to name a few. Taking this into account Xreate offers DSL concept by employing //interpretation//. ==Interpretation== Interpretation stands for pre evaluation of program parts that are known at compile time. main= function ::int;entry { x= "a"::string. y= if (x=="b")::string; interpretation(force) {1} else {0}. y } In this example, identifier `y` has annotation `interpretation(force)` to enable compile-time interpretation for `y`. Consequenlty, function result is `0` with neither memory allocations for string variable `x` nor any computation at runtime. There are two annotations reserved to interfere with interpretation process: | `interpretation(force)` | Force compiler to apply interpretation for annotated expression, function, function argument| | `interpretation(suppress)` | Disable interpretation for annotated expression | ==Eligible Operations== | Atomic instructions| numbers, strings, identifiers | | Relational and logic operators | e.g. `x=="true"`, `x!=0`, operator `and` | | `if`, `switch` statements |[[#expansion|Statement expansion]] allowed| | loops statements | [[#expansion|Statement expansion]] allowed | | `call` statement | [[#function-call-interpreta|Function calls]], [[#partial-function|Partial function call interpretation]]| | index operator | e.g. `x= [1, 2, 3]. y= x[0]`, `info= {planet="Earth"}. x= info["planet"]`| | list operators | e.g. `x= [1..10]`, `x=["day", "night"]`| ==Expansion== Expansion stands for partial simplification or elimination of interpretable parts of certain statements. test = function(x:: int) :: int; entry { comm= "inc":: string; interpretation(force). y= if (comm == "inc") {x + 1} else {x} y } In this example, computation of `y` depends on `comm` and `x`. Annotated identifier `comm` should be interpretated but identifier `x` is unknown at compile-time. Thus, in order to satisfy opposite requierments //expansion// is enabled, i.e. due to possibility of condition interpretation, statement `if` is expanded to a just one of the child blocks, `x+1` in this example. Below more complex example of loop expansion: main = function(x:: int):: int; entry { commands = ["inc", "double", "dec"]:: [string]; interpretation(force). loop fold(commands->comm::string, x->operand):: int{ switch(comm):: int case ("inc"){ operand + 1 } case ("dec"){ operand - 1 } case ("double"){ operand * 2 } } } We force compiler to interpret variable `commands`. Consequenlty, compiler has no choice but to expand loop in order to get rid of `commands`. Result could be presented as: main = function(x:: int):: int; entry { x(1) = x + 1. x(2) = x(1) * 2. x(3) = x(2) - 1. x(3) } In other words, this mimics famous loop unrolling technique by putting several copies of the loop body in a row, each one for every item of a list of `commands`. IMPORTANT: As of now, expansion defined for `if`, `switch`, `loop fold` statements and for functions. ==Function call interpretation== Whole function body could be subject of interpretation. unwrap= function(data:: undef, keys:: undef):: undef; interpretation(force){ loop fold(keys->key:: string, data->a):: undef { a[key] } } start= function::num; entry{ result= unwrap( { a = { b = { c = "needle" } } }, ["a", "b", "c"])::undef. result == "needle" } Here, compiler is informed by respective annotation to interpret `unwrap`. Moreover, all arguments of `unwrap` are fully defined at compile-time and compiler simply replaces function call by calculated value. NOTE: In example, an arbitrary undefined type `undef` is used in order to be 100% sure that no compilation occurs, while interpretation pays no attention to a types for now. In simple cases interpretation analysis could determine that function is subject of interpretation on its own with no annotation hints provided. unwrap= function(data:: undef, keys:: undef):: undef { loop fold(keys->key:: string, data->a):: undef { a[key] } } start= function::num; entry{ result= unwrap( { a = { b = { c = "needle" } } }, ["a", "b", "c"])::undef; interpretation(force). result == "needle" } Only difference from example above is lack of annotation hint for `unwrap`. Developer requires interpretation of `result` and compiler accepts such code since analysis find out that `unwrap` is possible to interpret. There are, hovewer, more compilcated cases for analysis: |Direct recursion| Analysis is able to correctly find out whether function involving direct recursion(in which a function calls itself) is subject to interpretation | |Indirect recursion | As of now for processing of indirect recursion( in which a function is called not by itself but by another function) analysis rely on manually provided annotation hints | ==Partial function call interpretation== More compilcated case is when some of the function arguments are subject to interpretation and other to a compilation - //partial function interpretation//. evaluate= function(argument:: num, code:: string; interpretation(force)):: num { switch(code)::int case ("inc") {argument + 1} case ("dec") {argument - 1} case ("double") {argument * 2} case default {argument} } main = function:: int; entry { commands= ["inc", "double", "dec"]:: [string]; interpretation(force). loop fold(commands->comm::string, 10->operand):: int{ evaluate(operand, comm) } } Here function `evaluate` is subject to a partial interpretation. To enable partial interpretation for function some of its arguments should be annotated with `interpretation(force)`. Compiler finds all the distinctive interpretable arguments combinations the given function called with and produces function specializations, each for every finded distinctive arguments combination. For this example three different `evaluate` specializations are produced as below: evaluate1= function(argument:: num):: num { argument + 1 } evaluate2= function(argument:: num):: num { argument * 2 } evaluate3= function(argument:: num):: num { argument - 1 } main= function:: int; entry { operand= 10:: int. operand(1)= evaluate1(operand). operand(2)= evaluate2(operand(1)). operand(3)= evaluate3(operand(2)). operand(3) } TODO: find correct term in supercomilation for partial interpretation TODO: distinctive combinations w.r.t. equivalence relation TODO: mention oppostion between cloning/ additional arguments ==On interpretation analysis== Analysis follows classical type reconstruction algorithms to determine what expressions are subject to an interpretation and check correctness of reconstruction w.r.t. developer-provided annotations. Analysis consists of two general parts: | Inference | Infers is it possible to interpret expression based on known decisions for its arguments | | Unification | Assigns appropriate decision w.r.t. to an previously inferred expectations and developer-provided annotations |