Skip to content
This repository was archived by the owner on Apr 1, 2025. It is now read-only.

Commit 7bd2ac2

Browse files
authored
Merge pull request #716 from github/this-branch-name-left-intentionally-blank
Ingest Python
2 parents d7f9e09 + 6700d99 commit 7bd2ac2

File tree

14 files changed

+102
-61
lines changed

14 files changed

+102
-61
lines changed

semantic-analysis/semantic-analysis.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ library
7878
, filepath
7979
, fused-effects ^>= 1.1
8080
, hashable
81+
, language-python
8182
, semantic-source ^>= 0.2
8283
, text ^>= 1.2.3.1
8384
, transformers ^>= 0.5
Lines changed: 89 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,122 @@
1-
{-# LANGUAGE DeriveTraversable #-}
21
{-# LANGUAGE FlexibleContexts #-}
32
{-# LANGUAGE LambdaCase #-}
43
-- | This belongs in @semantic-python@ instead of @semantic-analysis@, but for the sake of expedience…
54
module Analysis.Syntax.Python
65
( -- * Syntax
7-
Term
8-
, Python(..)
6+
Term(..)
7+
, subterms
98
-- * Abstract interpretation
109
, eval0
1110
, eval
11+
-- * Parsing
12+
, parse
1213
) where
1314

1415
import Analysis.Effect.Domain hiding ((:>>>))
1516
import qualified Analysis.Effect.Statement as S
1617
import Analysis.Name
1718
import Analysis.Reference
18-
import qualified Analysis.Syntax as T
1919
import Analysis.VM
2020
import Control.Effect.Labelled
2121
import Control.Effect.Reader
22+
import Control.Monad (foldM)
23+
import Data.Foldable (for_)
2224
import Data.Function (fix)
23-
import Data.List.NonEmpty (NonEmpty)
24-
import Data.Text (Text)
25-
import Source.Span (Span)
25+
import Data.List.NonEmpty (nonEmpty)
26+
import Data.Maybe (mapMaybe)
27+
import qualified Data.Set as Set
28+
import Data.Text (pack)
29+
import qualified Language.Python.Common as Py
30+
import Language.Python.Version3.Parser
31+
import Source.Span (Pos (..), Span (..), point)
32+
import System.FilePath (takeBaseName)
2633

2734
-- Syntax
2835

29-
type Term = T.Term Python Name
36+
data Term
37+
= Module (Py.Module Py.SrcSpan)
38+
| Statement (Py.Statement Py.SrcSpan)
39+
| Expr (Py.Expr Py.SrcSpan)
40+
deriving (Eq, Ord, Show)
3041

31-
data Python t
32-
= Noop
33-
| Iff t t t
34-
| Bool Bool
35-
| String Text
36-
| Throw t
37-
| Let Name t t
38-
| t :>> t
39-
| Import (NonEmpty Text)
40-
| Function Name [Name] t
41-
| Call t [t]
42-
| Locate Span t
43-
deriving (Eq, Foldable, Functor, Ord, Show, Traversable)
44-
45-
infixl 1 :>>
42+
-- | Non-generic production of the recursive set of subterms.
43+
--
44+
-- This should be exactly the set of nodes which 'eval' can visit, i.e. it excludes comments, etc.
45+
subterms :: Term -> Set.Set Term
46+
subterms t = Set.insert t $ case t of
47+
Module (Py.Module ss) -> suite ss
48+
Statement (Py.Conditional cts e _) -> foldMap (\ (c, t) -> subterms (Expr c) <> suite t) cts <> suite e
49+
Statement (Py.Raise (Py.RaiseV3 e) _) -> maybe Set.empty (subterms . Expr . fst) e
50+
-- FIXME: Py.RaiseV2
51+
-- FIXME: whatever the second field is
52+
Statement (Py.StmtExpr e _) -> subterms (Expr e)
53+
Statement (Py.Fun _ _ _ ss _) -> suite ss
54+
-- FIXME: include 'subterms' of any default values
55+
Expr (Py.Call f as _) -> subterms (Expr f) <> foldMap (\case { Py.ArgExpr e _ -> subterms (Expr e) ; _ -> Set.empty }) as
56+
-- FIXME: support keyword args &c.
57+
_ -> Set.empty -- TBD, and terminals
58+
where
59+
suite = foldMap (subterms . Statement)
4660

4761

4862
-- Abstract interpretation
4963

50-
eval0 :: (Has (Env addr) sig m, HasLabelled Store (Store addr val) sig m, Has (Dom val) sig m, Has (Reader Reference) sig m, Has S.Statement sig m) => Term -> m val
64+
eval0 :: (Has (Env addr) sig m, HasLabelled Store (Store addr val) sig m, Has (Dom val) sig m, Has (Reader Reference) sig m, Has S.Statement sig m, MonadFail m) => Term -> m val
5165
eval0 = fix eval
5266

5367
eval
54-
:: (Has (Env addr) sig m, HasLabelled Store (Store addr val) sig m, Has (Dom val) sig m, Has (Reader Reference) sig m, Has S.Statement sig m)
68+
:: (Has (Env addr) sig m, HasLabelled Store (Store addr val) sig m, Has (Dom val) sig m, Has (Reader Reference) sig m, Has S.Statement sig m, MonadFail m)
5569
=> (Term -> m val)
5670
-> (Term -> m val)
5771
eval eval = \case
58-
T.Var n -> lookupEnv n >>= maybe (dvar n) fetch
59-
T.Term s -> case s of
60-
Noop -> dunit
61-
Iff c t e -> do
62-
c' <- eval c
63-
dif c' (eval t) (eval e)
64-
Bool b -> dbool b
65-
String s -> dstring s
66-
Throw e -> eval e >>= ddie
67-
Let n v b -> do
68-
v' <- eval v
69-
let' n v' (eval b)
70-
t :>> u -> do
71-
t' <- eval t
72-
u' <- eval u
73-
t' >>> u'
74-
Import ns -> S.simport ns >> dunit
75-
Function n ps b -> letrec n (dabs ps (foldr (\ (p, a) m -> let' p a m) (eval b) . zip ps))
76-
Call f as -> do
77-
f' <- eval f
78-
as' <- traverse eval as
79-
dapp f' as'
80-
Locate s t -> local (setSpan s) (eval t)
72+
Module (Py.Module ss) -> suite ss
73+
Statement (Py.Import is sp) -> setSpan sp $ do
74+
for_ is $ \ Py.ImportItem{ Py.import_item_name = ns } -> case nonEmpty ns of
75+
Nothing -> pure ()
76+
Just ss -> S.simport (pack . Py.ident_string <$> ss)
77+
dunit
78+
Statement (Py.Pass sp) -> setSpan sp dunit
79+
Statement (Py.Conditional cts e sp) -> setSpan sp $ foldr (\ (c, t) e -> do
80+
c' <- eval (Expr c)
81+
dif c' (suite t) e) (suite e) cts
82+
Statement (Py.Raise (Py.RaiseV3 e) sp) -> setSpan sp $ case e of
83+
Just (e, _) -> eval (Expr e) >>= ddie -- FIXME: from clause
84+
Nothing -> dunit >>= ddie
85+
-- FIXME: RaiseV2
86+
-- FIXME: whatever the second field is
87+
Statement (Py.StmtExpr e sp) -> setSpan sp (eval (Expr e))
88+
Statement (Py.Fun n ps _r ss sp) -> let ps' = mapMaybe (\case { Py.Param n _ _ _ -> Just (ident n) ; _ -> Nothing }) ps in setSpan sp $ letrec (ident n) (dabs ps' (foldr (\ (p, a) m -> let' p a m) (suite ss) . zip ps'))
89+
Expr (Py.Var n sp) -> setSpan sp $ let n' = ident n in lookupEnv n' >>= maybe (dvar n') fetch
90+
Expr (Py.Bool b sp) -> setSpan sp $ dbool b
91+
Expr (Py.Strings ss sp) -> setSpan sp $ dstring (pack (mconcat ss))
92+
Expr (Py.Call f as sp) -> setSpan sp $ do
93+
f' <- eval (Expr f)
94+
as' <- traverse eval (mapMaybe (\case { Py.ArgExpr e _ -> Just (Expr e) ; _ -> Nothing }) as)
95+
-- FIXME: support keyword args &c.
96+
dapp f' as'
97+
_ -> fail "TBD"
8198
where
82-
setSpan s r = r{ refSpan = s }
99+
setSpan s = case fromSpan s of
100+
Just s -> local (\ r -> r{ refSpan = s })
101+
_ -> id
102+
fromSpan Py.SpanEmpty = Nothing
103+
fromSpan (Py.SpanPoint _ l c) = Just (point (Pos l c))
104+
fromSpan (Py.SpanCoLinear _ l c1 c2) = Just (Span (Pos l c1) (Pos l c2))
105+
fromSpan (Py.SpanMultiLine _ l1 l2 c1 c2) = Just (Span (Pos l1 c1) (Pos l2 c2))
106+
suite [] = dunit
107+
suite (s:ss) = do
108+
s' <- eval (Statement s)
109+
foldM (\ into each -> do
110+
each' <- eval (Statement each)
111+
into >>> each') s' ss
112+
ident = name . pack . Py.ident_string
113+
114+
115+
-- Parsing
116+
117+
parse :: FilePath -> IO Term
118+
parse path = do
119+
src <- readFile path
120+
case parseModule src (takeBaseName path) of
121+
Left err -> fail (show err)
122+
Right (Py.Module ss, _) -> pure (Module (Py.Module ss))

semantic-ast/semantic-ast.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ library
7676
, semantic-source ^>= 0.2
7777
, tasty ^>= 1.2.3
7878
, tasty-hunit ^>= 0.10.0.2
79-
, template-haskell >= 2.15 && < 2.19
79+
, template-haskell >= 2.15 && < 2.22
8080
, text ^>= 1.2.3.1
8181
, tree-sitter ^>= 0.9.0.0
8282
, unordered-containers ^>= 0.2.10

semantic-codeql/semantic-codeql.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ library
5959
, semantic-proto ^>= 0
6060
, semantic-source ^>= 0.2
6161
, semantic-tags ^>= 0.0
62-
, template-haskell >= 2.15 && < 2.19
62+
, template-haskell >= 2.15 && < 2.22
6363
, text ^>= 1.2.3
6464
, tree-sitter ^>= 0.9
6565
, tree-sitter-ql ^>= 0.1.0.2

semantic-go/semantic-go.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ library
5959
, semantic-proto ^>= 0
6060
, semantic-source ^>= 0.2
6161
, semantic-tags ^>= 0.0
62-
, template-haskell >= 2.15 && < 2.19
62+
, template-haskell >= 2.15 && < 2.22
6363
, text ^>= 1.2.3
6464
, tree-sitter ^>= 0.9
6565
, tree-sitter-go ^>= 0.5.0.1

semantic-java/semantic-java.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ library
5959
, semantic-proto ^>= 0
6060
, semantic-source ^>= 0.2
6161
, semantic-tags ^>= 0.0
62-
, template-haskell >= 2.15 && < 2.19
62+
, template-haskell >= 2.15 && < 2.22
6363
, text ^>= 1.2.3
6464
, tree-sitter ^>= 0.9
6565
, tree-sitter-java ^>= 0.7.0.1

semantic-json/semantic-json.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ library
5656
, base >= 4.13 && < 5
5757
, semantic-ast
5858
, semantic-tags ^>= 0.0
59-
, template-haskell >= 2.15 && < 2.19
59+
, template-haskell >= 2.15 && < 2.22
6060
, text ^>= 1.2.3
6161
, tree-sitter ^>= 0.9
6262
, tree-sitter-json ^>= 0.7.0.1

semantic-php/semantic-php.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ library
5757
, semantic-proto ^>= 0
5858
, semantic-source ^>= 0.2
5959
, semantic-tags ^>= 0.0
60-
, template-haskell >= 2.15 && < 2.19
60+
, template-haskell >= 2.15 && < 2.22
6161
, text ^>= 1.2.3
6262
, tree-sitter ^>= 0.9
6363
, tree-sitter-php ^>= 0.5.0.0

semantic-python/semantic-python.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ library
6565
, semantic-proto ^>= 0
6666
, semantic-source ^>= 0.2
6767
, semantic-tags ^>= 0.0
68-
, template-haskell >= 2.15 && < 2.19
68+
, template-haskell >= 2.15 && < 2.22
6969
, text ^>= 1.2.3
7070
, tree-sitter ^>= 0.9
7171
, tree-sitter-python ^>= 0.9.0.2

semantic-ruby/semantic-ruby.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ library
5959
, semantic-proto ^>= 0
6060
, semantic-source ^>= 0.2
6161
, semantic-tags ^>= 0.0
62-
, template-haskell >= 2.15 && < 2.19
62+
, template-haskell >= 2.15 && < 2.22
6363
, text ^>= 1.2.3
6464
, tree-sitter ^>= 0.9
6565
, tree-sitter-ruby ^>= 0.5.0.2

0 commit comments

Comments
 (0)