Next: Index for m0 builtins and stack instructions, Previous: Programs for patterns and macros, Up: Top [Contents][Index]
| • M4 emulation example: | ||
| • M6 emulation example: | ||
| • GPM emulation example: |
The emulation examples are examples of defining a macro language in m0.
It shows the different possibilities and solutions possible with m0.
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 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 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 three 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.
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_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
Setting the defaults. This is also the default of m0.
0_chars_pattern:[]-\@+*?~
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
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;);stack_c 1 - dup =0 if stack_a end stop endif 0_pattern:m4;[(,][@00- ]*[!-@ff];stack_a begin-1 ;1 0_pattern:m4;,;stack_c dup <=1 if stack_a end endif
Applicable for: all
First some special characters are defined to make the definition of macros easier.
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 name of the builtin macro and the word "builtin"
to be used by this code to copy a builtin to a new name.
A macro to check for this and select the correct option is defined to handle this. The macro is actually a large program that:
The program uses the function calls, the macro is further empty because of this.
The builtin macro 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_specialchar: ;[@00-@2f@3a-@40@5b-@5e@60@7b-@ff] 0_specialchar:B;[@00-@40@5b-@5e@60@7b-@ff] 0_def_mcall:\Bdefine(;4_define;0;nr11 ;m4;;m4exec 0_def_mcall:define;4_define;0;nn00;m4;;m4builtin 0_setinfo:define;(;m4builtin 0_define:4_define;;;nn00;0;;+-+ stack_e getarg1 stack_f getarg2 stack_d getarg3 "builtin" strcmp if stack_g getarg4 stack_a pop_to_3 swap "m4builtin" "m4builtin" "copy" fun_call pop_to_1 "\B" copyfr_f cat copyfr_g cat "\B" copyfr_e cat copyfr_g 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 -+-;0
Applicable for: posix gnu bsd
Similar to the define. The only difference is the use of push instead of define.
0_def_mcall:\Bpushdef(;4_push;0;nr11 ;m4;;m4exec 0_def_mcall:pushdef;4_push;0;nn00;m4;;m4builtin 0_setinfo:pushdef;(;m4builtin 0_define:4_push;;;nn00;0;;+-+ stack_e getarg1 stack_f getarg2 stack_d getarg3 "builtin" strcmp if stack_g getarg4 stack_a pop_to_3 swap "m4builtin" "m4builtin" "pushcopy" fun_call pop_to_1 "\B" copyfr_f cat copyfr_g cat "\B" copyfr_e cat copyfr_g 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
Applicable for: posix gnu bsd
0_define:\Bpopdef(;;pop;nr11 ;m4;;stack_d "m4exec" putarg2 "\B" getarg1 cat "\ " cat putarg1;m4exec 0_define:popdef;;pop;nn00;m4;;stack_d "m4exec" putarg2 "\B" getarg1 cat "\ " cat putarg1;m4builtin 0_setinfo:popdef;(;m4builtin
Applicable for: all
0_define:\Bundefine(;;undefine;nr11 ;m4;;stack_d "m4exec" putarg2 "\B" getarg1 cat "\ " cat putarg1;m4exec 0_define:undefine;;undefine;nn00;m4;;stack_d "m4exec" putarg2 "\B" getarg1 cat "\ " cat putarg1;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;0_get_var:101 ;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:\Bchangequote\ ;0_changequote;0;nn11 ;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_clr_pattern:string 0_pattern:string;[@00-@ff]~;stack_b 1 stack_a begin-1 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_pattern:comment;[@00-@ff]~;begin-1 0_pattern:comment;+-+ -+-;end stop 0_define:#;+-+#$1 -+-;;nn00 ;comment;;;m4exec
Applicable for: posix gnu bsd
to do
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:\Bbuiltin(;;m4builtin;rr11 ;m4;;m4exec 0_def_mcall:builtin;;m4builtin;nn00;m4;;m4builtin 0_setinfo:builtin;(;m4builtin
Applicable for: all
0_define:\Binclude(;;include;rr11 ;m4;;;m4exec 0_define:include;;include;nn00;m4;;;m4builtin 0_setinfo:include;(;m4builtin
Applicable for: all
0_define:\Bsinclude(;;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 recurrently scanned for macros.
To have the builtin not to recurrently scan the output
the setting thereof is overruled with the recur_n.
0_define:\Bplace(;;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:\Bsplace(;;sinclude;nr11 ;m4;;;m4exec 0_define:splace;;sinclude;nn00;m4;;recur_n;m4builtin 0_setinfo:splace;(;m4builtin
Applicable for: all
0_define:\Bdivert\ ;;divert;nr11 ;m4;;;m4exec 0_define:divert;;divert;nn00;m4;;;m4builtin 0_setinfo:divert;\ ;m4builtin
Applicable for: all
0_define:\Bundivert\ ;;undivert;nr11 ;m4;;;m4exec 0_define:undivert;;undivert;nn00;m4;;;m4builtin 0_setinfo:undivert;\ ;m4builtin
Applicable for: all
0_define:\Bdivnum\ ;;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:\B__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:\B__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:\B__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:\B__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:\B0_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:\Berrprint(;;errprint;nr11 ;m4;;;m4exec 0_define:errprint;;errprint;nn00;m4;;;m4builtin 0_setinfo:errprint;(;m4builtin
Applicable for: posix gnu bsd
0_define:\Bm4exit(;;exit;nr11;m4;;;m4exec 0_define:m4exit;;exit;nn00;m4;;;m4builtin 0_setinfo:m4exit;(;m4builtin
Applicable for: all
0_define:\Bindex(;;strindex;rr11 ;m4;;;m4exec 0_define:index;;strindex;nn00;m4;;;m4builtin 0_setinfo:index;(;m4builtin
Applicable for: all
0_define:\Bsubstr(;;substr;rr11 ;m4;;;m4exec 0_define:substr;;substr;nn00;m4;;;m4builtin 0_setinfo:substr;(;m4builtin
Applicable for: all
0_define:\Btranslit(;;strtrans;rr11 ;m4;;;m4exec 0_define:translit;;strtrans;nn00;m4;;;m4builtin 0_setinfo:translit;(;m4builtin
Applicable for: posix gnu bsd
0_define:\Bm4wrap(;;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:\Besyscmd(;;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:\Bsyscmd(;;shell;nr11 ;m4;;;m4exec 0_define:syscmd;;shell;nn00;m4;;recur_n;m4builtin 0_setinfo:syscmd;(;m4builtin
Applicable for: all
0_define:\Bsysval\ ;;get_var;nn11 ;m4;;stack_a pop_to_1 -8;m4exec 0_define:sysval;;get_var;nn00;m4;;stack_a pop_to_1 -8 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:\Bmaketemp(;;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:\Bmkstemp(;;tempfile;nr11 ;m4;;;m4exec 0_define:mkstemp;;tempfile;nn00;m4;;recur_n;m4builtin 0_setinfo:mkstemp;(;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 special macro in the 0 macro set is called.
The information text of the macros in the m4builtin macro set is used to determine if a macro has optional arguments or not.
A builtin is returned quoted as name, builtin, info field of name,.
If the macro defn is not called from within another macro, then the builtin is not
output. This simulates (partly) the behaviour in m4.
0_def_mcall:\Bdefn(;4_defn;0;rr11 ;m4;;m4exec 0_def_mcall:defn;4_defn;0;nn00;m4;;m4builtin 0_setinfo:defn;(;m4builtin 0_define:4_defn;+-+0_ifmacro:+-+$1-+-;+-+4_level:+-+0_get_var:101 $1+-+-+-0_get_var:102 ,0_get_var:101 builtin0_get_var:102 ,0_get_var:101 4_inf_builtin:$1 0_get_var:102 ,-+- -+-;+-+4_inf_def:$1 -+-;m4builtin -+-;;rn00;0 0_define:4_inf_builtin:;+-+0_infofield:$1;m4builtin -+-;;rn01;0 0_define:4_inf_def:;+-+0_info:\B$1\ ;m4exec -+-;;rn01;0 0_define:4_level:;$1;;rr01;0;;stack_a m_depth <=3 ifthen pop;0
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.
0_define:\Bdnl\ ;;;+-+nn11 -+-;comment;;;m4exec 0_define:dnl;;;+-+nn00 -+-;comment;;;m4builtin 0_setinfo:dnl;\ ;m4builtin 0_define: 0_dnl;;;+-+nn00 -+-;comment;;;m4exec
Applicable for: all
0_define:\Bincr(;$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:\Bdecr(;$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:\Bshift(;$@;;rr11 ;m4;;base_=_1;m4exec 0_define:shift;$@;;nn00;m4;;base_=_1;m4builtin 0_setinfo:shift;(;m4builtin
Applicable for: gnu bsd
0_define:\Bindir(;$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:\Bifdef(;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:\Blen(;$1;;rr11 ;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:\Bifelse(;$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
Applicable for: all
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;testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest;stack_d 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:\Beval(;;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:\Bexpr(;;num2str;rr11 ;m4eval;;;m4exec 0_def_mcall:expr;0_eval;0;nn00;m4;;m4builtin 0_setinfo:expr;(;m4builtin
Applicable for: gnu bsd
Like in the builtin ifelse also here an intermediary macro is used for the builtin
macro.
0_pattern:m4format;[@00-@ff]~;stack_d 2 0_pattern:m4format;%[0-9];stack_e begin+0 0_append_pattern:m4format;[0-9]*\D;stack_e get_st+1 0_pattern:m4format;%[0-9]+s;stack_d copyto_e stack_e getarg copyto_d strlen - " " str* putoutst stack_d putout-0 1 + 0_pattern:m4format;%s;stack_d dup getarg putout 1 + 0_pattern:m4format;%[1-9][0-9]*d;stack_d copyto_e stack_e getarg 0 + copyto_d strlen - " " str* putoutst stack_d putout-0 1 + 0_pattern:m4format;%[0][0-9]+d;stack_e get_st+2 stack_d copyto_e stack_e getarg 0 + copyto_d strlen - "0" str* putoutst stack_d putout-0 1 + 0_pattern:m4format;%d;stack_d dup getarg 0 + putout 1 + 0_define:\Bformat(;;;rr11 ;m4;m4format;;m4exec 0_def_mcall:format;0_format;0;nn00;m4;;m4builtin 0_setinfo:format;(;m4builtin 0_define:0_format;4_format($@);;rn00;m4;;;0 0_define:4_format(;;;nr01;m4;m4format;;0
Applicable for: posix gnu bsd
to do
Applicable for: gnu bsd
to do
Applicable for: gnu bsd
to do
Applicable for: posix gnu bsd
to do
Applicable for: posix gnu bsd
to do
Applicable for: all
Finally switch to the m4exec macro set.
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_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 recurrent 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
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.
The DEF uses the push function so that
it can also be popped at the end of macros.
0_program:defargs;+-+ stack_a pop_to_3 "" "nn00" "gpm" "arggpm" "" "gpmdef" stack_c getarg2 -+-"0_zero"+-+ cat putarg2-+- 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: M6 emulation example, Up: Emulation examples [Contents][Index]