Description
Ano is not by any mean a general purpose programming language. Although applications can be written in pure Ano, the goal of Ano is to offer domain specific language for application kernel and event callback mechanism for own C-functions, and also hide the complexity of X windows and unix audio systems, plus offer one simple mechanism for applications to use them. See bottom of this page how to bind own functions useable in Ano scripts.
Ano script compiles to C, and is further compiled and linked as static part of the application. That means that compiled executable does not include Ano script anymore nor compile anything when it runs, everything is already there.
Provided Ano compiler is written in perl, and handles script syntax which looks a bit like asm. When script is compiled, the resulting file dsl_ano.h, is produced. Adventurous hacker can easily write his/her own script compiler which outputs compatible dsl_ano.h if asm syntax feels discomfortable. For now the syntax is near to asm for a reason: I like asm!
Ano script is line-based, which means that only one instruction or keyword is permitted per line:
; This is right and works okay mov cat (0) mov dog (1) loop (mov i (0); i < 10; inc i) { dump i } ; This is wrong and won't work mov cat (0) ; mov dog (1) loop (mov i (0); i < 10; inc i) { dump i }
Ano script compiler expects input file to be UTF-8 encoded, and supports include-directive, so script can be divided into several files:
include "the_other_script_file.ano"
Somewhere in Ano script, usually in the beginning, couple of tags can be specified telling some details about the script. Tags must be commented out and tags must start with @. Tags include:
@ANO_SCRIPT_NAME name of the script, must be single word @ANO_SCRIPT_VERSION version number of the script @ANO_SCRIPT_DESCRIPTION couple of descriptive words about the script @ANO_FN_NAMED_PARAMS list of named parameter declaration files for binded functions, see below for more information @ANO_UNVEIL_FILES list of files and dirs to pass to unveil(2) if unveil(2) is available and enabled, see below for more information @ANO_FLAGS_VAR_NAME_SUBS substitute variable names with very short aliases for faster processing @ANO_FLAGS_VAR_WARN_UNUSED warn if some variable is declared but not used
For example, script header may look like:
; ; @ANO_SCRIPT_NAME Example ; @ANO_SCRIPT_VERSION 0.0.1 ; @ANO_SCRIPT_DESCRIPTION This is just an example script ; ; @ANO_FLAGS_VAR_NAME_SUBS yes ; @ANO_FLAGS_VAR_WARN_UNUSED no ;
Tick in a box -style is also supported:
; @ANO_FLAGS_VAR_NAME_SUBS [x] ; @ANO_FLAGS_VAR_WARN_UNUSED [ ]
Content of @ANO_SCRIPT_* tags can be viewed in application along with some other info by using -V command line switch. Unveil list is not displayed.
Named parameters
Named parameters is a way to write more readable script, as every parameter given either internal engine functions, or own functions binded to Ano script, can be written as follows:
window_set_attrs (\ window_handle: hwnd, \ attribute: 1, \ attribute_value: 0)
Compared to above, the traditional way of calling functions will of course work too, even when the named parameter tag is present in Ano script header. As you can see, example above is more readable than this, as it documents itself:
window_set_attrs (hwnd, 1, 0)
Ano compiler has support for naming the parameters for every function provided by the engine. If named parameters are needed for own functions binded to Ano script, it needs a small file where those functions and their parameters are named. This is FOSS Mixer named parameter file, check the FOSS Mixer's Ano script and look for the functions listed here to get the idea:
; ; The purpose of this file is to allow named parameters in Ano script when ; calling functions listed below. This file is defined in Ano script by ; @ANO_FN_NAMED_PARAMS tag. See the Ano script for live use. ; ; Functions are declared as: ; ; function_name(name_of_parameter_1, name_of_parameter_2, ...) ; ; That allows functions to be called in Ano script like: ; ; function_name (\ ; name_of_parameter_1: value_1, \ ; name_of_parameter_2: value_2) ; bsd_prepare(window_handle) bsd_disperse() bsd_mixer_sel(widget_name, steps, step) bsd_mixer_rec(widget_name, steps, step) bsd_mixer_mix(widget_name, steps, step) bsd_mixer_ext(widget_name, steps, step) bsd_mixer_tst(widget_name, steps, step) bsd_mixer_tst_vol(widget_name, steps, step) bsd_mixer_tst_pan(widget_name, steps, step)
That file must be given as a parameter to @ANO_FN_NAMED_PARAMS tag in Ano script header, for example:
; ; @ANO_FN_NAMED_PARAMS "/path/to/file/my_named_funcs.txt" ;
Tag can have several files as a parameter, separated by comma.
In named parameter file, lines starting with semicolon (;) are treated as comments and ignored. Empty lines are skipped, spaces chopped, and function declarations can be spawned over multiple lines if needed, for example:
; This line is ignored function_name ( name_of_parameter_1, name_of_parameter_2)
Unveil
Unveil is a mechanism to unveil parts of a restricted filesystem access. If unveil is enabled, all access to filesystem is disabled by default. Whatever directories and/or files Ano script needs to access, they must be added to unveil list with desired permissions. This feature needs unveil(2) call to be available (at the time of writing, only OpenBSD supports it).
Each file or directory to unveil, the format is simply a directory or file name and its desired permissions separated by equals sign (=) with optional spaces around the delimiter. Each directory/file name and the permission pair must be double quoted. See unveil(2) for more information and permission characters available.
There is special characters with special meaning in directory and file paths:
- A tilde ~ as first character will be replaced by user home directory,
- At sign @ as first character will be replaced by application installation prefix (that is the value for --prefix option given to configure script),
- A dot . as first character is considered as current directory, and
- A dollar or peso sign $ as first character and a word after it is considered as environment variable.
Example:
; ; @ANO_UNVEIL_FILES "/tmp = rw", \ ; "/some/dir/somewhere = rx", \ ; "/some/dir/somewhere/and/some/file = r" ;
Datatypes
Ano supports eight different datatypes in variables, where five is most commonly used, number, string, handle, color and point. The remaining three, blob, pointer and image are reserved for special cases.
Variable type can be set when variable is first used, by setting the type along with it content. Possible types are:
- $ for blob datatype
- * for pointer datatype
- @ for handle datatype
- # for color datatype
- % for image datatype
- & for point datatype
See examples below.
Datatype can also be set when variable is declared:
var [number] x var [string] x var [blob] x var [pointer] x var [handle] x var [color] x var [image] x var [point] x
Number
Number datatype supports also RPN expressions. Expression result is cached when first evaulated if possible to avoid unnecessary evaluations in future. If expression is not cacheable, for example if it contains variables, expression is evaluated every time when instruction executes. Using math or other functions in RPN expressions is not supported. Number datatype has several subtypes:
mov x (1.0) mov x ([int8] 1) mov x ([int16] 1) mov x ([int32] 1) mov x ([int64] 1) mov x ([int128] 1) mov x ([uint8] 1) mov x ([uint16] 1) mov x ([uint32] 1) mov x ([uint64] 1) mov x ([uint128] 1) mov x ([char] 1) mov x ([short] 1) mov x ([int] 1) mov x ([long] 1) mov x ([uchar] 1) mov x ([ushort] 1) mov x ([uint] 1) mov x ([ulong] 1) mov x ([float] 1) mov x ([double] 1) mov x ((1.0 + 2.0) + 3.0 * 4.0 / y)
String
String datatype has couple of subtypes:
mov x ("Hello, world!") mov x ([utf-8] "Hello, world!") mov x ([utf-32] "Ehtoota, Vorssa!")
Blob
For special use only.
mov x ($ "file_to_load")
Pointer
For special use only.
mov x (*0)
Handle
Handle datatype is storage for function handles, for example, window_open() returns this kind of handle to pass forward to other functions, like window_close().
mov x (@0)
Color
Color datatype is storage for colors, for example, color mixing function color_mix() uses two of these to mix them together. Color datatype supports following methods:
- red, get or set the red color component
- green, get or set the green color component
- blue, get or set the blue color component
- alpha, get or set the alpha value
- brightness, get the pixel brightness
mov x ("indianred") mov x (# 0x11, 0x22, 0x33, 0x44) mov x.red (# 0x11) mov x.green (# 0x22) mov x.blue (# 0x33) mov x.alpha (# 0x44) mov b (x.brightness)
Image
For special use only. Image datatype supports following methods:
- width, get image width in pixels
- height, get image height in pixels
mov x (% "file_to_load") mov w (x.width) mov h (x.height)
Point
Point datatype is storage for three dimensional point, for example, one of the interpolation functions coords_intp_hermite_x() uses this datatype for its control points. Point datatype supports following methods:
- x, get or set the X coordinate
- y, get or set the Y coordinate
- z, get or set the Z coordinate
- angle_xy, calculate vector angle in degrees by its position, or manipulate XY coordinates by given angle
- mag_xy, calculate magnitude of 2 dimensional point, or manipulate XY coordinates by given magnitude
- mag_xyz, calculate magnitude of 3 dimensional point, or manipulate XYZ coordinates by given magnitude
mov x (& 1.1, 2.2, 3.3) mov x.x (& 1.1) mov x.y (& 2.2) mov x.z (& 3.3) mov a (x.angle_xy) mov d (x.mag_xy) mov d (x.mag_xyz) mov x.angle_xy (4.4) mov x.mag_xy (5.5) mov x.mag_xyz (6.6)
Variables
There is nothing special what comes to variables, they work just like variables in any other scripting language. Every variables is always casted to some datatype, which can be manipulated using conversion instruction set. Variable names starting with underscore (_) are treated as local variables. Local variables are visible and useable only in function where they are declared. Use of local variables is recommended where applicable.
Variables can be declared before use by using var command, or by moving data straight into it, when it is automatically declared and initialized. var alias is let, use whichever you wish.
There is following variables predefined in every Ano script:
- rc for global return value, when external function, subroutine or whatever returns something, returned data is stored here
- NULL for empty string, works just like its equivalent in C language
- INVALID for invalid handle value (equals to 0), for example, if some functions returns a handle and rc variable equals INVALID, that function returned zero
Example:
&subroutine ([float] 123) dump rc ; These are equivalents... mov string ("") dump string mov string (NULL) dump string ; ...and so are these mov handle (@0) dump handle mov handle (INVALID) dump handle end function subroutine (_value) { ; Return value is in rc variable after return ret _value * _value }
Variables supports triggers when their state or value changes. See variable triggers for further information.
Instruction set
Ano script is made from instructions. Each supported instruction useable in Ano script is explained below. Some of the subsets can be disabled to pass --disable-extended switch to configure script. If extended set is not needed, disabling it makes the exeutable bit smaller and probably saves memory a bit. Disabled instruction sets include:
- Logical instruction set,
- Math instruction set,
- Gaussian instruction set, and
- Checksum instruction set
External function call
To call external function, function name to call is divided into two by at sign (@). Left part tells the shared object where the function is located, and right part is the function name. Left part can be left empty, in that case the function is looked in calling process. There are many external functions in Detroit engine, please take a closer look of them in Engine function reference.
Flags affected: none
Example:
; Sleep for one second, this is engine's function, as there is no library name sleep (1, 0) ; ; This is abort() in libc. In order to abort(3) call to work properly, it ; return value type must be set in application's dynload_ret structure ; (dynload_ret_app.h), like this: ; ; static struct dynload_ret dynload_ret_t[] = { ; { "abort", RETURN_TYPE_VOID }, ; libc.so@abort exit
If you want to call external library functions, like the ones in libc, you need to create a wrapper function for each of them, as explained in Own functions binded to Ano section in bottom of this page. Example above will work though, as abort() does not need parameters, hence the wrapper function is not needed.
Basic instruction set
Basic set of instructions explained. Examples are for instruction demonstration only, they may or may not compile.
abs
Compute the absolute value of x. See fabs(3).
Flags affected: none
Example:
mov x (-1.0) abs x
add
Addition of two operands. The result of the addition is assigned to the first operand.
Flags affected: s, z
Example:
mov x (1.0) add x (1.0)
call or &
Call subroutine. If subroutine destination is variable, variable is evaluated when instruction is executed. See also callback & function section in this page.
Flags affected: none
Example:
mov x ("non_existent_subroutine") call x ([float] 1.0, [float] 2.0) mov x ("subroutine") call x ([float] 1.0, [float] 2.0) ; These two are equivalents call "subroutine" ([float] 1.0, [float] 2.0) &subroutine([float] 1.0, [float] 2.0) end ; This is traditional and now deprecated way to define a subroutine : "subroutine" (_param1, _param2) ret _param1 ; Subroutine should be written this way function subroutine (_param1, _param2) { ret _param1 }
cmp
Compare two operands.
Flags affected: a, b, e, s, z
Example:
mov x (1.0) cmp x (2.0)
dec
Decrement by 1. The result of the subtraction is assigned to the operand.
Flags affected: s, z
Example:
mov x (1.0) dec x
div
Divide of two operands. The result of the addition is assigned to the first operand.
Flags affected: none
Example:
mov x (1.0) div x (0.5)
end
End procedure, for example a callback function. It is possible to return code from procedure, ranging from 0 to 255, to remote caller. See Remote control for more details. Other inprocess return codes are ignored.
end ends following procedures:
- alarm (see timer snippet),
- callback (see menu and widget resource guides),
- sighandler,
- thread (see thread creation snippet), and
- main program, usually after control was passed to callback routines.
Flags affected: none
Example:
; Use default procedure return code end end (123) mov x ([int8] 123) end x
inc
Increment by 1. The result of the addition is assigned to the operand.
Flags affected: s, z
Example:
mov x (1.0) inc x
label or :
Define a label. Label is usually a jump destination, callable function, a callback function, a timer or a thread.
Flags affected: none
Example:
; This is traditional way to define a label... label "the_end" end ; ...and this is shortcut to above : "the_end" end
mod
Compute the remainder x/y. See fmod(3).
Flags affected: none
Example:
mov x (1.0) mod x (1.0)
mov
Move data by copying the second operand to the first operand.
Flags affected: none
Example:
mov x (1.0)
deliver or put
Move data by copying the second operand to the first operand. If called from thread, first operand (destination) is variable in main thread. Acts like mov in single thread application.
Flags affected: none
Example:
See fetch
fetch or get
Move data by copying the second operand to the first operand. If called from thread, second operand (source) is fetched from main thread. Acts like mov in single thread application.
Flags affected: none
Example:
mov x (1) ; Create two threads for (mov i (0); i < 2; inc i) { thread_spawn ("My thread", "my_thread") } ; Wait for a while for threads to complete sleep (1, 0) ; This outputs 3 which comes from either thread dump result end thread my_thread { mov x ("invalidate") ; This outputs either 2 or 3, x is initialized in main thread, ; and i is the main thread loop counter fetch y (x + i) dump y ; Deliver y to main thread deliver result (y) }
mul
Multiplication of two operands. The result of the multiplication is assigned to the first operand.
Flags affected: none
Example:
mov x (1.0) mul x (1.0)
neg
Two's complement negation. The operand is subtracted from zero and result is assigned in the operand.
Flags affected: s, z
Example:
mov x (1.0) neg x
not
One's complement negation.
Flags affected: s, z
Example:
mov x (1.0) not x
pow
Compute x raised to the power y. See pow(3).
Flags affected: none
Example:
mov x (1.0) pow x (1.0)
rand
Generate random number. The result is assigned to the first operand.
Flags affected: none
Example:
rand x (10)
ret
Return from subroutine to next instruction after the call. ret can have optional return value, which is stored in internal variable rc after the return.
Flags affected: none
Example:
&subroutine1([float] 1.0, [float] 2.0) &subroutine2([float] 3.0, [float] 4.0) dump rc end function subroutine1 (_param1, _param2) { ret } function subroutine2 (_param1, _param2) { ret _param1 * _param2 }
sub
Subtraction of two operands. The result of the subtraction is assigned to the first operand.
Flags affected: s, z
Example:
mov x (1.0) sub x (1.0)
test
Logical compare.
Flags affected: s, z
Example:
mov x (1.0) test x (1.0)
Logical instruction set
Logical operations are done in 64 bit resolution and are useable with non-floating number formats. Examples are for instruction demonstration only, they may or may not compile.
and
Logical AND.
Flags affected: s, z
Example:
mov x (1.0) and x (1.0)
or
Logical inclusive OR.
Flags affected: s, z
Example:
mov x (1.0) or x (1.0)
xor
Logical exclusive OR.
Flags affected: s, z
Example:
mov x (1.0) xor x (1.0)
Branching instruction set
If jump destination label is variable, variable is evaluated when instruction is executed. Examples are for instruction demonstration only, they may or may not compile.
ja
Jump if condition flag a is set.
Example:
mov x (1.0) cmp x (2.0) ja "the_end" end : "the_end" end
jae
Jump if condition flag a or e is set.
Example:
mov x (1.0) cmp x (2.0) jae "the_end" end : "the_end" end
jb
Jump if condition flag b is set.
Example:
mov x (1.0) cmp x (2.0) jb "the_end" end : "the_end" end
jbe
Jump if condition flag b or e is set.
Example:
mov x (1.0) cmp x (2.0) jbe "the_end" end : "the_end" end
je
Jump if condition flag e is set.
Example:
mov x (1.0) cmp x (2.0) je "the_end" end : "the_end" end
jne
Jump if condition flag e is not set.
Example:
mov x (1.0) cmp x (2.0) jne "the_end" end : "the_end" end
js
Jump if condition flag s is set.
Example:
mov x (1.0) sub x (2.0) js "the_end" end : "the_end" end
jns
Jump if condition flag s is not set.
Example:
mov x (1.0) sub x (2.0) jns "the_end" end : "the_end" end
jz
Jump if condition flag z is set.
Example:
mov x (1.0) dec x jz "the_end" end : "the_end" end
jnz
Jump if condition flag z is not set.
Example:
mov x (1.0) dec x jnz "the_end" end : "the_end" end
jmp
Unconditional jump.
Example:
jmp "the_end" end : "the_end" end
Condition flag instruction set
All condition flags are automatically cleared at application startup. Examples are for instruction demonstration only, they may or may not compile.
clall
Clear condition flags a, b, e, s, z.
Example:
clall
cla
Clear condition flag a.
Example:
cla
clb
Clear condition flag b.
Example:
clb
cle
Clear condition flag e.
Example:
cle
cls
Clear condition flag s.
Example:
cls
clz
Clear condition flag z.
Example:
clz
stall
Set condition flags a, b, e, s, z.
Example:
stall
sta
Set condition flag a.
Example:
sta
stb
Set condition flag b.
Example:
stb
ste
Set condition flag e.
Example:
ste
sts
Set condition flag s.
Example:
sts
stz
Set condition flag z.
Example:
stz
Math instruction set
Math instructions explained. Examples are for instruction demonstration only, they may or may not compile.
acos
Computes the principle value of the arc cosine of x. See acos(3).
Flags affected: none
Example:
acos n (1.0)
acosh
Computes the principle value of the inverse hyperbolic cosine of x. See acosh(3).
Flags affected: none
Example:
acosh n (1.0)
asin
Computes the principal value of the arc sine of x. See asin(3).
Flags affected: none
Example:
asin n (1.0)
asinh
Computes the principle value of the inverse hyperbolic sine of x. See asinh(3).
Flags affected: none
Example:
asinh n (1.0)
atan
Computes the principle value of the arc tangent of x. See atan(3).
Flags affected: none
Example:
atan n (1.0)
atanh
Computes the principle value of the inverse hyperbolic tangent of x. See atanh(3).
Flags affected: none
Example:
atanh n (1.0)
atan2
Computes the principal value of the arc tangent of y/x. See atan2(3).
Flags affected: none
Example:
atan n (1.0, 2.0)
cbrt
Computes the cube root of x. See cbrt(3).
Flags affected: none
Example:
cbrt n (1.0)
ceil
Return the smallest integral value greater than or equal to x. See ceil(3).
Flags affected: none
Example:
ceil n (1.1)
copysign
Return x with its sign changed to y's. See copysign(3).
Flags affected: none
Example:
copysign n (1.0, -1.0)
cos
Computes the cosine of x. See cos(3).
Flags affected: none
Example:
cos n (1.0)
cosh
Computes the hyperbolic cosine of x. See cosh(3).
Flags affected: none
Example:
cosh n (1.0)
dim
Return the positive difference between their arguments: x - y if x > y, +0 if x is less than or equal to y. Might not be supported on every platform. See fdim(3).
Flags affected: none
Example:
dim n (1.0, 2.0)
erf
Calculates the complementary error function of x. See erf(3).
Flags affected: none
Example:
erf n (1.0)
erfc
Calculates the complementary error function of x. See erfc(3).
Flags affected: none
Example:
erfc n (1.0)
exp
Computes e**x, the base-e exponential of x. See exp(3).
Flags affected: none
Example:
exp n (1.0)
expm1
Computes e**x, the base-e exponential of x. See expm1(3).
Flags affected: none
Example:
expm1 n (1.0)
exp2
Computes 2**x, the base-2 exponential of x. Might not be supported on every platform. See exp2(3).
Flags affected: none
Example:
exp2 n (1.0)
floor
Return the largest integral value less than or equal to x. See floor(3).
Flags affected: none
Example:
floor n (1.1)
fma
Compute (x*y)+z, rounded as one ternary operation. Might not be supported on every platform. See fma(3).
Flags affected: none
Example:
fma n (1.0, 2.0, 3.0)
frexp
Break the floating-point number value into a normalized fraction and an integral power of 2. See frexp(3).
Flags affected: none
Example:
frexp f (1.2, i)
hypot
Computes the sqrt(x*x+y*y) without undue overflow or underflow. See hypot(3).
Flags affected: none
Example:
hypot n (1.0, 2.0)
ilogb
Return the exponent of x as a signed integer value. Might not be supported on every platform. See ilogb(3).
Flags affected: none
Example:
ilogb n (1.0)
isfinite
Return non-zero if and only if x is a finite number. Might not be supported on every platform. See isfinite(3).
Flags affected: s, z
Example:
isfinite n (1.0)
isinf
Return non-zero if and only if x is an infinity. Might not be supported on every platform. See isinf(3).
Flags affected: s, z
Example:
isinf n (1.0)
isnan
Return non-zero if and only if x is a NaN. See isnan(3).
Flags affected: s, z
Example:
isnan n (1.0)
isnormal
Return non-zero if and only if x is a non-zero normalized number. Might not be supported on every platform. See isnormal(3).
Flags affected: s, z
Example:
isnormal n (1.0)
jn
Computes the Bessel function of the first kind of the integer order n. See jn(3).
Flags affected: none
Example:
jn n (1.0, 2.0)
j0
Compute the Bessel function of the first kind of the order 0. See j0(3).
Flags affected: none
Example:
j0 n (1.0)
j1
Compute the Bessel function of the first kind of the order 1. See j1(3).
Flags affected: none
Example:
j1 n (1.0)
ldexp
Multiply x by 2 to the power n. See ldexp(3).
Flags affected: none
Example:
ldexp n (1.0, [int] 2)
lgamma
Calculates the natural logarithm of the absolute value of the gamma function of x. Might not be supported on every platform. See lgamma(3).
Flags affected: none
Example:
lgamma n (1.0)
log
Computes the value of the natural logarithm of argument x. See log(3).
Flags affected: none
Example:
log n (1.0)
logb
Return the exponent of x. See logb(3).
Flags affected: none
Example:
logb n (1.0)
log1p
Computes the value of the natural logarithm of argument x. See log1p(3).
Flags affected: none
Example:
log1p n (1.0)
log2
Computes the value of the logarithm of argument x to base 2. Might not be supported on every platform. See log2(3).
Flags affected: none
Example:
log2 n (1.0)
log10
Computes the value of the logarithm of argument x to base 10. See log10(3).
Flags affected: none
Example:
log10 n (1.0)
max
Return x or y, whichever is larger. See fmax(3).
Flags affected: none
Example:
max n (1.0, 2.0)
min
Return x or y, whichever is smaller. See fmin(3).
Flags affected: none
Example:
min n (1.0, 2.0)
modf
Break value into integral and fractional parts, each of which has the same sign as the argument. See modf(3).
Flags affected: none
Example:
modf f (1.2, i)
nan
Return a quiet NaN. Might not be supported on every platform. See nan(3).
Flags affected: none
Example:
nan n (0)
nearbyint
Return the integral value nearest to x according to the prevailing rounding mode. Might not be supported on every platform. See nearbyint(3).
Flags affected: none
Example:
nearbyint n (1.1)
nextafter
Return the next machine representable number from x in direction y. Might not be supported on every platform. See nextafter(3).
Flags affected: none
Example:
nextafter n (1.1, 2.0)
nexttoward
Return the next machine representable number from x in direction of y. Might not be supported on every platform. See nexttoward(3).
Flags affected: none
Example:
nexttoward n (1.1, 2.0)
remainder
Compute the value r such that r = x - n*y, where n is the integer nearest the exact value of x/y. Might not be supported on every platform. See remainder(3).
Flags affected: none
Example:
remainder n (1.0, 2.0)
rint
Return the integral value nearest to x. See rint(3).
Flags affected: none
Example:
rint n (1.1)
round
Return the integral value nearest to x rounding half-way cases away from zero, regardless of the current rounding direction. See round(3).
Flags affected: none
Example:
round n (1.1)
scalbn
Return x*(2**n) computed by exponent manipulation. Might not be supported on every platform. See scalbn(3).
Flags affected: none
Example:
scalbn n (1.0, [int] 2)
signbit
Returns non-zero if the value of the argument's sign is negative, otherwise 0. Might not be supported on every platform. See signbit(3).
Flags affected: s, z
Example:
signbit n (1.0)
sin
Computes the sine of x. See sin(3).
Flags affected: none
Example:
sin n (1.0)
sinh
Computes the hyperbolic sine of x. See sinh(3).
Flags affected: none
Example:
sinh n (1.0)
sqrt
Compute the non-negative square root of x. See sqrt(3).
Flags affected: none
Example:
sqrt n (1.0)
tan
Computes the tangent of x. See tan(3).
Flags affected: none
Example:
tan n (1.0)
tanh
Computes the hyperbolic tangent of x. See tanh(3).
Flags affected: none
Example:
tanh n (1.0)
tgamma
Calculates the gamma function of x. Might not be supported on every platform. See tgamma(3).
Flags affected: none
Example:
tgamma n (1.0)
trunc
Return the integral value nearest to but no larger in magnitude than x. Might not be supported on every platform. See trunc(3).
Flags affected: none
Example:
trunc n (1.1)
yn
Computes the Bessel function of the second kind for the integer order n for the positive integer value x. See yn(3).
Flags affected: none
Example:
yn n (1.0, 2.0)
y0
Compute the linearly independent Bessel function of the second kind of the order 0. See y0(3).
Flags affected: none
Example:
y0 n (1.0)
y1
Compute the linearly independent Bessel function of the second kind of the order 1. See y1(3).
Flags affected: none
Example:
y1 n (1.0)
Gaussian instruction set
Examples are for instruction demonstration only, they may or may not compile.
gdens
Compute Gaussian density of x.
Flags affected: none
Example:
gdens n (1.0)
gdist
Compute Gaussian distribution of x.
Flags affected: none
Example:
gdist n (1.0)
grand
Return Gaussian distributed random number of distribution x and sigma y.
Flags affected: none
Example:
grand n (1.0, 2.0)
Checksum instruction set
Examples are for instruction demonstration only, they may or may not compile.
crc32
Compute CRC32 checksum of x.
Flags affected: none
Example:
crc32 n ("Hello, world!")
crc64
Compute CRC64 checksum of x.
Flags affected: none
Example:
crc64 n ("Hello, world!")
Conversion instruction set
Examples are for instruction demonstration only, they may or may not compile.
ston
Convert string to number.
Flags affected: none
Example:
mov x ("1.0") ston x
bton
Convert blob to number.
Flags affected: none
Example:
mov x ($ "blob") bton x
pton
Convert pointer to number.
Flags affected: none
Example:
mov x (*0) pton x
hton
Convert handle to number.
Flags affected: none
Example:
mov x (@0) hton x
ntos
Convert number to string.
Flags affected: none
Example:
mov x (1.0) ntos x
ntob
Convert number to blob.
Flags affected: none
Example:
mov x (1.0) ntob x
ntop
Convert number to pointer.
Flags affected: none
Example:
mov x (1.0) ntop x
ntoh
Convert number to handle.
Flags affected: none
Example:
mov x (1.0) ntop x
toi8
Set number type to 8 bit integer.
Flags affected: none
Example:
mov x (1.0) toi8 x
toi16
Set number type to 16 bit integer.
Flags affected: none
Example:
mov x (1.0) toi16 x
toi32
Set number type to 32 bit integer.
Flags affected: none
Example:
mov x (1.0) toi32 x
toi64
Set number type to 64 bit integer.
Flags affected: none
Example:
mov x (1.0) toi64 x
toi128
Set number type to 128 bit integer. Might not be supported on every platform.
Flags affected: none
Example:
mov x (1.0) toi128 x
tou8
Set number type to 8 bit unsigned integer.
Flags affected: none
Example:
mov x (1.0) tou8 x
tou16
Set number type to 16 bit unsigned integer.
Flags affected: none
Example:
mov x (1.0) tou16 x
tou32
Set number type to 32 bit unsigned integer.
Flags affected: none
Example:
mov x (1.0) tou32 x
tou64
Set number type to 64 bit unsigned integer.
Flags affected: none
Example:
mov x (1.0) tou64 x
tou128
Set number type to 128 bit unsigned integer. Might not be supported on every platform.
Flags affected: none
Example:
mov x (1.0) tou128 x
tochar
Set number type to char.
Flags affected: none
Example:
mov x (1.0) tochar x
toshort
Set number type to short.
Flags affected: none
Example:
mov x (1.0) toshort x
toint
Set number type to int.
Flags affected: none
Example:
mov x (1.0) toint x
tolong
Set number type to long.
Flags affected: none
Example:
mov x (1.0) tolong x
touchar
Set number type to unsigned char.
Flags affected: none
Example:
mov x (1.0) touchar x
toushort
Set number type to unsigned short.
Flags affected: none
Example:
mov x (1.0) toushort x
touint
Set number type to unsigned int.
Flags affected: none
Example:
mov x (1.0) touint x
toulong
Set number type to unsigned long.
Flags affected: none
Example:
mov x (1.0) toulong x
tofloat
Set number type to float.
Flags affected: none
Example:
mov x (1.0) tofloat x
todouble
Set number type to double.
Flags affected: none
Example:
mov x (1.0) todouble x
Supplemental instruction set
Examples are for instruction demonstration only, they may or may not compile.
dump
Print parameter type and content. For debug use only.
Flags affected: none
Example:
mov x (1.0) dump x
Evaluates and prints given parameter string to standard output. Some special characters can be used with the string. Those include:
- \0 for string termination,
- \a bell,
- \b backspace,
- \t horizontal tab,
- \n line feed,
- \v vertical tab,
- \f form feed, and
- \r for carriage return.
Flags affected: none
Example:
mov x (1.0) ; Prints content of x with and without newline print x . "\n" print x ; Evaluates an expression and prints it with the string print (x - 0.5) . " is halfway between\n" print "The result is: " . (x + 0.5) . "\n" print "The value is: " . x . "\n" print 123 . " is very odd number." "\n" ; Prints three separated strings with newline print "The first, " "the second, " "and the last." "\n" ; Prints x three times without newline print x . "" . x .. x ; Prints string with empty expression with newline print "This is the string" .. "\n" mov y (123) mov x ("y + 1") ; Prints string without evaluation ; outputs: y + 1 print "x is considered as string here: " . x . "\n" ; Prints and evaluates x ; outputs: 124 print x print "\n" mov x (1.0) mov y ("'Value is ' . (1.0 + x - 0.5) . ''") ; These does not evaluate expression... ; outputs: 'Value is ' . (1.0 + x - 0.5) . '' print "" . y . "\n" inc x print "" . y . "\n" ; ...but these does ; outputs: Value is 2.5 print y print "\n" inc x ; outputs: Value is 3.5 print y print "\n"
Condition flags
Almost all instructions changes condition flags according to instruction result. Condition flags affects the behaviour of branching instructions, and can be manipulated by using condition flag instruction set.
- A is set if first operand is greater, clear if its not.
- B is set if second operand is greater, clear if its not.
- E is set if operands are equal, clear if they are not.
- S is set if operand is signed, clear if its unsigned.
- Z is set if operand is zero, clear if its not.
High level statements
There is some high level statements and keywords available to write more tight or readable script. High level operands are exploded to smaller pieces by Ano compiler. In the end, they are a selection of Ano instructions, not real instructions by themselves.
Some of the statements has condition in it, for example for and while. Only single condition is supported at the moment.
; This does not work... while (i < 10 && j > 10) { } ; ...but this does while (i < 10) { }
High level statements supports nesting.
for
⭐ New in 0.2.6.
A for statement is somewhat similar than equally named statement in C language. for syntax is:
for (init statement ; condition ; loop counter statement) { }
Init statement usually initializes the loop counter variable, and it runs always only once. Condition tests if the loop must be stopped or not, and loop counter statement usually increases or decreases the loop counter variable. Any of the three statements can be left empty. Init and loop counter parts can have multiple statements separated by comma. For example, this loop runs ten times:
for (mov i (0); i < 10; inc i) { dump i }
Above loop can be written in plain Ano script this way (and that's what Ano compiler actually does):
; Init statement mov i (0) : "loop" ; Loop counter statement inc i ; Condition cmp i (10) jb "loop_payload" jmp "loop_done" : "loop_payload" ; Loop payload dump i ; Loop back to begin jmp "loop" : "loop_done"
Unlike loop statement, with for it is possible to make endless loop, just like with its C equivalent. The following example makes a loop that never stops. Stopping the loop is still possible by using break keyword, or any other branching instruction to escape from the loop.
; This loop is endless for (;;) { }
loop
⭐ New in 0.2.6.
A loop statement looks a bit like for(), but works a little bit differently. loop always runs the content of the loop at least once. The check whether the loop must stop or not is done at the end of the loop, after running the loop payload. loop contains three statements, just like for():
loop (init statement ; condition ; loop counter statement) { }
Init statement usually initializes the loop counter variable, and it runs always only once. Condition tests if the loop must be stopped or not, and loop counter statement usually increases or decreases the loop counter variable. Any of the three statements can be left empty. Init and loop counter parts can have multiple statements separated by comma. For example, this loop runs ten times:
loop (mov i (0); i < 10; inc i) { dump i }
Above loop can be written in plain Ano script like this:
; Init statement mov i (0) : "loop" ; Loop payload dump i ; Loop counter statement inc i ; Condition cmp i (10) jb "loop"
If condition part is left empty, loop differs from for() statement; with loop, you cannot make endless loop like shown below, which will run the loop payload once. loop always needs condition statement in order it to roll.
; This loop is not endless, it runs once loop (;;) { }
while
⭐ New in 0.2.6.
A while loop keeps executing a block of instructions as long as condition is true. while contains one statement, the condition, which checks if the loop must be stopped:
while (condition) { }
For example, this loop runs ten times:
mov i (0) while (i < 10) { dump i inc i }
Above loop can be written in plain Ano script as here:
mov i (0) : "loop" ; Condition cmp i (10) jb "loop_payload" jmp "loop_done" : "loop_payload" ; Loop payload dump i inc i ; Loop back to begin jmp "loop" : "loop_done"
If condition is left empty, while creates an endless loop. Stopping the loop is still possible by using break keyword, or any other branching instruction to escape from the loop.
do
⭐ New in 0.2.6.
A do loop is similar to while loop, except it executes the loop payload at least once. The check whether the loop must stop or not is done at the end of the loop, after running the loop payload. do statement contains condition, which checks if the loop must be stopped:
do (condition) { }
For example, this loop runs ten times:
mov i (0) do (i < 10) { dump i inc i }
Above loop can be written in plain Ano script as shown in this example:
mov i (0) : "loop" ; Loop payload dump i inc i ; Condition cmp i (10) jb "loop"
If condition is left empty, do has no practical effect, as it just runs the loop payload once.
define
⭐ New in 0.2.8.
A define keyword defines a macro. Macros are like typeless source variables. When macro is used, it is evaluated and gets proper type. Macros can be redefined when needed, and undefined by using empty value.
define message ("Macro example") define expression (1 + (2 + 3) * multiplier) ; Just print message out, this macro is like constant string mov _msg (message) dump _msg ; Expression in macro is evaluated when macro is moved to _msg mov multiplier (1) mov _msg (expression) ; _msg has a value of 6 dump _msg ; mov multiplier (2) mov _msg (expression) ; _msg has a value of 11 dump _msg ; mov multiplier (4) mov _msg (expression) ; _msg has a value of 21 dump _msg ; Undefine macro define expression () ; This causes runtime error as macro is undefined now... ;mov _msg (expression) ; ...but macro can be redefined when needed define expression (_msg + 1) mov _msg (expression) ; _msg has a value of 22 dump _msg exit
Macros does not support recursion, this example won't work and causes an error:
define expression (1 + (2 + 3) * expression) mov _msg (expression)
synchronized
⭐ New in 0.2.7.
Synchronization is made for thread safe computing to prevent multiple threads accessing the same resource at the same time, which could produce unwanted results. Synchronization is achieved by using synchronized blocks. Only one thread can access and run instructions and functions in this block at a time.
There is attributes available to use with synchronized statement:
- oid, the object identifier, attribute sets the identifier for each synchronized block. oid type is always positive integer (cast to [int]). Keep the oid number as small as possible.
If oid attribute is left out, default value of 0 will be used.
Syncronization example snippets:
synchronized [oid: 1] { ; Do serialized work here } synchronized [oid: 1] { ; When some thread is working in synchronization block above, this ; block is also serialized as oid number is the same } synchronized { ; This block uses oid 0 by default synchronized [oid: 2] { ; This block is nested } }
As seen above, synchronized statements can be nested, if needed for some obscure reason.
More practical example, using synchronized statement with threads:
mov counter (1) thread_spawn ("First thread", "my_thread") thread_spawn ("Second thread", "my_thread") thread_spawn ("Third thread", "my_thread") mov _msg ("Press <ctrl-c> to exit") dump _msg end thread my_thread { ; Do parallel work here... synchronized { ; ...do some serialized work... rand _nap (3) sleep (_nap + 1, 0) dump counter inc counter } ; ...and return doing things in parallel here }
alarm & thread
⭐ New in 0.2.7.
Alarm, or timer, can be set by using timer function available in Detroit engine. Traditional and now deprecated way to set the timer is:
; Set alarm to trigger one second from now timer (1, 0, "my_timer) end : "my_timer" mov _msg ("Hello from timer!") dump _msg end
Timer should be set this way using alarm statement:
; Set alarm to trigger one second from now timer (1, 0, "my_timer) end alarm my_timer { mov _msg ("Hello from timer!") dump _msg }
If alarm function needs to end abruptly, always use end instruction to end it, never use ret instruction with alarm. There is some attributes available for alarm statement:
- respawn sets the new timeout for the timer. Immediate value or variable is supported. If variable or RPN expression is used, it must be placed inside parentheses.
- repeat sets the repeat count for the timer. Immediate value or variable is supported. If variable or RPN expression is used, it must be placed inside parentheses.
Example:
alarm my_timer [respawn: 10:20] { ; This timer respawnds continuously at 10 second and ; 20 nanosecond interval } alarm my_timer [respawn: 5, repeat: 10] { ; This timer spawns 10 times at 5 second interval } mov repeat_count (10) alarm my_timer [repeat: (repeat_count + 1)] { ; This timer respawns eleven times using original timer interval }
Timer can be stopped by calling timer function again with a time of 0.
Thread can be spawned by using thread_spawn function available in Detroit engine. Traditional and now deprecated way to spawn a thread is:
thread_spawn ("Ted the thread", "my_thread") end : "my_thread" mov _msg ("Hello from thread!") dump _msg end
Thread should be declared this way by using thread statement:
thread_spawn ("Ted the thread", "my_thread") end thread my_thread { mov _msg ("Hello from thread!") dump _msg }
If thread needs to end abruptly, always use end instruction to end it, never use ret instruction with threads. There is some attributes available for thread statement:
- stacksize sets the stack size in bytes for the thread.
- guardsize sets the guard size in bytes for thread's stack memory.
Example:
thread my_thread_a [stacksize: 4096] { ; This thread has stack size of 4096 bytes } thread my_thread_b [stacksize: 4096, guardsize: 1024] { ; This thread has stack size of 4096 bytes, and ; one kilobyte block guarding the stack }
callback & function
⭐ New in 0.2.7.
Callback functions are like subroutines, often plugged in menu items for example. When menu item is selected, that item callback function is called. See menu resources and widget resources for more information about their callback functions.
If callback function needs to return something, it must always use end instruction with return code instead of ret. Never use ret with callback functions.
; This is traditional and now deprecated way to define a callback function, ; note the end instruction which is mandatory here : "my_cb" (_item, _position, _tag, _flag) dump _item dump _position dump _tag dump _flag end ; Callback function should be written this way, ; end instruction is needed only if this callback returns something, ; or it ends abruptly callback my_cb (_item, _position, _tag, _flag) { dump _item dump _position dump _tag dump _flag }
Functions (also known as subroutines) are callable by using call instruction. They differ from callbacks that they use different return mechanism. Because of that, ret instruction is needed if subroutine needs to return something to caller. Never use end with subroutines.
; This is traditional and now deprecated way to define a subroutine : "my_subroutine" (_param1, _param2) dump _param1 dump _param2 ret ; Subroutine should be written this way function my_subroutine (_param1, _param2) { dump _param1 dump _param2 } ; This subroutine returns the sum of two params function my_subroutine (_param1, _param2) { ret _param1 + _param2 }
main & finalize
⭐ New in 0.2.9.
The entry point of a program where the execution starts can be wrapped in main block, just like in C programming language for example. Main block can have optional exit attribute with exit code in it. If exit attribute is not specified, main block just ends and passes control to possible window callback functions.
; ; Execution starts in main block below, so any instruction ; written before main block are defunct. ; main { print "Execution starts here.\n" }
When there is no exit attribute, main block termination behaves like end instruction at the end of the block.
main { print "This is main block which does not exit, useful " \ "when control is passed to callback functions.\n" print "Press <ctrl-c> to exit.\n" }
This program exits with exit code of zero:
main [exit: 0] { print "This is main block which exits automatically with " \ "exit code of zero.\n" }
If finalize block is defined, it is called automatically on exit:
main [exit: 0] { print "This is main block which exits automatically and " \ "spawns finalize block as it is defined below.\n" } function my_function { ; This spawns finalize block too. exit } finalize { print "This block is started automatically when exit function " \ "is called. This block does not pass to control to " \ "main block anymore." ; Calling exit function here does not have any practical effect. ; exit }
Main block can also use variable to specify the exit code, which can of course to change in runtime:
main [exit: (_retval)] { print "This is main block which exits with exit code of 123.\n" mov _retval (123) }
switch/case/default
⭐ New in 0.2.7.
A switch/case/default statement is similar than in more popular languages, but it has some divergence, as shown in nonsense example below. The basic idea is that switch keyword allows a variable to be tested against a list of values or other variables, called cases. switch block is declared as follows:
switch (variable) { ; Colon after expression is optional case expression : ; What to do if this case is true ; break or next is optional break case expression : ; What to do if this case is true ; next or break is optional next default : ; What to do if none of the above cases was true ; break or next is optional break }
The nonsense but working example:
mov i (1) mov j (2) mov k (3) switch (i) { case "Hello, world!": mov j (2) ; This falls through to next case, as there is no break case 1: mov j (1) break case 2: mov j (3) ; This jumps back to first case and starts over next case k: mov j (4) break default: }
Unlike in C and other languages, default keyword is mandatory after case keywords. A next loop control keyword can be used in place of break, which will start the case roulette all over again. A break loop control keyword will break the switch statement as normal. A colon after case keyword can be left out, it is not mandatory.
try/catch/finally
⭐ New in 0.2.7.
A try/catch/finally statement is similar than in some other programming languages. The statement consists of try block with zero or more catch keywords in it, which specify handlers for different return codes from function(s) called in try block. Instructions in finally block always executes before control flow exits from try block. Unlike some other languages, finally block is mandatory after catch keywords, even when it is empty. A colon after catch and finally keywords can be left out, it is not mandatory.
try/catch/finally is declared as follows:
try { function_to_be_tested (...) ... &function_to_be_tested (...) ... catch : catch (optional_expression) : finally : }
For example:
try { ; Get the return value in rc variable &my_function([int] 123) ; There can be some other code as well catch (123): ; my_function failed once catch (rc <= 123): ; my_function failed twice finally: } exit function my_function (_value) { ret _value }
try block can have optional variable which value to check. If it is not specified, the default rc variable is used. If variable value is zero when function to be tested returns, control flow is passed directly to finally block, and if it is non-zero, control flow is passed to first catch block if there is any. If catch keyword does not contain an expression or condition in it, variable to be tested is tested against [int] 0 value.
An example of try statement with use of optional variable:
mov my_val ([int] 0) try my_val { &my_function ; These two catches are equal, test my_val instead of rc catch: catch (1): ; Function set my_val to one, which means error finally: } exit function my_function { mov my_val (1) }
As said, catch is an optional part of try block. catch can have optional expression or condition with it. Expression is just single value. When that value is compared, and if true, control flow is passed in that catch block. Condition is similar than for loop condition statement. For example:
try { catch: catch (1): catch (rc == 10): catch (rc != 10): catch (rc <= 10): catch (rc >= 10): catch (rc < 10): catch (rc > 10): finally: }
A finally block is mandatory even when it is not needed. Instructions in finally block will always be executed before control flow exits from try block, even when using break keyword.
It is possible to pass the control flow back to first catch block by using throw keyword. throw is like unconditional jump, and throw keyword can have optional parameter to pass to catches. For example:
try { mov rc (1) catch (1): ; rc is 1 by default, so this block is catched throw (2) catch (2): ; When rc is 2, this block is catched throw (3) finally: }
Loop control keywords, break and next, can be used in try/catch/finally block.
- break jumps directly at the start of finally block.
- next jumps directly at the start of try block, running functions to be tested again.
Loop control
To control loops when they run, there is two keywords available for that. Both of these keywords are only useable inside the high level statements discussed above.
- break stops the loop immediately. Its like an unconditional jump out from the loop.
- next jumps straight to next iteration of the loop, and if condition check does not stop the loop, it starts over.
To clarify the logic with small example:
; Init statement mov i (0) : "loop" ; The start of the loop ; Loop payload dump i ; Loop counter statement, next jumps here inc i ; Check statement cmp i (10) jb "loop" ; The end of the loop, break jumps here
It is safe to use any other branching instruction to achieve the same effect than using the loop control keywords listed above. They are just shortcuts to branching instructions for more convenient use.
Conditional tests
⭐ New in 0.2.8.
These oneliner shortcuts helps to write more tight Ano script. Simple tests with rc variable can be squeezed to one line as listed below.
Conditional tests for jumping to label:
- if_null, test if rc variable is NULL and jump to
label. Example:
; Jump to label_to_jump if rc is NULL if_null label_to_jump
cmp rc (NULL) je "label_to_jump"
- unless_null, test if rc variable is not NULL and jump to label.
- if_invalid, test if rc variable is INVALID and jump to label.
- unless_invalid, test if rc variable is not INVALID and jump to label.
- if_zero, test if rc variable is 0 and jump to label.
- unless_zero, test if rc variable is not 0 and jump to label.
Conditional tests for calling subroutine instead of jumping to label:
- call_if_null, test if rc variable is NULL and call
subroutine. Example:
; Call function_to_call subroutine if rc is NULL call_if_null function_to_call
cmp rc (NULL) jne "rc_is_not_null" call "function_to_call" : "rc_is_not_null"
- call_unless_null, test if rc variable is not NULL and call subroutine.
- call_if_invalid, test if rc variable is INVALID and call subroutine.
- call_unless_invalid, test if rc variable is not INVALID and call subroutine.
- call_if_zero, test if rc variable is 0 and call subroutine.
- call_unless_zero, test if rc variable is not 0 and call subroutine.
Conditional tests for returning from function. These shortcuts accepts optional return code as a parameter. Default return code is zero if not set.
- ret_if_null, test if rc variable is NULL and return
from function. Example:
; Return from this function if rc is NULL ret_if_null ; Optional return code can also be set: ret_if_null 123
cmp rc (NULL) jne "rc_is_not_null" ret : "rc_is_not_null"
- ret_unless_null, test if rc variable is not NULL and return from function.
- ret_if_invalid, test if rc variable is INVALID and return from function.
- ret_unless_invalid, test if rc variable is not INVALID and return from function.
- ret_if_zero, test if rc variable is 0 and return from function.
- ret_unless_zero, test if rc variable is not 0 and return from function.
Conditional tests for ending callback. These shortcuts accepts optional end code as a parameter. Default end code is zero if not set.
- end_if_null, test if rc variable is NULL and end
callback. Example:
; End this callback if rc is NULL end_if_null ; Optional end code can also be set: end_if_null 123
cmp rc (NULL) jne "rc_is_not_null" end : "rc_is_not_null"
- end_unless_null, test if rc variable is not NULL and end callback.
- end_if_invalid, test if rc variable is INVALID and end callback.
- end_unless_invalid, test if rc variable is not INVALID and end callback.
- end_if_zero, test if rc variable is 0 and end callback.
- end_unless_zero, test if rc variable is not 0 and end callback.
Conditional tests for exiting the program. These shortcuts accepts optional exit code as a parameter. Default exit code is zero if not set.
- exit_if_null, test if rc variable is NULL and exit
program. Example:
; Exit program if rc is NULL exit_if_null ; Optional exit code can also be set: exit_if_null 1
cmp rc (NULL) jne "rc_is_not_null" exit : "rc_is_not_null"
- exit_unless_null, test if rc variable is not NULL and exit program.
- exit_if_invalid, test if rc variable is INVALID and exit program.
- exit_unless_invalid, test if rc variable is not INVALID and exit program.
- exit_if_zero, test if rc variable is 0 and exit program.
- exit_unless_zero, test if rc variable is not 0 and exit program.
Subsystem tests
⭐ New in 0.2.9.
Configure script has switches to disable or enable certain features and subsystems with --disable and --enable switches. For example debugging can be enabled with --enable-debug switch, and GUI subsystem can be disabled with --disable-gui switch. In some cases it may be handy to test those flags in Ano script.
If debugging is enabled (that is, if engine/Makefile has -DPROG_HAS_DEBUG switch for C compiler) instructions inside debug_enabled block are executed, otherwise they are ignored:
debug_enabled { ; Do something if debugging is enabled with --enable-debug ; configure switch. }
debug_enabled block supports level attribute to check the debug level. For example, if engine/Makefile has -DPROG_HAS_DEBUG=5 switch for C compiler, following block of code would execute:
debug_enabled [level: 5] { ; Do something if debugging is enabled with --enable-debug ; configure switch and it has value of 5. }
Existence of each subsystem in Detroit engine can be checked this way:
audio_enabled { ; ...do something if audio subsystem is enabled... } bob_enabled { ; ...do something if bob subsystem is enabled... } draw_enabled { ; ...do something if drawing subsystem is enabled... } gui_enabled { ; ...do something if gui subsystem is enabled... } input_enabled { ; ...do something if input driver subsystem is enabled... } menu_enabled { ; ...do something if menu subsystem is enabled... } remote_enabled { ; ...do something if remote control subsystem is enabled... } widget_enabled { ; ...do something if widget subsystem is enabled... }
Signal handlers
⭐ New in 0.2.8.
A signal can report some more or less unexpected behavior to program. Each signal delivered can be catched by declaring a simple sighandler block. Block needs no further initialization. sighandler block syntax is:
sighandler signal { }
Where signal is one of:
- hup for catching HUP (hangup) signal,
- int for catching INT (interrupt) signal,
- term for catching TERM (software termination from kill) signal, and
- quit for catching QUIT signal.
Keep signal handlers as small as possible. A couple of sighandler examples:
; Do not declare new variables in signal handlers, declare needed ; ones in advance. mov msg (NULL) ; As there is no exit() call, program just sits here until <ctrl-c> ; is pressed. When done so, sighandler for int signal is ; automatically called. end sighandler hup { ; This handler is called when HUP (hangup) signal is received mov msg ("SIGHUP here!") dump msg ; exit() does not have any practical effect in signal handler, ; as program is already shutting down ;exit } sighandler int { ; This handler is called when INT (interrupt) signal is received, ; for example when pressing <ctrl-c> mov msg ("SIGINT here!") dump msg } sighandler term { ; This handler is called when TERM (software termination from kill) ; signal is received mov msg ("SIGTERM here!") dump msg } sighandler quit { ; This handler is called when QUIT signal is received mov msg ("SIGQUIT here!") dump msg }
Variable triggers
⭐ New in 0.2.8.
Variable triggers are like handlers that runs when variable state or value changes. Trigger mechanism needs no initialization, it is enough just to declare a trigger block which name is the global variable where to attach the trigger. There is no way to unset the trigger in runtime. Simple example may look like:
mov my_var (1) add my_var (2) end trigger my_var { fetch _tmp (my_var) print "This is trigger for my_var, localized as _tmp\n" print "_tmp value is " . _tmp . "\n" }
Above example outputs:
This is trigger for my_var, localized as _tmp _tmp value is 1 This is trigger for my_var, localized as _tmp _tmp value is 3
Trigger runs immediately after value change. These attributes are available to use with trigger block:
-
mode, sets the trigger running mode. Defaults to blocking and can
be one of:
- blocking, trigger blocks the calling thread until it completes,
- concurrent, trigger runs concurrent with calling thread, and
- threaded is an alias for concurrent.
Simple example above can be set to run concurrent with calling thread by using threaded attribute:
mov my_var (1) add my_var (2) end trigger my_var [mode: threaded] { fetch _tmp (my_var) print "This is trigger for my_var, localized as _tmp\n" print "_tmp value is " . _tmp . "\n" }
As said, trigger runs when its variable state or value changes. Commands listed below does not spawn the trigger even when they modify the destination variable:
- deliver alias put, and
- fetch alias get.
Trigger mechanism has optional trigger_eval block to decide if the actual trigger should be run or not. For example, trigger_eval may examine some condition, and if true, it exits with return value of zero. When that happens, calling thread starts the actual trigger immediately. If evaluation block returns some other value than zero, trigger block will not be started. Evaluation block runs always in blocking mode, so it blocks the calling thread until evaluation block ends. Evaluation block does not support mode attribute. Above example can be extended as:
mov my_var (1) add my_var (2) end trigger my_var [mode: threaded] { fetch _tmp (my_var) print "This is trigger for my_var, localized as _tmp\n" print "_tmp value is " . _tmp . "\n" } trigger_eval my_var { ; If trigger should not be run, end with nonzero value: end -1 ; If trigger should be run, end with zero value: end 0 ; ...or just end, as zero is the default value: end ; If there is no end at the end of the block, return value is ; zero by default, which causes actual trigger to start. }
Function hooks
⭐ New in 0.2.9.
Each function can have hook subroutines defined by function attributes. Hooks are like constructors and destructors for functions called automatically. Hook can access and manipulate function variables directly. Also, variables defined by the hook are available to use with the function.
Function attributes for defining hooks include:
- entry attribute parameter is the name of the hook to call before function instructions are executed,
- return attribute parameter is the name of the hook to call just before control flow is passed back to caller of the function.
function my_function [entry: my_fnc_entry, return: my_fnc_ret] (_var1, _var2) { ; Entry hook is spawned here. print "This is subroutine with hooks.\n" ; This spawns the return hook and ends this function: ret ; Return hook is spawned at the end of the function too. } hook my_fnc_entry { ; This hook is called before my_function starts executing. ; ; my_function parameters _var1 and _var2 are accessible ; here. } hook my_fnc_ret { ; This hook is called just before control flow returns ; back to caller of my_function. ; ; my_function parameters _var1 and _var2 are accessible ; here along with other my_function variables. }
Hooks are available with following functions:
- alarms aka. timers,
- callbacks,
- subroutines (aka. functions), and
- threads.
Binding own functions to Ano script
There is couple of files to modify to get C functions useable by Ano script. Each file is explained below with simple examples. All of the files are located in package's root directory, same place where the configure script is. Example project Vesmir makes use of own functions binded to Ano script, check that for live example.
dynload_bind_app.c
Own function needs a wrapper function which is the function what the engine calls. That wrapper must check the sanity of function parameters supplied in Ano script and call the actual function. Wrapper function must have dynload_ at the start of its name.
/* This is the actual wrapper function called from Ano script */ void dynload_my_fn(struct a_funcs *c) { struct par_my_fn a; if(dynload_my_fn_pre(c, &a) != 0) { return; } my_fn(a.param1, a.param2); } /* Check function prerequisities, for example, sanity of the parameters can be checked here, its important to check that parameter count, c->c, is what it is expected to be */ int dynload_my_fn_pre(struct a_funcs *c, struct par_my_fn *a) { /* Two parameters is needed to fill param1 and param2 */ if(c->c != 2) { dynload_error_params(c, 2); return(-1); } /* Parameters are available in c->p array */ a->param1 = dynload_param_get_number(&c->p[0]); a->param2 = dynload_param_get_number(&c->p[1]); return(0); }
dynload_bind_app.h
This file is for defining structs and other stuff for above functions.
/* This is the structure to store function parameters provided by Ano script */ struct par_my_fn { double param1, param2; };
dynload_ret_app.h
Every function return value type must be specified here, as engine needs to know which kind of data each function returns, if any. Add each function to dynload_ret_t structure with proper return value type. See engine/dynload_str.h for possible types. For example, dynload_ret_t structure may look like this:
static struct dynload_ret dynload_ret_t[] = { { "my_fn", RETURN_TYPE_VOID }, /* This marks the end of the strucure, do not remove this line */ { NULL, RETURN_TYPE_NONE } };
symbols.dyn
Every function name must be added there for exporting them in executable's symbol table. Function name must be prefixed with dynload_. For example, file may look like this:
{ dynload_my_fn; # Dynamic symbols provided by engine # ... };
Makefile and Makefile.in
Place myfuncs.c in SRC array to get it compiled.
myfuncs.c
The file for own functions. Our example function may look like this:
/* This is the actual function to make the Mojo */ void my_fn(double param1, double param2) { printf("Two params are %f and %f.\n", param1, param2); }
declarations_app.h
If there is a need to declare your function, this file may be used for that. For example, file may look like:
#include "structures_app.h" void my_fn(double, double);
Keep the include statement there, it is needed for compiling the application.
Ano script
Example to call my_fn() function in Ano script:
my_fn (1.0, 2.0) end
If function returns a value of any kind, it is always stored in internal variable rc.
Copyright © 2023, Jani Salonen <salojan at goto10 piste co>. Piste is finnish word and means dot. All rights reserved.