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

Commit afa4511

Browse files
author
Patrick Thomson
authored
Merge pull request #52 from github/examples-docs
Add documentation detailing example uses for the CLI.
2 parents 3d390d6 + 4947963 commit afa4511

File tree

4 files changed

+321
-0
lines changed

4 files changed

+321
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
`semantic` is a Haskell library and command line tool for parsing, analyzing, and comparing source code.
44

5+
In a hurry? Check out our documentation of [example uses for the `semantic` command line tool](docs/examples.md).
6+
57
| Table of Contents |
68
| :------------- |
79
| [Usage](#usage) |

docs/examples.md

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# Quick usage examples
2+
3+
## Parse trees
4+
5+
Semantic uses [tree-sitter](https://github.com/tree-sitter/tree-sitter) to generate parse trees, but layers in a more generalized notion of syntax terms across all supported programming languages. We'll see why this is important when we get to diffs and program analysis, but for now let's just inspect some output. It helps to have a simple program to parse, so let's create one. Open a file `test.A.py` and paste in the following:
6+
7+
``` python
8+
def Foo(x):
9+
return x
10+
print Foo("hi")
11+
```
12+
13+
Now, let's generate an abstract syntax tree (AST).
14+
15+
``` bash
16+
$ semantic parse test.A.py
17+
(Statements
18+
(Annotation
19+
(Function
20+
(Identifier)
21+
(Identifier)
22+
(Return
23+
(Identifier)))
24+
(Empty))
25+
(Call
26+
(Identifier)
27+
(Call
28+
(Identifier)
29+
(TextElement)
30+
(Empty))
31+
(Empty)))
32+
```
33+
34+
The default s-expression output is a good format for quickly visualizing the structure of code. We can see that there is a function declared and that then there is a call expression, nested in another call expression which matches the function calls to `print` and `Foo`. Feel free to play with some of the other output formats, for example the following will give back the same AST, but in JSON and with much more information about each node including things like the span and range of each syntactic element in the original source file.
35+
36+
``` bash
37+
$ semantic parse --json test.A.py
38+
```
39+
40+
## Diffs
41+
42+
Now, let's look at a simple, syntax aware diff. Create a second file `test.B.py` that looks like this (The function `Foo` has been renamed).
43+
44+
``` python
45+
def Bar(x):
46+
return x
47+
print Bar("hi")
48+
```
49+
50+
First, let's just see what the diff looks like.
51+
52+
``` bash
53+
$ git diff --no-index test.A.py test.B.py
54+
```
55+
``` diff
56+
diff --git a/test.A.py b/test.B.py
57+
index 81f210023..5f37f4260 100644
58+
--- a/test.A.py
59+
+++ b/test.B.py
60+
@@ -1,3 +1,3 @@
61+
-def Foo(x):
62+
+def Bar(x):
63+
return x
64+
-print Foo("hi")
65+
+print Bar("hi")
66+
```
67+
68+
Now, let's look at a syntax aware diff.
69+
70+
``` bash
71+
$ semantic diff test.A.py test.B.py
72+
(Statements
73+
(Annotation
74+
(Function
75+
{ (Identifier)
76+
->(Identifier) }
77+
(Identifier)
78+
(Return
79+
(Identifier)))
80+
(Empty))
81+
(Call
82+
(Identifier)
83+
(Call
84+
{ (Identifier)
85+
->(Identifier) }
86+
(TextElement)
87+
(Empty))
88+
(Empty)))
89+
```
90+
91+
Notice the difference? Instead of showing that entire lines were added and removed, the semantic diff is aware that the identifier of the function declaration and function call changed. Pretty cool.
92+
93+
## Import graphs
94+
95+
OK, now for the fun stuff. Semantic can currently produce a couple of different graph-based views of source code, let's first take a look at import graphs. An import graph shows how files include other files within a software project. For this example, we are going to write a little bit more code in order to see this work. Start by creating a couple of new files:
96+
97+
``` python
98+
# main.py
99+
import numpy as np
100+
from a import foo as foo_
101+
102+
def qux():
103+
return foo_()
104+
105+
foo_(1)
106+
```
107+
108+
``` python
109+
# a.py
110+
def foo(x):
111+
return x
112+
```
113+
114+
Now, let's graph.
115+
116+
``` bash
117+
$ semantic graph main.py
118+
digraph
119+
{
120+
121+
"a.py (Module)" [style="dotted, rounded" shape="box"]
122+
"main.py (Module)" [style="dotted, rounded" shape="box"]
123+
"numpy (Unknown Module)" [style="dotted, rounded" shape="box" color="red" fontcolor="red"]
124+
"main.py (Module)" -> "a.py (Module)" [len="5.0" label="imports"]
125+
"main.py (Module)" -> "numpy (Unknown Module)" [len="5.0" label="imports"]
126+
}
127+
```
128+
129+
To make this easier to visualize, let's use the `dot` utility from `graphviz` and write this graph to SVG:
130+
131+
```
132+
$ semantic graph main.py | dot -Tsvg > main.html && open main.html
133+
```
134+
135+
You'll get something that looks like this:
136+
137+
![an import graph](images/import_graph.svg)
138+
139+
## Call graphs
140+
141+
Call graphs expand on the import graphing capabilities by adding in some additional vertices and edges to the graph to identify named symbols and the connections between them. Taking the same example code, simply add `--call` to the invocation of semantic:
142+
143+
```
144+
$ semantic graph --calls main.py | dot -Tsvg > main.html && open main.html
145+
```
146+
147+
![a call graph](images/call_graph.svg)

docs/images/call_graph.svg

Lines changed: 133 additions & 0 deletions
Loading

docs/images/import_graph.svg

Lines changed: 39 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)