summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDibyendu Majumdar <mobile@majumdar.org.uk>2023-09-26 17:43:36 +0100
committerDibyendu Majumdar <mobile@majumdar.org.uk>2026-08-10 11:11:18 -0700
commitdca50fde64f64cd105c35651e12ea09f81db2cc3 (patch)
tree4f8316733e7e5eab2d63daadcb29feddf62622d3
parenta7818ec324de3031462e5c4d4cb055bf7fde4ebb (diff)
downloadSimple-dca50fde64f64cd105c35651e12ea09f81db2cc3.tar.gz
Simple-dca50fde64f64cd105c35651e12ea09f81db2cc3.zip
Chapter 2: Arithmetic
Co-authored-by: Cliff Click <cliffc@acm.org> Co-authored-by: Thalia Archibald <thalia@archibald.dev> Co-authored-by: Yichen Yan <oraluben@outlook.com> Co-authored-by: Tamás Hadházy <85063808+Hels15@users.noreply.github.com> Co-authored-by: XmiliaH <45106915+XmiliaH@users.noreply.github.com> Co-authored-by: Hsiang-Ying Fu <66062782+fuhsnn@users.noreply.github.com> Co-authored-by: hadha <helsgithub15@gmail.com> Co-authored-by: RobertObkircher <robert.obkircher@gmail.com>
-rw-r--r--README.md2
-rw-r--r--docs/chapter02/02-demonstration-peephole.svg54
-rw-r--r--docs/chapter02/02-demonstration-pre-peephole-nokill.svg100
-rw-r--r--docs/chapter02/02-demonstration-pre-peephole.svg87
-rw-r--r--docs/chapter02/02-grammar.md40
-rw-r--r--docs/chapter02/02-post-peephole-ex1.gv17
-rw-r--r--docs/chapter02/02-post-peephole-ex1.svg56
-rw-r--r--docs/chapter02/02-pre-peephole-ex1.gv34
-rw-r--r--docs/chapter02/02-pre-peephole-ex1.svg168
-rw-r--r--docs/chapter02/Makefile12
-rw-r--r--docs/chapter02/README.md282
-rw-r--r--docs/chapter02/lattice.gv40
-rw-r--r--docs/chapter02/lattice.svg159
-rw-r--r--src/main/java/com/seaofnodes/simple/GraphVisualizer.java117
-rw-r--r--src/main/java/com/seaofnodes/simple/Parser.java75
-rw-r--r--src/main/java/com/seaofnodes/simple/Utils.java39
-rw-r--r--src/main/java/com/seaofnodes/simple/node/AddNode.java36
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ConstantNode.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/DivNode.java34
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MinusNode.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MulNode.java32
-rw-r--r--src/main/java/com/seaofnodes/simple/node/Node.java172
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ReturnNode.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StartNode.java23
-rw-r--r--src/main/java/com/seaofnodes/simple/node/SubNode.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/type/Type.java40
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeInteger.java41
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter01Test.java26
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter02Test.java66
29 files changed, 1816 insertions, 39 deletions
diff --git a/README.md b/README.md
index 2fda7c8b..364f3da4 100644
--- a/README.md
+++ b/README.md
@@ -38,7 +38,7 @@ intermediate representation.
The Simple language is styled after a subset of C or Java.
* [Chapter 1](docs/chapter01/README.md): Script that returns an integer literal, i.e., an empty function that takes no arguments and returns a single integer value. The `return` statement.
-* Chapter 2: Simple binary arithmetic such as addition, subtraction, multiplication, division
+* [Chapter 2](docs/chapter02/README.md): Simple binary arithmetic such as addition, subtraction, multiplication, division
with constants. Peephole optimization / simple constant folding.
* Chapter 3: Local variables, and assignment statements. Read on RHS, SSA, more peephole optimization if local is a
constant.
diff --git a/docs/chapter02/02-demonstration-peephole.svg b/docs/chapter02/02-demonstration-peephole.svg
new file mode 100644
index 00000000..90ab6ca4
--- /dev/null
+++ b/docs/chapter02/02-demonstration-peephole.svg
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by graphviz version 2.40.1 (20161225.0304)
+ -->
+<!-- Title: chapter02 Pages: 1 -->
+<svg width="114pt" height="220pt" viewBox="0.00 0.00 114.00 220.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 216)">
+<title>chapter02</title>
+<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-216 110,-216 110,4 -4,4"/>
+<g id="clust1" class="cluster">
+<title>cluster_Nodes</title>
+<polygon fill="none" stroke="#000000" points="8,-8 8,-204 98,-204 98,-8 8,-8"/>
+</g>
+<!-- Start1 -->
+<g id="node1" class="node">
+<title>Start1</title>
+<polygon fill="#ffff00" stroke="#000000" points="80,-196 26,-196 26,-160 80,-160 80,-196"/>
+<text text-anchor="middle" x="53" y="-173.8" font-family="Times,serif" font-size="14.00" fill="#000000">Start</text>
+</g>
+<!-- Con_5 -->
+<g id="node2" class="node">
+<title>Con_5</title>
+<ellipse fill="none" stroke="#000000" cx="43" cy="-106" rx="27" ry="18"/>
+<text text-anchor="middle" x="43" y="-101.8" font-family="Times,serif" font-size="14.00" fill="#000000">#3</text>
+</g>
+<!-- Con_5&#45;&gt;Start1 -->
+<g id="edge1" class="edge">
+<title>Con_5-&gt;Start1</title>
+<path fill="none" stroke="#000000" stroke-dasharray="1,5" d="M45.5234,-124.1686C46.5929,-131.869 47.8647,-141.0257 49.0532,-149.5834"/>
+<polygon fill="#000000" stroke="#000000" points="45.6001,-150.1633 50.4426,-159.5867 52.5336,-149.2003 45.6001,-150.1633"/>
+<text text-anchor="middle" x="43.2998" y="-126.5686" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">0</text>
+</g>
+<!-- Return6 -->
+<g id="node3" class="node">
+<title>Return6</title>
+<polygon fill="#ffff00" stroke="#000000" points="80.1025,-52 25.8975,-52 25.8975,-16 80.1025,-16 80.1025,-52"/>
+<text text-anchor="middle" x="53" y="-29.8" font-family="Times,serif" font-size="14.00" fill="#000000">Return</text>
+</g>
+<!-- Return6&#45;&gt;Start1 -->
+<g id="edge2" class="edge">
+<title>Return6-&gt;Start1</title>
+<path fill="none" stroke="#ff0000" d="M64.3196,-52.2322C69.9396,-62.3585 76.1294,-75.4211 79,-88 82.5597,-103.599 82.5597,-108.401 79,-124 76.9144,-133.1394 73.0765,-142.534 68.9781,-150.8724"/>
+<polygon fill="#ff0000" stroke="#ff0000" points="65.8583,-149.2853 64.3196,-159.7678 72.0595,-152.5328 65.8583,-149.2853"/>
+<text text-anchor="middle" x="62.096" y="-54.6322" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">0</text>
+</g>
+<!-- Return6&#45;&gt;Con_5 -->
+<g id="edge3" class="edge">
+<title>Return6-&gt;Con_5</title>
+<path fill="none" stroke="#000000" d="M50.4766,-52.1686C49.4071,-59.869 48.1353,-69.0257 46.9468,-77.5834"/>
+<polygon fill="#000000" stroke="#000000" points="43.4664,-77.2003 45.5574,-87.5867 50.3999,-78.1633 43.4664,-77.2003"/>
+<text text-anchor="middle" x="48.253" y="-54.5686" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">1</text>
+</g>
+</g>
+</svg> \ No newline at end of file
diff --git a/docs/chapter02/02-demonstration-pre-peephole-nokill.svg b/docs/chapter02/02-demonstration-pre-peephole-nokill.svg
new file mode 100644
index 00000000..3d4f53df
--- /dev/null
+++ b/docs/chapter02/02-demonstration-pre-peephole-nokill.svg
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by graphviz version 2.40.1 (20161225.0304)
+ -->
+<!-- Title: chapter02 Pages: 1 -->
+<svg width="258pt" height="220pt" viewBox="0.00 0.00 258.00 220.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 216)">
+<title>chapter02</title>
+<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-216 254,-216 254,4 -4,4"/>
+<g id="clust1" class="cluster">
+<title>cluster_Nodes</title>
+<polygon fill="none" stroke="#000000" points="8,-8 8,-204 242,-204 242,-8 8,-8"/>
+</g>
+<!-- Start1 -->
+<g id="node1" class="node">
+<title>Start1</title>
+<polygon fill="#ffff00" stroke="#000000" points="165,-196 111,-196 111,-160 165,-160 165,-196"/>
+<text text-anchor="middle" x="138" y="-173.8" font-family="Times,serif" font-size="14.00" fill="#000000">Start</text>
+</g>
+<!-- Con_2 -->
+<g id="node2" class="node">
+<title>Con_2</title>
+<ellipse fill="none" stroke="#000000" cx="43" cy="-106" rx="27" ry="18"/>
+<text text-anchor="middle" x="43" y="-101.8" font-family="Times,serif" font-size="14.00" fill="#000000">#1</text>
+</g>
+<!-- Con_2&#45;&gt;Start1 -->
+<g id="edge1" class="edge">
+<title>Con_2-&gt;Start1</title>
+<path fill="none" stroke="#000000" stroke-dasharray="1,5" d="M60.8688,-119.5426C73.5668,-129.1664 90.9688,-142.3553 106.03,-153.7701"/>
+<polygon fill="#000000" stroke="#000000" points="104.028,-156.6444 114.1118,-159.8953 108.2562,-151.0656 104.028,-156.6444"/>
+<text text-anchor="middle" x="58.6452" y="-121.9426" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">0</text>
+</g>
+<!-- Con_3 -->
+<g id="node3" class="node">
+<title>Con_3</title>
+<ellipse fill="none" stroke="#000000" cx="115" cy="-106" rx="27" ry="18"/>
+<text text-anchor="middle" x="115" y="-101.8" font-family="Times,serif" font-size="14.00" fill="#000000">#2</text>
+</g>
+<!-- Con_3&#45;&gt;Start1 -->
+<g id="edge2" class="edge">
+<title>Con_3-&gt;Start1</title>
+<path fill="none" stroke="#000000" stroke-dasharray="1,5" d="M120.6854,-123.7978C123.237,-131.7854 126.3069,-141.3955 129.1486,-150.2913"/>
+<polygon fill="#000000" stroke="#000000" points="125.8432,-151.4462 132.2203,-159.9069 132.5113,-149.316 125.8432,-151.4462"/>
+<text text-anchor="middle" x="118.4618" y="-126.1978" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">0</text>
+</g>
+<!-- Add4 -->
+<g id="node4" class="node">
+<title>Add4</title>
+<ellipse fill="none" stroke="#000000" cx="89" cy="-34" rx="27" ry="18"/>
+<text text-anchor="middle" x="89" y="-29.8" font-family="Times,serif" font-size="14.00" fill="#000000">+</text>
+</g>
+<!-- Add4&#45;&gt;Con_2 -->
+<g id="edge3" class="edge">
+<title>Add4-&gt;Con_2</title>
+<path fill="none" stroke="#000000" d="M78.3311,-50.6992C72.6414,-59.6048 65.5223,-70.7476 59.1684,-80.693"/>
+<polygon fill="#000000" stroke="#000000" points="56.0456,-79.08 53.6111,-89.3913 61.9445,-82.8487 56.0456,-79.08"/>
+<text text-anchor="middle" x="80.5547" y="-53.0992" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">1</text>
+</g>
+<!-- Add4&#45;&gt;Con_3 -->
+<g id="edge4" class="edge">
+<title>Add4-&gt;Con_3</title>
+<path fill="none" stroke="#000000" d="M95.427,-51.7978C98.3866,-59.9936 101.963,-69.8976 105.2447,-78.9855"/>
+<polygon fill="#000000" stroke="#000000" points="101.9585,-80.1902 108.647,-88.407 108.5424,-77.8126 101.9585,-80.1902"/>
+<text text-anchor="middle" x="93.2034" y="-54.1978" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">2</text>
+</g>
+<!-- Con_5 -->
+<g id="node5" class="node">
+<title>Con_5</title>
+<ellipse fill="none" stroke="#000000" cx="207" cy="-106" rx="27" ry="18"/>
+<text text-anchor="middle" x="207" y="-101.8" font-family="Times,serif" font-size="14.00" fill="#000000">#3</text>
+</g>
+<!-- Con_5&#45;&gt;Start1 -->
+<g id="edge5" class="edge">
+<title>Con_5-&gt;Start1</title>
+<path fill="none" stroke="#000000" stroke-dasharray="1,5" d="M192.3669,-121.2693C183.6447,-130.3708 172.3525,-142.1539 162.3214,-152.6212"/>
+<polygon fill="#000000" stroke="#000000" points="159.6869,-150.3117 155.2947,-159.9533 164.7408,-155.1551 159.6869,-150.3117"/>
+<text text-anchor="middle" x="194.5905" y="-123.6693" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">0</text>
+</g>
+<!-- Return6 -->
+<g id="node6" class="node">
+<title>Return6</title>
+<polygon fill="#ffff00" stroke="#000000" points="211.1025,-52 156.8975,-52 156.8975,-16 211.1025,-16 211.1025,-52"/>
+<text text-anchor="middle" x="184" y="-29.8" font-family="Times,serif" font-size="14.00" fill="#000000">Return</text>
+</g>
+<!-- Return6&#45;&gt;Start1 -->
+<g id="edge6" class="edge">
+<title>Return6-&gt;Start1</title>
+<path fill="none" stroke="#ff0000" d="M178.1741,-52.2377C170.2935,-76.9072 156.1635,-121.1402 146.9358,-150.0269"/>
+<polygon fill="#ff0000" stroke="#ff0000" points="143.4876,-149.3195 143.7786,-159.9104 150.1557,-151.4497 143.4876,-149.3195"/>
+<text text-anchor="middle" x="175.9505" y="-54.6377" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">0</text>
+</g>
+<!-- Return6&#45;&gt;Con_5 -->
+<g id="edge7" class="edge">
+<title>Return6-&gt;Con_5</title>
+<path fill="none" stroke="#000000" d="M189.8039,-52.1686C192.3565,-60.1594 195.41,-69.7181 198.2308,-78.5486"/>
+<polygon fill="#000000" stroke="#000000" points="194.9006,-79.6259 201.2777,-88.0866 201.5687,-77.4957 194.9006,-79.6259"/>
+<text text-anchor="middle" x="187.5803" y="-54.5686" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">1</text>
+</g>
+</g>
+</svg> \ No newline at end of file
diff --git a/docs/chapter02/02-demonstration-pre-peephole.svg b/docs/chapter02/02-demonstration-pre-peephole.svg
new file mode 100644
index 00000000..49624652
--- /dev/null
+++ b/docs/chapter02/02-demonstration-pre-peephole.svg
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by graphviz version 2.40.1 (20161225.0304)
+ -->
+<!-- Title: chapter02 Pages: 1 -->
+<svg width="186pt" height="292pt" viewBox="0.00 0.00 186.00 292.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 288)">
+<title>chapter02</title>
+<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-288 182,-288 182,4 -4,4"/>
+<g id="clust1" class="cluster">
+<title>cluster_Nodes</title>
+<polygon fill="none" stroke="#000000" points="8,-8 8,-276 170,-276 170,-8 8,-8"/>
+</g>
+<!-- Start1 -->
+<g id="node1" class="node">
+<title>Start1</title>
+<polygon fill="#ffff00" stroke="#000000" points="90,-268 36,-268 36,-232 90,-232 90,-268"/>
+<text text-anchor="middle" x="63" y="-245.8" font-family="Times,serif" font-size="14.00" fill="#000000">Start</text>
+</g>
+<!-- Con_2 -->
+<g id="node2" class="node">
+<title>Con_2</title>
+<ellipse fill="none" stroke="#000000" cx="63" cy="-178" rx="27" ry="18"/>
+<text text-anchor="middle" x="63" y="-173.8" font-family="Times,serif" font-size="14.00" fill="#000000">#1</text>
+</g>
+<!-- Con_2&#45;&gt;Start1 -->
+<g id="edge1" class="edge">
+<title>Con_2-&gt;Start1</title>
+<path fill="none" stroke="#000000" stroke-dasharray="1,5" d="M63,-196.1686C63,-203.869 63,-213.0257 63,-221.5834"/>
+<polygon fill="#000000" stroke="#000000" points="59.5001,-221.5867 63,-231.5867 66.5001,-221.5868 59.5001,-221.5867"/>
+<text text-anchor="middle" x="60.7764" y="-198.5686" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">0</text>
+</g>
+<!-- Con_3 -->
+<g id="node3" class="node">
+<title>Con_3</title>
+<ellipse fill="none" stroke="#000000" cx="135" cy="-178" rx="27" ry="18"/>
+<text text-anchor="middle" x="135" y="-173.8" font-family="Times,serif" font-size="14.00" fill="#000000">#2</text>
+</g>
+<!-- Con_3&#45;&gt;Start1 -->
+<g id="edge2" class="edge">
+<title>Con_3-&gt;Start1</title>
+<path fill="none" stroke="#000000" stroke-dasharray="1,5" d="M119.7307,-193.2693C110.6292,-202.3708 98.8461,-214.1539 88.3788,-224.6212"/>
+<polygon fill="#000000" stroke="#000000" points="85.6429,-222.4073 81.0467,-231.9533 90.5927,-227.3571 85.6429,-222.4073"/>
+<text text-anchor="middle" x="117.5071" y="-195.6693" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">0</text>
+</g>
+<!-- Add4 -->
+<g id="node4" class="node">
+<title>Add4</title>
+<ellipse fill="none" stroke="#000000" cx="63" cy="-106" rx="27" ry="18"/>
+<text text-anchor="middle" x="63" y="-101.8" font-family="Times,serif" font-size="14.00" fill="#000000">+</text>
+</g>
+<!-- Add4&#45;&gt;Con_2 -->
+<g id="edge3" class="edge">
+<title>Add4-&gt;Con_2</title>
+<path fill="none" stroke="#000000" d="M63,-124.1686C63,-131.869 63,-141.0257 63,-149.5834"/>
+<polygon fill="#000000" stroke="#000000" points="59.5001,-149.5867 63,-159.5867 66.5001,-149.5868 59.5001,-149.5867"/>
+<text text-anchor="middle" x="60.7764" y="-126.5686" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">1</text>
+</g>
+<!-- Add4&#45;&gt;Con_3 -->
+<g id="edge4" class="edge">
+<title>Add4-&gt;Con_3</title>
+<path fill="none" stroke="#000000" d="M78.2693,-121.2693C88.197,-131.197 101.3153,-144.3153 112.4363,-155.4363"/>
+<polygon fill="#000000" stroke="#000000" points="110.2067,-158.1564 119.7527,-162.7527 115.1564,-153.2067 110.2067,-158.1564"/>
+<text text-anchor="middle" x="80.4929" y="-123.6693" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">2</text>
+</g>
+<!-- Return5 -->
+<g id="node5" class="node">
+<title>Return5</title>
+<polygon fill="#ffff00" stroke="#000000" points="80.1025,-52 25.8975,-52 25.8975,-16 80.1025,-16 80.1025,-52"/>
+<text text-anchor="middle" x="53" y="-29.8" font-family="Times,serif" font-size="14.00" fill="#000000">Return</text>
+</g>
+<!-- Return5&#45;&gt;Start1 -->
+<g id="edge5" class="edge">
+<title>Return5-&gt;Start1</title>
+<path fill="none" stroke="#ff0000" d="M41.6804,-52.2322C36.0604,-62.3585 29.8706,-75.4211 27,-88 16.3208,-134.7969 13.0904,-150.0596 27,-196 29.9564,-205.7645 35.3252,-215.3771 41.03,-223.7238"/>
+<polygon fill="#ff0000" stroke="#ff0000" points="38.2871,-225.9027 47.0095,-231.9169 43.9414,-221.7761 38.2871,-225.9027"/>
+<text text-anchor="middle" x="39.4568" y="-54.6322" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">0</text>
+</g>
+<!-- Return5&#45;&gt;Add4 -->
+<g id="edge6" class="edge">
+<title>Return5-&gt;Add4</title>
+<path fill="none" stroke="#000000" d="M55.5234,-52.1686C56.5929,-59.869 57.8647,-69.0257 59.0532,-77.5834"/>
+<polygon fill="#000000" stroke="#000000" points="55.6001,-78.1633 60.4426,-87.5867 62.5336,-77.2003 55.6001,-78.1633"/>
+<text text-anchor="middle" x="53.2998" y="-54.5686" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">1</text>
+</g>
+</g>
+</svg> \ No newline at end of file
diff --git a/docs/chapter02/02-grammar.md b/docs/chapter02/02-grammar.md
new file mode 100644
index 00000000..ce875eda
--- /dev/null
+++ b/docs/chapter02/02-grammar.md
@@ -0,0 +1,40 @@
+# Grammar for Chapter 1
+
+```antlrv4
+grammar SimpleLanguage;
+
+program
+ : statement EOF
+ ;
+
+statement
+ : 'return' expression ';'
+ ;
+
+expression
+ : additiveExpression
+ ;
+
+additiveExpression
+ : multiplicativeExpression (('+' | '-') multiplicativeExpression)*
+ ;
+
+multiplicativeExpression
+ : unaryExpression (('*' | '/') unaryExpression)*
+ ;
+
+unaryExpression
+ : ('-') unaryExpression
+ | primaryExpression
+ ;
+
+primaryExpression
+ : INTEGER_LITERAL
+ | '(' expression ')'
+ ;
+
+INTEGER_LITERAL
+ : [1-9][0-9]*
+ | [0]
+ ;
+```
diff --git a/docs/chapter02/02-post-peephole-ex1.gv b/docs/chapter02/02-post-peephole-ex1.gv
new file mode 100644
index 00000000..6c2b23ae
--- /dev/null
+++ b/docs/chapter02/02-post-peephole-ex1.gv
@@ -0,0 +1,17 @@
+digraph chapter02 {
+/*
+return 1+2*3+-5;
+*/
+ rankdir=BT;
+ ordering="in";
+ concentrate="true";
+ subgraph cluster_Nodes {
+ Start1 [ shape=box style=filled fillcolor=yellow label="Start" ];
+ Con_13 [ label="#2" ];
+ Return14 [ shape=box style=filled fillcolor=yellow label="Return" ];
+ }
+ edge [ fontname=Helvetica, fontsize=8 ];
+ Con_13 -> Start1[taillabel=0 style=dotted];
+ Return14 -> Start1[taillabel=0 color=red];
+ Return14 -> Con_13[taillabel=1];
+}
diff --git a/docs/chapter02/02-post-peephole-ex1.svg b/docs/chapter02/02-post-peephole-ex1.svg
new file mode 100644
index 00000000..db27e30d
--- /dev/null
+++ b/docs/chapter02/02-post-peephole-ex1.svg
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by graphviz version 2.43.0 (0)
+ -->
+<!-- Title: chapter02 Pages: 1 -->
+<svg width="114pt" height="220pt"
+ viewBox="0.00 0.00 114.00 220.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 216)">
+<title>chapter02</title>
+<polygon fill="white" stroke="transparent" points="-4,4 -4,-216 110,-216 110,4 -4,4"/>
+<g id="clust1" class="cluster">
+<title>cluster_Nodes</title>
+<polygon fill="none" stroke="black" points="8,-8 8,-204 98,-204 98,-8 8,-8"/>
+</g>
+<!-- Start1 -->
+<g id="node1" class="node">
+<title>Start1</title>
+<polygon fill="yellow" stroke="black" points="80,-196 26,-196 26,-160 80,-160 80,-196"/>
+<text text-anchor="middle" x="53" y="-174.3" font-family="Times,serif" font-size="14.00">Start</text>
+</g>
+<!-- Con_13 -->
+<g id="node2" class="node">
+<title>Con_13</title>
+<ellipse fill="none" stroke="black" cx="43" cy="-106" rx="27" ry="18"/>
+<text text-anchor="middle" x="43" y="-102.3" font-family="Times,serif" font-size="14.00">#2</text>
+</g>
+<!-- Con_13&#45;&gt;Start1 -->
+<g id="edge1" class="edge">
+<title>Con_13&#45;&gt;Start1</title>
+<path fill="none" stroke="black" stroke-dasharray="1,5" d="M45.42,-123.95C46.52,-131.65 47.85,-140.97 49.09,-149.64"/>
+<polygon fill="black" stroke="black" points="45.65,-150.32 50.53,-159.72 52.58,-149.33 45.65,-150.32"/>
+<text text-anchor="middle" x="42.42" y="-126.55" font-family="Helvetica,sans-Serif" font-size="8.00">0</text>
+</g>
+<!-- Return14 -->
+<g id="node3" class="node">
+<title>Return14</title>
+<polygon fill="yellow" stroke="black" points="86,-52 20,-52 20,-16 86,-16 86,-52"/>
+<text text-anchor="middle" x="53" y="-30.3" font-family="Times,serif" font-size="14.00">Return</text>
+</g>
+<!-- Return14&#45;&gt;Start1 -->
+<g id="edge2" class="edge">
+<title>Return14&#45;&gt;Start1</title>
+<path fill="none" stroke="red" d="M64.05,-52.39C69.74,-62.38 76.08,-75.42 79,-88 82.62,-103.59 82.62,-108.41 79,-124 76.9,-133.04 73.03,-142.32 68.91,-150.53"/>
+<polygon fill="red" stroke="red" points="65.69,-149.14 64.05,-159.61 71.86,-152.45 65.69,-149.14"/>
+<text text-anchor="middle" x="61.05" y="-54.99" font-family="Helvetica,sans-Serif" font-size="8.00">0</text>
+</g>
+<!-- Return14&#45;&gt;Con_13 -->
+<g id="edge3" class="edge">
+<title>Return14&#45;&gt;Con_13</title>
+<path fill="none" stroke="black" d="M50.53,-52.3C49.43,-60.02 48.1,-69.29 46.87,-77.89"/>
+<polygon fill="black" stroke="black" points="43.39,-77.5 45.44,-87.9 50.32,-78.49 43.39,-77.5"/>
+<text text-anchor="middle" x="47.53" y="-54.9" font-family="Helvetica,sans-Serif" font-size="8.00">1</text>
+</g>
+</g>
+</svg>
diff --git a/docs/chapter02/02-pre-peephole-ex1.gv b/docs/chapter02/02-pre-peephole-ex1.gv
new file mode 100644
index 00000000..411de5ee
--- /dev/null
+++ b/docs/chapter02/02-pre-peephole-ex1.gv
@@ -0,0 +1,34 @@
+digraph chapter02 {
+/*
+return 1+2*3+-5;
+*/
+ rankdir=BT;
+ ordering="in";
+ concentrate="true";
+ subgraph cluster_Nodes {
+ Start1 [ shape=box style=filled fillcolor=yellow label="Start" ];
+ Con_2 [ label="#1" ];
+ Con_3 [ label="#2" ];
+ Con_4 [ label="#3" ];
+ Mul5 [ label="*" ];
+ Con_6 [ label="#5" ];
+ Minus7 [ label="-" ];
+ Add8 [ label="+" ];
+ Add9 [ label="+" ];
+ Return10 [ shape=box style=filled fillcolor=yellow label="Return" ];
+ }
+ edge [ fontname=Helvetica, fontsize=8 ];
+ Con_2 -> Start1[taillabel=0 style=dotted];
+ Con_3 -> Start1[taillabel=0 style=dotted];
+ Con_4 -> Start1[taillabel=0 style=dotted];
+ Mul5 -> Con_3[taillabel=1];
+ Mul5 -> Con_4[taillabel=2];
+ Con_6 -> Start1[taillabel=0 style=dotted];
+ Minus7 -> Con_6[taillabel=1];
+ Add8 -> Mul5[taillabel=1];
+ Add8 -> Minus7[taillabel=2];
+ Add9 -> Con_2[taillabel=1];
+ Add9 -> Add8[taillabel=2];
+ Return10 -> Start1[taillabel=0 color=red];
+ Return10 -> Add9[taillabel=1];
+}
diff --git a/docs/chapter02/02-pre-peephole-ex1.svg b/docs/chapter02/02-pre-peephole-ex1.svg
new file mode 100644
index 00000000..29c76bd8
--- /dev/null
+++ b/docs/chapter02/02-pre-peephole-ex1.svg
@@ -0,0 +1,168 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by graphviz version 2.43.0 (0)
+ -->
+<!-- Title: chapter02 Pages: 1 -->
+<svg width="278pt" height="436pt"
+ viewBox="0.00 0.00 278.00 436.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 432)">
+<title>chapter02</title>
+<polygon fill="white" stroke="transparent" points="-4,4 -4,-432 274,-432 274,4 -4,4"/>
+<g id="clust1" class="cluster">
+<title>cluster_Nodes</title>
+<polygon fill="none" stroke="black" points="8,-8 8,-420 262,-420 262,-8 8,-8"/>
+</g>
+<!-- Start1 -->
+<g id="node1" class="node">
+<title>Start1</title>
+<polygon fill="yellow" stroke="black" points="162,-412 108,-412 108,-376 162,-376 162,-412"/>
+<text text-anchor="middle" x="135" y="-390.3" font-family="Times,serif" font-size="14.00">Start</text>
+</g>
+<!-- Con_2 -->
+<g id="node2" class="node">
+<title>Con_2</title>
+<ellipse fill="none" stroke="black" cx="222" cy="-178" rx="27" ry="18"/>
+<text text-anchor="middle" x="222" y="-174.3" font-family="Times,serif" font-size="14.00">#1</text>
+</g>
+<!-- Con_2&#45;&gt;Start1 -->
+<g id="edge1" class="edge">
+<title>Con_2&#45;&gt;Start1</title>
+<path fill="none" stroke="black" stroke-dasharray="1,5" d="M230.65,-195.54C245.5,-226.47 271.62,-294.07 243,-340 227.56,-364.77 196.8,-378.29 172.04,-385.46"/>
+<polygon fill="black" stroke="black" points="170.97,-382.13 162.2,-388.08 172.77,-388.89 170.97,-382.13"/>
+<text text-anchor="middle" x="227.65" y="-198.14" font-family="Helvetica,sans-Serif" font-size="8.00">0</text>
+</g>
+<!-- Con_3 -->
+<g id="node3" class="node">
+<title>Con_3</title>
+<ellipse fill="none" stroke="black" cx="63" cy="-322" rx="27" ry="18"/>
+<text text-anchor="middle" x="63" y="-318.3" font-family="Times,serif" font-size="14.00">#2</text>
+</g>
+<!-- Con_3&#45;&gt;Start1 -->
+<g id="edge2" class="edge">
+<title>Con_3&#45;&gt;Start1</title>
+<path fill="none" stroke="black" stroke-dasharray="1,5" d="M77.57,-337.17C86.92,-346.25 99.3,-358.29 110.17,-368.86"/>
+<polygon fill="black" stroke="black" points="107.79,-371.43 117.4,-375.89 112.67,-366.41 107.79,-371.43"/>
+<text text-anchor="middle" x="74.57" y="-339.77" font-family="Helvetica,sans-Serif" font-size="8.00">0</text>
+</g>
+<!-- Con_4 -->
+<g id="node4" class="node">
+<title>Con_4</title>
+<ellipse fill="none" stroke="black" cx="135" cy="-322" rx="27" ry="18"/>
+<text text-anchor="middle" x="135" y="-318.3" font-family="Times,serif" font-size="14.00">#3</text>
+</g>
+<!-- Con_4&#45;&gt;Start1 -->
+<g id="edge3" class="edge">
+<title>Con_4&#45;&gt;Start1</title>
+<path fill="none" stroke="black" stroke-dasharray="1,5" d="M135,-340.3C135,-348.02 135,-357.29 135,-365.89"/>
+<polygon fill="black" stroke="black" points="131.5,-365.9 135,-375.9 138.5,-365.9 131.5,-365.9"/>
+<text text-anchor="middle" x="132" y="-342.9" font-family="Helvetica,sans-Serif" font-size="8.00">0</text>
+</g>
+<!-- Mul5 -->
+<g id="node5" class="node">
+<title>Mul5</title>
+<ellipse fill="none" stroke="black" cx="135" cy="-250" rx="27" ry="18"/>
+<text text-anchor="middle" x="135" y="-246.3" font-family="Times,serif" font-size="14.00">*</text>
+</g>
+<!-- Mul5&#45;&gt;Con_3 -->
+<g id="edge4" class="edge">
+<title>Mul5&#45;&gt;Con_3</title>
+<path fill="none" stroke="black" d="M120.43,-265.17C110.25,-275.06 96.48,-288.45 84.97,-299.64"/>
+<polygon fill="black" stroke="black" points="82.53,-297.13 77.8,-306.62 87.41,-302.15 82.53,-297.13"/>
+<text text-anchor="middle" x="117.43" y="-267.77" font-family="Helvetica,sans-Serif" font-size="8.00">1</text>
+</g>
+<!-- Mul5&#45;&gt;Con_4 -->
+<g id="edge5" class="edge">
+<title>Mul5&#45;&gt;Con_4</title>
+<path fill="none" stroke="black" d="M135,-268.3C135,-276.02 135,-285.29 135,-293.89"/>
+<polygon fill="black" stroke="black" points="131.5,-293.9 135,-303.9 138.5,-293.9 131.5,-293.9"/>
+<text text-anchor="middle" x="132" y="-270.9" font-family="Helvetica,sans-Serif" font-size="8.00">2</text>
+</g>
+<!-- Con_6 -->
+<g id="node6" class="node">
+<title>Con_6</title>
+<ellipse fill="none" stroke="black" cx="207" cy="-322" rx="27" ry="18"/>
+<text text-anchor="middle" x="207" y="-318.3" font-family="Times,serif" font-size="14.00">#5</text>
+</g>
+<!-- Con_6&#45;&gt;Start1 -->
+<g id="edge6" class="edge">
+<title>Con_6&#45;&gt;Start1</title>
+<path fill="none" stroke="black" stroke-dasharray="1,5" d="M192.43,-337.17C183.08,-346.25 170.7,-358.29 159.83,-368.86"/>
+<polygon fill="black" stroke="black" points="157.33,-366.41 152.6,-375.89 162.21,-371.43 157.33,-366.41"/>
+<text text-anchor="middle" x="189.43" y="-339.77" font-family="Helvetica,sans-Serif" font-size="8.00">0</text>
+</g>
+<!-- Minus7 -->
+<g id="node7" class="node">
+<title>Minus7</title>
+<ellipse fill="none" stroke="black" cx="207" cy="-250" rx="27" ry="18"/>
+<text text-anchor="middle" x="207" y="-246.3" font-family="Times,serif" font-size="14.00">&#45;</text>
+</g>
+<!-- Minus7&#45;&gt;Con_6 -->
+<g id="edge7" class="edge">
+<title>Minus7&#45;&gt;Con_6</title>
+<path fill="none" stroke="black" d="M207,-268.3C207,-276.02 207,-285.29 207,-293.89"/>
+<polygon fill="black" stroke="black" points="203.5,-293.9 207,-303.9 210.5,-293.9 203.5,-293.9"/>
+<text text-anchor="middle" x="204" y="-270.9" font-family="Helvetica,sans-Serif" font-size="8.00">1</text>
+</g>
+<!-- Add8 -->
+<g id="node8" class="node">
+<title>Add8</title>
+<ellipse fill="none" stroke="black" cx="140" cy="-178" rx="27" ry="18"/>
+<text text-anchor="middle" x="140" y="-174.3" font-family="Times,serif" font-size="14.00">+</text>
+</g>
+<!-- Add8&#45;&gt;Mul5 -->
+<g id="edge8" class="edge">
+<title>Add8&#45;&gt;Mul5</title>
+<path fill="none" stroke="black" d="M138.76,-196.3C138.21,-204.02 137.55,-213.29 136.94,-221.89"/>
+<polygon fill="black" stroke="black" points="133.44,-221.67 136.22,-231.9 140.43,-222.17 133.44,-221.67"/>
+<text text-anchor="middle" x="135.76" y="-198.9" font-family="Helvetica,sans-Serif" font-size="8.00">1</text>
+</g>
+<!-- Add8&#45;&gt;Minus7 -->
+<g id="edge9" class="edge">
+<title>Add8&#45;&gt;Minus7</title>
+<path fill="none" stroke="black" d="M153.88,-193.5C163.14,-203.17 175.47,-216.06 185.93,-226.98"/>
+<polygon fill="black" stroke="black" points="183.69,-229.71 193.14,-234.51 188.75,-224.87 183.69,-229.71"/>
+<text text-anchor="middle" x="150.88" y="-196.1" font-family="Helvetica,sans-Serif" font-size="8.00">2</text>
+</g>
+<!-- Add9 -->
+<g id="node9" class="node">
+<title>Add9</title>
+<ellipse fill="none" stroke="black" cx="140" cy="-106" rx="27" ry="18"/>
+<text text-anchor="middle" x="140" y="-102.3" font-family="Times,serif" font-size="14.00">+</text>
+</g>
+<!-- Add9&#45;&gt;Con_2 -->
+<g id="edge10" class="edge">
+<title>Add9&#45;&gt;Con_2</title>
+<path fill="none" stroke="black" d="M156.2,-120.83C168.18,-131.06 184.69,-145.15 198.18,-156.66"/>
+<polygon fill="black" stroke="black" points="196.23,-159.61 206.11,-163.44 200.78,-154.28 196.23,-159.61"/>
+<text text-anchor="middle" x="153.2" y="-123.43" font-family="Helvetica,sans-Serif" font-size="8.00">1</text>
+</g>
+<!-- Add9&#45;&gt;Add8 -->
+<g id="edge11" class="edge">
+<title>Add9&#45;&gt;Add8</title>
+<path fill="none" stroke="black" d="M140,-124.3C140,-132.02 140,-141.29 140,-149.89"/>
+<polygon fill="black" stroke="black" points="136.5,-149.9 140,-159.9 143.5,-149.9 136.5,-149.9"/>
+<text text-anchor="middle" x="137" y="-126.9" font-family="Helvetica,sans-Serif" font-size="8.00">2</text>
+</g>
+<!-- Return10 -->
+<g id="node10" class="node">
+<title>Return10</title>
+<polygon fill="yellow" stroke="black" points="125,-52 59,-52 59,-16 125,-16 125,-52"/>
+<text text-anchor="middle" x="92" y="-30.3" font-family="Times,serif" font-size="14.00">Return</text>
+</g>
+<!-- Return10&#45;&gt;Start1 -->
+<g id="edge12" class="edge">
+<title>Return10&#45;&gt;Start1</title>
+<path fill="none" stroke="red" d="M75.73,-52.27C53.67,-77.55 17,-127.09 17,-177 17,-177 17,-177 17,-251 17,-290.8 4.09,-307.45 27,-340 43.69,-363.72 74.05,-377.3 98.34,-384.76"/>
+<polygon fill="red" stroke="red" points="97.41,-388.14 107.99,-387.51 99.33,-381.4 97.41,-388.14"/>
+<text text-anchor="middle" x="72.73" y="-54.87" font-family="Helvetica,sans-Serif" font-size="8.00">0</text>
+</g>
+<!-- Return10&#45;&gt;Add9 -->
+<g id="edge13" class="edge">
+<title>Return10&#45;&gt;Add9</title>
+<path fill="none" stroke="black" d="M103.87,-52.3C109.82,-60.98 117.12,-71.63 123.6,-81.08"/>
+<polygon fill="black" stroke="black" points="120.71,-83.06 129.25,-89.33 126.49,-79.1 120.71,-83.06"/>
+<text text-anchor="middle" x="100.87" y="-54.9" font-family="Helvetica,sans-Serif" font-size="8.00">1</text>
+</g>
+</g>
+</svg>
diff --git a/docs/chapter02/Makefile b/docs/chapter02/Makefile
new file mode 100644
index 00000000..12659b8f
--- /dev/null
+++ b/docs/chapter02/Makefile
@@ -0,0 +1,12 @@
+GV := $(wildcard *.gv)
+SVG := $(patsubst %.gv,%.svg,$(GV))
+
+.PHONY: all
+all: $(SVG)
+
+%.svg: %.gv
+ dot -Tsvg $< > $@
+
+.PHONY: clean
+clean:
+ @rm -f $(SVG)
diff --git a/docs/chapter02/README.md b/docs/chapter02/README.md
new file mode 100644
index 00000000..0e3f2a8d
--- /dev/null
+++ b/docs/chapter02/README.md
@@ -0,0 +1,282 @@
+# Chapter 2: Arithmetic
+
+
+# Table of Contents
+
+1. [Extensions to Intermediate Representation](#extensions-to-intermediate-representation)
+2. [Peephole Optimizations](#peephole-optimizations)
+3. [Constant Folding and Constant Propagation](#constant-folding-and-constant-propagation)
+4. [Nodes Pre Peephole Optimization](#nodes-pre-peephole-optimization)
+5. [Post-peephole](#post-peephole)
+6. [Demonstration](#demonstration)
+
+You can also read [this chapter](https://github.com/SeaOfNodes/Simple/tree/linear-chapter02) in a linear Git revision history on the [linear](https://github.com/SeaOfNodes/Simple/tree/linear) branch and [compare](https://github.com/SeaOfNodes/Simple/compare/linear-chapter01...linear-chapter02) it to the previous chapter.
+
+
+In this chapter we extend the language grammar to include arithmetic operations such as addition, subtraction,
+multiplication, division, and unary minus. This allows us to write statements such as:
+
+```
+return 1 + 2 * 3 + -5;
+```
+
+Here is the [complete language grammar](02-grammar.md) for this chapter.
+
+## Extensions to Intermediate Representation
+
+In [Chapter 1](../chapter01/README.md) we introduced following nodes.
+
+| Node Name | Type | Description | Inputs | Value |
+|-----------|---------|------------------------------------|------------------------------------------------------------------|-------------------------------------------------------|
+| Start | Control | Start of function | None | None for now as we do not have function arguments yet |
+| Return | Control | End of function | Predecessor control node, Data node value | Return value of the function |
+| Constant | Data | Constants such as integer literals | None, however Start node is set as input to enable graph walking | Value of the constant |
+
+We extend the set of nodes by adding following additional node types.
+
+| Node Name | Type | Description | Inputs | Value |
+|-----------|------|-------------------------------|------------------------------------------------------------|-----------------------------|
+| Add | Data | Add two values | Two data nodes, values are added, order not important | Result of the add operation |
+| Sub | Data | Subtract a value from another | Two data nodes, values are subtracted, order matters | Result of the subtract |
+| Mul | Data | Multiply two values | Two data nodes, values are multiplied, order not important | Result of the multiply |
+| Div | Data | Divide a value by another | Two data nodes, values are divided, order matters | Result of the division |
+| Minus | Data | Negate a value | One data node, value is negated | Result of the unary minus |
+
+
+## *Value* equality vs *Reference* equality
+
+In much of Simple, Nodes are looked up (`find()` calls) via *reference*
+equality. This is by far the most common case. In [Chapter
+9](../chapter09/README.md) we introduce *value* equality for the first time.
+In both cases the choice of value-vs-reference equality is intentional: it is
+*never* correct to "just pick one or the other kind of equality". When in
+doubt check the context: only *Global Value Numbering* uses value equality;
+everywhere we mean reference equality.
+
+
+## Peephole Optimizations
+
+Nodes in the graph can be peephole-optimized. The graph is viewed through a
+"peephole", a small chunk of graph, and if a certain pattern is detected we
+locally rewrite the graph.
+
+During parsing, these peephole optimizations are particularly easy to check and
+apply: there are no uses (yet) of a just-created Node from a just-parsed piece
+of syntax, so there's no effort to the "rewrite" part of the problem. We just
+replace in-place before installing Nodes into the graph.
+
+This replacement might allow us to *kill* the unused inputs from the replaced
+Node, basically doing a modest Dead Code Elimination during parsing.
+
+E.g. Suppose we already parsed out a constant `1`, and a constant `2`; then when we
+parse an `Add(1,2)`, the peephole rule for constant math replaces the Add with a
+constant `3`. At this point, we also *kill* the unused `Add`, which recursively
+may *kill* the unused constants `1` and `2`.
+
+
+
+## Constant Folding and Constant Propagation
+
+In this chapter and next we focus on a particular peephole optimization:
+constant folding and constant propagation. Since we do not have non-constant values
+until [Chapter 4](../chapter04/README.md), the main feature we demonstrate now is constant folding.
+However, we introduce some additional ideas into the compiler at this stage, to
+set the scene for Chapter 4.
+
+It is useful for the compiler to know at various points of the program whether
+a node's value is a constant. The compiler can use this knowledge to perform various
+optimizations such as:
+
+* Evaluate expressions at compile time and replace an expression with a
+ constant. This idea can be extended in a number of ways and is called
+ [Partial Evaluation](https://en.wikipedia.org/wiki/Partial_evaluation).
+* The compiler may be able to identify regions of code that are dead and no
+ longer needed, such as when a conditional branch always take one of the
+ branches ([Chapter 6](../chapter06/README.md)).
+* Pointers may be known to be not-null, and a null check can be skipped ([Chapter 10](../chapter10/README.md)).
+* Array indices may be known to be in-range, and a range check can be skipped.
+* Many additional optimizations are possible when the compiler learns more
+ about the possible set of Node values.
+
+In order to achieve above, we need a way to talk about what kinds of values a Node can take
+on at runtime - we call these Types, and we annotate Nodes with Types.
+
+The Type annotation serves two purposes:
+
+* it defines the set of operations allowed on the Node
+* it defines the set of values the Node takes on
+
+The Type itself is represented as a Java class; the Java classes are used as
+convenient to represent the Type's structure; all types are subtypes of the
+class `Type`. For now, we only have the following hierarchy of types:
+
+```
+Type
++-- TypeInteger
+```
+
+It turns out that the set of values associated with a Type at a specific Node
+can be conveniently represented as a *lattice*
+[wiki/Lattice_(order)](https://en.wikipedia.org/wiki/Lattice_(order)). Our lattice has the following structure:
+
+![Lattice](./lattice.svg)
+
+Our lattice elements can be one of three types:
+
+* The highest element is "top", denoted by ⊤; assigning ⊤ means that the Node's
+ value may or may not be a compile time constant.
+* All elements in the middle are constants.
+* The lowest is "bottom", denoted by ⊥; assigning ⊥ means that we know that the
+ Node's value is **not** a compile time constant.
+
+An invariant of peephole optimizations is that the type of a Node always moves
+*up* the lattice (towards "top"); peepholes are *pessmistic* assuming the worst
+until they can prove better. A later *optimistic* optimization will start all
+Nodes at *top* and move Types *down* the lattice as eager assumptions are
+proven wrong.
+
+In later chapters we will explore extending this lattice, as it frequently
+forms the heart of core optimizations we want our compiler to do.
+
+We add a `_type` field to every Node, to store its current computed best
+`Type`. We need a field to keep the optimizer runtime linear, and later when
+doing an optimistic version of constant propagation (called [Sparse Conditional
+Constant Propagation](https://en.wikipedia.org/wiki/Sparse_conditional_constant_propagation)).
+
+We add a `_type` field even to `Start` and `Return`, and later to all control
+Nodes - because the Sea of Nodes does not distinguish between control and data.
+Both nodes are equally peepholed and optimized, and this will be covered
+starting in [Chapter 4](../chapter04/README.md) and [Chapter
+5](../chapter05/README.md).
+
+
+There are other important properties of the Lattice that we discuss in [Chapter
+4](../chapter04/README.md) and [Chapter 10](../chapter10/README.md), such as the "meet" and "join" operators and their rules.
+
+
+## Nodes Pre Peephole Optimization
+
+The following visual shows how the graph looks like pre-peephole optimization:
+
+![Example Visual](./02-pre-peephole-ex1.svg)
+
+* Control nodes appear as square boxes with yellow background
+* Control edges are in bold red
+* The edges from Constants to Start are shown in dotted lines as these are not true control edges
+* We label each edge with its position in the node's list of inputs.
+
+
+## Post-peephole
+
+![Example Visual](./02-post-peephole-ex1.svg)
+
+
+## Demonstration
+To demonstrate how the optimisation works, let's consider the following code:
+```
+return 1+2;
+```
+This is parsed as:
+```java
+ var lhs = parseMultiplication();
+ if (match("+")) return new AddNode(lhs, parseAddition()).peephole();
+```
+When the peephole is called on the AddNode node it first calls the compute function:
+```java
+ public final Node peephole( ) {
+ // Compute initial or improved Type
+ Type type = _type = compute();
+```
+```java
+ public Type compute() {
+ if( in(1)._type instanceof TypeInteger i0 &&
+ in(2)._type instanceof TypeInteger i1 ) {
+ if (i0.isConstant() && i1.isConstant())
+ return TypeInteger.constant(i0.value()+i1.value());
+ }
+ return Type.BOTTOM;
+ }
+```
+In our case both of the operands(`1`, `2`) are constants, and instance of
+`TypeInteger`.
+Since `compute` returns a type, and we know that both of our operands are constants, we can do
+constant propagation/folding where we compute the result of the expression so that instead of having `1+2` we can
+have `3`.
+
+The 'TypeInteger' interface inherits from type but also has a field that stores the value observed for that type.
+```java
+ private final long _con
+```
+
+AddNode has the layout:
+
+![Example Visual](./02-demonstration-pre-peephole.svg)
+
+Since, we already know the value of both of the operands, we can just add them together and replace the
+old node with a constant node that just holds the result of the expression.
+
+To do this - we have to recognize this happens when the type computed for the node is already a constant, but the node
+is still not an instance of constant node.
+
+This can be captured by:
+```java
+// Replace constant computations from non-constants with a constant node
+if (!(this instanceof ConstantNode) && type.isConstant()) {
+}
+```
+Note how we return a constant type when doing constant folding
+```java
+return TypeInteger.constant(i0.value()+i1.value());
+```
+We can create a constant node in the following way:
+```java
+return new ConstantNode(type).peephole();
+```
+This recursively calls the peephole algorithm.
+Now, we end up with this:
+
+![Example Visual](./02-demonstration-pre-peephole-nokill.svg)
+
+Notice, how we added the constant node holding a constant value of `3`.
+The remaining add node is unused and can be killed.
+
+We can call the kill function before this way:
+```java
+// Replace constant computations from non-constants with a constant node
+if (!(this instanceof ConstantNode) && type.isConstant()) {
+ kill(); // Kill `this` because replacing with a Constant
+ return new ConstantNode(type).peephole();
+```
+This is responsible for deleting the dangling add node.
+
+```java
+public void kill( ) {
+ assert isUnused(); // Has no uses, so it is dead
+ for( int i=0; i<nIns(); i++ )
+ setDef(i,null); // Set all inputs to null, recursively killing unused Nodes
+
+ _inputs.clear();
+ _type=null; // Flag as dead
+ assert isDead(); // Really dead now
+}
+```
+
+`isUnused` - This makes sure the current node has no outputs.
+Ideally, in our case there would be one output as the return statement is
+using the add expression.
+However, the optimisation happens before even creating the return node, which means that at this point
+there are no users of the add node.
+
+We can now freely go ahead and delete the node.
+
+```java
+for(int i=0; i<nIns(); i++ )
+ setDef(i,null); // Set all inputs to null, recursively killing unused Nodes
+
+_inputs.clear();
+_type=null;
+```
+
+We finally end up with this:
+
+![Example Visual](./02-demonstration-peephole.svg)
diff --git a/docs/chapter02/lattice.gv b/docs/chapter02/lattice.gv
new file mode 100644
index 00000000..fe8df136
--- /dev/null
+++ b/docs/chapter02/lattice.gv
@@ -0,0 +1,40 @@
+digraph Lattice {
+ compound = true;
+ // Define nodes
+ splines=false;
+ node [shape=circle, style=filled];
+ nodesep=0.6;
+
+ // ----------------------
+ // Integers
+ INT_TOP [label=<<B>⊤</B >:int>, fillcolor=lightblue, shape=oval];
+ INT_BOT [label=<<B>⊥</B>:int>, fillcolor=lightblue, shape=oval];
+
+ INT_MIN [label="-n" , fillcolor=lightblue];
+ INT_MINDOTS [label="...", fillcolor=lightblue shape=oval];
+ INT_MINUS [label="-1" , fillcolor=lightblue];
+ INT_ZERO [label="0" , fillcolor=lightblue];
+ INT_PLUS [label="1" , fillcolor=lightblue];
+ INT_MAXDOTS [label="...", fillcolor=lightblue, shape=oval];
+ INT_MAX [label="+n" , fillcolor=lightblue];
+
+ INT_TOP -> INT_MIN;
+ INT_TOP -> INT_MINDOTS;
+ INT_TOP -> INT_MINUS;
+ INT_TOP -> INT_ZERO;
+ INT_TOP -> INT_PLUS;
+ INT_TOP -> INT_MAXDOTS;
+ INT_TOP -> INT_MAX;
+ INT_MIN -> INT_BOT;
+ INT_MINDOTS -> INT_BOT;
+ INT_MINUS -> INT_BOT;
+ INT_ZERO -> INT_BOT;
+ INT_PLUS -> INT_BOT;
+ INT_MAXDOTS -> INT_BOT;
+ INT_MAX -> INT_BOT;
+
+ INT_MIN -> INT_MINDOTS -> INT_MINUS -> INT_ZERO -> INT_PLUS -> INT_MAXDOTS -> INT_MAX [style=invis];
+
+ // Additional customization
+ {rank=same; INT_MIN; INT_MINDOTS; INT_MINUS; INT_ZERO; INT_PLUS; INT_MAXDOTS; INT_MAX; }
+}
diff --git a/docs/chapter02/lattice.svg b/docs/chapter02/lattice.svg
new file mode 100644
index 00000000..b11d57aa
--- /dev/null
+++ b/docs/chapter02/lattice.svg
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by graphviz version 12.2.0 (20241103.1931)
+ -->
+<!-- Title: Lattice Pages: 1 -->
+<svg width="564pt" height="194pt"
+ viewBox="0.00 0.00 563.75 194.31" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 190.31)">
+<title>Lattice</title>
+<polygon fill="white" stroke="none" points="-4,4 -4,-190.31 559.75,-190.31 559.75,4 -4,4"/>
+<!-- INT_TOP -->
+<g id="node1" class="node">
+<title>INT_TOP</title>
+<ellipse fill="lightblue" stroke="black" cx="276.6" cy="-168.31" rx="33.95" ry="18"/>
+<text text-anchor="start" x="259.72" y="-164.26" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊤</text>
+<text text-anchor="start" x="275.47" y="-164.26" font-family="Times New Roman,serif" font-size="14.00">:int</text>
+</g>
+<!-- INT_MIN -->
+<g id="node3" class="node">
+<title>INT_MIN</title>
+<ellipse fill="lightblue" stroke="black" cx="18.6" cy="-93.15" rx="18.6" ry="18.6"/>
+<text text-anchor="middle" x="18.6" y="-88.1" font-family="Times New Roman,serif" font-size="14.00">&#45;n</text>
+</g>
+<!-- INT_TOP&#45;&gt;INT_MIN -->
+<g id="edge1" class="edge">
+<title>INT_TOP&#45;&gt;INT_MIN</title>
+<path fill="none" stroke="black" d="M247,-158.92C197.13,-144.78 96.96,-116.37 47.53,-102.36"/>
+<polygon fill="black" stroke="black" points="48.71,-99.06 38.13,-99.69 46.8,-105.79 48.71,-99.06"/>
+</g>
+<!-- INT_MINDOTS -->
+<g id="node4" class="node">
+<title>INT_MINDOTS</title>
+<ellipse fill="lightblue" stroke="black" cx="107.6" cy="-93.15" rx="27" ry="18"/>
+<text text-anchor="middle" x="107.6" y="-88.1" font-family="Times New Roman,serif" font-size="14.00">...</text>
+</g>
+<!-- INT_TOP&#45;&gt;INT_MINDOTS -->
+<g id="edge2" class="edge">
+<title>INT_TOP&#45;&gt;INT_MINDOTS</title>
+<path fill="none" stroke="black" d="M250.93,-156.2C221.31,-143.38 172.55,-122.27 140.26,-108.29"/>
+<polygon fill="black" stroke="black" points="141.94,-105.21 131.38,-104.45 139.16,-111.63 141.94,-105.21"/>
+</g>
+<!-- INT_MINUS -->
+<g id="node5" class="node">
+<title>INT_MINUS</title>
+<ellipse fill="lightblue" stroke="black" cx="196.6" cy="-93.15" rx="18.6" ry="18.6"/>
+<text text-anchor="middle" x="196.6" y="-88.1" font-family="Times New Roman,serif" font-size="14.00">&#45;1</text>
+</g>
+<!-- INT_TOP&#45;&gt;INT_MINUS -->
+<g id="edge3" class="edge">
+<title>INT_TOP&#45;&gt;INT_MINUS</title>
+<path fill="none" stroke="black" d="M260.02,-152.15C247.97,-141.13 231.55,-126.11 218.43,-114.12"/>
+<polygon fill="black" stroke="black" points="220.83,-111.57 211.09,-107.41 216.11,-116.74 220.83,-111.57"/>
+</g>
+<!-- INT_ZERO -->
+<g id="node6" class="node">
+<title>INT_ZERO</title>
+<ellipse fill="lightblue" stroke="black" cx="276.6" cy="-93.15" rx="18" ry="18"/>
+<text text-anchor="middle" x="276.6" y="-88.1" font-family="Times New Roman,serif" font-size="14.00">0</text>
+</g>
+<!-- INT_TOP&#45;&gt;INT_ZERO -->
+<g id="edge4" class="edge">
+<title>INT_TOP&#45;&gt;INT_ZERO</title>
+<path fill="none" stroke="black" d="M276.6,-149.97C276.6,-141.92 276.6,-132.14 276.6,-123.02"/>
+<polygon fill="black" stroke="black" points="280.1,-123.13 276.6,-113.13 273.1,-123.13 280.1,-123.13"/>
+</g>
+<!-- INT_PLUS -->
+<g id="node7" class="node">
+<title>INT_PLUS</title>
+<ellipse fill="lightblue" stroke="black" cx="355.6" cy="-93.15" rx="18" ry="18"/>
+<text text-anchor="middle" x="355.6" y="-88.1" font-family="Times New Roman,serif" font-size="14.00">1</text>
+</g>
+<!-- INT_TOP&#45;&gt;INT_PLUS -->
+<g id="edge5" class="edge">
+<title>INT_TOP&#45;&gt;INT_PLUS</title>
+<path fill="none" stroke="black" d="M292.96,-152.15C305.01,-141 321.48,-125.74 334.51,-113.68"/>
+<polygon fill="black" stroke="black" points="336.82,-116.31 341.78,-106.95 332.06,-111.18 336.82,-116.31"/>
+</g>
+<!-- INT_MAXDOTS -->
+<g id="node8" class="node">
+<title>INT_MAXDOTS</title>
+<ellipse fill="lightblue" stroke="black" cx="443.6" cy="-93.15" rx="27" ry="18"/>
+<text text-anchor="middle" x="443.6" y="-88.1" font-family="Times New Roman,serif" font-size="14.00">...</text>
+</g>
+<!-- INT_TOP&#45;&gt;INT_MAXDOTS -->
+<g id="edge6" class="edge">
+<title>INT_TOP&#45;&gt;INT_MAXDOTS</title>
+<path fill="none" stroke="black" d="M302.32,-156.04C331.46,-143.28 379,-122.45 410.79,-108.53"/>
+<polygon fill="black" stroke="black" points="412.17,-111.74 419.93,-104.52 409.36,-105.33 412.17,-111.74"/>
+</g>
+<!-- INT_MAX -->
+<g id="node9" class="node">
+<title>INT_MAX</title>
+<ellipse fill="lightblue" stroke="black" cx="534.6" cy="-93.15" rx="21.15" ry="21.15"/>
+<text text-anchor="middle" x="534.6" y="-88.1" font-family="Times New Roman,serif" font-size="14.00">+n</text>
+</g>
+<!-- INT_TOP&#45;&gt;INT_MAX -->
+<g id="edge7" class="edge">
+<title>INT_TOP&#45;&gt;INT_MAX</title>
+<path fill="none" stroke="black" d="M306.19,-158.92C355.31,-144.99 453.21,-117.23 503.38,-103.01"/>
+<polygon fill="black" stroke="black" points="504.07,-106.45 512.73,-100.35 502.16,-99.71 504.07,-106.45"/>
+</g>
+<!-- INT_BOT -->
+<g id="node2" class="node">
+<title>INT_BOT</title>
+<ellipse fill="lightblue" stroke="black" cx="276.6" cy="-18" rx="33.95" ry="18"/>
+<text text-anchor="start" x="259.72" y="-13.95" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊥</text>
+<text text-anchor="start" x="275.47" y="-13.95" font-family="Times New Roman,serif" font-size="14.00">:int</text>
+</g>
+<!-- INT_MIN&#45;&gt;INT_BOT -->
+<g id="edge8" class="edge">
+<title>INT_MIN&#45;&gt;INT_BOT</title>
+<path fill="none" stroke="black" d="M36.33,-87.13C77.31,-75.51 179.45,-46.55 236.48,-30.38"/>
+<polygon fill="black" stroke="black" points="237.13,-33.83 245.79,-27.73 235.22,-27.09 237.13,-33.83"/>
+</g>
+<!-- INT_MIN&#45;&gt;INT_MINDOTS -->
+<!-- INT_MINDOTS&#45;&gt;INT_BOT -->
+<g id="edge9" class="edge">
+<title>INT_MINDOTS&#45;&gt;INT_BOT</title>
+<path fill="none" stroke="black" d="M129.69,-82.59C157.87,-70.39 206.96,-49.14 240.58,-34.59"/>
+<polygon fill="black" stroke="black" points="241.73,-37.91 249.51,-30.72 238.95,-31.48 241.73,-37.91"/>
+</g>
+<!-- INT_MINDOTS&#45;&gt;INT_MINUS -->
+<!-- INT_MINUS&#45;&gt;INT_BOT -->
+<g id="edge10" class="edge">
+<title>INT_MINUS&#45;&gt;INT_BOT</title>
+<path fill="none" stroke="black" d="M210.17,-79.75C221.61,-69.28 238.28,-54.04 252.06,-41.44"/>
+<polygon fill="black" stroke="black" points="254.05,-44.36 259.07,-35.03 249.33,-39.19 254.05,-44.36"/>
+</g>
+<!-- INT_MINUS&#45;&gt;INT_ZERO -->
+<!-- INT_ZERO&#45;&gt;INT_BOT -->
+<g id="edge11" class="edge">
+<title>INT_ZERO&#45;&gt;INT_BOT</title>
+<path fill="none" stroke="black" d="M276.6,-74.82C276.6,-66.77 276.6,-56.98 276.6,-47.86"/>
+<polygon fill="black" stroke="black" points="280.1,-47.98 276.6,-37.98 273.1,-47.98 280.1,-47.98"/>
+</g>
+<!-- INT_ZERO&#45;&gt;INT_PLUS -->
+<!-- INT_PLUS&#45;&gt;INT_BOT -->
+<g id="edge12" class="edge">
+<title>INT_PLUS&#45;&gt;INT_BOT</title>
+<path fill="none" stroke="black" d="M342.55,-80.07C331.3,-69.66 314.75,-54.33 301.04,-41.64"/>
+<polygon fill="black" stroke="black" points="303.78,-39.4 294.06,-35.17 299.02,-44.53 303.78,-39.4"/>
+</g>
+<!-- INT_PLUS&#45;&gt;INT_MAXDOTS -->
+<!-- INT_MAXDOTS&#45;&gt;INT_BOT -->
+<g id="edge13" class="edge">
+<title>INT_MAXDOTS&#45;&gt;INT_BOT</title>
+<path fill="none" stroke="black" d="M421.42,-82.44C393.56,-70.24 345.4,-49.14 312.32,-34.65"/>
+<polygon fill="black" stroke="black" points="314.1,-31.61 303.54,-30.8 311.29,-38.02 314.1,-31.61"/>
+</g>
+<!-- INT_MAXDOTS&#45;&gt;INT_MAX -->
+<!-- INT_MAX&#45;&gt;INT_BOT -->
+<g id="edge14" class="edge">
+<title>INT_MAX&#45;&gt;INT_BOT</title>
+<path fill="none" stroke="black" d="M514.35,-86.41C471.9,-74.38 372.87,-46.3 316.93,-30.44"/>
+<polygon fill="black" stroke="black" points="318.05,-27.12 307.47,-27.76 316.14,-33.85 318.05,-27.12"/>
+</g>
+</g>
+</svg>
diff --git a/src/main/java/com/seaofnodes/simple/GraphVisualizer.java b/src/main/java/com/seaofnodes/simple/GraphVisualizer.java
new file mode 100644
index 00000000..4c625be1
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/GraphVisualizer.java
@@ -0,0 +1,117 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.node.*;
+
+import java.util.*;
+
+/**
+ * Simple visualizer that outputs GraphViz dot format.
+ * The dot output must be saved to a file and run manually via dot to generate the SVG output.
+ * Currently, this is done manually.
+ */
+public class GraphVisualizer {
+
+ public String generateDotOutput(Parser parser) {
+
+ // Since the graph has cycles, we need to create a flat list of all the
+ // nodes in the graph.
+ Collection<Node> all = findAll(parser);
+ StringBuilder sb = new StringBuilder();
+ sb.append("digraph chapter02 {\n");
+ sb.append("/*\n");
+ sb.append(parser.src());
+ sb.append("\n*/\n");
+
+ // To keep the Scopes below the graph and pointing up into the graph we
+ // need to group the Nodes in a subgraph cluster, and the scopes into a
+ // different subgraph cluster. THEN we can draw edges between the
+ // scopes and nodes. If we try to cross subgraph cluster borders while
+ // still making the subgraphs DOT gets confused.
+ sb.append("\trankdir=BT;\n"); // Force Nodes before Scopes
+
+ // Preserve node input order
+ sb.append("\tordering=\"in\";\n");
+
+ // Merge multiple edges hitting the same node. Makes common shared
+ // nodes much prettier to look at.
+ sb.append("\tconcentrate=\"true\";\n");
+
+ // Just the Nodes first, in a cluster no edges
+ nodes(sb, all);
+
+ // Walk the Node edges
+ nodeEdges(sb, all);
+
+ sb.append("}\n");
+ return sb.toString();
+ }
+
+
+ private void nodes(StringBuilder sb, Collection<Node> all) {
+ // Just the Nodes first, in a cluster no edges
+ sb.append("\tsubgraph cluster_Nodes {\n"); // Magic "cluster_" in the subgraph name
+ for( Node n : all ) {
+ sb.append("\t\t").append(n.uniqueName()).append(" [ ");
+ String lab = n.glabel();
+ // control nodes have box shape
+ // other nodes are ellipses, i.e. default shape
+ if( n.isCFG() )
+ sb.append("shape=box style=filled fillcolor=yellow ");
+ sb.append("label=\"").append(lab).append("\" ");
+ sb.append("];\n");
+ }
+ sb.append("\t}\n"); // End Node cluster
+ }
+
+
+ // Walk the node edges
+ private void nodeEdges(StringBuilder sb, Collection<Node> all) {
+ // All them edge labels
+ sb.append("\tedge [ fontname=Helvetica, fontsize=8 ];\n");
+ for( Node n : all ) {
+ // In this chapter we do display the Constant->Start edge;
+ int i=0;
+ for( Node def : n._inputs ) {
+ if( def != null ) {
+ // Most edges land here use->def
+ sb.append('\t').append(n.uniqueName()).append(" -> ");
+ sb.append(def.uniqueName());
+ // Number edges, so we can see how they track
+ sb.append("[taillabel=").append(i);
+ if( n instanceof ConstantNode && def instanceof StartNode )
+ sb.append(" style=dotted");
+ // control edges are colored red
+ else if( def.isCFG() )
+ sb.append(" color=red");
+ sb.append("];\n");
+ }
+ i++;
+ }
+ }
+ }
+
+ /**
+ * Finds all nodes in the graph.
+ */
+ private Collection<Node> findAll(Parser parser) {
+ final StartNode start = Parser.START;
+ final HashMap<Integer, Node> all = new HashMap<>();
+ for( Node n : start._outputs )
+ walk(all, n);
+ return all.values();
+ }
+
+ /**
+ * Walk a subgraph and populate distinct nodes in the all list.
+ */
+ private void walk(HashMap<Integer, Node> all, Node n) {
+ if (all.get(n._nid) != null) return; // Been there, done that
+ all.put(n._nid, n);
+ for (Node c : n._inputs)
+ if (c != null)
+ walk(all, c);
+ for (Node c : n._outputs)
+ if (c != null)
+ walk(all, c);
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/Parser.java b/src/main/java/com/seaofnodes/simple/Parser.java
index 806c985d..72da8eeb 100644
--- a/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/src/main/java/com/seaofnodes/simple/Parser.java
@@ -1,6 +1,7 @@
package com.seaofnodes.simple;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.*;
/**
* The Parser converts a Simple source program to the Sea of Nodes intermediate
@@ -30,6 +31,8 @@ public class Parser {
START = new StartNode();
}
+ String src() { return new String( _lexer._input ); }
+
public ReturnNode parse() {
var ret = (ReturnNode) parseStatement();
if (!_lexer.isEOF()) throw error("Syntax error, unexpected " + _lexer.getAnyNextToken());
@@ -57,20 +60,61 @@ public class Parser {
* </pre>
* @return an expression {@link Node}, never {@code null}
*/
- private ReturnNode parseReturn() {
+ private Node parseReturn() {
var expr = require(parseExpression(), ";");
- return new ReturnNode(START, expr);
+ return new ReturnNode(START, expr).peephole();
}
/**
* Parse an expression of the form:
*
* <pre>
- * expr : primaryExpr
+ * expr : additiveExpr
* </pre>
* @return an expression {@link Node}, never {@code null}
*/
- private Node parseExpression() {
+ private Node parseExpression() { return parseAddition(); }
+
+ /**
+ * Parse an additive expression
+ *
+ * <pre>
+ * additiveExpr : multiplicativeExpr (('+' | '-') multiplicativeExpr)*
+ * </pre>
+ * @return an add expression {@link Node}, never {@code null}
+ */
+ private Node parseAddition() {
+ var lhs = parseMultiplication();
+ if (match("+")) return new AddNode(lhs, parseAddition()).peephole();
+ if (match("-")) return new SubNode(lhs, parseAddition()).peephole();
+ return lhs;
+ }
+
+ /**
+ * Parse an multiplicativeExpr expression
+ *
+ * <pre>
+ * multiplicativeExpr : unaryExpr (('*' | '/') unaryExpr)*
+ * </pre>
+ * @return a multiply expression {@link Node}, never {@code null}
+ */
+ private Node parseMultiplication() {
+ var lhs = parseUnary();
+ if (match("*")) return new MulNode(lhs, parseMultiplication()).peephole();
+ if (match("/")) return new DivNode(lhs, parseMultiplication()).peephole();
+ return lhs;
+ }
+
+ /**
+ * Parse a unary minus expression.
+ *
+ * <pre>
+ * unaryExpr : ('-') unaryExpr | primaryExpr
+ * </pre>
+ * @return a unary expression {@link Node}, never {@code null}
+ */
+ private Node parseUnary() {
+ if (match("-")) return new MinusNode(parseUnary()).peephole();
return parsePrimary();
}
@@ -78,14 +122,14 @@ public class Parser {
* Parse a primary expression:
*
* <pre>
- * primaryExpr : integerLiteral
+ * primaryExpr : integerLiteral | Identifier | '(' expression ')'
* </pre>
+ * @return a primary {@link Node}, never {@code null}
*/
private Node parsePrimary() {
- _lexer.skipWhiteSpace();
- if (_lexer.isNumber())
- return parseIntegerLiteral();
- throw error("Syntax error, expected integer literal");
+ if( _lexer.isNumber() ) return parseIntegerLiteral();
+ if( match("(") ) return require(parseExpression(), ")");
+ throw errorSyntax("integer literal");
}
/**
@@ -96,7 +140,7 @@ public class Parser {
* </pre>
*/
private ConstantNode parseIntegerLiteral() {
- return new ConstantNode(_lexer.parseNumber());
+ return (ConstantNode) new ConstantNode(_lexer.parseNumber()).peephole();
}
//////////////////////////////////
@@ -146,6 +190,12 @@ public class Parser {
_input = buf;
}
+ // Very handy in the debugger, shows the unparsed program
+ @Override
+ public String toString() {
+ return new String(_input, _position, _input.length - _position);
+ }
+
// True if at EOF
private boolean isEOF() {
return _position >= _input.length;
@@ -209,11 +259,11 @@ public class Parser {
boolean isNumber() {return isNumber(peek());}
boolean isNumber(char ch) {return Character.isDigit(ch);}
- private long parseNumber() {
+ private Type parseNumber() {
String snum = parseNumberString();
if (snum.length() > 1 && snum.charAt(0) == '0')
throw error("Syntax error: integer values cannot start with '0'");
- return Long.parseLong(snum);
+ return TypeInteger.constant(Long.parseLong(snum));
}
private String parseNumberString() {
int start = _position;
@@ -246,5 +296,4 @@ public class Parser {
return new String(_input, start, 1);
}
}
-
}
diff --git a/src/main/java/com/seaofnodes/simple/Utils.java b/src/main/java/com/seaofnodes/simple/Utils.java
new file mode 100644
index 00000000..45f15327
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/Utils.java
@@ -0,0 +1,39 @@
+package com.seaofnodes.simple;
+
+import java.util.ArrayList;
+
+public class Utils {
+ public static RuntimeException TODO() { return TODO("Not yet implemented"); }
+ public static RuntimeException TODO(String msg) { return new RuntimeException(msg); }
+
+ /**
+ * Fast, constant-time, element removal. Does not preserve order
+ *
+ * @param array ArrayList to modify
+ * @param i element to be removed
+ * @return element removed
+ */
+ public static <E> E del(ArrayList<E> array, int i) {
+ if ( i >= 0 && i < array.size() ) {
+ E tmp = array.get(i);
+ E last = array.removeLast();
+ if (i < array.size()) array.set(i, last);
+ return tmp;
+ }
+ return null;
+ }
+
+ /**
+ * Search a list for an element by reference
+ *
+ * @param ary List to search in
+ * @param x Object to be searched
+ * @return >= 0 on success, -1 on failure
+ */
+ public static <E> int find( ArrayList<E> ary, E x ) {
+ for( int i=0; i<ary.size(); i++ )
+ if( ary.get(i)==x )
+ return i;
+ return -1;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/AddNode.java b/src/main/java/com/seaofnodes/simple/node/AddNode.java
new file mode 100644
index 00000000..7c1968c5
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/AddNode.java
@@ -0,0 +1,36 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.*;
+
+public class AddNode extends Node {
+ public AddNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
+
+ @Override public String label() { return "Add"; }
+
+ @Override public String glabel() { return "+"; }
+
+ @Override
+ StringBuilder _print1(StringBuilder sb) {
+ in(1)._print0(sb.append("("));
+ in(2)._print0(sb.append("+"));
+ return sb.append(")");
+ }
+
+
+ @Override
+ public Type compute() {
+ if( in(1)._type instanceof TypeInteger i0 &&
+ in(2)._type instanceof TypeInteger i1 ) {
+ if (i0.isConstant() && i1.isConstant())
+ return TypeInteger.constant(i0.value()+i1.value());
+ }
+ return Type.BOTTOM;
+ }
+
+ @Override
+ public Node idealize () {
+ // TODO: add of 0, and many other peepholes added in Chaper04
+ return null;
+ }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
index 21f65084..43a22426 100644
--- a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
@@ -1,6 +1,7 @@
package com.seaofnodes.simple.node;
import com.seaofnodes.simple.Parser;
+import com.seaofnodes.simple.type.Type;
/**
* A Constant node represents a constant value. At present, the only constants
@@ -15,11 +16,27 @@ import com.seaofnodes.simple.Parser;
* The Constant's value is the value stored in it.
*/
public class ConstantNode extends Node {
+ final Type _con;
+ public ConstantNode( Type type ) {
+ super(Parser.START);
+ _con = type;
+ }
- public final long _value;
+ @Override
+ public String label() { return "#"+_con; }
- public ConstantNode(long value) {
- super(Parser.START);
- _value = value;
+ @Override
+ public String uniqueName() { return "Con_" + _nid; }
+
+ @Override
+ StringBuilder _print1(StringBuilder sb) {
+ return _con.print(sb);
}
+
+ @Override
+ public Type compute() { return _con; }
+
+ @Override
+ public Node idealize() { return null; }
+
}
diff --git a/src/main/java/com/seaofnodes/simple/node/DivNode.java b/src/main/java/com/seaofnodes/simple/node/DivNode.java
new file mode 100644
index 00000000..e3c3616a
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/DivNode.java
@@ -0,0 +1,34 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+
+public class DivNode extends Node {
+ public DivNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
+
+ @Override public String label() { return "Div"; }
+
+ @Override public String glabel() { return "//"; }
+
+ @Override
+ StringBuilder _print1(StringBuilder sb) {
+ in(1)._print0(sb.append("("));
+ in(2)._print0(sb.append("/"));
+ return sb.append(")");
+ }
+
+ @Override
+ public Type compute() {
+ if (in(1)._type instanceof TypeInteger i0 &&
+ in(2)._type instanceof TypeInteger i1) {
+ if (i0.isConstant() && i1.isConstant())
+ return i1.value() == 0
+ ? TypeInteger.ZERO
+ : TypeInteger.constant(i0.value()/i1.value());
+ }
+ return Type.BOTTOM;
+ }
+
+ @Override
+ public Node idealize() { return null; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/MinusNode.java b/src/main/java/com/seaofnodes/simple/node/MinusNode.java
new file mode 100644
index 00000000..0d59ef25
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/MinusNode.java
@@ -0,0 +1,28 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+
+public class MinusNode extends Node {
+ public MinusNode(Node in) { super(null, in); }
+
+ @Override public String label() { return "Minus"; }
+
+ @Override public String glabel() { return "-"; }
+
+ @Override
+ StringBuilder _print1(StringBuilder sb) {
+ in(1)._print0(sb.append("(-"));
+ return sb.append(")");
+ }
+
+ @Override
+ public Type compute() {
+ if (in(1)._type instanceof TypeInteger i0)
+ return i0.isConstant() ? TypeInteger.constant(-i0.value()) : i0;
+ return Type.BOTTOM;
+ }
+
+ @Override
+ public Node idealize() { return null; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/MulNode.java b/src/main/java/com/seaofnodes/simple/node/MulNode.java
new file mode 100644
index 00000000..26c28281
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/MulNode.java
@@ -0,0 +1,32 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+
+public class MulNode extends Node {
+ public MulNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
+
+ @Override public String label() { return "Mul"; }
+
+ @Override public String glabel() { return "*"; }
+
+ @Override
+ StringBuilder _print1(StringBuilder sb) {
+ in(1)._print0(sb.append("("));
+ in(2)._print0(sb.append("*"));
+ return sb.append(")");
+ }
+
+ @Override
+ public Type compute() {
+ if (in(1)._type instanceof TypeInteger i0 &&
+ in(2)._type instanceof TypeInteger i1) {
+ if (i0.isConstant() && i1.isConstant())
+ return TypeInteger.constant(i0.value()*i1.value());
+ }
+ return Type.BOTTOM;
+ }
+
+ @Override
+ public Node idealize() { return null; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/Node.java b/src/main/java/com/seaofnodes/simple/node/Node.java
index 5d74e206..fd233108 100644
--- a/src/main/java/com/seaofnodes/simple/node/Node.java
+++ b/src/main/java/com/seaofnodes/simple/node/Node.java
@@ -1,5 +1,8 @@
package com.seaofnodes.simple.node;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.type.Type;
+
import java.util.*;
/**
@@ -36,6 +39,13 @@ public abstract class Node {
*/
public final ArrayList<Node> _outputs;
+
+ /**
+ * Current computed type for this Node. This value changes as the graph
+ * changes and more knowledge is gained about the program.
+ */
+ public Type _type;
+
/**
* A private Global Static mutable counter, for unique node id generation.
* To make the compiler multi-threaded, this field will have to move into a TLS.
@@ -50,8 +60,53 @@ public abstract class Node {
_outputs = new ArrayList<>();
for( Node n : _inputs )
if( n != null )
- n._outputs.add( this );
+ n.addUse( this );
+ }
+
+ // Easy reading label for debugger, e.g. "Add" or "Region" or "EQ"
+ public abstract String label();
+
+ // Unique label for graph visualization, e.g. "Add12" or "Region30" or "EQ99"
+ public String uniqueName() { return label() + _nid; }
+
+ // Graphical label, e.g. "+" or "Region" or "=="
+ public String glabel() { return label(); }
+
+
+ // ------------------------------------------------------------------------
+
+ // Debugger Printing.
+
+ // {@code toString} is what you get in the debugger. It has to print 1
+ // line (because this is what a debugger typically displays by default) and
+ // has to be robust with broken graph/nodes.
+ @Override
+ public final String toString() {
+ // TODO: The print evolves in later chapters. For now, a simple
+ // recursive print does well. Later with control flow and later still
+ // with loops, we need some serious algorithm work to get a decent
+ // print. So you should consider the "print" to always be a work-in-
+ // progress as we march through the chapters.
+ return print();
+ }
+
+ // This is a *deep* print. This version will fail on cycles, which we will
+ // correct later when we can parse programs with loops. We print with a
+ // tik-tok style; the common _print0 calls the per-Node _print1, which
+ // calls back to _print0;
+ public final String print() {
+ return _print0(new StringBuilder()).toString();
}
+ // This is the common print: check for DEAD and print "DEAD" else call the
+ // per-Node print1.
+ final StringBuilder _print0(StringBuilder sb) {
+ return isDead()
+ ? sb.append(uniqueName()).append(":DEAD")
+ : _print1(sb);
+ }
+ // Every Node implements this.
+ abstract StringBuilder _print1(StringBuilder sb);
+
/**
* Gets the ith input node
@@ -62,6 +117,8 @@ public abstract class Node {
public int nIns() { return _inputs.size(); }
+ public Node out(int i) { return _outputs.get(i); }
+
public int nOuts() { return _outputs.size(); }
public boolean isUnused() { return nOuts() == 0; }
@@ -70,6 +127,119 @@ public abstract class Node {
/**
+ * Change a <em>def</em> into a Node. Keeps the edges correct, by removing
+ * the corresponding <em>use->def</em> edge. This may make the original
+ * <em>def</em> go dead. This function is co-recursive with {@link #kill}.
+ *
+ * @param idx which def to set
+ * @param new_def the new definition
+ * @return new_def for flow coding
+ */
+ Node setDef(int idx, Node new_def ) {
+ Node old_def = in(idx);
+ if( old_def == new_def ) return this; // No change
+ // If new def is not null, add the corresponding def->use edge
+ // This needs to happen before removing the old node's def->use edge as
+ // the new_def might get killed if the old node kills it recursively.
+ if( new_def != null )
+ new_def.addUse(this);
+ if( old_def != null && // If the old def exists, remove a def->use edge
+ old_def.delUse(this) ) // If we removed the last use, the old def is now dead
+ old_def.kill(); // Kill old def
+ // Set the new_def over the old (killed) edge
+ _inputs.set(idx,new_def);
+ // Return self for easy flow-coding
+ return new_def;
+ }
+
+ // Breaks the edge invariants, used temporarily
+ protected <N extends Node> void addUse(N n) { _outputs.add(n); }
+
+ // Remove node 'use' from 'def's (i.e. our) output list, by compressing the list in-place.
+ // Return true if the output list is empty afterward.
+ // Error is 'use' does not exist; ok for 'use' to be null.
+ protected boolean delUse( Node use ) {
+ Utils.del(_outputs, Utils.find(_outputs, use));
+ return _outputs.size() == 0;
+ }
+
+ /**
+ * Kill a Node with no <em>uses</em>, by setting all of its <em>defs</em>
+ * to null. This may recursively kill more Nodes and is basically dead
+ * code elimination. This function is co-recursive with {@link #setDef}.
+ */
+ public void kill( ) {
+ assert isUnused(); // Has no uses, so it is dead
+ for( int i=0; i<nIns(); i++ )
+ setDef(i,null); // Set all inputs to null, recursively killing unused Nodes
+ _inputs.clear();
+ _type=null; // Flag as dead
+ assert isDead(); // Really dead now
+ }
+
+ // Mostly used for asserts and printing.
+ boolean isDead() { return isUnused() && nIns()==0 && _type==null; }
+ /**
+ * We allow disabling peephole opt so that we can observe the
+ * full graph, vs the optimized graph.
+ */
+ public static boolean _disablePeephole = false;
+
+ /**
+ * Try to peephole at this node and return a better replacement Node if
+ * possible. We compute a {@link Type} and then check and replace:
+ * <ul>
+ * <li>if the Type {@link Type#isConstant}, we replace with a {@link ConstantNode}</li>
+ * <li>in a future chapter we will look for a
+ * <a href="https://en.wikipedia.org/wiki/Common_subexpression_elimination">Common Subexpression</a>
+ * to eliminate.</li>
+ * <li>we ask the Node for a better replacement (again, none enabled in this chapter)</li>
+ * </ul>
+ */
+ public final Node peephole( ) {
+ // Compute initial or improved Type
+ Type type = _type = compute();
+
+ if (_disablePeephole)
+ return this; // Peephole optimizations turned off
+
+ // Replace constant computations from non-constants with a constant node
+ if (!(this instanceof ConstantNode) && type.isConstant()) {
+ kill(); // Kill `this` because replacing with a Constant
+ return new ConstantNode(type).peephole();
+ }
+
+ // Future chapter: Global Value Numbering goes here
+
+ // Ask each node for a better replacement
+ Node n = idealize();
+ if( n != null ) return n;
+
+ return this; // No progress
+ }
+
+
+ /**
+ * This function needs to be
+ * <a href="https://en.wikipedia.org/wiki/Monotonic_function">Monotonic</a>
+ * as it is part of a Monotone Analysis Framework.
+ * <a href="https://www.cse.psu.edu/~gxt29/teaching/cse597s21/slides/08monotoneFramework.pdf">See for example this set of slides</a>.
+ * <p>
+ * For Chapter 2, all our Types are really integer constants, and so all
+ * the needed properties are trivially true, and we can ignore the high
+ * theory. Much later on, this will become important and allow us to do
+ * many fancy complex optimizations trivially... because theory.
+ * <p>
+ * compute() needs to be stand-alone, and cannot recursively call compute
+ * on its inputs programs are cyclic (have loops!) and this will just
+ * infinitely recurse until stack overflow. Instead, compute typically
+ * computes a new type from the {@link #_type} field of its inputs.
+ */
+ public abstract Type compute();
+
+ public abstract Node idealize();
+
+ /**
* Used to allow repeating tests in the same JVM. This just resets the
* Node unique id generator, and is done as part of making a new Parser.
*/
diff --git a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
index 85bbc83f..7fa470f8 100644
--- a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
@@ -1,5 +1,7 @@
package com.seaofnodes.simple.node;
+import com.seaofnodes.simple.type.*;
+
/**
* The Return node has two inputs. The first input is a control node and the
* second is the data node that supplies the return value.
@@ -18,6 +20,21 @@ public class ReturnNode extends Node {
public Node ctrl() { return in(0); }
public Node expr() { return in(1); }
+ @Override
+ public String label() { return "Return"; }
+
+ @Override
+ StringBuilder _print1(StringBuilder sb) {
+ return expr()._print0(sb.append("return ")).append(";");
+ }
+
@Override public boolean isCFG() { return true; }
+ @Override
+ public Type compute() {
+ return Type.BOTTOM;
+ }
+
+ @Override
+ public Node idealize() { return null; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/StartNode.java b/src/main/java/com/seaofnodes/simple/node/StartNode.java
index 04bd6bb4..99e240ad 100644
--- a/src/main/java/com/seaofnodes/simple/node/StartNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StartNode.java
@@ -1,9 +1,13 @@
package com.seaofnodes.simple.node;
+import com.seaofnodes.simple.type.Type;
+
/**
- * The Start node represents the start of the function. For now, we do not have any inputs to Start because our function does not
- * yet accept parameters. When we add parameters the value of Start will be a tuple, and will require Projections to extract the values.
- * We discuss this in detail in Chapter 9: Functions and Calls.
+ * The Start node represents the start of the function. For now, we do not
+ * have any inputs to Start because our function does not yet accept
+ * parameters. When we add parameters the value of Start will be a tuple, and
+ * will require Projections to extract the values. We discuss this in detail
+ * in Chapter 9: Functions and Calls.
*/
public class StartNode extends Node {
@@ -11,6 +15,19 @@ public class StartNode extends Node {
super();
}
+ @Override
+ public String label() { return "Start"; }
+
+ @Override
+ StringBuilder _print1(StringBuilder sb) {
+ return sb.append(label());
+ }
+
@Override public boolean isCFG() { return true; }
+ @Override
+ public Type compute() { return Type.BOTTOM; }
+
+ @Override
+ public Node idealize() { return null; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/SubNode.java b/src/main/java/com/seaofnodes/simple/node/SubNode.java
new file mode 100644
index 00000000..d2e32c61
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/SubNode.java
@@ -0,0 +1,33 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+
+public class SubNode extends Node {
+ public SubNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
+
+ @Override public String label() { return "Sub"; }
+
+ @Override public String glabel() { return "-"; }
+
+ @Override
+ StringBuilder _print1(StringBuilder sb) {
+ in(1)._print0(sb.append("("));
+ in(2)._print0(sb.append("-"));
+ return sb.append(")");
+ }
+
+ @Override
+ public Type compute() {
+ if (in(1)._type instanceof TypeInteger i0 &&
+ in(2)._type instanceof TypeInteger i1) {
+ if (i0.isConstant() && i1.isConstant())
+ return TypeInteger.constant(i0.value()-i1.value());
+ }
+ return Type.BOTTOM;
+ }
+
+ @Override
+ public Node idealize() { return null; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/type/Type.java b/src/main/java/com/seaofnodes/simple/type/Type.java
new file mode 100644
index 00000000..7ea17bfb
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/type/Type.java
@@ -0,0 +1,40 @@
+package com.seaofnodes.simple.type;
+
+
+/**
+ * These types are part of a Monotone Analysis Framework,
+ * @see <a href="https://www.cse.psu.edu/~gxt29/teaching/cse597s21/slides/08monotoneFramework.pdf">see for example this set of slides</a>.
+ * <p>
+ * The types form a lattice; @see <a href="https://en.wikipedia.org/wiki/Lattice_(order)">a symmetric complete bounded (ranked) lattice.</a>
+ * <p>
+ * This wild lattice theory will be needed later to allow us to easily beef up
+ * the analysis and optimization of the Simple compiler... but we don't need it
+ * now, just know that it is coming along in a later Chapter.
+ * <p>g
+ * One of the fun things here is that while the theory is deep and subtle, the
+ * actual implementation is darn near trivial and is generally really obvious
+ * what we're doing with it. Right now, it's just simple integer math to do
+ * simple constant folding e.g. 1+2 == 3 stuff.
+ */
+
+public class Type {
+
+ // ----------------------------------------------------------
+ // Simple types are implemented fully here. "Simple" means: the code and
+ // type hierarchy are simple, not that the Type is conceptually simple.
+ static final byte TBOT = 0; // Bottom (ALL)
+ static final byte TTOP = 1; // Top (ANY)
+ static final byte TSIMPLE = 2; // End of the Simple Types
+ static final byte TINT = 3; // All Integers; see TypeInteger
+ public final byte _type;
+
+ public boolean is_simple() { return _type < TSIMPLE; }
+ private static final String[] STRS = new String[]{"Bot","Top"};
+ protected Type(byte type) { _type = type; }
+
+ public static final Type BOTTOM = new Type( TBOT ); // ALL
+
+ public boolean isConstant() { return _type == TTOP; }
+
+ public StringBuilder print(StringBuilder sb) {return is_simple() ? sb.append(STRS[_type]) : sb;}
+}
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeInteger.java b/src/main/java/com/seaofnodes/simple/type/TypeInteger.java
new file mode 100644
index 00000000..e0324da5
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/type/TypeInteger.java
@@ -0,0 +1,41 @@
+package com.seaofnodes.simple.type;
+
+/**
+ * Integer Type
+ */
+public class TypeInteger extends Type {
+
+ public final static TypeInteger ZERO= new TypeInteger(0);
+
+ /**
+ * The constant value observed for this type
+ */
+ private final long _con;
+
+ public TypeInteger(long con) {
+ super(TINT);
+ _con = con;
+ }
+
+ public static TypeInteger constant(long con) { return new TypeInteger(con); }
+
+ @Override
+ public String toString() {
+ return print(new StringBuilder()).toString();
+ }
+
+ @Override
+ public StringBuilder print(StringBuilder sb) { return sb.append(_con); }
+
+ @Override
+ public boolean isConstant() { return true; }
+
+ public long value() { return _con; }
+
+ @Override
+ public boolean equals( Object o ) {
+ if( o==this ) return true;
+ if( !(o instanceof TypeInteger i) ) return false;
+ return _con==i._con;
+ }
+}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter01Test.java b/src/test/java/com/seaofnodes/simple/Chapter01Test.java
index a91aabb1..49edfb5b 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter01Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter01Test.java
@@ -1,6 +1,7 @@
package com.seaofnodes.simple;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
import org.junit.Test;
import static org.junit.Assert.*;
@@ -17,7 +18,7 @@ public class Chapter01Test {
Node expr = ret.expr();
if( expr instanceof ConstantNode con ) {
assertEquals(start,con.in(0));
- assertEquals(1, con._value);
+ assertEquals(TypeInteger.constant(1), con._type);
} else {
fail();
}
@@ -30,7 +31,7 @@ public class Chapter01Test {
StartNode start = Parser.START;
for( Node use : start._outputs )
if( use instanceof ConstantNode con )
- assertEquals(0,con._value);
+ assertEquals(TypeInteger.constant(0),con._type);
}
@Test
@@ -54,13 +55,9 @@ public class Chapter01Test {
}
@Test
- public void testBad3() {
- try {
- new Parser("return --12;").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Syntax error, expected integer literal",e.getMessage());
- }
+ public void testNotBad3() {
+ // this test used to fail in chapter 1
+ assertEquals("return 12;", new Parser("return --12;").parse().print());
}
@Test
@@ -73,15 +70,10 @@ public class Chapter01Test {
}
}
- // Negative numbers require unary operator support that is not in scope
@Test
- public void testBad5() {
- try {
- new Parser("return -100;").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Syntax error, expected integer literal",e.getMessage());
- }
+ public void testNotBad5() {
+ // this test used to fail in chapter 1
+ assertEquals("return -100;", new Parser("return -100;").parse().print());
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter02Test.java b/src/test/java/com/seaofnodes/simple/Chapter02Test.java
new file mode 100644
index 00000000..36e62f9b
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/Chapter02Test.java
@@ -0,0 +1,66 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class Chapter02Test {
+
+ @Test
+ public void testParseGrammar() {
+ Node._disablePeephole = true; // disable peephole so we can observe full graph
+ Parser parser = new Parser("return 1+2*3+-5;");
+ ReturnNode ret = parser.parse();
+ assertEquals("return (1+((2*3)+(-5)));", ret.print());
+ GraphVisualizer gv = new GraphVisualizer();
+ System.out.println(gv.generateDotOutput(parser));
+ Node._disablePeephole = false;
+ }
+
+ @Test
+ public void testAddPeephole() {
+ Parser parser = new Parser("return 1+2;");
+ ReturnNode ret = parser.parse();
+ assertEquals("return 3;", ret.print());
+ }
+
+ @Test
+ public void testSubPeephole() {
+ Parser parser = new Parser("return 1-2;");
+ ReturnNode ret = parser.parse();
+ assertEquals("return -1;", ret.print());
+ }
+
+ @Test
+ public void testMulPeephole() {
+ Parser parser = new Parser("return 2*3;");
+ ReturnNode ret = parser.parse();
+ assertEquals("return 6;", ret.print());
+ }
+
+ @Test
+ public void testDivPeephole() {
+ Parser parser = new Parser("return 6/3;");
+ ReturnNode ret = parser.parse();
+ assertEquals("return 2;", ret.print());
+ }
+
+ @Test
+ public void testMinusPeephole() {
+ Parser parser = new Parser("return 6/-3;");
+ ReturnNode ret = parser.parse();
+ assertEquals("return -2;", ret.print());
+ }
+
+ @Test
+ public void testExample() {
+ Parser parser = new Parser("return 1+2*3+-5;");
+ ReturnNode ret = parser.parse();
+ assertEquals("return 2;", ret.print());
+ GraphVisualizer gv = new GraphVisualizer();
+ System.out.println(gv.generateDotOutput(parser));
+ }
+
+}