aboutsummaryrefslogtreecommitdiff
path: root/pf.icl
blob: 342ee8205c3014453f378819ee2c0a92956f7e34 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
module pf

import StdArray
import StdBool
import StdFile
from StdFunc import flip, o
import StdMisc
import StdString
import StdTuple

import GenEq

import Control.Applicative
import Control.Monad
import Data.Either
import Data.Error
from Data.Func import $
import Data.Functor
import Data.List
import Data.Maybe
import System.CommandLine

import Yard

:: Expr
	= Lambda Ident Expr
	| Ident Ident
	| Literal Literal
	| (@) infixl Expr Expr

:: Literal
	= LitInt Int
	| LitBool Bool

:: Ident :== String

:: Token
	= TBackSlash
	| TParenOpen
	| TParenClose
	| TArrow
	| TBool Bool
	| TInt Int
	| TIdent Ident

derive gEq Token; instance == Token where == a b = a === b

tokenize :: ([Char] -> MaybeError String [Token])
tokenize = flip tok []
where
	tok :: [Char] [Token] -> MaybeError String [Token]
	tok []           tks = Ok $ reverse tks
	tok ['\\':cs]    tks = tok cs [TBackSlash:tks]
	tok ['(':cs]     tks = tok cs [TParenOpen:tks]
	tok [')':cs]     tks = tok cs [TParenClose:tks]
	tok ['-':'>':cs] tks = tok cs [TArrow:tks]
	tok ['T':'r':'u':'e':cs]     tks = tok cs [TBool True:tks]
	tok ['F':'a':'l':'s':'e':cs] tks = tok cs [TBool False:tks]
	tok [c:cs]       tks
	| isSpace c          = tok cs tks
	| isDigit c          = tok rest [TInt (toInt (toString digs)):tks]
		with (digs,rest) = span isDigit [c:cs]
	| isIdentChar c      = tok rest [TIdent (toString ids):tks]
		with (ids,rest)  = span isIdentChar [c:cs]
	| otherwise          = Error $ "Unexpected character '" +++ {c} +++ "' in input."

	isIdentChar :: Char -> Bool
	isIdentChar c = isAlpha c || c == '_'

parse :: String -> MaybeError String Expr
parse s = tokenize (fromString s) >>= cast o runParser expr
where
	cast :: (Either String a, [b]) -> MaybeError String a
	cast (Right e, []) = Ok e
	cast (Right _, _)  = Error "Not all input could be consumed."
	cast (Left _, _)   = Error "Parse error"

	simple :: Parser Token Expr
	simple = liftM2 lambda (item TBackSlash *> some ident <* item TArrow) expr
		<|> Literal <$> LitInt  <$> int
		<|> Literal <$> LitBool <$> bool
		<|> item TParenOpen *> expr <* item TParenClose
		<|> Ident <$> ident
	where
		lambda :: [Ident] Expr -> Expr
		lambda [x]    e = Lambda x e
		lambda [x:xs] e = Lambda x (lambda xs e)

	expr :: Parser Token Expr
	expr = liftM2 (\f xs -> app [Ident f:xs]) ident (some simple)
		<|> simple
	where
		app :: [Expr] -> Expr
		app [e] = e
		app es = app (init es) @ last es

	ident :: Parser Token Ident
	ident = (\(TIdent t) -> t) <$> satisfy (\t -> t=:(TIdent _))

	int :: Parser Token Int
	int = (\(TInt i) -> i) <$> satisfy (\t -> t=:(TInt _))

	bool :: Parser Token Bool
	bool = (\(TBool b) -> b) <$> satisfy (\t -> t=:(TBool _))

hasIdent :: Ident Expr -> Bool
hasIdent id (Ident n) = id == n
hasIdent id (Literal _) = False
hasIdent id (Lambda n e) = id <> n && hasIdent id e
hasIdent id (f @ x) = hasIdent id f || hasIdent id x

print :: Expr -> String
print e = foldl (+++) "" (pr False e [])
where
	pr :: Bool Expr [String] -> [String]
	pr p l=:(Lambda _ _) st =
		[if p "(\\" "\\":intersperse " " vars] ++
		[" -> ":pr False rhs [if p ")" "":st]]
	where
		(vars,rhs) = getLambdas l

		getLambdas :: Expr -> ([Ident], Expr)
		getLambdas (Lambda x rhs) = let (is,e) = getLambdas rhs in ([x:is],e)
		getLambdas e = ([], e)

	pr _ (Ident id)  st = [id:st]
	pr _ (Literal l) st = pLit l st
	pr p (f @ x)     st = [if p "(" "":pr False f [" ":pr True x [if p ")" "":st]]]

	pLit :: Literal [String] -> [String]
	pLit (LitInt i)  st = [toString i:st]
	pLit (LitBool b) st = [toString b:st]

optim :: Expr -> Expr
optim (Lambda id e)
| hasIdent id e = case optim e of
	Ident id` -> if (id == id`) (Ident "id") (Ident "const" @ e)
	e=:(f @ x) -> optim (optApp e)
	e -> Lambda id e
| otherwise = Ident "const" @ optim e
where
	optApp :: Expr -> Expr
	optApp (f @ x) = case moveOutside id (f @ x) of
		a=:(f @ Ident id`) -> if (id == id`) f (Ident "const" @ a)
		a                  -> Ident "const" @ a
optim (f @ x) = case optim f @ optim x of
	Ident "flip" @ Ident "ap" @ Ident "id" -> Ident "join"
	Ident "ap" @ f @ Ident "id" -> optim (Ident "join" @ f)
	Ident "join" @ (Ident "(o)" @ f @ g) -> optim (Ident "(>>=)" @ f @ g)
	e -> e
optim e = e

moveOutside :: Ident Expr -> Expr
moveOutside _ (Ident id) = Ident id
moveOutside _ (Literal l) = Literal l
moveOutside id (f @ x)
| hasIdent id f && hasIdent id x = Ident "ap" @ Lambda id f @ Lambda id x @ Ident id
| hasIdent id f = case moveOutside id f of
	Ident id -> Ident "flip" @ Ident "id" @ x @ Ident id
	g @ Ident id -> Ident "flip" @ g @ x @ Ident id
| hasIdent id x = case moveOutside id x of
	e=:(Ident id) -> f @ e
	g @ e=:(Ident id) -> Ident "(o)" @ f @ g @ e
moveOutside _ (Lambda x e) = Lambda x e // TODO

Start w
# ([prg:cmd],w) = getCommandLine w
| length cmd <> 1 = err ("Usage: " +++ prg +++ " EXPRESSION") w
# e = parse (hd cmd)
| isError e = err (fromError e) w
# e = fromOk e
# (io,w) = stdio w
# io = io <<< "Request: " <<< print e <<< "\n"
# io = io <<< "Result:  " <<< print (optim e) <<< "\n"
# (_,w) = fclose io w
= w
where
	err :: a *World -> *World | <<< a
	err e w = snd (fclose (stderr <<< e <<< "\n") w)