diff --git a/documentation/Concepts/interpretation.xml b/documentation/Concepts/interpretation.xml
index e102841..e5e6ec3 100644
--- a/documentation/Concepts/interpretation.xml
+++ b/documentation/Concepts/interpretation.xml
@@ -1,869 +1,882 @@
InterpretationInterpretation is a compiler's mode
reserved to evaluate, expand and simplify parts of a given program based on
information available in this mode. On the other hand, Interpretation is a
middle man, or an intermediate level, between the Transcend and Brute
levels, as it facilitates communication between those by means of
interpreting data of respective layers. It can be further divided into Upper
and Lower Interpretations depending on the interaction with the layer we are
focused on at the moment.mode
whereby - как это перевести? - это режим с помощью которого xreate
интерпретирует? Авторский смысл: это режим(компилятора) В КОТОРОМ компилятор
интерпретирует. Program: Речь идет о компьютерной программе,
или может приложении для смартфона, или алгоритме? Тогда лучше употребить
соответственно software ('piece of software') / app / algorithm. Вообще
"наши" компьютерные программы - это у них software, а не programs - -
ДА ТУТ СЛОВО PROGRAM ПОДХОДИТ,
особенно в контексте обработки программы с какимито целями, как здесь, чтоб
ее интерпретировать.name="tests/interpretation.cpp: Interpretation.Doc_Intr_1"
main= function:: int; entry
{
x= "a":: string.
y= if (x=="b"):: string; i12n(on)
{1} else {0}.
y
}In this example, the identifier
y has an attached annotation
i12(on) which indicates that
the compiler should use compile-time interpretation to evaluate
y. Once the simplification
process is over, the function returns 0,
with neither memory allocations for the string variable x
nor any computation at runtime.Annotation - может лучше value или
parameter? - да Annotation
подходитThere are two annotations reserved
to control the interpretation process:i12n(on)
Forces compiler to interpret the annotated expression, function, or
function argument. It yields error if the expression is impossible to
interpret.i12n(off)
Disables interpretation for the annotated expression.Eligible ExpressionsCurrently the compiler is able to
interpret the following expressions:Atomic instructions: numbers,
strings, identifiers.Relational and logic
operators e.g. x==true,
x!=0.if,
switch statements.
[[#late-interpretation-or-e|Statement expansion]] allowed.Loop statements.
[[#late-interpretation-or-e|Statement expansion]] allowed.Functions.
[[#function-interpretation|Function calls]],
[[#partial-or-late-function|Partial function call
interpretation]].interpreta - это так и нужно, или
неполное слово? - этот
кусок текста на сайте превратится в ссылку, это адрес ссылки, такой
должен бытьIndex operator e.g.
x = {1, 2, 3}.
y = x[0], info = {planet="Earth"}.
x = info["planet"].List operators e.g.
x = [1..10]. y={"day",
"night"}.Function InterpretationThe whole function body may be
subject to interpretation if it consists of interpretable expressions
only.name="tests/interpretation.cpp: Interpretation.Doc_FnIntr_1"
unwrap = function(data::unknType, keys::unknType):: unknType; i12n(on)
{
loop fold(keys->key::string, data->record):: unknType
{
record[key]
}
}
test = function:: bool; entry
{
book = unwrap({
Library = {
Shelf = {
Book = "Aristotle's Politics"
}}}, {"Library", "Shelf", "Book"}):: unknType.
book == "Aristotle's Politics"
}The above example demonstrates
the unwrap function which
is intended to be fully interpretable, as indicated by the function
header's annotation. Obviously, the interpretable function requires that
all its arguments are also interpretable. In this case the compiler is
able to calculate the function's result at compile time with no byte code
produced. Here is what the compiled code looks like (which will be also
optimized out during the consequent compilation phases):name="tests/interpretation.cpp: Interpretation.Doc_FnIntr_1"
test = function:: bool; entry
{
book = "Aristotle's Politics":: string; i12n(on).
book == "Aristotle's Politics"
}The example also reveals a number
of similarities with dynamically typed programming languages:number of similarities - тут точно нужен
артикль, неопределенный (a) если в смысле "много схожестей", и
определенный (the) - если речь идет о точном их количестве. a dynamically
typed - неопределенный артикль никогда не употребляется при множественных
объектах - OKRelaxed
types. Notice unknType
type which has not been defined. It's interpreted well because the
compiler completely ignores the type system since everything can be
checked at compile time anyway. The Interpretation mode is exactly the
level where the relaxed type system is possible without any
performance penalties or safety concerns.Introspection
abilities. Notice how it is allowed to treat list fields as
string keys, so functions like unwrap
can get list field name as a parameter. Possible errors, such as the
list not having the requested field are easily spotted by the compiler
during interpretation and there are no concealed runtime bugs. In a
sense, it is a fair trade off between introspection expressiveness and
possible compilation errors.no hard to catch runtime bugs - не
вполне ясно. В смысле, "легко заметить баги проявившиеся во время
операции"? Тогда лучше "its is easy to see runtime bugs", или может
"the runtime bugs are possible" (если смысл такой, что могут
проявиться ошибки/баги"). It's и другие подобные сокращения: лучше
употреблять полную форму - it is, так как it's это разговорный вариант
- OK.Additional reason for the
arbitrary undefined type unknType
being used in the example is to ensure that no compilation occurs and
everything is done at the interpretation mode.In simple cases interpretation
analysis could determine that a function is subject to interpretation with
no annotation hints provided.name="tests/interpretation.cpp: Interpretation.Doc_FnIntr_2"
unwrap = function(data::unknType, keys::unknType):: unknType
{
loop fold(keys->key::string, data->record):: unknType
{
record[key]
}
}
test = function:: bool; entry
{
book = unwrap({
Library = {
Shelf = {
Book = "Aristotle's Politics"
}}}, {"Library", "Shelf", "Book"}):: unknType; i12n(on).
book == "Aristotle's Politics"
}The only difference from the
example above is the lack of annotation hint for unwrap.
It can be seen that interpretation of the variable book
is required which in its turn depends on unwrap.
In this case analysis is capable enough to determine that unwrap
is indeed possible to interpret, so no errors occur."Developer requires interpretation" - тут
речь о разработчике-программисте? Не вполне понятен смысл, поэтому пока
оставляю как есть. - да
программист, исправилThere are, however, more
complicated cases for interpretation analysis:Direct recursion.
Interpretation analysis is able to correctly determine whether a
function involving direct recursion (where the function calls itself)
is subject to interpretation or not.Indirect recursion.
Currently, when processing the indirect recursion (where a function is
called not by itself but rather by another function), the analysis
usually fails and relies on manually provided annotation hints.Below is an example of a direct
recursion:name="tests/interpretation.cpp: Interpretation.Doc_FnIntr_3"
unwrap = function(data:: X):: bool
{
if (data[0] == "the-end"):: bool
{true} else {unwrap(data[0])}
}
entry = function:: bool; entry
{
unwrap({{{{"the-end"}}}}):: bool; i12n(on)
}Function unwrap
unwraps the nested list until the desired value is found. No function
level annotation is required.Late Interpretation or
ExpansionLate
Interpretation can be conceptualized as a partial
expansion, i.e. a simplification or elimination of interpretable parts of
certain statements.name="tests/interpretation.cpp: Interpretation.Doc_LateIntr_1"
test = function(x:: int):: int; entry
{
comm= "inc":: string; i12n(on).
y= if (comm == "inc"):: int
{x + 1} else {x}.
y
}In this example, computation of
y depends on
comm and x.
On the one hand, comm has
an annotation that requires interpretation, while on the other hand
x is unknown at the
compile-time and thus cannot be interpreted. In this case the only way to
satisfy contradictory requirements is to expand
the if statement, since it
is only possible to interpret condition part of the statement, leaving
conditional blocks unchanged. In other words, the if
statement is expanded
which results in only one of the child blocks being compiled,
x+1 in this example, based
on already known fact that the else
block would never be executed."to interpret condition part of the
statement" - это не смог понять. - `if
(...) {..} else {...}` is a statement. It consists of condition part `if
(..)`, and two blocks - "if-true" block and "if-false" block
Due to the fact that expansion,
as opposed to "pure interpretation", leaves some portion of the code for
subsequent compilation it can also be called late
interpretation for the result depends on runtime information
and has memory and performance footprint.as having runtime footprint - не понял.
Что-то связанное с требованиями к памяти? Но это кажется о программах, а
не о interpretation - переписал
фразуBelow is a more complex example
of a loop expansion:name="tests/interpretation.cpp: Interpretation.Doc_LateIntr_2"
main = function(x:: int):: int; entry
{
commands = {"inc", "double", "dec"}:: [string]; i12n(on).
loop fold(commands->comm::string, x->operand):: int
{
switch(comm):: int
case ("inc") {operand + 1}
case ("dec") {operand - 1}
case ("double") {operand * 2}
}
}
Identifier commands
contains a list of operations that need to be interpreted as indicated by
the corresponding annotation. Operand x
is assigned at runtime. This is the same situation as in previous example,
and it triggers expansion as expected. The result after expansion looks as
follows:name="tests/interpretation.cpp: Interpretation.Doc_LateIntr_2"
main = function(x:: int):: int; entry
{
x{1} = x + 1:: int.
x{2} = x{1} * 2:: int.
x{3} = x{2} * 1:: int.
x{3}
}In other words, this mimics the
well known loop unrolling technique by putting several copies of the loop
body in a row, each one for every item in the list commands.As of now, the following
statements support late interpretation:Branching statements:
if, switch,
switch variant,
switch late.Loop statements:
loop fold.Functions.Other operators:
query late.Partial or Late Function
InterpretationXreate supports cases where a
function has mixed arguments in terms of interpretation, some of which
need to be interpreted, while others need to be compiled.name="tests/interpretation.cpp: Interpretation.Doc_LateFnIntr_1"
evaluate= function(argument:: num, code:: string; i12n(on)):: num
{
switch(code):: num
case ("inc") {argument + 1}
case ("dec") {argument - 1}
case ("double") {argument * 2}
case default {argument}
}
main = function(init::int):: int; entry
{
commands= {"inc", "double", "dec"}:: [string]; i12n(on).
loop fold(commands->comm::string, init->operand):: int
{
evaluate(operand, comm)
}
}Looking at the function
evaluate's signature in
this example we can see only one argument code
that requires interpretation. This means that the function evaluate
is subject to a partial interpretation or, in other words,
late function
interpretation.In general, to enable late interpretation
for a function, at least one of its arguments should be annotated as
i12n(on). What compiler
does next is to generate a number of distinctive function
specializations. Each unique combination of interpretable
argument values corresponds to its own function specialization. This
should be used with cautiousness, since compiler can generate large
amounts of code in some cases.What compiler does next is to generate
number - то же самое, что в примеч. ниже (a number - несколько, the number
- конкретное число).To enable late interpretation for function - тут перед
function обязательно нужен артикль - определенный (the), если речь идет о
evaluate, или неопределенный (а) если речь идет о любой функции (я указал
the) - OKBased on the above example, three
different evaluate
specializations are generated as follows:name="tests/interpretation.cpp: Interpretation.Doc_LateFnIntr_1"
main= function(init::int):: int; entry
{
operand = init:: int.
operand{1} = evaluate1(operand):: int.
operand{2} = evaluate2(operand{1})::int.
operand{3} = evaluate3(operand{2})::int.
operand(3)
}
- Transcend and Interpretation
+ QueriesSYNTAX:
**intrinsic query** (//predicate//):: type; [//annotations-list//]predicate
Denotes a Transcend predicate.Represents the value of the Transcend
predicate predicate in the
format as follows:If a predicate has only one argument,
returns a list of elements of an appropriate type: int,
string, variant or tuple.in case of several arguments, the
arguments comprise a record. The function returns a list of
records.Predicates correspond to
variants.Interpretation can be viewed as the middle
ground between high level Transcend and low level Brute. In essence, the
principal role of Interpretation is to serve as a link between them by
interpreting data for Brute
from Transcend. The special built-in function intrinsic query
is provided to query data from Transcend allowing to process it
further.As a short example, assume that we have
Transcend facts as follows:person("Ben").This data can be queried as below:Persons = type [string]. //we expect a list of strings
persons = function:: Persons
{
intrinsic query("person"):: Persons
}The persons
function in this example returns a list of persons supplied by Transcend.
The example can be slightly rewritten using slave
types which are reserved to automatically identify an appropriate
type for the returned value. The next excerpt is equivalent to the
previous one:Person = type slave person. //equivalent to string
Persons = type [Person]
persons = function:: Persons
{
intrinsic query("person"):: Persons
}
+
+
+
+
- Querying allows to use powerful Transcend
+ Querying Example: GUI
+
+ Querying allows to use powerful Transcend
capabilities to solve convoluted problems, consequently retrieving
reasoning solutions by Brute for efficient processing. Consider a more
complicated example dealing with a GUI.
- First, let us start off defining more or
- less semantically an application's GUI on Transcend level:
+ First, let us start off defining more or
+ less semantically an application's GUI on the Transcend level:
- name="tests/association.cpp: Association.Doc_IntrinsicQuery_2", lines=15
+ name="tests/association.cpp: Association.Doc_IntrinsicQuery_2", lines=15
%Layout consists of two blocks:
block(my_content; my_nav).
%Assign roles to the blocks:
role(my_content, body).
role(my_nav, navigation).
%Navigation block can be in either an iconized or expanded form:
form(none; expanded; iconized).
allowed_form(my_nav, (expanded; iconized)).
%Visual theme:
background(my_content, color(blue)).
background(my_nav, color(grey)).
- Above we have described GUI consisting of
+ Above we have described GUI consisting of
two blocks: main content and navigation blocks. The same application can
- look differently depending on platform or viewport properties. Let us
+ look differently depending on a platform's or viewport properties. Let us
define a platform:
- name="tests/association.cpp: Association.Doc_IntrinsicQuery_2", lines=15
+ name="tests/association.cpp: Association.Doc_IntrinsicQuery_2", lines=15
% The height is greater than the viewport's width:
orientation(portrait).
- Having an application's semantic description
+ Having an application's semantic description
as well as a platform's description we can now combine them using
additional rules to produce the final result: how the application should
look like on the given platform.
- name="tests/association.cpp: Association.Doc_IntrinsicQuery_2", lines=15
+ name="tests/association.cpp: Association.Doc_IntrinsicQuery_2", lines=15
% Portrait orientation rules:
%Determine appopriate navigation list's form:
form(Nav, iconized):-
orientation(portrait);
allowed_form(Nav, iconized);
role(Nav, navigation).
%Determine blocks' layout:
align(Nav, bottom_of(Body)):-
orientation(portrait);
role(Nav, navigation);
role(Body, body).
% Landscape orientation rules:
form(Nav, expanded):-
orientation(landscape);
allowed_form(Nav, expanded);
role(Nav, navigation).
align(Nav, left_of(Body)):-
orientation(landscape);
role(Nav, navigation);
role(Body, body).
- In short, the rules above read that we
- place the expanded navigation block my_nav
- and the content block my_content in
- a row for wide displays; conversely, my_content
- and iconized my_nav on top of each
+ In short, the rules above read that we place
+ the expanded navigation block my_nav
+ and the content block my_content in a
+ row for wide displays; conversely, my_content
+ and iconized my_nav on top of each
other for narrow displays.
- After Transcend has decided actual forms
- and blocks alignment, the next step is to retrieve solutions denoted by
- the align and form
+ After Transcend has decided actual forms and
+ blocks alignment, the next step is to retrieve solutions denoted by the
+ align and form
predicates and to actually draw GUI elements:
- name="tests/association.cpp: Association.Doc_IntrinsicQuery_2", lines=15
+ name="tests/association.cpp: Association.Doc_IntrinsicQuery_2", lines=15
//Define types:
Block = type slave block.
Role = type variant {body, navigation}.
Alignment = type variant{
bottom_of:: Block,
left_of::Block
}.
Layout = type {Role, Alignment}.
//Determine role and form:
drawBlock = function(block:: Block):: Drawing
{
forms = intrinsic query ("form"):: [Form].
formCurrent = getBlockForm(forms, block):: Form.
roles = intrinsic query ("role"):: [Role].
roleCurrent = getBlockRole(block):: Role.
switch variant (roleCurrent):: Drawing
case (body)
{drawBody(formCurrent)}
case (navigation)
{drawNavigation(formCurrent)}
}
//Determine layout
draw = function:: Drawing; entry
{
layout = intrinsic query ("layout"):: Layout.
blockA = layout[0]:: Block.
switch variant (layout[1]->blockB):: Drawing
case (bottom_of)
{drawVerical(blockA, blockB)}
case (left_of)
{drawHorizontal(blockA, blockB)}
}
- Notice that the draw
- and drawBlock functions work with
+ Notice that the draw
+ and drawBlock functions work with
compile time data from Transcend, thus all this code after interpretation
gets simplified into the following:
- drawVertical(drawBody(none()), drawNavigation(iconized())).
+ drawVertical(drawBody(none()), drawNavigation(iconized())).
- The example above demonstrates the
- possibility to delegate to Transcend intelligent
+ The example above demonstrates the
+ possibility to delegate to Transcend intelligent
tasks, such as adapting a GUI to a particular platform, and
leaving Brute with efficient implementation matters.Domain Specific Languages and
InterpretationDSL is an idea of expressing
various concepts in a 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. It is possible to use interpretation abilities to emulate DSL.
Developer can express the desired functionality in the form of nested
lists of numbers, variants and strings which are then processed by a
partially interpreted function. Such function in its turn transforms the
input data into a set of low level compilation instructions so there is no
runtime overhead.On Interpretation
AnalysisAnalysis follows classical
type reconstruction
algorithms to determine which expressions are subject to interpretation
and check the correctness of reconstruction w.r.t. developer-provided
annotations. Analysis consists of two general parts:Inference.
Infers if it is possible to interpret an expression based on its
already inferred argument's decisions.Unification.
Assigns an appropriate decision w.r.t. previously inferred
expectations and developer-provided hints as well.
\ No newline at end of file
diff --git a/documentation/Transcend/transcend.xml b/documentation/Transcend/transcend.xml
index 26d6895..757e703 100644
--- a/documentation/Transcend/transcend.xml
+++ b/documentation/Transcend/transcend.xml
@@ -1,578 +1,578 @@
TranscendTranscend is a compilation phase and a
process of reasoning about a program in order to influence final compilation
results.First, the compiler extracts
annotations from a source code
as well as other logic facts
from a number of different sources. Second, logic facts are coupled with
logic rules, that describe how
to process input data, to form a complete logic
program. Finally, a solver processes the logic program and
produces decisions that affect and control compilation in a number of ways.
Crucial difference of annotations from language keywords is that they do not
have any predefined meaning and semantic; only custom developer provided
rules make them matter.Currently,
an external logic solver Clasp is used to perform Transcend tasks. Hence,
logic rules are written(and annotations are converted to) in the ASP format:
one of standard first order logic notations.Reasoning SolutionsTranscend prepares solutions to
verify, control and guide compilation process. For each uncertain
situation the compiler consults reasoning solutions to know how to
proceed. Thus, many compilation aspects are delegated to Transcend and are
ultimately exposed to a software developer. Transcend solution are used in
a number of ways as follows:Direct
+ xlink:href="/d/concepts/interpretation/#queries">Direct
connection via the query
built-in function. A program can request and explicitly process
received data from Transcend as it sees fit.Function
level polymorphism. This is the principal way for Transcend to
interfere in compilation. Polymorphism is based on a possibility for a
function to have several specializations. Each time the function is
invoked in a client code, Transcend decides which exactly
specialization to call. This gives a deep control over final program
behaviour.Module
level polymorphism. Transcend decides which modules to choose
that satisfy a client code's requirements.The compiler allows
Transcend to add hidden parameters every time specific functions are
invoked. This is implemented to support the Context
concept.Diagnostics. Transcend is
able to validate different aspects of a program and print diagnostic
messages to signal found inconsistencies.As an example, suppose we want
to control functions visibility and want to separate the visibility(private)
functions from the rest of a program:name="tests/transcend.cpp: Transcend.Doc_Diagnostics1"
sum = function(a:: int, b:: int) :: int; visibility(private)
{
a + b
}
calculate = function(a:: int, b:: int):: int; visibility(pub_interface)
{
sum(a, b)
}
test = function(a:: int, b:: int) :: int; entry
{
sum(a, b) // we may not invoke this directly
}Transcend converts the code
above to logic facts as follows(the relevant part is shown):function(sum)
function(calculate)
function(test)
bind_func(sum,visibility(private))
bind_func(calculate,visibility(pub_interface))
bind_func(test,entry)
cfa_parent(0,function(sum))
cfa_parent(1,function(calculate))
cfa_parent(2,function(test))
cfa_call(1,sum)
cfa_call(2,calculate)At this point all the
annotations are completely unknown to the compiler. Next, we need to write
rules to check that the visibility(private)
functions are called either by another visibility(private)
ones or by the visibility(pub_interface)
functions which are intended to act as a contact point, the only allowed
gate between the private part and the rest of a program:name="tests/transcend.cpp: Transcend.Doc_Diagnostics1"
% Define which functiones are allowed to invoke private ones
allowed(
visibility(private);
visibility(pub_interface)
).
% Check every invocation for violations:
warning("Visibility violation", FuncFrom, FuncTo):-
cfa_call(ScopeFrom, FuncTo);
bind_func(FuncTo, visibility(private));
scope_func_dict(ScopeFrom, FuncFrom);
{ bind_func(FuncFrom, Visibility): allowed(Visibility) } 0.Given the rules Transcend goes through all
the invocations to check are there any possible visibility violations. For
this example Transcend generates a diagnostic message: warning("Visibility violation",test,sum).This way it is possible to validate and
guide compilation process by analysing program properties employing
provided processing rules.Annotations' SyntaxXreate's annotations comprise optional
supplementary information to be processed by the compiler along with a
source code proper with an end goal being to affect a compilation process.
Annotations are special expressions in the following form:LiteralsStrings, numbers. Example:
5, "success"PredicatesPredicates have zero or more
arguments. Example: final, status(broken), version("12.0.3", unstable)NegationExample: -i12n, -access(allowed)Various Xreate's entities can be annotated.
At the time following entities are supported:ExpressionsCode
blocks and contextFunctions
and Function
GuardsModulesExpressionsSYNTAX:
//expression// **::** //type//**;** //annotation-list//.type
The expression's typeannotation-list
one or more annotations attached to the expression. Annotations are
delimited by semicolon.An example:name="tests/transcend.cpp: Transcend.Doc_Expressions1"
test = function:: int; entry
{
x = 5:: int; arithmetic(fast). //an annotation applied to an identifier
y = ((x - 8):: int; arithm_optimization(off)) * 2:: int. //an annotation applied to a nested expression
x+y
}For readability's sake compound
statements(e.g. loops)
have slightly different syntax for annotations. See particular
statement's syntax for details.A current limitation is that
expression's annotations are always preceded by a type, hence to
annotate nested expressions it is necessary to duplicate the
expression's type.Code Blocks and ContextSYNTAX:
**context** :: //annotations-list//.annotations-list
A list of annotations delimited by semicolonCode block annotations are called
context. The keyword
context is reserved to declare
annotations within an enclosing code block.An example:name="tests/transcend.cpp: Transcend.Doc_Codeblocks1"
test = function:: int
{
context:: arithm_optimization(off).
x = 10 :: int.
2 * x - 16
}Special AnnotationsDespite of "annotations do not have any
predefined meaning" being a rule of thumb, still there are deviations in
Xreate; few annotation that have special meaning are:entrySpecifies the entry point of a
program. See Function
Syntax.finalSpecifies the fixed point of
loops. See Loops.Annotations and ReasoningAnnotations is a mechanism to express
additional pieces of information and can occur in a source code or
accompanying files. They are of declarative nature in a sense that they
express specific properties of and relations between different entities,
such as: identifiers and expressions, statements, code blocks, functions,
modules or even a whole program.Annotations are explicit
facts about a source code provided by a code writer in an
explicit form. Beside annotations, there are other sources of information,
in other words, implicit
facts that are automatically inferred by various analysis
phases to express useful insights regarding an analysed code. Such pieces
aren't exposed directly to a developer but accessed and used internally at
the reasoning phase along with annotations. All types of input are summed
up below:Explicit annotationsAnnotations provided by a code
writer in order to explicitly affect compilation.Code analysisDifferent code analyses provide
information implicitly inferred from a source code.SupervisionExternal facts that reflect a
hardware and/or an environment where compilation takes place or
where a program is supposed to work. There are many supervision
layers possible, from custom compiler's mode, to OS- or computer-
or even network-wide levels. In other words, supervision covers
local or client supplied annotations, e.g. prohibits web cam
usage, or external network resources access.AuditThird party supplied external
information that reflects additional software properties, e.g.
results of security audit.Reasoning rulesReasoning infers additional facts
from a pool of previously gathered information. It is done by
using resoning rules
that govern reasoning process.Why Annotations MatterThis section goes through differences that
annotations have over traditional statements.ExtensibleAnnotations are not a part of language
syntax and there is no predefined set of allowed annotations. A
developer is free to define and use custom annotations to express
desired properties without prior changes to the compiler.Indicative NatureUnlike language statements that have well
defined compilation output, annotation do not. They are rather of
suggestive nature and actual impact depends on many factors. Annotations
comprise only an input for a reasoning procedure that ultimately decides
how exactly to influence compilation.CooperationAnnotations are gathered from different
sources enabling annotations to complement each other. The compiler
blends in annotations originated from various sources seamlessly to
improve decisions quality.External Sources and
AdaptabilityAnnotations can be used not only to
express properties of a program itself but also properties of a local
environment(OS, computer, local network) where the program is supposed
to run. Thus, in fact, the compiler operates by information that is
usually inaccessible during software development and can't be reflected
in a program code otherwise. In essence, it is postcompilation
- a process of adjustments to a particular environment and functioning
mode.FeedbackTraditionally software has a rigid
strictly defined hierarchical structure of interaction between
components. For example, let us focus on a client-service model. A
service works the same regardless of a particular client in order to be
compatible with as wide set of different clients as possible. However
clients may use only small subset of service functionality or use it in
a specific and predictable order. Generally speaking, the service in
case of having additional information about a particular client is able
to serve requests more efficiently in many situations.It can be conceptualized as a
feedback from a client.
Annotations is a good candidate to express feedback to improve
interaction quality.ResiliencyAs a program grows and goes through
multiple reworks and rewrites many initial assumptions about the code
and relation between different parts invalidated. Some implementation
details and approaches became outdated and code quality deteriorates. A
dIfferent picture is if the code uses annotations to express important
properties. Any changes in an underlying code lead to a restarting of a
reasoning procedure which adjust implementation decisions to be as
optimal in a changed environment as before.
diff --git a/documentation/exploitation.xml b/documentation/exploitation.xml
index 299281a..784c0f9 100644
--- a/documentation/exploitation.xml
+++ b/documentation/exploitation.xml
@@ -1,490 +1,541 @@
ExploitationThis chapter discusses exploiting
external resources, such as files, as a particular instance of a
side effects problem that
inevitably stems from an interaction with the outside world. Unlike virtualization,
- an another documentation's topic that tackles I/O, exploitation approaches
- subject from a different angle — it is concerned with an order
- of operations, sequence in which different clients jointly use
- the same resource and it deals with corresponding difficulties, e.g. ensures
- proper resource initialization before actual usage.
+ another topic that deals with I/O, exploitation approaches the subject from
+ a different angle — it is concerned with order
+ of operations, a sequence in which different clients jointly use
+ the same resource, and it deals with corresponding difficulties, e.g.
+ ensures proper initialization of a resource before its actual usage.
SyntaxAnnotationsSYNTAX:
**use**(//resource-id//)
**init**(//resource-id//)resource-id
— user-defined resource identifier
- Annotates function or code
+ Annotates a function or code
block as such that exploits resource resource-id.GuardsSYNTAX:
**exploitation(init)**
**exploitation(none)**Specializations that are
recognized by exploitation reasoning. Each specialization corresponds to
an initialization strategy:exploitation(init)
is expected to perform actual resource initialization.exploitation(none)
- is expected to do nothing as initialization isn't necessary or done
- elsewhere.
+ is expected to do nothing as initialization is either not necessary
+ or done elsewhere.
BackgroundIn software engineering, the idea to avoid
- side effects have received considerable traction. Indeed, side effects is
- something that is hard to take into account and thus programs that have
- side effects are inherently unsafe, thus best coding practices are
- rightfully suggest to isolate side effects producing code as much as
- possible. It's so called pure functional languages whose philosophy goes
- even further and frames side effects as something opposite of "pure", and
- everything is built around effectless computations to the point that some
- languages' design itself includes side effects producing constructs, such
- as I/O, as an afterthought, as something almost unnecessary.
-
- However, in reality the opposite is true,
- most applications' sole responsibility is to communicate with "outside
- world", reacting to the external events and change "world state"
+ side effects has received considerable traction. Indeed, side effects are
+ something that is hard to take into account, and thus programs that have
+ side effects are inherently unsafe, so best coding practices rightfully
+ suggest to isolate the code that produces side effects as much as
+ possible. It is so called pure functional languages whose philosophy goes
+ even further and frames side effects as something opposite of "pure" and
+ built around effectless computations, to the point that some languages'
+ design itself includes side effects producing constructs, such as I/O,
+ describing them as an afterthought, as something almost
+ unnecessary.
+
+ Я тут взял на себя смелость
+ подредактировать последнее предложение, посмотри внимательно - все ли я
+ правильно понял
+
+ However, in reality the opposite is true, as
+ most applications' sole responsibility is to communicate with the "outside
+ world", reacting to the external events and changing the "world's state"
accordingly. As a consequence, side effects usually are the
- only important effects the
- program produce and surely deserve first class support from a programming
- language and justify efforts to develop approach to alleviate related
- safety and performance concerns.
+ only important effects
+ produced by a program, and they surely deserve a first class support from
+ a programming language and justify the efforts to develop an approach to
+ alleviate the related safety and performance concerns.
Exploitation Plan
- One complexity of taking side effects into
- account is the fact that final result depends on an exact
- operations order. This harshly impacts both performance and
- safety, for many techniques, e.g. caching, parallelization can neither be
- automatically performed nor validated since they are based on various
+ One complexity with taking side effects
+ into account is that the final result depends on an exact
+ order of operations. For many techniques, this harshly impacts
+ both performance and safety, e.g. caching or parallelization can neither
+ be automatically performed nor validated since they are based on various
degrees of reordering or deal with possibly undetermined beforehand order
of execution.
- In this chapter, it is assumed, that final
- effects of execution fully defined by exploitation
+ Мне не очень нравится фраза
+ "deal with possibly undetermined beforehand order of execution". Выглядит
+ очень громоздко. Может, лучше сказать "or are connected with such an order
+ of execution that was not determined beforehand".
+
+ In this chapter, it is assumed that final
+ effects of execution are fully defined by exploitation
path — for a particular code path that can occur during
execution, it is its part consisting of only relevant code blocks., i.e.
- those that deal with an exploited resource. Other code blocks do not
- influence exploitation effects and so are excluded from consideration.
- Thus reasoning about effects is reduced to considering all possible
- exploitation paths, checking do they meet certain requirements that define
- valid exploitation and making corrections if needed and possible.
-
- Result of the reasoning is called
- exploitation plan —
- specification that defines exact order and strategy of using a given
+ those that deal with an exploited resource. Other (unannotated) code
+ blocks are assumed to not produce exploitation effects, so they are
+ excluded from consideration. Thus reasoning about effects is reduced to
+ considering all possible exploitation paths, checking if they meet certain
+ requirements that define valid exploitation, and making corrections if
+ needed and possible.
+
+ The result of the reasoning is called
+ exploitation plan — a
+ specification that defines the exact order and strategy of using a given
resource in order to comply with imposed requirements.With all above said, the discussed approach
can be presented as follows:
- Annotations are used to express some
- aspects of side effects to enable further reasoning. They indicate
- code blocks that deal with resource as well as provide additional
- information about how exactly it is exploited, e.g. use, initialize or
- deinitialize resource.
+ Annotations are used to express certain
+ aspects of side effects to enable further reasoning. They indicate the
+ code blocks that deal with a resource as well as provide additional
+ information about how exactly it is exploited, e.g. using,
+ initializing or deinitializing the resource.
- Existing code paths, extracted during
- source code processing, coupled with relevant annotations is enough to
- construct all possible exploitation paths and analyze them. Analysis
- determines possible(weak)
- paths that can occur or not during particular execution as well as
- certain paths(strong)
- that occur always no matter what. Also it checks are exploitation
- paths valid against certain rules, e.g. initialization occurs always
- before actual usage and is it possible to correct invalid
- paths.
+ Existing code paths extracted during
+ source code processing and coupled with relevant annotations are
+ enough to construct all possible exploitation paths and analyze them.
+ The analysis determines possible (weak)
+ paths that can either occur or not occur during particular execution,
+ as well as certain paths (strong)
+ that occur under any conditions. Also it checks if an exploitation
+ path is valid against certain exploitation rules, e.g. initialization
+ always occurs before the actual usage, and if it is possible to
+ correct the invalid paths.
- Reasoning's result is an exploitation
- plan that dictates order and strategy of exploitation is presented in
- form of appropriate specialization for polymorphic functions that deal
- with resources in order to ensure safe exploitation to the extent
- based on provided annotations.
+ The reasoning's result is an
+ exploitation plan that dictates the order and strategy of exploitation
+ presented in the form of an appropriate specialization for polymorphic
+ functions that deal with resources in order to ensure safe
+ exploitation to the extent based on provided annotations.
- Exploitation related side effects are
- viewed as a set of additional restrictions over operations order. Only
- subset of possible reorders is still valid w.r.t. side effects.
- Transcend's task is to find out refined set of valid orders. Thus
- techniques that rely on reordering enjoy additional information to
- make safe optimizations.
+ Exploitation-related side effects are
+ viewed as a set of additional restrictions over the operations' order.
+ Only the subset of possible reorders is still valid w.r.t. side
+ effects. Transcend's task is to achieve a refined set of valid orders.
+ Thus techniques that rely on reordering enjoy an additional
+ information to make safe optimizations.... and it serves three major goals:
- Safety. Validates existing exploitation
- plan or is it possible to safely exploit given resource at all.
- Compiler signals error if a given exploitation plan is invalid, i.e.
- does not satisfy requirements w.r.t. side effects as expressed by
- annotations.
+ Safety. Validates the existing
+ exploitation plan, or determines if it is possible at all to safely
+ exploit a given resource. Compiler signals an error if a given
+ exploitation plan is invalid, i.e. does not satisfy requirements
+ w.r.t. side effects as expressed by annotations.Regression Resilience. When
it comes to using external resources, some spurious dependencies
usually occur between otherwise isolated, independent components of a
- program. Sometimes refactoring and other code changes break those
- dependencies inevitably introducing regressions. Exploitation catches
- this sort of regressions and automatically regenerates exploitation
- plan suited for a changed conditions.
+ program. Sometimes refactoring or other code changes break those
+ dependencies, which inevitably results in regressions. Exploitation
+ catches this sort of regressions and automatically regenerates the
+ exploitation plan suited for changed conditions.Performance. Generated exploitation
plans are optimal in a sense that they cut off superfluous operations,
- for example, removing resource initialization in several places if it
- can be done safely in a single one, thus reducing overall
+ for example, by preventing resource initialization in several places
+ if it can be done safely in just one place, thus reducing overall
overhead.Domination Analysis
- When it comes to a reasoning about order of
- execution flow and possible code paths, crucial vehicle for that is
+ When it comes to reasoning about the order
+ of execution and possible code paths, the crucial vehicle for that is
domination analysis producing
dominator tree as an
output.
- Unlike the usual function-bounded domination
- analysis, when separate domination tree is produced for each function
- defined in a program, Exploitation requires program bound analysis, that
- is to take into account control flow across all functions in a program. It
- is computationally intensive task to perform analysis over a whole
+ Unlike the usual function-bound domination
+ analysis, when a separate domination tree is produced for each function
+ defined in a program, Exploitation requires a program-bound analysis, that
+ is taking into account the control flow across all functions in a program.
+ It is a computationally intensive task to perform an analysis over a whole
program, however it is compensated by the fact that Exploitation only
- takes into account code blocks that deal with, or in other words, exploit
- external resources. Thus there is no necessity to build full dominator
- tree, only the relevant parts are constructed, just enough to make sound
- exploitation plan decisions.
+ takes into account the code blocks that deal with or, in other words,
+ exploit external resources. Thus there is no necessity to build a full
+ dominator tree, only the relevant parts are constructed, just enough to
+ make sound exploitation plan decisions.Empty Exploitation Plan. Effect Free
ComputationsValidation of exploitation path is done
- against some predefined constraints. Depending on complexity of a
- constraints, i.e. number of different exploitation events that are seeking
- for in each path, reasoning goals categorized into several groups:
+ against some predefined constraints. Depending on complexity of the
+ constraints, i.e. the number of different exploitation events that are
+ sought for in each path, reasoning goals are categorized into several
+ groups:
Zero Order Exploitation.
- Meaning that all paths are checked in terms is there exploitation at
- all or no, is there at least a single exploitation event along the
- path.
+ Meaning that all paths are checked to determine if there is
+ exploitation at all or not, and if there is at least a single
+ exploitation event along the path.
First Order Exploitation.
- Deals with a situations when it's enough to check only two different
- exploitation event occur in a required order. It can be useful for
- example, to check whether all resource uses occur after it is
- initialized.
+ Deals with the situations where it is enough to check only two
+ different exploitation events occurring in a required order. It can be
+ useful for example, to check whether all resource uses occur after it
+ is initialized.
+
+ "to check only two
+ different exploitation events occur in a required order" - нужно
+ уточнить смысл фразы. "Уточнить только два события, осуществляющиеся в
+ требуемом порядке", или "уточнить, осуществляются ли два этих
+ события"? Я использовал герундий (occur - occurring), как более
+ нейтральный вариант.Higher Order Exploitation.
- Expresses constraints involving several(more than two) exploitation
+ Expresses constraints involving several (more than two) exploitation
events and relations between them.Empty
- Exploitation is an important instance of zero order constraint.
- It useful mechanism for developer to annotate function or part of a
- program as effect free in terms of exploitation. Thus, efectless, clean or
- pure code can be clearly separated from effectful part and compiler raises
- compilation error in case of accidental mixing or using "wrong" type of
- code in non appropriate environment.
+ Exploitation is an important instance of the zero order
+ constraint. It is a useful mechanism for the developer to annotate a
+ function or part of a program as effect free in terms of exploitation.
+ Thus, effectless, clean or pure code can be clearly separated from the
+ effectful part, and compiler raises a compilation error in case of
+ accidental mixing or using "wrong" type of code in a non-appropriate
+ environment.
Resource Initialization
- One important problem related to an
+ One important problem related to
exploitation order is to ensure that a given resource is properly
- initialized before its first usage and additionally it is not initialized
- more then once during exploitation session. This is instance of first
- order exploitation since in a validation mode it is enough to check
- exploitation plan to ensure that every resource usage preceded by resource
- initialization at some point in the past,
- i.e. previously in the exploitation path.
+ initialized before its first usage and, additionally, that it is not
+ initialized more than once during the exploitation session. This is the
+ instance of first order exploitation since in a validation mode it is
+ enough to check the exploitation plan to ensure that every resource usage
+ was preceded by resource initialization at some point in the
+ past, i.e. previously in the
+ exploitation path.
- For planning mode, the problem is addressed
- as follows:
+ For the planning mode, the problem is
+ addressed as follows:
- Central idea of the algorithm is to
- consider candidates for initialization only among code blocks that
- dominate given usage
- site. Obviously, initialization in dominating block precedes usage for
- any possible code path.
+ The central idea of the algorithm is to
+ consider candidates for initialization only among those code blocks
+ that dominate a given
+ usage site. Obviously, initialization in the dominating block precedes
+ usage for any possible code path.One or more dominator blocks are chosen
- for actual initialization in such way that they are cover all found
+ for actual initialization in such way that they cover all the found
usage sites.
- For code blocks chosen for
- initialization specialization exploitation(init)
- is set, for the rest specialization exploitation(none)
- is used.
+ For the code blocks chosen for
+ initialization, the specialization exploitation(init)
+ is set, while for the rest of the code blocks the specialization
+ exploitation(none) is
+ used.
- Look at the example below:
+ Please take a look at the example
+ below:name="tests/exploitation.cpp: Doc_ResourceInit_1", lines=15
import raw("scripts/cfa/payload.lp").
import raw("scripts/exploitation/exploitation.lp"). //exploitation reasoning
import raw("scripts/exploitation/test1.assembly.lp").
guard:: exploitation(init)
{
openFile = function(filePrev:: FILE_P):: FILE_P; init(file)
{
fopen("/tmp/test", "w")::FILE_P
}
}
guard:: exploitation(none)
{
openFile = function(filePrev:: FILE_P):: FILE_P
{
filePrev::int
}
}
test = function:: int; entry
{
seq
{ f0 = undef:: FILE_P. f0 }
{
//Scope #1:
f1 = openFile(f0):: FILE_P.
f1
}
{ //Scope #2:
f2 = openFile(f1):: FILE_P.
f2
}
{
//Scope #3:
sizeWritten = fwrite("Attempt to write..", 12, 1, f2):: int; use(file).
sizeWritten
}
{
//Scope #4:
fclose(f2):: int; use(file)
}
{ sizeWritten :: int}
}There is the function test
- that executes sequentially next commands: open a file(scopes #1, #2),
- write some text(scope #3) and finally, close the file(scope #4). It
- represents simple work flow with an external resource.
+ that executes sequentially the following commands: open a file (scopes #1
+ and #2), write some text (scope #3), and finally, close the file (scope
+ #4). It represents a simple work flow with an external resource.
In order to connect the code to the
- exploitation the functions fwrite and
- fclose in scopes #3 and #4
+ exploitation, the functions fwrite
+ and fclose in scopes #3 and #4
respectively are annotated with annotation use(file).
This information is used by reasoning to look whether it is possible to
- initialize given resource before actual usage as well as where and when
+ initialize a given resource before actual usage as well as where and when
exactly to initialize it. Function openFile
is annotated as init(file) meaning it
can initialize depending on
- chosen strategy. The function is invoked both in scope #1 and scope #2.
- Both scopes are executed strictly
+ the chosen strategy. The function is invoked both in scope #1 and scope
+ #2. Both scopes are executed strictly
before scopes #3, #4. Thus it is indeed possible to initialize
- resource before usage. Next task for exploitation is to choose correct
- exploitation plan, i.e. to assign strategies for all possible
- initialization places in the effort to initialize resource only once.
+ the resource before usage. Next task for exploitation is to choose the
+ correct exploitation plan, i.e. to assign strategies for all possible
+ initialization places in the effort to initialize the resource only once.
Here, it means that only one invocation of openFile
is assigned with exploitation(init)
- to actually initialize the file. Other one is automatically marked with
- exploitation(none) to invoke
+ to actually initialize the file. The other one is automatically marked
+ with exploitation(none) to invoke a
different specialization of openFile
- that does nothing since the file is already initialized.
+ that does nothing since the file was already initialized.
+?>
\ No newline at end of file
diff --git a/documentation/index.xml b/documentation/index.xml
index 8c227b4..38c577d 100644
--- a/documentation/index.xml
+++ b/documentation/index.xml
@@ -1,425 +1,434 @@
Xreate ManualXreate is an open source general purpose high
level programming language designed to write efficient and safe computer
programs.
- Here "high level" is all about a developer
- oriented side focused on an ability to easily write, read, reuse, as well as
- adapt software to a constantly changing environment or business goals. In
- this respect, any software product can be evaluated on the basis of three
- dimensions: efficiency, safety, and flexibility. Unfortunately, those
- properties are proved to be largely contradictory, for it is manageable to
- write either efficient (yet unsafe) or safe (yet impractical) code, but not
- both. Thus, the ultimate goal of the language is to allow developers to
- produce code that would have all these properties at the same time. Blending
- features of seemingly incompatible programming paradigms is a basis of
- Xreate's design principles.
+ Here "high level" refers to the developer
+ oriented side meaning exactly an ability to easily write, read, reuse, as
+ well as adapt software to a constantly changing environment or business
+ goals. In this respect, any software product can be evaluated on the basis
+ of three dimensions: efficiency, safety, and flexibility. Unfortunately,
+ those properties are proved to be largely contradictory, for it is
+ manageable to write either efficient (yet unsafe) or safe (yet impractical)
+ code, but not both. Thus, the ultimate goal of the language is to allow
+ developers to produce code that would have all these properties at the same
+ time. Blending features of seemingly incompatible programming paradigms is a
+ basis of Xreate's design principles.To achieve the aforementioned design goals,
Xreate consists of three distinctive layers:Brute.
The lowest layer is called Brute
— this is code that is intended to be actually compiled. Code
on this level implements actual software functionality. It resembles the
usual imperative languages' apparatus and consists of executable
instructions such as arithmetic, branching, input / output, etc.Transcend.
Brute alone is not enough to constitute a full-fledged language since
code requires various non-executable metadata to express developer's
intents, check correctness, validity and perform other types of
analyses. In Xreate everything of this sort belongs to a declarative
type layer called Transcend.
Transcend is a logic reasoner that is appropriate to do management-type
work — it analyzes, oversees and controls Brute by guiding compilation
process. More precisely, everything on this level, logic or transcend
facts and rules, is gathered and sent to an external logic solver to
make solutions that are brought back in order to guide compilation.
Unlike usual static analysis tools, Transcend directly controls
compilation(see Basic
Example) and able to make decisions even based on data available
only at runtime(see Late
Transcend)Interpretation.
There is also Interpretation
— the intermediate level resembling dynamically typed languages that is
- used as a contact point and interpreter between Brute and
- Transcend.
+ used as a contact point and interpreter between Brute and Transcend. See
+ an example.
On a syntactic level, Xreate is a procedural
language with extensive use of annotations
— arbitrary unconstrained metadata that a software developer can attach to
different language constructs, variables and code blocks. Annotations are
completely invisible for the compiler proper and used by Transcend more as a
suggestion conveying additional information."a different language
constructs": если подразумевается "конструкции разных языков", тогда лучше
"different languages' constructs". Если конструкции языка, в целом, то тогда
артикль a не нуженThere are several extensions already
implemented to give a feeling what does this structure can be used for.
Containers
chapter describes that it is possible to reason about and automatically
choose the most appropriate data structure's implementation depending on how
it is used in the code. Look at the example below:x = [1, 2, 3]:: [int].Container x
does not have well defined implementation just yet. Only by looking how it
is used throughout the code, the compiler is able to decide how exactly to
store container's data.Interaction of different components and joint
use of external resources is covered by Exploitation:logger = createFileLogger("/some/file"):: Logger.
...
write(logger, "First log entry").
...
write(logger, "Last log entry").Exploitation reasoning allows to determine
when it is the first,
last access to resources such
as files, in other words, it infers access order. As a result it is possible
to automatically initialize / destruct related resources. Unlike RAII, an
another related technique, Exploitation is reserved to manage resources
usage that spans across different parts of a program: modules, plugins,
etc.Virtualization
reasoning also helps to work with external resources by enabling access
control if and when it is needed
only. Example:openFile("/some/file"):: string; assign_sizo(zoneA).
openFile("/some/file"):: string; assign_sizo(zoneB).If the compiler recognizes file access from
the different zones, as in this example, it applies an appropriate
virtualization strategy enough to ensure that instructions that belong to
different zones do not interfere with each other.Unlike "pure", academic languages, Xreate
targets safe and reliable usage of effectful computations such as IO that is
covered above as well as mutable structures described in the Communication
chapter.Note, that the described extensions are not
part of the compiler and developers can write their own custom transcend
rules to cover other aspects.Basic ExampleTo demonstrate what Xreate is all about,
basic example is given below:name="tests/introduction.cpp: Introduction.Doc_Example_1", lines=15
guard:: iAmVeryFast
{
div = function(a:: int, b:: int):: int
{
a / b
}
}
guard:: iAmVerySafe
{
div = function(a:: int, b:: int):: int
{
if ( b == 0 ):: int { zeroDivisionErrCode() } else { a / b }
}
}
test = function:: int; entry; iAmVerySecure
{
div(10, 5)
}Here entry point of the program is a
function test recognized so by the
compiler because of annotation entry
in its signature. There are also two functions with the same name
div called specializations.
Each specialization has a guard that defines a condition
that has to be met in order to invoke this particular specialization. In
the example, specializations of div
have iAmVeryFast and iAmVerySafe
guards, respectively. Let's say that a code author writes two
specializations where the first one is a very fast division
implementation, while the second one is a very safe division
implementation since it checks division by zero, being "unacceptably slow"
due to an extra check instruction, though. This is a basis of polymorphism
— client's code test is able to work
with any specialization, and the compiler must decide which one to invoke
with the only hint it has — annotation iAmVerySecure
in the function test's
signature."provides two specializations"
- возможно, лучший вариант "designates/assigns/allocates two
specializations". Или даже просто specifies/indicates. (PS заменил на
specifies)"unbearably slow" - я бы
заменил на более нейтральное "too slow". Unbearable - это скорее об
ощущениях человека. Или, если под "unbearably" имеется в виду "недопустимо
медленный", тогда - unacceptably slow.All annotations (except entry)
are custom defined by developer itself.This is when Transcend comes into play. By
adding a transcend rule as shown below it is possible to associate
annotation iAmVerySecure with
invocation of specialization guarded by iAmVerySafe:name="tests/introduction.cpp: Introduction.Doc_Example_1", lines=15
dfa_callguard(SiteInv, iAmVerySafe):-
dfa_callfn(SiteInv, div);
SiteInv = s(_, _, ScopeInv);
cfa_parent(ScopeInv, function(FnInv));
bind_func(FnInv, iAmVerySecure).Transcend rules are written in ASP syntax —
common syntax to write logic programs. This particular rule reads that for
any function annotated with iAmVerySecure,
certain specialization iAmVerySafe is
chosen for div invocation.In this example an appropriate
specialization is statically resolved, so the other specialization isn't
even compiled.the, потому что их всего
две.By providing custom rules it is possible to
implement any polymorphism strategy, be it performed statically or
dynamically. The example demonstrates basic workflow: Transcend gathers
available information about a program such as annotations and using custom
- rules makes a decision to guide compilation process, particularly by
- selecting appropriate specializations as in the above example.
+ rules makes decisions to guide various aspects of compilation process,
+ particularly by selecting appropriate specializations as in the above
+ example.
More Advanced ExampleSuppose we write a program to generate a web
page consisting of several blocks(e.g. a header, a footer) constructed
independently by different parts of our program. In order to organise the
code, we express our intention
that all blocks should be sent to a client in a very specific order: first
a header, then a body and footer, as below:name="tests/exploitation.cpp: Exploitation.Doc_ExampleEov_1", lines=15
eov_expect(
webpage, header, body; %we expect body to be sent after header
webpage, body, footer %.. and footer after body
).This is similar to Exploitation:
we are working with the external resource webpage
and want to take into account an exact order of resource exploiting
operations. Then, we write code like this:send("Header"):: Content; eov_checkpoint(webpage, header).We mark the operations we are interesting in
as checkpoints(here
header is the name of a checkpoint
and webpage is the resource the
checkpoint refers to) and want to know are checkpoints executed in the
expected(as defined above) order.If it so happens that these blocks are
constructed in the correct order in our program we may
send them immediately. Otherwise, we must cache already constructed
content till a whole page is generated to ensure correctness. In other
words, clearly, there is an opportunity for optimizations for caching has
not only memory overhead but delays response latency(time before the first
block is sent) as well. We write two implementations immediate_output
and cached_output each for the
corresponding case:name="tests/exploitation.cpp: Exploitation.Doc_ExampleEov_1", lines=15
//type to store either sending error code or cached content
Content = type variant {
errcode:: int,
message:: string
}.
//Send immediately:
guard:: immediate_output
{
send = function(content:: string):: Content
{
errcode(printf("%s", content))
}
}
//Cache content to send it later:
guard:: cached_output
{
send = function(content:: string):: Content
{
message(content)
}
}These implementations should be registered
for the compiler to know which one to use:name="tests/exploitation.cpp: Exploitation.Doc_ExampleEov_1", lines=15
eov_analysis(
success, immediate_output;
fail, cached_output
).Predicate eov_analysis
compares actual checkpoints execution order with the expected one and
chooses either of two implementations depending on the result. This way,
it is guarantied that immediate sending is chosen only if it is safe to do
so to avoid unnecessary caching.Thus we can safely change and adapt our
program knowing that clients always receive web page's content in the
correct order by automatically employing the most efficient content
delivery strategy depending on particular circumstances.Differences from other
languagesit is convenient to talk about
intentions in
order to outline a vast landscape of programming languages and point out
- the place of Xreate on it, i.e. to compare languages on a basis do they
+ the place of Xreate on it, i.e. to compare languages depending on do they
allow to express a developer's intentions along with raw code that can be
- used on many occasions, e.g. to validate code's correctness. Traditionally
- type system is used to declare intentions. At closer look, types in
- (imperative) statically typed languages contain information as
- follows:
+ used on many occasions, e.g. to validate the code's correctness.
+ Traditionally type system is used to declare intentions. At closer look,
+ types in (imperative) statically typed languages contain inseparable
+ combination of the following:
Intentions, such as "that
- variable is a string".
+ variable is a string".
- Usage patterns. A new code
+ Usage patterns: a new code
should play nicely with the rest of a program, e.g. if a program works
- with Unicode, a new string variable should be also of Unicode
- type.
+ with unicode, a new string variable should be also of unicode family
+ type(even if this is not necessary for a given part of code)Platform constraints, e.g.
if a platform supports wide strings natively, a new string variable
should also be a wide string.In this regard, in general,
statically typed languages are overconstrained,
- they require developers to provide more information then they intend to.
- This generally hinders code reuse and adaptation; to work on a new
- platform, code requires porting: a process of re-expressing underlying
- intentions once again with the new platform's constraints.
+ they require developers to provide more information than they intend to.
+ This usually hinders code reuse and adaptation; to work on a new platform,
+ software requires porting: a process of re-expressing underlying
+ intentions once again with the new platform's constraints.
On the other side, dynamically
- typed languages in this regard are underconstrained,
+ typed languages are underconstrained,
since they do not allow to express all desirable intentions. This leads to
- a disastrous inefficiency and code fragility because any errors can be
- caught only at runtime.
+ disastrous inefficiency and code fragility because any errors can be
+ caught only at runtime.
- OOP languages are hugely
- successful among other reasons because they provide classes
- to more finely express intentions, e.g. String,
+ As an example, OOP languages are
+ hugely successful among other reasons because they provide
+ classes to more
+ finely express intentions, e.g. String,
UnicodeString,
Utf8String with exact
behaviour being hidden in implementation details.
- Xreate in its turn offers
- annotations for developers to express intentions, e.g. string;i_dont_need(cow)
- (copy-on-write implementation). Annotations alone is not enough to fully
- determine necessary types thus the compiler finishes them by looking at
- usage patterns along with platform properties in order to infer most
- efficient implementations.
-
- Also there is an area beyond
- type systems capabilities, since types are designed to express intentions
- only about data flow. However as shown in the previous example it is also
- useful to express intentions about program structure properties, such as
- operations order.
+ Xreate in its turn is based on
+ the idea to allow developers express as much or as little intentions as
+ they want to. This is a way to reach one of the the language's goals to
+ facilitate writing of easily reusable and adaptable software. On a syntax
+ level, annotations are used to express intentions, e.g. string;i_dont_need(utf8)(note
+ the modality, annotations can express boundaries, preferences, etc. ).
+
+
+ This approach obviously leads to problems
+ with software's performance and efficiency. For most high level languages,
+ compilers rely on defaults to fill in unspecified by the developer gaps in
+ the exact implementation details. Usually languages provide standard,
+ default data structures the developer have no control over. However Xreate
+ follows another approach: to resolve this contradiction, the compiler
+ determines necessary implementation details not only depending on
+ annotations, but also by looking at usage patterns along with platform
+ properties trying to infer most efficient implementations in any given
+ circumstances.