aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/Main.hs2
-rw-r--r--examples/test.lisp4
-rw-r--r--lang.cabal3
-rw-r--r--package.yaml1
-rw-r--r--src/Lib.hs97
-rw-r--r--src/Types.hs44
-rw-r--r--src/Utils.hs2
-rw-r--r--stack.yaml4
-rw-r--r--stack.yaml.lock14
9 files changed, 139 insertions, 32 deletions
diff --git a/app/Main.hs b/app/Main.hs
index 1829c62..978cafc 100644
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -26,7 +26,7 @@ repl config = do
result <- lift $ runExceptT $ runReaderT (runInlineScript input) config
case result of
Left (LException ex) -> outputStrLn $ "Error: " ++ ex
- Right () -> outputStrLn $ input
+ Right () -> return ()
repl config
main :: IO ()
diff --git a/examples/test.lisp b/examples/test.lisp
index a84d887..b753827 100644
--- a/examples/test.lisp
+++ b/examples/test.lisp
@@ -3,4 +3,6 @@ foo ; test1
1
2 )
"string with space"
-"another \n\"string\"" \ No newline at end of file
+"another \n\"string\""
+(let id
+ (\[a] a))
diff --git a/lang.cabal b/lang.cabal
index 2f2a91f..aa2e3dd 100644
--- a/lang.cabal
+++ b/lang.cabal
@@ -38,6 +38,7 @@ library
, containers
, haskeline ==0.8.2
, mtl
+ , regex-tdfa ==1.3.2
default-language: Haskell2010
executable lang-exe
@@ -53,6 +54,7 @@ executable lang-exe
, haskeline ==0.8.2
, lang
, mtl
+ , regex-tdfa ==1.3.2
default-language: Haskell2010
test-suite lang-test
@@ -69,4 +71,5 @@ test-suite lang-test
, haskeline ==0.8.2
, lang
, mtl
+ , regex-tdfa ==1.3.2
default-language: Haskell2010
diff --git a/package.yaml b/package.yaml
index 894f5f4..7a2296c 100644
--- a/package.yaml
+++ b/package.yaml
@@ -24,6 +24,7 @@ dependencies:
- containers
- mtl
- haskeline == 0.8.2
+- regex-tdfa == 1.3.2
ghc-options:
- -Wall
diff --git a/src/Lib.hs b/src/Lib.hs
index 28ab062..13e4234 100644
--- a/src/Lib.hs
+++ b/src/Lib.hs
@@ -5,34 +5,13 @@ module Lib (
import qualified Data.Map as M
import qualified Data.Bifunctor as B
+import qualified Data.List as L
import Control.Monad.Except
import Control.Monad.Reader
-import Data.List
-import Debug.Trace
-import System.Console.Haskeline
+import Text.Regex.TDFA
import Types
import Utils
-data AST
- = ASTNumber Double
- | ASTSymbol String
- | ASTBoolean Bool
- | ASTString String
- | ASTVector [AST]
- | ASTHashMap (M.Map AST AST)
- | ASTFunction (AST -> AST)
-
-instance (Show AST) where
- show (ASTNumber n) = show n
- show (ASTSymbol s) = show s
- show (ASTBoolean b) = show b
- show (ASTString s) = show s
- show (ASTVector v) = "[" ++ intercalate " " (map show v) ++ "]"
- show (ASTHashMap m) =
- let flattenMap = M.assocs .> map (\(k, v) -> [k, v]) .> concat
- in "[" ++ intercalate " " (map show $ flattenMap m) ++ "]"
- show (ASTFunction _) = "<fn>"
-
_tokenize :: [String] -> String -> String -> LContext [String]
_tokenize acc current src = case src of
"" -> return $ reverse current : acc
@@ -53,13 +32,14 @@ _tokenize acc current src = case src of
[] -> ("", -1)
(string, stringLength) = consume xs
stringDropped = drop (stringLength) xs
+ withQuotes = "\"" ++ string ++ "\""
in do
when (stringLength == -1) $ throwError (LException "Unbalanced string literal")
- _tokenize (string : acc) "" stringDropped
+ _tokenize (withQuotes : acc) "" stringDropped
| x `elem` [' ', '\n', '\t', '\r'] ->
_tokenize (reverse current : acc) "" xs
| x `elem` ['(', ')', '[', ']', '{', '}', '\\'] ->
- _tokenize ([x] : acc) "" xs
+ _tokenize ([x] : reverse current : acc) "" xs
| otherwise ->
_tokenize acc (x : current) xs
@@ -70,8 +50,66 @@ tokenize src = do
$> reverse
.> filter (\s -> length s > 0)
-parse :: [String] -> [AST]
-parse src = []
+validateBalance :: [String] -> [AST] -> LContext [AST]
+validateBalance allowed asts = do
+ when (ASTSymbol "(" `elem` asts && "(" `notElem` allowed)
+ $ throwError $ LException "Unbalanced function call"
+ when (ASTSymbol "[" `elem` asts && "[" `notElem` allowed)
+ $ throwError $ LException "Unbalanced vector"
+ when (ASTSymbol "{" `elem` asts && "{" `notElem` allowed)
+ $ throwError $ LException "Unbalanced hash map"
+ return asts
+
+asPairs :: [a] -> LContext [(a, a)]
+asPairs [] = return []
+asPairs (a:b:rest) = do
+ restPaired <- asPairs rest
+ return $ (a, b) : restPaired
+asPairs _ = throwError $ LException "Odd number of elements to pair up"
+
+parseToken :: String -> AST
+parseToken token
+ | isNumber token = ASTNumber (read token)
+ | isString token = ASTString $ removeQuotes token
+ | isBoolean token = ASTBoolean $ asBoolean token
+ | otherwise = ASTSymbol token
+ where
+ numberRegex = "^-?[[:digit:]]+(\\.[[:digit:]]+)?$"
+ isNumber :: String -> Bool
+ isNumber t = t =~ numberRegex
+ isString t = "\"" `L.isPrefixOf` t
+ removeQuotes s = drop 1 s $> take (length s - 2)
+ isBoolean t = t `elem` ["true", "false"]
+ asBoolean t = if t == "true" then True else False
+
+_parse :: [AST] -> [String] -> LContext [AST]
+_parse acc' [] = do
+ acc <- validateBalance [] acc'
+ return $ reverse acc
+_parse acc (")":rest) = do
+ let children' = takeWhile (/= ASTSymbol "(") acc
+ children <- validateBalance ["("] children'
+ let fnCall = ASTFunctionCall (reverse children)
+ let newAcc = fnCall : drop (length children + 1) acc
+ _parse newAcc rest
+_parse acc ("]":rest) = do
+ let children' = takeWhile (/= ASTSymbol "[") acc
+ children <- validateBalance ["["] children'
+ let vec = ASTVector (reverse children)
+ let newAcc = vec : drop (length children + 1) acc
+ _parse newAcc rest
+_parse acc ("}":rest) = do
+ let children' = takeWhile (/= ASTSymbol "{") acc
+ children <- validateBalance ["{"] children'
+ pairs <- asPairs $ reverse children
+ let vec = ASTHashMap (M.fromList pairs)
+ let newAcc = vec : drop (length children + 1) acc
+ _parse newAcc rest
+_parse acc (token:rest) =
+ _parse (parseToken token : acc) rest
+
+parse :: [String] -> LContext [AST]
+parse = _parse []
runScriptFile :: String -> LContext ()
runScriptFile fileName = do
@@ -83,5 +121,6 @@ runInlineScript src = do
tokenized <- tokenize src
config <- ask
when (configVerboseMode config) $ liftIO $ putStrLn $ "tokenized:\t\t" ++ show tokenized
- let parsed = parse tokenized
- return ()
+ parsed <- parse tokenized
+ when (configVerboseMode config) $ liftIO $ putStrLn $ "parsed:\t\t\t" ++ show parsed
+ liftIO $ mapM_ putStrLn (map show parsed)
diff --git a/src/Types.hs b/src/Types.hs
index 94428b6..5779845 100644
--- a/src/Types.hs
+++ b/src/Types.hs
@@ -1,7 +1,11 @@
+{-# OPTIONS_GHC -Wno-missing-export-lists #-}
module Types where
import Control.Monad.Except
import Control.Monad.Reader
+import qualified Data.Map as M
+import qualified Data.List as L
+import Utils
newtype LException = LException String
data Config = Config {
@@ -11,3 +15,43 @@ data Config = Config {
}
type LContext a = ReaderT Config (ExceptT LException IO) a
+
+data AST
+ = ASTNumber Double
+ | ASTSymbol String
+ | ASTBoolean Bool
+ | ASTString String
+ | ASTVector [AST]
+ | ASTFunctionCall [AST]
+ | ASTHashMap (M.Map AST AST)
+ | ASTFunction (AST -> AST)
+
+instance (Show AST) where
+ show (ASTNumber n) = show n
+ show (ASTSymbol s) = s
+ show (ASTBoolean b) = show b
+ show (ASTString s) = show s
+ show (ASTVector v) = "[" ++ L.intercalate " " (map show v) ++ "]"
+ show (ASTFunctionCall v) = "(" ++ L.intercalate " " (map show v) ++ ")"
+ show (ASTHashMap m) =
+ let flattenMap = M.assocs .> map (\(k, v) -> [k, v]) .> concat
+ in "{" ++ L.intercalate " " (map show $ flattenMap m) ++ "}"
+ show (ASTFunction _) = "<fn>"
+
+instance (Eq AST) where
+ ASTNumber a == ASTNumber b = a == b
+ ASTSymbol a == ASTSymbol b = a == b
+ ASTBoolean a == ASTBoolean b = a == b
+ ASTString a == ASTString b = a == b
+ ASTVector a == ASTVector b = a == b
+ ASTHashMap a == ASTHashMap b = a == b
+ _ == _ = False
+
+instance (Ord AST) where
+ ASTNumber a <= ASTNumber b = a <= b
+ ASTSymbol a <= ASTSymbol b = a <= b
+ ASTBoolean a <= ASTBoolean b = a <= b
+ ASTString a <= ASTString b = a <= b
+ ASTVector a <= ASTVector b = a <= b
+ ASTHashMap a <= ASTHashMap b = a <= b
+ _ <= _ = False
diff --git a/src/Utils.hs b/src/Utils.hs
index 2497c9f..227e120 100644
--- a/src/Utils.hs
+++ b/src/Utils.hs
@@ -1,3 +1,5 @@
+{-# OPTIONS_GHC -Wno-missing-export-lists #-}
+{-# OPTIONS_GHC -Wno-missing-signatures #-}
module Utils where
(.>) = flip (.)
diff --git a/stack.yaml b/stack.yaml
index 0a4ef42..703378b 100644
--- a/stack.yaml
+++ b/stack.yaml
@@ -5,4 +5,6 @@ resolver:
compiler: ghc-9.4.2
require-stack-version: ==2.7.5
extra-deps:
- - haskeline-0.8.2 \ No newline at end of file
+ - haskeline-0.8.2
+ - regex-base-0.94.0.2
+ - regex-tdfa-1.3.2 \ No newline at end of file
diff --git a/stack.yaml.lock b/stack.yaml.lock
index 5200f97..81770bd 100644
--- a/stack.yaml.lock
+++ b/stack.yaml.lock
@@ -11,4 +11,18 @@ packages:
sha256: a51f2be1d61d077b8c972d762e9799983f08c95cae5123cf8ade8803e3250579
original:
hackage: haskeline-0.8.2
+- completed:
+ hackage: regex-base-0.94.0.2@sha256:4ff4425c710cddf440dfbac6cd52310bb6b23e17902390ff71c9fc7eaafc4fcc,2643
+ pantry-tree:
+ size: 531
+ sha256: 6415af6c7af19d83283a19407cbf9ece0dc9cf9be9a0d9560d0456247cfa0d00
+ original:
+ hackage: regex-base-0.94.0.2
+- completed:
+ hackage: regex-tdfa-1.3.2@sha256:3d8571a5ce87ee89ae0de602f1461df9a8236257f80548e9bee71585a26e3927,6845
+ pantry-tree:
+ size: 2674
+ sha256: 2725c8313eaf9dc1403f27b691b02143aa6ec546adaea7ab5c985b0f9a777014
+ original:
+ hackage: regex-tdfa-1.3.2
snapshots: []