Skip to content

Commit b626af8

Browse files
committed
ENH Add read_int and read_double functions
These are very simple. The choice of API was to allow for a default on empty, but to error on anything else that cannot be parsed This patch also adds the ``__assert`` function for internal use and generalizes some of the transformation mechanics closes #78
1 parent e29a218 commit b626af8

File tree

8 files changed

+83
-4
lines changed

8 files changed

+83
-4
lines changed

ChangeLog

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Version 1.2.0+
33
* Use ZStd compression for temporary files from preprocess()
44
* Correctly handle subpaths in samples for collect (fixes #141)
55
* Add to_string() to int and double types (partially fixes #78 & fixes #81)
6+
* Add read_int() and read_double() functions (fixes #78)
67

78
Version 1.2.0 2020-07-12 by luispedro
89
* Add load_fastq_directory to builtin functions

NGLess/BuiltinFunctions.hs

+6
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,14 @@ builtinFunctions =
4343
,Function (FuncName "__check_count") (Just NGLMappedReadSet) [] NGLCounts countCheckArgs False []
4444
,Function (FuncName "countfile") (Just NGLString) [ArgCheckFileReadable] NGLCounts [] False [FunctionCheckReturnAssigned]
4545
,Function (FuncName "write") (Just NGLAny) [] NGLVoid writeArgs False []
46+
4647
,Function (FuncName "print") (Just NGLAny) [] NGLVoid [] False []
4748

49+
,Function (FuncName "read_int") (Just NGLString) [] NGLInteger [ArgInformation "on_empty_return" False NGLInteger []] False []
50+
,Function (FuncName "read_double") (Just NGLString) [] NGLDouble [ArgInformation "on_empty_return" False NGLDouble []] False []
51+
52+
,Function (FuncName "__assert") (Just NGLBool) [] NGLVoid [] False []
53+
4854
,Function (FuncName "__merge_samfiles") (Just (NGList NGLString)) [] NGLMappedReadSet [] False []
4955
]
5056

NGLess/Interpret.hs

+24
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ import Control.DeepSeq (NFData(..))
8080
import qualified Data.Vector as V
8181
import Safe (atMay)
8282
import Control.Error (note)
83+
import Text.Read (readEither)
8384

8485
import System.IO
8586
import System.Directory
@@ -335,8 +336,11 @@ interpretFunction' (FuncName "count") expr args Nothing = runNGLessIO (execu
335336
interpretFunction' (FuncName "__check_count") expr args Nothing = runNGLessIO (executeCountCheck expr args)
336337
interpretFunction' (FuncName "countfile") expr args Nothing = runNGLessIO (executeCountFile expr args)
337338
interpretFunction' (FuncName "print") expr args Nothing = executePrint expr args
339+
interpretFunction' (FuncName "read_int") expr args Nothing = executeReadInt expr args
340+
interpretFunction' (FuncName "read_double") expr args Nothing = executeReadDouble expr args
338341
interpretFunction' (FuncName "paired") mate1 args Nothing = runNGLessIO (executePaired mate1 args)
339342
interpretFunction' (FuncName "select") expr args (Just b) = executeSelectWBlock expr args b
343+
interpretFunction' (FuncName "__assert") expr [] args = executeAssert expr args
340344
interpretFunction' fname@(FuncName fname') expr args Nothing = do
341345
traceExpr ("executing module function: '"++T.unpack fname'++"'") expr
342346
execF <- findFunction fname
@@ -536,6 +540,24 @@ executePrint :: NGLessObject -> [(T.Text, NGLessObject)] -> InterpretationEnvIO
536540
executePrint (NGOString s) [] = liftIO (T.putStr s) >> return NGOVoid
537541
executePrint err _ = throwScriptError ("Cannot print " ++ show err)
538542

543+
executeReadInt :: NGLessObject -> [(T.Text, NGLessObject)] -> InterpretationEnvIO NGLessObject
544+
executeReadInt (NGOString "") kwargs = NGOInteger <$> lookupIntegerOrScriptError "read_int" "on_empty_return" kwargs
545+
executeReadInt (NGOString s) _ = case readEither (T.unpack s) of
546+
Right val -> return $! NGOInteger val
547+
Left err -> throwDataError ("Could not parse integer from '"++T.unpack s++"'. Error: "++err)
548+
executeReadInt s _ = throwScriptError ("Cannot parse this object as integer: "++ show s)
549+
550+
executeReadDouble :: NGLessObject -> [(T.Text, NGLessObject)] -> InterpretationEnvIO NGLessObject
551+
executeReadDouble (NGOString "") kwargs = NGODouble <$> lookupDoubleOrScriptError "read_int" "on_empty_return" kwargs
552+
executeReadDouble (NGOString s) _ = case readEither (T.unpack s) of
553+
Right val -> return $! NGODouble val
554+
Left err -> throwDataError ("Could not parse double from '"++T.unpack s++"'. Error: "++err)
555+
executeReadDouble s _ = throwScriptError ("Cannot parse this object as double: "++ show s)
556+
557+
executeAssert (NGOBool True) _ = return $! NGOVoid
558+
executeAssert (NGOBool False) _ = throwScriptError "Assert failed"
559+
executeAssert _ _ = throwShouldNotOccur "Assert did not receive a boolean!"
560+
539561
executeSelectWBlock :: NGLessObject -> [(T.Text, NGLessObject)] -> Block -> InterpretationEnvIO NGLessObject
540562
executeSelectWBlock input@NGOMappedReadSet{ nglSamFile = isam} args (Block (Variable var) body) = do
541563
paired <- lookupBoolOrScriptErrorDef (return True) "select" "paired" args
@@ -727,6 +749,8 @@ evalBinary BOpPathAppend a b = case (a,b) of
727749
(NGOString pa, NGOString pb) -> return . NGOString $! T.pack (T.unpack pa </> T.unpack pb)
728750
_ -> nglTypeError ("Operator </>: invalid arguments" :: String)
729751

752+
evalBinary BOpEQ (NGOString a) (NGOString b) = return . NGOBool $! a == b
753+
evalBinary BOpNEQ (NGOString a) (NGOString b) = return . NGOBool $! a /= b
730754
evalBinary op a b = do
731755
a' <- asDouble a
732756
b' <- asDouble b

NGLess/NGLess.hs

+12
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
{- Copyright 2013-2020 NGLess Authors
2+
- License: MIT
3+
-}
14
{-# LANGUAGE OverloadedStrings, FlexibleContexts #-}
25
module NGLess
36
( NGLessIO
@@ -23,6 +26,8 @@ module NGLess
2326
, lookupStringListOrScriptErrorDef
2427
, lookupIntegerOrScriptError
2528
, lookupIntegerOrScriptErrorDef
29+
, lookupDoubleOrScriptError
30+
, lookupDoubleOrScriptErrorDef
2631
, lookupSymbolOrScriptError
2732
, lookupSymbolOrScriptErrorDef
2833
, lookupSymbolListOrScriptError
@@ -93,6 +98,13 @@ lookupIntegerOrScriptErrorDef defval context name args = case lookup name args o
9398
Just (NGOInteger v) -> return v
9499
Just other -> throwScriptError ("Expected an integer in argument " ++ T.unpack name ++ " in context '" ++ context ++ "' instead observed: " ++ show other)
95100

101+
lookupDoubleOrScriptError :: (MonadError NGError m) => String-> T.Text -> KwArgsValues -> m Double
102+
lookupDoubleOrScriptError = requiredLookup lookupDoubleOrScriptErrorDef
103+
lookupDoubleOrScriptErrorDef defval context name args = case lookup name args of
104+
Nothing -> defval
105+
Just (NGODouble v) -> return v
106+
Just other -> throwScriptError ("Expected a double in argument " ++ T.unpack name ++ " in context '" ++ context ++ "' instead observed: " ++ show other)
107+
96108
lookupSymbolOrScriptError :: (MonadError NGError m) => String-> T.Text -> KwArgsValues -> m T.Text
97109
lookupSymbolOrScriptError = requiredLookup lookupSymbolOrScriptErrorDef
98110
lookupSymbolOrScriptErrorDef defval context name args = case lookup name args of

NGLess/Transform.hs

+8-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ checkSimple expr = forM_ expr (checkSimple1 . snd) *> return expr
9090
Condition{} -> throwShouldNotOccur "Non-simple expression (Condition)"
9191
Assignment{} -> throwShouldNotOccur "Non-simple expression (Assignment)"
9292
FunctionCall{} -> throwShouldNotOccur "Non-simple expression (FunctionCall)"
93-
MethodCall{} -> throwShouldNotOccur "Non-simple expression (MethodCall)"
93+
-- Rewriting for MethodCall is not implemented!
94+
MethodCall{} -> return () -- throwShouldNotOccur "Non-simple expression (MethodCall)"
9495

9596
ListExpression s -> mapM_ checkSimple0 s
9697
UnaryOp _ a -> checkSimple0 a
@@ -522,6 +523,12 @@ addTemporaries = addTemporaries' 0
522523
put (n + 1)
523524
tell [Assignment v e]
524525
return (Lookup t v)
526+
functionCallTemp e@BinaryOp{} = do
527+
n <- get
528+
let v = Variable (T.pack $ "temp$"++show n)
529+
put (n + 1)
530+
tell [Assignment v e]
531+
return (Lookup Nothing v)
525532
functionCallTemp e = return e
526533

527534
{-| Calculation of hashes for output method calls

NGLess/Types.hs

+10-3
Original file line numberDiff line numberDiff line change
@@ -204,10 +204,17 @@ checkbop BOpGT a b = checknum a *> checknum b *> return (Just NGLBool)
204204
checkbop BOpGTE a b = checknum a *> checknum b *> return (Just NGLBool)
205205
checkbop BOpLT a b = checknum a *> checknum b *> return (Just NGLBool)
206206
checkbop BOpLTE a b = checknum a *> checknum b *> return (Just NGLBool)
207-
checkbop BOpEQ a b = checknum a *> checknum b *> return (Just NGLBool)
208-
checkbop BOpNEQ a b = checknum a *> checknum b *> return (Just NGLBool)
209-
210207
checkbop BOpPathAppend a b = softCheck NGLString a *> softCheck NGLString b *> return (Just NGLString)
208+
checkbop BOpNEQ a b = checkbop BOpEQ a b
209+
checkbop BOpEQ a b = do
210+
t <- liftM3 (\a b c -> a <|> b <|> c)
211+
(softCheckPair NGLInteger a b)
212+
(softCheckPair NGLDouble a b)
213+
(softCheckPair NGLString a b)
214+
when (isNothing t) $
215+
errorInLineC ["Comparison operators (== or !=) must be applied to a pair of strings or numbers"]
216+
return (Just NGLBool)
217+
211218

212219
softCheck :: NGLType -> Expression -> TypeMSt (Maybe NGLType)
213220
softCheck expected expr = do

docs/sources/whatsnew.rst

+6
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,16 @@ What's New (History)
66
Unreleased development version
77
------------------------------
88

9+
User-visible improvements
10+
~~~~~~~~~~~~~~~~~~~~~~~~~
11+
12+
- Adds conversion from string to numbers (int or double) and back
13+
914
Bugfixes
1015
~~~~~~~~
1116
- Fix cases where sample names contain ``/`` and ``collect()`` (`issue 141
1217
<https://github.com/ngless-toolkit/ngless/issues/141>`__)
18+
1319

1420

1521
Version 1.2.0

tests/type-conversions/types.ngl

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
ngless "1.2"
2+
3+
val = 1
4+
__assert(val.to_string() == "1")
5+
6+
vald = 1.5
7+
__assert(vald.to_string() == "1.5")
8+
9+
s = "2"
10+
__assert(read_int(s) == 2)
11+
__assert(read_int("", on_empty_return=3) == 3)
12+
13+
s = "2.2"
14+
__assert(read_double(s) == 2.2)
15+
__assert(read_double("", on_empty_return=3.8) == 3.8)
16+

0 commit comments

Comments
 (0)