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

Commit 1f74e60

Browse files
author
Patrick Thomson
authored
Merge pull request #35 from github/semantic-core
Core intermediate language
2 parents 31fc1c0 + 937505e commit 1f74e60

File tree

18 files changed

+1467
-1
lines changed

18 files changed

+1467
-1
lines changed

cabal.project

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
packages: vendor/* vendor/proto3-suite vendor/haskell-tree-sitter/languages/* semantic.cabal
1+
packages: vendor/* vendor/proto3-suite vendor/haskell-tree-sitter/languages/* semantic.cabal semantic-core/semantic-core.cabal
22

33
package proto3-suite

semantic-core/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2019 GitHub
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

semantic-core/README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# semantic-core
2+
3+
Semantic core intermediate language (experimental)
4+
5+
6+
## Development
7+
8+
This project consists of a Haskell package named `semantic-core`. The library’s sources are in [`src`][].
9+
10+
Development of `semantic-core` is typically done using `cabal new-build`:
11+
12+
```shell
13+
cabal new-build # build the library
14+
cabal new-repl # load the package into ghci
15+
cabal new-test # build and run the doctests
16+
```
17+
18+
[`src`]: https://github.com/github/semantic/tree/master/semantic-core/src

semantic-core/Setup.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import Distribution.Simple
2+
main = defaultMain

semantic-core/semantic-core.cabal

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
cabal-version: 2.2
2+
3+
name: semantic-core
4+
version: 0.0.0.0
5+
synopsis: Semantic core intermediate language
6+
-- description:
7+
homepage: https://github.com/github/semantic-core
8+
-- bug-reports:
9+
license: MIT
10+
license-file: LICENSE
11+
author: Rob Rix
12+
maintainer: [email protected]
13+
-- copyright:
14+
category: Language
15+
build-type: Simple
16+
extra-source-files: README.md
17+
18+
tested-with: GHC == 8.6.4
19+
20+
library
21+
exposed-modules: Analysis.Concrete
22+
, Analysis.Eval
23+
, Analysis.FlowInsensitive
24+
, Analysis.ImportGraph
25+
, Analysis.ScopeGraph
26+
, Analysis.Typecheck
27+
, Control.Effect.Readline
28+
, Data.Core
29+
, Data.File
30+
, Data.Loc
31+
, Data.Name
32+
, Data.Stack
33+
-- other-modules:
34+
-- other-extensions:
35+
build-depends: algebraic-graphs ^>= 0.3
36+
, base >= 4.11 && < 5
37+
, containers ^>= 0.6
38+
, directory ^>= 1.3
39+
, filepath ^>= 1.4
40+
, fused-effects ^>= 0.4
41+
, haskeline ^>= 0.7.5
42+
, prettyprinter ^>= 1.2.1
43+
, semigroupoids ^>= 5.3
44+
, transformers ^>= 0.5.6
45+
hs-source-dirs: src
46+
default-language: Haskell2010
47+
ghc-options: -Weverything -Wno-missing-local-signatures -Wno-missing-import-lists -Wno-implicit-prelude -Wno-safe -Wno-unsafe -Wno-name-shadowing -Wno-monomorphism-restriction -Wno-missed-specialisations -Wno-all-missed-specialisations
48+
if (impl(ghc >= 8.6))
49+
ghc-options: -Wno-star-is-type
50+
51+
test-suite doctest
52+
type: exitcode-stdio-1.0
53+
main-is: Doctest.hs
54+
build-depends: base >=4.9 && <4.13
55+
, doctest >=0.7 && <1.0
56+
, QuickCheck
57+
, semantic-core
58+
hs-source-dirs: test
59+
default-language: Haskell2010
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
{-# LANGUAGE FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, LambdaCase, MultiParamTypeClasses, NamedFieldPuns, RecordWildCards, TypeApplications, TypeOperators, UndecidableInstances #-}
2+
module Analysis.Concrete
3+
( Concrete(..)
4+
, concrete
5+
, concreteAnalysis
6+
, heapGraph
7+
, heapValueGraph
8+
, heapAddressGraph
9+
, addressStyle
10+
) where
11+
12+
import qualified Algebra.Graph as G
13+
import qualified Algebra.Graph.Export.Dot as G
14+
import Analysis.Eval
15+
import Control.Applicative (Alternative (..))
16+
import Control.Effect
17+
import Control.Effect.Fail
18+
import Control.Effect.Fresh
19+
import Control.Effect.NonDet
20+
import Control.Effect.Reader hiding (Local)
21+
import Control.Effect.State
22+
import Control.Monad ((<=<), guard)
23+
import qualified Data.Core as Core
24+
import Data.File
25+
import Data.Function (fix)
26+
import qualified Data.IntMap as IntMap
27+
import qualified Data.IntSet as IntSet
28+
import Data.Loc
29+
import qualified Data.Map as Map
30+
import Data.Monoid (Alt(..))
31+
import Data.Name
32+
import Prelude hiding (fail)
33+
34+
type Precise = Int
35+
type Env = Map.Map Name Precise
36+
37+
newtype FrameId = FrameId { unFrameId :: Precise }
38+
deriving (Eq, Ord, Show)
39+
40+
data Concrete
41+
= Closure Loc Name Core.Core Precise
42+
| Unit
43+
| Bool Bool
44+
| String String
45+
| Obj Frame
46+
deriving (Eq, Ord, Show)
47+
48+
objectFrame :: Concrete -> Maybe Frame
49+
objectFrame (Obj frame) = Just frame
50+
objectFrame _ = Nothing
51+
52+
data Frame = Frame
53+
{ frameEdges :: [(Core.Edge, Precise)]
54+
, frameSlots :: Env
55+
}
56+
deriving (Eq, Ord, Show)
57+
58+
type Heap = IntMap.IntMap Concrete
59+
60+
61+
-- | Concrete evaluation of a term to a value.
62+
--
63+
-- >>> snd (concrete [File (Loc "bool" emptySpan) (Core.Bool True)])
64+
-- [Right (Bool True)]
65+
concrete :: [File Core.Core] -> (Heap, [File (Either (Loc, String) Concrete)])
66+
concrete
67+
= run
68+
. runFresh
69+
. runHeap
70+
. traverse runFile
71+
72+
runFile :: ( Carrier sig m
73+
, Effect sig
74+
, Member Fresh sig
75+
, Member (Reader FrameId) sig
76+
, Member (State Heap) sig
77+
)
78+
=> File Core.Core
79+
-> m (File (Either (Loc, String) Concrete))
80+
runFile file = traverse run file
81+
where run = runReader (fileLoc file)
82+
. runFailWithLoc
83+
. fix (eval concreteAnalysis)
84+
85+
concreteAnalysis :: ( Carrier sig m
86+
, Member Fresh sig
87+
, Member (Reader Loc) sig
88+
, Member (Reader FrameId) sig
89+
, Member (State Heap) sig
90+
, MonadFail m
91+
)
92+
=> Analysis Precise Concrete m
93+
concreteAnalysis = Analysis{..}
94+
where alloc _ = fresh
95+
bind name addr = modifyCurrentFrame (updateFrameSlots (Map.insert name addr))
96+
lookupEnv n = do
97+
FrameId frameAddr <- ask
98+
val <- deref frameAddr
99+
heap <- get
100+
pure (val >>= lookupConcrete heap n)
101+
deref = gets . IntMap.lookup
102+
assign addr value = modify (IntMap.insert addr value)
103+
abstract _ name body = do
104+
loc <- ask
105+
FrameId parentAddr <- ask
106+
pure (Closure loc name body parentAddr)
107+
apply eval (Closure loc name body parentAddr) a = do
108+
frameAddr <- fresh
109+
assign frameAddr (Obj (Frame [(Core.Lexical, parentAddr)] mempty))
110+
local (const loc) . (frameAddr ...) $ do
111+
addr <- alloc name
112+
assign addr a
113+
bind name addr
114+
eval body
115+
apply _ f _ = fail $ "Cannot coerce " <> show f <> " to function"
116+
unit = pure Unit
117+
bool b = pure (Bool b)
118+
asBool (Bool b) = pure b
119+
asBool v = fail $ "Cannot coerce " <> show v <> " to Bool"
120+
string s = pure (String s)
121+
asString (String s) = pure s
122+
asString v = fail $ "Cannot coerce " <> show v <> " to String"
123+
-- FIXME: differential inheritance (reference fields instead of copying)
124+
-- FIXME: copy non-lexical parents deeply?
125+
frame = do
126+
lexical <- asks unFrameId
127+
pure (Obj (Frame [(Core.Lexical, lexical)] mempty))
128+
-- FIXME: throw an error
129+
-- FIXME: support dynamic imports
130+
edge e addr = modifyCurrentFrame (\ (Frame ps fs) -> Frame ((e, addr) : ps) fs)
131+
addr ... m = local (const (FrameId addr)) m
132+
133+
updateFrameSlots f frame = frame { frameSlots = f (frameSlots frame) }
134+
135+
modifyCurrentFrame f = do
136+
addr <- asks unFrameId
137+
Just (Obj frame) <- deref addr
138+
assign addr (Obj (f frame))
139+
140+
141+
lookupConcrete :: Heap -> Name -> Concrete -> Maybe Precise
142+
lookupConcrete heap name = run . evalState IntSet.empty . runNonDet . inConcrete
143+
where -- look up the name in a concrete value
144+
inConcrete = inFrame <=< maybeA . objectFrame
145+
-- look up the name in a specific 'Frame', with slots taking precedence over parents
146+
inFrame (Frame ps fs) = maybeA (Map.lookup name fs) <|> getAlt (foldMap (Alt . inAddress . snd) ps)
147+
-- look up the name in the value an address points to, if we haven’t already visited it
148+
inAddress addr = do
149+
visited <- get
150+
guard (addr `IntSet.notMember` visited)
151+
-- FIXME: throw an error if we can’t deref @addr@
152+
val <- maybeA (IntMap.lookup addr heap)
153+
modify (IntSet.insert addr)
154+
inConcrete val
155+
maybeA = maybe empty pure
156+
157+
158+
runHeap :: (Carrier sig m, Member Fresh sig) => ReaderC FrameId (StateC Heap m) a -> m (Heap, a)
159+
runHeap m = do
160+
addr <- fresh
161+
runState (IntMap.singleton addr (Obj (Frame [] mempty))) (runReader (FrameId addr) m)
162+
163+
164+
-- | 'heapGraph', 'heapValueGraph', and 'heapAddressGraph' allow us to conveniently export SVGs of the heap:
165+
--
166+
-- > λ let (heap, res) = concrete [ruby]
167+
-- > λ writeFile "/Users/rob/Desktop/heap.dot" (export (addressStyle heap) (heapAddressGraph heap))
168+
-- > λ :!dot -Tsvg < ~/Desktop/heap.dot > ~/Desktop/heap.svg
169+
heapGraph :: (Precise -> Concrete -> a) -> (Either Core.Edge Name -> Precise -> G.Graph a) -> Heap -> G.Graph a
170+
heapGraph vertex edge h = foldr (uncurry graph) G.empty (IntMap.toList h)
171+
where graph k v rest = (G.vertex (vertex k v) `G.connect` outgoing v) `G.overlay` rest
172+
outgoing = \case
173+
Unit -> G.empty
174+
Bool _ -> G.empty
175+
String _ -> G.empty
176+
Closure _ _ _ parentAddr -> edge (Left Core.Lexical) parentAddr
177+
Obj frame -> fromFrame frame
178+
fromFrame (Frame es ss) = foldr (G.overlay . uncurry (edge . Left)) (foldr (G.overlay . uncurry (edge . Right)) G.empty (Map.toList ss)) es
179+
180+
heapValueGraph :: Heap -> G.Graph Concrete
181+
heapValueGraph h = heapGraph (const id) (const fromAddr) h
182+
where fromAddr addr = maybe G.empty G.vertex (IntMap.lookup addr h)
183+
184+
heapAddressGraph :: Heap -> G.Graph (EdgeType, Precise)
185+
heapAddressGraph = heapGraph (\ addr v -> (Value v, addr)) (fmap G.vertex . (,) . either Edge Slot)
186+
187+
addressStyle :: Heap -> G.Style (EdgeType, Precise) String
188+
addressStyle heap = (G.defaultStyle vertex) { G.edgeAttributes }
189+
where vertex (_, addr) = maybe (show addr <> " = ?") (((show addr <> " = ") <>) . fromConcrete) (IntMap.lookup addr heap)
190+
edgeAttributes _ (Slot name, _) = ["label" G.:= fromName name]
191+
edgeAttributes _ (Edge Core.Import, _) = ["color" G.:= "blue"]
192+
edgeAttributes _ (Edge Core.Lexical, _) = ["color" G.:= "green"]
193+
edgeAttributes _ _ = []
194+
fromConcrete = \case
195+
Unit -> "()"
196+
Bool b -> show b
197+
String s -> show s
198+
Closure (Loc p (Span s e)) n _ _ -> "\\\\ " <> fromName n <> " [" <> p <> ":" <> showPos s <> "-" <> showPos e <> "]"
199+
Obj _ -> "{}"
200+
showPos (Pos l c) = show l <> ":" <> show c
201+
fromName (User s) = s
202+
fromName (Gen sym) = fromGensym sym
203+
fromName (Path p) = show p
204+
fromGensym (Root s) = s
205+
fromGensym (ss :/ (s, i)) = fromGensym ss <> "." <> s <> show i
206+
207+
data EdgeType
208+
= Edge Core.Edge
209+
| Slot Name
210+
| Value Concrete
211+
deriving (Eq, Ord, Show)

0 commit comments

Comments
 (0)