@@ -161,18 +161,21 @@ data Input
161161
162162read :: Repl.InputT M Input
163163read =
164− do line <- Repl.getInputLine "> "
165− case line of
164+ do maybeLine <- Repl.getInputLine "> "
165+ case maybeLine of
166166 Nothing ->
167167 return Exit
168168
169− Just chars ->
170− case categorize chars of
169+ Just line ->
170+ let
171+ lines = Lines line []
172+ in
173+ case categorize lines of
171174 Done input -> return input
172− Continue -> readMore chars
175+ Continue -> readMore lines
173176
174177
175−readMore :: [Char] -> Repl.InputT M Input
178+readMore :: Lines -> Repl.InputT M Input
176179readMore previousLines =
177180 do input <- Repl.getInputLineWithInitial "| " (" ","")
178181 case input of
@@ -181,11 +184,55 @@ readMore previousLines =
181184
182185 Just chars ->
183186 let
184− allLines = previousLines ++ '\n' : chars
187+ lines = addLine chars previousLines
185188 in
186− case categorize allLines of
189+ case categorize lines of
187190 Done input -> return input
188− Continue -> readMore allLines
191+ Continue -> readMore lines
192+
193+
194+
195+-- LINES
196+
197+
198+data Lines =
199+ Lines
200+ { _prevLine :: String
201+ , _revLines :: [String]
202+ }
203+
204+
205+addLine :: [Char] -> Lines -> Lines
206+addLine line (Lines x xs) =
207+ Lines line (x:xs)
208+
209+
210+isBlank :: Lines -> Bool
211+isBlank (Lines prev rev) =
212+ null rev && all (==' ') prev
213+
214+
215+isSingleLine :: Lines -> Bool
216+isSingleLine (Lines _ rev) =
217+ null rev
218+
219+
220+endsWithBlankLine :: Lines -> Bool
221+endsWithBlankLine (Lines prev _) =
222+ all (==' ') prev
223+
224+
225+linesToByteString :: Lines -> BS_UTF8.ByteString
226+linesToByteString (Lines prev rev) =
227+ BS_UTF8.fromString (unlines (reverse (prev:rev)))
228+
229+
230+getFirstLine :: Lines -> String
231+getFirstLine (Lines x xs) =
232+ case xs of
233+ [] -> x
234+ y:ys -> getFirstLine (Lines y ys)
235+
189236
190237
191238
@@ -197,18 +244,18 @@ data CategorizedInput
197244 | Continue
198245
199246
200−categorize :: [Char] -> CategorizedInput
201−categorize chars
202− | all (==' ') chars = Done Skip
203− | startsWithColon chars = Done (toCommand chars)
204− | startsWithKeyword "import" chars = attemptImport chars
205− | otherwise = attemptDeclOrExpr chars
247+categorize :: Lines -> CategorizedInput
248+categorize lines
249+ | isBlank lines = Done Skip
250+ | startsWithColon lines = Done (toCommand lines)
251+ | startsWithKeyword "import" lines = attemptImport lines
252+ | otherwise = attemptDeclOrExpr lines
206253
207254
208−attemptImport :: [Char] -> CategorizedInput
209−attemptImport chars =
255+attemptImport :: Lines -> CategorizedInput
256+attemptImport lines =
210257 let
211− src = BS_UTF8.fromString (chars ++ "\n")
258+ src = linesToByteString lines
212259 parser = P.specialize (\_ _ _ -> ()) PM.chompImport
213260 in
214261 case P.fromByteString parser (\_ _ -> ()) src of
@@ -216,63 +263,67 @@ attemptImport chars =
216263 Done (Import name src)
217264
218265 Left () ->
219− if endsWithBlankLine chars
220− then Done (Import "ERR" src)
221− else Continue
266+ ifFail lines (Import "ERR" src)
267+
222268
269+ifFail :: Lines -> Input -> CategorizedInput
270+ifFail lines input =
271+ if endsWithBlankLine lines
272+ then Done input
273+ else Continue
223274
224−attemptDeclOrExpr :: [Char] -> CategorizedInput
225−attemptDeclOrExpr chars =
275+
276+ifDone :: Lines -> Input -> CategorizedInput
277+ifDone lines input =
278+ if isSingleLine lines || endsWithBlankLine lines
279+ then Done input
280+ else Continue
281+
282+
283+attemptDeclOrExpr :: Lines -> CategorizedInput
284+attemptDeclOrExpr lines =
226285 let
227− src = BS_UTF8.fromString (chars ++ "\n")
286+ src = linesToByteString lines
228287 exprParser = P.specialize (toExprPosition src) PE.expression
229288 declParser = P.specialize (toDeclPosition src) PD.declaration
230289 in
231290 case P.fromByteString declParser (,) src of
232291 Right (decl, _) ->
233292 case decl of
234− PD.Value _ (A.At _ (Src.Value (A.At _ name) _ _ _)) -> Done (Decl name src)
235− PD.Union _ (A.At _ (Src.Union (A.At _ name) _ _ )) -> Done (Type name src)
236− PD.Alias _ (A.At _ (Src.Alias (A.At _ name) _ _ )) -> Done (Type name src)
293+ PD.Value _ (A.At _ (Src.Value (A.At _ name) _ _ _)) -> ifDone lines (Decl name src)
294+ PD.Union _ (A.At _ (Src.Union (A.At _ name) _ _ )) -> ifDone lines (Type name src)
295+ PD.Alias _ (A.At _ (Src.Alias (A.At _ name) _ _ )) -> ifDone lines (Type name src)
237296 PD.Port _ _ -> Done Port
238297
239298 Left declPosition
240− | startsWithKeyword "type" chars ->
241− if endsWithBlankLine chars
242− then Done (Type "ERR" src)
243− else Continue
299+ | startsWithKeyword "type" lines ->
300+ ifFail lines (Type "ERR" src)
244301
245− | startsWithKeyword "port" chars ->
302+ | startsWithKeyword "port" lines ->
246303 Done Port
247304
248305 | otherwise ->
249306 case P.fromByteString exprParser (,) src of
250307 Right _ ->
251− Done (Expr src)
308+ ifDone lines (Expr src)
252309
253310 Left exprPosition ->
254− if endsWithBlankLine chars
255− then Done (if declPosition <= exprPosition then Expr src else Decl "ERR" src)
256− else Continue
257−
311+ if exprPosition >= declPosition then
312+ ifFail lines (Expr src)
313+ else
314+ ifFail lines (Decl "ERR" src)
258315
259−endsWithBlankLine :: [Char] -> Bool
260−endsWithBlankLine chars =
261− case reverse (List.lines chars) of
262− [] -> False
263− cs:_ -> all (==' ') cs
264316
265−
266−startsWithColon :: [Char] -> Bool
267−startsWithColon chars =
268− case dropWhile (==' ') chars of
317+startsWithColon :: Lines -> Bool
318+startsWithColon lines =
319+ case dropWhile (==' ') (getFirstLine lines) of
269320 [] -> False
270321 c:_ -> c == ':'
271322
272323
273−toCommand :: [Char] -> Input
274−toCommand chars =
275− case drop 1 $ dropWhile (==' ') chars of
324+toCommand :: Lines -> Input
325+toCommand lines =
326+ case drop 1 $ dropWhile (==' ') (getFirstLine lines) of
276327 "reset" -> Reset
277328 "exit" -> Exit
278329 "quit" -> Exit
@@ -280,8 +331,11 @@ toCommand chars =
280331 rest -> Help (Just (takeWhile (/=' ') rest))
281332
282333
283−startsWithKeyword :: [Char] -> [Char] -> Bool
284−startsWithKeyword keyword line =
334+startsWithKeyword :: [Char] -> Lines -> Bool
335+startsWithKeyword keyword lines =
336+ let
337+ line = getFirstLine lines
338+ in
285339 List.isPrefixOf keyword line &&
286340 case drop (length keyword) line of
287341 [] -> True