1
1
mirror of https://github.com/github/semantic.git synced 2024-12-22 06:11:49 +03:00

State holds projected nodes.

This commit is contained in:
Rob Rix 2017-08-16 11:13:11 -04:00
parent 61281b8b65
commit d1011231b9
2 changed files with 18 additions and 16 deletions

View File

@ -1,4 +1,4 @@
{-# LANGUAGE DataKinds, GADTs, InstanceSigs, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TypeFamilies, TypeOperators #-} {-# LANGUAGE DataKinds, GADTs, InstanceSigs, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, StandaloneDeriving, TypeFamilies, TypeOperators, UndecidableInstances #-}
-- | Assignment of AST onto some other structure (typically terms). -- | Assignment of AST onto some other structure (typically terms).
-- --
-- Parsing yields an AST represented as a Rose tree labelled with symbols in the languages grammar and source locations (byte Range and Span). An Assignment represents a (partial) map from AST nodes onto some other structure; in essence, its a parser that operates over trees. (For our purposes, this structure is typically Terms annotated with source locations.) Assignments are able to match based on symbol, sequence, and hierarchy; thus, in @x = y@, both @x@ and @y@ might have the same symbol, @Identifier@, the left can be assigned to a variable declaration, while the right can be assigned to a variable reference. -- Parsing yields an AST represented as a Rose tree labelled with symbols in the languages grammar and source locations (byte Range and Span). An Assignment represents a (partial) map from AST nodes onto some other structure; in essence, its a parser that operates over trees. (For our purposes, this structure is typically Terms annotated with source locations.) Assignments are able to match based on symbol, sequence, and hierarchy; thus, in @x = y@, both @x@ and @y@ might have the same symbol, @Identifier@, the left can be assigned to a variable declaration, while the right can be assigned to a variable reference.
@ -118,7 +118,7 @@ import Data.Semigroup
import qualified Data.Source as Source (Source, slice, sourceBytes) import qualified Data.Source as Source (Source, slice, sourceBytes)
import GHC.Stack import GHC.Stack
import qualified Info import qualified Info
import Prelude hiding (head, until) import Prelude hiding (until)
import Text.Parser.Combinators as Parsers import Text.Parser.Combinators as Parsers
import TreeSitter.Language import TreeSitter.Language
@ -223,7 +223,7 @@ firstSet = iterFreer (\ assignment _ -> case assignment of
-- | Run an assignment over an AST exhaustively. -- | Run an assignment over an AST exhaustively.
assignBy :: (Bounded grammar, Ix grammar, Symbol grammar, Show grammar, Eq ast, F.Recursive ast, Foldable (F.Base ast)) assignBy :: (Bounded grammar, Ix grammar, Symbol grammar, Show grammar, Eq (F.Base ast ast), F.Recursive ast, Foldable (F.Base ast))
=> (F.Base ast () -> Node grammar) -- ^ A function to project a 'Node' from the ast. => (F.Base ast () -> Node grammar) -- ^ A function to project a 'Node' from the ast.
-> Source.Source -- ^ The source for the parse tree. -> Source.Source -- ^ The source for the parse tree.
-> Assignment ast grammar a -- ^ The 'Assignment to run. -> Assignment ast grammar a -- ^ The 'Assignment to run.
@ -233,7 +233,7 @@ assignBy toNode source assignment ast = bimap (fmap (either id show)) fst (runAs
{-# INLINE assignBy #-} {-# INLINE assignBy #-}
-- | Run an assignment of nodes in a grammar onto terms in a syntax over an AST exhaustively. -- | Run an assignment of nodes in a grammar onto terms in a syntax over an AST exhaustively.
runAssignment :: forall grammar a ast. (Bounded grammar, Ix grammar, Symbol grammar, Eq ast, F.Recursive ast, Foldable (F.Base ast)) runAssignment :: forall grammar a ast. (Bounded grammar, Ix grammar, Symbol grammar, Eq (F.Base ast ast), F.Recursive ast, Foldable (F.Base ast))
=> (F.Base ast () -> Node grammar) -- ^ A function to project a 'Node' from the ast. => (F.Base ast () -> Node grammar) -- ^ A function to project a 'Node' from the ast.
-> Source.Source -- ^ The source for the parse tree. -> Source.Source -- ^ The source for the parse tree.
-> Assignment ast grammar a -- ^ The 'Assignment' to run. -> Assignment ast grammar a -- ^ The 'Assignment' to run.
@ -249,13 +249,13 @@ runAssignment toNode source = \ assignment state -> go assignment state >>= requ
-> (x -> State ast -> Either (Error (Either String grammar)) (result, State ast)) -> (x -> State ast -> Either (Error (Either String grammar)) (result, State ast))
-> State ast -> State ast
-> Either (Error (Either String grammar)) (result, State ast) -> Either (Error (Either String grammar)) (result, State ast)
run assignment yield initialState = assignment `seq` expectedSymbols `seq` state `seq` maybe (anywhere Nothing) (atNode . F.project) (listToMaybe stateNodes) run assignment yield initialState = assignment `seq` expectedSymbols `seq` state `seq` maybe (anywhere Nothing) atNode (listToMaybe stateNodes)
where atNode node = case assignment of where atNode node = case assignment of
Location -> yield (nodeLocation (toNode (() <$ node))) state Location -> yield (nodeLocation (toNode (() <$ node))) state
CurrentNode -> yield (() <$ node) state CurrentNode -> yield (() <$ node) state
Source -> yield (Source.sourceBytes (Source.slice (nodeByteRange (toNode (() <$ node))) source)) (advance state) Source -> yield (Source.sourceBytes (Source.slice (nodeByteRange (toNode (() <$ node))) source)) (advance state)
Children child -> do Children child -> do
(a, state') <- go child state { stateNodes = toList node } >>= requireExhaustive (a, state') <- go child state { stateNodes = F.project <$> toList node } >>= requireExhaustive
yield a (advance state' { stateNodes = stateNodes }) yield a (advance state' { stateNodes = stateNodes })
Advance -> yield () (advance state) Advance -> yield () (advance state)
Choose _ choices | Just choice <- IntMap.lookup (toIndex (nodeSymbol (toNode (() <$ node)))) choices -> yield choice state Choose _ choices | Just choice <- IntMap.lookup (toIndex (nodeSymbol (toNode (() <$ node)))) choices -> yield choice state
@ -286,26 +286,28 @@ runAssignment toNode source = \ assignment state -> go assignment state >>= requ
requireExhaustive :: HasCallStack => (result, State ast) -> Either (Error (Either String grammar)) (result, State ast) requireExhaustive :: HasCallStack => (result, State ast) -> Either (Error (Either String grammar)) (result, State ast)
requireExhaustive (a, state) = let state' = dropAnonymous state in case stateNodes state' of requireExhaustive (a, state) = let state' = dropAnonymous state in case stateNodes state' of
[] -> Right (a, state') [] -> Right (a, state')
node : _ -> Left (nodeError [] (toNode (() <$ F.project node))) node : _ -> Left (nodeError [] (toNode (() <$ node)))
dropAnonymous state = state { stateNodes = dropWhile ((/= Regular) . symbolType . nodeSymbol . toNode . fmap (const ()) . F.project) (stateNodes state) } dropAnonymous state = state { stateNodes = dropWhile ((/= Regular) . symbolType . nodeSymbol . toNode . fmap (const ())) (stateNodes state) }
-- Advances the state past the current (head) node (if any), dropping it off stateNodes, and updating stateOffset & statePos to its end; or else returns the state unchanged. -- Advances the state past the current (head) node (if any), dropping it off stateNodes, and updating stateOffset & statePos to its end; or else returns the state unchanged.
advance state@State{..} advance state@State{..}
| node : rest <- stateNodes | node : rest <- stateNodes
, Node{..} <- toNode (() <$ F.project node) = State (Info.end nodeByteRange) (Info.spanEnd nodeSpan) rest , Node{..} <- toNode (() <$ node) = State (Info.end nodeByteRange) (Info.spanEnd nodeSpan) rest
| otherwise = state | otherwise = state
-- | State kept while running 'Assignment's. -- | State kept while running 'Assignment's.
data State ast = State data State ast = State
{ stateOffset :: {-# UNPACK #-} !Int -- ^ The offset into the Source thus far reached, measured in bytes. { stateOffset :: {-# UNPACK #-} !Int -- ^ The offset into the Source thus far reached, measured in bytes.
, statePos :: {-# UNPACK #-} !Info.Pos -- ^ The (1-indexed) line/column position in the Source thus far reached. , statePos :: {-# UNPACK #-} !Info.Pos -- ^ The (1-indexed) line/column position in the Source thus far reached.
, stateNodes :: ![ast] -- ^ The remaining nodes to assign. Note that 'children' rules recur into subterms, and thus this does not necessarily reflect all of the terms remaining to be assigned in the overall algorithm, only those “in scope.” , stateNodes :: ![F.Base ast ast] -- ^ The remaining nodes to assign. Note that 'children' rules recur into subterms, and thus this does not necessarily reflect all of the terms remaining to be assigned in the overall algorithm, only those “in scope.”
} }
deriving (Eq, Show)
makeState :: [ast] -> State ast deriving instance (Eq (F.Base ast ast)) => Eq (State ast)
makeState = State 0 (Info.Pos 1 1) deriving instance (Show (F.Base ast ast)) => Show (State ast)
makeState :: F.Recursive ast => [ast] -> State ast
makeState = State 0 (Info.Pos 1 1) . fmap F.project
-- Instances -- Instances
@ -351,7 +353,7 @@ instance Eq grammar => Alternative (Assignment ast grammar) where
many :: HasCallStack => Assignment ast grammar a -> Assignment ast grammar [a] many :: HasCallStack => Assignment ast grammar a -> Assignment ast grammar [a]
many a = Many a `Then` return many a = Many a `Then` return
instance (Eq grammar, Show grammar, Show ast) => Parsing (Assignment ast grammar) where instance (Eq grammar, Show grammar, Show (F.Base ast ast)) => Parsing (Assignment ast grammar) where
try :: HasCallStack => Assignment ast grammar a -> Assignment ast grammar a try :: HasCallStack => Assignment ast grammar a -> Assignment ast grammar a
try = id try = id
@ -378,7 +380,7 @@ instance MonadState (State ast) (Assignment ast grammar) where
get = Get `Then` return get = Get `Then` return
put s = Put s `Then` return put s = Put s `Then` return
instance (Show grammar, Show ast) => Show1 (AssignmentF ast grammar) where instance (Show grammar, Show (F.Base ast ast)) => Show1 (AssignmentF ast grammar) where
liftShowsPrec sp sl d a = case a of liftShowsPrec sp sl d a = case a of
Get -> showString "Get" Get -> showString "Get"
Put s -> showsUnaryWith showsPrec "Put" d s Put s -> showsUnaryWith showsPrec "Put" d s

View File

@ -42,7 +42,7 @@ data Parser term where
-- | A parser producing 'AST' using a 'TS.Language'. -- | A parser producing 'AST' using a 'TS.Language'.
ASTParser :: (Bounded grammar, Enum grammar) => Ptr TS.Language -> Parser (AST grammar) ASTParser :: (Bounded grammar, Enum grammar) => Ptr TS.Language -> Parser (AST grammar)
-- | A parser producing an à la carte term given an 'AST'-producing parser and an 'Assignment' onto 'Term's in some syntax type. -- | A parser producing an à la carte term given an 'AST'-producing parser and an 'Assignment' onto 'Term's in some syntax type.
AssignmentParser :: (Bounded grammar, Ix grammar, Show grammar, TS.Symbol grammar, Syntax.Error :< fs, Apply1 Foldable fs, Apply1 Functor fs, Eq ast, Recursive ast, Foldable (Base ast)) AssignmentParser :: (Bounded grammar, Ix grammar, Show grammar, TS.Symbol grammar, Syntax.Error :< fs, Apply1 Foldable fs, Apply1 Functor fs, Eq (Base ast ast), Recursive ast, Foldable (Base ast))
=> Parser ast -- ^ A parser producing AST. => Parser ast -- ^ A parser producing AST.
-> (forall x. Base ast x -> Node grammar) -- ^ A function extracting the symbol and location. -> (forall x. Base ast x -> Node grammar) -- ^ A function extracting the symbol and location.
-> Assignment ast grammar (Term (Union fs) (Record Location)) -- ^ An assignment from AST onto 'Term's. -> Assignment ast grammar (Term (Union fs) (Record Location)) -- ^ An assignment from AST onto 'Term's.