Skip to content

Commit

Permalink
ENH Add read_int and read_double functions
Browse files Browse the repository at this point in the history
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
  • Loading branch information
luispedro committed Dec 4, 2020
1 parent d3fe910 commit 7a8d6f7
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 4 deletions.
1 change: 1 addition & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Version 1.2.0+
* Use ZStd compression for temporary files from preprocess()
* Correctly handle subpaths in samples for collect (fixes #141)
* Add to_string() to int and double types (partially fixes #78 & fixes #85)
* Add read_int() and read_double() functions (fixes #78)

Version 1.2.0 2020-07-12 by luispedro
* Add load_fastq_directory to builtin functions
Expand Down
6 changes: 6 additions & 0 deletions NGLess/BuiltinFunctions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,14 @@ builtinFunctions =
,Function (FuncName "__check_count") (Just NGLMappedReadSet) [] NGLCounts countCheckArgs False []
,Function (FuncName "countfile") (Just NGLString) [ArgCheckFileReadable] NGLCounts [] False [FunctionCheckReturnAssigned]
,Function (FuncName "write") (Just NGLAny) [] NGLVoid writeArgs False []

,Function (FuncName "print") (Just NGLAny) [] NGLVoid [] False []

,Function (FuncName "read_int") (Just NGLString) [] NGLInteger [ArgInformation "on_empty_return" False NGLInteger []] False []
,Function (FuncName "read_double") (Just NGLString) [] NGLDouble [ArgInformation "on_empty_return" False NGLDouble []] False []

,Function (FuncName "__assert") (Just NGLBool) [] NGLVoid [] False []

,Function (FuncName "__merge_samfiles") (Just (NGList NGLString)) [] NGLMappedReadSet [] False []
]

Expand Down
24 changes: 24 additions & 0 deletions NGLess/Interpret.hs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ import Control.DeepSeq (NFData(..))
import qualified Data.Vector as V
import Safe (atMay)
import Control.Error (note)
import Text.Read (readEither)

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

executeReadInt :: NGLessObject -> [(T.Text, NGLessObject)] -> InterpretationEnvIO NGLessObject
executeReadInt (NGOString "") kwargs = NGOInteger <$> lookupIntegerOrScriptError "read_int" "on_empty_return" kwargs
executeReadInt (NGOString s) _ = case readEither (T.unpack s) of
Right val -> return $! NGOInteger val
Left err -> throwDataError ("Could not parse integer from '"++T.unpack s++"'. Error: "++err)
executeReadInt s _ = throwScriptError ("Cannot parse this object as integer: "++ show s)

executeReadDouble :: NGLessObject -> [(T.Text, NGLessObject)] -> InterpretationEnvIO NGLessObject
executeReadDouble (NGOString "") kwargs = NGODouble <$> lookupDoubleOrScriptError "read_int" "on_empty_return" kwargs
executeReadDouble (NGOString s) _ = case readEither (T.unpack s) of
Right val -> return $! NGODouble val
Left err -> throwDataError ("Could not parse double from '"++T.unpack s++"'. Error: "++err)
executeReadDouble s _ = throwScriptError ("Cannot parse this object as double: "++ show s)

executeAssert (NGOBool True) _ = return $! NGOVoid
executeAssert (NGOBool False) _ = throwScriptError "Assert failed"
executeAssert _ _ = throwShouldNotOccur "Assert did not receive a boolean!"

executeSelectWBlock :: NGLessObject -> [(T.Text, NGLessObject)] -> Block -> InterpretationEnvIO NGLessObject
executeSelectWBlock input@NGOMappedReadSet{ nglSamFile = isam} args (Block (Variable var) body) = do
paired <- lookupBoolOrScriptErrorDef (return True) "select" "paired" args
Expand Down Expand Up @@ -727,6 +749,8 @@ evalBinary BOpPathAppend a b = case (a,b) of
(NGOString pa, NGOString pb) -> return . NGOString $! T.pack (T.unpack pa </> T.unpack pb)
_ -> nglTypeError ("Operator </>: invalid arguments" :: String)

evalBinary BOpEQ (NGOString a) (NGOString b) = return . NGOBool $! a == b
evalBinary BOpNEQ (NGOString a) (NGOString b) = return . NGOBool $! a /= b
evalBinary op a b = do
a' <- asDouble a
b' <- asDouble b
Expand Down
12 changes: 12 additions & 0 deletions NGLess/NGLess.hs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
{- Copyright 2013-2020 NGLess Authors
- License: MIT
-}
{-# LANGUAGE OverloadedStrings, FlexibleContexts #-}
module NGLess
( NGLessIO
Expand All @@ -23,6 +26,8 @@ module NGLess
, lookupStringListOrScriptErrorDef
, lookupIntegerOrScriptError
, lookupIntegerOrScriptErrorDef
, lookupDoubleOrScriptError
, lookupDoubleOrScriptErrorDef
, lookupSymbolOrScriptError
, lookupSymbolOrScriptErrorDef
, lookupSymbolListOrScriptError
Expand Down Expand Up @@ -93,6 +98,13 @@ lookupIntegerOrScriptErrorDef defval context name args = case lookup name args o
Just (NGOInteger v) -> return v
Just other -> throwScriptError ("Expected an integer in argument " ++ T.unpack name ++ " in context '" ++ context ++ "' instead observed: " ++ show other)

lookupDoubleOrScriptError :: (MonadError NGError m) => String-> T.Text -> KwArgsValues -> m Double
lookupDoubleOrScriptError = requiredLookup lookupDoubleOrScriptErrorDef
lookupDoubleOrScriptErrorDef defval context name args = case lookup name args of
Nothing -> defval
Just (NGODouble v) -> return v
Just other -> throwScriptError ("Expected a double in argument " ++ T.unpack name ++ " in context '" ++ context ++ "' instead observed: " ++ show other)

lookupSymbolOrScriptError :: (MonadError NGError m) => String-> T.Text -> KwArgsValues -> m T.Text
lookupSymbolOrScriptError = requiredLookup lookupSymbolOrScriptErrorDef
lookupSymbolOrScriptErrorDef defval context name args = case lookup name args of
Expand Down
9 changes: 8 additions & 1 deletion NGLess/Transform.hs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ checkSimple expr = forM_ expr (checkSimple1 . snd) *> return expr
Condition{} -> throwShouldNotOccur "Non-simple expression (Condition)"
Assignment{} -> throwShouldNotOccur "Non-simple expression (Assignment)"
FunctionCall{} -> throwShouldNotOccur "Non-simple expression (FunctionCall)"
MethodCall{} -> throwShouldNotOccur "Non-simple expression (MethodCall)"
-- Rewriting for MethodCall is not implemented!
MethodCall{} -> return () -- throwShouldNotOccur "Non-simple expression (MethodCall)"

ListExpression s -> mapM_ checkSimple0 s
UnaryOp _ a -> checkSimple0 a
Expand Down Expand Up @@ -522,6 +523,12 @@ addTemporaries = addTemporaries' 0
put (n + 1)
tell [Assignment v e]
return (Lookup t v)
functionCallTemp e@BinaryOp{} = do
n <- get
let v = Variable (T.pack $ "temp$"++show n)
put (n + 1)
tell [Assignment v e]
return (Lookup Nothing v)
functionCallTemp e = return e

{-| Calculation of hashes for output method calls
Expand Down
13 changes: 10 additions & 3 deletions NGLess/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -204,10 +204,17 @@ checkbop BOpGT a b = checknum a *> checknum b *> return (Just NGLBool)
checkbop BOpGTE a b = checknum a *> checknum b *> return (Just NGLBool)
checkbop BOpLT a b = checknum a *> checknum b *> return (Just NGLBool)
checkbop BOpLTE a b = checknum a *> checknum b *> return (Just NGLBool)
checkbop BOpEQ a b = checknum a *> checknum b *> return (Just NGLBool)
checkbop BOpNEQ a b = checknum a *> checknum b *> return (Just NGLBool)

checkbop BOpPathAppend a b = softCheck NGLString a *> softCheck NGLString b *> return (Just NGLString)
checkbop BOpNEQ a b = checkbop BOpEQ a b
checkbop BOpEQ a b = do
t <- liftM3 (\a b c -> a <|> b <|> c)
(softCheckPair NGLInteger a b)
(softCheckPair NGLDouble a b)
(softCheckPair NGLString a b)
when (isNothing t) $
errorInLineC ["Comparison operators (== or !=) must be applied to a pair of strings or numbers"]
return (Just NGLBool)


softCheck :: NGLType -> Expression -> TypeMSt (Maybe NGLType)
softCheck expected expr = do
Expand Down
6 changes: 6 additions & 0 deletions docs/sources/whatsnew.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,16 @@ What's New (History)
Unreleased development version
------------------------------

User-visible improvements
~~~~~~~~~~~~~~~~~~~~~~~~~

- Adds conversion from string to numbers (int or double) and back

Bugfixes
~~~~~~~~
- Fix cases where sample names contain ``/`` and ``collect()`` (`issue 141
<https://github.com/ngless-toolkit/ngless/issues/141>`__)



Version 1.2.0
Expand Down
16 changes: 16 additions & 0 deletions tests/type-conversions/types.ngl
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
ngless "1.2"

val = 1
__assert(val.to_string() == "1")

vald = 1.5
__assert(vald.to_string() == "1.5")

s = "2"
__assert(read_int(s) == 2)
__assert(read_int("", on_empty_return=3) == 3)

s = "2.2"
__assert(read_double(s) == 2.2)
__assert(read_double("", on_empty_return=3.8) == 3.8)

0 comments on commit 7a8d6f7

Please sign in to comment.