Skip to content

Commit

Permalink
Distinguish between foreign and native packages
Browse files Browse the repository at this point in the history
  • Loading branch information
owickstrom committed Jul 17, 2016
1 parent 9a13862 commit b256998
Show file tree
Hide file tree
Showing 40 changed files with 466 additions and 246 deletions.
3 changes: 2 additions & 1 deletion cli/Oden/CLI/Lint.hs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
module Oden.CLI.Lint where

import Oden.QualifiedName
import Oden.Scanner

import Oden.CLI
import Oden.CLI.Build

lint :: FilePath -> CLI ()
lint path = do
_ <- compileFile (OdenSourceFile path ["main"])
_ <- compileFile (OdenSourceFile path (NativePackageName ["main"]))
return ()
14 changes: 9 additions & 5 deletions cli/Oden/CLI/PrintPackage.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module Oden.CLI.PrintPackage where

import Oden.Pretty ()
import Oden.Scanner
import Oden.QualifiedName

import Oden.CLI
import Oden.CLI.Build
Expand All @@ -16,30 +17,33 @@ import Text.PrettyPrint.Leijen hiding ((<$>))
render :: Doc -> String
render doc = displayS (renderPretty 0.4 120 doc) ""

mainPkg :: PackageName
mainPkg = NativePackageName ["main"]

printInferred :: FilePath -> CLI ()
printInferred path = do
(_, pkg) <- inferFile (OdenSourceFile path ["main"])
(_, pkg) <- inferFile (OdenSourceFile path mainPkg)
liftIO $ putStrLn $ render $ pretty pkg

printTypes :: FilePath -> CLI ()
printTypes path = do
(_, TypedPackage _ _ definitions) <- inferFile (OdenSourceFile path ["main"])
(_, TypedPackage _ _ definitions) <- inferFile (OdenSourceFile path mainPkg)
liftIO $ putStrLn $ render $ vcat $ map prettyScheme definitions
where
prettyScheme (Definition _ name (scheme, _)) = pretty name <+> text ":" <+> pretty scheme
prettyScheme _ = empty

printEnv :: FilePath -> CLI ()
printEnv path = do
env <- fst <$> inferFile (OdenSourceFile path ["main"])
env <- fst <$> inferFile (OdenSourceFile path mainPkg)
liftIO $ putStrLn $ render $ pretty env

printResolved :: FilePath -> CLI ()
printResolved path = do
pkg <- snd <$> resolveImplsInFile (OdenSourceFile path ["main"])
pkg <- snd <$> resolveImplsInFile (OdenSourceFile path mainPkg)
liftIO $ putStrLn $ render $ pretty pkg

printCompiled :: FilePath -> CLI ()
printCompiled path = do
pkg <- compileFile (OdenSourceFile path ["main"])
pkg <- compileFile (OdenSourceFile path mainPkg)
liftIO $ putStrLn $ render $ pretty pkg
3 changes: 2 additions & 1 deletion cli/Oden/CLI/Run.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module Oden.CLI.Run where

import Oden.Backend
import Oden.Backend.Go
import Oden.QualifiedName
import Oden.Scanner

import Control.Monad.Reader
Expand All @@ -16,7 +17,7 @@ import Oden.CLI.Build

run :: FilePath -> CLI ()
run path = do
pkg <- compileFile (OdenSourceFile path ["main"])
pkg <- compileFile (OdenSourceFile path (NativePackageName ["main"]))
tmpDir <- liftIO getTemporaryDirectory
tmp <- liftIO (createTempDirectory tmpDir "oden-run.go")
files <- liftEither (codegen (GoBackend tmp) pkg)
Expand Down
30 changes: 15 additions & 15 deletions go/src/oden/cmd/importer/importer.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import (
"oden/importer"
)

type PackageResponse struct {
Package *importer.Package `json:"package"`
}

type ErrorResponse struct {
Error string `json:"error"`
}
Expand All @@ -21,24 +25,25 @@ func respondWithError(err error) string {
return string(b)
}

func getPackageObjectsJSON(pkgName string) string {
objs, err := importer.GetPackageObjects(pkgName)
func getPackageJSON(pkgName string) string {
pkg, err := importer.GetPackage(pkgName)

if err != nil {
return respondWithError(err)
}

b, err := json.Marshal(importer.Scope{objs})
resp := PackageResponse{pkg}
b, err := json.Marshal(resp)
if err != nil {
return respondWithError(err)
}

return string(b)
}

//export GetPackageObjects
func GetPackageObjects(name *C.char) *C.char {
return C.CString(getPackageObjectsJSON(C.GoString(name)))
//export GetPackage
func GetPackage(name *C.char) *C.char {
return C.CString(getPackageJSON(C.GoString(name)))
}

func usage() {
Expand Down Expand Up @@ -74,19 +79,14 @@ func main() {
return
}

objs, err := importer.GetPackageObjects(os.Args[2])
if err != nil {
die("%s\n", err)
}

switch cmd {
case "print":
fmt.Println(objs)
case "print-json":
bs, err := json.Marshal(objs)
pkg, err := importer.GetPackage(os.Args[2])
if err != nil {
die("%s\n", err)
}
fmt.Println(string(bs))
fmt.Println(pkg)
case "print-json":
fmt.Println(getPackageJSON(os.Args[2]))
}
}
18 changes: 10 additions & 8 deletions go/src/oden/importer/objects.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package importer
import (
"go/importer"
"go/types"
"strings"
)

func tupleToSlice(tu *types.Tuple) []Type {
Expand Down Expand Up @@ -86,12 +85,12 @@ func encodeType(t types.Type) Type {

// When n.Obj().Pkg() is nil the Named type is defined in the universe. We
// represent that with an empty package name slice.
var pkgSegments []string = []string{}
var pkgName string = ""
if n.Obj().Pkg() != nil {
pkgSegments = strings.Split(n.Obj().Pkg().Path(), "/")
pkgName = n.Obj().Pkg().Name()
}
return NewNamed(
pkgSegments,
pkgName,
n.Obj().Name(),
encodeType(n.Underlying()))
}
Expand Down Expand Up @@ -152,11 +151,13 @@ func encodeType(t types.Type) Type {
}
}

func GetPackageObjects(pkgName string) (objs []Object, err error) {
pkg, err := importer.Default().Import(pkgName)
func GetPackage(pkgPath string) (*Package, error) {
var err error
pkg, err := importer.Default().Import(pkgPath)
if err != nil {
return objs, err
return nil, err
}
objs := []Object{}

for _, n := range pkg.Scope().Names() {
obj := pkg.Scope().Lookup(n)
Expand Down Expand Up @@ -190,5 +191,6 @@ func GetPackageObjects(pkgName string) (objs []Object, err error) {
}
}
}
return objs, nil
p := Package{pkg.Name(), objs}
return &p, nil
}
13 changes: 7 additions & 6 deletions go/src/oden/importer/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,14 @@ func NewBasic(name string, untyped bool) Basic {
}

type Named struct {
Kind string `json:"kind"`
Pkg []string `json:"pkg"`
Name string `json:"name"`
Underlying Type `json:"underlying"`
Kind string `json:"kind"`
Pkg string `json:"pkg"`
Name string `json:"name"`
Underlying Type `json:"underlying"`
// Methods []Func `json:"methods"`
}

func NewNamed(pkg []string, name string, underlying Type) Named {
func NewNamed(pkg string, name string, underlying Type) Named {
return Named{"named", pkg, name, underlying}
}

Expand All @@ -107,6 +107,7 @@ type Object struct {
Type Type `json:"type"`
}

type Scope struct {
type Package struct {
Name string `json:"name"`
Objects []Object `json:"objects"`
}
1 change: 1 addition & 0 deletions oden.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ library
Oden.Output.Compiler.Validation.Typed,
Oden.Output.Desugar,
Oden.Output.Imports,
Oden.Output.Identifier,
Oden.Output.Infer,
Oden.Output.Instantiate,
Oden.Output.Parser,
Expand Down
5 changes: 5 additions & 0 deletions regression-test/src/importforeign.oden
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package importforeign/main

import foreign "math"

main() = println(math.Floor(math.Pi))
1 change: 1 addition & 0 deletions regression-test/src/importforeign.oden.expected.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3
70 changes: 51 additions & 19 deletions src/Oden/Backend/Go.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module Oden.Backend.Go (
import Control.Monad.Except
import Control.Monad.Reader

import Data.List (sortOn, find, intercalate)
import Data.List (sortOn, find)
import Data.Maybe (maybeToList)
import qualified Data.Set as Set

Expand All @@ -31,7 +31,7 @@ import Oden.Core.Typed

import Oden.Identifier
import Oden.Metadata
import Oden.QualifiedName (QualifiedName(..))
import Oden.QualifiedName (PackageName(..), QualifiedName(..))
import Oden.SourceInfo hiding (fileName)
import qualified Oden.SourceInfo as SourceInfo
import qualified Oden.Type.Monomorphic as Mono
Expand All @@ -41,7 +41,7 @@ type Codegen = ReaderT MonomorphedPackage (Except CodegenError)
newtype GoBackend = GoBackend FilePath

isUniverseTypeConstructor :: String -> Mono.Type -> Bool
isUniverseTypeConstructor expected (Mono.TCon _ (FQN [] (Identifier actual))) =
isUniverseTypeConstructor expected (Mono.TCon _ (FQN (NativePackageName []) (Identifier actual))) =
actual == expected
isUniverseTypeConstructor _ _ = False

Expand Down Expand Up @@ -362,9 +362,10 @@ genExpr expr = case expr of
return [ AST.LiteralComment (genSourceInfo (getSourceInfo e))
, value ]

(Block _ [] _) -> return emptyStructLiteral
Block _ [] _ ->
return emptyStructLiteral

(Block _ exprs t) -> do
Block _ exprs t -> do
blockType <- genType t
initStmts <- concat <$> mapM genWithSourceInfo (init exprs)
let lastExpr = last exprs
Expand Down Expand Up @@ -419,12 +420,14 @@ genExpr expr = case expr of
Foreign _ (ForeignBinaryOperator _) _ ->
error "cannot codegen foreign binary operator without a full binary application"


genBlock :: MonoTypedExpr -> Codegen AST.Block
genBlock expr = do
returnStmt <- AST.ReturnStmt . (:[]) <$> genExpr expr
let comment = AST.StmtComment (genSourceInfo (getSourceInfo expr))
return (AST.Block [comment, returnStmt])


genTopLevel :: Identifier
-> Mono.Type
-> MonoTypedExpr
Expand Down Expand Up @@ -462,21 +465,29 @@ genTopLevel name type' expr = do
, AST.Decl (AST.VarDecl var)
]


genInstance :: InstantiatedDefinition -> Codegen [AST.TopLevelDeclaration]
genInstance = \case
InstantiatedDefinition (Identifier _defName) _si name expr ->
genTopLevel name (typeOf expr) expr
InstantiatedMethod _si name expr ->
genTopLevel name (typeOf expr) expr


genMonomorphed :: MonomorphedDefinition -> Codegen [AST.TopLevelDeclaration]
genMonomorphed (MonomorphedDefinition _ name mt expr) =
genTopLevel name mt expr


genImport :: ImportedPackage TypedPackage -> Codegen AST.ImportDecl
genImport (ImportedPackage _ identifier (TypedPackage (PackageDeclaration _ pkgName) _ _)) =
AST.ImportDecl <$> genIdentifier identifier
<*> return (AST.InterpretedStringLiteral (intercalate "/" pkgName))
genImport (ImportedPackage ref identifier _) =
case ref of
ImportReference{} ->
error "TODO: Don't do codegen for native import. Better yet, exclude from IR."
ImportForeignReference _ pkgPath ->
AST.ImportDecl <$> genIdentifier identifier
<*> return (AST.InterpretedStringLiteral pkgPath)


-- | Return the import alias name for the fmt package and possibly the code for
-- importing fmt (if not imported by the user).
Expand All @@ -488,8 +499,9 @@ getFmtImport pkgs =
let fmt = GI.Identifier "fmt"
in (fmt, Just (AST.ImportDecl fmt (AST.InterpretedStringLiteral "fmt")))
where
isFmtPackage (ImportedPackage _ _ (TypedPackage (PackageDeclaration _ ["fmt"]) _ _)) = True
isFmtPackage _ = False
isFmtPackage (ImportedPackage _ _ (TypedPackage (PackageDeclaration _ (ForeignPackageName "fmt")) _ _)) = True
isFmtPackage _ = False


prelude :: GI.Identifier -> [AST.TopLevelDeclaration]
prelude fmtAlias =
Expand All @@ -508,8 +520,17 @@ prelude fmtAlias =
(AST.Block [AST.SimpleStmt (AST.ExpressionStmt (fmtApplication (GI.Identifier "Println")))])
]


genPackageName :: PackageDeclaration -> Codegen GI.Identifier
genPackageName (PackageDeclaration _ pkgName) =
case pkgName of
NativePackageName segments -> return (safeName (last segments))
ForeignPackageName n -> return (safeName n)


genPackage :: MonomorphedPackage -> Codegen AST.SourceFile
genPackage (MonomorphedPackage (PackageDeclaration _ name) imports is ms) = do
genPackage (MonomorphedPackage pkgDecl imports is ms) = do
pkgName <- genPackageName pkgDecl
imports' <- mapM genImport imports

let (fmtAlias, fmtImport) = getFmtImport imports
Expand All @@ -520,16 +541,27 @@ genPackage (MonomorphedPackage (PackageDeclaration _ name) imports is ms) = do

let allTopLevel = prelude fmtAlias ++ is' ++ ms'

return (AST.SourceFile (AST.PackageClause (safeName (last name))) allImports allTopLevel)
return (AST.SourceFile (AST.PackageClause pkgName) allImports allTopLevel)


toFilePath :: GoBackend -> PackageName -> Codegen FilePath
toFilePath _ [] = throwError (UnexpectedError "Package name cannot be empty")
toFilePath (GoBackend goPath) parts =
let isMain = last parts == "main"
dirParts = "src" : if isMain then init parts else parts
dir = foldl (</>) goPath dirParts
fileName = if isMain then "main.go" else last parts ++ ".go"
in return (dir </> fileName)
toFilePath (GoBackend goPath) =
\case
NativePackageName [] ->
throwError (UnexpectedError "Package name cannot be empty")

ForeignPackageName pkgName ->
throwError
(UnexpectedError $
"Cannot codegen a package with a native package name: " ++ pkgName)

NativePackageName segments ->
let isMain = last segments == "main"
dirParts = "src" : if isMain then init segments else segments
dir = foldl (</>) goPath dirParts
fileName = if isMain then "main.go" else last segments ++ ".go"
in return (dir </> fileName)


instance Backend GoBackend where
codegen backend pkg@(MonomorphedPackage (PackageDeclaration _ name) _ _ _) =
Expand Down
Loading

0 comments on commit b256998

Please sign in to comment.