prev | next | contents


THE PREPROCESSOR.

   OpenEuphoria has an integrated preprocessor which handles all aspects in the language that concern text manipulation only. The advantages of using a separate piece of software to do the processing are:

1 Macros.

   Macros are parametrized text. When some text in the source file is almost repetitive up to a few places, it is natural to think of making the text into a routine. This routine is then called using the changing parts, which are the parameters of each call.

   In order to handle the changing parts better, macros use a limited subset of OpenEuphoria statements in order to have a slightly more sophisticated text processing.

   As a first very simple example, assume the following is a quite customay piece of code:

	function get_thewhatever(integer i)
	return someSequence[i]
	end function
with various instances of whatever and someSequence. It seem quite intuitive then to define a macro that takes only the two variable parts and generates all the text above. We will learn how to do so, but suffice to say that it is enough to:
  1. Define a macro as follows:
    	%macro defgetThe(fname,rtseq)
    	function get_the%fname(integer i)
    	return %rtseq[i]
    	end function
    	%mend
    
    Don't worry for the parentheses, they will get explained later.
  2. Whenever the code snippet comes again, just issue %getThe(whatever,someSequence) and the text of the example will be generated. As the opportunitties to use the macro increase, you spare some more typing, eye strain and time.

   Note that strings undergo a minimal amount of processing only, so that perccent signs in strings will not be considered by the preprocessor.

1.1 Macro definition.

   A macro is any text that starts with the keyword %macro and ends with the keyword %mend. More precisely, it goes as follows:

	%macro macroname({parameters})
	... macro body ...
	%mend

   It looks like a routine, except there is no parameter type to be seen. It even looks more like a routine as you cannot start another macro definition inside a macro definition.

1.2 Macro variables and parameters.

   Macro parameters are instances of macro variables, and macro variables are always strings. This makes sense as macros are not general-purpose programs, but just text generators. Also, macro variables don't have metadata.

1.2.1 Scope of a variable.

   Each macro has its own scope. On top of this, here is also a global namespace where variables that can be seen by several macros exist. There are only two sorts of variables: local (private to a macro) and global. Macro parameters are always local.

   In order to make variable(s) global, just issue the call

%global {varname}
and each variable name in the list becomes global.

1.2.2 Declaring variables.

   There is no need to declare variables. Variables start their existence as soon as they get a value, which is done by issuing:

%let varname[%][operator]=value
where the optional operator is in the same list as for ordinary OpenEuphoria programming.

1.2.3 Using variables.

   The value of a macro variable is plugged into text by preceding it witn a percent sign, as in

return %fixedvalue
where the contents of the variable %fixedvalue are substituted for the string "%fixedvalue".

   A problem may appear if the macro variable name is followed by text that could as well be part of the variable name. In this case, you have to enclose the variable name inside parentheses, like in

return tax%(year)final
If the parentheses are not there, the value of %yearfinal will be requested and an error will most likely occur as that variable probably does not exist.

   As macro variables are strings, the behaviour of operators is somewhat ambiguous: does it apply to the string or to the quantity represented by the string? The answer is: it depends. Use the optional % to have the operator behave on the string, and use the normal operator to work on the value represented by the string. For example, if %v1 is "253", then

1.3 Flow control.

   As some pieces of code are made of repetitive, sequential or nearly so, statements, some constructs are available to handle this case.

1.3.1 The %for loop.

   Text depending on asequential value can be swiftly generated as follows:

%for index=start value %to end value [%by increment] %do
... text and macro commands ...
%end for

   index is a macro variable that may or may not be defined beforehand. start value, end value and optionally increment must evaluate to strings representing numbers. If no %by clause is present, increment is assumed to be "1". Each iteration of the %for loop generates text with index evaluated to its current value. Otherwise, the %for loop behaves exactly as the for loop.

   Thus:

%for %ind=%to %maxindex %do control%ind=GenCtrl(%ind+1) %end for
with %maxindex being "5" at the first iteration, will generate:
control1=GenCtrl(1)
control2=GenCtrl(2)
control3=GenCtrl(3)
control4=GenCtrl(4)
control5=GenCtrl(5)

1.3.2 The %if block.

   The %if block behaves exactly as an OpenEuphoria if block does:

%if condition %then
text
[%elsif condition %then text]
[%else text]
%end if

   If the condition of the %if statement is true, the corresponding text is evaluated and inserted; the text extends until the next %elsif, %else or %end if macro statement of the block. Then the block is exited, and processing resumes right after the %end if closing mark..

   Otherwise, the conditions of optional %elsif branches are evaluated, until one of them is true or all of them are false. In the former case, the corresponding branch is processed as if it were the original %if statement.

   If no %if or %elsif branch was rtaken, and if there is an %else statement, the text following it is processed.

   Then the block is exited.

1.4 Macro scoping.

   As the same macro could be defined in several places, some areas of code may need to shelter the macro you intend to use from unwanted ambiguity. You do this by using the markers %scope and %end scope. Global macro variables defined in this scope are not visible outside it, and get undefined when the end marker is reached; if they shadowed some other variable, the latter become visible again.

2 Macros and files.

2.1 Using a file as a macro.

   A whole file can be used as a macro, even though the text it contains may or may not contain macro statements. Coding:

%include filename %({name=value})
will replace the statement by the contents of the file, parsed as some ordinary macro text. This text may contain occurrences of the macro variables %name; they are defined in the file only, and have value on entering the file's text..

   Thus, issuing %include the.txt %(n="3",k="1.5") with the file the.txt as follows:

for i=1 to %n do
?power(i,%k)
end for
will insert the following: for i=1 to 3 do ?power(i,1.5) end for

3 Other typing assistance and eyestrain relievers.

3.1 Shorthands used in nonatom manipulation.

3.1.1 Referring to a whole (tail of) snonatom.

   The shorthand [index..] can be used to refer to all elements of a nonatom whose index is at least index. This amounts to omitting the -1 before the closing square bracket.

3.1.2 Referring to a whole sequence.

   An entire nonatom can be referred to by the slice [1..-1]. The following shorthands for this are supported:

[1..] [..] []

   The preprocessor normalizes all these to [1..-1].

3.1.3 Excluding a slice in a nonatom.

   It is quite customary for a sequence to be built as the concatenation of the head and tail of a sequence, which amounts to excluding some central part of it.

   For this reason, the preprocessor provides a shorthand for this kind of sequence operation. Thus, someSequence[i!j] stands for the equivalent, but more verbose and possibly less clear someSequence[1..i,j..-1].

3.1.4 Referring to the end element of a nonatom.

   The highest admissible index for a sequence can be represented as end or $. Both are translated to -1 by the preprocessor.

3.2 Type repetition.

   When several contiguous formal parameters in a routine declaration share the same type, you don't need to repeat the type: the preprocessor remembers the last explicit type and plugs it in when it is missing.

   Thus,

function f(integer i,j,k)
is equivalent to
function f(integer i,integer j,iinteger k)
Of course, there must be one explicit type in the formal parameter list at least.

3.3 Using underscores in numbers.

   Underscores may be freely used inside decimal, hexadecimal or scientific numbers to enhance readibility. An underscore is not a real digit since it must not start a number.

   Thus, #ffff0000 can be written #ff_ff_0000, #ffff_00_0_0_ etc... and still be read as 2^32-2^16.

3.4 Argument completion.

   When some complex expression is both on the left and right sides of an assignment, it seems quite bothersome to write it twice. It may be somewhat inefficient too.

   To address this quite frequent situation, you may replace, inside any function call on the right side of assignment, the left hand part by the _ mark. Thus,

someSequence[f(x,y),z][length(s)]=append(someSequence[f(x,y),z][length(s)],0)

may be written more efficiently as

someSequence[f(x,y),z][length(s)]=append(_,0)

3.5 Ignoring a function return.

   A function, or a routine that may return a value, may be called as a procedure. The syntax #(_)#=myproc() is somewhat cumbersome, so that some shorthand is probably useful. The ~ sign is recommended.


prev | next | contents