suve
Warning: Illegal string offset 'doublequoteopening' in /usr/home/svgames/domains/svgames.pl/public_html/dokuwiki/inc/parser/xhtml.php on line 766 Warning: Illegal string offset 'doublequoteclosing' in /usr/home/svgames/domains/svgames.pl/public_html/dokuwiki/inc/parser/xhtml.php on line 774 Warning: Illegal string offset 'doublequoteopening' in /usr/home/svgames/domains/svgames.pl/public_html/dokuwiki/inc/parser/xhtml.php on line 766 Warning: Illegal string offset 'doublequoteclosing' in /usr/home/svgames/domains/svgames.pl/public_html/dokuwiki/inc/parser/xhtml.php on line 774

awful: manual: functions

While awful provides a vast assortment of built-in functionality, an indispensable part of any programming language is the possibility to define new functions. Functions can be used as a mean to avoid code duplication - instead of copy-pasting fragments of code, one can put then into functions, and then just invoke those routines. It also provides a possibility to shorten certain expressions, improving their readability.
This article focuses on user-defined functions in awful.

Declaring functions

!fun :delta $a $b $c
  :pow &b i2
  :mul &a &c i4
  :return :sub $b $a
!nuf

:writeln s'4x*x + 2x + 5'
:writeln s'Delta: ' :delta i4 i2 i5

Defining a function is done using the !fun construct. It is named as such because, first: it's an abbreviation for efunctione, second: defining functions sure is fun. As can be seen from the example, the !fun construct should be followed by the function name, and then, a list of arguments. Arguments can be defined using either the dollar ($) or the ampersand (&) symbol - as of v.0.4.4, there is no difference between these two declarations. In the example above, function :delta is defined; it takes three arguments.

After the !fun construct, function code follows. The end of in-function code is marked using the !nuf construct. Sub-functions are not supported. Nesting function declarations will cause a fatal error.

Invoking functions

User-defined functions in awful can be called just the same way built-in functions are called - just slap a :functionName somewhere in the code and you're set. There are, however, two rules to remember:

  • Each function name must be unique. It is not possible to ereplacee a built-in function with a user-defined one. 1)
  • Function declaration must appear before any function calls.
:writeln s'cube CALL = ' :call s'cube' f+3.0
# :cube is undeclared here; explicit call would cause a parse error 

!fun :cube $NUM
   :return :pow $NUM i3
!nuf

:writeln s'cube NAME = ' :cube f-3.5  # :cube has been declared, so this will work

#~ This declaration would cause a parse error due to collision with built-in function :not

!fun :not $NUM
   :return :sub i0 $NUM
!nuf
~#

The second restriction can, however, be avoided by using the :call function. It expects the first argument to be a string containing the name of function to be called, and the remaining arguments are passed to the function invoked.

Return value

Aside from simply executing some code, functions can also be used as a mean to shorten an expression - by calculating the value in a separate function, we improve readability of the compound statement. Returning a value from a function is done using the :return function - it simply propagates the leftmost argument it receives - which can be either the result of an expression, a variable, or a value literal. After invoking this routine, the user-defined function will exit immediately, and execution flow will return to the place where the userland function was called.

!fun :funny $wat
   !if $wat
      :return s'lolwut'
   !fi
   # No :return statement here!
!nuf

:writeln :funny i1  # Will print 'lolwut'
:writeln :funny i0  # Will print '{NIL}'

When exiting the function by the !nuf construct (simply reaching end of function code), NIL is returned.

Variable scope

Each function call creates its own variable pool. When accessing a variable, either by the dollar ($) or the ampersand (&) symbol, the name is searched only among the highest-level (current level) variable pool, and the lowest-level (global) variable pool. If a function argument has the same name as a global variable, the argument will shadow it, making it inaccessible. If a local variable of given name is not found, a lookup in the global pool is performed; only then a new local variable is created.

!fun :scope $abc
   :set &abc i50
   :set &pqr i50  
   :set &xyz i50
!nuf

:set &abc i10
:set &pqr i10
:scope i15
:writeln $abc s'; ' $pqr s'; ' $xyz

In the example above, the :scope function performs three assignments. The first assignment to &abc modifies the function's local variable, leaving the global var unchanged. The second call modifies the global var, because after looking among local vars, the interpreter then checks the global var-pool. Lastly, the third assignment, after checking both local and global var-pools and not finding the variable name in either, will create a new local variable, and then use it to perform the assignment.

Function overloading

awful currently does not support function overloading - attempting to create a second user-defined function using the same name will cause a fatal error. However, if you're willing to put in some extra work, it is possible to create a function taking various numbers of parameters.

!fun :avg
   :set &arr :func-args
   :set &avg f0.0
   :set &limit :arr-count &arr
   :set &i $limit
   !while $i
      :add &avg $arr[:sub &i i1]
   !done
   :return :div $avg $limit
!nuf   

:writeln :avg i10 i12 i33
:writeln :avg i10 i31 i52 i72 i95

The example above demonstrates the of the :func-args function. It returns an array containing all the parameters passed to the function, with indexes starting from 0. The example above simply calculates the average value of provided arguments, but one could also use an !if to check the value count of the argument array and perform different kind of operations depending on it.

Variable references

Variable references can be passed to user-defined functions just like to built-in functions, and will work similarly.

!fun :twoHalf $a $b
   :mul &a i5
   :mul &b i2
   :return :div $a $b
!nuf

:set &a &b i10
:writeln $a s- - $b
:writeln :twoHalf $a $b
:writeln $a s- - $b
:writeln :twoHalf &a &b
:writeln $a s- - $b

When a variable is passed to a function by-value (using the dollar $ symbol), a copy of the original value is created and passed to the function. Whatever changes the function will make to the local variable, the original remains unchanged. When a variable is passed by reference (using the ampersand & symbol), the function receives the original variable and any in-function operations will alter its value.

Note that it is currently impossible to use variable references with :func-args, as arrays are always copied by-value and references are not preserved.

1)
Before v.0.4.3 it was possible to create a user-function with the same name as a built-in, however, parse-time name resolution would always choose the built-in function - thus, one always had to use :call to invoke such functions.

wikipage modified on 2014/0601/2317