Skip to content

Commit 33dd87f

Browse files
authored
Unary operators (#51)
* Unary ops & some initial error reworking. * Add unary negate to prelude * Prelude tidy
1 parent af9d9f9 commit 33dd87f

File tree

11 files changed

+206
-33
lines changed

11 files changed

+206
-33
lines changed

lib/prelude.eu

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -164,37 +164,45 @@ lookup-alts(syms, d, b): foldr(lookup-or-in(b), d, syms)
164164
## Boolean
165165
##
166166

167-
` { doc: "`not(b) - toggle boolean.`"
167+
` { doc: "`not(b) - toggle boolean."
168168
export: :suppress }
169169
not: __NOT
170170

171-
` { doc: "`l && r`` - true if and only if `l` and `r` are true."
171+
` { doc: "`!x` - not x, toggle boolean."
172+
precedence: :bool-unary }
173+
(! b): b not
174+
175+
` { doc: "`¬x` - not x, toggle boolean."
176+
precedence: :bool-unary }
177+
(¬ b): b not
178+
179+
` { doc: "`l && r` - true if and only if `l` and `r` are true."
172180
export: :suppress
173181
associates: :left
174182
precedence: :bool-prod }
175183
(l && r): __AND(l, r)
176184

177-
` { doc: "`and(l, r)`` - true if and only if `l` and `r` are true."
185+
` { doc: "`and(l, r)` - true if and only if `l` and `r` are true."
178186
export: :suppress }
179187
and: __AND
180188

181-
` { doc: "`l ∧ r`` - true if and only if `l` and `r`"
189+
` { doc: "`l ∧ r` - true if and only if `l` and `r`"
182190
export: :suppress
183191
associates: :left
184192
precedence: :bool-prod }
185193
(l ∧ r): l && r
186194

187-
` { doc: "`or(l, r)`` - true if and only if `l` or `r` is true."
195+
` { doc: "`or(l, r)` - true if and only if `l` or `r` is true."
188196
export: :suppress }
189197
or: __OR
190198

191-
` { doc: "`l || r`` - true if and only if `l` or `r`"
199+
` { doc: "`l || r` - true if and only if `l` or `r`"
192200
export: :suppress
193201
associates: :left
194202
precedence: :bool-sum }
195203
(l || r): __OR(l, r)
196204

197-
` { doc: "`l ∨ r`` - true if and only if `l` or `r`"
205+
` { doc: "`l ∨ r` - true if and only if `l` or `r`"
198206
export: :suppress
199207
associates: :left
200208
precedence: :bool-sum }
@@ -444,13 +452,10 @@ nth(n, l): l drop(n dec) head
444452
` { doc: "`l !! n` - return `n`th item of list if it exists, otherwise error."}
445453
(l !! n): l nth(n)
446454

447-
# TODO: Can't use this until lists are exposed to eu as cons is not
448-
# currently lazy enough
449455
` { doc: "`repeat(i)` - return infinite list of instances of item `i`."
450456
export: :suppress }
451457
repeat(i): __CONS(i, repeat(i))
452458

453-
# TODO: sections
454459
` { doc: "`foldl(op, i, l)` - left fold operator `op` over list `l` starting from value `i` "
455460
# example: foldl(+, i, [1,2,3]) //=> (((i + 1) + 2) + 3)
456461
export: :suppress }
@@ -470,9 +475,7 @@ _impl: {
470475
export: :suppress }
471476
count(l): foldl(_impl.const-apply2(inc), 0, l)
472477

473-
# TODO: lambdas
474478
` { doc: "`map(f, l)` - map function `f` over list `l`"
475-
# example: map(|x|(x+2),[1,2,3]) //=> [3,4,5]
476479
export: :suppress }
477480
map(f, l): if(l nil?, l, cons(l head f, l tail map(f)))
478481

src/Eucalypt/Core/Desugar.hs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,15 @@ translateDeclarationForm a _k Located {locatee = form} =
157157
(PropertyDecl _ expr) -> varifyTranslate expr
158158
(FunctionDecl _ as expr) -> lam as <$> varifyTranslate expr
159159
(OperatorDecl _ l r expr) -> newOp l r <$> varifyTranslate expr
160+
(LeftOperatorDecl _ x expr) -> newUnOp UnaryPrefix x <$> varifyTranslate expr
161+
(RightOperatorDecl _ x expr) -> newUnOp UnaryPostfix x <$> varifyTranslate expr
160162
where
161163
newOp l r expr =
162164
let (fixity, precedence) = determineFixity a
163165
in CoreOperator fixity precedence $ lam [l, r] expr
166+
newUnOp fixity x expr =
167+
let precedence = determinePrecedence a
168+
in CoreOperator fixity precedence $ lam [x] expr
164169
varifyTranslate = translate >=> return . varify
165170

166171

@@ -206,6 +211,8 @@ translateBlock blk = do
206211
(PropertyDecl k _) -> k
207212
(FunctionDecl k _ _) -> k
208213
(OperatorDecl k _ _ _) -> k
214+
(LeftOperatorDecl k _ _) -> k
215+
(RightOperatorDecl k _ _) -> k
209216
in atomicName name
210217
bindings =
211218
map

src/Eucalypt/Core/Metadata.hs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ precedenceClasses :: [(String, Precedence)]
8282
precedenceClasses =
8383
[ ("lookup", 100)
8484
, ("call", 90)
85+
, ("bool-unary", 88)
8586
, ("exp", 85)
8687
, ("prod", 80)
8788
, ("sum", 75)
@@ -113,6 +114,15 @@ determineFixity (Just meta) = (fixity, fromMaybe 50 prec)
113114
_ -> 50
114115
determineFixity Nothing = (InfixLeft, 50)
115116

117+
-- | Determine precedence when fixity is already known (i.e. unary).
118+
determinePrecedence :: Maybe CoreExpr -> Precedence
119+
determinePrecedence (Just meta) =
120+
fromMaybe 50 $
121+
readUnevaluatedMetadata "precedence" meta $ \case
122+
(CorePrim (CoreInt n)) -> fromInteger n
123+
(CorePrim (CoreSymbol cls)) -> (fromMaybe 50 (lookup cls precedenceClasses))
124+
_ -> 50
125+
determinePrecedence _ = 50
116126

117127
-- | Check (unevaluated) metadata for target annotations and their
118128
-- documentation

src/Eucalypt/Driver/Evaluator.hs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ import Eucalypt.Driver.Lib (getResource)
4646
import Eucalypt.Driver.Options (Command(..), EucalyptOptions(..))
4747
import qualified Eucalypt.Driver.Stg as STG
4848
import Eucalypt.Reporting.Error (EucalyptError(..))
49-
import Eucalypt.Reporting.Report (reportErrors)
49+
import Eucalypt.Reporting.Report (reportErrors, tryOrReport)
5050
import Eucalypt.Source.Error (DataParseException(..))
5151
import Eucalypt.Source.TomlSource
5252
import Eucalypt.Source.YamlSource
@@ -183,8 +183,9 @@ parseUnits :: (Traversable t, Foldable t) => t Input -> IO [TranslationUnit]
183183
parseUnits inputs = do
184184
asts <- traverse parseInputToCore inputs
185185
case partitionEithers (toList asts) of
186-
(errs@(_:_), _) -> reportErrors errs >> exitFailure
187-
([], []) -> reportErrors [NoSource] >> exitFailure
186+
-- TODO: propagate all errors
187+
(e:_, _) -> throwM e
188+
([], []) -> throwM NoSource
188189
([], units) -> return units
189190

190191

@@ -274,7 +275,7 @@ parseInputsAndImports inputs = do
274275

275276
-- | Implement the Evaluate command, read files and render
276277
evaluate :: EucalyptOptions -> IO ExitCode
277-
evaluate opts = do
278+
evaluate opts = tryOrReport $ do
278279
when (cmd == Parse) (parseAndDumpASTs opts >> exitSuccess)
279280

280281
-- Stage 1: parse inputs and translate to core units

src/Eucalypt/Reporting/Classes.hs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{-|
2+
Module : Eucalypt.Reporting.Classes
3+
Description : Type classes to implement for error reporting
4+
Copyright : (c) Greg Hawkins, 2018
5+
License :
6+
Maintainer : greg@curvelogic.co.uk
7+
Stability : experimental
8+
-}
9+
module Eucalypt.Reporting.Classes where
10+
11+
import Eucalypt.Reporting.Location as L
12+
import Text.PrettyPrint as P
13+
14+
class Reportable a where
15+
-- | Location in SourceCode
16+
code :: a -> Maybe L.SourceSpan
17+
-- | Formatted error report
18+
report :: a -> P.Doc

src/Eucalypt/Reporting/Error.hs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
1+
{-|
2+
Module : Eucalypt.Syntax.Error
3+
Description : Aggregated error type for all types of errors
4+
Copyright : (c) Greg Hawkins, 2018
5+
License :
6+
Maintainer : greg@curvelogic.co.uk
7+
Stability : experimental
8+
-}
19
module Eucalypt.Reporting.Error
210
where
311

12+
import Control.Exception (SomeException)
13+
import Control.Exception.Safe
414
import qualified Eucalypt.Core.Error as Core
15+
import qualified Eucalypt.Driver.Error as Driver
16+
import Eucalypt.Reporting.Classes
517
import qualified Eucalypt.Source.Error as Source
618
import qualified Eucalypt.Syntax.Error as Syntax
7-
import qualified Eucalypt.Driver.Error as Driver
8-
import Control.Exception (SomeException)
9-
19+
import qualified Text.PrettyPrint as P
1020

1121
-- | All the types of error that Eucalypt can experience and report
1222
data EucalyptError
@@ -15,4 +25,13 @@ data EucalyptError
1525
| Syntax Syntax.SyntaxError
1626
| System SomeException
1727
| Command Driver.CommandError
18-
deriving (Show)
28+
deriving (Show, Typeable)
29+
30+
instance Exception EucalyptError
31+
32+
instance Reportable EucalyptError where
33+
code (Syntax e) = code e
34+
code _ = Nothing
35+
36+
report (Syntax e) = report e
37+
report e = P.text $ show e

src/Eucalypt/Reporting/Report.hs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,40 @@
1+
{-|
2+
Module : Eucalypt.Reporting.Report
3+
Description : Facilities for reporting errors
4+
Copyright : (c) Greg Hawkins, 2018
5+
License :
6+
Maintainer : greg@curvelogic.co.uk
7+
Stability : experimental
8+
-}
19
module Eucalypt.Reporting.Report where
210

3-
import System.IO (hPrint, stderr)
11+
import Eucalypt.Reporting.Error
12+
import Eucalypt.Reporting.Classes
13+
import System.Exit
14+
import System.IO
15+
import qualified Text.PrettyPrint as P
16+
import Control.Exception.Safe
417

518
-- | Report any errors to stderr
619
reportErrors :: Show a => [a] -> IO ()
720
reportErrors = mapM_ (hPrint stderr)
21+
22+
23+
24+
-- | Report an error to the console
25+
reportToConsole :: Reportable a => a -> IO ()
26+
reportToConsole e = hPutStr stderr $ P.render $ report e
27+
28+
29+
30+
-- | Attempt an IO action, but report and abort in the case of a
31+
-- reportable error.
32+
tryOrReport :: IO a -> IO a
33+
tryOrReport action = do
34+
result <- tryJust eucalyptError action
35+
case result of
36+
Left e -> reportToConsole e >> exitFailure
37+
Right v -> return v
38+
where
39+
eucalyptError :: EucalyptError -> Maybe EucalyptError
40+
eucalyptError = Just

src/Eucalypt/Syntax/Ast.hs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,25 +157,33 @@ type DeclarationForm = Located DeclarationForm_
157157
-- This may be any of
158158
-- * a property declaration
159159
-- * a function declaration
160-
-- * an operator declaration
160+
-- * an operator declaration (binary, left or right unary)
161161
data DeclarationForm_
162162

163-
= PropertyDecl AtomicName Expression |
163+
= PropertyDecl AtomicName Expression
164164
-- ^ A simple property declaration: @key: value-expression@
165165

166-
FunctionDecl AtomicName [ParameterName] Expression |
166+
| FunctionDecl AtomicName [ParameterName] Expression
167167
-- ^ A function declaration @f(x, y, z): value-expression@
168168

169-
OperatorDecl AtomicName ParameterName ParameterName Expression
169+
| OperatorDecl AtomicName ParameterName ParameterName Expression
170170
-- ^ A binary operator declaration @(x ** y): value-expression@
171171

172+
| LeftOperatorDecl AtomicName ParameterName Expression
173+
-- ^ A left unary operator declaration @(! x): value-expression@
174+
175+
| RightOperatorDecl AtomicName ParameterName Expression
176+
-- ^ A right unary operator declaration @(x !): value-expression@
177+
172178
deriving (Eq, Show, Generic, ToJSON)
173179

174180

175181
instance HasLocation DeclarationForm_ where
176182
stripLocation (PropertyDecl n e) = PropertyDecl n (stripLocation e)
177183
stripLocation (FunctionDecl f ps e) = FunctionDecl f ps (stripLocation e)
178184
stripLocation (OperatorDecl n l r e) = OperatorDecl n l r (stripLocation e)
185+
stripLocation (LeftOperatorDecl n o e) = LeftOperatorDecl n o (stripLocation e)
186+
stripLocation (RightOperatorDecl n o e) = RightOperatorDecl n o (stripLocation e)
179187

180188
type BlockElement = Located BlockElement_
181189

@@ -236,6 +244,14 @@ func f params e = at nowhere $ FunctionDecl (NormalName f) params e
236244
oper :: String -> String -> String -> Expression -> DeclarationForm
237245
oper o l r e = at nowhere $ OperatorDecl (OperatorName o) l r e
238246

247+
-- | Create an operator declaration
248+
loper :: String -> String -> Expression -> DeclarationForm
249+
loper o x e = at nowhere $ LeftOperatorDecl (OperatorName o) x e
250+
251+
-- | Create an operator declaration
252+
roper :: String -> String -> Expression -> DeclarationForm
253+
roper o x e = at nowhere $ RightOperatorDecl (OperatorName o) x e
254+
239255
-- | Create an annotated block element
240256
ann :: Expression -> DeclarationForm -> BlockElement
241257
ann a decl = at nowhere $ Declaration Annotated { annotation = Just a, content = decl }

src/Eucalypt/Syntax/Error.hs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,34 @@
1+
{-|
2+
Module : Eucalypt.Syntax.Error
3+
Description : Syntax errors from source or input specs
4+
Copyright : (c) Greg Hawkins, 2018
5+
License :
6+
Maintainer : greg@curvelogic.co.uk
7+
Stability : experimental
8+
-}
19
module Eucalypt.Syntax.Error where
210

311
import Control.Exception.Safe
12+
import Data.List.NonEmpty as NE
413
import Data.Void
14+
import Eucalypt.Reporting.Classes
15+
import Eucalypt.Reporting.Location
516
import qualified Text.Megaparsec as M
17+
import qualified Text.PrettyPrint as P
618

719
newtype SyntaxError
820
= MegaparsecError (M.ParseError (M.Token String) Void)
921
deriving (Show, Eq, Typeable)
1022

1123
instance Exception SyntaxError
24+
25+
toSpan :: NonEmpty M.SourcePos -> SourceSpan
26+
toSpan positions = (h, h)
27+
where
28+
h = SourcePosition $ NE.head positions
29+
30+
-- | Make SyntaxError 'Reportable'
31+
instance Reportable SyntaxError where
32+
code (MegaparsecError pe) = Just . toSpan . M.errorPos $ pe
33+
report (MegaparsecError pe) = P.text "SYNTAX ERROR" P.$$ P.text msg
34+
where msg = M.parseErrorPretty pe

0 commit comments

Comments
 (0)