1
1
module frege.repl.FregeRepl where
2
2
3
- import frege.compiler .Data
4
- import frege.compiler .Import
3
+ import Compiler .Data
4
+ import Compiler .Import
5
5
import frege.Version
6
- import frege.java. Net
6
+ import Java. Net
7
7
import Data.List
8
- import frege.interpreter.FregeScriptCompiler hiding (compile )
9
- import frege.interpreter.FregeInterpreter
10
- import frege.compiler.EclipseUtil as EU ()
8
+ import Interpreter.FregeScriptCompiler hiding (compile )
9
+ import Interpreter.FregeInterpreter
10
+ import Compiler.Utilities as U ()
11
+ import Compiler.EclipseUtil as EU ()
12
+ import Compiler.DocUtils as DU (docit , docSym , DL , Doc , emitHtml )
13
+ import Java.Swing (JFrame , invokeLater )
14
+ import Java.Awt (Component , ActionListener )
15
+ import Repl.Gui
11
16
12
17
data ReplState = ReplState {
13
18
lastExternalScript :: Maybe String ,
@@ -19,6 +24,7 @@ data Repl a = Repl {un :: StateT ReplState Interpreter a} where
19
24
get = Repl $ StateT (\ s -> return (s , s ))
20
25
put s = Repl $ StateT (\ _ -> return (() , s ))
21
26
modify f = Repl $ StateT (\ s -> return (() , f s ))
27
+ run repl state = evalStateT (Repl.un repl ) state
22
28
23
29
instance Monad Repl where
24
30
return = Repl . return
@@ -59,7 +65,7 @@ evalScript (Eval line) = do
59
65
Repl . lift . Interpreter. put $ s. {currentScript <- (++ newLine ++ line)}
60
66
return NoOutput
61
67
Expression {variableName= var} -> do
62
- res <- Repl . lift $ evalShow line
68
+ res <- Repl . lift $ showVariable line var g
63
69
Repl . lift $ Interpreter. put oldInterpState
64
70
case res of
65
71
Left err -> return $ ReplError err
@@ -121,8 +127,9 @@ evalScript (Load filePath) = do
121
127
122
128
evalScript Java = do
123
129
state <- Repl. get
124
- return $ ReplSuccess (maybe " " id state. lastJavaSource)
125
-
130
+ showgui s = liftIO $ Runnable. new (javaSourceGUI s) >>= invokeLater >> return NoOutput
131
+ maybe (return NoOutput ) showgui state. lastJavaSource
132
+
126
133
evalScript Reload = do
127
134
state <- Repl. get
128
135
case state. lastExternalScript of
@@ -141,10 +148,87 @@ evalScript Reset = do
141
148
142
149
evalScript Skip = return NoOutput
143
150
evalScript Help = return $ ReplSuccess help
151
+
152
+ evalScript (HelpDoc source) = do
153
+ fregecRes <- Repl . lift $ compile " \"\" " -- Import current definitions
154
+ let response = maybe (return errmsg) (\ m -> liftIO (gui m) >> return NoOutput )
155
+ errmsg = ReplError [source ++ " cannot be resolved!" ]
156
+ gui content = invokeLater =<< Runnable. new (helpGUI source content)
157
+ case fregecRes of
158
+ CompilationSuccess (c@ CompilationInfo {state= global}) -> oneof [helpdoc, packDoc] where
159
+ helpdoc = fst $ StG. run (helpDoc source) global
160
+ packDoc = fst $ StG. run (packDocumentation source) global
161
+ oneof = response . listToMaybe . catMaybes
162
+ _ -> response $ Just " "
163
+
144
164
evalScript Version = Repl. get >>= (\ s -> return $ ReplSuccess version)
145
165
evalScript DefMulti = return NoOutput
146
166
evalScript Quit = return NoOutput
147
167
168
+ sNameToQName sName = do
169
+ g <- getST
170
+ qNames <- U. resolve (VName g. thisPack) Position. null sName
171
+ return (listToMaybe qNames) -- Just taking first resolved
172
+
173
+ helpDoc :: String -> StG (Maybe String )
174
+ helpDoc source = do
175
+ global <- getST
176
+ qNameMay <- sNameToQName (createSName source)
177
+ let symMay = qNameMay >>= (\ qname -> qname. findit global)
178
+ maybe (return Nothing ) (\ sym -> symbolDocumentation sym >>= return . Just ) symMay
179
+
180
+ createSName s
181
+ | Just (Just ns : Just ty : Just id : _) <- s `match` #^ (.*) \. (.*) \. (\ p{Lu }.* )$# = with2con ns ty id
182
+ | Just (Just ns : Just ty : Just id : _) <- s `match` #^ (.*) \. (.*) \. (.*) $# = with2var ns ty id
183
+ | Just (Just ty : Just id : _) <- s `match` #^ (.*) \. (\ p{Lu }.* )$# = with1con ty id
184
+ | Just (Just ty : Just id : _) <- s `match` #^ (.*) \. (.*) $# = with1var ty id
185
+ | s ~ #^\ p{Lu }.*$# = Simple (Token CONID s 1 0 0 [] )
186
+ | otherwise = Simple (Token VARID s 1 0 0 [] )
187
+ where with2con ns ty id = With2 (qual ns) (qual ty) (con id )
188
+ with2var ns ty id = With2 (qual ns) (qual ty) (var id )
189
+ with1con ty id = With1 (qual ty) (con id )
190
+ with1var ty id = With1 (qual ty) (var id )
191
+ qual name = Token QUALIFIER name 1 0 0 []
192
+ con name = Token CONID name 1 0 0 []
193
+ var name = Token VARID name 1 0 0 []
194
+
195
+ symbolDocumentation :: Symbol -> StG String
196
+ symbolDocumentation sym = do
197
+ sw <- doio $ StringWriter. new ()
198
+ p <- doio $ StringWriter. printer sw
199
+ changeST Global . {gen <- GenSt . {printer= p}}
200
+ g <- getST
201
+ let ds = docSym g sym
202
+ dl = DL (Just " func" ) [ds]
203
+ doc = Doc [dl]
204
+ emitHtml false doc -- html without CSS, swing does not understand
205
+ doio $ g. printer. close
206
+ result <- doio $ sw. toString
207
+ return result
208
+
209
+ packDocumentation :: String -> StG (Maybe String )
210
+ packDocumentation pack = do
211
+ g <- getST
212
+ r <- getFP pack
213
+ case r of
214
+ Right (Just fp) -> case fp. doc of
215
+ " " -> return . Just $ (" Undocumented package " ++ pack)
216
+ text -> do
217
+ let doc = Doc $ docit g (Just text)
218
+ sw <- doio $ StringWriter. new ()
219
+ p <- doio $ StringWriter. printer sw
220
+ changeST Global . {gen <- GenSt . {printer= p}}
221
+ emitHtml false doc -- html without CSS, swing does not understand
222
+ doio $ g. printer. close
223
+ result <- doio $ sw. toString
224
+ return . Just $ result
225
+ Left ex -> return Nothing
226
+ sonst -> return . Just $ (" (java class?) " ++ pack)
227
+
228
+ match s regex = groups <$> s =~ regex where groups m = [m. group i | i <- [1 .. groupCount m]]
229
+
230
+ pure native groupCount :: Matcher -> Int
231
+
148
232
ioException :: IOException -> Repl ReplResult
149
233
ioException e = return $ ReplError [Throwable. getMessage e]
150
234
@@ -176,7 +260,7 @@ showVariable source var g = do
176
260
case symbolMay of
177
261
Nothing -> return . Left $ [" Not found: " ++ var]
178
262
Just symbol -> showSymbol source var symbol g
179
-
263
+
180
264
showSymbol source var symbol g | isVariable g symbol = do
181
265
state <- Interpreter. get
182
266
if isString g symbol then do
@@ -185,7 +269,7 @@ showSymbol source var symbol g | isVariable g symbol = do
185
269
Left err -> return $ Left [err. getMessage]
186
270
Right value -> return . Right $ toString value
187
271
else do
188
- Interpreter. put $ state. {currentScript <- (++ newLine ++ var ++ " = " ++ source)}
272
+ Interpreter. put $ state. {currentScript <- (++ newLine ++ (variableDeclScript var source) )}
189
273
let showScript = buildShowScript var g symbol
190
274
showResult <- evalShow showScript
191
275
return showResult
@@ -223,18 +307,20 @@ positionAndName a b = case Symbol.pos a <=> Symbol.pos b of
223
307
Eq -> comparing (QName. base • Symbol. name) a b
224
308
ne -> ne
225
309
226
- cmdHelp = [(" :type <expression>" , " Display the type of an expression" ),
227
- (" :{" , " Start multiline definitions" ),
228
- (" :}" , " End multiline defintions" ),
310
+ cmdHelp = [(" :type <expression>" , " Display the type of an expression." ),
229
311
(" :browse <module name>" , " Display the names in a module if " ++
230
- " a module name is provided otherwise display the names in the default REPL module" ),
231
- (" :load <url or file>" , " Load Frege code snippets from an URL or file" ),
232
- (" :r" , " Reload the last script file" ),
233
- (" :history" , " Display the source history for definitions in the default REPL module" ),
234
- (" :reset" , " Reset the session discarding all evaluated scripts" ),
235
- (" :version" , " Display Frege version" ),
236
- (" :q or :quit" , " Quit REPL" ),
237
- (" :help" , " Display this help message" )]
312
+ " a module name is provided otherwise display the names in the default REPL module." ),
313
+ (" :load <url or file>" , " Load Frege code snippets from an URL or file." ),
314
+ (" :java" , " View Java translation of last compiled Frege source." ),
315
+ (" :r" , " Reload the last script file." ),
316
+ (" :history" , " Display the source history for definitions in the default REPL module." ),
317
+ (" :reset" , " Reset the session discarding all evaluated scripts." ),
318
+ (" :version" , " Display Frege version." ),
319
+ (" :{" , " Start multiline definitions." ),
320
+ (" :}" , " End multiline defintions." ),
321
+ (" :help <name>" , " Display the documentation for the given name." ++
322
+ " If the name is not provided, display this help message." ),
323
+ (" :q or :quit" , " Quit REPL" )]
238
324
239
325
f `on` g = \ x \ y -> f (g x) (g y)
240
326
@@ -247,7 +333,7 @@ renderCmdHelp cmdHelp = map render cmdHelp where
247
333
help = intercalate newLine $ header ++ body where
248
334
header = [" At the prompt, you can enter Frege code snippets to get them evaluated." ,
249
335
" The output or compilation errors will be printed below the prompt." ,
250
- " In addition to Frege code, the following commands are supported:" ]
336
+ " In addition to Frege code, the following commands are supported:\n " ]
251
337
body = renderCmdHelp cmdHelp
252
338
253
339
runFile :: String -> Repl ()
@@ -289,25 +375,87 @@ urlContents url = do
289
375
inStream <- URL. openStream url
290
376
scanner <- Scanner. new inStream
291
377
scanner. useDelimiter " \\ Z"
292
- scanner. next
378
+ scanner. next `finally` scanner . close
293
379
294
380
fileContents filePath = do
295
381
file <- File. new filePath
296
382
scanner <- Scanner. fromFile file " utf-8"
297
383
scanner. useDelimiter " \\ Z"
298
- scanner. next
384
+ scanner. next `finally` scanner . close
299
385
300
386
data Scanner = mutable native java.util.Scanner where
301
387
native new :: InputStream -> IO Scanner
302
388
native fromFile new :: MutableIO File -> String -> IO Scanner throws FileNotFoundException
303
389
native useDelimiter :: Scanner -> String -> IO ()
304
390
native next :: Scanner -> IO String
305
-
306
- runRepl repl state = evalStateT (Repl. un repl) state
391
+
392
+ javaSourceGUI :: String -> IO ()
393
+ javaSourceGUI javaSource = do
394
+ frame:: JFrame <- JFrame. new " Java Source"
395
+ newContentPane:: JPanel <- BorderLayout. new () >>= JPanel. new
396
+ frame. setDefaultCloseOperation JFrame. dispose_on_close
397
+ editor:: JEditorPane <- JEditorPane. new " text/plain" javaSource
398
+ editor. setEditable false
399
+ scrollPane <- JScrollPane. new editor
400
+ dimension <- Dimension. new 600 600
401
+ JComponent. setPreferredSize scrollPane dimension
402
+ Container. add newContentPane scrollPane (asObject BorderLayout. center)
403
+ frame. setContentPane newContentPane
404
+ frame. pack
405
+ frame. setVisible true
406
+
407
+
408
+ helpGUI :: String -> String -> IO ()
409
+ helpGUI title content = do
410
+ frame <- JFrame. new (title ++ " - Documentation" )
411
+ frame. setDefaultCloseOperation JFrame. dispose_on_close
412
+ newContentPane:: JPanel <- BorderLayout. new () >>= JPanel. new
413
+ let html :: String
414
+ html = substring content $ indexOf content " <HTML>" -- Skip DOCTYPE
415
+ editor:: JEditorPane <- JEditorPane. new " text/html" html
416
+ editor. setEditable false
417
+ helpLinksListener <- FregeJavaProxy. with showDesktop HyperlinkListener. clazz
418
+ editor. addHyperlinkListener helpLinksListener
419
+ scrollPane <- JScrollPane. new editor
420
+ dimension <- Dimension. new 600 600
421
+ JComponent. setPreferredSize scrollPane dimension
422
+ Container. add newContentPane scrollPane (asObject BorderLayout. center)
423
+ newContentPane. setOpaque true
424
+ frame. setContentPane newContentPane
425
+ frame. pack
426
+ frame. setVisible true
427
+
428
+ data FregeJavaProxy = pure native frege.memoryjavac.FregeJavaProxy where
429
+ native with frege . memoryjavac . FregeJavaProxy.with :: (Object -> Method -> ObjectArr -> ST s a ) -> Class c -> IO c
430
+
431
+ showDesktop :: Object -> Method -> ObjectArr -> IO ()
432
+ showDesktop _ _ args = do
433
+ event <- return $ asHyperlinkEvent $ args. elemAt 0
434
+ d <- Desktop. getDesktop ()
435
+ desktopSupported <- d. isDesktopSupported
436
+ let url = HyperlinkEvent. getURL event
437
+ navigateRelative = either throw d. browse . URI. new $ fixHelpLink event. getDescription
438
+ navigate = maybe navigateRelative (\ u -> toURI u >>= d. browse) url
439
+ when (event. getEventType == HyperlinkEvent_EventType. activated) navigate
440
+
441
+
442
+ fixHelpLink (m~#^\.\. (.*) $# ) = maybe helpRoot (\ s -> helpRoot ++ s) $ m. group 1
443
+ fixHelpLink s = s
444
+
445
+ helpRoot = " http://try.frege-lang.org/doc"
446
+
447
+ native toURI :: URL -> ST s URI throws URISyntaxException
448
+ pure native asObject " (java.lang.Object)" :: a -> Object
449
+
307
450
308
451
pure native isEmpty :: String -> Bool
452
+ pure native replaceAll :: String -> String -> String -> String
453
+ pure native indexOf :: String -> String -> Int
454
+ pure native substring :: String -> Int -> String
455
+ | String -> Int -> Int -> String
309
456
310
- data Command = Version | Help | Browse | BrowseModule String | Type String | Load String | Eval String |
457
+ data Command = Version | Help | HelpDoc String | Browse |
458
+ BrowseModule String | Type String | Load String | Eval String |
311
459
Java | DefMulti | History | Reload | Reset | Skip | Quit where
312
460
313
461
parse :: String -> Either String Command
@@ -321,8 +469,9 @@ data Command = Version | Help | Browse | BrowseModule String | Type String | Loa
321
469
parseCommand (m ~#^: l .*?\ s + (.* )# ) =
322
470
maybe (Left " Missing external script path!" ) (Right . Load ) $ m . group 1
323
471
parseCommand " :version" = Right Version
472
+ parseCommand (m ~#: help \ s + (.* )# ) = maybe (Right Help ) (Right . HelpDoc ) $ m . group 1
324
473
parseCommand " :help" = Right Help
325
- parseCommand (m ~#^: t .*?\ s + (.* )# ) =
474
+ parseCommand (m ~#^: t .*?\ s + (.* )# ) =
326
475
maybe (Left " Missing expression!" ) (Right . Type ) $ m . group 1
327
476
parseCommand " :{" = Right DefMulti
328
477
parseCommand " :history" = Right History
@@ -348,13 +497,13 @@ initInterpreterState = do
348
497
urlarr <- URLArray. new 0
349
498
loader <- ClassLoader. current >>= URLClassLoader. new urlarr
350
499
classes <- HashMap. new ()
351
- time <- currentTimeMillis ()
500
+ time <- System. currentTimeMillis ()
352
501
let interpreterState = InterpreterState {
353
502
loader = loader,
354
503
classes = classes,
355
- moduleName = " script.Main " ++ show time,
504
+ moduleName = " repl " ++ show time ++ " .Repl " ,
356
505
currentScript = " " ,
357
- modulePrelude = " "
506
+ transformDefs = id
358
507
}
359
508
return interpreterState
360
509
@@ -363,7 +512,6 @@ vmName = maybe "" id $ System.getProperty "java.vm.name"
363
512
javaVersion = maybe " " id $ System. getProperty " java.version"
364
513
365
514
pure native toString :: Object -> String
366
- native currentTimeMillis java. lang. System. currentTimeMillis :: () -> IO Long
367
515
368
516
welcome = " Welcome to Frege " ++ version ++ " (" ++ vmVendor ++ " " ++ vmName ++
369
517
" , " ++ javaVersion ++ " )"
@@ -375,12 +523,12 @@ printResult _ NoOutput = return ()
375
523
376
524
main [file] = do
377
525
interpreterState <- initInterpreterState
378
- evalInterpreter (runRepl (runFile file) initReplState) interpreterState
526
+ evalInterpreter (Repl. run (runFile file) initReplState) interpreterState
379
527
main _ = do
380
528
console <- ConsoleReader. new ()
381
529
console. setPrompt initReplState. prompt
382
530
console. setExpandEvents false
383
531
console. println welcome
384
532
interpreterState <- initInterpreterState
385
- evalInterpreter (runRepl (repl console) initReplState) interpreterState
386
-
533
+ evalInterpreter (Repl. run (repl console) initReplState) interpreterState
534
+ System. exit 0
0 commit comments