2편에서 튜링 머신과 튜링 완전한 프로그래밍 언어에 대해 설명했다.

이제 직접 튜링 완전한 프로그래밍 언어를 만들어보자.

 

교수님 습관으로 프로그래밍 언어 만들기

내가 만들 프로그래밍 언어는 사실 우리과 교수님의 말버릇이다.

이왕 만드는 거 재밌게 만드는게 좋으니까, 평소에 좋아하는 교수님의 말버릇을 주제로 만들어봤다.

(이런 글을 써도 되는지 모르겠지만, 혹시 문제가 되면 바로 지우겠습니다. 죄송합니다 교수님)

 

교수님의 평소 강의 말버릇이 조금 독특하신데,

영어 강의를 진행하실 때 명사 앞에 the 와 a를 거의 매번 같이 붙이신다.

 

그래서 듣다보면 디어,,, 디어,,,가 반복되는데, 어느새 우리 과 컴공 사람들은 대부분 아는 교수님의 시그니처가 됐다.

(다시 한 번 말하지만 교수님을 희화하하려는 것이 아닙니다... 저는 교수님 수업 정말 재밌게 잘 들었습니다. 교수님 사랑해요)

 

그리고 수업 중간 중간에 '이런건 앞으로 여러분들 인생에 아무짝에도 쓸모없는거죠' 라고 하면서 자주 설명을 해주신다.

근데 그렇게 말씀하셔놓고 설명도 자세하게 해주시고, 내용도 흥미로워서 재밌게 들었는데 이것도 넣어봤다.

 

언어의 구조는 2편에서 설명한 BrainFu**과 동일하게 만들었다.

포인터 증가
디이 포인터 감소
. 포인터가 가리키는 값 1 증가
, 포인터가 가리키는 값 1 감소
포인터에 저장된 값 ASCII 코드로 출력
어어 키보드로 값을 입력 받아 포인터에 저장
앞으로인생에있어서 반복문 시작
아무쓸모도없는거죠 반복문 종료(포인터 값이 0이 아니면 시작으로 이동)

 

재미를 위해서 코드의 시작과 끝에는 교수님의 시작과 끝 멘트인

'헬로에브리원투데이이즈' 와 ''오늘은여기까지하겠습니다' 를 넣어야 하도록 해봤다.

 

이제 이 언어로 HELLO WORLD를 출력해보자

헬로에브리원투데이이즈

디..... ..
앞으로인생에있어서
디..... .....
디..... .....
디..... .....
디..... .....
디이 디이 디이 디이,
아무쓸모도없는거죠

디.. 어
디, 어
디..... .어
어
디..... ....어

디...
앞으로인생에있어서
디..... .....
디이,
아무쓸모도없는거죠
디...어

디..... .....
앞으로인생에있어서
디..... ....
디..... ...
디..... ...
디..... ...
디..... ..
디이 디이 디이 디이 디이,
아무쓸모도없는거죠
디,,,어
디, 어
디..어
디,,,,어
디,,어

오늘은여기까지하겠습니다

컴파일러 동작 과정 알아보기

https://gobae.tistory.com/94

 

[컴파일러] 토크나이저, 렉서, 파서 (Tokenizer, Lexer, Parser)

컴파일러 [컴파일러] 컴파일러란? 컴파일러 컴파일러는 명령어 번역 프로그램이다. 컴파일러는 소스 코드 혹은 원시 코드를 목적 코드로 옮겨주는 역할을 한다. 쉽게 설명하면 여기서 소스 코드

gobae.tistory.com

 

 

아직 프로그래밍 언어나 컴파일러에 대한 수업을 들어보질 않아서, 네이버 블로그를 참고했다.

프로그래밍 언어가 기계화로 바뀌는 과정은 구문 분석, 최적화, 코드 생성, 링킹의 과정을 거친다고 한다.

 

그러나 우리는 실제 기계어로 변환해주는 컴파일러를 구현하지는 않을 거고, 여러 파일을 합쳐 실행파일을 만드는 기능도 필요없다.

(어차피못만든다)

최적화도 할 필요가 없다. 최적화가 필요하면 이런걸 쓰겠냐

 

그래서 구문 분석후 실행의 역할만 하면 된다.

그래서 컴파일러의 Tokenizer, Lexar, Parser를 이용한 구문 분석 과정을 조금 참고해서 구현해봤다.

 

Tokenizer은 코드를 최소한의 의미를 가지는 '토큰'으로 쪼개는 역할을 한다.

또한 이 과정에서 필요 없는 문자(공백, 주석) 등을 제거한다.

 

Lexar는 Tokenizer에 의해 쪼개진 토큰의 의미를 분석한다. (해당 토큰에 의미를 부여한다)

 

Parser는 의미가 부여된 토큰들을 계층 구조로 나타낸다.

 

해당 과정을 모두 거치고 나면 분석된 구문은 트리 형태로 정렬 된다.

이렇게 구문을 분석해 만든 트리를 AST(Abstract Syntax Tree)라고 한다.

 

Tokenizer 구현하기

class Compiler():

    def compile(self,filename):
        ...

    def tokenize(self, file):
        lines = file.readlines()
        tokens = []

        #split every lines by blank
        for line in lines:
            words = line.strip().split() #remove \n
            if words == []: continue #skip empty line

            for word in words:
                start = 0
                now = 0
                while now < len(word):
                    if word[now] == '디':
                        if not start == now:
                            tokens.append(word[start:now])
                        if now+1 < len(word) and word[now+1] == '이':
                            tokens.append(word[now:now+2])
                            now += 2
                            start = now
                        else:
                            tokens.append(word[now])
                            now += 1
                            start = now

                    elif word[now] == '어':
                        if not start == now:
                            tokens.append(word[start:now])
                        if now+1 < len(word) and word[now+1] == '어':
                            tokens.append(word[now:now+2])
                            now += 2
                            start = now
                        else:
                            tokens.append(word[now])
                            now += 1
                            start = now
                    elif word[now] == '앞' or word[now] == '아':
                        if not start == now:
                            tokens.append(word[start:now])
                        tokens.append(word[now:now+9])
                        now += 9
                        start = now
                    else:
                        now += 1

                if not start == now:
                    tokens.append(word[start:now])

        return tokens

 

우리 코드의 Token은 각각의 명령어들이다.

Compilier 클래스를 만들고 tokenize라는 메서드 함수를 만들어 구현했다.

 

우리 프로그래밍 언어에서 공백은 필요가 없기에 공백을 모두 제거하고, 명령어 단위로 묶어서 토큰을 만들었다.

그리고 보통 포인터 값 증가/감소 명령어인 . , 는 붙여서 많이 쓰이기 때문에 붙여서 토큰으로 인식해도 무방할 것 같아 그렇게 진행했다.

 

위의 예시의 HELLO WORLD 코드를 토큰으로 분해하면 다음과 같다.

['헬로에브리원투데이이즈', 
'디', '.....', '..', 
'앞으로인생에있어서', 
'디', '.....', '.....', 
'디', '.....', '.....', 
'디', '.....', '.....', 
'디', '.....', '.....', 
'디이', '디이', '디이', '디이', ',', 
'아무쓸모도없는거죠', 
'디', '..', '어', '디', ',', '어', 
'디', '.....', '.', '어', '어', 
'디', '.....', '....', '어', 
'디', '...', 
'앞으로인생에있어서', 
'디', '.....', '.....', '디이', ',', 
'아무쓸모도없는거죠', 
'디', '..', '어', 
'디', '.....', '.....', 
'앞으로인생에있어서', 
'디', '.....', '....', 
'디', '.....', '...', 
'디', '.....', '...', 
'디', '.....', '...', 
'디', '.....', '..', 
'디이', '디이', '디이', '디이', '디이', ',', 
'아무쓸모도없는거죠', 
'디', ',,,', '어', 
'디', ',', '어', 
'디', '..', '어', 
'디', ',,,,', '어', 
'디', ',,', '어', 
'오늘은여기까지하겠습니다']

 

AST 구현없이 실행시키기 

사실 여기서 의미를 부여하고 계층 구조를 만드는게 필요한가 싶었다.

토큰 자체가 하나하나 매우 간단한 명령어이고, 계층 구조를 만들 필요가 있는 건 반복문 뿐인데

계층 구조를 만드는거보다, 그냥 스택에다가 분기 명령어 위치 저장해놓고 쓰는게 더 편할 것 같았다.

    def excute(self, tokens):
        tape = [0]
        pointer = 0
        stack = []

        #checking syntax
        if not(tokens[0] == '헬로에브리원투데이이즈' and tokens[-1] == '오늘은여기까지하겠습니다'):
            print("Syntax Error!")
            exit(0)
        
        # for i in range(1, len(tokens)-1, 1):
        i = 1
        while i < (len(tokens) - 1):
            if tokens[i] == '디':
                pointer += 1
                if pointer >=  len(tape):
                    tape.append(0)
            elif tokens[i] == '디이':
                pointer -= 1
                if pointer < 0:
                    print("pointer underflow error!")
                    exit(0)
            elif tokens[i] == '어':
                # print(tape, pointer)
                print(chr(tape[pointer]), end="")
            elif tokens[i] == '어어':
                tape[pointer] = int(input())
            elif tokens[i] == '앞으로인생에있어서':
                if tape[pointer] == 0:
                    while not tokens[i] == '아무쓸모도없는거죠' and i < len(tokens):
                        i += 1
                else:
                    stack.append(i)
            elif tokens[i] == '아무쓸모도없는거죠':
                if tape[pointer] == 0:
                    stack.pop()
                else:
                    i = stack[-1]
            else:
                number = 0
                for char in tokens[i]:
                    if char == '.':
                        number += 1
                    else:
                        number -= 1
                
                tape[pointer] += number
            
            i += 1
        
        print('\0')

 

다시봐도 정말 쓰레기 같은 하드 코딩이다.

하지만 이걸 열심히 해봤자, 아무 도움이 안된다는 걸 알기에

심심풀이용으로 대충 만들어봤다.


실행 결과

잘 나온다

ㅋㅋㅋㅋㅋㅋㅋ

 

구현하면서 신기했던건, 문법이 간단하고 한 번 구조를 잘 만들어놓으니까 정말 프로그래밍 언어처럼 잘 동작한다.

 

중간에 다른거도 출력해보려고 코드를 바꿔봤는데 결과가 잘 안나와서,

내가 컴파일러 구현에 실수한게 있었나 하고 봤는데 알고보니까 코드에 띄어쓰기 오류가 있었다.

이 정도면 진짜 프로그래밍 언어가 아닐까?

 

물론 이걸 컴파일러라고 부르기엔 애매하고, 인터프리터라고 하는 게 더 알맞은 표현 아닐까 싶지만 이 정도는 그냥 넘어가자.

 

챗 지피티한테도 알려줬는데 잘 못한다.

너무 어려운걸 시켰나보다.


마무리하며

사실 시험기간에 그냥 심심해서 떠올린 아이디어가 생각보다 공부할게 많은 주제였던것 같다.

그리고 오토마타에서 추상적으로 다루기만 했던 내용들을 실제로는 이런거 만들때도 쓰이는구나 라는 것도 배울 수 있었다.

용두사미처럼 끝난 거 같은데 많은 분들이 재밌어 해주셨으면 좋겠다.

 

아 그리고 교수님 제가 많이 존경합니다.

이번학기 수업 재밌게 잘 들었습니다.

문제가 되면 지우겠습니다.

1편을 읽고 오신 분들이라면, 오토마타가 무슨 과목인지 감을 잡았을거라 생각한다. (그랬으면 좋겠다)

2편에선 '튜링 머신과 튜링 완전'에 대해 설명하고

3편에서 '난해한 프로그래밍 언어 만들기' 에 대해 설명해보려고 한다.


튜링 머신이란?

1편에서 괄호 문법 예시를 통해 문제를 해결하는 방법 (문제 => 문장과 언어 => 기계 )에 대해 설명했고,

PDA를 통해 '추상화한 기계' 에 대한 예시를 들어봤다.

 

튜링 머신 또한 특정한 문제를 해결하기 위한, 추상화 된 기계이다.

그러나 PDA보다 조금 더 강력한(?) 기계라고 할 수 있다.

  • 메모리로 테이프(가로로 길게 연결된 형태의 메모리)를 가진다. (길이는 무한하다고 가정한다)
  • Control Unit은 임의의 상태(State)를 가지며, 테이프의 값을 한 번에 하나씩 읽는다.
  • 테이프의 값을 읽은 후에는 상태를 변경하며, 테이프에 값을 새로 입력하고 테이프를 좌, 또는 우로 한 칸 이동시킬 수 있다.

 

튜링머신은 스택을 사용하는 PDA와는 다르게 좌우로 자유롭게 움직일 수 있는 메모리인 테이프를 사용한다.

그리고 이 테이프의 길이는 이론상 무한하다고 가정하기 때문에, PDA로는 해결할 수 없는 다양한 문제를 해결할 수 있다.


덧셈 계산기

임의의 숫자 2개가 주어졌을 때 덧셈을 하는 기계를 만들어야한다고 생각해보자.

이 기계는 단순히 문자열이 문장에 속하는 지 판단하여 끝나는 간단한 기계가 아니다.

예, 아니오 응답이 아닌 덧셈 결과를 응답해야한다.

우선 튜링 머신으로 덧셈 계산기를 구현하기 위해 덧셈 연산을 간단화해보자.

숫자를 단순하게 1의 연속으로 변경해보자. (이진법이 아님에 주의하자, 2는 1 두개, 3은 1 세개이다.)

 

그렇다면 이제 튜링 머신은 다음과 같은 연산을 진행하면 된다.

  1. 1을 만나면 1을 그대로 입력하고 오른쪽으로 이동한다.
  2. +를 만나면 그 자리에 1을 입력하고 오른쪽으로 이동한다.
  3. 테이프에서 공백을 만날때까지 1,2를 반복한다.
  4. 공백을 만나면 왼쪽으로 이동 후 1을 공백으로 변경한다.


보편 튜링 머신

튜링 머신은 강력한 기계이지만, 할 수 있는 일이 정해져있다는 한계가 존재한다.

 

  1. 1을 만나면 1을 그대로 입력하고 오른쪽으로 이동한다.
  2. +를 만나면 그 자리에 1을 입력하고 오른쪽으로 이동한다.
  3. 테이프에서 공백을 만날때까지 1,2를 반복한다.
  4. 공백을 만나면 왼쪽으로 이동 후 1을 공백으로 변경한다.

우리는 튜링 머신을 설계할때 앞서 설계한 것처럼, 특정한 상황에서의 동작만 정의할 수 있고, 이는 유한하다.

만약 우리가 설계한 범위를 벗어난 상황을 만나면(중간에 -와 같은 정의되지 않은 값을 만나거나, 4번까지 수행이 끝난 상황)

이 상황을 halt(정지)라고 부르며, 튜링 머신은 더 이상 동작할 수 없다.

 

이렇게 제한적인 튜링 머신의 사용 문제를 해결하기 위해 '보편 튜링 머신'이라는 게 등장했다.

보편 튜링 머신은 튜링 머신을 시뮬레이션 할 수 있는 머신이다.

 

1. 위에서 정의한 튜링 머신의 상황에 따른 동작 결과(1,2,3,4)를 표로 만들어 보편 튜링 머신에 저장해둔다.

2. 튜링 머신으로 실행시켜볼 내용(2+3)도 저장한다.

3. 저장된 상태표를 읽어 튜링 머신의 동작 과정을 그래프로 만들어낸다.

4. 실행 시킬 내용을 읽으며 실제로 수행해야할 작업을 그래프를 순회하며 수행한다.

 

이 과정을 통해 보편 튜링 머신은 모든 튜링 머신의 기능과 동작을 흉내낼 수 있다.

 

여기서 상태표를 우리가 설계하는 명령어, 실행시킬 작업을 데이터라고 하면,

명령어와 데이터를 함께 메모리에 적재하고 실행시키는 기계. 즉 폰 노이만 구조 컴퓨터의 원형이라고 할 수 있다.


튜링 완전

보편 튜링 머신에 튜링 머신의 상태표와 실행시킬 작업을 저장해 튜링 머신을 시뮬레이션 해볼 수 있다고 했다.

 

이때 상태표를 명령어(프로그래밍 언어), 실행시킬 작업을 데이터(실행시킬 코드)라고 하면,

즉 우리가 쓰는 폰 노이만 구조 컴퓨터를 추상화했다고 볼 수 있다.

 

그렇다면 튜링 머신과 동치인 언어, 즉 튜링 머신과 동일한 기능을 할 수 있는 언어가 있다면 "튜링 완전한 언어"라고 부른다.

그리고 튜링 머신의 기능은 사실 그리 복잡하지 않다.

  • 특정한 조건을 판단할 수 있다(조건 분기문이 존재한다 ex: if, goto...)
  • 임의의 메모리를 가리키는 포인터가 존재하고, 해당 포인터를 통해 메모리의 값을 변경할 수 있다.

현존하는 대부분의 프로그래밍 언어는 튜링 완전하다고 할 수 있다.

그러나 튜링 머신은 테이프의 길이가 무한하다고 가정하지만, 현실세계에서 무한한 메모리를 구현할 수는 없으므로

만약 메모리가 무한하다면 튜링 완전한 경우를 "느슨한 튜링 완전"이라고 부른다.


튜링 완전한 프로그래밍 언어 만들기

 

드디어 1편에서 설명한 '난해한 프로그래밍 언어 만들기' 에 대해 이야기할 차례이다.

 

BrainFu**

가장 작은 컴파일러로 구현할 수 있는 "완전 튜링한 언어 만들기" 를 목표로 해서 나온 언어이다.

이 프로그래밍 언어는 단 8개의 명령어만을 가지는데 다음과 같다.

> 포인터 증가
< 포인터 감소
+ 포인터가 가리키는 값 1 증가
- 포인터가 가리키는 값 1 감소
. 포인터의 값을 ASCII 문자로 출력
, ASCII 코드 값을 입력받아 포인터 메모리에 저장
[ 반복문 시작
] 반복문 종료(포인터의 값이 0이 아니면 ]로 돌아가 반복)

 

++++++++++
[>+++++++>++++++++++>+++>+<<<<-]
>++.>+.+++++++..+++.>++++++++++++++.------------.<<+++++++++++++++.>.+++.------.--------.>+.

 

이건 BrainFu** 언어로 Hello, world!를 출력하는 코드이다!

 

쓰레기 같은 가독성으로 어그로를 끌어 유명해졌지만, 이 언어는 "튜링 완전한 언어"이다.

임의의 테이프(메모리)를 가리키고 있으며, 메모리의 값을 수정 가능하고, 분기 명령어가 존재한다.

 

사실 이것 외에도 "엄랭", "재즈랭" 등 온라인 상에서 유행하는 밈을 이용해 '난해한 프로그래밍 언어 만들기'가 꾸준히 진행되고 있다.

https://esolangs.org/wiki/Main_Page

 

Esolang, the esoteric programming languages wiki

This wiki is dedicated to the fostering and documentation of programming languages designed to be unique, difficult to program in, or just plain weird. Why not join us on IRC? For readers Featured language Thue is an esoteric programming language based aro

esolangs.org

위키도 있다고 하니 궁금한 사람들은 찾아보자.

똥을 굳이 찍어먹어봐야하나

 

다음 글에선 "직접 튜링 완전한 언어" 를 만들고 컴파일러를 재작하는 과정을 알아보자.

왜 하냐고요?

 

그야 재밌으니까

ㅋㅋ

 

 

 

 

이번학기 전공으로 '오토마타' 라는 과목을 배웠다.

이번 학기 들었던 전공 중에서 가장 재미있게 들었던 과목 중 하나이지만,

자료구조, 어셈블리언어 같은 과목들에 비해 이걸 배워서 도대체 어따쓰지? 라는 생각이 계속해서 드는 과목이었다.

 

나만 이런 생각을 하는 건 아닐거라고 생각하고, 몇가지 글 들을 인터넷에서 뒤적거려봤고,

오토마타 과목의 마지막에 다루는 튜링 머신의 개념을 프로그래밍 언어와 컴파일러 설계에 활용할 수 있다는 사실을 알았다.

 

https://kciter.so/posts/crafting-esolang/#%ED%8A%9C%EB%A7%81-%EA%B8%B0%EA%B3%84

 

난해한 프로그래밍 언어 만들어보기

잉여 코딩이라는 말을 들어본 적 있는가? 잉여 코딩은 돈 버는데엔 쓸모없지만 취미로 즐겁게 코딩하는 것을 의미한다. 최근 몇 년 사이엔 대부분 업무와 직간접적으로 연관이 있는 코딩만 했는

kciter.so

그래서 위의 '난해한 프로그래밍 언어 만들어보기' 라는 글을 읽고

튜링 완전한 프로그래밍 언어와 컴파일러를 만드는 과정을 써보려고 한다.

 

이 시리즈를 통해 오토마타 과목을 수강한 학우분들은 '오토마타를 이런식으로 사용할 수 있구나' 라는 사실을

오토마타를 수강하지 않았지만 고민하는 학우분들에게 오토마타가 어떤 과목인지 대략적으로 전달할 수 있었으면 좋겠다.


1. 오토마타란?

Automata

오토마타는 추상적인 계산기로, 정해진 규칙에 따라 입력을 읽으면서 내부의 상태를 바꾸고 결과를 출력하는 기계의 수학적 모델이다. 단어 'automaton'은 '스스로 작동하는'을 뜻하는 그리스어 단어 'αυτόματος'에서 유래했으며, 한국어에서는 보통 복수형 'automata'를 음차한 '오토마타'로 지칭한다.  - 나무위키 -

 

여기서 핵심은 '기계의 수학적 모델'이다.

오토마타 과목에선 한 학기 동안 여러 문제를 해결할 수 위한 추상화되어 있는 기계들을 배운다.

 

여기서 문제는 보통 "어떤 문장이 한 언어에 속하는가?"로 먼저 추상화된다. 

 

문제 => 문장과 언어 => 기계

 

오토마타는 위와 같이 문제를 문장과 언어로 추상화해서 해결해주는 기계를 설계하는 과목이다.

이렇게만 이야기하면 하나도 이해가 안 가니 예시를 들어보자.


중첩된 괄호 문제

어떤 프로그래밍 언어의 문법에 괄호가 존재한다고 하자. 

실제 프로그래밍 언어의 괄호 문법을 적용하면 너무 복잡해지니, 조금 쉬운 괄호 문법을 예시로 들어보자.

 

이 프로그래밍 언어에선 아래와 같이 괄호를 사용할 수 있다.

  1. 괄호는 중첩해서 사용이 가능하다.
  2. 열린 괄호의 갯수만큼 닫는 괄호가 존재해야한다.
  3. 닫힌 괄호가 열린 괄호보다 먼저 나와선 안된다.

ex)

  • (( )) => O
  • ((( ))) => O
  • (( )))) => X

이제 이 프로그래밍 언어로 만들어진 코드의 괄호 문법을 검사하는 컴파일러를 우리가 만들어야한다고 하자.

컴파일러는 해당 프로그래밍 언어의 괄호 문법을 어떻게 효율적으로 검사할 수 있을까?

 

문제를 언어로 추상화하기

먼저 코드를 한 줄 씩 해석하며 열린 괄호와 닫힌 괄호를 만날때마다 각각 알파벳 a,b로 바꿔 문자열을 만들어보자.

그렇다면 올바른 괄호 구조를 사용한 코드의 경우 aabb, aaabbb 와 같은 구조의 문자열이 만들어질 것이다.

 

그렇다면 이제 어떤 언어를 다음과 같은 문자열들의 집합이라고 정의해보자.

 

이러면 프로그래밍 언어의 괄호 문법을 검사하는 과정은,

코드를 해석하며 만든 문자열(aabb, aabbb)이 위의 언어에 속하는가? 로 추상화된다.

정확하게는 문자열이 언어가 정의한 집합에 속하는가? 이지만 안 중요하니까 넘어가자.
어떤 문자열이 언어가 정의한 집합에 속하는 경우 그 문자열은 언어의 문장이다라고 이야기한다.

 

 

이제 별다른 설명이 없어도 괄호 문법을 만족하는 언어를 컴파일하며 만든 문자열(ex: aaabbb)은,

반드시 위의 언어에 속할거라는 사실을 우리는 쉽게 유추할 수 있다.

 

문장이 언어인지 판독하는 기계 만들기

그렇다면 다음은 어떤 유한한 길이의 문자열(ex: aaabbb)이 주어졌을때,

해당 문자열이 위의 언어에 속하는지 판단하는 기계를 설계하면 된다.

 

한 학기 동안 여러 종류의 기계를 배우지만 위와 같은 언어는

PDA(Pushdown Automata)를 통해 쉽게 판독할 수 있다.

 

PDA는 위와 같이 input file, Control unit, Stack 3가지 구조로 이루어진 간단한 기계이다.

조금 더 자세히 살펴보면 다음과 같은 기계를 추상화 한 것을 PDA라고 부른다.

 

  • 스택을 메모리로 가진다. (스택의 메모리는 무한하며 우리가 원하는 내용을 저장할 수 있다고 가정한다.)
  • Control unit은 input file에서 문자(알파벳)을 한번에 하나씩 읽어온다.
  • Control unit은 input file에서 읽은 알파벳과, 스택의 탑에 위치한 정보를 바탕으로, 다음에 어떤 행동을 할 지 결정한다.

 

우리는 이제 input file에 우리가 언어에 속하는지 아닌지 판단할 문자열(aaabbb)을 넣고,

문자열을 하나씩 읽으며 스택을 사용해 판단을 내리도록 Control unit을 설계하면 된다.

 

우선 input 파일에 우리가 판단할 문자열(aaabbb)를 넣고,

Stack의 맨 위에는 스택의 시작 상태를 의미하는 임의의 값(Z)를 넣는다.

 

Control Unit은 Input의 알파벳을 앞에서부터 하나씩 읽으며 다음과 같은 연산을 수행한다.

 

1. a를 읽는다면 스택에 1을 push한다.

2. b를 읽는다면 스택의 맨 위에 위치한 값을 pop한다.

 

3. input 파일에서 더 이상 읽을 게 없을때 스택의 탑에 시작상태(Z)가 남아있다면, input 문자열은 언어에 속한다!

 

우리는 위의 과정을 통해 앞서 정의한 프로그래밍 언어의 괄호 문법을 판단하는 추상화된 기계를 설계했다.

물론 지금까지의 설명은 오토마타를 듣지 않은 사람들의 쉬운 이해를 위해 여러 부분을 생략했다.

 

설계한 기계의 문제점

  • 닫힌 괄호가 열린 괄호보다 먼저 나와선 안된다.

이 기계는 위의 정의한 괄호 문법에 맞지 않아도,

열린괄호와 닫힌 괄호의 총 개수만 맞다면 문제가 없다고 판독한다.

 

예를 들면 (()()) 와 같은 경우 => aababb로 번역되고,

우리가 정의한 기계는 이 문자열을 언어에 속한다고 판독한다.

 

물론 우리가 아는 프로그래밍 언어의 괄호 문법은 오히려 저게 옳은 문법이라고 생각하지만,

문제는 우리가 만든 기계가 처음 의도한 것과 다른 결과를 만든다는 점에 있다.

 

사실 PDA에서 중요한 상태(State)를 빼고 설명하다보니, 조금 힘들어졌다.

 

실제로는 Control Unit이 매 순간 특정한 상태(State)를 가지고,

input을 하나씩 읽을때마다 상태를 변경하거나, 유지하며 Stack의 탑을 변화시킨다.

 

즉 Control Unit은 (input 알파벳, 현재 상태, 스택의 탑) 이라는 3가지 정보를 이용해,

(다음 상태, 스택의 탑) 이라는 결과를 결정하는 일을 한다.

 

 

이 과정을 함수나 그래프로도 표현할 수 있으며,

오토마타 과목에선 PDA를 포함한 여러 기계를 함수와 그래프로 설계하는 방법을 배운다.


마무리

이 글을 쓴 이유는 "PDA가 무엇인가"를 설명하기 위해서가 아닌 "오토마타가 어떤 과목인가"에 대한 대략적인 설명과,

다음 글에서 설명할 "튜링 머신"을 이해하기 위한 전반적인 배경지식을 설명하기 위해서이다.

 

그래서 기계와 언어에 대해 설명할때 상태(State)와 같은 내용들을 과감히 생략했다.

어차피 오토마타를 들은 분들은 무슨 소리하는지 다 알거고,

아직 안 들은 사람들은 대충 이런 과목이구나 정도만 알아도 도움이 될 거라고 생각했다.

 

다음 글에선 '난해한 프로그래밍 언어' 를 설계하기 위한 과정과 튜링 머신에 대해 알아보자.

 

 

 

 

+ Recent posts