Skip to content

Commit c6f659f

Browse files
committed
ffi: init
1 parent 24cbfa4 commit c6f659f

File tree

12 files changed

+257
-21
lines changed

12 files changed

+257
-21
lines changed

Diff for: .hlint.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
# Ignore some builtin hints
6060
# - ignore: {name: Use let}
6161
# - ignore: {name: Use const, within: SpecialModule} # Only within certain modules
62+
- ignore: {name: Use fewer imports, within: Ffi}
6263

6364

6465
# Define some custom infix operators

Diff for: examples/ffi.in

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
external "sayhello" = do
2+
greet :: String -> IO
3+
printNumbers :: List{Int} -> Int -> IO
4+
printf :: String -> Int -> IO
5+
end
6+
external "__default" = do
7+
puts :: List{Char} -> IO
8+
end
9+
10+
let main = do
11+
puts ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0']
12+
printNumbers [1, 2, 3, 4, 5], 5
13+
printf "Hello, %d!\n", 42
14+
end

Diff for: indigo.cabal

+11
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ common warnings
1717
-- ghc-options: -Wall -Wno-incomplete-uni-patterns -O0 -fno-code
1818
ghc-options: -Wall -Wno-incomplete-uni-patterns
1919

20+
flag ffi
21+
description: FFI
22+
default: True
23+
2024
library
2125
import: warnings
2226
exposed-modules: , Parser
@@ -26,6 +30,7 @@ library
2630
, Verifier
2731
, Util
2832
, RegInst
33+
, Ffi
2934
other-modules: Paths_indigo
3035
build-depends: base,
3136
containers,
@@ -44,6 +49,12 @@ library
4449
executable-path,
4550
filepath,
4651
random
52+
if flag(ffi)
53+
build-depends:
54+
libffi,
55+
unix
56+
cpp-options: -DFFI
57+
4758
hs-source-dirs: lib
4859
default-language: Haskell2010
4960
default-extensions: OverloadedStrings,

Diff for: indigo.vim

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ syn region inImportList start='(' skip='([^)]\{-})' end=')' keepend contained c
3737

3838
syn keyword inImportMod contained as qualified hiding
3939
syn keyword inInfix as
40-
syn keyword inStructure struct trait impl for let
40+
syn keyword inStructure struct trait impl for let external
4141
syn keyword inTypedef type
4242
syn keyword inNewtypedef newtype
4343
syn keyword inTypeFam family

Diff for: lib/AST.hs

+18
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ module AST where
22

33
import Data.Binary qualified
44
import GHC.Generics (Generic)
5+
import VM qualified
56

67
data Expr
78
= Var String Position
@@ -54,6 +55,8 @@ data Expr
5455
| Trait {name :: String, methods :: [Expr]}
5556
| Impl {trait :: String, for :: String, methods :: [Expr]}
5657
| StrictEval Expr
58+
| External String [Expr]
59+
| CharLit Char
5760
deriving
5861
( Show
5962
, Generic
@@ -111,6 +114,8 @@ children (Trait _ a) = a
111114
children (Impl _ _ a) = a
112115
children (FuncDec _ _) = []
113116
children (StrictEval a) = [a]
117+
children (External _ a) = a
118+
children (CharLit _) = []
114119

115120
newtype Position = Position (Int, Int) deriving (Show, Generic, Ord)
116121

@@ -133,6 +138,7 @@ data Type
133138
| Float
134139
| Bool
135140
| String
141+
| Char
136142
| Any
137143
| None
138144
| Unknown
@@ -154,6 +160,7 @@ instance Show Type where
154160
show (List t) = "List{" ++ show t ++ "}"
155161
show (StructT structName) = structName
156162
show Self = "Self"
163+
show Char = "Char"
157164

158165
newtype Program = Program {exprs :: [Expr]} deriving (Show, Eq, Generic)
159166

@@ -227,6 +234,17 @@ typeOf (Trait _ _) = error "Cannot infer type of trait"
227234
typeOf (Impl{}) = error "Cannot infer type of impl"
228235
typeOf (Then _ b) = typeOf b
229236
typeOf (StrictEval x) = typeOf x
237+
typeOf (External _ _) = error "Cannot infer type of external"
238+
typeOf (CharLit _) = Char
239+
240+
typeToData :: Type -> VM.Data
241+
typeToData Int = VM.DInt 0
242+
typeToData Float = VM.DFloat 0
243+
typeToData Bool = VM.DBool False
244+
typeToData String = VM.DString ""
245+
typeToData (StructT "IO") = VM.DNone -- Hmmm...
246+
typeToData Char = VM.DChar ' '
247+
typeToData x = error $ "Cannot convert type " ++ show x ++ " to data"
230248

231249
-- typeOf x = error $ "Cannot infer type of " ++ show x
232250

Diff for: lib/BytecodeCompiler.hs

+41-17
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
-- module BytecodeCompiler (runTestProgram, locateLabel, printAssembly, compileProgram, CompilerState (..), Function) where
22
module BytecodeCompiler where
33

4-
import AST (zeroPosition)
4+
import AST (typeToData, zeroPosition)
55
import AST qualified as Parser.Type (Type (Unknown))
66
import Control.Monad (when, (>=>))
77
import Control.Monad.Loops (firstM)
@@ -12,6 +12,7 @@ import Data.Maybe (fromJust, isJust, isNothing)
1212
import Data.Text (isPrefixOf, splitOn)
1313
import Data.Text qualified
1414
import Data.Text qualified as T
15+
import Debug.Trace
1516
import Foreign ()
1617
import Foreign.C.Types ()
1718
import GHC.Generics (Generic)
@@ -35,6 +36,14 @@ data Function = Function
3536
}
3637
deriving (Show, Generic)
3738

39+
data External = External
40+
{ name :: String
41+
, returnType :: Parser.Type
42+
, args :: [Parser.Type]
43+
, from :: String
44+
}
45+
deriving (Show)
46+
3847
data CompilerState a = CompilerState
3948
{ program :: Parser.Program
4049
, -- , functions :: [(String, String, [Instruction])]
@@ -46,6 +55,7 @@ data CompilerState a = CompilerState
4655
, traits :: [Parser.Expr]
4756
, impls :: [Parser.Expr]
4857
, currentContext :: String -- TODO: Nested contexts
58+
, externals :: [External]
4959
}
5060
deriving (Show)
5161

@@ -57,7 +67,7 @@ data Let = Let
5767
deriving (Show)
5868

5969
initCompilerState :: Parser.Program -> CompilerState a
60-
initCompilerState prog = CompilerState prog [] [] [] 0 [] [] [] "__outside"
70+
initCompilerState prog = CompilerState prog [] [] [] 0 [] [] [] "__outside" []
6171

6272
allocId :: StateT (CompilerState a) IO Int
6373
allocId = do
@@ -217,32 +227,39 @@ compileExpr (Parser.FuncCall funcName args _) = do
217227
functions' <- gets functions
218228
funcDecs' <- gets funcDecs
219229
curCon <- gets currentContext
230+
externals' <- gets externals
220231
let fun = case findFunction (curCon ++ "@" ++ funcName) functions' argTypes of
221232
(Just lf) -> lf
222233
Nothing -> case findFunction funcName functions' argTypes of
223234
(Just f) -> f
224235
Nothing -> Function{baseName = unmangleFunctionName funcName, funame = funcName, function = [], types = []}
225236
let funcDec = find (\(Parser.FuncDec name' _) -> name' == baseName fun) funcDecs'
226-
237+
let external = find (\x -> x.name == funcName) externals'
227238
-- If the funcName starts with curCon@, it's a local function
228239
let callWay = (if T.pack (curCon ++ "@") `isPrefixOf` T.pack fun.funame then CallLocal (funame fun) else Call (funame fun))
229240

230-
case funcDec of
231-
(Just fd) -> do
232-
if length args == length (Parser.types fd) - 1
233-
then concatMapM compileExpr args >>= \args' -> return (args' ++ [callWay])
234-
else
241+
case external of
242+
Just (External _ ereturnType _ from) -> do
243+
let retT = typeToData ereturnType
244+
args' <- concatMapM compileExpr (reverse args)
245+
return $ [Push retT] ++ args' ++ [CallFFI funcName from (length args)]
246+
Nothing ->
247+
case funcDec of
248+
(Just fd) -> do
249+
if length args == length (Parser.types fd) - 1
250+
then concatMapM compileExpr args >>= \args' -> return (args' ++ [callWay])
251+
else
252+
concatMapM compileExpr args >>= \args' ->
253+
return $
254+
args'
255+
++ [PushPf (funame fun) (length args')]
256+
Nothing ->
235257
concatMapM compileExpr args >>= \args' ->
236258
return $
237259
args'
238-
++ [PushPf (funame fun) (length args')]
239-
Nothing ->
240-
concatMapM compileExpr args >>= \args' ->
241-
return $
242-
args'
243-
++ [ LLoad funcName
244-
, CallS
245-
]
260+
++ [ LLoad funcName
261+
, CallS
262+
]
246263
compileExpr fd@(Parser.FuncDec _ _) = do
247264
modify (\s -> s{funcDecs = fd : funcDecs s})
248265
return [] -- Function declarations are only used for compilation
@@ -303,10 +320,11 @@ compileExpr (Parser.FuncDef origName args body) = do
303320
compileExpr (Parser.Var x _) = do
304321
functions' <- gets functions
305322
curCon <- gets currentContext
323+
externals' <- gets externals
306324
-- traceM $ "Looking for " ++ x ++ " in " ++ show (map baseName functions')
307325
-- traceM $ "Looking for " ++ curCon ++ "@" ++ x ++ " in " ++ show (map baseName functions')
308326
let fun = any ((== x) . baseName) functions' || any ((== curCon ++ "@" ++ x) . baseName) functions'
309-
if fun || x `elem` internalFunctions
327+
if fun || x `elem` internalFunctions || x `elem` map (\f -> f.name) externals'
310328
then compileExpr (Parser.FuncCall x [] zeroPosition)
311329
else return [LLoad x]
312330
compileExpr (Parser.Let name value) = do
@@ -416,6 +434,12 @@ compileExpr (Parser.Then a b) = do
416434
b' <- compileExpr b
417435
return $ a' ++ b'
418436
compileExpr (Parser.UnaryMinus x) = compileExpr x >>= \x' -> return (x' ++ [Push $ DInt (-1), Mul])
437+
compileExpr (Parser.External _ []) = return []
438+
compileExpr (Parser.External from ((Parser.FuncDec name types) : xs)) = do
439+
modify (\s -> s{externals = External{name, returnType = last types, args = init types, from} : externals s})
440+
_ <- compileExpr (Parser.External from xs)
441+
return []
442+
compileExpr (Parser.CharLit x) = return [Push $ DChar x]
419443
compileExpr x = error $ show x ++ " is not implemented"
420444

421445
createVirtualFunctions :: StateT (CompilerState a) IO ()

Diff for: lib/FFI.hs

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
{-# LANGUAGE CPP #-}
2+
3+
module Ffi where
4+
5+
import Foreign.Ptr (FunPtr)
6+
7+
#if defined(MIN_VERSION_Win32)
8+
import Control.Exception (bracket)
9+
import Foreign.Ptr (castPtrToFunPtr)
10+
import System.Win32.DLL
11+
import System.Win32.Types (HMODULE)
12+
#else
13+
import System.Posix.DynamicLinker
14+
#endif
15+
16+
-- | The dynamic library containing the C runtime.
17+
crtPath :: FilePath
18+
#if defined(mingw32_HOST_OS)
19+
crtPath = "msvcrt.dll"
20+
#elif defined(darwin_HOST_OS)
21+
crtPath = "libc.dylib"
22+
#else
23+
-- On other OSes, it suffices to use the name of the current
24+
-- executable, as the dyanmic loader can chase dependencies until it
25+
-- finds the C runtime.
26+
crtPath = ""
27+
#endif
28+
29+
-- | The file extension used for dynamic libraries.
30+
dllext :: String
31+
#if defined(mingw32_HOST_OS)
32+
dllext = "dll"
33+
#elif defined(darwin_HOST_OS)
34+
dllext = "dylib"
35+
#else
36+
dllext = "so"
37+
#endif
38+
39+
-- | The Haskell representation of a loaded dynamic library.
40+
#if defined(MIN_VERSION_Win32)
41+
type DynLib = HMODULE
42+
#else
43+
type DynLib = DL
44+
#endif
45+
46+
-- | Return the address of a function symbol contained in a dynamic library.
47+
dynLibSym :: DynLib -> String -> IO (FunPtr a)
48+
#if defined(MIN_VERSION_Win32)
49+
dynLibSym source symbol = do
50+
addr <- getProcAddress source symbol
51+
return $ castPtrToFunPtr addr
52+
#else
53+
dynLibSym = dlsym
54+
#endif
55+
56+
-- | Load a dynamic library, perform some action on it, and then free it.
57+
withDynLib :: FilePath -> (DynLib -> IO a) -> IO a
58+
#if defined(MIN_VERSION_Win32)
59+
withDynLib file f = bracket (loadLibrary file) freeLibrary f
60+
#else
61+
withDynLib file = withDL file [RTLD_NOW]
62+
#endif
63+
64+
-- | Open a dynamic library.
65+
dynLibOpen :: FilePath -> IO DynLib
66+
#if defined(MIN_VERSION_Win32)
67+
dynLibOpen = loadLibrary
68+
#else
69+
dynLibOpen x = dlopen x [RTLD_NOW]
70+
#endif

Diff for: lib/Parser.hs

+20-1
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,9 @@ validType =
207207
<|> do
208208
keyword "Self"
209209
return Self
210+
<|> do
211+
keyword "Char"
212+
return Char
210213
<|> do
211214
lookAhead $ satisfy isUpper
212215
StructT <$> identifier
@@ -215,6 +218,9 @@ validType =
215218
stringLit :: Parser String
216219
stringLit = lexeme $ char '\"' *> manyTill L.charLiteral (char '\"')
217220

221+
charLit :: Parser Char
222+
charLit = lexeme $ char '\'' *> L.charLiteral <* char '\''
223+
218224
funcDec :: Parser Expr
219225
funcDec = do
220226
name <- identifier <|> gravis
@@ -457,6 +463,17 @@ impl = do
457463
symbol "end"
458464
return $ Impl name for methods
459465

466+
external :: Parser Expr
467+
external = do
468+
keyword "external"
469+
from <- stringLit
470+
symbol "="
471+
symbol "do"
472+
newline'
473+
decs <- funcDec `sepEndBy` newline'
474+
symbol "end"
475+
return $ External from decs
476+
460477
term :: Parser Expr
461478
term =
462479
choice
@@ -465,10 +482,12 @@ term =
465482
, FloatLit <$> try float
466483
, IntLit <$> try integer
467484
, StringLit <$> try stringLit
485+
, CharLit <$> try charLit
468486
, symbol "True" >> return (BoolLit True)
469487
, symbol "False" >> return (BoolLit False)
470488
, -- , letExpr
471-
struct
489+
external
490+
, struct
472491
, import_
473492
, doBlock
474493
, impl

0 commit comments

Comments
 (0)