Add REPL
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,3 +11,4 @@ node_modules
|
|||||||
mkday.py
|
mkday.py
|
||||||
tmp
|
tmp
|
||||||
min.js.gz
|
min.js.gz
|
||||||
|
src/Revision.newt
|
||||||
|
|||||||
7
Makefile
7
Makefile
@@ -8,7 +8,12 @@ RUNJS=node
|
|||||||
|
|
||||||
all: newt.js
|
all: newt.js
|
||||||
|
|
||||||
newt.js: ${SRCS}
|
|
||||||
|
REV=$(shell git rev-parse --short HEAD)
|
||||||
|
src/Revision.newt: .PHONY
|
||||||
|
echo "module Revision\nimport Prelude\ngitRevision : String\ngitRevision = \"${REV}\"" > src/Revision.newt
|
||||||
|
|
||||||
|
newt.js: ${SRCS} src/Revision.newt
|
||||||
-rm build/* >/dev/null
|
-rm build/* >/dev/null
|
||||||
$(RUNJS) bootstrap/newt.js src/Main.newt -o newt.js
|
$(RUNJS) bootstrap/newt.js src/Main.newt -o newt.js
|
||||||
|
|
||||||
|
|||||||
86
src/Lib/ReplParser.newt
Normal file
86
src/Lib/ReplParser.newt
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
module Lib.ReplParser
|
||||||
|
|
||||||
|
import Prelude
|
||||||
|
import Lib.Parser.Impl
|
||||||
|
import Lib.Parser
|
||||||
|
import Lib.Token
|
||||||
|
import Lib.Common
|
||||||
|
import Data.List1
|
||||||
|
|
||||||
|
data ReplCommand
|
||||||
|
= Load String
|
||||||
|
| OutputJS String
|
||||||
|
| Verbose (Maybe Int)
|
||||||
|
| GetDoc String
|
||||||
|
| BrowseCmd QName
|
||||||
|
| HelpCmd
|
||||||
|
|
||||||
|
kw : String → Parser String
|
||||||
|
kw s = satisfy (\t => t.val.text == s) "Expected \{show s}"
|
||||||
|
|
||||||
|
replString : Parser String
|
||||||
|
replString = do
|
||||||
|
token StartQuote
|
||||||
|
s <- token StringKind
|
||||||
|
token EndQuote
|
||||||
|
pure s
|
||||||
|
|
||||||
|
replInt : Parser Int
|
||||||
|
replInt = do
|
||||||
|
t <- token Number
|
||||||
|
pure $ stringToInt t
|
||||||
|
|
||||||
|
replQN : Parser QName
|
||||||
|
replQN = do
|
||||||
|
ident <- uident
|
||||||
|
rest <- many $ token Projection
|
||||||
|
let name = joinBy "" (ident :: rest)
|
||||||
|
pure $ uncurry QN $ unsnoc $ split1 name "."
|
||||||
|
|
||||||
|
data ArgType = ArgNone | ArgString | ArgIdent | ArgOptInt | ArgQName
|
||||||
|
|
||||||
|
argCon : ArgType → U
|
||||||
|
argCon ArgNone = ReplCommand
|
||||||
|
argCon ArgOptInt = Maybe Int → ReplCommand
|
||||||
|
argCon ArgIdent = String → ReplCommand
|
||||||
|
argCon ArgString = String → ReplCommand
|
||||||
|
argCon ArgQName = QName → ReplCommand
|
||||||
|
|
||||||
|
data CmdDesc : U where
|
||||||
|
MkCmd : String → String → (arg : ArgType) → argCon arg → CmdDesc
|
||||||
|
|
||||||
|
commands : List CmdDesc
|
||||||
|
commands
|
||||||
|
= MkCmd ":h" "Show this help" ArgNone HelpCmd
|
||||||
|
:: MkCmd ":help" "Show this help" ArgNone HelpCmd
|
||||||
|
:: MkCmd ":l" "load file" ArgString Load
|
||||||
|
:: MkCmd ":load" "load file" ArgString Load
|
||||||
|
:: MkCmd ":o" "write javascript file" ArgString OutputJS
|
||||||
|
:: MkCmd ":out" "write javascript file" ArgString OutputJS
|
||||||
|
:: MkCmd ":v" "change verbosity" ArgOptInt Verbose
|
||||||
|
:: MkCmd ":d" "document function" ArgIdent GetDoc
|
||||||
|
:: MkCmd ":doc" "document function" ArgIdent GetDoc
|
||||||
|
:: MkCmd ":b" "browse namespace" ArgQName BrowseCmd
|
||||||
|
-- type at point
|
||||||
|
-- solve hole
|
||||||
|
-- search by prefix (for autocomplete - ideally include types at point, but we'd need recovery)
|
||||||
|
-- Ideally we could auto-import too
|
||||||
|
-- case split
|
||||||
|
:: Nil
|
||||||
|
|
||||||
|
parseCommand : Parser ReplCommand
|
||||||
|
parseCommand = do
|
||||||
|
key <- ident
|
||||||
|
let (Just cmd) = lookup key commands
|
||||||
|
| _ => fail "Unknown command"
|
||||||
|
the (Parser ReplCommand) $ case cmd of
|
||||||
|
MkCmd _ _ ArgNone cstr => pure cstr
|
||||||
|
MkCmd _ _ ArgOptInt cstr => cstr <$> optional replInt
|
||||||
|
MkCmd _ _ ArgIdent cstr => cstr <$> (ident <|> uident)
|
||||||
|
MkCmd _ _ ArgString cstr => cstr <$> replString
|
||||||
|
MkCmd _ _ ArgQName cstr => cstr <$> replQN
|
||||||
|
where
|
||||||
|
lookup : String → List CmdDesc → Maybe CmdDesc
|
||||||
|
lookup key (cmd@(MkCmd nm _ _ _) :: rest) =
|
||||||
|
if key == nm then Just cmd else lookup key rest
|
||||||
|
lookup key Nil = Nothing
|
||||||
@@ -19,7 +19,9 @@ lookup qn@(QN ns nm) top =
|
|||||||
Just mod => lookupMap' qn mod.modDefs
|
Just mod => lookupMap' qn mod.modDefs
|
||||||
Nothing => Nothing
|
Nothing => Nothing
|
||||||
|
|
||||||
-- TODO - look at imported namespaces, and either have a map of imported names or search imported namespaces..
|
lookupAll : String → TopContext -> List TopEntry
|
||||||
|
lookupAll raw top =
|
||||||
|
mapMaybe (flip lookup top) $ (QN top.ns raw) :: map (flip QN raw) top.imported
|
||||||
|
|
||||||
lookupRaw : String -> TopContext -> Maybe TopEntry
|
lookupRaw : String -> TopContext -> Maybe TopEntry
|
||||||
lookupRaw raw top =
|
lookupRaw raw top =
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ import Lib.Tokenizer
|
|||||||
import Lib.TopContext
|
import Lib.TopContext
|
||||||
import Lib.Types
|
import Lib.Types
|
||||||
import Lib.Syntax
|
import Lib.Syntax
|
||||||
|
import Lib.ReplParser
|
||||||
import Node
|
import Node
|
||||||
import Serialize
|
import Serialize
|
||||||
|
import Revision
|
||||||
|
|
||||||
-- For editors, dump some information about the context (fc, name, type)
|
-- For editors, dump some information about the context (fc, name, type)
|
||||||
jsonTopContext : M Json
|
jsonTopContext : M Json
|
||||||
@@ -259,10 +261,72 @@ cmdLine (fn :: args) = do
|
|||||||
(out, files) <- cmdLine args
|
(out, files) <- cmdLine args
|
||||||
pure (out, fn :: files)
|
pure (out, fn :: files)
|
||||||
|
|
||||||
|
browseTop : QName → M Unit
|
||||||
|
browseTop qn@(QN ns x) = do
|
||||||
|
top <- getTop
|
||||||
|
let ns = snoc ns x
|
||||||
|
let (Just mod) = lookupMap' ns top.modules
|
||||||
|
| _ => putStrLn "module \{show qn} not loaded"
|
||||||
|
go $ listValues mod.modDefs
|
||||||
|
where
|
||||||
|
go : List TopEntry → M Unit
|
||||||
|
go Nil = pure MkUnit
|
||||||
|
go (e :: es) = do
|
||||||
|
putStrLn "\{show e.name} : \{rpprint Nil e.type}"
|
||||||
|
go es
|
||||||
|
|
||||||
|
replHeader : M Unit
|
||||||
|
replHeader = putStrLn "Newt REPL (\{show gitRevision})\n"
|
||||||
|
|
||||||
|
getDoc : String → M Unit
|
||||||
|
getDoc name = do
|
||||||
|
top <- getTop
|
||||||
|
for_ (lookupAll name top) $ \e =>
|
||||||
|
putStrLn "\{show e.name} : \{rpprint Nil e.type}"
|
||||||
|
|
||||||
|
replHelp : M Unit
|
||||||
|
replHelp =
|
||||||
|
for_ commands $ \cmd => do
|
||||||
|
let args = case cmd of
|
||||||
|
MkCmd _ _ ArgNone _ => ""
|
||||||
|
MkCmd _ _ ArgQName _ => " Lib.Types"
|
||||||
|
MkCmd _ _ ArgOptInt _ => " [ 1 ]"
|
||||||
|
MkCmd _ _ ArgIdent _ => " map"
|
||||||
|
MkCmd _ _ ArgString _ => " \"name\""
|
||||||
|
case cmd of
|
||||||
|
(MkCmd kw desc _ _) => putStrLn "\{kw}\{args} - \{desc}"
|
||||||
|
|
||||||
|
runCommand : ReplCommand → M Unit
|
||||||
|
runCommand (Load fn) = processFile fn
|
||||||
|
runCommand (HelpCmd) = replHelp
|
||||||
|
runCommand (BrowseCmd qn) = browseTop qn
|
||||||
|
runCommand (GetDoc name) = getDoc name
|
||||||
|
runCommand (Verbose Nothing) = modifyTop [ verbose $= _+_ 1 ]
|
||||||
|
runCommand (Verbose (Just v)) = modifyTop [ verbose := v ]
|
||||||
|
runCommand (OutputJS fn) = writeSource fn
|
||||||
|
|
||||||
|
runRepl : M Unit
|
||||||
|
runRepl = do
|
||||||
|
liftIO $ putStr "> "
|
||||||
|
Right line <- liftIO {M} $ readLine
|
||||||
|
| Left err => pure MkUnit
|
||||||
|
let (Right toks) = tokenise "<stdin>" line
|
||||||
|
| Left err => putStrLn (showError line err) >> runRepl
|
||||||
|
let (Right cmd) = parse "<stdin>" parseCommand toks
|
||||||
|
| Left err => putStrLn (showError line err) >> runRepl
|
||||||
|
catchError (runCommand cmd) (\ err => putStrLn $ showError line err)
|
||||||
|
runRepl
|
||||||
|
|
||||||
|
-- TODO translate args into REPL commands?
|
||||||
main' : M Unit
|
main' : M Unit
|
||||||
main' = do
|
main' = do
|
||||||
(arg0 :: args) <- liftIO {M} $ getArgs
|
(arg0 :: args) <- liftIO {M} $ getArgs
|
||||||
| _ => error emptyFC "error reading args"
|
| _ => error emptyFC "error reading args"
|
||||||
|
-- run REPL if there are no args
|
||||||
|
let (_ :: _) = args
|
||||||
|
| _ => do
|
||||||
|
replHeader
|
||||||
|
runRepl
|
||||||
(out, files) <- cmdLine args
|
(out, files) <- cmdLine args
|
||||||
traverse processFile files
|
traverse processFile files
|
||||||
|
|
||||||
@@ -274,7 +338,6 @@ main' = do
|
|||||||
Nothing => pure MkUnit
|
Nothing => pure MkUnit
|
||||||
Just name => writeSource name
|
Just name => writeSource name
|
||||||
|
|
||||||
|
|
||||||
main : IO Unit
|
main : IO Unit
|
||||||
main = do
|
main = do
|
||||||
-- we'll need to reset for each file, etc.
|
-- we'll need to reset for each file, etc.
|
||||||
|
|||||||
@@ -34,3 +34,24 @@ pfunc exitFailure : ∀ a. String → a := `(_, msg) => {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}`
|
}`
|
||||||
|
|
||||||
|
pfunc putStr uses (MkIORes MkUnit): String → IO Unit := `(s) => (w) => {
|
||||||
|
let {writeSync} = require('fs');
|
||||||
|
let buf = new TextEncoder().encode(s);
|
||||||
|
writeSync(1, buf);
|
||||||
|
return Prelude_MkIORes(Prelude_MkUnit, w);
|
||||||
|
}`
|
||||||
|
|
||||||
|
pfunc readLine uses (MkIORes Left Right) : IO (Either String String) := `(w) => {
|
||||||
|
let {readSync} = require('fs');
|
||||||
|
let buf = Buffer.alloc(1024);
|
||||||
|
let p = 0
|
||||||
|
while (readSync(0, buf, p, 1, null)) {
|
||||||
|
if (buf[p++] == 10) return Prelude_MkIORes(Prelude_Right(new TextDecoder().decode(buf.slice(0,p))),w);
|
||||||
|
if (p + 10 > buf.length) {
|
||||||
|
let tmp = Buffer.alloc(buf.length * 1.5);
|
||||||
|
tmp.set(buf);
|
||||||
|
buf = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Prelude_MkIORes(Prelude_Left("EOF"));
|
||||||
|
}`
|
||||||
|
|||||||
Reference in New Issue
Block a user