4 Commits

Author SHA1 Message Date
ecf1115742 Better handling of interrupts in REPL 2025-01-27 16:46:41 -06:00
33c2119708 Don't require available library to run REPL or decoder
All checks were successful
Test, Build, and Release / test (push) Successful in 1m11s
Test, Build, and Release / build (push) Successful in 1m12s
2025-01-27 16:28:40 -06:00
3b833ca75b Gracefully ignore no-op redefs
All checks were successful
Test, Build, and Release / test (push) Successful in 1m11s
Test, Build, and Release / build (push) Successful in 1m14s
2025-01-27 16:19:59 -06:00
203bc1898d README typo 2025-01-27 16:07:32 -06:00
5 changed files with 49 additions and 44 deletions

View File

@ -10,7 +10,7 @@ tricu is the word for "tree" in Lojban: `(x1) is a tree of species/cultivar (x2)
- Tree Calculus operator: `t`
- Assignments: `x = t t`
- Immutabile definitions
- Immutable definitions
- Lambda abstraction syntax: `id = (\a : a)`
- List, Number, and String literals: `[(2) ("Hello")]`
- Function application: `not (not false)`
@ -45,7 +45,7 @@ tricu > 12
[Releases are available for Linux.](https://git.eversole.co/James/tricu/releases)
Or you can easily build and/or run this project using [Nix](https://nixos.org/download/).
Or you can easily build and run this project using [Nix](https://nixos.org/download/).
- Quick Start (REPL):
- `nix run git+https://git.eversole.co/James/tricu`

View File

@ -10,26 +10,26 @@ import qualified Data.Set as Set
evalSingle :: Env -> TricuAST -> Env
evalSingle env term
| SDef name [] body <- term =
if
| Map.member name env ->
errorWithoutStackTrace $
"Error: Identifier '" ++ name ++ "' is already defined."
| otherwise ->
let res = evalAST env body
in Map.insert "!result" res (Map.insert name res env)
| SApp func arg <- term =
let res = apply (evalAST env func) (evalAST env arg)
| SDef name [] body <- term
= case Map.lookup name env of
Just existingValue
| existingValue == evalAST env body -> env
| otherwise -> errorWithoutStackTrace $
"Unable to rebind immutable identifier: " ++ name
Nothing ->
let res = evalAST env body
in Map.insert "!result" res (Map.insert name res env)
| SApp func arg <- term
= let res = apply (evalAST env func) (evalAST env arg)
in Map.insert "!result" res env
| SVar name <- term =
case Map.lookup name env of
Just v ->
Map.insert "!result" v env
| SVar name <- term
= case Map.lookup name env of
Just v -> Map.insert "!result" v env
Nothing ->
errorWithoutStackTrace $ "Variable `" ++ name ++ "` not defined\n\
\This error should never occur here. Please report this as an issue."
| otherwise =
Map.insert "!result" (evalAST env term) env
| otherwise
= Map.insert "!result" (evalAST env term) env
evalTricu :: Env -> [TricuAST] -> Env
evalTricu env x = go env (reorderDefs env x)

View File

@ -59,9 +59,8 @@ main = do
case args of
Repl -> do
putStrLn "Welcome to the tricu REPL"
putStrLn "You can exit with `CTRL+D` or the `:_exit` command.`"
library <- liftIO $ evaluateFile "./lib/base.tri"
repl $ Map.delete "!result" library
putStrLn "You can exit with `CTRL+D` or the `!exit` command.`"
repl Map.empty
Evaluate { file = filePaths, form = form } -> do
result <- case filePaths of
[] -> do
@ -77,8 +76,7 @@ main = do
value <- case filePaths of
[] -> getContents
(filePath:_) -> readFile filePath
library <- liftIO $ evaluateFile "./lib/base.tri"
putStrLn $ decodeResult $ result $ evalTricu library $ parseTricu value
putStrLn $ decodeResult $ result $ evalTricu Map.empty $ parseTricu value
runTricu :: String -> T
runTricu input =

View File

@ -8,6 +8,7 @@ import Research
import Control.Exception (SomeException, catch)
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Catch (handle, MonadCatch)
import Data.Char (isSpace)
import Data.List (dropWhile, dropWhileEnd, intercalate)
import System.Console.Haskeline
@ -15,33 +16,37 @@ import System.Console.Haskeline
import qualified Data.Map as Map
repl :: Env -> IO ()
repl env = runInputT defaultSettings (loop env)
repl env = runInputT defaultSettings (withInterrupt (loop env))
where
loop :: Env -> InputT IO ()
loop env = do
loop env = handle (interruptHandler env) $ do
minput <- getInputLine "tricu < "
if
| Nothing <- minput -> outputStrLn "Exiting tricu"
| Just s <- minput, strip s == "!exit" -> outputStrLn "Exiting tricu"
| Just s <- minput, strip s == "" -> do
outputStrLn ""
loop env
| Just s <- minput, strip s == "!import" -> do
path <- getInputLine "File path to load < "
if
| Nothing <- path -> do
outputStrLn "No input received; stopping import."
loop env
| Just p <- path -> do
loadedEnv <- liftIO $ evaluateFileWithContext env (strip p) `catch` \e -> errorHandler env e
loop $ Map.delete "!result" (Map.union loadedEnv env)
| Just s <- minput -> do
if
| take 2 s == "--" -> loop env
| otherwise -> do
case minput of
Nothing -> outputStrLn "Exiting tricu"
Just s
| strip s == "!exit" -> outputStrLn "Exiting tricu"
| strip s == "" -> loop env
| strip s == "!import" -> do
path <- getInputLine "File path to load < "
case path of
Nothing -> do
outputStrLn "No input received; stopping import."
loop env
Just p -> do
loadedEnv <- liftIO $ evaluateFileWithContext env
(strip p) `catch` \e -> errorHandler env e
loop $ Map.delete "!result" (Map.union loadedEnv env)
| take 2 s == "--" -> loop env
| otherwise -> do
newEnv <- liftIO $ processInput env s `catch` errorHandler env
loop newEnv
interruptHandler :: Env -> Interrupt -> InputT IO ()
interruptHandler env _ = do
outputStrLn "Interrupted with CTRL+C\n\
\You can use the !exit command or CTRL+D to exit"
loop env
processInput :: Env -> String -> IO Env
processInput env input = do
let asts = parseTricu input

View File

@ -26,6 +26,7 @@ executable tricu
base >=4.7
, cmdargs
, containers
, exceptions
, haskeline
, megaparsec
, mtl
@ -52,6 +53,7 @@ test-suite tricu-tests
base
, cmdargs
, containers
, exceptions
, haskeline
, megaparsec
, mtl