2012-07-13 2 views
2

나는 하스켈에서 brainfuck 인터프리터를 운동/재미 프로젝트로 쓰려고하는데, 나는 약간의 문제에 봉착했다.하스켈에서 데이터 유형을 확인하려면 어떻게해야합니까?

Brainfuck의 "while 루프"구조는 일련의 명령으로 대괄호 안에 들어 있습니다. [ 데이터 생성자 안에 루프 내부에 연산자를 저장하는 방식으로 구문 트리를 작성하려고합니다.

이 순간에 명령 및 구문 "나무"모양에 대한 데이터를 선언하는 방법입니다

data Operator = Plus 
       | Minus 
       | RShift 
       | LShift 
       | Dot 
       | Comma 
       | SBracket [Operator] 
       | EBracket 
       deriving (Show, Eq) 


type STree = [Operator] 

난 할 노력하고있어 것은이 "+><[.>]" 같은 명령 String을하고로 구문 분석 다음과 같습니다 STree : 나는이 목록의 머리가 있는지 확인하는 방법을 모르겠어요 때문에

[Plus, RShift, LShift, SBracket [Dot, RShift], EBracket] 

지금까지, 나는 String에서 한 차원 목록을 얻을에만 수 있어요 SBracket 새로운 목록을 운영자 목록에 추가하십시오. 내가 좋아하는 것 무엇

matchChar :: Char -> Maybe Operator 
matchChar c = case c of 
       '+' -> Just Plus 
       '-' -> Just Minus 
       '>' -> Just RShift 
       '<' -> Just LShift 
       '.' -> Just Dot 
       ',' -> Just Comma 
       '[' -> Just (SBracket []) 
       ']' -> Just EBracket 
       _ -> Nothing 

getChars :: [Char] -> STree 
getChars str = foldr toOp [] str 
    where 
      toOp x acc = case matchChar x of 
          Just a -> a:acc 
          Nothing -> acc 

head accSBracket 인스턴스가 있는지 확인한다 할 수있을하고 만약 그렇다면, 대신에 붙이는 : 여기

은 내가 구문 분석을 할 사용하고 함수의 새 Operator을 목록에 추가하려면 SBracketOperator 목록 앞에 추가하십시오.

나는 패턴 매칭 (toOp x ((SBracket list):xs) = ...)뿐만 아니라 명시 적으로 목록 (if head acc == SBracket ...)의 머리를 확인 하려고 시도했지만, 이런 것들 중 어느 것도 제대로 작동합니다.

도움이 될 것입니다.

답변

7

먼저 SBracket [Operator]Bracket STree으로 다시 정의하고 EBracket을 제거하십시오. 그런 다음 부모의 목록뿐만 아니라 "현재의 STree"를 모두 추적하도록 파서를 변경합니다. 괄호가 붙을 때마다 현재 트리를 상위 목록으로 밀어 넣고 새 트리를 만듭니다. 끝 괄호가 생기면 현재 트리를 가져 와서 Bracket 생성자로 감싸고 첫 번째 부모를 튕겨서 괄호를 추가하여 현재 트리로 만듭니다.


여기거나 작동하지 않을 수 있습니다 전액 검증되지 않은 (이 빌려 GHC이없는) 버전입니다 :

data Operator = Plus 
       | Minus 
       | RShift 
       | LShift 
       | Dot 
       | Comma 
       | Bracket [Operator] 
       deriving (Show, Eq) 

parse :: [Char] -> Either String [Operator] 
parse str = parse' str [] [] 
    where parse' :: [Char] -> [Operator] -> [[Operator]] -> Either String [Operator] 
     parse' [] context [] = Right (reverse context) 
     parse' [] context _ = Left "unclosed []" 
     parse' (']':cs) _ [] = Left "unexpected ]" 
     parse' (c:cs) ctx stack 
      | c == '+' = parse' cs (Plus:ctx) stack 
      | c == '-' = parse' cs (Minus:ctx) stack 
      | c == '>' = parse' cs (RShift:ctx) stack 
      | c == '<' = parse' cs (LShift:ctx) stack 
      | c == '.' = parse' cs (Dot:ctx) stack 
      | c == ',' = parse' cs (Comma:ctx) stack 
      | c == '[' = parse' cs [] (ctx:stack) 
      | c == ']' = parse' cs (Bracket (reverse ctx):s) tack 
      | otherwise = parse' cs ctx stack 
      where (s:tack) = stack 
+0

(A case 문 매핑 및 함수 맵핑'+ - <>. ,'Plus','Minus' 등은 가드 체인보다 좋을 수도 있습니다 :)) – huon

+0

@dbaupp : 이러한 함수는 특수하기 때문에 '['와 ']'를 처리 할 수 ​​없습니다. 그리고 그것이 오른쪽 가장자리에서 너무 멀리 떨어져서 가고있는 것처럼 느껴지는 경우를 사용했습니다. –

+0

약간의 수정을 거친 후 ('where'는 모든 가드 뒤에 있어야합니다.) 경비원은'| Bool -> (...)'대신에'| Bool = (...)'형식을 사용합니다. 두 번째에서 마지막 가드 명령문의 'context'는'ctx '여야합니다). 나는이 일을 깨기 위해 조금 더 나은 것을 이해할 수 있지만 도움을 주셔서 감사합니다! –

관련 문제