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` - Tree Calculus operator: `t`
- Assignments: `x = t t` - Assignments: `x = t t`
- Immutabile definitions - Immutable definitions
- Lambda abstraction syntax: `id = (\a : a)` - Lambda abstraction syntax: `id = (\a : a)`
- List, Number, and String literals: `[(2) ("Hello")]` - List, Number, and String literals: `[(2) ("Hello")]`
- Function application: `not (not false)` - Function application: `not (not false)`
@ -45,7 +45,7 @@ tricu > 12
[Releases are available for Linux.](https://git.eversole.co/James/tricu/releases) [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): - Quick Start (REPL):
- `nix run git+https://git.eversole.co/James/tricu` - `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 -> TricuAST -> Env
evalSingle env term evalSingle env term
| SDef name [] body <- term = | SDef name [] body <- term
if = case Map.lookup name env of
| Map.member name env -> Just existingValue
errorWithoutStackTrace $ | existingValue == evalAST env body -> env
"Error: Identifier '" ++ name ++ "' is already defined." | otherwise -> errorWithoutStackTrace $
| otherwise -> "Unable to rebind immutable identifier: " ++ name
Nothing ->
let res = evalAST env body let res = evalAST env body
in Map.insert "!result" res (Map.insert name res env) in Map.insert "!result" res (Map.insert name res env)
| SApp func arg <- term = | SApp func arg <- term
let res = apply (evalAST env func) (evalAST env arg) = let res = apply (evalAST env func) (evalAST env arg)
in Map.insert "!result" res env in Map.insert "!result" res env
| SVar name <- term = | SVar name <- term
case Map.lookup name env of = case Map.lookup name env of
Just v -> Just v -> Map.insert "!result" v env
Map.insert "!result" v env
Nothing -> Nothing ->
errorWithoutStackTrace $ "Variable `" ++ name ++ "` not defined\n\ errorWithoutStackTrace $ "Variable `" ++ name ++ "` not defined\n\
\This error should never occur here. Please report this as an issue." \This error should never occur here. Please report this as an issue."
| otherwise = | otherwise
Map.insert "!result" (evalAST env term) env = Map.insert "!result" (evalAST env term) env
evalTricu :: Env -> [TricuAST] -> Env evalTricu :: Env -> [TricuAST] -> Env
evalTricu env x = go env (reorderDefs env x) evalTricu env x = go env (reorderDefs env x)

View File

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

View File

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

View File

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