packages feed

zot-0.0.2: src/LambdaToSki.hs

module LambdaToSki ( main ) where

import Parse ( Parse, spot, token, eof, ( >*> ), alt, build, list1, recL1 )
import Data.Char ( isLower, isAlphaNum )

main :: IO ()
main = interact $ show . lambdaToSki . readLambda

data Lambda = Var String | Apply Lambda Lambda | Fun String Lambda | S | K | I

instance Show Lambda where
	show S			= "s"
	show K			= "k"
	show I			= "i"
	show ( Apply f a )	= "`" ++ show f ++ show a
	show ( Fun p e )	= "(fun " ++ p ++ " " ++ show e ++ ")"
	show ( Var v )		= "(var " ++ v ++ ")"

onlySKI :: Lambda -> Bool
onlySKI S		= True
onlySKI K		= True
onlySKI I		= True
onlySKI ( Apply f a )	= onlySKI f && onlySKI a
onlySKI _		= False

makeFun :: [ String ] -> Lambda -> Lambda
makeFun [ p ] ex	= Fun p ex
makeFun ( p : ps ) ex	= Fun p $ makeFun ps ex
makeFun _ _		= error "makeFun error: need 1 parameter at least"

lambdaToSki :: Lambda -> Lambda
lambdaToSki ( Fun x e )		= out x e
lambdaToSki ( Apply f a )	= Apply ( lambdaToSki f ) ( lambdaToSki a )
lambdaToSki _			= error "lambdaToSki error"

out :: String -> Lambda -> Lambda
out _ e
	| onlySKI e	= Apply K e
out x0 v@( Var x1 )
	| x1 == x0	= I
	| otherwise	= Apply K v
out x0 ( Apply f a )	= Apply ( Apply S  $ out x0 f ) $ out x0 a
out x0 ( Fun x1 e )
	| x0 == x1	= Apply K $ out x0 e
	| otherwise	= out x0 $ out x1 e
out _ _			= error "never occur"

readLambda :: String -> Lambda
readLambda = fst . fst . head . ( parseLambda >*> eof ) . lexer

parseLambda :: Parse String Lambda
parseLambda = parseApply `alt` parseFun

parseApply :: Parse String Lambda
parseApply = recL1 Apply parseAtom

parseFun :: Parse String Lambda
parseFun = token "\\" >*> parseParams >*> token "->" >*> parseLambda
	`build` ( \( "\\", ( ps, ( "->", e ) ) ) -> makeFun ps e )

parseAtom :: Parse String Lambda
parseAtom = ( spot ( isLower . head ) `build` Var ) `alt` parseParens

parseParens :: Parse String Lambda
parseParens = token "(" >*> parseLambda >*> token ")" `build` fst . snd

parseParams :: Parse String [ String ]
parseParams = list1 $ spot $ isLower . head

lexer :: String -> [ String ]
lexer ""			= [ ]
lexer ( ' ' : rest )		= lexer rest
lexer ( '\n' : rest )		= lexer rest
lexer ( '\\' : rest )		= "\\" : lexer rest
lexer ( '-' : '>' : rest )	= "->" : lexer rest
lexer ( '(' : rest )		= "(" : lexer rest
lexer ( ')' : rest )		= ")" : lexer rest
lexer ca@( c : _ )
	| isLower c		= let ( ret, rest ) = span isAlphaNum ca in
					ret : lexer rest
lexer ca			= error $ "lexer error: " ++ ca