diff --git a/.gitignore b/.gitignore
index 2349edd..b66981f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -45,4 +45,7 @@ Thumbs.db
Desktop.ini
-*.versionsBackup
\ No newline at end of file
+*.versionsBackup
+
+# Ignore Intellij files
+*.iml
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 2ff8be4..db70952 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,16 +3,15 @@
4.0.0
frege
frege-repl
- 1.0.1
+ 1.0.2-SNAPSHOT
jar
frege-repl
https://github.com/Frege/frege-repl
UTF-8
src/main/frege
- frege-3.21.232-g7b05453
1.0.5
- 1.0.0
+ 1.0.2-SNAPSHOT
2.10
4.2.2
4.11
diff --git a/src/main/frege/frege/repl/FregeRepl.fr b/src/main/frege/frege/repl/FregeRepl.fr
index ec100a6..6ffd71f 100644
--- a/src/main/frege/frege/repl/FregeRepl.fr
+++ b/src/main/frege/frege/repl/FregeRepl.fr
@@ -1,23 +1,30 @@
module frege.repl.FregeRepl where
-import frege.compiler.Data
-import frege.compiler.Import
+import Compiler.Data
+import Compiler.Import
import frege.Version
-import frege.java.Net
+import Java.Net
import Data.List
-import frege.interpreter.FregeScriptCompiler hiding(compile)
-import frege.interpreter.FregeInterpreter
-import frege.compiler.Data(Global)
-import frege.compiler.EclipseUtil as EU()
+import Interpreter.FregeScriptCompiler hiding(compile)
+import Interpreter.FregeInterpreter
+import Compiler.Utilities as U()
+import Compiler.EclipseUtil as EU()
+import Compiler.DocUtils as DU(docit, docSym, DL, Doc, emitHtml)
+import Java.Swing (JFrame, invokeLater)
+import Java.Awt (Component, ActionListener)
+import Repl.Gui
data ReplState = ReplState {
lastExternalScript :: Maybe String,
+ lastJavaSource :: Maybe String,
prompt :: String
}
data Repl a = Repl {un :: StateT ReplState Interpreter a} where
get = Repl $ StateT (\s -> return (s, s))
- put s = Repl $ StateT (\_ -> return ((), s))
+ put s = Repl $ StateT (\_ -> return ((), s))
+ modify f = Repl $ StateT (\s -> return ((), f s))
+ run repl state = evalStateT (Repl.un repl) state
instance Monad Repl where
return = Repl . return
@@ -47,7 +54,8 @@ evalScript (Eval line) = do
oldInterpState <- Repl . lift $ Interpreter.get
fregecRes <- Repl . lift $ compile line
case fregecRes of
- CompilationSuccess (c@CompilationInfo{sourceInfo=sourceInfo,state=g}) ->
+ CompilationSuccess (c@CompilationInfo{javaSource=javaSrc, sourceInfo=sourceInfo,state=g}) -> do
+ Repl.modify $ ReplState.{lastJavaSource = Just javaSrc}
case sourceInfo of
Module -> return $ ReplSuccess $ "Loaded module: " ++ modName where
modName = moduleName g
@@ -57,7 +65,7 @@ evalScript (Eval line) = do
Repl . lift . Interpreter.put $ s.{currentScript <- (++ newLine ++ line)}
return NoOutput
Expression{variableName=var} -> do
- res <- Repl . lift $ evalShow line
+ res <- Repl . lift $ showVariable line var g
Repl . lift $ Interpreter.put oldInterpState
case res of
Left err -> return $ ReplError err
@@ -116,7 +124,12 @@ evalScript (Load filePath) = do
return res
_ -> return res
Left err -> return $ ReplError [err]
-
+
+evalScript Java = do
+ state <- Repl.get
+ showgui s = liftIO $ Runnable.new (javaSourceGUI s) >>= invokeLater >> return NoOutput
+ maybe (return NoOutput) showgui state.lastJavaSource
+
evalScript Reload = do
state <- Repl.get
case state.lastExternalScript of
@@ -135,10 +148,87 @@ evalScript Reset = do
evalScript Skip = return NoOutput
evalScript Help = return $ ReplSuccess help
+
+evalScript (HelpDoc source) = do
+ fregecRes <- Repl . lift $ compile "\"\"" -- Import current definitions
+ let response = maybe (return errmsg) (\m -> liftIO (gui m) >> return NoOutput)
+ errmsg = ReplError [source ++ " cannot be resolved!"]
+ gui content = invokeLater =<< Runnable.new (helpGUI source content)
+ case fregecRes of
+ CompilationSuccess (c@CompilationInfo{state=global}) -> oneof [helpdoc, packDoc] where
+ helpdoc = fst $ StG.run (helpDoc source) global
+ packDoc = fst $ StG.run (packDocumentation source) global
+ oneof = response . listToMaybe . catMaybes
+ _ -> response $ Just ""
+
evalScript Version = Repl.get >>= (\s -> return $ ReplSuccess version)
evalScript DefMulti = return NoOutput
evalScript Quit = return NoOutput
+sNameToQName sName = do
+ g <- getST
+ qNames <- U.resolve (VName g.thisPack) Position.null sName
+ return (listToMaybe qNames) -- Just taking first resolved
+
+helpDoc :: String -> StG (Maybe String)
+helpDoc source = do
+ global <- getST
+ qNameMay <- sNameToQName (createSName source)
+ let symMay = qNameMay >>= (\qname -> qname.findit global)
+ maybe (return Nothing) (\sym -> symbolDocumentation sym >>= return . Just) symMay
+
+createSName s
+ | Just (Just ns : Just ty : Just id : _) <- s `match` #^(.*)\.(.*)\.(\p{Lu}.*)$# = with2con ns ty id
+ | Just (Just ns : Just ty : Just id : _) <- s `match` #^(.*)\.(.*)\.(.*)$# = with2var ns ty id
+ | Just (Just ty : Just id : _) <- s `match` #^(.*)\.(\p{Lu}.*)$# = with1con ty id
+ | Just (Just ty : Just id : _) <- s `match` #^(.*)\.(.*)$# = with1var ty id
+ | s ~ #^\p{Lu}.*$# = Simple (Token CONID s 1 0 0 [])
+ | otherwise = Simple (Token VARID s 1 0 0 [])
+ where with2con ns ty id = With2 (qual ns) (qual ty) (con id)
+ with2var ns ty id = With2 (qual ns) (qual ty) (var id)
+ with1con ty id = With1 (qual ty) (con id)
+ with1var ty id = With1 (qual ty) (var id)
+ qual name = Token QUALIFIER name 1 0 0 []
+ con name = Token CONID name 1 0 0 []
+ var name = Token VARID name 1 0 0 []
+
+symbolDocumentation :: Symbol -> StG String
+symbolDocumentation sym = do
+ sw <- doio $ StringWriter.new ()
+ p <- doio $ StringWriter.printer sw
+ changeST Global.{gen <- GenSt.{printer=p}}
+ g <- getST
+ let ds = docSym g sym
+ dl = DL (Just "func") [ds]
+ doc = Doc [dl]
+ emitHtml false doc -- html without CSS, swing does not understand
+ doio $ g.printer.close
+ result <- doio $ sw.toString
+ return result
+
+packDocumentation :: String -> StG (Maybe String)
+packDocumentation pack = do
+ g <- getST
+ r <- getFP pack
+ case r of
+ Right (Just fp) -> case fp.doc of
+ "" -> return . Just $ ("Undocumented package " ++ pack)
+ text -> do
+ let doc = Doc $ docit g (Just text)
+ sw <- doio $ StringWriter.new ()
+ p <- doio $ StringWriter.printer sw
+ changeST Global.{gen <- GenSt.{printer=p}}
+ emitHtml false doc -- html without CSS, swing does not understand
+ doio $ g.printer.close
+ result <- doio $ sw.toString
+ return . Just $ result
+ Left ex -> return Nothing
+ sonst -> return . Just $ ("(java class?) " ++ pack)
+
+match s regex = groups <$> s =~ regex where groups m = [m.group i | i <- [1..groupCount m]]
+
+pure native groupCount :: Matcher -> Int
+
ioException :: IOException -> Repl ReplResult
ioException e = return $ ReplError [Throwable.getMessage e]
@@ -170,7 +260,7 @@ showVariable source var g = do
case symbolMay of
Nothing -> return . Left $ ["Not found: " ++ var]
Just symbol -> showSymbol source var symbol g
-
+
showSymbol source var symbol g | isVariable g symbol = do
state <- Interpreter.get
if isString g symbol then do
@@ -179,7 +269,7 @@ showSymbol source var symbol g | isVariable g symbol = do
Left err -> return $ Left [err.getMessage]
Right value -> return . Right $ toString value
else do
- Interpreter.put $ state.{currentScript <- (++ newLine ++ var ++ " = " ++ source)}
+ Interpreter.put $ state.{currentScript <- (++ newLine ++ (variableDeclScript var source))}
let showScript = buildShowScript var g symbol
showResult <- evalShow showScript
return showResult
@@ -217,18 +307,20 @@ positionAndName a b = case Symbol.pos a <=> Symbol.pos b of
Eq -> comparing (QName.base • Symbol.name) a b
ne -> ne
-cmdHelp = [(":type ", "Display the type of an expression"),
- (":{", "Start multiline definitions"),
- (":}", "End multiline defintions"),
+cmdHelp = [(":type ", "Display the type of an expression."),
(":browse ", "Display the names in a module if " ++
- "a module name is provided otherwise display the names in the default REPL module"),
- (":load ", "Load Frege code snippets from an URL or file"),
- (":r", "Reload the last script file"),
- (":history", "Display the source history for definitions in the default REPL module"),
- (":reset", "Reset the session discarding all evaluated scripts"),
- (":version", "Display Frege version"),
- (":q or :quit", "Quit REPL"),
- (":help", "Display this help message")]
+ "a module name is provided otherwise display the names in the default REPL module."),
+ (":load ", "Load Frege code snippets from an URL or file."),
+ (":java", "View Java translation of last compiled Frege source."),
+ (":r", "Reload the last script file."),
+ (":history", "Display the source history for definitions in the default REPL module."),
+ (":reset", "Reset the session discarding all evaluated scripts."),
+ (":version", "Display Frege version."),
+ (":{", "Start multiline definitions."),
+ (":}", "End multiline defintions."),
+ (":help ", "Display the documentation for the given name." ++
+ " If the name is not provided, display this help message."),
+ (":q or :quit", "Quit REPL")]
f `on` g = \x \y -> f (g x) (g y)
@@ -241,7 +333,7 @@ renderCmdHelp cmdHelp = map render cmdHelp where
help = intercalate newLine $ header ++ body where
header = ["At the prompt, you can enter Frege code snippets to get them evaluated.",
"The output or compilation errors will be printed below the prompt.",
- "In addition to Frege code, the following commands are supported:"]
+ "In addition to Frege code, the following commands are supported:\n"]
body = renderCmdHelp cmdHelp
runFile :: String -> Repl ()
@@ -283,26 +375,88 @@ urlContents url = do
inStream <- URL.openStream url
scanner <- Scanner.new inStream
scanner.useDelimiter "\\Z"
- scanner.next
+ scanner.next `finally` scanner.close
fileContents filePath = do
file <- File.new filePath
scanner <- Scanner.fromFile file "utf-8"
scanner.useDelimiter "\\Z"
- scanner.next
+ scanner.next `finally` scanner.close
data Scanner = mutable native java.util.Scanner where
native new :: InputStream -> IO Scanner
native fromFile new :: MutableIO File -> String -> IO Scanner throws FileNotFoundException
native useDelimiter :: Scanner -> String -> IO ()
native next :: Scanner -> IO String
-
-runRepl repl state = evalStateT (Repl.un repl) state
-pure native isEmpty :: String -> Bool
+javaSourceGUI :: String -> IO ()
+javaSourceGUI javaSource = do
+ frame::JFrame <- JFrame.new "Java Source"
+ newContentPane::JPanel <- BorderLayout.new () >>= JPanel.new
+ frame.setDefaultCloseOperation JFrame.dispose_on_close
+ editor::JEditorPane <- JEditorPane.new "text/plain" javaSource
+ editor.setEditable false
+ scrollPane <- JScrollPane.new editor
+ dimension <- Dimension.new 600 600
+ JComponent.setPreferredSize scrollPane dimension
+ Container.add newContentPane scrollPane (asObject BorderLayout.center)
+ frame.setContentPane newContentPane
+ frame.pack
+ frame.setVisible true
+
+
+helpGUI :: String -> String -> IO ()
+helpGUI title content = do
+ frame <- JFrame.new (title ++ " - Documentation")
+ frame.setDefaultCloseOperation JFrame.dispose_on_close
+ newContentPane:: JPanel <- BorderLayout.new () >>= JPanel.new
+ let html :: String
+ html = substring content $ indexOf content "" -- Skip DOCTYPE
+ editor::JEditorPane <- JEditorPane.new "text/html" html
+ editor.setEditable false
+ helpLinksListener <- FregeJavaProxy.with showDesktop HyperlinkListener.clazz
+ editor.addHyperlinkListener helpLinksListener
+ scrollPane <- JScrollPane.new editor
+ dimension <- Dimension.new 600 600
+ JComponent.setPreferredSize scrollPane dimension
+ Container.add newContentPane scrollPane (asObject BorderLayout.center)
+ newContentPane.setOpaque true
+ frame.setContentPane newContentPane
+ frame.pack
+ frame.setVisible true
+
+data FregeJavaProxy = pure native frege.memoryjavac.FregeJavaProxy where
+ native with frege.memoryjavac.FregeJavaProxy.with :: (Object -> Method -> ObjectArr -> ST s a) -> Class c -> IO c
+
+showDesktop :: Object -> Method -> ObjectArr -> IO ()
+showDesktop _ _ args = do
+ event <- return $ asHyperlinkEvent $ args.elemAt 0
+ d <- Desktop.getDesktop ()
+ desktopSupported <- d.isDesktopSupported
+ let url = HyperlinkEvent.getURL event
+ navigateRelative = either throw d.browse . URI.new $ fixHelpLink event.getDescription
+ navigate = maybe navigateRelative (\u -> toURI u >>= d.browse) url
+ when (event.getEventType == HyperlinkEvent_EventType.activated) navigate
+
+
+fixHelpLink (m~#^\.\.(.*)$#) = maybe helpRoot (\s -> helpRoot ++ s) $ m.group 1
+fixHelpLink s = s
+
+helpRoot = "http://try.frege-lang.org/doc"
+
+native toURI :: URL -> ST s URI throws URISyntaxException
+pure native asObject "(java.lang.Object)" :: a -> Object
-data Command = Version | Help | Browse | BrowseModule String | Type String | Load String | Eval String |
- DefMulti | History | Reload | Reset | Skip | Quit where
+
+pure native isEmpty :: String -> Bool
+pure native replaceAll :: String -> String -> String -> String
+pure native indexOf :: String -> String -> Int
+pure native substring :: String -> Int -> String
+ | String -> Int -> Int -> String
+
+data Command = Version | Help | HelpDoc String | Browse |
+ BrowseModule String | Type String | Load String | Eval String |
+ Java | DefMulti | History | Reload | Reset | Skip | Quit where
parse :: String -> Either String Command
parse s = parse' $ trim s where
@@ -315,11 +469,13 @@ data Command = Version | Help | Browse | BrowseModule String | Type String | Loa
parseCommand (m~#^:l.*?\s+(.*)#) =
maybe (Left "Missing external script path!") (Right . Load) $ m.group 1
parseCommand ":version" = Right Version
+ parseCommand (m~#:help\s+(.*)#) = maybe (Right Help) (Right . HelpDoc) $ m.group 1
parseCommand ":help" = Right Help
- parseCommand (m~#^:t.*?\s+(.*)#) =
+ parseCommand (m~#^:t.*?\s+(.*)#) =
maybe (Left "Missing expression!") (Right . Type) $ m.group 1
parseCommand ":{" = Right DefMulti
parseCommand ":history" = Right History
+ parseCommand ":java" = Right Java
parseCommand ":r" = Right Reload
parseCommand ":reset" = Right Reset
parseCommand ":q" = Right Quit
@@ -332,6 +488,7 @@ derive Show Command
initReplState :: ReplState
initReplState = ReplState{
lastExternalScript = Nothing,
+ lastJavaSource = Nothing,
prompt = "frege> " --Default prompt
}
@@ -340,14 +497,13 @@ initInterpreterState = do
urlarr <- URLArray.new 0
loader <- ClassLoader.current >>= URLClassLoader.new urlarr
classes <- HashMap.new ()
- time <- currentTimeMillis ()
- let history = ""
+ time <- System.currentTimeMillis ()
let interpreterState = InterpreterState {
loader = loader,
classes = classes,
- moduleName = "script.Main" ++ show time,
+ moduleName = "repl" ++ show time ++ ".Repl",
currentScript = "",
- modulePrelude = ""
+ transformDefs = id
}
return interpreterState
@@ -356,7 +512,6 @@ vmName = maybe "" id $ System.getProperty "java.vm.name"
javaVersion = maybe "" id $ System.getProperty "java.version"
pure native toString :: Object -> String
-native currentTimeMillis java.lang.System.currentTimeMillis :: () -> IO Long
welcome = "Welcome to Frege " ++ version ++ " (" ++ vmVendor ++ " " ++ vmName ++
", " ++ javaVersion ++ ")"
@@ -368,12 +523,12 @@ printResult _ NoOutput = return ()
main [file] = do
interpreterState <- initInterpreterState
- evalInterpreter (runRepl (runFile file) initReplState) interpreterState
+ evalInterpreter (Repl.run (runFile file) initReplState) interpreterState
main _ = do
console <- ConsoleReader.new ()
console.setPrompt initReplState.prompt
console.setExpandEvents false
console.println welcome
interpreterState <- initInterpreterState
- evalInterpreter (runRepl (repl console) initReplState) interpreterState
-
+ evalInterpreter (Repl.run (repl console) initReplState) interpreterState
+ System.exit 0
diff --git a/src/main/frege/frege/repl/Gui.fr b/src/main/frege/frege/repl/Gui.fr
new file mode 100644
index 0000000..05db45a
--- /dev/null
+++ b/src/main/frege/frege/repl/Gui.fr
@@ -0,0 +1,88 @@
+module frege.repl.Gui where
+
+import Java.Net (URI, URL)
+import Java.Awt (Component)
+
+pure native asHyperlinkListener "(javax.swing.event.HyperlinkListener)" :: Object -> HyperlinkListener
+pure native asHyperlinkEvent "(javax.swing.event.HyperlinkEvent)" :: Object -> HyperlinkEvent
+
+
+native setSize :: Component -> Int -> Int -> IO ()
+
+data Desktop = mutable native java.awt.Desktop where
+ native getDesktop java.awt.Desktop.getDesktop :: () -> IO Desktop
+ native isDesktopSupported :: Desktop -> IO Bool
+ native browse :: Desktop -> URI -> IO () throws IOException
+
+data HyperlinkEvent = pure native javax.swing.event.HyperlinkEvent where
+ pure native getURL :: HyperlinkEvent -> Maybe URL
+ pure native getDescription :: HyperlinkEvent -> String
+ pure native getEventType :: HyperlinkEvent -> HyperlinkEvent_EventType
+
+data HyperlinkEvent_EventType = pure native javax.swing.event.HyperlinkEvent.EventType where
+ pure native entered javax.swing.event.HyperlinkEvent.EventType.ENTERED :: HyperlinkEvent_EventType
+ pure native exited javax.swing.event.HyperlinkEvent.EventType.EXITED :: HyperlinkEvent_EventType
+ pure native activated javax.swing.event.HyperlinkEvent.EventType.ACTIVATED :: HyperlinkEvent_EventType
+ pure native equals :: HyperlinkEvent_EventType -> Object -> Bool
+ pure native hashCode :: HyperlinkEvent_EventType -> Int
+ pure native toString :: HyperlinkEvent_EventType -> String
+
+data BorderLayout = mutable native java.awt.BorderLayout where
+ native new :: () -> IO BorderLayout
+ | Int -> Int -> IO BorderLayout
+ pure native north java.awt.BorderLayout.NORTH :: String
+ pure native south java.awt.BorderLayout.SOUTH :: String
+ pure native east java.awt.BorderLayout.EAST :: String
+ pure native west java.awt.BorderLayout.WEST :: String
+ pure native center java.awt.BorderLayout.CENTER :: String
+ pure native before_first_line java.awt.BorderLayout.BEFORE_FIRST_LINE :: String
+ pure native after_last_line java.awt.BorderLayout.AFTER_LAST_LINE :: String
+ pure native before_line_begins java.awt.BorderLayout.BEFORE_LINE_BEGINS :: String
+ pure native after_line_ends java.awt.BorderLayout.AFTER_LINE_ENDS :: String
+ pure native page_start java.awt.BorderLayout.PAGE_START :: String
+ pure native page_end java.awt.BorderLayout.PAGE_END :: String
+ pure native line_start java.awt.BorderLayout.LINE_START :: String
+ pure native line_end java.awt.BorderLayout.LINE_END :: String
+
+data Container = mutable native java.awt.Container where
+ native add :: Container -> Component -> IO Component
+ | Container -> Component -> Object -> IO ()
+
+ native setLayout :: Container -> LayoutManager -> IO ()
+
+data JPanel = mutable native javax.swing.JPanel where
+ native new :: () -> IO JPanel
+ | LayoutManager -> IO JPanel
+
+data LayoutManager = mutable native java.awt.LayoutManager
+
+data JScrollPane = mutable native javax.swing.JScrollPane where
+ native new :: Component -> IO JScrollPane
+ native setVerticalScrollBarPolicy :: JScrollPane -> Int -> IO ()
+ native setHorizontalScrollBarPolicy :: JScrollPane -> Int -> IO ()
+ pure native horizontal_scrollbar_as_needed javax.swing.JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED :: Int
+ pure native vertical_scrollbar_always javax.swing.JScrollPane.VERTICAL_SCROLLBAR_ALWAYS :: Int
+ pure native horizontal_scrollbar_always javax.swing.JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS :: Int
+
+data JComponent = mutable native javax.swing.JComponent where
+ native setPreferredSize :: JComponent -> Dimension -> IO ()
+
+data Dimension = mutable native java.awt.Dimension where
+ native new :: Int -> Int -> IO Dimension
+
+data JTextArea = mutable native javax.swing.JTextArea where
+ native new :: String -> Int -> Int -> IO JTextArea
+
+instance Eq HyperlinkEvent_EventType where
+ e1 == e2 = e1.equals $ asObject e2
+
+data HyperlinkListener = pure native javax.swing.event.HyperlinkListener where
+ native hyperlinkUpdate :: HyperlinkListener -> HyperlinkEvent -> IO ()
+ native clazz "javax.swing.event.HyperlinkListener.class" :: Class HyperlinkListener
+
+data JEditorPane = mutable native javax.swing.JEditorPane where
+ native new :: String -> String -> IO JEditorPane
+ native addHyperlinkListener :: JEditorPane -> HyperlinkListener -> IO ()
+ native setEditable :: JEditorPane -> Bool -> IO ()
+
+private pure native asObject "(java.lang.Object)" :: a -> Object
\ No newline at end of file