-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathloop.y
155 lines (136 loc) · 5.07 KB
/
loop.y
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
{
import Data.Char (isDigit)
import qualified Data.Map as Map
import Data.Maybe (fromMaybe)
import System.Environment
}
%name parse
%tokentype { Token }
%error { parseError }
%token
var { TokenVar $$ }
int { TokenConst $$ }
':=' { TokenAss }
'+' { TokenPlus }
'-' { TokenMinus }
';' { TokenSeq }
'LOOP' { TokenLoop }
'WHILE' { TokenWhile }
'DO' { TokenDo }
'END' { TokenEnd }
%%
Exp : var ':=' var '+' int { AssP $1 $3 $5 }
| var ':=' var '-' int { AssM $1 $3 $5 }
| Exp ';' Exp { Seq $1 $3 }
| 'LOOP' var 'DO' Exp 'END' { Loop $2 $4}
| 'WHILE' var 'DO' Exp 'END' { While $2 $4}
{
type Var = Int
type Const = Int
data Exp
= AssP Var Var Const
| AssM Var Var Const
| Seq Exp Exp
| Loop Var Exp
| While Var Exp
instance Show Exp where
show (AssP v1 v2 c) = "x" ++ (show v1) ++ " := x" ++ (show v2) ++ " + " ++ (show c)
show (AssM v1 v2 c) = "x" ++ (show v1) ++ " := x" ++ (show v2) ++ " - " ++ (show c)
show (Seq e1 e2 ) = (show e1) ++ ";\n" ++ (show e2)
show (Loop c b ) = "LOOP x" ++ (show c) ++ " DO\n" ++ (show b)
show (While c b ) = "WHILE x" ++ (show c) ++ " DO\n" ++ (show b)
data Token
= TokenVar Var
| TokenConst Const
| TokenAss
| TokenPlus
| TokenMinus
| TokenSeq
| TokenLoop
| TokenWhile
| TokenDo
| TokenEnd
deriving Show
lexer :: String -> [Token]
lexer [] = []
lexer ('#':cs) = skipLine cs
lexer (' ':cs) = lexer cs
lexer ('\n':cs) = lexer cs
lexer ('\r':cs) = lexer cs
lexer (':':'=':cs) = TokenAss : lexer cs
lexer ('+':cs) = TokenPlus : lexer cs
lexer ('-':cs) = TokenMinus : lexer cs
lexer (';':cs) = TokenSeq : lexer cs
lexer ('L':'O':'O':'P':cs) = TokenLoop : lexer cs
lexer ('l':'o':'o':'p':cs) = TokenLoop : lexer cs
lexer ('W':'H':'I':'L':'E':cs) = TokenWhile : lexer cs
lexer ('w':'h':'i':'l':'e':cs) = TokenWhile : lexer cs
lexer ('D':'O':cs) = TokenDo : lexer cs
lexer ('d':'o':cs) = TokenDo : lexer cs
lexer ('E':'N':'D':cs) = TokenEnd : lexer cs
lexer ('e':'n':'d':cs) = TokenEnd : lexer cs
lexer ('X':cs) = lexVar cs
lexer ('x':cs) = lexVar cs
lexer (c:cs)
| isDigit c = lexNum (c:cs)
where lexNum cs = let (num,rest) = span isDigit cs
in TokenConst (read num) : lexer rest
lexer cs = error $ "Invalid syntax near " ++ take 5 cs ++ "..."
lexVar cs = let (num, rest) = span isDigit cs
in TokenVar (read num) : lexer rest
skipLine ('\n':cs) = lexer cs
skipLine (_:cs) = skipLine cs
parseError :: [Token] -> a
parseError tokens = error $ "Error parsing" ++ (show tokens)
eval :: Exp -> Map.Map Var Const -> Const
eval exp initial = fromMaybe 0 $ Map.lookup 0 $ evalIntoDict exp initial
where
loop :: Int -> Exp -> Map.Map Var Const -> Map.Map Var Const
loop 0 exp dict = dict
loop n exp dict = loop (n - 1) exp $ evalIntoDict exp dict
while :: Var -> Exp -> Map.Map Var Const -> Map.Map (Map.Map Var Const) Bool -> Map.Map Var Const
while cond exp dict states
| Map.member dict states = error $ "Infinite loop detected. In \n" ++ (show $ While cond exp)
| (fromMaybe 0 $ Map.lookup cond dict) == 0 = dict
| otherwise = while cond exp (evalIntoDict exp dict) (Map.insert dict True states)
evalIntoDict :: Exp -> Map.Map Var Const -> Map.Map Var Const
evalIntoDict (AssP x y c) dict = Map.insert x (max 0 $ (fromMaybe 0 $ Map.lookup y dict) + c) dict
evalIntoDict (AssM x y c) dict = evalIntoDict (AssP x y (-1*c)) dict
evalIntoDict (Seq e1 e2) dict = evalIntoDict e2 $ evalIntoDict e1 dict
evalIntoDict l@(Loop cond exp) dict = loop (fromMaybe 0 $ Map.lookup cond dict) exp dict
evalIntoDict l@(While cond exp) dict = while cond exp dict Map.empty
evalString :: String -> Map.Map Var Const -> Const
evalString s dict = eval (parse $ lexer $ s) dict
evalStringEmpty :: String -> Const
evalStringEmpty s = evalString s Map.empty
evalFile :: FilePath -> Map.Map Var Const -> IO ()
evalFile file dict = do
s <- readFile file
print $ evalString s dict
evalFileEmpty :: FilePath -> IO ()
evalFileEmpty file = evalFile file Map.empty
isInteger :: String -> Bool
isInteger s = case reads s :: [(Integer, String)] of
[(_, "")] -> True
_ -> False
main :: IO ()
main = do
args <- getArgs
case args of
[] -> do
putStrLn "LOOP / WHILE interpreter"
putStrLn " Usage: "
putStrLn " loop filename [x0 x1 x2 ...]\n"
putStrLn " LOOP Syntax:"
putStrLn " L ::= xn := xm + c"
putStrLn " | xn := xm - c"
putStrLn " | L; L"
putStrLn " | LOOP xn DO L END\n"
putStrLn " WHILE Syntax:"
putStrLn " W ::= xn := xm + c"
putStrLn " | xn := xm - c"
putStrLn " | W; W"
putStrLn " | WHILE xn DO W END\n"
(f:t) -> do
evalFile f $ (Map.fromList . zip [0..] . map read . fst . span isInteger) t
}