Quotes

M4 를 사용하는데 있어서 quotes 은 shell 에서와 같이 제일 중요한 부분입니다. M4 에서 quotes 은 다음과 같은 용도로 사용됩니다.

1 . 매크로 확장을 방지하기 위해

2 . `' 을 이용해 공백을 제거하고 token 을 분리 or 결합하기 위해

$ m4  <<\@                                 $ m4   <<\@
define(`_join',100)                        define(`_join',100)
_join         # quotes 이 없으므로            foo_join         # foo_join 은 새로운
@             # 정상적으로 확장된다.            @                # 이름을 구성한다.

100                                        foo_join
--------------------------------------------------------------

$ m4   <<\@                                $ m4   <<\@
define(`_join',100)                        define(`_join',100)
`_join'       # quotes 을 하면              foo`'_join         # `' 를 이용해
@             # 확장이 안된다.                @                  # token 을 분리

_join                                      foo100             # 공백없이 붙는다.
--------------------------------------------------------------

                                           $ m4   <<\@
                                           define(`_join',100)
                                           `foo'_join        # `foo' 와 분리되어
                                           @                 # _join 은 확장된다.

                                           foo100            # 공백없이 붙는다.

M4 에서 기본적으로 사용되는 quotes 은 왼쪽에는 ` ( backtick ) 문자를 사용하고 오른쪽은 ' ( apostrophe ) 문자를 사용합니다. 이렇게 다른 문자를 사용하는 이유는 quotes 을 nesting 해서 사용할 수가 있기 때문입니다.

M4 에서 매크로 확장은 CPP 의 function-like 매크로 에서처럼 전달된 인수 에서, 그리고 확장 결과에 대해서도 발생합니다. token 스트림을 읽어들이는 과정에서 앞서 정의된 매크로 이름이 발견되면 확장을 한 후에 다음 token 으로 넘어가는 것이 아니라 확장 결과를 다시 입력 스트림으로 넣어서 더 이상 확장이 발생하지 않을 때까지 읽어들이는 과정을 반복합니다.

다음 예제를 보면 bar 매크로를 define 한적이 없는데 barzoo 로 확장되는 것을 볼 수 있습니다. 이것은 두 번째 define 에서 foo 가 앞선 매크로 정의에 따라 먼저 bar 로 확장되었기 때문입니다.

$ m4 <<\@                    1. foo 는 현재 매크로 정의가 없으므로 그대로 foo 가 되고
define( foo, bar)               bar 도 현재 매크로 정의가 없으므로 그대로 bar 가 되어
define( foo, zoo)               foo 매크로는 bar 로 정의된다.
                             2. foo 매크로는 현재 bar 로 정의되어 있으므로 bar 로 확장되고
bar                             zoo 는 현재 매크로 정의가 없으므로 그대로 zoo 가 되어
@                               bar 매크로는 zoo 로 정의된다.
                             3. 결과적으로 define( bar, zoo) 와 같게 되어
zoo                             bar 매크로는 zoo 로 확장됩니다.

token 스트림을 읽어들이는 과정에서 매크로 이름을 만나도 확장을 하지않고 다음 token 으로 넘어가는 경우가 있는데 바로 해당 token 이 quote 되었을 경우 입니다.

$ m4 <<\@                      
define( foo, bar)                1. foo 매크로가 bar 로 정의됨.
define( `foo', zoo)              2. quotes 에 의해 foo 는 확장이 일어나지 않고 
                                    다시 foo 매크로가 zoo 로 정의됨.
bar                              3. bar 매크로는 이전에 정의된 적이 없으므로
@                                

bar                              4. 그대로 bar 가 된다.
----------------------------

$ m4 <<\@
define( foo, bar)
define( `foo', zoo)

foo                              # foo 매크로는 zoo 로 확장된다.
@

zoo

token 을 읽어들이고 확장하는 과정을 evaluate 한다고 하는데 매 단계 evaluate 마다 quotes 이 한 단계씩 strip off 됩니다.

1. cleanup 은 현재 매크로 정의가 없으므로 그대로 cleanup 이 되고
   `this is the cleanup message.' 는 확장없이 quotes 이 strip off 되어 
   cleanup 매크로는 this is the cleanup message. 로 정의된다.
2. cleanup 매크로가 확장되면 this is the cleanup message 가 되는데
   확장 결과를 살펴보면 cleanup 매크로가 존재하므로 다시 확장이 되고
   확장 결과를 살펴보면 cleanup 매크로가 존재하므로 다시 확장이 되고... 무한 반복된다.

$ m4 <<\@
define( cleanup, `This is the cleanup message.')
cleanup
@

this is the this is the this is the ....   <---- infinite loop
---------------------------------------------------

1. `this is the `cleanup' message.' 는 확장없이 quotes 이 strip off 되어 
   cleanup 매크로는 this is the `cleanup' message. 로 정의된다.
2. cleanup 매크로가 확장되면 this is the `cleanup' message 가 되는데
   cleanup 이름에 quotes 이 존재하므로 확장되지 않고 strip off 되어
   this is the cleanup message. 가 된다.

$ m4 <<\@    
define( cleanup, `This is the `cleanup' message.')    # cleanup 에 quotes 추가
cleanup
@

This is the cleanup message.
$ m4 <<\@                             
define( foo, `$0')                  1. foo 는 현재 매크로 정의가 없으므로
                                       그대로 foo 가 되고 `$0' 는 quotes 이 제거되어
foo                                    foo 매크로는 $0 로 정의된다.
@                                   2. foo 는 $0 로 확장되는데 $0 는 매크로 자신의
            Ctrl-c 종료                 이름과 같으므로 foo 로 확장되고 foo 는 다시
^C     <--- infinite loop              $0 로 확장되고... 무한 반복된다.
----------------------------

$ m4 <<\@
define( foo, ``$0'')                1. ``$0'' 는 quotes 이 한 단계 제거되어
                                       foo 매크로는 `$0' 로 정의된다.  
foo                                 2. foo 매크로는 `foo' 로 확장되고
@                                   3. `foo' 에서 확장이 중단되고
                                       quotes 이 제거되어 foo 가 된다. 
foo                                 4. 다음 token 을 읽어들임 ...

숫자나 특수문자는 매크로 이름이 될 수 없으므로 확장이 일어나지 않기 때문에 quote 을 하지 않아도 됩니다.

$ m4 <<\@              
define(`foo', `bar')          1. `foo' 는 확장없이 quotes 이 제거되어 foo 가 되고
define(`bar', 100)               `bar' 도 확장없이 quotes 이 제거되어 bar 가 되어
                                  foo 매크로는 bar 로 정의된다.
foo                           2. `bar' 는 확장없이 quotes 이 제거되어 bar 가 되고
@                                 bar 매크로는 100 으로 정의 된다.
                              3.  foo 매크로는 bar 로 확장되고 다시
100                               bar 매크로가 100 으로 확장된다. 
--------------------------------

$ m4 <<\@
define(`foo', ``bar'')              1. foo 매크로는 `bar' 로 정의되고
define(`bar', 100)                  2. bar 매크로는 100 으로 정의된다.

foo                                 3. foo 매크로는 `bar' 로 확장되고
@
                                    4. `bar' 에서 확장이 중단되고  
bar                                    quotes 이 제거되어 bar 가 된다.
$ m4 <<\@
define(`foo', ``$*'')               1. foo 매크로는 `$*' 로 정의된다.

foo(100,200,300)                    2. $* 는 전체 원소를 나타내므로 정의에 따라
@                                      `100,200,300' 로 확장된다.

100,200,300                         3. quotes 이 제거되어 100,200,300 가 된다.
----------------------------

$ m4 <<\@
define(`foo', ``$@'')               1. foo 매크로는 `$@' 로 정의된다.

foo(100,200,300)                    2. $@ 는 각각의 원소를 quote 한 것이므로
@                                      ``100',`200',`300'' 으로 확장된다.

`100',`200',`300'                   3. quotes 이 한 단계 제거된다.

전달된 인수에서 prescan 에 의한 확장

$ m4 <<\@
define(`definenum', `define(`num', `99')')     1. definenum 매크로는
                                                  define(`num', `99') 로 정의된다.
num                                            2. num 매크로는 정의된 적이 없으므로
definenum num                                     그대로 num 이 된다.
@                                              3. definenum 매크로가 확장되면 정의에 따라
                                                  define(`num', `99') 가 실행된다.
num                                            4. num 매크로가 정의되었으므로
 99                                               99 로 확장된다.
-----------------------------------------

$ m4 <<\@
define(`definenum', define(`num', `99'))       1. define 매크로에 quotes 이 없으므로
                                                  prescan 에의해 확장이 되어
num                                               num 매크로는 99 로 정의되고 
definenum                                         definenum 매크로는 void 로 정의된다.
@                                              2. 이미 num 매크로가 정의된 상태이므로
                                                  99 로 확장되고 definenum 매크로는 
99                                                void 로 확장된다.
$ m4  <<\@                                  1. 전달된 인수가 prescan 에의해 quotes 이
define(`exch', ``$2', `$1'')                   한 단계씩 제거되어 exch 매크로는
define(exch(`expansion text', `macro'))        `$2', `$1' 로 정의 된다.
                                            2. 인수들은 확장없이 quotes 이 제거되고 
macro                                          exch 매크로 정의에 따라
@                                              `macro', `expansion text' 로 확장된다.
                                            3. define(`macro', `expansion text') 가되어
expansion text                                 macro 매크로는 expansion text 로 정의된다.
$ m4 <<\@
define(`args', ``NAME', `Marie'')     1. args 매크로는 `NAME', `Marie' 로 정의된다.
define(args)                          2. 전달된 args 가 prescan 에의해 확장되면 
NAME                                     define(`NAME', `Marie') 가 되고
                                         NAME 은 Marie 로 정의된다.

                                      1. define 매크로의 확장 결과는 void 이므로 
args(define(`args',`Rachel'))            args() 가 확장되면 NAME, Marie 가 된다.
args                                  2. 인수 부분에서 args 가 Rachel 로 재정의
@                                        됐으므로 args 값은 Rachel 이 된다.

Marie 

NAME, Marie 
Rachel

함수가 중첩될 경우 quotes 의 처리

아래와 같은 매크로 호출이 있을 경우 zoo 매크로의 확장 결과가 `The brown fox jumped over' 이면 그 결과가 두 번째와 같이 그대로 bar 매크로의 인수 값으로 사용되는 것입니다. 마찬가지로 bar 매크로의 확장 결과가 ``The brown fox jumped over the lazy dog'' 이면 그 결과가 세 번째와 같이 그대로 foo 매크로의 인수 값으로 사용됩니다. 인수 값으로 사용될 때는 prescan 에 의해 먼저 quotes 이 한 단계 제거되겠죠. 마지막으로 foo 매크로의 확장 결과가 ```The brown fox jumped over the lazy dog''' 이면 최종 결과에 대해서 main scan 이 발생하므로 실제 출력 결과는 ``The brown fox jumped over the lazy dog'' 가 됩니다.

foo( bar( zoo(`The brown fox')))                     # 첫 번째 

foo( bar(`The brown fox jumped over'))               # 두 번째 

foo(``The brown fox jumped over the lazy dog'')      # 세 번째

확장 결과에 따른 quotes 처리 주의할 점

$ m4 <<\@
define(`foo', a'a)           1. foo 매크로는 a'a 로 정의된다.
define(`echo', `$@')         2. echo 매크로는 $@ 로 정의된다.
foo                          3. foo 가 확장되면 a'a 가 된다.

echo(foo)                    1. 전달된 foo 인수가 prescan 에의해 확장이 되면 a'a 가 되고
@                               echo 매크로의 정의에 따라 $@ 는 `a'a' 로 확장된다.
                                확장 결과에 main scan 이 발생하면
a'a                             `a' 에서 quotes 이 제거되어 aa' 가 된다.
aa'
$ m4 <<\@
define(`l', `<[>')           1. 매크로 l 은 <[> 로 정의된다.
define(`r', `<]>')           2. 매크로 r 은 <]> 로 정의된다.
changequote(`[', `]')        3. defn 은 결과에 quotes 을 붙여 출력하므로
defn([l])defn([r])])            defn([l]) 는 [<[>] 로 확장되는데 여기서 처음 [ 문자는 
defn([l],[r])                   quote 의 시작이 되므로 마지막 ]) 까지 quote 처리가 되어 
@                               결과적으로 <[>]defn([r])) 가 된다.
                             4. defn([l],[r]) 에서 [l] 은 [<[>] 로 확장되고
<[>]defn([r]))                  [r] 은 [<]>] 로 확장되면 결과가 [<[>][<]>] 가 되므로
<[>][<]>                        main scan 에의해 quotes 이 제거되면 <[>][<]> 가 된다.

출력에 newline 을 추가하는 방법

M4 에서는 기본적으로 escape character 를 사용할 수 없기 때문에 출력에 \n 를 사용할 수 없습니다.

$ m4 <<\@
11111111
format(`The brown fox jumped over\n the lazy dog\n')  
22222222
@       
11111111
The brown fox jumped over\n the lazy dog\n         # newline 추가가 안된다.
22222222

따라서 newline 을 추가하려면 다음과 같이 quotes 을 활용해야 합니다.

$ m4 <<\@
11111111
format(`The brown fox jumped over the lazy dog
')                                               <---- trailing newline 추가
22222222
@       
11111111
The brown fox jumped over the lazy dog
                                                 <---- newline 이 추가됨
22222222
---------------------------------------

$ m4 <<\@
11111111
format(`The brown fox jumped over
the lazy dog

')
22222222
@
11111111
The brown fox jumped over
the lazy dog


22222222

changequote

changequote ( left, right )
changequote
매크로의 확장 결과는 void 입니다.

M4 는 shell 에서처럼 escape character 기능이 없습니다. 따라서 기본적으로 quotes 으로 사용되는 `( backtick ) 문자를 출력에 사용할 수 없는데요. 하지만 changequote built-in 매크로를 이용하면 quotes 문자 를 사용자가 임의로 변경해 사용할 수 있습니다. quotes 문자는 2 문자 이상 사용할 수 있고 quotes 문자를 변경해 사용한 후에 다시 디폴트 문자로 돌아가려면 changequote 매크로를 () 없이 사용하면 됩니다.

$ m4 <<\@
define(`escape', changequote(<!,!>)foo`bar<!!>changequote)
changequote(<!,!>)escape<!!>changequote
@

foo`bar     # changequote 을 이용하면 ` 문자를 출력할 수 있다.

아래는 GNU Autoconf 패키지의 m4 파일에서 발췌한 내용인데요. 여기서 [, ] 문자가 변경되어 사용되는 quotes 문자입니다. 스크립트 내용 중에는 sh 스크립트가 포함되는데 sh 에서는 backtick 문자가 명령 치환에 사용되고 single quotes 도 사용되므로 변경해서 사용하는 것입니다.

아래 내용 중 ac_cv_exeext=`expr "$ac_file" : ['[^.]*\(\..*\)']` 에서 바깥쪽 [, ] 문자는 안쪽의 [, ] 문자를 유지하기 위한 m4 quotes 입니다.

# _AC_COMPILER_EXEEXT_O
# ---------------------
# Check for the extension used when `-o foo'.  Try to see if ac_cv_exeext,
# as computed by _AC_COMPILER_EXEEXT_DEFAULT is OK.
m4_define([_AC_COMPILER_EXEEXT_O],
[AC_MSG_CHECKING([for executable suffix])
AS_IF([AC_TRY_EVAL(ac_link)],
[# If both `conftest.exe' and `conftest' are `present' (well, observable)
# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
# work properly (i.e., refer to `conftest.exe'), while it won't with
# `rm'.
for ac_file in `(ls conftest.exe; ls conftest; ls conftest.*) 2>/dev/null`; do
  case $ac_file in
    *.$ac_ext | *.o | *.obj | *.xcoff | *.tds | *.d | *.pdb ) ;;
    *.* ) ac_cv_exeext=`expr "$ac_file" : ['[^.]*\(\..*\)']`
          export ac_cv_exeext
          break;;
    * ) break;;
  esac
done],
. . .
. . .

define built-in 매크로 이름에 앞에 m4_ 가 붙은 것은 m4 명령의 -P 옵션 사용입니다.

Quotes 사용을 일시적으로 중지하기

changequote(,) 형식을 사용하면 quotes 사용을 중지할 수 있습니다.

$ m4 <<\@                                  $ m4 <<\@ 
define(`foo', 100)                         changequote(,)
changequote(,)                             define(`foo', 100)   # 매크로 이름에
                                                                # quotes 이 포함된다.
`foo'                                      `foo'
foo                                        foo
@                                          indir(`foo')
                                           @
`100'   # quotes 에서도 확장이된다.                    # 매크로 이름에 quotes 특수문자가 포함되어
100                                        `foo'   # 확장되지 않는다 (indir 을 이용해야 한다)
                                           foo
                                           100

다음은 autoconf 패키지의 m4 파일 내용 중 일부인데 sh 스크립트 내용 중에 [0-5] 가 존재하여 일시적으로 quotes 사용을 중지하고 다시 설정하는 것을 볼 수 있습니다.

    [if test -n "$gl_use_threads_default"; then
       gl_use_threads="$gl_use_threads_default"
     else
changequote(,)dnl                             # 일시적으로 quotes 사용을 중지
       case "$host_os" in
         osf*) gl_use_threads=no ;;
         cygwin* | msys*)
               case `uname -r` in
                 1.[0-5].*) gl_use_threads=no ;;
                 *)         gl_use_threads=yes ;;
               esac
               ;;
         *)    gl_use_threads=yes ;;
       esac
changequote([,])dnl                           # 다시 quotes 을 [ , ] 로 설정
     fi
    ])
  if test "$gl_use_threads" = yes || test "$gl_use_threads" = posix; then
. . .
. . .

사용자 정의 quote 매크로

스크립트를 작성하다 보면 quotes 추가하는 것이 필요할 때가 있습니다. 이때는 다음과 같이 quote, dquote, dquote_elt 매크로를 정의해 사용할 수 있습니다.

$ m4 -del -t dquote -t dquote-elt -t ifelse <<\@ 
define(`quote', `ifelse(`$#', `0', `', ``$*'')')
define(`dquote', ``$@'')
define(`dquote_elt', `ifelse(`$#', `0', `', `$#', `1', ```$1''',
                             ```$1'',$0(shift($@))')')

quote(`foo', `bar', `zoo')
dquote(`foo', `bar', `zoo')
dquote_elt(`foo', `bar', `zoo')
dquote(dquote_elt(`foo', `bar', `zoo'))
@

m4trace:5: -1- ifelse -> `foo,bar,zoo'               # quote()
foo,bar,zoo
m4trace:6: -1- dquote -> ``foo',`bar',`zoo''         # dquote()
`foo',`bar',`zoo'
m4trace:7: -1- ifelse -> ``foo'',dquote_elt(shift(`foo',`bar',`zoo'))
`foo',m4trace:7: -1- ifelse -> ``bar'',dquote_elt(shift(`bar',`zoo'))
`bar',m4trace:7: -1- ifelse -> ``zoo''               # dquote_elt()
`zoo'
m4trace:8: -2- ifelse -> ``foo'',dquote_elt(shift(`foo',`bar',`zoo'))
m4trace:8: -2- ifelse -> ``bar'',dquote_elt(shift(`bar',`zoo'))
m4trace:8: -2- ifelse -> ``zoo''
m4trace:8: -1- dquote -> ```foo'',``bar'',``zoo'''   # dquote(dquote_elt())
``foo'',``bar'',``zoo''
---------------------------------------------------------------------------

quote(`foo', `bar', `zoo')               --->   `foo,bar,zoo'               로 확장
dquote(`foo', `bar', `zoo')              --->   ``foo',`bar',`zoo''         로 확장
dquote_elt(`foo', `bar', `zoo')          --->   ``foo'',``bar'',``zoo''     로 확장
dquote(dquote_elt(`foo', `bar', `zoo'))  --->   ```foo'',``bar'',``zoo'''   로 확장

Quiz

다음 스크립트는 정상적으로 동작하는 것 같지만 문제가 있습니다. 어디가 잘못되었을까요?

$ m4 <<\@
define(`UL', `<ul>LI($1)</ul>')
define(`LI', `<li>$1</li>')

UL(`foo')
@                      

<ul><li>foo</li></ul>

만약에 UL 매크로의 인수 값으로 현재 정의되어 있는 매크로 이름을 전달하면 다음과 같이 정상적으로 동작하지 않게 됩니다.

$ m4 <<\@
define(`UL', `<ul>LI($1)</ul>')
define(`LI', `<li>$1</li>')

UL(`UL')      // 현재 정의되어 있는 매크로 이름을 인수로 전달
@

<ul><li><ul><li></li></ul></li></ul>
------------------------------------

1. define `UL' 은 확장없이 quotes 이 strip off 되어 UL 이 되고
   `<ul>LI($1)</ul>' 도 확장없이 quotes 이 strip off 되어 <ul>LI($1)</ul> 이 되어
   UL 매크로는 <ul>LI($1)</ul> 로 정의된다.

2. define `LI' 은 확장없이 quotes 이 strip off 되어 LI 이 되고
   `<li>$1</li>' 도 확장없이 quotes 이 strip off 되어 <li>$1</li> 이 되어
   LI 매크로는 <li>$1</li> 로 정의된다.

3. UL(`UL') 이 확장될 때 `UL' 은 quotes 이 strip off 되어 UL 이 되어
   <ul>LI(UL)</ul> 로 확장된다.

4. 확장된 결과를 살펴보면 LI 매크로와 UL 매크로가 존재하므로
   먼저 인수부분의 UL 매크로가 확장되어 <ul>LI(<ul>LI()</ul>)</ul> 가 된다.

5. 다시 확장된 결과를 살펴보면 LI() 매크로가 존재하므로 
   <ul>LI(<ul><li></li></ul>)</ul> 로 확장된다.

6. 다시 확장된 결과를 살펴보면 LI 매크로가 존재하므로
   <ul><li><ul><li></li></ul></li></ul> 로 확장됩니다.

따라서 현재 정의되어 있는 매크로 이름이 인수로 전달될 경우에도 정상적으로 동작하기 위해서는 다음과 같이 $1 에도 quote 을 해줘야 합니다. 이것은 매크로 확장은 quotes 이나 terminal token 을 만날때 까지 recursive 하게 확장되기 때문입니다.

$ m4 <<\@
define(`UL', `<ul>LI(`$1')</ul>')         // $1 에도 quotes 을 추가
define(`LI', `<li>`$1'</li>')           

UL(`UL')
@

<ul><li>UL</li></ul>
--------------------------------

1. define UL 매크로는 <ul>LI(`$1')</ul> 로 정의된다.

2. define LI 매크로는 <li>`$1'</li> 로 정의된다.

3. UL(`UL') 이 확장될 때 `UL' 은 quotes 이 strip off 되어 UL 이 되고
   <ul>LI(`$1')</ul> 정의에 따라 <ul>LI(`UL')</ul> 로 확장된다.

4. 확장된 결과를 살펴보면 LI 매크로가 존재하므로 LI(`UL') 가 확장되어
   <li>`$1'</li> 정의에 따라 <ul><li>`UL'</li></ul> 로 확장된다.

5. `UL' 은 확장없이 quotes 이 strip off 되고 최종적으로 
   <ul><li>UL</li></ul> 가 됩니다.

이번에는 ifelse built-in 매크로를 이용해 반복 기능을 구현하는 것입니다.
UL(`UL', `LI', `IT') 와 같이 여러 개의 값을 인수로 사용할 수 있습니다.

$ m4 <<\@
define(`UL', `<ul>IT($@)</ul>')
define(`LI', `ifelse(`$1', `', `', `<li>`$1'</li>')')
define(`IT', `ifelse($#, 1, `LI(`$1')', `LI(`$1')IT(shift($@))')')

UL(`UL', `LI', `IT')
@

<ul><li>UL</li><li>LI</li><li>IT</li></ul>
------------------------------------------

1. define UL 매크로는 <ul>IT($@)</ul> 로 정의된다.

2. define LI 매크로는 ifelse(`$1', `', `', `<li>`$1'</li>') 로 정의된다.

3. define IT 매크로는 ifelse($#, 1, `LI(`$1')', `LI(`$1')IT(shift($@))') 로 정의된다.

4. UL(`UL', `LI', `IT') 가 확장될 때 `UL', `LI', `IT' 는 quotes 이 strip off 되지만
   $@ 에 의해 다시 quotes 이 추가되어 <ul>IT(`UL', `LI', `IT')</ul> 로 확장됩니다.

5. 확장된 결과를 살펴보면 IT 매크로가 존재하므로 다음과 같이 확장된다.
   <ul>ifelse(`3', 1, `LI(`UL')', `LI(`UL')IT(shift(`UL', `LI', `IT'))')</ul>

6. 확장된 결과를 살펴보면 ifelse 매크로가 존재하므로 다음과 같이 확장된다.
   `3' --> 3 으로 1 --> 1 으로 `LI(`UL')' --> LI(`UL') ... 로 확장

   <ul>LI(`UL')IT(shift(`UL', `LI', `IT'))</ul>

7. 확장된 결과를 살펴보면 LI 매크로와 IT 매크로가 존재하므로
   LI 매크로는 ifelse(`UL', `', `', `<li>`UL'</li>')  --->  <li>`UL'</li> 로 확장되고
   IT 매크로는 ifelse(`2', 1, `LI(`LI')', `LI(`LI')IT(shift(`LI', `IT'))')
   --->  LI(`LI')IT(shift(`LI', `IT')) 로 확장되어 최종 결과가

   <ul><li>`UL'</li>LI(`LI')IT(shift(`LI', `IT'))</ul> 가 된다.

8. 확장된 결과를 살펴보면 LI 매크로와 IT 매크로가 존재하는데 
   이것은 6. 번 확장 결과와 닮은 꼴이므로 반복이 되면 결과적으로

   <ul><li>UL</li><li>LI</li><li>IT</li></ul> 가 됩니다.