diff --git a/src/Deftype.hs b/src/Deftype.hs index 03c36a1d..82381d0c 100644 --- a/src/Deftype.hs +++ b/src/Deftype.hs @@ -29,10 +29,10 @@ import Validate -- | This function creates a "Type Module" with the same name as the type being defined. -- A type module provides a namespace for all the functions that area automatically -- generated by a deftype. -moduleForDeftype :: Env -> TypeEnv -> Env -> [String] -> String -> [Ty] -> [XObj] -> Maybe Info -> Maybe Env -> Either TypeError (String, XObj, [XObj]) +moduleForDeftype :: Maybe Env -> TypeEnv -> Env -> [String] -> String -> [Ty] -> [XObj] -> Maybe Info -> Maybe Env -> Either TypeError (String, XObj, [XObj]) moduleForDeftype innerEnv typeEnv env pathStrings typeName typeVariables rest i existingEnv = let typeModuleName = typeName - typeModuleEnv = fromMaybe (Env (Map.fromList []) (Just innerEnv) (Just typeModuleName) [] ExternalEnv 0) existingEnv + typeModuleEnv = fromMaybe (Env (Map.fromList []) innerEnv (Just typeModuleName) [] ExternalEnv 0) existingEnv -- The variable 'insidePath' is the path used for all member functions inside the 'typeModule'. -- For example (module Vec2 [x Float]) creates bindings like Vec2.create, Vec2.x, etc. insidePath = pathStrings ++ [typeModuleName] diff --git a/src/Eval.hs b/src/Eval.hs index d001cfbe..70c645d6 100644 --- a/src/Eval.hs +++ b/src/Eval.hs @@ -455,11 +455,14 @@ macroExpand ctx xobj = ) XObj (Lst [XObj (Lst (XObj Macro _ _ : _)) _ _]) _ _ -> evalDynamic ctx xobj XObj (Lst (x@(XObj (Sym _ _) _ _) : args)) i t -> do - (_, f) <- evalDynamic ctx x + (next, f) <- evalDynamic ctx x case f of Right m@(XObj (Lst (XObj Macro _ _ : _)) _ _) -> do (newCtx', res) <- evalDynamic ctx (XObj (Lst (m : args)) i t) pure (newCtx', res) + -- TODO: Determine a way to eval primitives generally and remove this special case. + Right p@(XObj (Lst [(XObj (Primitive prim) _ _), (XObj (Sym (SymPath _ "defmodule") _) _ _), _]) _ _) -> + getPrimitive prim p next args _ -> do (newCtx, expanded) <- foldlM successiveExpand (ctx, Right []) args pure @@ -763,44 +766,54 @@ annotateWithinContext qualifyDefn ctx xobj = do Right ok -> pure (ctx, Right ok) primitiveDefmodule :: Primitive -primitiveDefmodule xobj ctx@(Context env i typeEnv pathStrings proj lastInput execMode history) (XObj (Sym (SymPath [] moduleName) _) _ _ : innerExpressions) = do - let defineIt :: MetaData -> IO (Context, Either EvalError XObj) - defineIt meta = do - let parentEnv = getEnv env pathStrings - innerEnv = Env (Map.fromList []) (Just parentEnv) (Just moduleName) [] ExternalEnv 0 - newModule = XObj (Mod innerEnv) (xobjInfo xobj) (Just ModuleTy) - globalEnvWithModuleAdded = envInsertAt env (SymPath pathStrings moduleName) (Binder meta newModule) - ctx' = Context globalEnvWithModuleAdded (Just (innerEnv {envParent = i})) typeEnv (pathStrings ++ [moduleName]) proj lastInput execMode history - (ctxAfterModuleDef, res) <- liftIO $ foldM step (ctx', dynamicNil) innerExpressions - pure (popModulePath ctxAfterModuleDef {contextInternalEnv = i}, res) - (newCtx, result) <- - case lookupBinder (SymPath pathStrings moduleName) env of - Just (Binder _ (XObj (Mod innerEnv) _ _)) -> do - let ctx' = Context env (Just innerEnv {envParent = i}) typeEnv (pathStrings ++ [moduleName]) proj lastInput execMode history -- TODO: use { = } syntax instead - (ctxAfterModuleAdditions, res) <- liftIO $ foldM step (ctx', dynamicNil) innerExpressions - pure (popModulePath ctxAfterModuleAdditions {contextInternalEnv = i}, res) -- TODO: propagate errors... - Just (Binder meta (XObj (Lst [XObj MetaStub _ _, _]) _ _)) -> - defineIt meta - Just (Binder _ _) -> - pure (evalError ctx ("Can't redefine '" ++ moduleName ++ "' as module") (xobjInfo xobj)) - Nothing -> - defineIt emptyMeta - pure $ case result of - Left err -> (newCtx, Left err) - Right _ -> (newCtx, dynamicNil) +primitiveDefmodule xobj ctx@(Context env i _ pathStrings _ _ _ _) (XObj (Sym (SymPath [] moduleName) _) _ _ : innerExpressions) = + do + -- N.B. The `envParent` rewrite at the end of this line is important! + -- lookups delve into parent envs by default, which is normally what we want, but in this case it leads to problems + -- when submodules happen to share a name with an existing module or type at the global level. + maybe (defineNewModule emptyMeta) updateExistingModule (lookupBinder (SymPath [] moduleName) ((getEnv env pathStrings) {envParent = Nothing})) + >>= defineModuleBindings + >>= \(newCtx, result) -> + case result of + Left err -> pure (newCtx, Left err) + Right _ -> pure (popModulePath (newCtx {contextInternalEnv = (join (fmap envParent (contextInternalEnv newCtx)))}), dynamicNil) where - step (ctx', r) x = - case r of - Left _ -> pure (ctx', r) - Right _ -> do - (newCtx, res) <- macroExpand ctx' x - case res of - Left err -> pure (newCtx, Left err) - Right e -> do - (newCtx', res') <- evalDynamic newCtx e - case res' of - Left err -> pure (newCtx', Left err) - Right _ -> pure (newCtx', r) + updateExistingModule :: Binder -> IO (Context, Either EvalError XObj) + updateExistingModule (Binder _ (XObj (Mod innerEnv) _ _)) = + let ctx' = + ctx + { contextInternalEnv = Just innerEnv {envParent = i}, + contextPath = ((contextPath ctx) ++ [moduleName]) + } + in (pure (ctx', dynamicNil)) + updateExistingModule (Binder meta (XObj (Lst [XObj MetaStub _ _, _]) _ _)) = + defineNewModule meta + updateExistingModule _ = + pure (evalError ctx ("Can't redefine '" ++ moduleName ++ "' as module") (xobjInfo xobj)) + + defineNewModule :: MetaData -> IO (Context, Either EvalError XObj) + defineNewModule meta = + pure (ctx', dynamicNil) + where + moduleEnv = Env (Map.fromList []) (Just (getEnv env pathStrings)) (Just moduleName) [] ExternalEnv 0 + newModule = XObj (Mod moduleEnv) (xobjInfo xobj) (Just ModuleTy) + updatedGlobalEnv = envInsertAt env (SymPath pathStrings moduleName) (Binder meta newModule) + -- The parent of the internal env needs to be set to i here for contextual `use` calls to work. + -- In theory this shouldn't be necessary; but for now it is. + ctx' = ctx {contextGlobalEnv = updatedGlobalEnv, contextInternalEnv = Just moduleEnv {envParent = i}, contextPath = ((contextPath ctx) ++ [moduleName])} + + defineModuleBindings :: (Context, Either EvalError XObj) -> IO (Context, Either EvalError XObj) + defineModuleBindings (context, Left e) = pure (context, Left e) + defineModuleBindings (context, _) = + foldM step (context, dynamicNil) innerExpressions + + step :: (Context, Either EvalError XObj) -> XObj -> IO (Context, Either EvalError XObj) + step (ctx', Left e) _ = pure (ctx', Left e) + step (ctx', Right _) expressions = + (macroExpand ctx' expressions) + >>= \(ctx'', res) -> case res of + Left _ -> pure (ctx'', res) + Right r -> evalDynamic ctx'' r primitiveDefmodule _ ctx (x : _) = pure (evalError ctx ("`defmodule` expects a symbol, got '" ++ pretty x ++ "' instead.") (xobjInfo x)) primitiveDefmodule _ ctx [] = @@ -1206,7 +1219,7 @@ primitiveEval _ ctx [val] = do case arg of Left err -> pure (newCtx, Left err) Right evald -> do - (newCtx', expanded) <- macroExpand ctx evald + (newCtx', expanded) <- macroExpand newCtx evald case expanded of Left err -> pure (newCtx', Left err) Right ok -> do diff --git a/src/Primitives.hs b/src/Primitives.hs index 21567497..ed92cd79 100644 --- a/src/Primitives.hs +++ b/src/Primitives.hs @@ -612,7 +612,7 @@ primitiveDeftype xobj ctx (name : rest) = deftype' nameXObj typeName typeVariableXObjs = do let pathStrings = contextPath ctx env = contextGlobalEnv ctx - innerEnv = fromMaybe env (contextInternalEnv ctx) + innerEnv = (contextInternalEnv ctx) typeEnv = contextTypeEnv ctx typeVariables = mapM xobjToTy typeVariableXObjs (preExistingModule, preExistingMeta) = @@ -629,6 +629,7 @@ primitiveDeftype xobj ctx (name : rest) = case creatorFunction innerEnv typeEnv env pathStrings tyName okTypeVariables rest i preExistingModule of Right (typeModuleName, typeModuleXObj, deps) -> let structTy = StructTy (ConcreteNameTy tyName) okTypeVariables + updatedGlobal = envInsertAt env (SymPath pathStrings typeModuleName) (Binder preExistingMeta typeModuleXObj) typeDefinition = -- NOTE: The type binding is needed to emit the type definition and all the member functions of the type. XObj @@ -642,7 +643,7 @@ primitiveDeftype xobj ctx (name : rest) = (Just TypeTy) ctx' = ( ctx - { contextGlobalEnv = envInsertAt env (SymPath pathStrings typeModuleName) (Binder preExistingMeta typeModuleXObj), + { contextGlobalEnv = updatedGlobal, contextTypeEnv = TypeEnv (extendEnv (getTypeEnv typeEnv) tyName typeDefinition) } ) diff --git a/src/Sumtypes.hs b/src/Sumtypes.hs index 72e32aa2..842a61bf 100644 --- a/src/Sumtypes.hs +++ b/src/Sumtypes.hs @@ -24,10 +24,10 @@ getCase cases caseNameToFind = found : _ -> Just found [] -> Nothing -moduleForSumtype :: Env -> TypeEnv -> Env -> [String] -> String -> [Ty] -> [XObj] -> Maybe Info -> Maybe Env -> Either TypeError (String, XObj, [XObj]) +moduleForSumtype :: Maybe Env -> TypeEnv -> Env -> [String] -> String -> [Ty] -> [XObj] -> Maybe Info -> Maybe Env -> Either TypeError (String, XObj, [XObj]) moduleForSumtype innerEnv typeEnv env pathStrings typeName typeVariables rest i existingEnv = let typeModuleName = typeName - typeModuleEnv = fromMaybe (Env (Map.fromList []) (Just innerEnv) (Just typeModuleName) [] ExternalEnv 0) existingEnv + typeModuleEnv = fromMaybe (Env (Map.fromList []) innerEnv (Just typeModuleName) [] ExternalEnv 0) existingEnv insidePath = pathStrings ++ [typeModuleName] in do let structTy = StructTy (ConcreteNameTy typeName) typeVariables diff --git a/test/vector2.carp b/test/vector2.carp index 2e645318..cd097956 100644 --- a/test/vector2.carp +++ b/test/vector2.carp @@ -14,7 +14,7 @@ (assert-op test &(init 1.0 2.0) &(init 1.0 1.0) "/= operator works" - Vector2./=) + /=) (assert-equal test &(init 3.0 3.0) &(add &(init 2.0 1.0) &(init 1.0 2.0)) diff --git a/test/vector3.carp b/test/vector3.carp index 3fa1b1f6..5bc95993 100644 --- a/test/vector3.carp +++ b/test/vector3.carp @@ -14,7 +14,7 @@ (assert-op test &(init 1.0 2.0 3.0) &(init 1.0 1.0 3.0) "/= operator works" - Vector3./=) + /=) (assert-op test &(init 1.0 2.0 3.0) &(init 1.000001 2.000001 3.000001) "approx works" diff --git a/test/vectorn.carp b/test/vectorn.carp index 844d564c..42b110c3 100644 --- a/test/vectorn.carp +++ b/test/vectorn.carp @@ -16,7 +16,7 @@ (assert-op test &(init 4 [1.0 2.0 3.0 4.0]) &(init 4 [1.0 1.0 3.0 4.0]) "/= operator works" - VectorN./=) + /=) (assert-equal test &(init 3 [3.0 3.0 4.5]) &(Maybe.unsafe-from