Skip to content

Commit be698c0

Browse files
added readme, replaced JS_TAG hard-coded values with constants from the quickjs.h header in Quickjs.Types
1 parent 9b64ce2 commit be698c0

File tree

6 files changed

+128
-79
lines changed

6 files changed

+128
-79
lines changed

.github/workflows/haskell.yml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,17 @@ name: Tests CI
1919

2020
on:
2121
push:
22-
schedule:
23-
- cron: "0 0 * * 1"
22+
branches:
23+
- master
24+
paths:
25+
- 'src/**'
26+
- 'test/**'
27+
- 'quickjs/**'
28+
- 'stack.yaml'
29+
- 'quickjs-hs.cabal'
30+
pull_request:
31+
branches:
32+
- master
2433

2534
jobs:
2635
build:

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ cabal.project.local~
2222
.HTF/
2323
.ghc.environment.*
2424
.stack-work/
25-
*~
25+
*~
26+
.DS_Store

README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# quickjs-hs
2+
3+
![Tests CI](https://github.com/goodlyrottenapple/quickjs-hs/workflows/Tests%20CI/badge.svg)
4+
5+
6+
This package provides a Haskell wrapper for the [QuickJS](https://bellard.org/quickjs/) library.
7+
8+
## Features
9+
10+
The functionality is quite basic and is currently limited to:
11+
- evaluating JS code
12+
- calling a JS function in the global scope
13+
- marshalling [Aeson Values](https://hackage.haskell.org/package/aeson-1.5.3.0/docs/Data-Aeson.html#t:Value) to and from JSValues.
14+
15+
16+
## Examples
17+
Evaluate an expression:
18+
19+
```haskell
20+
import Quickjs
21+
22+
one_plus_two = quickjs $ do
23+
res <- eval "1+2"
24+
liftIO $ print res
25+
```
26+
27+
Declare a function and call it on an argument:
28+
29+
```haskell
30+
call_f = quickjs $ do
31+
_ <- eval_ "f = (x) => x+1"
32+
res <- eval "f(2)"
33+
liftIO $ print res
34+
```
35+
36+
Pass a Haskell value (which has a [ToJSON](https://hackage.haskell.org/package/aeson-1.5.3.0/docs/Data-Aeson.html#t:ToJSON) instance) to the JS runtime:
37+
38+
```haskell
39+
aeson_marshall = quickjs $ do
40+
_ <- eval_ "f = (x) => x+1"
41+
res <- withJSValue (3::Int) $ \x -> call "f" [x]
42+
liftIO $ print res
43+
```
44+
45+
## Contributing
46+
47+
Please feel free to report bugs/submit feature requests via the [github issue tracker](https://github.com/goodlyrottenapple/quickjs-hs/issues) and submit any pull requests to the [git repository](https://github.com/goodlyrottenapple/quickjs-hs/)

src/.DS_Store

-6 KB
Binary file not shown.

src/Quickjs.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Module : Quickjs
55
Description : Haskell bindings to the [QuickJS](https://bellard.org/quickjs/) library
66
Copyright : (c) Samuel Balco, 2020
77
License : MIT
8-
Maintainer : sam@definitelynotspam.email
8+
Maintainer : goodlyrottenapple@gmail.com
99
1010
This is a very basic wrapper for the [QuickJS](https://bellard.org/quickjs/) .
1111
@@ -334,7 +334,7 @@ forLoop end f = go 0
334334

335335
jsObjectToJSON :: (MonadCatch m, MonadIO m) => JSContextPtr -> JSValue -> m (HashMap Text Value)
336336
jsObjectToJSON ctxPtr obj = do
337-
let flags = toCType JSGPNStringMask .|. toCType JSGPNSymbolMask .|. toCType JSGPNEnumOnly
337+
let flags = unJSGPNMask $ jsGPNStringMask .|. jsGPNSymbolMask .|. jsGPNEnumOnly
338338
properties <- liftIO $ malloc
339339
plen <- jsGetOwnPropertyNames ctxPtr obj properties flags
340340
`catch` (\(e::SomeJSRuntimeException) -> do

src/Quickjs/Types.hsc

Lines changed: 66 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
{-# LANGUAGE QuasiQuotes, TemplateHaskell, OverloadedStrings, MultiParamTypeClasses, FlexibleInstances, DeriveGeneric, DeriveAnyClass #-}
1+
{-# LANGUAGE QuasiQuotes, TemplateHaskell, OverloadedStrings, MultiParamTypeClasses, FlexibleInstances, GeneralizedNewtypeDeriving #-}
22

33
module Quickjs.Types where
44

5-
import qualified Data.Map as Map
6-
import GHC.Generics
7-
import Data.Aeson (ToJSON(..))
5+
import qualified Data.Map as Map
6+
import Data.Bits (Bits)
87
import Foreign.C.Types
98
import Foreign.Ptr (plusPtr)
109
import Foreign.Storable (Storable(..))
@@ -84,63 +83,63 @@ class FromCType ty cty where
8483
fromCType :: cty -> Maybe ty
8584

8685

87-
data JSTagEnum = JSTagFirst
88-
| JSTagBigDecimal
89-
| JSTagBigInt
90-
| JSTagBigFloat
91-
| JSTagSymbol
92-
| JSTagString
93-
| JSTagModule
94-
| JSTagFunctionBytecode
95-
| JSTagObject
96-
| JSTagInt
97-
| JSTagBool
98-
| JSTagNull
99-
| JSTagUndefined
100-
| JSTagUninitialized
101-
| JSTagCatchOffset
102-
| JSTagException
103-
| JSTagFloat64
104-
deriving (Show, Eq, Generic, ToJSON)
86+
data JSTagEnum = JSTagFirst
87+
| JSTagBigDecimal
88+
| JSTagBigInt
89+
| JSTagBigFloat
90+
| JSTagSymbol
91+
| JSTagString
92+
| JSTagModule
93+
| JSTagFunctionBytecode
94+
| JSTagObject
95+
| JSTagInt
96+
| JSTagBool
97+
| JSTagNull
98+
| JSTagUndefined
99+
| JSTagUninitialized
100+
| JSTagCatchOffset
101+
| JSTagException
102+
| JSTagFloat64
103+
deriving (Show, Eq)
105104

106105
instance Num a => ToCType JSTagEnum a where
107-
toCType JSTagFirst = -11
108-
toCType JSTagBigDecimal = -11
109-
toCType JSTagBigInt = -10
110-
toCType JSTagBigFloat = -9
111-
toCType JSTagSymbol = -8
112-
toCType JSTagString = -7
113-
toCType JSTagModule = -3
114-
toCType JSTagFunctionBytecode = -2
115-
toCType JSTagObject = -1
116-
toCType JSTagInt = 0
117-
toCType JSTagBool = 1
118-
toCType JSTagNull = 2
119-
toCType JSTagUndefined = 3
120-
toCType JSTagUninitialized = 4
121-
toCType JSTagCatchOffset = 5
122-
toCType JSTagException = 6
123-
toCType JSTagFloat64 = 7
106+
toCType JSTagFirst = #{const JS_TAG_FIRST}
107+
toCType JSTagBigDecimal = #{const JS_TAG_BIG_DECIMAL}
108+
toCType JSTagBigInt = #{const JS_TAG_BIG_INT}
109+
toCType JSTagBigFloat = #{const JS_TAG_BIG_FLOAT}
110+
toCType JSTagSymbol = #{const JS_TAG_SYMBOL}
111+
toCType JSTagString = #{const JS_TAG_STRING}
112+
toCType JSTagModule = #{const JS_TAG_MODULE}
113+
toCType JSTagFunctionBytecode = #{const JS_TAG_FUNCTION_BYTECODE}
114+
toCType JSTagObject = #{const JS_TAG_OBJECT}
115+
toCType JSTagInt = #{const JS_TAG_INT}
116+
toCType JSTagBool = #{const JS_TAG_BOOL}
117+
toCType JSTagNull = #{const JS_TAG_NULL}
118+
toCType JSTagUndefined = #{const JS_TAG_UNDEFINED}
119+
toCType JSTagUninitialized = #{const JS_TAG_UNINITIALIZED}
120+
toCType JSTagCatchOffset = #{const JS_TAG_CATCH_OFFSET}
121+
toCType JSTagException = #{const JS_TAG_EXCEPTION}
122+
toCType JSTagFloat64 = #{const JS_TAG_FLOAT64}
124123

125124

126125
instance (Eq a, Num a) => FromCType JSTagEnum a where
127-
fromCType (-11) = Just JSTagBigDecimal
128-
fromCType (-10) = Just JSTagBigInt
129-
fromCType (-9) = Just JSTagBigFloat
130-
fromCType (-8) = Just JSTagSymbol
131-
fromCType (-7) = Just JSTagString
132-
fromCType (-3) = Just JSTagModule
133-
fromCType (-2) = Just JSTagFunctionBytecode
134-
fromCType (-1) = Just JSTagObject
135-
fromCType 0 = Just JSTagInt
136-
fromCType 1 = Just JSTagBool
137-
fromCType 2 = Just JSTagNull
138-
fromCType 3 = Just JSTagUndefined
139-
fromCType 4 = Just JSTagUninitialized
140-
fromCType 5 = Just JSTagCatchOffset
141-
fromCType 6 = Just JSTagException
142-
fromCType 7 = Just JSTagFloat64
143-
fromCType _ = Nothing
126+
fromCType (#{const JS_TAG_BIG_DECIMAL}) = Just JSTagBigDecimal
127+
fromCType (#{const JS_TAG_BIG_INT}) = Just JSTagBigInt
128+
fromCType (#{const JS_TAG_BIG_FLOAT}) = Just JSTagBigFloat
129+
fromCType (#{const JS_TAG_SYMBOL}) = Just JSTagSymbol
130+
fromCType (#{const JS_TAG_STRING}) = Just JSTagString
131+
fromCType (#{const JS_TAG_MODULE}) = Just JSTagModule
132+
fromCType (#{const JS_TAG_FUNCTION_BYTECODE}) = Just JSTagFunctionBytecode
133+
fromCType (#{const JS_TAG_OBJECT}) = Just JSTagObject
134+
fromCType (#{const JS_TAG_INT}) = Just JSTagInt
135+
fromCType (#{const JS_TAG_BOOL}) = Just JSTagBool
136+
fromCType (#{const JS_TAG_NULL}) = Just JSTagNull
137+
fromCType (#{const JS_TAG_UNDEFINED}) = Just JSTagUndefined
138+
fromCType (#{const JS_TAG_UNINITIALIZED}) = Just JSTagUninitialized
139+
fromCType (#{const JS_TAG_CATCH_OFFSET}) = Just JSTagCatchOffset
140+
fromCType (#{const JS_TAG_EXCEPTION}) = Just JSTagException
141+
fromCType (#{const JS_TAG_FLOAT64}) = Just JSTagFloat64
142+
fromCType _ = Nothing
144143

145144
data JSTypeEnum = JSTypeFromTag JSTagEnum
146145
| JSIsNumber
@@ -149,31 +148,24 @@ data JSTypeEnum = JSTypeFromTag JSTagEnum
149148
| JSIsError
150149
deriving Show
151150

152-
instance ToJSON JSTypeEnum where
153-
toJSON (JSTypeFromTag t) = toJSON t
154-
toJSON JSIsNumber = toJSON ("JSIsNumber" :: String)
155-
toJSON JSIsArray = toJSON ("JSIsArray" :: String)
156-
toJSON JSIsDate = toJSON ("JSIsDate" :: String)
157-
toJSON JSIsError = toJSON ("JSIsError" :: String)
158-
159-
160151
data JSEvalType = Global | Module
161152

162153

163154
instance Num a => ToCType JSEvalType a where
164-
toCType Global = 0
165-
toCType Module = 1
155+
toCType Global = #{const JS_EVAL_TYPE_GLOBAL}
156+
toCType Module = #{const JS_EVAL_TYPE_MODULE}
166157

167158

168-
data JSGPNMask = JSGPNStringMask | JSGPNSymbolMask | JSGPNPrivateMask | JSGPNEnumOnly | JSGPNSetEnum
169-
-- deriving (Num, Eq, Bits)
159+
newtype JSGPNMask = JSGPNMask { unJSGPNMask :: CInt }
160+
deriving (Eq, Bits)
170161

171-
instance Num a => ToCType JSGPNMask a where
172-
toCType JSGPNStringMask = 1
173-
toCType JSGPNSymbolMask = 2
174-
toCType JSGPNPrivateMask = 4
175-
toCType JSGPNEnumOnly = 16
176-
toCType JSGPNSetEnum = 32
162+
#{enum JSGPNMask, JSGPNMask
163+
, jsGPNStringMask = JS_GPN_STRING_MASK
164+
, jsGPNSymbolMask = JS_GPN_SYMBOL_MASK
165+
, jsGPNPrivateMask = JS_GPN_PRIVATE_MASK
166+
, jsGPNEnumOnly = JS_GPN_ENUM_ONLY
167+
, jsGPNSetEnum = JS_GPN_SET_ENUM
168+
}
177169

178170

179171
quickjsCtx :: Context

0 commit comments

Comments
 (0)