Parsecの使い方が分かったよ!超簡単じゃん!(今更)前はHaskellの基本もよく理解できてなかったから、その詳細に呑まれてたんだなぁ。Haskell使って2日目とか3日目だったし。てことで、S式っぽいもののパーサ。(2 . 3)みたいなのはパースできないけど、今回の目的には必要ないので、わざと。

import Text.ParserCombinators.Parsec

data SExpr = 
   SymS String -- symbol
 | IntS Integer -- integer
 | BoolS Bool -- #t,#f(boolean)
 | StringS String --string
 | ListS [SExpr] deriving (Eq,Show)

parseS :: String -> SExpr
parseS input = case (parse sexprParser "what's this?" input) of
 Left err -> undefined
 Right x -> x

symParser :: Parser SExpr --parse symbol,and boolean
symParser =
 do {s1 <- letter <|> (oneOf "%_!$&+-*/~") ;
     s2 <- many (letter <|> digit <|> (oneOf "%_!$+-*/&~")) ;
     return (SymS ([s1]++s2))}

boolParser :: Parser SExpr
boolParser =
 do {c1 <- char '#' ;
     c2 <- (char 't' <|> char 'f') ;
     return (BoolS (case c2 of {'t'->True;'f'->False}))}

strParser :: Parser SExpr
strParser =
  do {char '"' ;
      s <- many (noneOf "\"") ;
      char '"' ;
      return (StringS s)}
 
numParser :: Parser SExpr
numParser =
 do {n <- many1 digit ;
     return (IntS (read n))}

listParser :: Parser SExpr
listParser =
 do {char '(' ;
     ls <- sepBy sexprParser spaces ;
     char ')' ;
     return (ListS ls)}

sexprParser :: Parser SExpr
sexprParser = 
     symParser 
 <|> boolParser
 <|> strParser 
 <|> numParser 
 <|> listParser

仕組みはよく分からんけど。これって効率とかどうなんだ?あと、parse関数の第二引数が何のために存在するのか謎。いやー、けどこんな簡単なんだったら、GHC External Coreのパーサを直接書いてもいいかな〜とか思い始めた。ていうか、自分で書かなくても、GHCのどっかにあるはずなんだけど、GHCのソースは入り組んでて、ちょろっとコピペしてくるってわけにもいかんだろうなーとか、そういうのが。まあ、知らんけど。そのへんは冬休みの宿題ってことで。

あとは機械的

data Term = 
   Var String  -- variable
 | Lit Literal -- literal
 | Tuples [Term] 
 | Lam Args Term
 | Let String Term Term 
 | Case Term [(Pat,Term)] 
 | FApp String [Term]
 | CApp String [Term] -- constructor application
 | App Term Term -- application(general) 
 | Juncs [Term]  -- junc
 | Plus [Term]
 | Hylo Term Term Term
 | Tags (Int , Term) 
    deriving (Eq,Show)

data Literal =    --適宜修正
    HfUnit
  | HfBool Bool 
  | HfString String
  | HfInteger Integer 
  | HfChar Char
  | HfDouble Double 
     deriving (Eq,Show)

data Args = Argv String | Vars [String] deriving (Eq,Show)
data Pat = PVar String | PTuples [Pat] | CPat String [Pat] deriving (Eq,Show)

--S式をpattern式に
s2pat :: SExpr -> Pat
s2pat (SymS s) 
 = case (isUpper (head s)) of
    True -> CPat s []
    False -> PVar s
s2pat (ListS ((SymS a):cdr))
 = case (isUpper (head a)) of
    True -> CPat a (map s2pat cdr)
    False -> PTuples ((PVar a):(map s2pat cdr))

--S式をTermに
s2term :: SExpr -> Term
s2term (SymS v) = Var v
s2term (BoolS v) = Lit (HfBool v)
s2term (StringS v) = Lit (HfString v)
s2term (IntS v) = Lit (HfInteger v)
s2term (ListS [SymS "lambda" , ListS args , body]) -- lambda式
 = Lam (Vars (map (\x -> case x of (SymS s)->s) args)) (s2term body)
s2term (ListS [SymS "let" , ListS [SymS a,v] , body]) -- let式
 = Let a (s2term v) (s2term body)
s2term (ListS [SymS "case" , t0 , ListS body])
 = Case (s2term t0) (map (\x->case x of (ListS [p,t])-> (s2pat p,s2term t)) body)
s2term (ListS ((SymS f):args)) -- function application
 = case (isUpper (head f)) of
    True -> (CApp f (map s2term args))
    False -> (FApp f (map s2term args))

これでもう(Lam (Vars ["x" ,"y"]) ...)とか書かなくていい。

>s2term $ parseS "(lambda (n) (case n ((Nil 0) ((Cons a as) (+ a (sum as))))))"
Lam (Vars ["n"]) (Case (Var "n") [(CPat "Nil" [],Lit (HfInteger 0)),(CPat "Cons" [PVar "a",PVar "as"],FApp "+" [Var "a",FApp "sum" [Var "as"]])])

ルール
・大文字から始まるシンボルは、型構成子とみなす。小文字から始まるシンボルは変数
・let式の構文は、(let (var val) body)。(let ( (x 3) (y 4) ) body)みたいなことはできない

あー、あとはFusion書くだけだなぁ。ってか、そこがメインだけど


気になる点。
その1。「Case式の平坦化」の意味を私が誤解してるような気がしてきた。具体的に、どういう処理を指してるんだろうか。ていうか、どういう処理にしろ、あまり真面目に悩む必要もないかもしれない。多分、関数の微妙な表現形式の違い(let式がネストしてるかどうかとか)によって、得られるhylo-formが変わるというようなことはあるんだろうけど、んでもって、そういうのもあるから、HyloFusionは、最適化プロセスの最後の方に持ってくるほうがいいとか、そういう話なのかもしれないけど、でも、そういうのやりだすと、私はHaskell(のサブセットの)コンパイラ書くのかい?とか、そういう話になっちゃうしなぁ。適当にお茶濁しとけばいいかぁ的な。

その2。いまんとこ、(私が定義している)hylo-formは関手に関する情報([|phi,tau,psi|]F,GのF,Gの部分)を持ってないけど、これは問題ないんだろうか。いい大人なんだから、それくらい推測してよね、とか。まあ、ダメだったらそのうち考える。

その3。hylo-formに変換するには、(\x1 ... xn -> case t0 of {p1->t1;...;pm->tm})という形の式を変換するわけだけど、変換後の式の形は(\x1 ... x(n-1) -> hylo ....)みたいなのになってる。てことは、仮引数の順番を考えてやらんといかんのではないかとか。Warm Fusionのときは、t0式に現れる仮引数とそうでない仮引数をまず区別してた記憶が。あとでよく考える