packages feed

tala-frontend-0.1.0: src/Progmasoft/Tala/Resolver/NameResolution.hs

-- SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com>
-- SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-1.1

{- | Convert renamed identifiers into stable symbol identities and diagnose
unresolved or reserved names before type checking.
-}
module Progmasoft.Tala.Resolver.NameResolution (NameResolution (..), defaultNameResolution, runNameResolution) where

import Progmasoft.Tala.AST
import Progmasoft.Tala.Diagnostic
import Progmasoft.Tala.RuntimeCall (builtinConsoleSymbol, builtinSystemSymbol)

-- | Pluggable name-resolution pass over a fully renamed syntax tree.
newtype NameResolution = NameResolution
    { resolveRenamedAST :: RenamedAST -> Either [Diagnostic] ResolvedAST
    -- ^ Resolve every identifier or return all name-resolution diagnostics.
    }

-- | Execute a supplied name-resolution implementation.
runNameResolution :: NameResolution -> RenamedAST -> Either [Diagnostic] ResolvedAST
runNameResolution = resolveRenamedAST

-- | Default compiler name resolver using the AST's assigned symbol identities.
defaultNameResolution :: NameResolution
defaultNameResolution = NameResolution resolveTree

resolveTree :: RenamedAST -> Either [Diagnostic] ResolvedAST
resolveTree (RenamedAST tree) = case traverseTree tree of
    (resolved, []) -> Right (ResolvedAST resolved)
    (_, problems) -> Left problems

traverseTree :: SyntaxTree RenamedName () -> (SyntaxTree ResolvedName (), [Diagnostic])
traverseTree (SyntaxTree namespace declarations) =
    let values = map resolveDeclaration declarations in (SyntaxTree namespace (map fst values), concatMap snd values)

resolveDeclaration :: Declaration RenamedName () -> (Declaration ResolvedName (), [Diagnostic])
resolveDeclaration declaration = case declaration of
    TypeDeclaration spanValue sourceName _ members ->
        let name = resolveName spanValue sourceName; resolvedMembers = map resolveDeclaration members
         in (TypeDeclaration spanValue (fst name) () (map fst resolvedMembers), snd name ++ concatMap snd resolvedMembers)
    TemplateTypeDeclaration spanValue sourceName _ sourceParameters members ->
        let name = resolveName spanValue sourceName
            parameters = map resolveTemplateParameter sourceParameters
            resolvedMembers = map resolveDeclaration members
         in ( TemplateTypeDeclaration
                spanValue
                (fst name)
                ()
                (map fst parameters)
                (map fst resolvedMembers)
            , snd name ++ concatMap snd parameters ++ concatMap snd resolvedMembers
            )
    EnumDeclaration spanValue sourceName _ underlying cases ->
        let name = resolveName spanValue sourceName
         in (EnumDeclaration spanValue (fst name) () underlying cases, snd name)
    FunctionDeclaration spanValue sourceName _ returnSyntax sourceParameters sourceBody isStatic access ->
        let name = resolveName spanValue sourceName
            parameters = map resolveParameter sourceParameters
            (body, bodyProblems) = resolveBlock sourceBody
         in ( FunctionDeclaration spanValue (fst name) () returnSyntax (map fst parameters) body isStatic access
            , snd name ++ concatMap snd parameters ++ bodyProblems
            )

resolveParameter :: Parameter RenamedName () -> (Parameter ResolvedName (), [Diagnostic])
resolveParameter (Parameter spanValue name _ syntax) = let (resolved, problems) = resolveName spanValue name in (Parameter spanValue resolved () syntax, problems)

resolveTemplateParameter ::
    TemplateParameter RenamedName () ->
    (TemplateParameter ResolvedName (), [Diagnostic])
resolveTemplateParameter parameter =
    let (resolved, problems) = resolveName (templateParameterSpan parameter) (templateParameterName parameter)
     in ( TemplateParameter
            (templateParameterSpan parameter)
            resolved
            ()
            (templateParameterKind parameter)
            (templateParameterIsPack parameter)
            (templateParameterDefault parameter)
        , problems
        )

resolveBlock :: Block RenamedName () -> (Block ResolvedName (), [Diagnostic])
resolveBlock (Block statements) = let values = map resolveStatement statements in (Block (map fst values), concatMap snd values)

resolveStatement :: Statement RenamedName () -> (Statement ResolvedName (), [Diagnostic])
resolveStatement statement = case statement of
    BindingStatement spanValue kind syntax name _ value ->
        let (resolvedName, nameProblems) = resolveName spanValue name; (resolvedValue, valueProblems) = resolveExpression value
         in (BindingStatement spanValue kind syntax resolvedName () resolvedValue, nameProblems ++ valueProblems)
    AssignmentStatement spanValue name _ value ->
        let (resolvedName, nameProblems) = resolveName spanValue name; (resolvedValue, valueProblems) = resolveExpression value
         in (AssignmentStatement spanValue resolvedName () resolvedValue, nameProblems ++ valueProblems)
    ReturnStatement spanValue value -> let (resolved, problems) = resolveOptional value in (ReturnStatement spanValue resolved, problems)
    IfStatement spanValue condition trueBlock falseBlock ->
        let (resolvedCondition, conditionProblems) = resolveExpression condition
            (resolvedTrue, trueProblems) = resolveBlock trueBlock
            (resolvedFalse, falseProblems) = case falseBlock of
                Nothing -> (Nothing, [])
                Just value -> let (block, problems) = resolveBlock value in (Just block, problems)
         in (IfStatement spanValue resolvedCondition resolvedTrue resolvedFalse, conditionProblems ++ trueProblems ++ falseProblems)
    WhileStatement spanValue condition body ->
        let (resolvedCondition, conditionProblems) = resolveExpression condition
            (resolvedBody, bodyProblems) = resolveBlock body
         in (WhileStatement spanValue resolvedCondition resolvedBody, conditionProblems ++ bodyProblems)
    DoWhileStatement spanValue body condition ->
        let (resolvedBody, bodyProblems) = resolveBlock body
            (resolvedCondition, conditionProblems) = resolveExpression condition
         in (DoWhileStatement spanValue resolvedBody resolvedCondition, bodyProblems ++ conditionProblems)
    ForStatement spanValue initializer condition updates body ->
        let (resolvedInitializer, initializerProblems) = case initializer of
                Nothing -> (Nothing, [])
                Just value -> let (resolved, problems) = resolveStatement value in (Just resolved, problems)
            (resolvedCondition, conditionProblems) = resolveOptional condition
            resolvedUpdates = map resolveStatement updates
            (resolvedBody, bodyProblems) = resolveBlock body
         in ( ForStatement spanValue resolvedInitializer resolvedCondition (map fst resolvedUpdates) resolvedBody
            , initializerProblems ++ conditionProblems ++ concatMap snd resolvedUpdates ++ bodyProblems
            )
    ForEachStatement spanValue kind syntax name annotation source body ->
        let (resolvedName, nameProblems) = resolveName spanValue name
            (resolvedSource, sourceProblems) = resolveExpression source
            (resolvedBody, bodyProblems) = resolveBlock body
         in ( ForEachStatement spanValue kind syntax resolvedName annotation resolvedSource resolvedBody
            , nameProblems ++ sourceProblems ++ bodyProblems
            )
    IncrementStatement spanValue name annotation ->
        let (resolvedName, problems) = resolveName spanValue name
         in (IncrementStatement spanValue resolvedName annotation, problems)
    CompoundAssignmentStatement spanValue operator name _ value ->
        let (resolvedName, nameProblems) = resolveName spanValue name
            (resolvedValue, valueProblems) = resolveExpression value
         in (CompoundAssignmentStatement spanValue operator resolvedName () resolvedValue, nameProblems ++ valueProblems)
    DiscardStatement spanValue value ->
        let (resolved, problems) = resolveExpression value in (DiscardStatement spanValue resolved, problems)
    BreakStatement spanValue value ->
        let (resolvedValue, problems) = resolveOptional value
         in (BreakStatement spanValue resolvedValue, problems)
    ContinueStatement spanValue -> (ContinueStatement spanValue, [])
    GuardStatement spanValue condition block ->
        let (resolvedCondition, conditionProblems) = resolveExpression condition
            (resolvedBlock, blockProblems) = resolveBlock block
         in (GuardStatement spanValue resolvedCondition resolvedBlock, conditionProblems ++ blockProblems)
    BlockStatement spanValue block ->
        let (resolvedBlock, problems) = resolveBlock block
         in (BlockStatement spanValue resolvedBlock, problems)
    ExpressionStatement spanValue value terminated -> let (resolved, problems) = resolveExpression value in (ExpressionStatement spanValue resolved terminated, problems)

resolveOptional :: Maybe (Expression RenamedName ()) -> (Maybe (Expression ResolvedName ()), [Diagnostic])
resolveOptional Nothing = (Nothing, [])
resolveOptional (Just value) = let (resolved, problems) = resolveExpression value in (Just resolved, problems)

resolveExpression :: Expression RenamedName () -> (Expression ResolvedName (), [Diagnostic])
resolveExpression expression = case expression of
    NameExpression spanValue name _ -> let (resolved, problems) = resolveName spanValue name in (NameExpression spanValue resolved (), problems)
    LiteralExpression spanValue literal _ -> (LiteralExpression spanValue literal (), [])
    MemberAccessExpression spanValue receiver member _ ->
        let (resolvedReceiver, problems) = resolveExpression receiver
         in (MemberAccessExpression spanValue resolvedReceiver member (), problems)
    MethodReferenceExpression spanValue receiver member _ ->
        let (resolvedReceiver, problems) = resolveExpression receiver
         in (MethodReferenceExpression spanValue resolvedReceiver member (), problems)
    CallExpression spanValue callee arguments _ ->
        let (resolvedCallee, firstProblems) = resolveExpression callee; values = map resolveExpression arguments
         in (CallExpression spanValue resolvedCallee (map fst values) (), firstProblems ++ concatMap snd values)
    UnaryExpression spanValue operator value _ -> let (resolved, problems) = resolveExpression value in (UnaryExpression spanValue operator resolved (), problems)
    BinaryExpression spanValue operator left right _ ->
        let (resolvedLeft, leftProblems) = resolveExpression left; (resolvedRight, rightProblems) = resolveExpression right
         in (BinaryExpression spanValue operator resolvedLeft resolvedRight (), leftProblems ++ rightProblems)
    IsPatternExpression spanValue subject patternValue _ ->
        let (resolvedSubject, subjectProblems) = resolveExpression subject
            (resolvedPattern, patternProblems) = resolvePattern patternValue
         in (IsPatternExpression spanValue resolvedSubject resolvedPattern (), subjectProblems ++ patternProblems)
    ConditionalExpression spanValue condition first second _ ->
        let (resolvedCondition, conditionProblems) = resolveExpression condition
            (resolvedFirst, firstProblems) = resolveExpression first
            (resolvedSecond, secondProblems) = resolveExpression second
         in ( ConditionalExpression spanValue resolvedCondition resolvedFirst resolvedSecond ()
            , conditionProblems ++ firstProblems ++ secondProblems
            )
    CoalesceExpression spanValue left fallback _ ->
        let (resolvedLeft, leftProblems) = resolveExpression left
            (resolvedFallback, fallbackProblems) = resolveExpression fallback
         in (CoalesceExpression spanValue resolvedLeft resolvedFallback (), leftProblems ++ fallbackProblems)
    AssignmentExpression spanValue operator name value _ ->
        let (resolvedName, nameProblems) = resolveName spanValue name
            (resolvedValue, valueProblems) = resolveExpression value
         in (AssignmentExpression spanValue operator resolvedName resolvedValue (), nameProblems ++ valueProblems)
    IncrementExpression spanValue isPrefix name _ ->
        let (resolvedName, problems) = resolveName spanValue name
         in (IncrementExpression spanValue isPrefix resolvedName (), problems)
    LoopExpression spanValue loop _ ->
        let (resolvedLoop, problems) = resolveStatement loop
         in (LoopExpression spanValue resolvedLoop (), problems)
    BlockExpression spanValue block _ ->
        let (resolvedBlock, problems) = resolveBlock block
         in (BlockExpression spanValue resolvedBlock (), problems)
    MatchExpression spanValue subjects arms _ ->
        let resolvedSubjects = map resolveExpression subjects
            resolvedArms = map resolveMatchArm arms
         in ( MatchExpression spanValue (map fst resolvedSubjects) (map fst resolvedArms) ()
            , concatMap snd resolvedSubjects ++ concatMap snd resolvedArms
            )
    CallableExpression spanValue explicit captures parameters body _ ->
        let resolvedCaptures = map resolveCapture captures
            resolvedParameters = map resolveParameter parameters
            (resolvedBody, bodyProblems) = resolveCallableBody body
         in ( CallableExpression
                spanValue
                explicit
                (map fst resolvedCaptures)
                (map fst resolvedParameters)
                resolvedBody
                ()
            , concatMap snd resolvedCaptures ++ concatMap snd resolvedParameters ++ bodyProblems
            )

resolveMatchArm :: MatchArm RenamedName () -> (MatchArm ResolvedName (), [Diagnostic])
resolveMatchArm (MatchArm spanValue patterns guard body) =
    let resolvedPatterns = map resolveMatchPattern patterns
        (resolvedGuard, guardProblems) = resolveOptional guard
        (resolvedBody, bodyProblems) = resolveExpression body
     in ( MatchArm spanValue (map fst resolvedPatterns) resolvedGuard resolvedBody
        , concatMap snd resolvedPatterns ++ guardProblems ++ bodyProblems
        )

resolveMatchPattern :: MatchPattern RenamedName () -> (MatchPattern ResolvedName (), [Diagnostic])
resolveMatchPattern patternValue = case patternValue of
    MatchWildcardPattern spanValue _ -> (MatchWildcardPattern spanValue (), [])
    MatchLiteralPattern spanValue literal _ -> (MatchLiteralPattern spanValue literal (), [])
    MatchNullPattern spanValue _ -> (MatchNullPattern spanValue (), [])
    MatchCasePattern spanValue name _ -> (MatchCasePattern spanValue name (), [])
    MatchTypePattern spanValue syntax Nothing _ -> (MatchTypePattern spanValue syntax Nothing (), [])
    MatchTypePattern spanValue syntax (Just name) _ ->
        let (resolvedName, problems) = resolveName spanValue name
         in (MatchTypePattern spanValue syntax (Just resolvedName) (), problems)

resolvePattern :: Pattern RenamedName () -> (Pattern ResolvedName (), [Diagnostic])
resolvePattern patternValue = case patternValue of
    WildcardPattern spanValue _ -> (WildcardPattern spanValue (), [])
    NullPattern spanValue _ -> (NullPattern spanValue (), [])
    LiteralPattern spanValue literal _ -> (LiteralPattern spanValue literal (), [])
    TypePattern spanValue syntax _ -> (TypePattern spanValue syntax (), [])
    RelationalPattern spanValue operator literal _ -> (RelationalPattern spanValue operator literal (), [])
    NotPattern spanValue nested _ ->
        let (resolved, problems) = resolvePattern nested
         in (NotPattern spanValue resolved (), problems)
    AndPattern spanValue left right _ -> resolvePair AndPattern spanValue left right
    OrPattern spanValue left right _ -> resolvePair OrPattern spanValue left right
    where
        resolvePair constructor spanValue left right =
            let (resolvedLeft, leftProblems) = resolvePattern left
                (resolvedRight, rightProblems) = resolvePattern right
             in (constructor spanValue resolvedLeft resolvedRight (), leftProblems ++ rightProblems)

resolveCapture :: Capture RenamedName () -> (Capture ResolvedName (), [Diagnostic])
resolveCapture (Capture spanValue mode name _ initializer) =
    let (resolvedName, nameProblems) = resolveName spanValue name
        (resolvedInitializer, initializerProblems) = resolveOptional initializer
     in ( Capture spanValue mode resolvedName () resolvedInitializer
        , nameProblems ++ initializerProblems
        )

resolveCallableBody ::
    CallableBody RenamedName () ->
    (CallableBody ResolvedName (), [Diagnostic])
resolveCallableBody body = case body of
    CallableExpressionBody expression ->
        let (resolved, problems) = resolveExpression expression
         in (CallableExpressionBody resolved, problems)
    CallableBlockBody block ->
        let (resolved, problems) = resolveBlock block
         in (CallableBlockBody resolved, problems)

resolveName :: SourceSpan -> RenamedName -> (ResolvedName, [Diagnostic])
resolveName spanValue name
    | renamedUnique name > 0 = (ResolvedName (SymbolId (renamedUnique name)) (renamedSpelling name), [])
    -- The names the language declares for every program.
    | SymbolId (renamedUnique name) `elem` [builtinSystemSymbol, builtinConsoleSymbol] =
        (ResolvedName (SymbolId (renamedUnique name)) (renamedSpelling name), [])
    | renamedUnique name == 0 =
        ( ResolvedName (SymbolId 0) (renamedSpelling name)
        ,
            [ Diagnostic
                NameResolutionStage
                Error
                "TAN0002"
                (Just spanValue)
                "reserved symbol id zero reached name resolution"
            ]
        )
    | otherwise =
        ( ResolvedName (SymbolId (-1)) (renamedSpelling name)
        ,
            [ Diagnostic
                NameResolutionStage
                Error
                "TAN0001"
                (Just spanValue)
                ("unknown name " ++ identifierText (renamedSpelling name))
            ]
        )