Next: Index for m0 builtins and stack instructions, Previous: Internal functioning of m0, Up: Top [Contents][Index]
| • M4 emulation example: | ||
| • M6 emulation example: | ||
| • GPM emulation example: | ||
| • CPP emulation example: |
In the previous Macro processing to Internal functioning of m0
the working of m0 has been explained. In this section the application
thereof is demonstrated by the implementation of several examples of macro processor
emulation. It shows the different possible solutions and the possibilities of m0.
Normally macro processors use words or atoms and a parser to proces these words. In m0
a start character set and a stop character set is used to emulate the use of words or atoms.
The bitap algorithm allows to use this efficiently.
The different examples have their own peculiarities and solutions for the emulation. The M4, M6 and GPM examples are all general purpose macro processors. The M4 macro processor is the most extensive of these three, and in contrast GPM is very small.
The most interesting peculiarities in the emulations are:
COPY can be used to make a copy of a macro.
In M4 this does not exist. Since the posix version a define used with a
defn can be used to make a copy. This can also copy a builtin function.
The handling of the copy of a builtin is handled internally in M4 using special tokens.
In the emulation information through the stack is used to handle this. Normally macros
in m0 can not exchange information through the stack, but for doing this
specific instructions can be used.
eval macro in the m4 emulation shows
the possibility of using a pattern and the instruction stacks to implement an infix
parser.
format macro in the m4 emulation shows
the possibilities of using a pattern for the argument replacement implementing
a printf like macro.
GO and GOBK macros to select
a text part in the arguments. In the emulation the steps and functioning of the
execution of macros (see The execution of a macro) together with
the instruction to stop macro execution
while collecting arguments are used to implement these macros for text selection.
m0 the macros always have a global scope. In the emulation
this is solved by pushing the macros when defined inside another macro
and popping the macros at the end of a macro using special characters appended
at the end of a definition that trigger the popping in a pattern for
argument substitution.
m0
that do not have recursive macro recognition set for argument collection and
definition expansion.
The emulation files are like any other file that contains sets of macros.
Therefore they are placed as the first input file.
The files to be processed follow hereafter. An example to use
the M4 emulation in the versions of GNU, v7, BSD or posix that are placed in the
emu directory:
m0 emu/m4gnu.m0 text.m4 m0 emu/m4v7.m0 text.m4 m0 emu/m4bsd.m0 text.m4 m0 emu/m4posix.m0 text.m4
Next: M6 emulation example, Up: Emulation examples [Contents][Index]
The m4 emulation is an example of defining a macro language in m0.
It is (at least now) not a goal to be 100% compatible with (GNU) m4 or to be complete.
Since m0 will also detect macros with invalid characters of m4 macros, it will
probably be very difficult to be 100% compatible for cases where certain characteristics of
m4 are used.
Also functions for regular expressions in m0 seem not necessary as this functionality can also
be accomplished with patterns. The macros related to regular expressions in (GNU) m4 are therefore not
implemented.
The following text of the emulation is generated from a source file for both text and code.
From this source file different versions of m4 are generated. These versions are:
v7The original version of m4, see The M4 Macro Processor, Brian W. Kernighan and Dennis M. Ritchie, Bell Laboratories, July 1, 1977.
posixA version with only the macros defined by posix, see e.g. m4.
gnuVersion with the macros defined in GNU m4, see GNU M4 manual.
bsdVersion with the macros defined in m4 of BSD, see e.g. FreeBSD m4 manual page.
Macros or code for all versions is indicated with all.
The emulation of m4 is using four macro sets:
0 is used to define all basic macros that are necessary to
be able to define the m4 macros. It is also used to hold some m4 macros that are
build using the basic macros. The emulation starts in this macro set and is switched to the m4exec macro set
to start emulation of the m4 input.
m4 macros are defined and executed in the macro set m4exec.
m4builtin macro set is used as a collection of builtin m4 macros that
can be called from the builtin macro.
Args macro set is used for the m4 command line options.
A big number of the builtin m4 macros are directly linked to the functions in m0.
Several patterns are used to implement the syntax of m4. Difficult is a new
definition of builtin macros using the defn macro. The use of defn requires
that its use is detected by a define and the define is a copy or a define depending on this.
Some builtin m4 macros are emulated using a collection of macros in the default macro set 0.
Quoting and comments are implemented as macros as there exists no built in functionality for this in m0.
Although it it not necessary to use three macro sets, it allows a relatively easy separation of the underlying macro code and the emulated macros. Another solution can be a proper naming of underlying macros such that these are unlikely accidentally called.
Applicable for: all
At the start the macros used to define the macros for m4 are defined.
This is all straightforward linking of macro names to builtin functions.
0_define:0_pattern:;;pattern;nr01;0 0_define:0_append_pattern:;;append_p;nr01;0 0_define:0_undefine:;;undefine;nr01;0 0_define:0_clr_pattern:;;clr_pat;nr01;0 0_define:0_copy_pattern:;;copy_pat;nr01;0 0_define:0_set_var:;;set_var;nr01;0 0_define:0_get_var:;;get_var;nr01;0 0_define:0_ifmacro:;;ifmacro?;rr01;0 0_define:0_argoptions:;;get_var;rr01;0;; stack_a pop_to_1 -7 0_define:0_chars_pattern:;;charpat;nr01;0 0_define:0_chars_args:;;chararg;nr01;0 0_define:0_specialchar:;;specialc;nr01;0 0_define:0_def_mcall:;;defmcall;nr01;0 0_define:0_atlast:;;at_last;nr01;0 0_define:0_atfirst:;;at_first;nr01;0 0_define:0_mset:;;macroset;nr01;0 0_define:0_info:;;info;nr01;0 0_define:0_infofield:;$9;info;nr01;0 0_define:0_setinfo:;;set_info;nr01;0 0_define:0_copy:;;copy;nr01;0 0_define:0_char:;;num2chr;nr01;0 0_define:0_program:;;program;nr01;0 0_define:0_divert:;;divert;nr01;0 0_define:0_undivert:;;undivert;nr01;0 0_define:0_divnum:;;get_var;nr01;0;; stack_a pop_to_1 -1 0_define:0_inter_pat:;;inter_p;nr01;0 0_define:0_tag_pat:;;tag_p;nr01;0 0_define:0_link_pat:;;link_prt;nr01;0
Setting the defaults. This is also the default of m0.
0_chars_pattern:[]-\@+*?~ 0_define:0_l1;0_char:1 ;;nn00;;;;0 0_define:0_r1;0_char:2 ;;nn00;;;;0 0_define:0_alt;0_char:3 ;;nn00;;;;0 0_define:0_slash;0_char:4 ;;nn00;;;;0
Applicable for: gnu bsd
Numbers can be larger than 0 – 9 (up to 9999) to select the arguments.
The second line defines the special characters for the arguments. This is missing the fifth character. It is thus not possible to select arguments from other stacks than the first stack.
0_chars_args:4$*@#;101;102;103;m4exec 0_chars_args:4$*@#;101;102;103;m4builtin
Applicable for: v7 posix
Numbers can only be 0 – 9 to select the arguments.
0_chars_args:1$*@#;101;102;103;m4exec 0_chars_args:1$*@#;101;102;103;m4builtin
0_chars_args:1$*@#$;111;112;103
Applicable for: all
In the next part a macro with a pattern is used to have quoting in the macros in the
default macro set 0 operational.
The quoting is thus not a builtin function in m0.
0_set_var:111;++-+ 0_set_var:112;--+- 0_pattern:0string;[@00-@ff]~;stack_b 1 stack_a begin-1 0_pattern:0string;++-+;stack_b 1 + 0_pattern:0string;--+-;stack_b 1 - dup =0 if stack_a end stop endif 0_define:++-+;$1;;nn00 ;0string
And defining a beginning pattern to be copied.
0_pattern:0start;[@00-@ff]~;stack_b 1 stack_a begin-1
Applicable for: all
First some special characters are defined to make the definition of macros easier.
The "\ " is used at the end of a macro to mean all characters except characters valid for a name.
This ends the detection of a macro name. It is used for all macros which have optional arguments.
Macros which must be followed by arguments end with a "(".
The "\B" is used at the start of a macro to mean all characters except characters valid for a name.
This is used to not start a macro inside a word.
0_specialchar: ;[@00-@2f@3a-@40@5b-@5e@60@7b-@ff] 0_specialchar:B;[@00-@40@5b-@5e@60@7b-@ff]
Applicable for: all
The collecting of command line options is executed in a separate
macro-set Args.
0_define:==Options;;get_var;rn00;;; stack_a pop_to_1 -8;Args 0_define:==Alloptions;;get_var;rn00;;; stack_a pop_to_1 -7;Args 0_define:==Mset:;;macroset;nr01;0;;;Args 0_pattern:args;[@00-@ff]~;stack_a begin-1 0_pattern:args;=;stack_a end begin 0_pattern:args;++-+ --+-;stack_a end stop
Setting the option of -P / --prefix-builtins to
prefix all builtins with m4_.
This is accomplished by deleting the macro m4_.
This macro is set as default to delete the m4_ part of the macro names.
Deleting this macro will leave the m4_ part.
0_define:-P;;undefine;nn00;;;stack_a "m4_" "0";Args 0_define:--prefix-builtins;;undefine;nn00;;;stack_a "m4_" "0";Args 0_define:m4_;;;nn00 0_divert:-1 0_mset:Args ==Alloptions ==Mset:0 0_divert:0
The other command line options are executed at the start of a new file.
0_def_mcall:-D;define;m4builtin;nn00;args;;Args 0_def_mcall:--define=;define;m4builtin;nn00;args;;Args 0_def_mcall:-U;undefine;m4builtin;nn00;args;;Args 0_def_mcall:--undefine=;undefine;m4builtin;nn00;args;;Args
Setting options at the start of a file by an mcall
to the ==Options macro in Args.
During this time diverting the output so that no output of options remain.
0_atfirst:m4_define(4_prev_div, divnum)m4_divert(-1)++-+ 4_Set_Options --+-m4_divert(4_prev_div)m4_dnl++-+ --+- 0_def_mcall:4_Set_Options;==Options;Args;++-+nn00 --+-;;;m4exec
Applicable for: all
The collecting of arguments in m4 macros is executed with a pattern named m4
containing small programs.
0_specialchar:(;[@00-@27@29-@ff] 0_pattern:m4;[\(]~;stack_b argpos =0 ifthen abort 0_pattern:m4;[@00-@ff]~;stack_c 0 0_pattern:m4;(;stack_c 1 + 0_pattern:m4;[(,][@00- ]*[!-@ff];stack_a begin-1 ;1 0_pattern:m4;);stack_c 1 - dup =0 if stack_a end stop endif 0_pattern:m4;,;stack_c dup <=1 if stack_a end endif
Applicable for: all
The define of m4 can not be directly linked to the function define.
In m4 a builtin macro can be named by using defn. These builtin
macros should be handled differently from a user macro.
Some code is necessary to handle the two cases.
Without the builtin definition, the define could be directly linked as:
0_define:\Bdefine(;;define;nr11 ;m4;;++-+stack_d "" putarg3 "rr11" putarg4 "m4" putarg5 "" putarg6 "" putarg7 "m4exec" putarg8 "\B" getarg1 cat "\ " cat putarg1--+-;m4exec
In this example a program, executed in the macro, sets
default values for the define function.
In this emulation the defn returns the information of the builtin macro on stack h.
When a builtin is returned by the defn stack h holds:
A macro to check for this and select the correct option is defined to handle this. The macro is actually a large program that:
0_program:define;++-+ stack_e argnum =0 ifthen stop getarg1 dup "" strcmp ifthen stop stack_f getarg2 stack_h if stack_a pop_to_2 copyfr_h swap "m4builtin" "m4builtin" "copy" fun_call pop_to_1 "\B" copyfr_h cat stack_h pop stack_a copyfr_h cat "\B" copyfr_e cat copyfr_h cat "m4exec" "m4exec" "copy" fun_call else stack_a pop_to_1 "\B" copyfr_e cat "\ " cat copyfr_f "" "rr11" "m4" "" "" "m4exec" "" "define" fun_call endif --+-
The program uses function calls, the macro is further empty because of this.
The m4builtin set is used to hold the information of the new builtin macro.
This has as a side effect that this new macro can also be called using the m4
builtin, which is not possible in m4.
0_define:\Bm4_define(;;;nr11 ;m4;; @define ;m4exec 0_define:define;;;nn00;m4;; @define ;m4builtin 0_setinfo:define;(;m4builtin
Applicable for: posix gnu bsd
Similar to the define. The only difference is the use of push instead of define.
0_program:push;++-+ stack_e argnum =0 ifthen stop getarg1 dup "" strcmp ifthen stop stack_f getarg2 stack_h if stack_a pop_to_2 copyfr_h swap "m4builtin" "m4builtin" "pushcopy" fun_call pop_to_1 "\B" copyfr_h cat stack_h pop stack_a copyfr_h cat "\B" copyfr_e cat copyfr_h cat "m4exec" "m4exec" "pushcopy" fun_call else stack_a pop_to_1 "\B" copyfr_e cat "\ " cat copyfr_f "" "rr11" "m4" "" "" "m4exec" "" "push" fun_call endif --+- 0_define:\Bm4_pushdef(;;;nr11 ;m4;; @push ;m4exec 0_define:pushdef;;;nn00;m4;; @push ;m4builtin 0_setinfo:pushdef;(;m4builtin
Applicable for: posix gnu bsd
defn should return the definition of a macro or in case of a builtin something
so that in a define a new macro can be defined for the builtin.
These two possibilities require a different handling and thus a
specific argument collection is defined that is similar to
the m4 argument collection, but puts the arguments to
stack b and calls a program to handle a builtin or a normal macro.
A builtin is returned on stack h to be used by a define.
Stack h is not freed and holds:
The information text of the macros in the m4builtin macro set is used to determine if a macro has optional arguments or not.
0_program:m4defnif;++-+ stack_b end dup "m4builtin" m?_num dup if copyto_h stack_h m?_info copyfr_b m?_name 1 free_no else stack_b pop "\B" swap cat "\" cat "m4exec" m?_num dup if stack_a copyfr_b m?_def cat endif endif --+- 0_pattern:m4defn;[\(]~;stack_b argpos =0 ifthen abort 0_pattern:m4defn;[@00-@ff]~;stack_c 0 stack_a "" 0_pattern:m4defn;(;stack_c 1 + 0_pattern:m4defn;[(,][@00- ]*[!-@ff];stack_b begin-1 ;1 0_pattern:m4defn;);stack_c 1 - dup =0 if @m4defnif stop endif 0_pattern:m4defn;,;stack_c dup <=1 if @m4defnif endif 0_define:\Bm4_defn(;$1;;nr11 ;m4defn;;;m4exec 0_define:defn;$1;;nn00;m4defn;;;m4builtin 0_setinfo:defn;(;m4builtin
Applicable for: posix gnu bsd
First a program is defined that selects between the two possible macros. It also sets the macro set.
0_program:undef;++-+ stack_d "\B" getarg1 cat
dup "\ " cat "m4exec" m?_num if
"\ " cat putarg1 else
"(" cat putarg1 endif
"m4exec" putarg2 --+-
The popdef is a link to the pop function
with the correct input set by the program.
0_define:\Bm4_popdef(;;pop;nr11 ;m4;; @undef ;m4exec 0_define:popdef;;pop;nn00;m4;; @undef ;m4builtin 0_setinfo:popdef;(;m4builtin
Applicable for: all
Similar to popdef, but with an undefine function.
0_define:\Bm4_undefine(;;undefine;nr11 ;m4;; @undef ;m4exec 0_define:undefine;;undefine;nn00;m4;; @undef ;m4builtin 0_setinfo:undefine;(;m4builtin
m4Applicable for: all
In the next part a macro with a pattern is used to have quoting operational in the m4 emulation.
0_set_var:101;` 0_set_var:102;' 0_set_var:103;, 0_set_var:100;$1 0_pattern:string;[@00-@ff]~;stack_b 1 stack_a begin-1 0_pattern:string;`;stack_b 1 + 0_pattern:string;';stack_b 1 - dup =0 if stack_a end stop endif 0_define:0_get_var:101 ;$1;;nn00 ;string;;;m4exec
Applicable for: all
The changequote calls the macro 0_changequote in macro set 0.
This enables the use of macros in the default macro set to perform the change of quotes.
0_def_mcall:\Bm4_changequote\ ;0_changequote;0;nr11 ;m4;;m4exec 0_def_mcall:changequote;0_changequote;0;nn00;m4;;m4builtin 0_setinfo:changequote;\ ;m4builtin 0_define:0_changequote;++-+0_undefine:0_get_var:101 ;m4exec 0_set_var:101;++-+$1--+- 0_set_var:102;++-+$2--+- 0_copy_pattern:0start;string 0_pattern:string;++-+$1--+-;stack_b 1 + 0_pattern:string;++-+$2--+-;stack_b 1 - dup =0 if stack_a end stop endif 0_define:0_get_var:101 ;0_get_var:100 ;;nn00 ;string;;;m4exec --+-;;rn00;0;;++-+stack_d argnum <=1 if "`" putarg1 "'" putarg2 endif--+-
Applicable for: posix gnu bsd
Also a comment is built as a macro.
0_set_var:104;# 0_pattern:comment;[@00-@ff]~;begin-1 0_pattern:comment;++-+ --+-;end stop 0_define:#;++-+$0$1 --+-;;nn00 ;comment;;stack_a argnum =0 ifthen end-1;m4exec
Applicable for: posix gnu bsd
Similar to changequote but with the
specific argument amounts of changecom.
It uses the __ifelse macro defined further down like an
m4 ifelse in the 0 macro set.
0_def_mcall:\Bm4_changecom\ ;0_changecom;0;nr11 ;m4;;m4exec 0_def_mcall:changecom;0_changecom;0;nr00;m4;;m4builtin 0_setinfo:changecom;\ ;m4builtin 0_define:0_changecom;++-+__ifelse(0_get_var:104 ,,,++-+0_undefine:0_get_var:104 ;m4exec --+-)0_set_var:104;++-+$1--+- 0_copy_pattern:0start;comment 0_pattern:comment;++-+$2--+-;end stop __ifelse(++-+$1--+-,,,++-+0_define:++-+$1--+-;++-+$--+-0$++-+1--+-++-+$2--+-;;nn00 ;comment;;stack_a argnum =0 ifthen end-1;m4exec --+-)--+-;;rn00;0;;++-+stack_d argnum <=1 if " " putarg2 endif--+-
Applicable for: gnu bsd
Defines a macro call to a macro stored in the first argument.
The macro set m4builtin is used for calling the builtin macros of m4.
The macros defined in the macro set m4builtin are only called from
this macro. Therefore these macros should not recurse their output, because
this is done by the builtin macro. Since they are never called normally,
the settings for argument collection are irrelevant.
For macros that have a different argument collection than the m4 of
builtin this represents a difficulty.
The builtin macro itself is also defined in the macro set m4builtin.
0_def_mcall:\Bm4_builtin(;;m4builtin;rr11 ;m4;;m4exec 0_def_mcall:builtin;;m4builtin;nn00;m4;;m4builtin 0_setinfo:builtin;(;m4builtin
Applicable for: all
0_define:\Bm4_include(;;include;rr11 ;m4;;;m4exec 0_define:include;;include;nn00;m4;;;m4builtin 0_setinfo:include;(;m4builtin
Applicable for: all
0_define:\Bm4_sinclude(;;sinclude;rr11 ;m4;;;m4exec 0_define:sinclude;;sinclude;nn00;m4;;;m4builtin 0_setinfo:sinclude;(;m4builtin
Applicable for: bsd
The place is like an include, but the contents
of the file is not recursively scanned for macros.
To have the builtin not to recursively scan the output
the setting thereof is overruled with the recur_n.
0_define:\Bm4_place(;;include;nr11 ;m4;;;m4exec 0_define:place;;include;nn00;m4;;recur_n;m4builtin 0_setinfo:place;(;m4builtin
Applicable for: bsd
Similar to place just silent for file errors.
0_define:\Bm4_splace(;;sinclude;nr11 ;m4;;;m4exec 0_define:splace;;sinclude;nn00;m4;;recur_n;m4builtin 0_setinfo:splace;(;m4builtin
Applicable for: all
0_define:\Bm4_divert\ ;;divert;nr11 ;m4;;;m4exec 0_define:divert;;divert;nn00;m4;;;m4builtin 0_setinfo:divert;\ ;m4builtin
Applicable for: all
0_define:\Bm4_undivert\ ;;undivert;nr11 ;m4;;;m4exec 0_define:undivert;;undivert;nn00;m4;;;m4builtin 0_setinfo:undivert;\ ;m4builtin
Applicable for: all
0_define:\Bm4_divnum\ ;;get_var;nn11 ;m4;;stack_a pop_to_1 -1;m4exec 0_define:divnum;;get_var;nn00;m4;;stack_a pop_to_1 -1;m4builtin 0_setinfo:divnum;\ ;m4builtin
__line__Applicable for: gnu bsd
0_define:\Bm4___line__\ ;;get_var;nn11 ;m4;;stack_a pop_to_1 -4;m4exec 0_define:__line__;;get_var;nn00;m4;;stack_a pop_to_1 -4;m4builtin 0_setinfo:__line__;\ ;m4builtin
__file__Applicable for: gnu bsd
0_define:\Bm4___file__\ ;;get_var;nn11 ;m4;;stack_a pop_to_1 -5;m4exec 0_define:__file__;;get_var;nn00;m4;;stack_a pop_to_1 -5;m4builtin 0_setinfo:__file__;\ ;m4builtin
__program__Applicable for: gnu
0_define:\Bm4___program__\ ;;get_var;nn11 ;m4;;stack_a pop_to_1 -6;m4exec 0_define:__program__;;get_var;nn00;m4;;stack_a pop_to_1 -6;m4builtin 0_setinfo:__program__;\ ;m4builtin
__unix__Applicable for: gnu
0_define:\Bm4___0_get_var:-2 __\ ;;;nn11;m4;;;m4exec 0_define:__0_get_var:-2 __;;;nn00;m4;;;m4builtin 0_setinfo:__0_get_var:-2 __;\ ;m4builtin
unixApplicable for: v7 bsd
0_define:\Bm4_0_get_var:-2 \ ;0_get_var:-2 ;;nn11;m4;;;m4exec 0_define:0_get_var:-2 ;0_get_var:-2 ;;nn00;m4;;;m4builtin 0_setinfo:0_get_var:-2 ;\ ;m4builtin
Applicable for: all
0_define:\Bm4_errprint(;;errprint;nr11 ;m4;;;m4exec 0_define:errprint;;errprint;nn00;m4;;;m4builtin 0_setinfo:errprint;(;m4builtin
Applicable for: posix gnu bsd
0_define:\Bm4_m4exit(;;exit;nr11;m4;;;m4exec 0_define:m4exit;;exit;nn00;m4;;;m4builtin 0_setinfo:m4exit;(;m4builtin
Applicable for: all
0_define:\Bm4_index(;;strindex;rr11 ;m4;;;m4exec 0_define:index;;strindex;nn00;m4;;;m4builtin 0_setinfo:index;(;m4builtin
Applicable for: all
0_define:\Bm4_substr(;;substr;rr11 ;m4;;;m4exec 0_define:substr;;substr;nn00;m4;;;m4builtin 0_setinfo:substr;(;m4builtin
Applicable for: all
0_define:\Bm4_translit(;;strtrans;rr11 ;m4;;;m4exec 0_define:translit;;strtrans;nn00;m4;;;m4builtin 0_setinfo:translit;(;m4builtin
Applicable for: posix gnu bsd
0_define:\Bm4_m4wrap(;;at_last;nr11 ;m4;;;m4exec 0_define:m4wrap;;at_last;nn00;m4;;;m4builtin 0_setinfo:m4wrap;(;m4builtin
Applicable for: gnu bsd
The output is of the shell function is recursive.
This is different from the m4 macro syscmd.
See the rr11 settings string.
0_define:\Bm4_esyscmd(;;shell;rr11 ;m4;;;m4exec 0_define:esyscmd;;shell;nn00;m4;;;m4builtin 0_setinfo:esyscmd;(;m4builtin
Applicable for: all
The output of syscmd is not recursive. See the n
in the nr11 settings string.
0_define:\Bm4_syscmd(;;shell;nr11 ;m4;;;m4exec 0_define:syscmd;;shell;nn00;m4;;recur_n;m4builtin 0_setinfo:syscmd;(;m4builtin
Applicable for: all
0_define:\Bm4_sysval\ ;;get_var;nn11 ;m4;;stack_a pop_to_1 -9;m4exec 0_define:sysval;;get_var;nn00;m4;;stack_a pop_to_1 -9 recur_n;m4builtin 0_setinfo:sysval;\ ;m4builtin
Applicable for: posix gnu bsd
Making the "safe" temporary file instead of the "unsafe" file in the
original m4.
0_define:\Bm4_maketemp(;;tempfile;rr11 ;m4;;;m4exec 0_define:maketemp;;tempfile;nn00;m4;;;m4builtin 0_setinfo:maketemp;(;m4builtin
Applicable for: posix gnu bsd
Same as maketemp, but the output is not recursive.
0_define:\Bm4_mkstemp(;;tempfile;nr11 ;m4;;;m4exec 0_define:mkstemp;;tempfile;nn00;m4;;recur_n;m4builtin 0_setinfo:mkstemp;(;m4builtin
Applicable for: all
The delete to newline as the m4 macro and a special
0_dnl (macro starts with a space!) used at the beginning and end of the input.
The dnl uses a similar pattern as comments to
read a single argument up to a newline.
0_pattern:m4dnl;[@00-@ff]~;begin-1 0_pattern:m4dnl;++-+ --+-;end stop 0_define:\Bm4_dnl\ ;;;++-+nn11 --+-;m4dnl;;;m4exec 0_define:dnl;;;++-+nn00 --+-;m4dnl;;;m4builtin 0_setinfo:dnl;\ ;m4builtin 0_define: 0_dnl;;;++-+nn00 --+-;m4dnl;;;m4exec
Applicable for: all
0_define:\Bm4_incr(;$1;;rr11 ;m4;;stack_d getarg1 1 + putarg1;m4exec 0_define:incr;$1;;nn00;m4;;stack_d getarg1 1 + putarg1;m4builtin 0_setinfo:incr;(;m4builtin
Applicable for: posix gnu bsd
0_define:\Bm4_decr(;$1;;rr11 ;m4;;stack_d getarg1 1 - putarg1;m4exec 0_define:decr;$1;;nn00;m4;;stack_d getarg1 1 - putarg1;m4builtin 0_setinfo:decr;(;m4builtin
Applicable for: posix gnu bsd
0_define:\Bm4_shift(;$@;;rr11 ;m4;;base_=_1;m4exec 0_define:shift;$@;;nn00;m4;;base_=_1;m4builtin 0_setinfo:shift;(;m4builtin
Applicable for: gnu bsd
This will just put the macro called by indir in the output
and recurse this macro.
0_define:\Bm4_indir(;$1($@);;rr11 ;m4;;base_=_1;m4exec 0_define:indir;$1($@);;nn00;m4;;base_=_1;m4builtin 0_setinfo:indir;(;m4builtin
Applicable for: all
The ifdef calls the macro 0_ifdef in macro set 0.
This enables the use of macros in the default macro set.
0_def_mcall:\Bm4_ifdef(;0_ifdef;0;rr11 ;m4;;m4exec 0_def_mcall:ifdef;0_ifdef;0;nn00;m4;;m4builtin 0_setinfo:ifdef;(;m4builtin 0_define:0_ifdef;++-+0_ifmacro:\B++-+$1--+-\ ;++-+$2--+-;++-+0_ifmacro:\B++-+$1--+-(;++-+$2--+-;++-+$3--+-;m4exec --+-;m4exec --+-;;rr00;0
Applicable for: all
0_define:\Bm4_len(;$1;;nr11 ;m4;;stack_a pop_to_2 argnum >0 if strlen else 0 endif;m4exec 0_define:len;$1;;nn00;m4;;stack_a pop_to_2 argnum >0 if strlen else 0 endif;m4builtin 0_setinfo:len;(;m4builtin
Applicable for: all
The ifelse macro is implemented with a pattern using
the special ifcmpset instruction in the programs.
The ifelse macro does not use the m4 pattern and therefore
the macro is cloned in the 0 macro set for use with the builtin
macro. Using the intermediary
macro 0_ifelse to output the arguments to the clone macro
4_ifelse so that the m4if pattern can be used.
0_pattern:m4if;[\(]~;stack_b argpos =0 ifthen abort 0_pattern:m4if;[@00-@ff]~;stack_c 0 stack_a "" 0 0_pattern:m4if;(;stack_c 1 + 0_pattern:m4if;);stack_c 1 - dup =0 if stack_a end ifcmpset stop endif 0_pattern:m4if;[(,][@00- ]*[!-@ff];stack_a begin-1 ;1 0_pattern:m4if;,;stack_c dup <=1 if stack_a end stack_d argnum 3 mod =0 if stack_a ifcmpset endif endif 0_define:\Bm4_ifelse(;$1;;rr11 ;m4if;; stack_a argnum =1 ifthen pop argnum =2 ifthen pop pop;m4exec 0_def_mcall:ifelse;0_ifelse;0;nn00;m4;;m4builtin 0_setinfo:ifelse;(;m4builtin 0_define:0_ifelse;4_ifelse($@);;rn00;m4;;;0 0_define:4_ifelse(;$1;;nr01;m4if;; stack_a argnum =1 ifthen pop argnum =2 ifthen pop pop;0 0_define:__ifelse(;$1;;rr01;m4if;; stack_a argnum =1 ifthen pop argnum =2 ifthen pop pop;0
Applicable for: all
The arguments are parsed and evaluated during the argument collection.
Therefore a specific pattern for the argument collection is defined
in the same way as for the ifelse macro.
Like in the builtin ifelse also here an intermediary macro is used for the builtin
macro.
The 4_eval macro can not be called with the arguments quoted, because the quotes
used (++-+ and --+-) interfere with the evaluation by the m4eval
pattern.
0_pattern:m4eval;[\(]~;stack_b argpos =0 ifthen abort 0_pattern:m4eval;[@00-@ff]~;stack_c 0 stack_d 1 0_specialchar:S;[@00-@2a@2b@2c@2d@2e@2f@3a-@41@43-@51@53-@57@59-@61@63-@71@73-@77@79-@ff] 0_specialchar:*;[@00-@29@2b-@ff] 0_specialchar:>;[@00-@3c@3f-@ff] 0_specialchar:<;[@00-@3b@3e-@ff] 0_specialchar:=;[@00-@20@22-@3b@3f-@ff] 0_specialchar:&;[@00-@25@27-@ff] 0_specialchar:|;[@00-@7b@7d-@ff] 0_specialchar:D;[@00-@2f@3a-@ff] 0_specialchar:R;[@00-@2f@3b-@40@5b-@60@7b-@ff] 0_pattern:m4eval;\S[0-9];stack_b begin+1 0_append_pattern:m4eval;[0-9a-zA-Z:]*\R;stack_b end-1 stack_d pop_to_0 0 0_pattern:m4eval;+[0-9];stack_d =0 if stack_b 19 opexif>= + else stack_b 0 30 opexif>= + endif 0_pattern:m4eval;-[0-9];stack_d =0 if stack_b 19 opexif>= - else stack_b 0 30 opexif>= - endif 0_pattern:m4eval;+\D;stack_b 19 opexif>= + stack_d 1 0_pattern:m4eval;-\D;stack_b 19 opexif>= - stack_d 1 0_pattern:m4eval;\**\*;stack_b 20 opexif>= * stack_d 1 0_pattern:m4eval;**;stack_b 21 opexif> power stack_d 1 0_pattern:m4eval;/;stack_b 20 opexif>= / stack_d 1 0_pattern:m4eval;%;stack_b 20 opexif>= mod stack_d 1 0_pattern:m4eval;~;stack_b 29 opexif>= ~ stack_d 1 0_pattern:m4eval;!\=;stack_b 29 opexif>= ! stack_d 1 0_pattern:m4eval;<<;stack_b 18 opexif>= shift_l stack_d 1 0_pattern:m4eval;>>;stack_b 18 opexif>= shift_r stack_d 1 0_pattern:m4eval;\>>\>;stack_b 17 opexif>= > stack_d 1 0_pattern:m4eval;\<<\<;stack_b 17 opexif>= < stack_d 1 0_pattern:m4eval;>=;stack_b 17 opexif>= >= stack_d 1 0_pattern:m4eval;<=;stack_b 17 opexif>= <= stack_d 1 0_pattern:m4eval;==;stack_b 16 opexif>= = stack_d 1 0_pattern:m4eval;!=;stack_b 16 opexif>= != stack_d 1 0_pattern:m4eval;this part is doing nothing, it is here to test the memory use of pattern; 0_pattern:m4eval;\==\=;stack_b 16 opexif>= = stack_d 1 0_pattern:m4eval;\&&\&;stack_b 15 opexif>= bit_and stack_d 1 0_pattern:m4eval;^;stack_b 14 opexif>= bit_exor stack_d 1 0_pattern:m4eval;\||\|;stack_b 13 opexif>= bit_or stack_d 1 0_pattern:m4eval;&&;stack_b 12 opexif>= and stack_d 1 0_pattern:m4eval;||;stack_b 11 opexif>= or stack_d 1 0_pattern:m4eval;(;stack_c 1 + dup >=2 if stack_b 0 oppush nop endif 0_pattern:m4eval;);stack_c 1 - dup >=1 if stack_b opexto nop endif stack_c dup =0 if stack_a end argnum =1 if stack_b opexall putarg1 endif stop endif 0_pattern:m4eval;[(,][@00- ]*[!-@ff];stack_a begin-1 ;1 0_pattern:m4eval;,;stack_c dup <=1 if stack_a end argnum =1 if stack_b opexall putarg1 endif endif 0_define:\Bm4_eval(;;num2str;rr11 ;m4eval;;;m4exec 0_def_mcall:eval;0_eval;0;nn00;m4;;m4builtin 0_setinfo:eval;(;m4builtin 0_define:0_eval;4_eval($*);;rn00;m4;;;0 0_define:4_eval(;;num2str;nn01;m4eval;;;0
Applicable for: bsd
Same as the eval macro of m4.
0_define:\Bm4_expr(;;num2str;rr11 ;m4eval;;;m4exec 0_def_mcall:expr;0_eval;0;nn00;m4;;m4builtin 0_setinfo:expr;(;m4builtin
Applicable for: gnu bsd
This macro uses a pattern for the argument substitution
to implement the printf like output.
0_pattern:m4format;[@00-@ff]~;stack_d 2 0_pattern:m4format;%;stack_e begin+0 0_tag_pat:m4format;1 0_append_pattern:m4format;-; 0_tag_pat:m4format;2 0_append_pattern:m4format;[0-9];stack_f begin+0 0_append_pattern:m4format;[0-9]*\D;stack_f get_st-1 endbegin 0_tag_pat:m4format;3 0_inter_pat:m4format;[0-9]+s;stack_e copyfr_f copyfr_d getarg copyto_d strlen - " " str* putoutst stack_d putout-0 1 + 0_tag_pat:m4format;4 0_inter_pat:m4format;[0-9]+s;stack_e copyfr_f copyfr_d getarg dup putoutst strlen - " " str* putout-0 stack_d 1 + 0_tag_pat:m4format;5 0_inter_pat:m4format;s;stack_e copyfr_d getarg putoutst stack_d 1 + 0_tag_pat:m4format;6 0_inter_pat:m4format;[1-9][0-9]*d;stack_e copyfr_f copyfr_d getarg 0 + copyto_d strlen - " " str* putoutst stack_d putout-0 1 + 0_tag_pat:m4format;7 0_inter_pat:m4format;[0][0-9]+d;stack_e copyfr_f get_st+2 copyfr_d getarg 0 + copyto_d strlen - "0" str* putoutst stack_d putout-0 1 + 0_tag_pat:m4format;8 0_inter_pat:m4format;d;stack_e copyfr_d getarg 0 + putoutst stack_d 1 + 0_link_pat:1;2;3;5;6;7;8 0_link_pat:2;4 0_define:\Bm4_format(;;;rr11 ;m4;m4format;;m4exec 0_define:format;;;nn00;m4;m4format;;m4builtin
Applicable for: posix gnu bsd
to do
0_define:\Bm4_dumpdef(;;;nr11 ;m4;;;m4exec
Applicable for: gnu bsd
to do
Applicable for: gnu bsd
to do
Applicable for: posix gnu bsd
to do
nop
0_define:\Bm4_traceon\ ;;;nr11 ;m4;;;m4exec 0_define:traceon;;;nn00;m4;;;m4builtin 0_setinfo:traceon;\ ;m4builtin
Applicable for: posix gnu bsd
to do
nop
0_define:\Bm4_traceoff\ ;;;nr11 ;m4;;;m4exec 0_define:traceoff;;;nn00;m4;;;m4builtin 0_setinfo:traceoff;\ ;m4builtin
Applicable for: gnu
not implemented
Applicable for: gnu
not implemented
Applicable for: all
Finally switch to the m4exec macro set.
The special chars for defining a set of characters is changed from "[" and "]"
to bytes with values 1 and 2. This avoids an issue when similar characters in m4 are
used to set quotes or comments.
Extra input is inserted to be sure that all macros are recognised at the start and the end of the input file. These macros do not generate output.
0_chars_pattern:0_l10_r1-\@+*?~ 0_atlast:++-+ 0_dnl --+- 0_mset:m4exec 0_dnl
Next: GPM emulation example, Previous: M4 emulation example, Up: Emulation examples [Contents][Index]
The m6 emulation is an example of defining a macro language in m0.
The m6 macro processor is a predecessor of m4. It does not seem to be used
anymore, but as an example it seems to be still useful.
Since there is no working m6 for current systems, the testing of this emulation is
limited.
The following text of the emulation is generated from a source file for both text and code.
The original m6 was written in Fortran and a later version can be found in
Unix v6 written in a pre K&R C (see https://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/source/m6).
However in this emulation only one version that is based on
The M6 Macro Processor, Andrew D. Hall, Bell Laboratories, April 12, 1972
is made and indicated with all.
A similar approach as is used in the m4 emulation could be used for the
m6 emulation. However, because the macros in m6 look a lot
like S-expressions as known from Lisp, a different solution is chosen.
In the macro set for execution of m6 macros a main macro is defined
that will trigger on all macros and will call the macro placed in the first
argument. This gives some difficulties, because 3 macros (GO, GOBK
and DNL) have a different behaviour for the collection of arguments.
Thus the emulation design of m4 might be a better choice, but for demonstration
the current design is also fine.
The main macro is defined in the m6exec macro set. The builtin and user macros
are defined in the m6def macro set.
Applicable for: all
At the start the macros used to define the macros for m6 are defined.
This is all straightforward linking of macro names to builtin functions.
0_define:0_pattern:;;pattern;nr01;0 0_define:0_set_var:;;set_var;nr01;0 0_define:0_get_var:;;get_var;nr01;0 0_define:0_chars_pattern:;;charpat;nr01;0 0_define:0_chars_args:;;chararg;nr01;0 0_define:0_specialchar:;;specialc;nr01;0 0_define:0_def_mcall:;;defmcall;nr01;0 0_define:0_atlast:;;at_last;nr01;0 0_define:0_mset:;;macroset;nr01;0
Setting the defaults. The first is also the default of m0.
The second line defines the special characters for the arguments. This is missing the fifth character.
It is thus not possible to select arguments from other stacks than the first stack.
0_chars_pattern:[]-\@+*?~ 0_chars_args:1$*@#;101;102;103;m6exec 0_chars_args:1$*@#;101;102;103;m6def 0_chars_args:1$*@#$;111;112;103
Applicable for: all
In the next part a macro with a pattern is used to have quoting in the macros in the
default macro set 0 operational.
The quoting is thus not a builtin function in m0.
0_set_var:111;+-+ 0_set_var:112;-+- 0_pattern:0string;[@00-@ff]~;stack_b 1 stack_a begin-1 0_pattern:0string;+-+;stack_b 1 + 0_pattern:0string;-+-;stack_b 1 - dup =0 if stack_a end stop endif 0_define:+-+;$1;;nn00 ;0string
Applicable for: all
In the next part a macro with a pattern is used to have quoting in m6.
0_set_var:101;< 0_set_var:102;> 0_set_var:103;, 0_pattern:string;[@00-@ff]~;stack_b 1 stack_a begin-1 0_pattern:string;<;stack_b 1 + 0_pattern:string;>;stack_b 1 - dup =0 if stack_a end stop endif 0_define:<;$1;;nn00 ;string;;;m6exec
Applicable for: all
The collecting of arguments in m6 macros is executed with a pattern named m6
containing small programs.
The macros GO or GOBK are special cases.
Therefore the pattern will detect these
and then all arguments after the second argument
are eaten when the argument equals 1.
If the argument does not equal 1, then the pattern stops, so
that the text after these macros is output.
In this case also an undocumented implementation feature
is used to let the collection of arguments continue until
the end of the buffer. In the implementation of m0,
the replacement text is put in a memory buffer that is
used as input to the bitap algorithm. At the end of the buffer
the collection of arguments (in this case only for these two
macros) ends and the execution of the macro continues.
This results in the effect as described in m6 for these
two macros.
The DNL macro is also a special case and
is also performed in this pattern. The argument
collection is stopped when a "\n" is encountered.
In the normal case all arguments 9 and larger
are also collected together into argument 9.
This is so defined in m6.
0_pattern:m6;[@00-@ff]~;begin-1 stack_b 0 stack_c 0
0_pattern:m6;+-+GO[,:;]-+-;stack_b argnum <1 if 1 endif
0_pattern:m6;+-+GOBK[,:;]-+-;stack_b argnum <1 if 1 endif
0_pattern:m6;+-+DNL[,:;]-+-;stack_c argnum <1 if 1 endif
0_pattern:m6;,;argnum 9 < if stack_a end begin endif
0_pattern:m6;+-+;-+-; recur_n
0_pattern:m6;+-+[:;]-+-;+-+stack_a end stack_b dup =0 if
stack_c dup =0 ifthen stop
else
dup =1 if
getarg2 =1 if
2
no_macro
else
stop
endif
endif
endif -+-
0_pattern:m6;+-+
-+-;stack_c dup =1 if stack_a end stop endif
Applicable for: all
The main macro will collect all arguments including the macro name
and will make an mcall to the macro.
This is therefore a relatively simple macro.
0_def_mcall:#;;m6def;rr00 ;m6;;m6exec
Applicable for: all
These two macros are a bit different from normal macros.
They will remove or keep the rest of a replacement text after the macro.
They are similar to an if but their influence is outside of the macro.
Therefore the m6 pattern will do the check of the if
of the argument for these macros. The called macros are nop
except for setting the no recurive in the GO macro.
0_define:GOBK;;;nn00;m6;;;m6def 0_define:GO;;;nn00;m6;;recur_n;m6def
Applicable for: all
The macros will be defined in the m6def macro set.
They will be called from the main macro. Therefore the argument collection has no significance.
0_define:DEF;;define;nn00;m6;;stack_a pop_to_3 "" "nn00" "" "" "" "m6def";m6def
Applicable for: all
0_define:COPY;;copy;nn00;m6;;stack_a pop_to_3 "m6def" "m6def";m6def
Applicable for: all
0_define:SEQ;$1;;nn00;m6;;stack_a pop_to_3 strcmp;m6def
Applicable for: all
0_define:SNE;$1;;nn00;m6;;stack_a pop_to_3 strcmp !;m6def
Applicable for: all
0_define:GT;$1;;nn00;m6;;stack_a pop_to_3 >;m6def
Applicable for: all
0_define:GE;$1;;nn00;m6;;stack_a pop_to_3 >=;m6def
Applicable for: all
0_define:LT;$1;;nn00;m6;;stack_a pop_to_3 <;m6def
Applicable for: all
0_define:LE;$1;;nn00;m6;;stack_a pop_to_3 <=;m6def
Applicable for: all
0_define:EQ;$1;;nn00;m6;;stack_a pop_to_3 =;m6def
Applicable for: all
0_define:NE;$1;;nn00;m6;;stack_a pop_to_3 !=;m6def
Applicable for: all
The IF will call a macro in the 0 macro set
to redo the arguments with the m6if pattern.
This pattern will do the to do the comparisons
and set the correct answer.
0_pattern:m6if;[@00-@ff]~;stack_a begin-1 0_pattern:m6if;[,:];+-+stack_a end begin stack_b argnum 2 mod =0 if argnum 1 - getarg =1 if argnum getarg endif endif-+- 0_pattern:m6if;[:]; stop 0_def_mcall:IF;6_IF;0;nn00;m6;;m6def 0_define:6_IF;0_IF$@:;;rn00;m6;;;0 0_define:0_IF;$b0;;nr00;m6if;;;0
Applicable for: all
0_define:SIZE;$1;;nn00;m6;;stack_a pop_to_2 strlen;m6def
Applicable for: all
0_define:SUBSTR;;substr;nn00;m6;;;m6def
Applicable for: all
0_define:ADD;$1;;nn00;m6;;stack_a pop_to_3 +;m6def
Applicable for: all
0_define:SUB;$1;;nn00;m6;;stack_a pop_to_3 -;m6def
Applicable for: all
0_define:MPY;$1;;nn00;m6;;stack_a pop_to_3 *;m6def
Applicable for: all
0_define:DIV;$1;;nn00;m6;;+-+stack_b getarg2 =0 if "" putarg1 else stack_a pop_to_3 / endif-+-;m6def
Applicable for: all
0_define:EXP;$1;;nn00;m6;;+-+stack_b getarg2 <0 if "" putarg1 else stack_a pop_to_3 power endif-+-;m6def
Applicable for: all
The DNL does nothing. All the work is performed in the m6
pattern.
0_define:DNL;;;nn00;;;;m6def
Applicable for: all
These macros are not implemented.
Applicable for: all
Finally switch to the m6exec macro set.
0_mset:m6exec
Next: CPP emulation example, Previous: M6 emulation example, Up: Emulation examples [Contents][Index]
The gpm emulation is an example of defining a macro language in m0.
The gpm macro processor is an early macro processor and is considered a
very early predecessor of m4. It does not seem to be used
anymore, but as an example it seems to be still useful.
Since there is no working gpm macro processor for current systems, the testing of this emulation is
limited.
The following text of the emulation is generated from a source file for both text and code.
In this emulation the version that is described in
GPMX A portable general purpose macro processor adapted for preprocessing FORTRAN, Robert C. Gammill, The Rand Corporation, Santa Monica, California, 7 June 1976
is made and indicated with all.
A similar approach as is used in the m6 emulation is used for the
gpm emulation.
In the macro set for execution of gpm macros a main macro is defined
that will trigger on all macros and will call the macro placed in the first
argument. The use of a macro call is necessary because it seems a practise that
called macros are first defined during argument collection. Thus the called macro
is not yet defined when called.
The main macro is defined in the gpmexec macro set. The builtin and user macros
are defined in the gpmdef macro set.
GPM has only 6 macros builtin. It is thus small and the characteristics of
gpm are used to add complexity.
Applicable for: all
At the start the macros used to define the macros for gpm are defined.
This is all straightforward linking of macro names to builtin functions.
0_define:0_pattern:;;pattern;nr01;0 0_define:0_set_var:;;set_var;nr01;0 0_define:0_get_var:;;get_var;nr01;0 0_define:0_chars_pattern:;;charpat;nr01;0 0_define:0_chars_args:;;chararg;nr01;0 0_define:0_specialchar:;;specialc;nr01;0 0_define:0_def_mcall:;;defmcall;nr01;0 0_define:0_atlast:;;at_last;nr01;0 0_define:0_mset:;;macroset;nr01;0 0_define:0_char:;;num2chr;nr01;0 0_define:0_program:;;program;nr01;0
Setting the defaults. These are different from the defaults of m0.
The second line defines the special characters for the arguments. This is missing the fifth character.
It is thus not possible to select arguments from other stacks than the first stack.
0_chars_pattern:{}-\@+*?~
0_chars_args:1%*@#;101;102;103;gpmexec
0_chars_args:1%*@#;101;102;103;gpmdef
0_chars_args:1$*@#$;111;112;103
Applicable for: all
In the next part a macro with a pattern is used to have quoting in the macros in the
default macro set 0 operational.
The quoting is thus not a builtin function in m0.
0_set_var:111;+-+
0_set_var:112;-+-
0_pattern:0string;{@00-@ff}~;stack_b 1 stack_a begin-1
0_pattern:0string;+-+;stack_b 1 +
0_pattern:0string;-+-;stack_b 1 - dup =0 if stack_a end stop endif
0_define:+-+;$1;;nn00 ;0string
Applicable for: all
In the next part a macro with a pattern is used to have quoting in gpm in
a similar way as m6.
0_set_var:101;<
0_set_var:102;>
0_set_var:103;:
0_pattern:string;{@00-@ff}~;stack_b 1 stack_a begin-1
0_pattern:string;<;stack_b 1 +
0_pattern:string;>;stack_b 1 - dup =0 if stack_a end stop endif
0_define:<;%1;;nn00 ;string;;;gpmexec
Applicable for: all
The collecting of arguments in gpm macros is executed with a pattern named gpm
containing small programs.
This pattern will also recognise the arithmetic for the
BAR macro. It will push the operators to the
operator stack. The BAR macro can execute these
operators.
0_pattern:gpm;{@00-@ff}~;stack_a begin-1
0_pattern:gpm;:;stack_a end begin
0_pattern:gpm;];stack_a end stop
0_pattern:gpm;:+:;stack_a 0 oppush +
0_pattern:gpm;:-:;stack_a 0 oppush -
0_pattern:gpm;:*:;stack_a 0 oppush *
0_pattern:gpm;:/:;stack_a 0 oppush /
Applicable for: all
The emulation is not using the default argument substitution, because the local defined macros should be deleted at the end of a macro. The argument substitution is executed at the end of a macro and is therefore the correct place to delete local macros.
Stack h holds the names of macros defined. This stack is not
freed at the end of a DEF.
A char with value 0 is used in macros to execute the deletion of
the macros whereby the name of the macros are stored on stack h.
A char with value 3 is used in the DEF macro to execute the deletion of
macros and to store the defined macro on stack h.
A char with value 2 is used in the VAL macro to output
the second argument and delete the last char (the char with value 0).
0_define:0_zero;0_char:0
;;nn00;;;;0
0_define:0_three;0_char:3
;;nn00;;;;0
0_define:0_two;0_char:2
;;nn00;;;;0
0_pattern:arggpm;%{0-9};stack_b getlast1 getarg putout
0_pattern:arggpm;0_zero;+-+ stack_a pop_to_2 "gpmdef"
stack_h while_st putarg1 "pop" fun_call pop endwhile
"" putout -+-
0_pattern:arggpm;0_three;+-+ stack_a pop_to_2 copyto_c "gpmdef"
stack_h while_st putarg1 "pop" fun_call pop endwhile
copyfr_c free_no "" putout -+-
0_pattern:arggpm;0_two;stack_b getarg2 putout "" putout
Applicable for: all
The main macro will collect all arguments including the macro name
and will make an mcall to the macro.
This is therefore a relatively simple macro.
0_def_mcall:[;;gpmdef;rr00;gpm;;gpmexec
Applicable for: all
The macros will be defined in the gpmdef macro set.
They will be called from the main macro. Therefore the argument collection has no significance.
A program to set all the options for the define or
push function is defined to be used by the DEF and
the UPDATE macro. The definition of a new macro is
appended with a zero character to activate the deletion of
macros at the end of a macro by the arggpm argument substitution.
0_program:defargs;+-+ stack_a pop_to_3 "" "nn00" "gpm" "arggpm" "" "gpmdef" stack_c getarg2 -+-"0_zero"+-+ cat putarg2-+-
The DEF uses the push function so that
it can also be popped at the end of macros.
0_define:DEF;0_three;push;nn00;gpm;arggpm; @defargs ;gpmdef
Applicable for: all
Is similar to DEF, but uses a define instead of
a push.
0_define:UPDATE;0_zero;define;nn00;gpm;arggpm; @defargs ;gpmdef
Applicable for: all
This macro has nothing to do. In m0 integers and strings are
automatically converted.
0_define:BIN;%10_zero;;nn00;gpm;arggpm;;gpmdef
Applicable for: all
Similar to BIN.
0_define:DEC;%10_zero;;nn00;gpm;arggpm;;gpmdef
Applicable for: all
Links to info, but needs to also output char with
0 to the output.
0_define:VAL;0_two0_zero;info;nn00;gpm;arggpm;recur_n;gpmdef
Applicable for: all
The operators are already stored by the gpm pattern.
So operator needs only to be executed here.
0_define:BAR;%20_zero;;nn00;gpm;arggpm;stack_a opexall;gpmdef
Applicable for: all
Finally switch to the gpmexec macro set.
0_mset:gpmexec
Previous: GPM emulation example, Up: Emulation examples [Contents][Index]
The C pre processor emulation is an example of defining a macro language in m0.
The C pre processor is called a macro processor, but it intentionally lacks a lot of the characteristics of a macro processor. It is especially geared towards supporting the programming in C (obviously!) instead of supporting general macro processing.
That CPP differs from a general macro processor can be seen in the emulation by the use of specific argument collection patterns per directive and the handling of the exceptions to support the C programming.
The following text of the emulation is generated from a source file for both text and code.
From this source file different versions of cpp are generated. These versions are:
krThe original K&R version of cpp, see Unix Time-Sharing System, UNIX programmer’s manual, Seventh Edition, Volume 2, Bell Laboratories, 1983.
c90The version defined in the standard C90 (ANSI C).
c99The version defined in the standard C99.
Code for all versions is indicated with all.
The c preprocessor emulation is performed in 2 stages, to keep edge cases easy to handle:
Applicable for: all
At the start the macros used to define the macros for cpp are defined.
This is all straightforward linking of macro names to builtin functions.
0_define:0_pattern:;;pattern;nr01;0 0_define:0_chars_pattern:;;charpat;nr01;0 0_define:0_chars_args:;;chararg;nr01;0 0_define:0_specialchar:;;specialc;nr01;0 0_define:0_set_var:;;set_var;nr01;0
Setting the defaults. The first is also the default of m0.
The second line defines the special characters for the arguments.
0_chars_pattern:[]-\@+*?~ 0_chars_args:2$*@#$;101;102;103;cpp 0_chars_args:2$*@#$;111;112;103
Applicable for: all
In the next part a macro with a pattern is used to have quoting in the macros in the
default macro set 0 operational.
The quoting is thus not a builtin function in m0.
0_set_var:111;+-+ 0_set_var:112;-+- 0_pattern:0string;[@00-@ff]~;stack_b 1 stack_a begin-1 0_pattern:0string;+-+;stack_b 1 + 0_pattern:0string;-+-;stack_b 1 - dup =0 if stack_a end stop endif 0_define:+-+;$1;;nn00;0string
Applicable for: all
Trigraphs are replaced.
0_define:??(;[;;rn00;;;;cpp
0_define:??);];;rn00;;;;cpp
0_define:??<;{;;rn00;;;;cpp
0_define:??>;};;rn00;;;;cpp
0_define:??=;#;;rn00;;;;cpp
0_define:??/;\;;rn00;;;;cpp
0_define:??';^;;rn00;;;;cpp
0_define:??!;|;;rn00;;;;cpp
0_define:??-;~;;rn00;;;;cpp
Applicable for: all
Backslash newlines are deleted.
Using a special character for the \.
0_specialchar:\;\ 0_define:+-+[@00-@5b@5d-@ff]\\ -+-;;;nn10;;;;cpp
Applicable for: all
Finally switch to the cpp macro set.
0_define:0_mset:;;macroset;nr01;0 0_mset:cpp
Applicable for: all
At the start the macros used to define the macros for cpp are defined.
This is all straightforward linking of macro names to builtin functions.
0_define:0_pattern:;;pattern;nr01;0 0_define:0_pat_copy:;;copy_pat;nr01;0 0_define:0_pat_append:;;append_p;nr01;0 0_define:0_set_var:;;set_var;nr01;0 0_define:0_get_var:;;get_var;nr01;0 0_define:0_chars_pattern:;;charpat;nr01;0 0_define:0_chars_args:;;chararg;nr01;0 0_define:0_specialchar:;;specialc;nr01;0 0_define:0_def_mcall:;;defmcall;nr01;0 0_define:0_atlast:;;at_last;nr01;0 0_define:0_macro_part:;;part_m;nr01;0 0_define:0_macro_tag:;;tag_m;nr01;0 0_define:0_link:;;link_prt;nr01;0 0_define:0_pat_part:;;inter_p;nr01;0 0_define:0_pat_tag:;;tag_p;nr01;0 0_define:0_program:;;program;nr01;0 0_define:0_shell:;;shell;nr01;0
Setting the defaults. The first is also the default of m0.
The other lines define the special characters for the arguments.
0_chars_pattern:[]-\@+*?~ 0_chars_args:2$*@#$;101;102;103;cpp 0_chars_args:2%*@!%;101;102;103;cppdef 0_chars_args:2%*@!%;101;102;103;incl 0_chars_args:2$*@#$;111;112;103
Applicable for: all
In the next part a macro with a pattern is used to have quoting in the macros in the
default macro set 0 operational.
The quoting is thus not a builtin function in m0.
0_set_var:111;+-+ 0_set_var:112;-+- 0_pattern:0string;[@00-@ff]~;stack_b 1 stack_a begin-1 0_pattern:0string;+-+;stack_b 1 + 0_pattern:0string;-+-;stack_b 1 - dup =0 if stack_a end stop endif 0_define:+-+;$1;;nn00;0string
Applicable for: all
In the next part a macro with a pattern is used to have quoting operational in the source. Quoting can be nested. The nesting depth is counted in the pattern on stack b.
0_set_var:101;` 0_set_var:102;' 0_set_var:103;, 0_pattern:string;[@00-@ff]~;stack_b 1 stack_a begin-1 0_pattern:string;`;stack_b 1 + 0_pattern:string;';stack_b 1 - dup =0 if stack_a end stop endif 0_define:`;$1;;nn00a;string;;;cpp 0_define:`;%1;;nn00;string;;;cppdef 0_define:`;%1;;nn00;string;;;incl
Applicable for: all
A string with double quotes should not be changed by macros. A double quote preceded by a backslash should not be treated as a string start or end. To allow for empty strings and being able to detect the second double quote in the pattern, the pattern starts with the first double quote (see the second 1 for post size in the options).
0_pattern:str;[@00-@ff]~;begin 0_pattern:str;[@00-@5b@5d-@ff]";end-1 stop 0_define:[@00-@5b@5d-@ff]";"$1";;nn11";str;;;cpp
Applicable for: all
Also a comment is built as a macro. Comments are deleted or replaced with a space.
The patterns for the comment actually collect the comment. This is not necessary, but can be useful if another type of replacement text is wanted.
0_pattern:comment1;[@00-@ff]~;begin-1 0_pattern:comment1;*/;end stop
In K&R a comment is deleted.
0_define:/*;;;rn00;comment1;;;cpp
In later versions the comment is replaced by a space.
0_define:/*; ;;rn00;comment1;;;cpp
The // comment is only from C99 onwards supported.
0_pattern:comment2;[@00-@ff]~;begin-1 0_pattern:comment2;+-+ -+-;end stop 0_define://; ;;rn00;comment2;;;cpp
Applicable for: all
First some special characters are defined to make the definition of macros easier.
The "\ " is used at the end of a macro to mean all characters except characters valid for a name.
This ends the detection of a macro name.
The "\B" is used at the start of a macro to mean all characters except characters valid for a name.
This is used to not start a macro inside a word.
0_specialchar: ;[@00-@2f@3a-@40@5b-@5e@60@7b-@ff] 0_specialchar:B;[@00-@40@5b-@5e@60@7b-@ff]
Applicable for: all
The collecting of arguments in cpp macros is executed with a pattern named cpp_args
containing small programs.
It is similar to the argument collection in m4, but it will also delete
white space at the end of an argument. In addition if no arguments are found
the macro expansion is cancelled.
Further to emulate the behaviour of argument expansion and the use of #
the top level macro will not use argument recursion.
0_specialchar:(;[@21-@27@29-@ff] 0_pattern:cpp_args;[@00-@ff]~;stack_c 0 stack_h m_depth =1 ifthen no_macro 0_pattern:cpp_args;(;stack_c 1 + 0_pattern:cpp_args;[@00- ]*[\(]~;stack_c dup =0 ifthen cancel 0_pattern:cpp_args;[(,][@00- ]*[!-@ff];stack_a begin-1 ;1 0_pattern:cpp_args;[!-@ff][@00- ,)];stack_a setend-1 ;1 0_pattern:cpp_args;);stack_c 1 - dup =0 if stack_a pushend stop endif 0_pattern:cpp_args;,;stack_c dup <=1 if stack_a pushend endif
Applicable for: all
Directives start with a # and in versions after K&R can contain spaces in the front or back.
In K&R need to detect the newline first directly followed by the #
and then the name of the directive.
For this there is no need to have a start of directives, the start is
included in the definition name of the directive.
0_specialchar:s;[ @07-@09@0b]
In C90 and onward need to detect the newline first, possible spaces, the #.
Possible spaces after the # are in the name of the directive.
0_macro_part:+-+ [\s]*#-+-;Ls;cpp
0_macro_tag:dir_1;cpp
defineApplicable for: all
First a pattern to collect the arguments of the define is defined.
Using stack a for the name and definition of the macro. Stack b is used to store the names of the arguments. Stack c is used as information for the state of collection. The collection state = 0 at start, = 1 when collecting name of macro, = 2 when collecting argument names and = 3 when collecting the definition. This collection state is used to select if a program of a pattern part should run, since some pattern parts can be triggered at wrong moments.
The pattern will also remove the spaces in front and at the back of arguments and definition.
0_specialchar:a;[_a-zA-Z] 0_specialchar:A;[_a-zA-Z0-9] 0_pattern:define;[@00-@20]*[_a-zA-Z]~;stack_a begin-1 stack_c 0 0_pat_tag:define;def1 0_pat_append:define;[_a-zA-Z0-9]*[@00-@20]~;stack_c dup =0 if stack_a end-1 begin stack_c 1 endif 0_pat_tag:define;def2 0_pat_part:define;[_a-zA-Z0-9]*[(]~;stack_c dup =0 if stack_a end-1 stack_c 2 endif 0_pat_append:define;[@00- ]*[!-@ff]~;stack_c dup =2 if stack_b begin-1 endif;1 0_pat_tag:define;def3 0_pat_part:define;[_a-zA-Z0-9]*[@21-@27@29-@2f@3a-@40@5b-@5e@60@7b-@ff]~;stack_c dup =0 if stack_a end-1 "Wrong name for macro" errprint endif 0_link:def1;def2;def3 0_pattern:define;,[@00- ]*[!-@ff]; stack_c dup =2 if stack_b begin-1 endif;1 0_pattern:define;[!-@ff][@00- ,)];stack_c dup =2 if stack_b setend-1 ;1 0_pattern:define;);stack_c dup =2 if stack_b pushend stack_c 3 endif 0_pattern:define;,;stack_c dup =2 if stack_b pushend endif 0_pattern:define;)[@00- ]*[!-@ff];stack_c dup =3 if stack_a begin-1 endif;1 0_pattern:define;[!-@ff][@00- ];stack_a setend-1 ;1 0_pattern:define;+-+ -+-;stack_a pushend stop
define in C90 and laterApplicable for: c90 c99
The directive define is linked to the start of directives.
The define will call 1_define in the cppdef macro set to handle the define.
0_def_mcall:[\s]*define\s;1_define;cppdef;rn11;define;;cpp;Ll 0_link:dir_1;dir_1
define in K&RApplicable for: kr
The define will call 1_define in the cppdef macro set to handle the define.
0_def_mcall:+-+ #define\s-+-;1_define;cppdef;rn11;define;;cpp
cppdefApplicable for: all
The two types of macros (with and without arguments) have to be defined differently.
A separate macro set for the definition of the macros is used to shield the definition from other macros.
A main macro (1_define) is called from the define in the cpp macro set.
In this macro all the handling is done in the cppdef macro set.
The arguments in the definition are replaced with the default argument replacements (e.g. $1).
The 2_def_args defines macros to replace the arguments in the definition with the default arguments.
The 2_undef_args undefines these macros.
Notice that the default characters for argument substitution in the cppdef macro set
are changed
(% instead of $ and ! instead of #) to make the
the substitution in the different macro sets easier.
The C90 and C99 version prohibit recursive macros.
This is handled by the 3_blue and 3_enable macros in the cpp macro set.
0_program:setbase; 1 - dup >0 ifthen set_base 0_define:1_define;+-+2_def_args(%b*)m4_ifelse(%b!,0,`2_define:\B%1\ ;3_blue($0)'%2`3_enable($0);;rn11;;;;cpp ',`2_define:\B%1\ ;3_blue($0)'%2`3_enable($0);;rr11;cpp_args;;stack_h %b! @setbase;cpp ')2_undef_args(%b*) -+-;;rn00;;;;cppdef
The 3_blue will push the macro name with a definition consisting of the macro name.
The new macro is not recursive and will thus not expand.
0_define:3_blue(;;push;nn01;cpp_args;;stack_a pop_to_2 dup "\B" cat-1 "\ " cat swap "" "nn11a" "" "" "" "cpp";cpp 0_define:3_enable(;;pop;nn01;cpp_args;;stack_a pop_to_2 "\B" cat-1 "\ " cat "cpp";cpp
The K&R version does not prohibit recursive macros
0_define:1_define;+-+2_def_args(%b*)m4_ifelse(%b!,0,`2_define:\B%1\ ;'%2`;;rn11;;;;cpp ',`2_define:\B%1\ ;'%2`;;rr11;cpp_args;;;cpp ')2_undef_args(%b*) -+-;;rn00;;;;cppdef
The macros used in the 1_define are
defined here.
0_define:2_define:;;define;nn01;0;;;cppdef 0_pattern:defargs;[\(]~;stack_b argpos =0 ifthen abort 0_pattern:defargs;[@00-@ff]~; stack_c 1 stack_a "" "" "" "rn11" "" "" "" "cppdef" 0_pattern:defargs;[(,][@00- ]*[!-@ff];stack_b begin-1 ;1 0_pattern:defargs;[,)];+-+ stack_b end dup strlen >0 if "\B" cat-1 "\ " cat putarg1 stack_c dup "$" cat-1 putarg2 pop 1 + "define" fun_call endif -+- 0_pattern:defargs;); stop 0_define:2_def_args(;;;nn01;defargs;;;cppdef 0_pattern:undefargs;[\(]~;stack_b argpos =0 ifthen abort 0_pattern:undefargs;[@00-@ff]~; stack_a "" "cppdef" 0_pattern:undefargs;[(,][@00- ]*[!-@ff];stack_b begin-1 ;1 0_pattern:undefargs;[,)];+-+ stack_b end dup strlen >0 if "\B" cat-1 "\ " cat putarg1 "undefine" fun_call endif -+- 0_pattern:undefargs;); stop 0_define:2_undef_args(;;;nn01;undefargs;;;cppdef
Applicable for: all
This is an ifelse like the m4 ifelse including the argument syntax.
The pattern will do the comparisons
and set the correct answer.
0_pattern:m4if;[\(]~;stack_b argpos =0 ifthen abort 0_pattern:m4if;[@00-@ff]~;stack_c 0 stack_a "" 0 0_pattern:m4if;(;stack_c 1 + 0_pattern:m4if;);stack_c 1 - dup =0 if stack_a end ifcmpset stop endif 0_pattern:m4if;[(,][@00- ]*[!-@ff];stack_a begin-1 ;1 0_pattern:m4if;,;stack_c dup <=1 if stack_a end stack_d argnum 3 mod =0 if stack_a ifcmpset endif endif 0_define:m4_ifelse(;%1;;rr01;m4if;; stack_a argnum =1 ifthen pop argnum =2 ifthen pop pop;cppdef
Applicable for: c90 c99
An argument in a string should not be replaced. In K&R it should be replaced.
0_define:[@00-@5b@5d-@ff]";"%1";;nn11";str;;;cppdef
Applicable for: c90 c99
The # is used to put quotes around an argument.
0_specialchar:#;[@00-@22@24-@ff] 0_pattern:stringy;[\A]~;stack_a begin-1 ;1 0_pattern:stringy;\A\ ;stack_a end-1 goback1 stop 0_pattern:stringy;+-+ -+-;stack_a end stop "no argument after #" errprint;1 0_define:\##\#;`"'%1`"';;rn11;stringy;;;cppdef
Applicable for: c90 c99
The ## is used to concatenate the part in front and afterwards.
The macro just deletes all spaces.
It uses a virtual space to allow other macros to expand the text after the ##.
0_define:[\s]*##[\s]*\a;;;nn01 ;;;;cppdef
Applicable for: c99
Just replace the __VA_ARGS__ with the default to output all arguments.
In the macro the start of the list is set.
0_define:__VA_ARGS__;$*;;nn00;;;;cppdef
undef in C90 and laterApplicable for: c90 c99
The undef is using the argument collection pattern macroname.
0_pattern:macroname;[\a]~;stack_a begin-1 0_pattern:macroname;\A\ ;stack_a end-1 0_pattern:macroname;+-+ -+-; stack_a pop_to_2 "\B" cat-1 "\ " cat stop 0_macro_tag:dir_2;cpp 0_define:[\s]*undef\s;;undefine;nn11;macroname;; "cpp";cpp;Ll 0_link:dir_1;dir_2
undef in K&RApplicable for: kr
Similar to the C90 version.
0_define:+-+ #define\s-+-;;undefine;nn11;macroname;; "cpp";cpp
includeApplicable for: all
First a pattern to collect the arguments of the include is defined.
Using stack a for the filename if in <>.
Using stack b for the filename if in "".
0_specialchar:";[@00-@21@23-@ff] 0_pattern:inclarg;[<]~;stack_a begin no_macro 0_pattern:inclarg;>;stack_a end-1 stack_c 1 0_pattern:inclarg;["]~;stack_b begin no_macro;1 0_pat_append:inclarg;[\"]*";stack_b end-1 stack_c 1;1 0_pattern:inclarg;+-+ -+-;stack_c =0 if "Error in name of include" errprint endif stop
include in C90 and laterApplicable for: c90 c99
The directive include is linked to the start of directives.
The include will set search directories and eventually
call 1_include in the incl macro set to handle the include.
0_macro_tag:dir_3;cpp 0_define:[\s]*include\s;+-+3_define:`3_filename';3_findfile($1,3_sys_dirs)3_findfile($b0,3_local_dirs);;rn00;;;;cpp 3_push:`3_local_dirs';3_local_dirs,3_getdir(3_filename);;rn00;;;;cpp 3_include(3_filename) 3_pop:`3_local_dirs';cpp -+-;;rr11;inclarg;;;cpp;Ll 0_link:dir_1;dir_3
include in K&RApplicable for: kr
Same as the C90 version, just macro name is different.
0_define:+-+ #include\s-+-;+-+3_define:`3_filename';3_findfile($1,3_sys_dirs)3_findfile($b0,3_local_dirs);;rn00;;;;cpp 3_push:`3_local_dirs';3_local_dirs,3_getdir(3_filename);;rn00;;;;cpp 3_include(3_filename) 3_pop:`3_local_dirs';cpp -+-;;rr11;inclarg;;;cpp
Applicable for: all
0_define:3_define:;;define;nr01;0;;;cpp 0_define:3_push:;;push;nr01;0;;;cpp 0_define:3_pop:;;pop;nr01;0;;;cpp
inclApplicable for: all
A separate macro set incl is used to perform
the trigraph and backslash newline replacement of
the first stage
when including a file.
0_define:1_include;;include;rn00;;;;incl 0_def_mcall:3_include(;1_include;incl;rr01;cpp_args;;cpp
Applicable for: all
Trigraphs are replaced.
0_define:??(;[;;rn00;;;;incl
0_define:??);];;rn00;;;;incl
0_define:??<;{;;rn00;;;;incl
0_define:??>;};;rn00;;;;incl
0_define:??=;#;;rn00;;;;incl
0_define:??/;\;;rn00;;;;incl
0_define:??';^;;rn00;;;;incl
0_define:??!;|;;rn00;;;;incl
0_define:??-;~;;rn00;;;;incl
Applicable for: all
Backslash newlines are deleted.
Using a special character for the \.
0_specialchar:\;\ 0_define:+-+[@00-@5b@5d-@ff]\\ -+-;;;nn10;;;;incl
Applicable for: all
Default directories are set.
The 3_findfile wil select the directory
where the file was found.
0_define:3_local_dirs;.;;rn00;;;;cpp 0_define:3_sys_dirs;/usr/local/include,/usr/include;;rn00;;;;cpp 0_define:3_findfile(;;findfile;rr01;cpp_args;;;cpp
Applicable for: all
A macro to extract the directory from a string containing the file name including the directory.
0_pattern:getdir;[@00-@ff]~;stack_a begin-1 0_pattern:getdir;/;stack_a argpos =0 if setend-0 else setend-1 endif 0_pattern:getdir;);pushend stop 0_define:3_getdir(;$1;;nr00;getdir;;;cpp
ifApplicable for: all
This pattern will perform the evaluation of the expression
of the if. This is similar to the pattern for the eval macro of the
m4 emulation.
Stacks b and d are used for the calculations and parsing.
Stack c is used for the counting of brackets (( )).
Stack e is used for holding the status of argument collection for
the defined operator.
It will output one of the macros 3_then or 3_else in stack a.
0_specialchar:S;[@00-@2a@2b@2c@2d@2e@2f@3a-@41@43-@51@53-@57@59-@61@63-@71@73-@77@79-@ff] 0_specialchar:*;[@00-@29@2b-@ff] 0_specialchar:>;[@00-@3c@3f-@ff] 0_specialchar:<;[@00-@3b@3e-@ff] 0_specialchar:=;[@00-@20@22-@3b@3f-@ff] 0_specialchar:&;[@00-@25@27-@ff] 0_specialchar:|;[@00-@7b@7d-@ff] 0_specialchar:D;[@00-@2f@3a-@ff] 0_specialchar:R;[@00-@2f@3b-@40@5b-@60@7b-@ff] 0_pattern:ifelse;[@00-@ff]~;stack_c 0 stack_d 1 stack_e 0 0_pattern:ifelse;\S[0-9];stack_b begin+1 0_pat_append:ifelse;[0-9a-zA-Z:]*\R;stack_b end-1 stack_d pop_to_0 0 0_pattern:ifelse;\B\a;stack_e dup >0 if stack_b begin+1 endif 0_pat_append:ifelse;[\A]*\ ;+-+stack_e dup >0 if stack_b end-1 "\B" cat-1 "\ " cat "cpp" stack_d pop_to_0 0 stack_e pop 0 y_macro endif -+- 0_pattern:ifelse;\Bdefined\ ;stack_b 31 opexif>= m?_num stack_d 1 stack_e pop 1 no_macro 0_pattern:ifelse;+[0-9];+-+stack_d =0 if stack_b 19 opexif>= + else stack_b 0 30 opexif>= + endif -+- 0_pattern:ifelse;-[0-9];+-+stack_d =0 if stack_b 19 opexif>= - else stack_b 0 30 opexif>= - endif -+- 0_pattern:ifelse;+\D;stack_b 19 opexif>= + stack_d 1 0_pattern:ifelse;-\D;stack_b 19 opexif>= - stack_d 1 0_pattern:ifelse;\**\*;stack_b 20 opexif>= * stack_d 1 0_pattern:ifelse;/;stack_b 20 opexif>= / stack_d 1 0_pattern:ifelse;%;stack_b 20 opexif>= mod stack_d 1 0_pattern:ifelse;~;stack_b 29 opexif>= ~ stack_d 1 0_pattern:ifelse;!\=;stack_b 29 opexif>= ! stack_d 1 0_pattern:ifelse;<<;stack_b 18 opexif>= shift_l stack_d 1 0_pattern:ifelse;>>;stack_b 18 opexif>= shift_r stack_d 1 0_pattern:ifelse;\>>\>;stack_b 17 opexif>= > stack_d 1 0_pattern:ifelse;\<<\<;stack_b 17 opexif>= < stack_d 1 0_pattern:ifelse;>=;stack_b 17 opexif>= >= stack_d 1 0_pattern:ifelse;<=;stack_b 17 opexif>= <= stack_d 1 0_pattern:ifelse;==;stack_b 16 opexif>= = stack_d 1 0_pattern:ifelse;!=;stack_b 16 opexif>= != stack_d 1 0_pattern:ifelse;\&&\&;stack_b 15 opexif>= bit_and stack_d 1 0_pattern:ifelse;^;stack_b 14 opexif>= bit_exor stack_d 1 0_pattern:ifelse;\||\|;stack_b 13 opexif>= bit_or stack_d 1 0_pattern:ifelse;&&;stack_b 12 opexif>= and stack_d 1 0_pattern:ifelse;||;stack_b 11 opexif>= or stack_d 1 0_pattern:ifelse;(;stack_c 1 + dup >=2 if stack_b 0 oppush nop endif 0_pattern:ifelse;);stack_c 1 - dup >=1 if stack_b opexto nop endif 0_pattern:ifelse;+-+ -+-;stack_b opexall dup =0 if stack_a "3_else" else stack_a "3_then" endif stop
if in C90 and laterApplicable for: c90 c99
The directive include is linked to the start of directives.
It will output the macro 3_then or 3_else to select the
proper text block.
0_macro_tag:dir_4;cpp 0_define:[\s]*if\s;$1;;rr11;ifelse;;;cpp;Ll 0_link:dir_1;dir_4
if in K&RApplicable for: kr
Same as the C90 version, just macro name is different.
0_define:+-+ #if\s-+-;$1;;rr11;ifelse;;;cpp
Applicable for: all
Pattern to select first text block and delete the rest.
Stack b is used to count the if endif blocks.
0_pattern:3then;[@00-@ff]~;stack_a begin-1 stack_b 1 0_pattern:3then;+-+ [\s]*#-+-; 0_pat_tag:3then;then_1 0_pat_append:3then;[\s]*if\ ; stack_b 1 + 0_pat_tag:3then;then_2 0_pat_part:3then;[\s]*else\ ; stack_b dup <=1 if stack_a pushend begin endif 0_link:then_1;then_2 0_pat_tag:3then;then_3 0_pat_part:3then;[\s]*endif\ ; stack_b 1 - dup <=0 if stack_a pushend goback1 stop endif 0_link:then_1;then_3 0_pat_tag:3then;then_4 0_pat_part:3then;[\s]*elif\ ;stack_b dup <=1 if stack_a pushend begin endif 0_link:then_1;then_4 0_pattern:3then;+-+ -+-; stack_a setend-0
3_then to select first text blockApplicable for: all
This macro is used when the if compare is true.
0_define:3_then;$1;;Rn00;3then;;;cpp
Applicable for: all
Pattern to select the else text block.
Stack b is used to count the if endif blocks.
Similar to the 3then pattern, but will stop at
#elif and output 3_if macro.
0_pattern:3else;[@00-@ff]~;stack_a begin-1 stack_b 1 0_pattern:3else;+-+ [\s]*#-+-; 0_pat_tag:3else;else_1 0_pat_append:3else;[\s]*if\ ; stack_b 1 + 0_pat_tag:3else;else_2 0_pat_part:3else;[\s]*else\ ; stack_b dup <=1 if stack_a pushend begin endif 0_link:else_1;else_2 0_pat_tag:3else;else_3 0_pat_part:3else;[\s]*endif\ ; stack_b 1 - dup <=0 if stack_a pushend goback1 stop endif 0_link:else_1;else_3 0_pat_tag:3else;else_4 0_pat_part:3else;[\s]*elif\ ; stack_b dup <=1 if stack_a pushend pop_to_2 "3_if" goback1 stop endif 0_link:else_1;else_4 0_pattern:3else;+-+ -+-; stack_a setend-0
3_else to select else text blockApplicable for: all
This macro is used when the if compare is false.
0_define:3_else;$2;;Rn00;3else;;;cpp
3_if to test next partApplicable for: all
This macro is used for performing an if like #if.
0_define:3_if;$1;;Rr00;ifelse;;;cpp 0_define:3_eval;$b0;;Rr00;ifelse;;;cpp
ifdef and ifndef in C90 and laterApplicable for: c90 c99
Using pattern macroname that
is also used by undef to get macro name.
0_macro_tag:dir_5;cpp 0_define:[\s]*ifdef\s;;ifmacro?;rn11;macroname;; "3_then" "3_else" "cpp" ;cpp;Ll 0_macro_tag:dir_6;cpp 0_define:[\s]*ifndef\s;;ifmacro?;rn11;macroname;; "3_else" "3_then" "cpp" ;cpp;Ll 0_link:dir_1;dir_5;dir_6
ifdef and ifndef in K&RApplicable for: kr
Same as the C90 version, just macro name is different.
0_define:+-+ #ifdef\s-+-;;ifmacro?;rn11;macroname;; "3_then" "3_else" "cpp" ;cpp 0_define:+-+ #ifndef\s-+-;;ifmacro?;rn11;macroname;; "3_else" "3_then" "cpp" ;cpp
Applicable for: c90 c99
Default macros.
0_define:\B__FILE__\ ;"$2";get_var;rn11;;; -5 ;cpp 0_define:\B__LINE__\ ;;get_var;rn11;;; -4 ;cpp 0_define:\B__DATE__\ ;"0_shell:echo -n "$(date +"%b %e %Y")" ";;rn11;;;;cpp 0_define:\B__TIME__\ ;"0_shell:echo -n "$(date +"%H:%M:%S")" ";;rn11;;;;cpp 0_define:\B__STDC__\ ;1;;rn11;;;;cpp
For C99 some more default macros are defined.
0_define:\B__STDC_VERSION__\ ;199901L;;rn11;;;;cpp 0_define:\B__STDC_HOSTED__\ ;1;;rn11;;;;cpp
Applicable for: test
To make text compare with other cpp easier empty lines are deleted.
0_define:+-+\s -+-;+-+ -+-;;rn00;;;;cpp 0_define:+-+ -+-;;;rn01;;;;cpp
Applicable for: all
Finally switch to the cpp macro set.
0_define:0_mset:;;macroset;nr01;0 0_mset:cpp
Previous: GPM emulation example, Up: Emulation examples [Contents][Index]