This commit is contained in:
2026-01-08 21:47:58 -08:00
parent 4c058bfb77
commit 61f82a9d5d
6 changed files with 181 additions and 3 deletions

1
.gitignore vendored
View File

@@ -11,3 +11,4 @@ node_modules
mkday.py
tmp
min.js.gz
src/Revision.newt

View File

@@ -8,7 +8,12 @@ RUNJS=node
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
$(RUNJS) bootstrap/newt.js src/Main.newt -o newt.js

86
src/Lib/ReplParser.newt Normal file
View 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

View File

@@ -19,7 +19,9 @@ lookup qn@(QN ns nm) top =
Just mod => lookupMap' qn mod.modDefs
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 raw top =

View File

@@ -18,8 +18,10 @@ import Lib.Tokenizer
import Lib.TopContext
import Lib.Types
import Lib.Syntax
import Lib.ReplParser
import Node
import Serialize
import Revision
-- For editors, dump some information about the context (fc, name, type)
jsonTopContext : M Json
@@ -259,10 +261,72 @@ cmdLine (fn :: args) = do
(out, files) <- cmdLine args
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' = do
(arg0 :: args) <- liftIO {M} $ getArgs
| _ => error emptyFC "error reading args"
-- run REPL if there are no args
let (_ :: _) = args
| _ => do
replHeader
runRepl
(out, files) <- cmdLine args
traverse processFile files
@@ -274,7 +338,6 @@ main' = do
Nothing => pure MkUnit
Just name => writeSource name
main : IO Unit
main = do
-- we'll need to reset for each file, etc.

View File

@@ -34,3 +34,24 @@ pfunc exitFailure : ∀ a. String → a := `(_, msg) => {
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"));
}`