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.

Note: the path is enclosed in double quotes, not single quotes, as it is not a pascal string.

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.fpas:
example program ch8_hex
{ convert a single digit to hex }
function hexdigit(digit);
begin
	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;

{ convert a byte value to a 2-digit hex string }
function tohex2(val   ;d1;d2);
begin
	d1 := int(val / 16);
	d2 := val % 16;
	tohex2 := hexdigit(d1) @ hexdigit(d2);
end;

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

function main(ARGV);
begin
	fawk_print(tohex2(24));
end;

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 begin
	fawk_print('Error: b shouldn\'t be 0 - nobody divides by zero');
	exit;
end;

if b < a then begin
	fawk_print('Error: ' @ b @ ' is smaller than ' @ a);
	exit;
end;

main.fbas:
example program ch8_ex2
function main(ARGV);
begin
	a := 5;

	b := (a-3)*6;
	include "ch8_err.fpas"
	fawk_print(a/b);

	b := (a-5)*6;
	include "ch8_err.fpas"
	fawk_print(a/b);
end;

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.fpas:
example program ch8_calc
a*b

main.fpas:
example program ch8_ex3
function main(ARGV);
begin
	a := 5;
	b := 2;
	fawk_print((include "ch8_calc.fpas")+1);
end;

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.