Local Variables
매크로 정의를 프로그래밍 언어에서 함수 정의와 같이 생각해볼 수 있습니다.
매크로 이름을 함수명으로, 매크로 body 를 함수 body 로 생각했을 때
local 변수는 어떻게 구현할 수 있을까요?
다음 예제를 보면 global 변수에 해당하는 foo
, bar
의 초기값이 각각 100
, 200
으로 설정되었지만 add
매크로를 호출하고 난후에는 각각 111
, 222
로 변경된 것을
볼 수 있습니다.
$ m4 <<\@
define(`foo', 100)dnl
define(`bar', 200)dnl
define(`add',
`define(`foo', $1)define(`bar', $2)eval( foo + bar )')dnl
foo
bar
add( 111, 222)
foo
bar
@
100
200
333
111 # foo, bar 값이 111, 222 로 변경됨
222
이것은 add
매크로의 body 가 실행될 때 define 에의해 새로운 값으로 정의되었기 때문인데요.
이때 define 대신에 pushdef
와 popdef
built-in 매크로를 사용하면 local 변수를 구현할 수 있습니다.
$ m4 <<\@
define(`foo', 100)dnl
define(`bar', 200)dnl
define(`add',
`pushdef(`foo', $1)pushdef(`bar', $2)eval( foo + bar )popdef(`foo', `bar')')dnl
foo
bar
add( 111, 222)
foo
bar
@
100
200
333
100 # foo, bar 값이 add 매크로 호출 전과 동일하다.
200
pushdef
pushdef ( MacroName )
pushdef ( MacroName, Replacement )()
가 없으면 매크로로 인식되지 않습니다.
pushdef 는 두 가지 형태로 사용할 수 있습니다. pushdef 을 할때 value 를 함께 정의할 수도 있고 먼저 이름만 pushdef 한 다음에 ( 이때 value 는 empty 가 됨 ) 나중에 define 매크로를 이용해 value 를 정의할 수 있습니다.
$ m4 <<\@
pushdef(`foo', `Expansion one.')dnl
foo
pushdef(`foo')dnl
foo
define(`foo', `Update two')dnl
foo
@
Expansion one.
# 값이 empty
Update two
popdef
popdef ( MacroName . . . )
()
가 없으면 매크로로 인식되지 않습니다.
매크로 이름을 일종의 stack 으로 볼 수 있습니다.
foo
라는 stack 에 pushdef
을 하면 현재 사용되고 있는 정의가 stack 에 저장되고
새로운 정의가 stack 의 top 에 위치하게 되어 사용됩니다.
현재 사용되고 있는 정의를 삭제하고 다시 이전 정의를 불러오려면 popdef
을 사용하면 됩니다.
매크로 정의가 존재하지 않는 상태에서의 pushdef
는 define
과 같고
stack 에 원소가 하나만 남았을 때 popdef
하는 것은 undefine
과 같습니다.
$ m4 <<\@ $ m4 <<\@
foo foo
define(`foo', `Expansion one.')dnl pushdef(`foo', `Expansion one.')dnl
foo foo
pushdef(`foo', `Expansion two.')dnl pushdef(`foo', `Expansion two.')dnl
foo foo
popdef(`foo')dnl popdef(`foo')dnl
foo foo
popdef(`foo')dnl popdef(`foo')dnl # undefine 과 같다.
foo foo
@ @
foo foo
Expansion one. Expansion one.
Expansion two. Expansion two.
Expansion one. Expansion one.
foo foo
stack 의 top 인 현재 정의를 변경할 때는 define
매크로를 사용하면 됩니다.
$ m4 <<\@
pushdef(`foo', `Expansion one.')dnl
foo
pushdef(`foo', `Expansion two.')dnl
foo
define(`foo', `Update two.')dnl # 현재 정의를 변경
foo
popdef(`foo')dnl
foo
@
Expansion one.
Expansion two.
Update two.
Expansion one.
undefine
매크로는 해당 stack 을 reset 시켜 undefined 상태로 만듭니다.
$ m4 <<\@
pushdef(`foo', `Expansion one.')dnl
foo
pushdef(`foo', `Expansion two.')dnl
foo
undefine(`foo')dnl
foo
@
Expansion one.
Expansion two.
foo # foo 는 undefined 상태가 된다.
Quiz
shift
built-in 매크로는 한 번에 하나씩만 인수를 shift 시킬 수 있는데요.
shift 횟수를 인수로 받을 수 있게 만들려면 어떻게 할까요?
$ m4 <<\@
define(`nshift', `pushdef(`num', eval($1))_$0(shift($@))popdef(`num')')
define(`_nshift', `ifelse( num, 0, `$@', `define(`num', decr(num))$0(shift($@))')')
nshift(2, `foo', `bar', `baz', `zoo' )
@
baz,zoo
-------------------------------------
1. nshift 는 pushdef(`num', eval($1))_$0(shift($@))popdef(`num') 로 정의된다.
1.1 pushdef 는 num 지역변수를 만들고 첫번째 인수값을 value 로 설정
여기서 eval 을 사용하면 인수값으로 산술식을 쓸수있는 장점이 있다.
1.2 $0 는 매크로 자신의 이름과 같으므로 _$0 는 _nshift 매크로 가 된다.
1.3 shift($@) 는 첫번째 인수를 제외한 나머지 인수를 전달
1.4 _nshift 매크로 실행이 완료되면 popdef 으로 사용이 완료된 지역변수 제거.
2. _nshift 는 ifelse( num, 0, `$@', `define(`num', decr(num))$0(shift($@))') 로 정의된다.
2.1 num == 0 이면 현재 $@ 값을 출력하고 종료
2.2 num != 0 이면 define(`num', decr(num))$0(shift($@)) 가 실행된다.
2.3 define 을 이용해 먼저 num 값을 -1 하고
2.4 $0 는 매크로 자신의 이름과 같으므로 다시 _nshift(shift($@)) 가 실행된다.
2.5 이때 shift($@) 매크로를 이용해 인수를 shift 시켜 전달한다.