diff options
| author | Jan Tuomi <jans.tuomi@gmail.com> | 2022-12-10 22:00:17 +0200 |
|---|---|---|
| committer | Jan Tuomi <jans.tuomi@gmail.com> | 2022-12-11 01:37:50 +0200 |
| commit | f522e39756ae0aef5e49585fc735c475cf3ff9e4 (patch) | |
| tree | 90a8fe239b064c5b1b20fe2ad2fbe7f7e792bff9 | |
| parent | 37e251578181213e49ba08d3de39f0b8b4f77427 (diff) | |
Refactor builtins, add new ones
| -rw-r--r-- | core/common.milch | 14 | ||||
| -rw-r--r-- | spec.md | 7 | ||||
| -rw-r--r-- | src/Builtins.hs | 250 | ||||
| -rw-r--r-- | test/Spec.hs | 8 |
4 files changed, 188 insertions, 91 deletions
diff --git a/core/common.milch b/core/common.milch index 2dc6698..a81a4ef 100644 --- a/core/common.milch +++ b/core/common.milch @@ -91,7 +91,7 @@ (let take (\[n xs] (match n 0 [] - otherwise (prepend (head xs) (take (- n 1) (tail xs)))))) + otherwise (cons (head xs) (take (- n 1) (tail xs)))))) (let drop (\[n xs] (match n @@ -102,7 +102,7 @@ (let map (\[f lst] (match lst [] [] - otherwise (prepend (f (head lst)) (map f (tail lst)))))) + otherwise (cons (f (head lst)) (map f (tail lst)))))) ; foldr :: (a -> b -> b) -> b -> [a] -> b (let foldr (\[f accumulator lst] @@ -116,14 +116,14 @@ [] [] otherwise (match (pred (head lst)) true - (prepend (head lst) (filter pred (tail lst))) + (cons (head lst) (filter pred (tail lst))) false (filter pred (tail lst)))))) (let _reverse (\[v a] (let x (head v)) (let xs (tail v)) - (let xa (prepend x a)) + (let xa (cons x a)) (match v [] a _ (_reverse xs xa)))) @@ -138,10 +138,10 @@ (match vs [] (match v delim [(reverse acc)] - otherwise [(prepend v (reverse acc))]) + otherwise [(cons v (reverse acc))]) otherwise (match v - delim (prepend (reverse acc) (_split-by delim [] vs)) - otherwise (_split-by delim (prepend v acc) vs))))) + delim (cons (reverse acc) (_split-by delim [] vs)) + otherwise (_split-by delim (cons v acc) vs))))) ; split vector by delimiter (let split-by (\[delim vals] @@ -7,7 +7,8 @@ ## Value types - Number -3.14 + Integer 3 + Float -3.14 Symbol PI Tag :tag Boolean true @@ -76,7 +77,7 @@ Drops the first element of a vector. (tail vec) -`prepend` +`cons` Pushes the first argument to the front of the second argument (vector). - (prepend x xs) + (cons x xs) diff --git a/src/Builtins.hs b/src/Builtins.hs index a83773b..511ef08 100644 --- a/src/Builtins.hs +++ b/src/Builtins.hs @@ -11,42 +11,43 @@ import Utils builtinEnv :: Env builtinEnv = M.fromList $ map (B.second Regular) [ - -- arithmetic - builtinAdd2, - builtinSubtract2, - builtinMultiply2, - builtinDivide2, - -- comparisons + -- number (integer, double) operations + builtinNumAdd2, + builtinNumSubtract2, + builtinNumMultiply2, + builtinNumDivide2, + builtinNumLt2, + builtinNumFloor, + builtinNumCeil, + -- equality builtinEq2, - builtinLt2, -- conversions builtinFmt, - builtinFloor, - builtinToDouble, + builtinToFloat, builtinParseInt, - -- vector operations - builtinHead, - builtinTail, - builtinPrepend, - builtinSortByFirst, - -- string operations - builtinSubstr, + builtinParseFloat, builtinStrToVec, - builtinLen, - -- vector & string operations - builtinConcat, - -- filesystem operations + -- sequence (vector, string) operations + builtinSeqHead, + builtinSeqTail, + builtinSeqCons, + builtinSortByFirst, + builtinSeqSlice, + builtinSeqLen, + builtinSeqConcat, + -- IO operations + builtinPrint, builtinReadFile, builtinWriteFile, builtinAppendFile, -- special - builtinPrint, builtinTry, ("unit", makeNonsenseAST ASTUnit), ("_", makeNonsenseAST ASTHole), ("otherwise", makeNonsenseAST ASTHole), builtinFatal, builtinKind, + builtinType, -- atom builtinAtom, builtinAtomUpdate, @@ -80,8 +81,8 @@ reservedKeyword name = (name, makeNonsenseAST $ ASTFunction Pure fn1) where -- BUILTINS -builtinAdd2 :: (String, AST) -builtinAdd2 = (name, makeNonsenseAST $ ASTFunction Pure fn1) where +builtinNumAdd2 :: (String, AST) +builtinNumAdd2 = (name, makeNonsenseAST $ ASTFunction Pure fn1) where name = "+" fn1 ast1@AST { an = ASTInteger a } = return $ makeNonsenseAST $ ASTFunction Pure $ fn2 where @@ -95,8 +96,8 @@ builtinAdd2 = (name, makeNonsenseAST $ ASTFunction Pure fn1) where fn2 ast2 = throwL (astPos ast2, argError2 name ast1 ast2) fn1 ast1 = throwL (astPos ast1, argError1 name ast1) -builtinSubtract2 :: (String, AST) -builtinSubtract2 = (name, makeNonsenseAST $ ASTFunction Pure fn1) where +builtinNumSubtract2 :: (String, AST) +builtinNumSubtract2 = (name, makeNonsenseAST $ ASTFunction Pure fn1) where name = "-" fn1 ast1@AST { an = ASTInteger a } = return $ makeNonsenseAST $ ASTFunction Pure $ fn2 where @@ -110,8 +111,8 @@ builtinSubtract2 = (name, makeNonsenseAST $ ASTFunction Pure fn1) where fn2 ast2 = throwL (astPos ast2, argError2 name ast1 ast2) fn1 ast1 = throwL (astPos ast1, argError1 name ast1) -builtinMultiply2 :: (String, AST) -builtinMultiply2 = (name, makeNonsenseAST $ ASTFunction Pure fn1) where +builtinNumMultiply2 :: (String, AST) +builtinNumMultiply2 = (name, makeNonsenseAST $ ASTFunction Pure fn1) where name = "*" fn1 ast1@AST { an = ASTInteger a } = return $ makeNonsenseAST $ ASTFunction Pure $ fn2 where @@ -125,8 +126,8 @@ builtinMultiply2 = (name, makeNonsenseAST $ ASTFunction Pure fn1) where fn2 ast2 = throwL (astPos ast2, argError2 name ast1 ast2) fn1 ast1 = throwL (astPos ast1, argError1 name ast1) -builtinDivide2 :: (String, AST) -builtinDivide2 = (name, makeNonsenseAST $ ASTFunction Pure fn1) where +builtinNumDivide2 :: (String, AST) +builtinNumDivide2 = (name, makeNonsenseAST $ ASTFunction Pure fn1) where name = "/" fn1 ast1@AST { an = ASTInteger a } = return $ makeNonsenseAST $ ASTFunction Pure $ fn2 where @@ -142,26 +143,42 @@ builtinDivide2 = (name, makeNonsenseAST $ ASTFunction Pure fn1) where fn2 ast2 = throwL (astPos ast2, argError2 name ast1 ast2) fn1 ast1 = throwL (astPos ast1, argError1 name ast1) -builtinEq2 :: (String, AST) -builtinEq2 = (name, makeNonsenseAST $ ASTFunction Pure fn1) where - name = "eq?" - fn1 ast1 = - return $ makeNonsenseAST $ ASTFunction Pure $ fn2 where - fn2 ast2 = return $ makeNonsenseAST $ ASTBoolean $ ast1 == ast2 - -builtinLt2 :: (String, AST) -builtinLt2 = (name, makeNonsenseAST $ ASTFunction Pure fn1) where +builtinNumLt2 :: (String, AST) +builtinNumLt2 = (name, makeNonsenseAST $ ASTFunction Pure fn1) where name = "lt?" - fn1 ast1 = + fn1 ast1@AST { an = ASTInteger a } = + return $ makeNonsenseAST $ ASTFunction Pure $ fn2 where + fn2 AST { an = ASTInteger b } = + return $ makeNonsenseAST $ ASTBoolean $ a < b + fn2 ast2 = throwL (astPos ast2, argError2 name ast1 ast2) + fn1 ast1@AST { an = ASTDouble a } = return $ makeNonsenseAST $ ASTFunction Pure $ fn2 where - fn2 ast2 = return $ makeNonsenseAST $ ASTBoolean $ ast1 < ast2 + fn2 AST { an = ASTDouble b } = + return $ makeNonsenseAST $ ASTBoolean $ a < b + fn2 ast2 = throwL (astPos ast2, argError2 name ast1 ast2) + fn1 ast1 = throwL (astPos ast1, argError1 name ast1) -builtinFloor :: (String, AST) -builtinFloor = (name, makeNonsenseAST $ ASTFunction Pure fn1) where +builtinNumFloor :: (String, AST) +builtinNumFloor = (name, makeNonsenseAST $ ASTFunction Pure fn1) where name = "floor" + fn1 ast1@AST { an = ASTInteger _ } = return ast1 fn1 AST { an = ASTDouble dbl } = return $ makeNonsenseAST $ ASTInteger $ floor dbl fn1 ast1 = throwL (astPos ast1, argError1 name ast1) +builtinNumCeil :: (String, AST) +builtinNumCeil = (name, makeNonsenseAST $ ASTFunction Pure fn1) where + name = "ceil" + fn1 ast1@AST { an = ASTInteger _ } = return ast1 + fn1 AST { an = ASTDouble dbl } = return $ makeNonsenseAST $ ASTInteger $ ceiling dbl + fn1 ast1 = throwL (astPos ast1, argError1 name ast1) + +builtinEq2 :: (String, AST) +builtinEq2 = (name, makeNonsenseAST $ ASTFunction Pure fn1) where + name = "eq?" + fn1 ast1 = + return $ makeNonsenseAST $ ASTFunction Pure $ fn2 where + fn2 ast2 = return $ makeNonsenseAST $ ASTBoolean $ ast1 == ast2 + builtinParseInt :: (String, AST) builtinParseInt = (name, makeNonsenseAST $ ASTFunction Pure fn1) where name = "parse-int" @@ -171,9 +188,18 @@ builtinParseInt = (name, makeNonsenseAST $ ASTFunction Pure fn1) where Nothing -> throwL (astPos ast1, argError1 name ast1) fn1 ast1 = throwL (astPos ast1, argError1 name ast1) -builtinToDouble :: (String, AST) -builtinToDouble = (name, makeNonsenseAST $ ASTFunction Pure fn1) where - name = "to-double" +builtinParseFloat :: (String, AST) +builtinParseFloat = (name, makeNonsenseAST $ ASTFunction Pure fn1) where + name = "parse-float" + fn1 ast1@AST { an = ASTString str } = + case (TR.readMaybe str) of + Just val -> return $ makeNonsenseAST $ ASTDouble $ val + Nothing -> throwL (astPos ast1, argError1 name ast1) + fn1 ast1 = throwL (astPos ast1, argError1 name ast1) + +builtinToFloat :: (String, AST) +builtinToFloat = (name, makeNonsenseAST $ ASTFunction Pure fn1) where + name = "to-float" fn1 AST { an = ASTInteger int } = return $ makeNonsenseAST $ ASTDouble $ fromIntegral int fn1 ast1 = throwL (astPos ast1, argError1 name ast1) @@ -196,79 +222,99 @@ builtinFmt = (name, makeNonsenseAST $ ASTFunction Pure fn1) where text' = T.replace (T.pack $ "{" ++ show n ++ "}") (T.pack $ xRepr) text in replaceAll (n + 1) xs text' -builtinHead :: (String, AST) -builtinHead = (name, makeNonsenseAST $ ASTFunction Pure fn1) where +builtinSeqHead :: (String, AST) +builtinSeqHead = (name, makeNonsenseAST $ ASTFunction Pure fn1) where name = "head" fn1 ast1@AST { an = ASTVector vec } = do when (length vec == 0) $ throwL (astPos ast1, name ++ " of empty vector") return $ head vec + fn1 ast1@AST { an = ASTString str } = + do when (length str == 0) $ throwL (astPos ast1, name ++ " of empty string") + return $ makeNonsenseAST $ ASTChar $ head str fn1 ast1 = throwL (astPos ast1, argError1 name ast1) -builtinTail :: (String, AST) -builtinTail = (name, makeNonsenseAST $ ASTFunction Pure fn1) where +builtinSeqTail :: (String, AST) +builtinSeqTail = (name, makeNonsenseAST $ ASTFunction Pure fn1) where name = "tail" fn1 ast1@AST { an = ASTVector vec } = do when (length vec == 0) $ throwL (astPos ast1, name ++ " of empty vector") return $ makeNonsenseAST $ ASTVector $ tail vec + fn1 ast1@AST { an = ASTString str } = + do when (length str == 0) $ throwL (astPos ast1, name ++ " of empty string") + return $ makeNonsenseAST $ ASTString $ tail str fn1 ast1 = throwL (astPos ast1, argError1 name ast1) -builtinSubstr :: (String, AST) -builtinSubstr = (name, makeNonsenseAST $ ASTFunction Pure fn1) where - name = "substr" +builtinSeqSlice :: (String, AST) +builtinSeqSlice = (name, makeNonsenseAST $ ASTFunction Pure fn1) where + name = "slice" fn1 ast1@AST { an = ASTInteger atInteger } = return $ makeNonsenseAST $ ASTFunction Pure $ fn2 where fn2 ast2@AST { an = ASTInteger lenInteger } = return $ makeNonsenseAST $ ASTFunction Pure $ fn3 where len = fromIntegral lenInteger :: Int at = fromIntegral atInteger :: Int + fn3 AST { an = ASTVector vec } = + return $ makeNonsenseAST $ ASTVector $ drop at .> take len $ vec fn3 AST { an = ASTString str } = return $ makeNonsenseAST $ ASTString $ drop at .> take len $ str fn3 ast3 = throwL (astPos ast3, argError3 name ast1 ast2 ast3) fn2 ast2 = throwL (astPos ast2, argError2 name ast1 ast2) fn1 ast1 = throwL (astPos ast1, argError1 name ast1) -builtinStrToVec :: (String, AST) -builtinStrToVec = (name, makeNonsenseAST $ ASTFunction Pure fn1) where - name = "to-vec" - fn1 AST { an = ASTString str } = - str $> map (\c -> [c]) - .> map (makeNonsenseAST . ASTString) - .> (makeNonsenseAST . ASTVector) - .> return - fn1 ast1 = throwL (astPos ast1, argError1 name ast1) - -builtinPrepend :: (String, AST) -builtinPrepend = (name, makeNonsenseAST $ ASTFunction Pure fn1) where - name = "prepend" +builtinSeqCons :: (String, AST) +builtinSeqCons = (name, makeNonsenseAST $ ASTFunction Pure fn1) where + name = "cons" + fn1 ast1@AST { an = ASTChar c } = + return $ makeNonsenseAST $ ASTFunction Pure $ fn2 where + fn2 AST { an = ASTString str } = + return $ makeNonsenseAST $ ASTString $ c : str + fn2 ast2 = throwL (astPos ast2, argError2 name ast1 ast2) fn1 ast1 = return $ makeNonsenseAST $ ASTFunction Pure $ fn2 where fn2 AST { an = ASTVector vec } = return $ makeNonsenseAST $ ASTVector $ ast1 : vec fn2 ast2 = throwL (astPos ast2, argError2 name ast1 ast2) -builtinPrint :: (String, AST) -builtinPrint = (name, makeNonsenseAST $ ASTFunction Impure fn1) where - name = "print!" - fn1 AST { an = ASTString str } = - do liftIO $ putStr $ str - return $ makeNonsenseAST ASTUnit - fn1 ast1 = throwL (astPos ast1, argError1 name ast1) - -builtinConcat :: (String, AST) -builtinConcat = (name, makeNonsenseAST $ ASTFunction Pure fn1) where +builtinSeqConcat :: (String, AST) +builtinSeqConcat = (name, makeNonsenseAST $ ASTFunction Pure fn1) where name = "concat" fn1 ast1@AST { an = ASTString str1 } = return $ makeNonsenseAST $ ASTFunction Pure $ fn2 where fn2 AST { an = ASTString str2 } = return $ makeNonsenseAST $ ASTString $ str1 ++ str2 fn2 ast2 = throwL (astPos ast2, argError2 name ast1 ast2) + fn1 ast1@AST { an = ASTVector vec1 } = + return $ makeNonsenseAST $ ASTFunction Pure $ fn2 where + fn2 AST { an = ASTVector vec2 } = + return $ makeNonsenseAST $ ASTVector $ vec1 ++ vec2 + fn2 ast2 = throwL (astPos ast2, argError2 name ast1 ast2) fn1 ast1 = throwL (astPos ast1, argError1 name ast1) -builtinLen :: (String, AST) -builtinLen = (name, makeNonsenseAST $ ASTFunction Pure fn1) where +builtinSeqLen :: (String, AST) +builtinSeqLen = (name, makeNonsenseAST $ ASTFunction Pure fn1) where name = "len" fn1 AST { an = ASTString str } = return $ makeNonsenseAST $ ASTInteger $ fromIntegral $ length str + fn1 AST { an = ASTVector vec } = + return $ makeNonsenseAST $ ASTInteger $ fromIntegral $ length vec + fn1 ast1 = throwL (astPos ast1, argError1 name ast1) + +builtinStrToVec :: (String, AST) +builtinStrToVec = (name, makeNonsenseAST $ ASTFunction Pure fn1) where + name = "to-vec" + fn1 AST { an = ASTString str } = + str $> map (\c -> [c]) + .> map (makeNonsenseAST . ASTString) + .> (makeNonsenseAST . ASTVector) + .> return + fn1 ast1 = throwL (astPos ast1, argError1 name ast1) + +builtinPrint :: (String, AST) +builtinPrint = (name, makeNonsenseAST $ ASTFunction Impure fn1) where + name = "print!" + fn1 AST { an = ASTString str } = + do liftIO $ putStr $ str + return $ makeNonsenseAST ASTUnit fn1 ast1 = throwL (astPos ast1, argError1 name ast1) builtinFatal :: (String, AST) @@ -286,6 +332,56 @@ builtinKind = (name, makeNonsenseAST $ ASTFunction Pure fn1) where return $ makeNonsenseAST $ ASTTag tagHash identifier fn1 ast1 = throwL (astPos ast1, argError1 name ast1) +builtinType :: (String, AST) +builtinType = (name, makeNonsenseAST $ ASTFunction Pure fn1) where + name = "type" + mkTag s = (s, s) $> B.first computeTagN .> uncurry ASTTag + tagInteger = mkTag "integer" + tagDouble = mkTag "double" + tagSymbol = mkTag "symbol" + tagBoolean = mkTag "boolean" + tagChar = mkTag "char" + tagString = mkTag "string" + tagTag = mkTag "tag" + tagVector = mkTag "vector" + tagFnCall = mkTag "function-call" + tagHashMap = mkTag "hash-map" + tagFunction = mkTag "function" + tagRecord = mkTag "record" + tagAtom = mkTag "atom" + tagUnit = mkTag "unit" + tagHole = mkTag "hole" + fn1 AST { an = ASTInteger _ } = + return $ makeNonsenseAST $ tagInteger + fn1 AST { an = ASTDouble _ } = + return $ makeNonsenseAST $ tagDouble + fn1 AST { an = ASTSymbol _ } = + return $ makeNonsenseAST $ tagSymbol + fn1 AST { an = ASTBoolean _ } = + return $ makeNonsenseAST $ tagBoolean + fn1 AST { an = ASTChar _ } = + return $ makeNonsenseAST $ tagChar + fn1 AST { an = ASTString _ } = + return $ makeNonsenseAST $ tagString + fn1 AST { an = ASTTag _ _ } = + return $ makeNonsenseAST $ tagTag + fn1 AST { an = ASTVector _ } = + return $ makeNonsenseAST $ tagVector + fn1 AST { an = ASTFunctionCall _ } = + return $ makeNonsenseAST $ tagFnCall + fn1 AST { an = ASTHashMap _ } = + return $ makeNonsenseAST $ tagHashMap + fn1 AST { an = ASTFunction _ _ } = + return $ makeNonsenseAST $ tagFunction + fn1 AST { an = ASTRecord _ _ _} = + return $ makeNonsenseAST $ tagRecord + fn1 AST { an = ASTAtom _ } = + return $ makeNonsenseAST $ tagAtom + fn1 AST { an = ASTUnit } = + return $ makeNonsenseAST $ tagUnit + fn1 AST { an = ASTHole } = + return $ makeNonsenseAST $ tagHole + builtinReadFile :: (String, AST) builtinReadFile = (name, makeNonsenseAST $ ASTFunction Impure fn1) where name = "read-file!" diff --git a/test/Spec.hs b/test/Spec.hs index 68e575c..f85ecac 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -32,7 +32,7 @@ parseTests = testGroup "parse" [ ] evaluateTests = testGroup "evaluate" [ - do let env = makeEnv [builtinAdd2] + do let env = makeEnv [builtinNumAdd2] (gotAST, LState { stateEnv = gotEnv }) <- expectSuccessL env $ evaluate (astFunctionCall [astSymbol "+", astInteger 1, astInteger 2]) @@ -42,7 +42,7 @@ evaluateTests = testGroup "evaluate" [ ] e2eTests = testGroup "e2e" [ - do let env = makeEnv [builtinSubtract2] + do let env = makeEnv [builtinNumSubtract2] let script1 = "(let sub2 (\\[a b] (- a b)))\n(sub2 3 2)" (gotASTs, LState { stateEnv = gotEnv }) <- expectSuccessL env $ runInlineScript "<test>" script1 @@ -77,14 +77,14 @@ e2eTests = testGroup "e2e" [ astVector [astInteger 3, astInteger 2]] assertEqual "" expectedLastAST (last gotASTs), - do let env = makeEnv [builtinAdd2, builtinMultiply2] + do let env = makeEnv [builtinNumAdd2, builtinNumMultiply2] let script1 = "(let f (\\[x] (let y (+ x 1)) (let z (+ 3 y)) (* 2 z)))\n(f 1)" (gotASTs, _) <- expectSuccessL env $ runInlineScript "<test>" script1 let expectedLastAST = astInteger 10 assertEqual "" expectedLastAST (last gotASTs), - do let env = makeEnv [builtinAdd2, builtinSubtract2, ("_", astHole)] + do let env = makeEnv [builtinNumAdd2, builtinNumSubtract2, ("_", astHole)] let script1 = "(let memo fibo (\\[n]\ \ (match n\ \ 0 0\ |
