M4
M4 는 원래 Brian Kernighan 과 P.J. Plauger 가 만든 매크로 프로세서가 시초입니다. Dennis Ritchie 가 이것을 발전시켜 M3 를 만들고 이것이 Rational FORTRAN 의 전처리기인 Ratfor 의 엔진으로 사용됩니다. 전처리기라고 해서 CPP 같은 수준이 아니고 프로그래밍 언어입니다. 당시 FORTRAN 은 주로 GOTO 와 statement number 를 이용해 프로그래밍을 했는데 아래 왼쪽과 같이 코드를 작성하면 Ratfor 가 오른쪽과 같이 변환해 줍니다. ( C++ 도 초기에는 전처리기를 거쳐 C 코드로 변환이 됐죠 )
if (a > b) { IF (.NOT.(A.GT.B)) GOTO 1
max = a MAX = A
} else { GOTO 2
max = b 1 CONTINUE # 왼쪽의 1, 2 숫자가
} MAX = B
2 CONTINUE # statement number
이후에 Brian Kernighan 과 Dennis Ritchie 가 UNIX operating system 을 위해 M3 를 확장해서 M4 를 만들고 Ratfor, C, Cobol 의 front-end 로 사용됩니다. 이전 매크로 프로세서와 달리 M4 는 M4 does not target any particular computer or human language 라서 general-purpose 매크로 프로세서입니다. 기본적으로 recursion 에 의해 동작하기 때문에 loop, recursion 을 쉽게 구현할 수 있습니다. ( while, for 같은 loop 문도 자기 자신이 계속 반복되는 것이므로 recursion 으로 쉽게 구현이 됩니다 ). 따라서 M4 에서는 infinite loop 와 stack overflow 가 기본입니다. 그리고 M4 는 일반 프로그래밍 언어와 같이 Turing-complete 합니다.
참조사이트: Generating code in M4
기본 사용법
M4 는 명령 라인에서 인수로 전달한 파일들을 확장자에 관계없이
앞에서부터 차례로 읽어들입니다.
이때 -D
( 매크로 define ) 옵션과 -U
( 매크로 undefine ) 옵션을
함께 사용할 수 있습니다.
$ cat base1.m4 $ cat base2.m4 $ cat hello.txt
define(`foo', 111) define(`bar', 222) hello foo bar zoo
---------------------------------------------------------
$ m4 base1.m4 base2.m4 hello.txt
hello 111 222 zoo
$ m4 -D zoo=333 base1.m4 base2.m4 hello.txt # -D zoo=333
hello 111 222 333
$ m4 -D zoo=333 base1.m4 -U foo base2.m4 hello.txt # -D zoo=333 -U foo
hello foo 222 333
$ m4 -D zoo=333 base1.m4 -D foo=1000 base2.m4 hello.txt # -D zoo=333 -D foo=1000
hello 1000 222 333
define
define ( MacroName )
define ( MacroName, Replacement )
매크로의 확장 결과는 void 입니다.()
가 없으면 매크로로 인식되지 않습니다.
M4 도 매크로가 사용되는 방식은 기본적으로 CPP 와 같습니다.
가령 FOO
매크로를 사용하려면 먼저 이전에 FOO
매크로가 정의되어 있어야 합니다.
매크로 이름 구성은 알파벳, 숫자, _
로 구성되고 맨 앞에 숫자가 올 수 없습니다.
확장이 되려면 매크로 이름 주위에 다른 문자가 와야 합니다.
다음 첫 번째의 경우 N
이 세 번 확장되지 않습니다.
매크로 이름 규칙은 매크로를 invoke 할 때 ( 사용할 때 ),
ifelse
매크로 사용시 적용되는 것으로 실제 매크로를 define 할 때는 이름으로 특수문자나 공백도 사용이 가능합니다. 해당 이름은ifdef
,defn
,indir
매크로에서 사용할 수 있습니다.
$ m4 <<\@ $ m4 <<\@
define( N, 200) define( N, 200)
if (NNN > 100) if (N > 100)
@ @
if (NNN > 100) if (200 > 100)
------------------------------
$ m4 <<\@
define(`Version2',A - 1)
99Version2:Version2_ Version22
@
99A - 1:Version2_ Version22
CPP 와 달리 M4 는 따로 object-like 매크로와 function-like 매크로 구분이 없습니다.
모두 define
built-in 매크로를 이용해 정의하고,
매크로를 정의할 때와 호출할 때 모두 이름과 괄호 사이에 공백이 있으면 안됩니다.
$ m4 <<\@ $ m4 <<\@
hello foo macro! define(foo) # 매크로 값이 empty
@
hello foo macro!
hello foo macro! @
hello macro!
--------------------------------------------------------
$ m4 <<\@ $ m4 <<\@
define(foo, m4) define(foo,$1 m4 $2)
hello foo macro! hello foo macro!
hello foo() macro! hello foo() macro!
hello foo(100) macro! hello foo(100) macro!
@ hello foo(,200) macro!
hello foo(100,200) macro!
hello m4 macro! hello foo(100,200,300) macro! # 인수가 3 개
hello m4 macro! hello foo (100,200) macro! # 괄호 앞에 공백
hello m4 macro! @
hello m4 macro!
hello m4 macro!
hello 100 m4 macro!
hello m4 200 macro!
hello 100 m4 200 macro!
hello 100 m4 200 macro!
hello m4 (100,200) macro!
------------------------------------------------------------
$ m4 <<\@
define(`Version2', A – 1)
Version2(arg1, arg2) Version2 (junk) garbage(trash)Version2()
@
A – 1 A – 1 (junk) garbage(trash)A – 1
매크로 body 에서 define 을 사용할 수도 있습니다.
$ m4 <<\@ $ m4 <<\@
define(`foo',`define(`bar', 100)') define(`foo',`define(`bar', 100)')
bar foo # foo 매크로가 확장된 후에는
@ bar # bar 매크로가 정의되므로
# bar 매크로가 정의되어 있지 @
bar # 않아 그대로 bar 가 된다.
100 # 100 이 된다.
다음은 H2
매크로를 사용할 때마다 H2_COUNT
값이 incr
( increment ) 된
값으로 재정의 됩니다.
$ m4 <<\@
define(`H2_COUNT', 0)
define(`H2',
`define(`H2_COUNT', incr(H2_COUNT))<h2>H2_COUNT. $1</h2>')
H2(First Section)
H2(Second Section)
H2(Conclusion)
@
<h2>1. First Section</h2>
<h2>2. Second Section</h2>
<h2>3. Conclusion</h2>
define 매크로가 실행됐을 때 반환값은
void
입니다.
따라서 위의H2
매크로 정의에서<h2>
태그 앞에 아무런 문자도 추가되지 않습니다.
value 앞의 공백은 제외된다.
매크로를 정의할 때 value 앞에 있는 공백은 출력에서 제외되지만
뒤에 있는 공백은 포함됩니다.
이것은 len
built-in 매크로를 이용해 인수 값의 길이를 구할 때도 동일하게 적용됩니다.
$ m4 <<\@
define(foo, m4 )
hello foo macro!
@
hello m4 macro! # value 앞에 있는 공백은 출력에서 제외된다.
-----------------------------
$ m4 <<\@
define(foo, $1 m4 $2 )
hello foo(100,200) macro!
@
hello 100 m4 200 macro!
------------------------------------
$ m4 <<\@
define(foo,$1 $2 $3 $4 $5 $6 $7 $8 $9 $10)
hello foo(100,200) macro!
@
hello 100 200 macro!
------------------------------------
$ m4 <<\@
define(foo,$1$2$3$4$5$6$7$8$9$10)
hello foo(100,200) macro!
@
hello 100200 macro!
------------------------------------
$ m4 <<\@
define(foo,$1`'$2`'$3`'$4`'$5`'$6`'$7`'$8`'$9`'$10)
hello foo(100,200) macro!
@
hello 100200 macro!
매크로를 공백 없이 붙여 사용하려면
다음은 plus
매크로를 +
로 정의한 후에 ++
를 만들려고 한 것인데요.
plusplus
의 경우는 매크로 이름이 달라지므로 사용할 수 없습니다.
$ m4 <<\@
define(`plus', `+')
plusplus
plus()plus # OK
plus`'plus # OK
@
plusplus
++
++
다음은 한 단계 indirection 을 한 것인데요.
oper()oper
의 경우 확장이 되면 plusoper
가 되므로 더 이상 확장이 일어나지 않게 됩니다.
매크로가 `'
로 분리되어 있을 경우는 각각에서 매크로 확장이 완료됩니다.
oper()
를 사용했을 경우도 ++
로 확장이 되려면 두 번째와 같이 oper 매크로 값에
`'
를 추가해 줍니다.
$ m4 <<\@ $ m4 <<\@
define(`plus', `+') define(`plus', `+')
define(`oper', `plus') define(`oper', `plus`'') # `' 를 추가
oper()oper oper()oper
oper`'oper oper`'oper
@ @
plusoper ++
++ ++
undefine
undefine ( MacroName . . . )
매크로의 확장 결과는 void 입니다.()
가 없으면 매크로로 인식되지 않습니다.
정의된 매크로를 삭제할 때는 undefine
built-in 매크로를 사용합니다.
$ m4 <<\@
define(`foo', 100)
foo
undefine(`foo')
foo
@
100
foo
한 번만 사용하는 매크로는 다음과 같이 정의합니다.
$ m4 <<\@
define(`msg', `undefine(`msg')Secret message.')
msg
msg # 두 번째 사용부터는 undefined 상태가 된다.
@
Secret message.
msg
defn
defn ( MacroName . . . )
()
가 없으면 매크로로 인식되지 않습니다.
defn
( definition ) 매크로는 MacroName
매크로의 정의에 quotes 을 붙여서 반환합니다.
인수가 2 개 이상 사용되면 각각 quotes 이 추가되고 공백 없이 결합되어 출력됩니다.
$ m4 <<\@
define(`message', `hello defn') 1. 매크로 message 는 hello defn 으로 정의된다.
defn(`message') 2. `message' 인수는 prescan 에 의해 message 가 된다.
@ 3. message 는 hello defn 으로 정의되므로 quote 을붙여
출력하면 `hello defn' 가 된다.
hello defn 4. 최종 결과에 main scan 이 일어나면 quotes 이 제거된다.
--------------------------------
$ m4 <<\@
define(`foo', bar) 1. 매크로 foo 는 bar 로 정의된다.
defn(foo) 2. 전달된 foo 인수가 prescan 에의해 확장되면 bar 가 된다.
@ 3. bar 에대한 정의는 존재하지 않으므로 결과는 empty 가 된다.
# empty
------------------------------
$ m4 <<\@
define(`name1', `foo') 1. 매크로 name1 은 foo 로 정의된다.
define(`name2', `bar') 2. 매크로 name2 는 bar 로 정의된다.
defn(`name1', `name2') 3. `name1' 은 prescan 에의해 name1 으로 확장되고 name1 은
@ foo 로 정의되므로 quote 을 하여 출력하면 `foo' 가 된다.
4. 마찬가지로 `name2' 는 `bar' 가 되고 두개가 결합되어
foobar 출력되면 `foo'`bar' 가 된다.
5. 마지막으로 main scan 에의해 quotes 이 제거되면 foobar 가된다.
-------------------------------
$ m4 <<\@
define(`foo', `This is `$0'') 1. 매크로 foo 는 This is `$0' 로 정의된다.
define(`bar', defn(`foo')) 2. defn(`foo') 인수가 prescan 에의해 확장되면
bar `foo' --> foo --> This is `$0' --> `This is `$0''
@ 가되어 define(`bar', `This is `$0'') 가 된다.
3. 매크로 bar 는 This is `$0' 로 정의된다.
This is bar 4. 매크로 bar 가 확장되면 This is bar 가 된다.
기본적으로 확장 결과에 quote 이 되어 출력되므로 더 이상 확장이 발생하지 않게 됩니다.
따라서 스트링을 값으로 갖는 매크로를 사용할 경우 defn
매크로를 사용해야 합니다.
다음과 같은 경우 defn
을 사용하지 않으면 매크로 값이 정상적으로 출력되지 않고
무한 loop 가 발생하거나 출력값이 변형됩니다.
$ m4 <<\@ $ m4 <<\@
define( `cleanup', `This is the cleanup message.') define( `foo', `$0')
defn(`cleanup') defn(`foo')
# cleanup # 무한 loop # foo # 무한 loop
@ @
This is the cleanup message. $0
------------------------------------------------
$ m4 <<\@
define(`string', `The macro dnl is very useful')
string
@
The macro # dnl 매크로에 의해 뒷부분이 삭제된다.
$ m4 <<\@
define(`string', `The macro dnl is very useful')
defn(`string')
@
The macro dnl is very useful
defn
을 이용해 built-in 매크로의 정의도 사용할 수 있습니다.
$ m4 <<\@
define(`zap', defn(`undefine')) 1. 매크로 zap 의 정의는 undefine 매크로와 같아진다.
zap(`undefine') 2. zap 을 이용해 기존의 undefine 매크로를 undefine.
undefine(`zap') 3. undefine built-in 매크로가 undefine 되어
@
undefine(zap) 4. 그대로 스트링으로 출력된다.
defn
은 MacroName
으로 공백이나 특수문자도 사용이 가능합니다.
$ m4 <<\@
define(`{my var}', `a strange one')
# 앞의 {my var} 는 매크로 이름 규칙에 맞지 않아
{my var} is defn(`{my var}'). # 확장이 안되지만 defn(`{my var}') 는 확장된다.
@
{my var} is a strange one.
----------------------------------
$ m4 <<\@
define(`_set', `define(`$1[$2]', `$3')')dnl
define(`_get', `defn(`$1[$2]')')dnl
_set(`myarray', 1, `alpha')dnl # define(`myarray[1]',`alpha') 가 된다.
_get(`myarray', 1) # defn(`myarray[1]') 가 된다.
_set(`myarray', `alpha', `omega')dnl # define(`myarray[alpha]', `omega') 가 된다.
_get(`myarray', _get(`myarray',1)) # defn(`myarray[alpha]') 가 된다.
defn(`myarray[alpha]')
@
alpha
omega
omega
indir
indir ( MacroName . . . )
()
가 없으면 매크로로 인식되지 않습니다.
정의된 매크로를 invoke 하는 ( 사용하는 ) 방법에는 direct call 과 indir
매크로를 이용한
indirect call 이 있습니다.
$ m4 <<\@ $ m4 <<\@
define(`msg', `{{ $1 }}') define(`msg', `{{ $1 }}')
msg(`hello') # direct call indir(`msg', `hello') # indirect call
@ @
{{ hello }} {{ hello }}
------------------------------------
$ m4 <<\@
indir(`define', `SIZE', 78)
SIZE
indir(`SIZE')
@
78
78
direct call 과 indirect call 은 다음과 같은 차이가 있습니다.
$ m4 <<\@
define(`foo', 100) 1. foo 매크로는 100 으로 정의된다.
foo( define(`foo', 200)) 2. define 매크로의 확장 결과는 void 이므로
foo foo() 가 확장되면 결과가 100 이 된다.
@ define 보다 먼저 foo 매크로가 실행중에 있는 상태이므로
인수 부분에서의 foo 재정의는 적용되지 않는다.
100 3. 인수 부분에서 foo 가 200 으로 재정의 됐으므로
200 foo 의 확장 결과는 200 이 된다.
-------------------------------
$ m4 <<\@
define(`foo', 100) 1. foo 매크로는 100 으로 정의된다.
indir(`foo', define(`foo', 200)) 2. define 매크로가 실행되어 foo 는 200 으로 재정의 된다.
foo indir 매크로에 의해 foo 가 확장되면 200 이 된다.
@ 3. 인수 부분에서 foo 가 200 으로 재정의 됐으므로
foo 의 확장 결과는 200 이 된다.
200
200
indir
는 MacroName
에 특수문자나 공백도 사용이 가능합니다.
$ m4 <<\@
define(`$$internal$macro', `Internal macro (name `$0')')
$$internal$macro # direct call
indir(`$$internal$macro') # indirect call
@
# direct call 은 매크로 이름 규칙에
$$internal$macro # 맞지 않아 확장되지 않는다.
Internal macro (name $$internal$macro)
indir
는 defn
과 달리 확장 결과에 자동으로 quotes 이 추가되지 않습니다.
$ m4 <<\@ $ m4 <<\@
define(`foo',100) define(`foo',100)
define(`name', `foo') define(`name', `foo')
defn(`name') indir(`name')
@ @
1. `foo' 1. foo
foo 2. foo 100 2. 100
builtin
builtin ( MacroName . . . )
()
가 없으면 매크로로 인식되지 않습니다.
M4 는 기본적으로 define
, undefine
, defn
매크로를 사용하여
built-in 매크로의 정의를 변경하거나 삭제하는 것도 가능합니다.
하지만 이와 같은 경우에도 builtin
매크로를 이용하면 기존의 디폴트 정의를 사용할 수 있습니다.
# 기존의 define 매크로를 create 로 변경하고 define 매크로는 undefine 을 이용해 삭제
$ m4 <<\@
define(`rename', `define(`$2',defn(`$1'))undefine(`$1')')
rename(`define',`create')
create(`vehicle',`truck') # create 를 이용해 vehicle 을 truck 으로 정의
vehicle
define(`fuel',`diesel')
fuel
@
truck # vehicle 은 truck 으로 확장된다.
define(fuel,diesel) # 기존의 define 매크로는 undefine 으로 삭제했으므로
fuel # 제대로 기능하지 않는다.
$ m4 <<\@
define(`TREE',`maple')dnl
undefine(`define',`undefine')dnl # define, undefine 두 매크로 모두 삭제
undefine(`TREE') # undefine(TREE)
TREE # maple
builtin(`undefine',`TREE')dnl # builtin 매크로를 이용해 기존의 undefine 매크로 사용
TREE # TREE
builtin(`define',`create',`builtin(`define',$@)')dnl # builtin 매크로를 사용해 create 를
create(`TREE',`ash')dnl # 기존의 define 매크로로 정의
TREE # ash
@
undefine(TREE)
maple
TREE
ash
m4 명령의
-P
옵션을 사용하면 built-in 매크로 이름에m4_
prefix 를 붙여 사용하는데builtin
매크로를 사용할 때는 붙일 필요가 없습니다.
dnl
M4 매크로 프로세서는 입력된 텍스트를 매크로 확장을 거쳐서 그대로 출력하기 때문에
define
같은 built-in 매크로를 사용할 때 입력한 newline 도 출력에 포함되게 됩니다.
따라서 불필요하게 공백 라인이 생기게 되는데요.
이때 dnl
( discard newline ) 매크로를 사용하면 입력된 위치부터 이후 newline 까지
포함해서 삭제됩니다.
따라서 comment 라인을 작성할 때도 사용됩니다.
dnl
라인에 존재하는 매크로는 실행되지 않습니다.
$ m4 <<\@ $ m4 <<\@
define( foo, 100) dnl comment line 111
define( bar, 200) dnl comment line 222
foo dnl comment line 333
bar define( foo, 100)dnl
@ define( bar, 200)dnl
<---- 2 개의 공백라인 foo
bar
100 @
200 100
200
changecom
changecom( [start], [end] )
changecom
m4 에서도 shell 에서처럼 #
문자를 이용해 comment 를 작성할 수 있습니다.
comment 내에서는 매크로 확장이 발생하지 않습니다.
그러니까 dnl
처럼 라인이 삭제되는 것이 아니고 매크로 확장이 suppress 되는 것입니다.
디폴트는 #
(start) ~ newline
(end) 문자인데
changecom
매크로를 이용해 사용자가 임의로 변경해 사용할 수 있습니다.
2 문자 이상의 스트링도 가능하고 ()
없이 사용하면 comment 기능이 disable 됩니다.
0. 디폴트는 # ...... newline
1. changecom( `!' ) ! ...... newline
2. changecom( `/*', `*/' ) /* ..... */
3. changecom comment 기능을 disable
$ m4 <<\@ $ m4 <<\@
define( foo, 100)dnl # define( foo, 100)dnl
define( bar, 200)dnl # define( bar, 200)dnl
foo foo
bar bar
@ @
100 # define( foo, 100)dnl
200 # define( bar, 200)dnl
foo
bar
----------------------------------------------------------
$ m4 <<\@ $ m4 <<\@
define( foo, 222) define( foo, `#222')
define( bar, 333) define( bar, 333)
111 foo bar 111 foo bar
@ @
111 222 333 111 #222 bar # bar 는 확장이 안된다.
-P , --prefix-builtins 옵션의 사용
Built-in 매크로와 이름 충돌을 피하기 위해 -P
옵션을 사용할 수 있습니다.
-P
옵션을 사용하면 built-in 매크로 이름 사용 시 m4_
prefix 를 붙여야 합니다.
$ m4 <<\@ $ m4 -P <<\@ # -P 옵션 사용
define(`M1',`text1')M1 define(`M1',`text1')M1
m4_define(`M1',`text1')M1 m4_define(`M1',`text1')M1
@ @
text1 define(M1,text1)M1
m4_define(M1,text1)text1 text1