AL-IV - Подробное описание

| RUSSIAN(Русский) | ENGLISH |  |
This function requires cookies. If the browser does not accept cookies, the light theme is used always.

## Introduction

Here a short description of the AL-IV (ALFOUR) programming language is represented. At this link you can find more detailed language description.

## 1. Class is a module

`/* //l CLASS {Hello|_world}, UNTESTED : //abcd,j FUNCTION Main|_hello_world : //e,j << 'Hello, World!' >> . //fgh END //i,j */ //l`
Notes: Any identifier (except a for loop variable name) should be at least 8 characters length, or a rest of its long name should be specified after symbol '|'. Class name `{Hello_world}` can be shortened to `{Hello}` when the class is referenced in other class (but not in an `IMPORT` statement). Class name is always starting from an uppercase letter. Class attribute `UNTESTED` allows compiling the class without testing it. A single static method of a class `Main` (full name in the example is `Main_hello_world`) allows to create an application containing such class. An operator sign `<<` outputs a line of code to a console (or adds an item to an array, or adds a string to an accumulating string). An operator sign `>>` waits an input of a string from a console operator. Operations << can be chained. An operator >> can be attached to a chain of << operations. E.g.: `<< 'Input X:' >> STR X_s|tring REAL X|_input_value = X_s.Real` A dot symbol '.' is ending a function. The statement END is finishing a class. All the keywords are written in the uppercase. Each class is placed in its own source text file. The AL-Iv code is always located between the end and the start multi-line comments symbols: `/*` and `*/`. Take into attention, that symbols of the start ( `*/` ) and the end ( `/*` ) of comments in the AL-IV are contrary to such symbols in C-like languages (this allows to place text on the AL-IV and e.g. on the C# in the same text file). The END class bracket should be separated from previous lines by at leastr one empty line (to differ to the END brackets in functions blocks if begin/end are used there).

## 2. Two rules continuing lines

`CASE any_changed ? // SQL-query to update a record in a database //e // corresponding to new values in input controls {DB} db|_TEMP_object = New_ole_db(connection_s(path), "", "") //f db << UPDATE Students, //c SET Exam3 = {Result_set}, //c WHERE ID = {Id_student} //c -------------------------- 'do update now' //e db.Exec ==> ; //b`

Notes: A line of code should not exceed 80 characters (not taking into account comments which are starting with `//` and continues to the end of the line). Ending spaces are ignored, tabulating symbols are accounting as single symbols. Single line contains single statement (there are exceptions: operation ==> can finish any simple statement, operations << and >> can finish statement <<) A statement can be continued a next line in one of two cases: A line of code is finished with one of characters '`(`', '`[`', '`,`'. The next line of code is not empty and it is not starting a new statement (i.e. it is not starting from an identifier or (without starting spaces) from a double quotation '`"`' (which are used to enclose a string constant). String concatenation does not require any special operation sign: string constants and expressions are written one by one or via spaces. Or a string constant concatenating is placed at the start of the next line of code (leading spaces are ignored). There are also operator comments in the AL/IV language: `------------------------------ 'text'` which are used to divide blocks onto sections of allowable size (no more then 7 simple and 7 block statements in a section). Simple statement is not ending with a special symbols like ';' (unlike to many other programming languages). The semicolon ';' is used to finish a block statement, and the dot '.' is used to finish a function (or another class level declaration).

## 3. Conditional statement

`/* CLASS {Equation}, TESTED : TEST Of_Square_eq : REAL r|esults_array[] Square_eq(1, 0, -1, R= r[]) //acd ASSERT r[].Count==2 && r[0].Near(-1) && r[1].Near(1) ----------------- 'D == 0' Square_eq(1, 2, 1, R= r[]) //acd ASSERT r[].Count==1 && r[0].Near(-1) ----------------- 'A == 0' Square_eq(0, 1, 1, R= r[]) ASSERT r[].Count==0 . -------------------- 'Ax^2 + Bx + C = 0' METHOD Square_eq|uation( REAL A|_coefficient, //b REAL B|_coefficient, REAL C|_coefficient, REAL R|esults_list[]) //c : //abcd R[].Clear //dk CASE A.Abs <= 1e-10 ? ==> ; //efgj REAL d|escriminant = B * B / 4 - A * C //b CASE d.Sign ? //eh [0]: R[] << -B / (2 * A) //ik [1]: REAL q|sqrtD_div2A = d.Sqrt R[] << -B / 2 / A - q //k R[] << -B / 2 / A + q //k ; . //g END */`
Notes: Amount of function parameters should not exceed 3, either while calling the function other parameters should be passed in form of an assignment: Name= value; Names of function parameters, public fields of classes are starting from the uppercase letter. Names of local variables, hidden fields of the class are starting from a lowercase letters. Functions can not return an array as a result (pseudo-variable RESULT is always a scalar). When it is necessary to "return" an array as a result of a function, it should be passed as a parameter. Arrays are always written together with square brackets. E.g.: `R[].Count` A conditional expression type of the statement CASE can be `BOOL`, `INT`, `STR` or a enumeration (`ENUM`). A statement CASE with a Boolean expression has only a single branch corresponding to a positive expression value and can have an `ELSE` branch (there is not in the example). Only the entire block statement (like the `CASE`) is ending by a symbol '`;`'. Indentation is used to decorate source code but the main information (fro a compiler) about blocks nesting is contained in the source text (including symbols ending blocks '`;`' and functions '``.``'). The `CASE` statement with integer, enumerated or string condition allows to specify several branches, in each of which an array of constants in square brackets is specified. A branch is executing if the condition expression is equal to one of its constants. After executing a brunch selected, the next statement following entire `CASE` block is executing. There are no special break statements after each branch. To exit the function (before ending its body) a statement `==>` is used. It can be standalone or docked to a simple statement. E.g.: `CASE A == B ? RESULT = TRUE ==> ;` A function is ending with the dot ('`.`'). It is allowed to place such '`.`' symbol following the symbol '`;`' ending a block statement, in the same line. Though it is not allowed to place two or more semicolons ending nesting blocks in the same line, or to place any text following the '`;`' ending block statement except comments (`//`).

`/* STRUCTURE {results|_of_square_equation} : //d REAL X1|_result //e REAL X2|_result //e INT N|umber_of_results . //f -------------------- 'Ax^2 + Bx + C = 0' FUNCTION Square_eq|uation( REAL A|_coefficient, REAL B|_coefficient, REAL C|_coefficient) ==> {results} : ---------------- 'single CASE ? version' REAL d|escriminant = B * B - 4 * A * C REAL aa|_A_doubled = 2 * A CASE ? //a [A.Near(0) || d < 0] ? ==> //ab [d.Near(0)] ? RESULT.N = 1 //ab RESULT.X1 = -B / aa RESULT.X2 = RESULT.X1 ELSE REAL q|sqrt_of_D = d.Sqrt //c RESULT.X1 = (-B - q)/ aa RESULT.X2 = (-B + q)/ aa RESULT.N = 2 ; . */`

Notes: A condition can be omitted in the header of the `CASE ?` statement. Each its branch is defined by a Boolean expression in square brackets (ending by a question mark). It is possible to declare also the `ELSE` branch. All branch expressions are calculated sequentially until the expression found with the TRUE value. In that case, a correspondent branch is executed, and entire statement is finished. If there are no true expressions, an `ELSE` branch is executed if it is present. Data type STRUCTURE is declaring on a class level. Its name (in figure brackets) should be starting from a lower case letter. Therefore, all the structures declared in a class, are always accessible for all the classes importing the class having those structures declarations. In a class declaration, all its fields are declaring like class fields or local variables. Structure fields names should always start from capital letters (since structure fields could not be hidden). A STRUCTURE declaration is ending with the dot '.' (like any other declaration of a class level).

## 4. Local variables scope

`/* METHOD Enter_command|_typed_on_console : STR c|ommand_entered << #NL "Enter command: " >> c //d CASE "stop" IN c ? //c << #NL "Really stop? " >> c CASE c.Lower IN ["yes", "y", "ok", "+"] ? //b Want_stop = TRUE ==> ; ; STR w|ords_of_command[] Split(w[], c, " ") ------------------------ 'recognize command' Command = 'NOTHING' Direction = 'FORWARD' Speed = 1 Volume = 2 Text = "" CASE w[0] ? //a ["go"]: Command = 'GO' ["run", "rn"]: Command = 'GO' Speed = 3 ["jump", "jm"]: Command = 'GO' Speed = 2 ["rotate", "rt"]: Command = 'ROTATE' ["look", "lk"]: Command = 'LOOK' ["take", "tk"]: Command = 'TAKE' ["put", "pt"]: Command = 'PUT' ["say", "s"]: Command = 'SAY' INT text_from = 1 //e CASE w[1] ? ["loud"]: Volume = 3 text_from = 2 //e ["quiet"]: Volume = 1 text_from = 2//e ; ["whisper", "ws"]: Command = 'SAY' Volume = 1 ELSE << " Unknown command" ==> ; CASE text_from > 0 ? //e FOR i IN [text_from TO w[].Count-1] : //e Text = _ w[i] " " ; //f Text = Text.Trim ; ------------------------ 'direction ?' CASE Command IN ['GO', 'LOOK', 'TAKE', 'PUT'] ? //b CASE w[1] ? //a ["forward", "f", ""]: NONE ["left", "l"]: Direction = 'LEFT' ["right", "r"]: Direction = 'RIGHT' ["back", "b"]: Direction = 'BACK' ELSE << " Unknown direction: " w[1] Command = 'NOTHING' ; ; . */`

Notes: A condition in the `CASE` statement can be a string. Items to be compared in such case are string constants. These are required to be unique. It is allowed to use ELSE in such case. For simple types except REAL (i.e. `BOOL`, `BYTE`, `INT`, `STR`) and for enumerations it is allowed to use operations `IN` and `!IN` in which the second operand is an array of constants (of the correspondent type), or a variable array. Such operation checks out a presence (or an absence) of the left value in the right array. For strings also it is possible to use operations `IN` and `!IN` to check out presence (absence) of a substring in the right string. Special symbols can be encoded in form #XX, where XX is an integer constant or one of symbolic names (`#NL`, `#CR`, `#LF`, `#TAB`, `#SP`, `#BK`, `#DEL`, `#BELL`, `#ESC`). A local variable declared at any place in a function becomes available to read and write at any below line in the same function to the end of the function. It is guaranteed that all the local variable will be initialized with its default values (0 for numbers, empty string - for strings, the first item in enumerations, NONE safe value - fro objects of classes). The single underline symbol '_' to the right side of the assignment sign always refers to a variable or field to the left side of the '=' sign.

## 5. Five simple data types ...

`/* CONST BOOL: Flag|_boolean = FALSE . CONST INT : //e Count|er_integer = 1_000 //d IValue|_arbitrary = -2_222_222 . //bd CONST REAL: Value|_number = 1.3E+10 . CONST STR : Chain|_of_characters = "ABC" . //fc CONST BYTE: Byte|_value = 255 . //g */`

Notes: Constants are declared in a class body (not in functions). In a single CONST statement it is possible to declare an arbitrary amount of constants of the same type - one constant in a single line of code. The block CONST is ending by '.'. While writing numeric constants underscore characters are ignored and can be used to separate groups of digits visually. E.g.: `0xAB12_cd20`, `0b1101_1100_0101_0011`, `0o377_105_210_114`, `-0.376209e-3` A capacity for simple data types is fixed and it is the same for a target platform. For integers usually 32 bits are used, for real numbers - maximum allowed (64 or 80 bits usually). Though actually precision depends mainly from a compiler implementation. There is no "character" data type. It is not recommended to work with bytes if this is possible: in some cases using bytes can lead to programmatic errors. If such type is used in a class, it should have an attribute `BYTES`.

## ... and enumerations

`/* CLASS {Car|_on_road}, UNTESTED : ENUM {traffic|_colors} : 'R|ED_LIGHT', //ab 'Y|ELLOW_LIGHT', //b 'G|REEN_LIGHT' . //b */`

`// can a car continue moving on a crossroad ?`

``/*` METHOD Can_move( {traffic} C|olor_lighting, BOOL A|lready_move_before) ==> BOOL : CASE C ? //c ['R']: RESULT = FALSE //bd ['G']: RESULT = TRUE //bd ['Y']: RESULT = A ; . //bd END */`

Notes:: A name of a enumeration is starting from a lower registry letter. A name of each enumerated item is enclosed into apostrophes. If a condition in the CASE statement is a enumeration, then it is not allowed to use ELSE, and all the members of the enumeration must be listed in branch selectors. All the enumeration items of all declared in a class enumerations must be unique . If same named items are imported from different classes, then a name of such item should be specified in form `{enumeration_name}.'ITEM_NAME'` If imported enumerations are same named with others imported ones, then it is necessary to specify its items in form: `{Class_name}.{enumeration_name}.'ITEM_NAME'`

## 6. Loop statement

`/* // print Fizz instead of numbers multiply to 3, // print Buzz - if a number is multiple to 5, // FizzBuzz - if it is multiple to 3 and 5, // All other numbers print as is. FOR i IN [1 TO 100] <----------------------------- //abcdе CASE ? [i % 3 == 0 && i % 5 == 0] ? << "FizzBuzz" #NL [i % 3 == 0]: << "Fizz" #NL [i % 5 == 0]: << "Buzz" #NL ELSE << i.Str #NL ; -------------------------------------------------> //е */`

Notes: A loop variable can have short only name. In the loop header, following the `IN` keyword, either a range of `INT` values is specified (in a form `[n TO m]` or `[n DOWNTO m]`), or an array. Loop variables need not (and do not use) a separate declaration. It can not be used out of the loop body, and next time it can be used only as a loop variable again (with an array of the same type). It is allowed to use in any block in the function itself operator bracket pairs `: ;`, `{ }`, `<-- -->`, `BEGIN END`, `begin end`. In case of arrow brackets `<-- -->` amount of minuses can be greater then two.

`/* FUNCTION Invert_array( REAL A|rray_of_numbers[*]) //c { FOR R IN A[0 ТО */2] <-------------------------- //ab REAL t|emp_number = R A[R.Index] = A[* - R.Index] //bde A[* - R.Index] = t ------------------------> } //bde */`

Notes: A loop variable is always become of the same type as the loop array items. Operand '`*`' in an expression in square brackets is a pseudo function returning an integer index of the last array item (for a string it returns an index of the last character). For empty array (or string) it returns -1. Symbol '*' in brackets following a parameter declaration means that the parameter is a fixed array and can accept fixed arrays or slices of dynamic arrays as actual parameters. It is recommended to use fixed array parameters if it is not necessary to change their size in a function. A pseudo function `Index` is applicable only to a loop variable and returns an index of an item which is enumerating. Any loop `FOR` is finished at some time. This can not eliminate slowdown of execution in case of many nesting long running loops but eliminate always infinitive looping. If it is really necessary to run an infinitive loop, a special form of a loop can be used: `FOR, INFINITE: body ;` and the class should have an attribute `INFINITE`.

`/* FUNCTION Undouble_quotes( STR S|tring_containing_double_quotes) ==> STR BEGIN BOOL just|_unquoted = FALSE RESULT = S //c FOR i IN [S.Len-1 DOWNTO 1] <------------------------ //ab CASE !just && S.[i-1 TO i] == ''""'' ? //d RESULT = RESULT.[0 TO i-1] RESULT.[i+1 TO *] //ce just = TRUE ELSE just = FALSE ; ----------------------------------------------------> END */`

Notes: A string can not be used as a looping array. A range should be used to iterate characters of a string (it does not occupy memory though it is concerned as an "array"). If some items will be deleted in an array while iterating it, then it is necessary to use a range starting from the upper index and ending with the lower index value (using a keyword `DOWNTO`). All the function parameters of simple types are passed to a function by value and can not be changed in a function body. The `RESULT` pseudo-variable can be changed any time, its final state will be returned. The string can be quoted using either by quotes "xxxxx" or by double apostrophes ''xxxxx''. It he first variant, inner apostrophes are treated as usual characters, in the second variant inner quotation marks does so. To concatenate string expressions these just are written one by one without any special sign, may be separated with space characters;

`/* FUNCTION Simple_numbers|_from_1_to_Last( INT A|rray_results[], INT Last|_number) : A[].Clear A[] << 1 << 2 << 3 << 5 << 7 << 11 << 13 << 17 FOR i IN [A.Count-1 DOWNTO 0] <------------ //a CASE A[i] > Last ? A[].Delete(i) ; ------------------------------------------> FOR i IN [19 TO Last] <-------------------- //a CASE i % 2 == 0 { CONTINUE i } //cd FOR n IN A[] <----------------------------- //b CASE i % n == 0 ? BREAK n, CONTINUE i ; //c ------------------------------------------> A[] << i -----------------------------> . */`

Notes: If a constructed array in brackets is enumerating in a loop, the form with increscent index `[x TO y]` or a form with decreasing index `[y DOWNTO x]` can be used. If an array variable is specified as enumerated, without any special range in indexes, then all its items are listed from the first (with index 0) to the last (*, or Count-1). In statements BREAK and CONTINUE a loop label is specified (a loop variable name). This allows to finish or continue a loop from a nesting loop body (but to jump to the start or to the end of an external loop it is required to `BREAK` first from all the nested loops). Amount of nesting blocks can not exceed 3 (actually 3.5 still it is allowed the 4th nested level with a single simple nesting statement), so in many cases interrupting or continuing a loop by a condition allowing to decrease amount of nesting blocks is more preferable then a conditional block at the end of a loop body.

`/* FUNCTION Binary_search|_in_array( INT A|rray_data[], INT W|hat_searching, BOOL Optimize|_median_DUMMY) //c : INT L|eft_bound = 0 INT R|ight_bound = A[].Count-1 FOR i|dummy IN A[] : //ab CASE L >= R-1 ? BREAK i ; INT M|iddle_index = L + (R - L) / 2 CASE (A[M] - W).Sign ? [0]: RESULT = M ==> [1]: R = M ELSE L = M ; ; CASE ? [W == A[L]] ? RESULT = L [W == A[R]] ? RESULT = R ELSE RESULT = -1 ; . */`

Notes: Since there are no WHILE loops, these should be replaced with `FOR` loops. The best way is to specify bounds sufficient to execute all need iterations, but without too big reserve (to avoid a situation looking like a suspension in case of error in code). In the sample above a loop is used enumerating the array on which working is required, so amount of iterations can not exceed amount of its items. Though actually not N items were handled but LOG2(N), this is not bad approximation in this case. If a loop variable is not used in the loop to calculate expressions, it should have in its name (at least in its long version) a substring '`dummy`' (in any register). The same rule is actual for a function parameter, which is not used in a function body: it should have a substring '`dummy`' in its name too. Even if it is used in inherited or ancestral classes.

`/* FUNCTION Binary_to_int|eger( STR S|tring_binary) ==> INT : FOR i IN [0 TO S.Len-1] : CASE S[i] ? ["0"]: RESULT = _.ShiftL(1) //df ["1"]: .. = _.ShiftL(1) | 1 //abcdfg ["_"]: NONE //ce ELSE ==> ; ; . */`

Notes: If bitwise operations are used in a class ('|', '&', '^', '~') then the class should have an attribute BITWISE. It is not allowed to have both bitwise and arithmetic operations in a single statement.\ There are no embedded shift operations. Functions should be used: `ShiftL|eft`, `ShiftR|ight`, `RotateL|eft`, `RotateR|ight`. All the variables (and the `RESULT`) are automatically initialized by zeroes of correspondent type. If it is necessary to specify an absence of any operations then a single constant `NONE` should be used. Operand '_' (the single underline symbol) is used as the reference to the same variable or field which is placed to the left of the assignment sign. Symbol '..' (two dots) at the left side of the assignment sign is used to refer to the same target variable or field as in the previous assignment statement (in the function).

// Output to screen using loops:

// ########## // ## ## // # # # # // # # # # // # ## # // # ## # // # # # # // # # # # // ## ## // ##########
`FUNCTION Crossed|_rectangle( INT N|umber_lines) : INT x|_position_of_1st_internal_dot = 2 FOR i IN [1 TO N] : CASE i == 1 || i == N ? FOR j IN [1 TO N] : << "#" ; << #NL CONTINUE i ; << "#" INT k|ount_spaces_outer = x - 2 INT s|paces_inner = N - 2 * (k + 2) FOR j IN [1 TO k] : << " " ; << "#" FOR j IN [ 1 TO s] : << " " ; CASE s > 0 || N % 2 == 0 ? << "#" ; FOR j IN [1 TO k] : << " " ; << "#"#NL CASE ? [i * 2 == N] ? NONE [i > N/2] ? x -= 1 ELSE x += 1 ; ; .`
`FUNCTION Crossed2|_with_string_variable( INT N|umber_lines) : INT x|_position_of_1st_internal_dot = 1 FOR i IN [1 TO N] : CASE i == 1 || i == N ? STR s|tring_line = "" FOR j|dummy IN [1 TO N] : s << "#" ; << s #NL CONTINUE i ; s[] << "#" FOR j IN [1 TO s.Len-2] : s << " " ; s[] << "#" s.[x] = "#" s[* - x] = "#" << s #NL CASE ? [i * 2 == N] ? NONE [i > N/2] ? x -= 1 ELSE x += 1 ; ; .`

// Get a string representation for an input array of integer numbers: // [0,1,2,3,4,7,8,10] ==> "0-4,7-8,10" // [4,7,10] ==> "4,7,10" // [2,3,8,9] ==> "2-3,8-9"

`FUN get_ranges(INT A|rray_sorted[]) ==> STR : RESULT = A[0].S FOR i IN [1 TO A[].Count-1] <----------------- CASE A[i] == A[i-1] + 1 ? STR suffix|_to_append = "-" A[i].S CONTINUE i ; RESULT << suffix "," A[i].S suffix = "" -----------------------------> RESULT << suffix .`

## 7. Recursion checkout

`/* FUNCTION Factorial(INT N|umber_argument) ==> INT, RECURSIVE : //ab CASE N <= 1 ? RESULT = 1 ELSE .. = N * (N - 1).Factorial ; . //cd */`

Notes: A recursive function calling itself directly, should have an attribute `RECURSIVE`. If the function is calling itself indirectly via a function which is always marked with the attribute `RECURSIVE`, it itself can be declared without the attribute. For recursive functions (with the `RECURSIVE` attribute) a control of recursion nesting level is added to the code. If some limit value was exceeded, a return to the first recursion level is occur, and then the function is returned without any other working. And if a result is required always `NONE` or correspondent type zero is returned. Static one parameter function always can be called in a form of object non-parameter method. E.g., `x.Sin` instead of `Sin(x)`. It is allowed to use the CASE ... ELSE ... ; statement a single line if it can be fit in one line.

## 8. PUSH a variable

`/* CASE A == 0 ? PUSH A = B : //ac PUSH B = C : //ac Results[] << Linear_eq ==> ; //d ; ; */ ... /* ---------------------- 'Ax = B' METHOD Linear_eq|uation ==> REAL : RESULT = B / A . //e */`

Notes: A value of a variable is saved and it gets a specified value (which is not necessary). A variable can be an array item, but it can not be an array. On the block PUSH exit, the variable is restored always (including a case when an execution of entire function is ended by a `==>` statement). In a function it is allowed not more then 3 nesting levels of statements `CASE` / `FOR` (if more then 3 nesting levels are used, the class should have an attribute `BAD`), but statements PUSH are not accounting for nesting. There are no exceptions. Divide by zero for real number gives a special value `INF|INITY`, for integer number - maximum possible integer value of a corresponding sign. In case when by some serious reason it is not possible to continue working, an operator `STOP "text"` can be used to stop working entire thread (or entire program, in case of the main thread). A class should have an attribute `STOPPING`, if the operator `STOP` is using in its functions.

## 9. Array with indexes from a enumeration

`// display a list of traffic lights burning`

``/* BOOL Is_lighting[{traffic}] //d ... FOR li|ghting IN Is_lighting[] : STR wh|at_lighting << li.Name ", " ; //ac ; CASE wh != "" ? << wh.[0 TO *-2] " are lighting" //b ELSE << "There are no lights which are lighting" ; */``

Notes: A variable can be declared at a moment of the first assigning a value to it, even in a nesting block. Then it is accessible to the end of entire function. This can lead to errors by a reason of absence of re-initialization of such variable (on further iterations). So, you will be warned if there are no preliminary initialization of an accumulator variable declared such way in the nesting loop. For a string an operation `S.[i TO j]` cuts a substring from index i to index j inclusive (indexing of arrays and strings is always starting from 0). E.g.: `"ABCD".[1 TO 2] == "BC"` An embedded pseudo function `Name` for an enumeration item is returning a short item name (in apostrophes).. A Boolean array with indexes from a enumeration can be used like a set in the Pascal language - as a set of named flags.

## 10. Class field attributes (INIT, READ). Strong and weak references.

`// an astronomic example`

``/* CLASS {Star_system}, SAFE : IMPORT: {Planet_or_star} ; {Planet} All_planets|_and_stars[] //e STR Name|_of_star_system END CLASS {Planet|_or_star}, SAFE : IMPORT: {Star_system} ; {Star_system} Star_|system_, INIT //abcef {Planet} Parent_|planet_or_star_, INIT //abcdf STR Name|_of_planet_or_star {Planet} Satellit_|s_list_[] BOOL Is_star -------------------------------- 'Rings' BOOL Is_ring|_of_particles, READ INT Color|_of_ring, READ //g METHOD Set_is_ring(INT C|olor_of_ring) : Is_ring = TRUE Color = C . //h ----------------- 'Main characteristics' REAL Dist|ance_to_parent_AU_perigely REAL Year|_time_years REAL Ecliptic_incl|ination_degrees REAL Ecliptic_exc|entricity REAL Apogee_days|_from_2000BC ------------------ 'Own characteristics' REAL Size|_relative_to_Earth REAL Mass|_relative_to_Earth REAL Inclination|_degrees REAL Day|_time_hours REAL Atmosphere|_dencity REAL Atmosphere|_height REAL Magnetic_f|ield_strength END */``

Notes: An object class field which name (both short and long) is ending by trailing underscore character '_', is a weak reference to an object. In the aexample above: `{Planet} Parent_|planet_or_star_` A weak reference is never retaining. When a referenced object is destroyed, all weak references to it become referencing to a special NONE object of a correspondent class. `NONE` object - is a special constant object, having all the fields and methods same as a real object of the same class. But values of its fields are zeroes and `NONE` objects (fore strings - empty strings, for dynamic arrays - empty arrays, for `BOOL` - value `FALSE`, for enumerations - the first item of a enumeration). Writing to a `NONE` object is not changing its value (always ignored). A class can not contain object fields which are strong references to objects which are ancestors or inheritances of the class. Nets and graphs can be built only using weak references. If a class {A} has strong references to a class {B}, then class {B} can not have strong references to a class {A}. If a class {A[1]} is referencing strongly a class {A[2]}, {A[2]} is referencing to {A[3]}, ..., {A[n-1]} to {A[n]}, then the class {A[n]} can not have strong references to objects of any of classes {A[1]}, {A[2]}, ..., {A[n-1]}. This rule guarantee impossibility of creating a ring of keeping each others objects. In result, a program is not requiring a garbage collector, and all objects are destroyed exactly at the moment when its strong referencing counter become zero (but: see about object "owners" below). An attribute `INIT|IALIZE` for a class field requires an initialization of the field while creating an instance of a class. An attribute `READ` protects a field from writing to it from other classes (except the class itself and its inheritance).

## 11. Object life time

`/*
`CLASS {Star_system}, SAFE : IMPORT {Planet_or_star} ; {Planet} All_planets|_and_stars[] STR Name|_of_star_system {Planet} Current_parent|_for_new_planets_and_stars METHOD Last|_planet ==> {Planet} := All_planets[*] . //g METHOD New_planet(STR Name|_planet) ==> {Planet}, NEW : //a RESULT = {Planet}(Star_ = THIS, //acd Parent_ = Current_parent, Name = Name), >> All_planets[] Current_parent.Satellit[] << RESULT CASE All_planets[].Count == 0 ? Current_parent = RESULT ; . METHOD Make|_planet(STR Name|_planet) : CASE "," IN Name ? STR names|_splitted[].Split(Name, ",") FOR n IN names[] : NONE = New_planet(n) ; ELSE NONE = New_planet(Name) ; . //... `FUNCTION Create_Sol_system ==> {Star_system}, NEW : //a RESULT = {Star_system} //a {Star_system} R|esulting_shortcut = RESULT {Planet} sun|_star = R.New_planet("Sun") ...Is_star = TRUE --------------------------------- 'inner planets' R.Make("Mercury,Venus") R.Make("Earth"]) PUSH R.Current_parent = R.Last : R.Make("Moon") ; PUSH R.Current_parent = R.New_planet("Mars") : R.Make("Fobos,Deimos") ; --------------------------------- 'asteroid belt (>200km size)' PUSH R.Current_parent = sun: R.Make("Ceres,Vesta,Pallas,Hygiea,Interamnia,Europa,Davida,Sylvia") R.Make("Eunomia,Euphrosyne,Cybele,Hektor,Juno,Camilla,Patientia,Banberga") R.Make("Psyche,Thisbe,Doris,Fortuna,Themis,Amphitrite,Egeria,Elektra,Iris") R.Make("Diotima,Aurora") ; --------------------------------- 'Jupiter + satellites > 100km' PUSH R.Current_planet = R.New_planet("Jupiter") : R.Make("Io,Europa,Ganimede,Callisto,Himalia") ; --------------------------------- 'Saturn + satellites > 100km' PUSH R.Current_planet = R.New_planet("Saturn") : R.Make("Prometheus,Pandora,Epimetheus,Janus") R.Make("Mimas,Enceladus,Tethys") R.Make("Dione,Helene,Rhea,Titan,Hyperion,Iapetus,Phoebe") ; --------------------------------- 'Uranus + satellites > 100km' PUSH R.Current_planet = R.New_planet("Uran") : R.Make("Juliet,Portia,Miranda,Ariel,Umbriel,Titania,Oberon,Sycorax") ; --------------------------------- 'Neptune + satellites > 200km' PUSH R.Current_planet = R.New_planet("Neptune") : R.Make("Despina,Galatea,Larissa,Proteus,Triton,Nereid") ; --------------------------------- 'Pluto + all known satellites' PUSH R.Current_planet = R.New_planet("Pluto") : R.Make("Charon,Styx,Nix,Kerberos,Hydra") ; --------------------------------- 'trans-pluto > 500km only' R.Make("Orcus,2002 XV93,2003 VS2,2007 JH43,Ixion,2004 GV9,2005 RN43") R.Make("1995 SM55,2002 MS4,Haumea,2014 US224,2002 AW197,2014 EZ51") R.Make("2004 XR190,2013 FY27,Sedna") //... .` END`
*/`

Notes: In an AL/IV program a lifetime of an object is defined at the moment when its created (but not only by a garbage collector or a programmer decision who could desire to destroy an object at some time he wants). For an object it should be defined would it be assigned to an external (for a function) field or variable, or would it be added to an array of strong references, or would it have its owner assigned to it. If at least once in a function body a new object created is assigned to a pseudo variable `RESULT`, then the function should have an attribute `NEW`. Such factory function can be called only by a separate statement (like a direct object creating statement). And an object returned by such function also can be added to an array of strong references (using the operation `>>`). Either the destination variable fro a new object should have a substring '`temp`' in its name. If an object is creating by a constructor and following comma an owner is specified (`OWNED BY` xx), then the object created will exist until its owner is destroyed (independently of its usage counters). If an object constructed by a factory function or a constructor is added to an array of references, then such array must be an array of strong references (not weak). When an object is added to an array, its usage counter is incremented and when it is removed from the array, the usage counter is decremented (very like to assigning a reference onto it to a variable or a field of an object). If a function body contains only a single statement, and it is an assignment a value to the pseudo-variable `RESULT`, then the keyword `RESULT` can be omitted.

## 12. Inheritance. Polymorphysm.

`/* CLASS {Input_person|_characteristics}, UNTESTED : IMPORT {Form}, {Dialog}, {Control} ; IMPORT {Label}, {Edit}, {Button} ; //cd BASE CLASS {Form} //ef {Edit} Surname|_person {Edit} Name|_first {Edit} Patro|nymic {Button} OK|_input_finished {Button} Cancel|_input CONSTRUCT: //gh Caption = "Input person information" NONE = New_label(THIS, "Surname:") //i Surname = New_edit(THIS, "Surname") NONE = New_label(THIS, "Name:") Name = New_edit(THIS, "Name") NONE = New_label(THIS, "Patronymic:") Patro = New_edit(THIS, "Patro") -------------------- 'Buttons panel' new_column("Buttons", 'CLIENT', DEFAULT_SIZES) OK = New_button(THIS, "OK", "OK"), >> grayable[] Set_default(OK) Cancel = New_button(THIS, "CANCEL", "Cancel") Set_cancel(Cancel) . OVERRIDE disabled ==> BOOL : //ab CASE sender_alias == "OK" ? RESULT = Surname.Text == "" || Name.Text == "" ; . END */`

Notes: There are no variables in the AL/IV language which could play role of pointers to functions. A single way to call a function which is unknown at design time (except its parameters) is virtual methods of a class. All the methods in a class are virtual. While redefining a method: a keyword `OVERRIDE` is written instead of the keyword `METHOD`; a short only name of the method is specified; parameters should be declared in the same order but its names could be other though the first letters must match; types of object parameters must be compatible, for all other types these must match. If a class is using other classes (to declare variables or fields, to call methods, to access constants etc.), then all used classes must be listed in an `IMPORT` statement. The `IMPORT` statement is written immediately following the class header and contains a list of used classes, finished by a dot or semicolon symbol. While descending a class from another class, following the IMPORT statement a statement `BASE CLASS {Ancestor_class}` should be written. In the BASE CLASS construction in parentheses a list of assignments to fields of the ancestor class object can be provided. Such assignments are working before assignments in a statement of the object creating: `{Type}(assignments_list)` To initialize an object after its creation a constructor of the class is called. The constructor is defined by a special function with the keyword`CONSTRUCT` as the header. The constructor is called after creating the object: at that time an initialization of base class fields in the statement `BASE CLASS(assignments)` is done, and (important!) all the assignments in a constructing statement `{Type}(assignments_list)` are also executed, and all the ancestor constructors was called already. If a function value is not required, it should be "assigned" to a constant `NONE`.

## 13. Destructors are dangerous

`/* CLASS {Bitmap|_image}, NATIVE, DESTRUCTORS : //b NATIVE: "using Windows;" . INT native_handle METHOD Handle ==> INT : CASE native_handle == 0 ? create_native_handle ; RESULT = native_handle . DESTRUCT: //a CASE native_handle != 0 ? release_native_handle ; . //a ... */`

Notes: In most cases destructors are not necessary (embedded destructor is enough to release strings, arrays and objects belonging to the object). Really user defined destructors are actually necessary only in cases when a class is keeping a descriptor of some "external" (relatively to the AL/IV language) object. Still time of destructor firing can not be predicted well, using it can lead to unstable or unpredictable work of an application. A class containing a destructor, should have an attribute `DESTRUCTORS`.

## 14. Native functions

`/* CLASS {Command_line}, NATIVE : //a NATIVE: "using System.Diagnostics;" . //f FUNCTION Get_command_line ==> STR, NATIVE : @ "" ''" if (test_command_line != "")"'' //bh " RESULT = test_command_line;" //g " else RESULT = return Environment.CommandLine;" . //g NATIVE: @ "private static string test_command_line = " #QU #QU ";" . //e FUNCTION Change_command_line(STR Val|ue_to_set), NATIVE : STR pr|efixed_by_application_name = #QU Application_name #QU //c " " X_Val NATIVE " test_command_line = X_pr;" . //c END */`

Notes: If a class is using native functions of a target language, then such class should have an attribute `NATIVE` in the header of the class. All such classes are depending on a target platform. The best way to provide as most independency as possible is in moving all such classes into separate subdirectories and specifying certain directories for a compiler depending on a target platform. E.g., MyProject\_Delphi, MyProject\_C#, etc. A function having an attribute NATIVE ahs a string constant as its body. Such constant is inserted into resulting compiled code without changes, and a correctness of the code written in the string is depending on a programmer only. To write a native code, it is necessary to know a target language and understand rules of a compiler from AL/IV to that language. A native function can start from a regular AL-IV code. In such case it therefore should end with a `NATIVE "code"` statement with a native code in a string constant. If a compiler converts a parameter name `A|ttribute` in the translated code into `X_A` (taking short name and adding a prefix `X_` to it), then in the native code it should be referenced by an identifier `X_A`. But this can depend on a certain compiler implementation. Additionally to functions with attribute NATIVE, separate statements `NATIVE: "string" .` can be placed in a source code of a class. These are using to insert additional code between functions, on a level of a class as a textual unit. Statements `NATIVE` placed before the first function in a class can be used for special purposes. E.g. to add directives "`using`" or "`uses`". But this rule depends on a certain compiler implementation. To simplify writing native code, a compiler can automatically create an inner local variable RESULT (like the Pascal), initialize it by the default `NONE` value, and return it at the end. It is better to assign a value to the `RESULT` variable rather then to use native `return expression` statement (less warnings from the end compiler). String literals in quotations `'' ... ''` (with double apostrophes) are very useful to write native code still it is possible to write double quote symbols there even without any mirroring. But it is necessary to remember that such literal should be the single in a line (after removing ending comments following `//`). Native classes do not require testing (though these also can be tested). Only its creator is responsible for its correctness. He (she) at least should provide enough safety to use such classes and to satisfy following requirements: there should no be any exceptions fired: always errors should be ignored, but without any bad "consequences" for a user's files or other data; information on important errors should be stored in a queue of errors accessible by the program via system functions; while testing it is not allowed to change any files except those having a substring '`TEST$`' (in any register) in its name or path; also while testing it is not allowed to change any data bases and tables in data bases except those having a substring '`TEST`' in the name (of a DB or of a table).

## Content

Introduction

1. Class is a module

2. Two rules continuing lines

3. Conditional statement

4. Local variables scope

5. Five simple data types ...

... and enumerations

6. Loop statement

7. Recursion checkout

8. PUSH a variable

9. Array with indexes from a enumeration

10. Class field attributes (INIT, READ). Strong and weak references.

11. Object life time

12. Inheritance. Polymorphysm.

13. Destructors are dangerous

14. Native functions

Content

`` Home