Chapter 8: Include and modular scripting

8.1. The include keyword

Syntax:

include "path"

where path is a path to an awk source file. The include directive is interpreted by the lexer so it can be anywhere in the code. A separator doesn't need to follow it.

How exactly path is interpreted is up to the host application. The default interpretation should be: "relative to the path of the script that is executing the include". If a host app has a different interpretation, e.g. search paths, it should document that.

Some host applications don't implement include at all - that should be documented in the host application as well.

A file included can contain further includes. There is a certain maximum depth of nested includes, which is at least 16 (the main script file included!). When reaching this limit, libfawk throws an error on the next nested include.

A major limitation is that the path must be a string literal - since the copy&paste happens in the earliest stage of parsing the code, it can not depend on runtime calculated values. (Some host applications may implement an eval() function that can be used to bypass this limitation).

8.2. Common use: include functions

The most common use of the include keyword is having a library of functions in lib_foo and then include "lib_foo" from a main script and call the functions. For example:

lib_hex.fbas:
example program ch8_hex
rem convert a single digit to hex
function hexdigit(digit)
	hexdigit = "?"
	if digit < 10 then hexdigit = digit
	if digit = 10 then hexdigit = "A"
	if digit = 11 then hexdigit = "B"
	if digit = 12 then hexdigit = "C"
	if digit = 13 then hexdigit = "D"
	if digit = 14 then hexdigit = "E"
	if digit = 15 then hexdigit = "F"
end function

rem convert a byte value to a 2-digit hex string
function tohex2(val   ,d1,d2)
	d1 = int(val / 16)
	d2 = val mod 16
	tohex2 = hexdigit(d1) @ hexdigit(d2)
end function

main.fbas:
example program ch8_ex1
include "ch8_hex.fbas"

fawk_print(tohex2(24))


8.3. Include statements

Since the include keyword can be anywhere in the code, it can be within a function body as well. This can be used for including statements for local execution:

ch8_err.fbas:
example program ch8_err
if b = 0 then
	fawk_print("Error: b shouldn't be 0 - nobody divides by zero")
	goto ret
end if

if b < a then
	fawk_print("Error: " @ b @ " is smaller than " @ a)
	goto ret
end if

main.fbas:
example program ch8_ex2
a = 5

b = (a-3)*6
include "ch8_err.fbas"
fawk_print(a/b)

b = (a-5)*6
include "ch8_err.fbas"
fawk_print(a/b)

rem there is no return or exit in basic, we need a goto label, with a no-op
ret: a=a

This script runs a few hardwired tests for a and b and exits on error. The reason for not using a function for this purpose is: this way err.fawk can force the main code to return (without a funtion call and a furhter "if" in the return value).

Note: this is similar to using an object-like macro in C.

8.4. Include an expression

In some rare situations a long and complex expression operating on local variables may be repeated many times in the code. This expression can be copied to a separate file and included in any expression context. Simplified example:

calc.fbas:
example program ch8_calc
a*b

main.fbas:
example program ch8_ex3
a = 5
b = 2
fawk_print((include "ch8_calc.fbas")+1)

The parenthesis around the include is not required, but is good practice: it makes sure the expression within the included file will not change the precedence of the remaining calculations around the include.