Summary Files Commits Branches @commit @diff @stats
diff options
context:
space:
mode:
Commit8edab5edc351724fa05c9cce7e64e74197e2a41d (patch)
Author
 Syndamia <kamen@syndamia.com>
2023-04-18 17:26:41 +0300
Committer
 Syndamia <kamen@syndamia.com>
2023-04-18 17:26:41 +0300
Files03ae7d486be6a69d81c40b1413cce831af3c52d7
syndamia.com-8edab5edc351724fa05c9cce7e64e74197e2a41d.tar
syndamia.com-8edab5edc351724fa05c9cce7e64e74197e2a41d.tar.gz
syndamia.com-8edab5edc351724fa05c9cce7e64e74197e2a41d.zip
Parentb2377649aded6e17bc9c5aff60eafc5f963aad0d (diff)
[blog/seoc] Fixed typos
1 files changed, 20 insertions, 20 deletions
-rw-r--r--src/blog/sane-explanation-of-compilers/part-1.md40
diff --git a/src/blog/sane-explanation-of-compilers/part-1.md b/src/blog/sane-explanation-of-compilers/part-1.md
index 6dd80af..fb6da4c 100644
--- a/src/blog/sane-explanation-of-compilers/part-1.md
+++ b/src/blog/sane-explanation-of-compilers/part-1.md
@@ -24,7 +24,7 @@
Recently I signed up for a small university course about compilers.
As I found out, most materials on the topic are either so surface level you barely learn anything, or so specific you need to already know the subject.
-My goal is to try and fill that void, explaning everything in a detailed way that is understandable for beginners.
+My goal is to try and fill that void, explaining everything in a detailed way that is understandable for beginners.
## Preface
@@ -33,7 +33,7 @@ However, writing code only with numbers is tedious, error-prone and simple stuff
That is why, over time, programmers have tried to abstract sets of instructions into words and text constructions that are easier to work with.
Thanks to abstraction, programmers have been able to create more and more complicated projects with less and less work, that is more portable and compatible with different machines.
-The tradeoff is that we need to translate our nice programmer code to numbers a CPU would understand.
+The trade-off is that we need to translate our nice programmer code to numbers a CPU would understand.
This job is delegated to a special type of program, called a compiler[^how-did-we-get-here], which is separated into three main layers/components:
1. Front-end: it understands the program, what the code is trying to do
@@ -59,9 +59,9 @@ could be interpreted either as a:
Note that I'm talking about syntax, the valid selection and layouts of characters, and not semantics, the underlying meaning.
Compiled programming languages associate specific templates of code (text) to their "corresponding" machine instructions, there isn't anything special you need to understand in a piece of code.
-In natural languages, you could construct a sentence which follows all gramatical rules, but is absolute nonsence, like ["Colorless green ideas sleep furiously"](https://en.wikipedia.org/wiki/Colorless_green_ideas_sleep_furiously).
+In natural languages, you could construct a sentence which follows all grammatical rules, but is absolute nonsense, like ["Colorless green ideas sleep furiously"](https://en.wikipedia.org/wiki/Colorless_green_ideas_sleep_furiously).
-The problem of semtantics is extremely difficult, however in our case we only care about syntax.
+The problem of semantics is extremely difficult, however in our case we only care about syntax.
And this is where math comes into play.
### Math
@@ -219,7 +219,7 @@ or `accb`:
S ⇒ aSb ⇒ accb
```
-where a, b and c are just english characters.
+where a, b and c are just English characters.
To simplify our lives, often rules with the same name are merged into one rule, separated by vertical bars (`S -> aSb | cc`).
This is practically alternation but in a different context.
@@ -230,7 +230,7 @@ N -> 0 | 1 | 2 | ... all possible numbers
E -> N | (E) | E + E | E - E | E * E | E / E | E ^ E
```
-which will sucessfully be able to match (generate) `((3 * 3) - (2 ^ 3)) + 2`:
+which will successfully be able to match (generate) `((3 * 3) - (2 ^ 3)) + 2`:
```grammar+
E ⇒ E + E ⇒ (E) + N ⇒ (E - E) + 2 ⇒ ((E) - (E)) + 2 ⇒ ((E * E) - (E ^ E)) + 2 ⇒ ((N * N) - (N ^ N)) + 2 ⇒ ((3 * 3) - (2 ^ 3)) + 2
@@ -319,7 +319,7 @@ How exactly we deal with these problems comes later, for now the takeaway is tha
#### Practical usage
Let's think a bit more about grammars: they're really good at handling structure, especially recursive structure, so it's best if we use them only for that.
-Our math expression grammar (from [here](#grammars)) shouldn't really care if we're trying to sum integers or floats or chars[^static-typing], it's important how everything is layed out and that our operations are done with numbers.
+Our math expression grammar (from [here](#grammars)) shouldn't really care if we're trying to sum integers or floats or chars[^static-typing], it's important how everything is laid out and that our operations are done with numbers.
This is why we're going to separate and generalize the code into [tokens](https://en.wikipedia.org/wiki/Lexical_analysis#Token), basically into building blocks.
As a rough example, we want `1 + (2 * 3)` to be transformed into `NUMBER MATHOP LBRACE NUMBER MATHOP NUMBER RBRACE`, `int var = 13;` into `TYPE NAME ASSIGNMENT NUMBER` and so on, where "TYPE", "NUMBER", "NAME", ... are tokens.
@@ -335,10 +335,10 @@ Usually we identify at least six token types:
- literals: numbers, strings, characters
- comments
-As you might've noticed, here we can use regular expressions, grouping consequtive characters is what regexes are good for.
+As you might've noticed, here we can use regular expressions, grouping consecutive characters is what regexes are good for.
Additionally, we're going to be reading the file, a decently slow operation, only once and for a comparatively very little time.
-In the compiler front-end, the part that implementes our regular expressions is called the "lexer", and the part that implements our grammars - the "parser".
+In the compiler front-end, the part that implements our regular expressions is called the "lexer", and the part that implements our grammars - the "parser".
In the last section we already talked about how easy implementing regexes (lexers) is and how difficult implementing grammars (parsers) is, let's look at the latter.
#### Parsing algorithms
@@ -351,7 +351,7 @@ Generally we categorize them into:
Furthermore, if our algorithms allow for ambiguity (meaning we can replace a name with any one of multiple expressions), we divide them into:
-- left-most derivation algirithms, where we choose the left-most valid expression
+- left-most derivation algorithms, where we choose the left-most valid expression
- right-most derivation algorithms, where we chose the right-most valid expression
The advantage and disadvantages of every type kind of depend on the exact algorithm and grammar.
@@ -400,7 +400,7 @@ We check the first k character of all expressions, and if anyone of them is a na
Production: (S + F)
```
- Now the first symbol in our production is also `(`, so we incremenet the symbols we're looking/peeking at
+ Now the first symbol in our production is also `(`, so we increment the symbols we're looking/peeking at
```
I v
@@ -530,7 +530,7 @@ It all boils down to using a whole bunch of conditional statements and tracing w
And now it's trivial, we read ")", so we go to state 8.
But if we're at state 8, don't continue reading, replace with state 1.
- Now that we're at state 1, if we read any character (including end of string), we'll say the string upto now matches and we're done!
+ Now that we're at state 1, if we read any character (including end of string), we'll say the string up to now matches and we're done!
It's also helpful to put what we do in different situations in another table:
@@ -619,12 +619,12 @@ It all boils down to using a whole bunch of conditional statements and tracing w
##### LR's family
LR is more of a family of algorithms, because there are multiple different ways to generate the table, each with it's own benefits and drawbacks.
-This subject is a really big can of beans, which I'm not knowlegeable enough to talk about.
+This subject is a really big can of beans, which I'm not knowledgeable enough to talk about.
What you need to know is that there are 3 often used types: [CLR](https://en.wikipedia.org/wiki/Canonical_LR_parser), [SLR](https://en.wikipedia.org/wiki/Simple_LR_parser) and [LALR](https://en.wikipedia.org/wiki/LALR_parser).
- CLR is an LR parser with 1 character lookahead (LR(1)), following [Knuth](TODO)'s original theory.
It requires a lot of memory for table generation, so Frank DeRemer created two simplifications:
-- the SLR and LALR, which use singificantly less memory but work with a bit less grammars
+- the SLR and LALR, which use significantly less memory but work with a bit less grammars
LALR is the most commonly used LR parser, however most compilers are custom-tailored pieces of software that very loosely follow the methods of LL and LR.
@@ -642,7 +642,7 @@ If you want to learn more, I would highly suggest:
#### Abstract syntax tree
We already decided that the lexer will output tokens (and characters) to the parser, but we didn't talk about what the parser is supposed to output.
-It definetly has to be some sort of structural representation of the overall code, for example, for `3 + 1 * 2` we have to be able to easily find out what the left and right handsides of the `+` and `*` are.
+It definitely has to be some sort of structural representation of the overall code, for example, for `3 + 1 * 2` we have to be able to easily find out what the left and right handsides of the `+` and `*` are.
Let's draw out[^nomnoml] this relationship, where the left arrow connects to the left handside and the right accordingly:
@@ -708,7 +708,7 @@ Let's look at the input string `2 * (1 + 3)`, while abstracting ourselves from a
![](./img/tree-building-2.svg)
- where the three dots singify there isn't anything there (but we expect there to be).
+ where the three dots signify there isn't anything there (but we expect there to be).
Somewhere we'll store that the next element we find has to be connected to the right handside.
- now we read `(`, we cannot have it by itself, so we directly decide we're working with `(E)` (in our full algorithm we would need a check for existance of matching brace).
@@ -734,7 +734,7 @@ lex is a lexer generator and yacc is a LALR parser generator, both of which gene
#### lex
-Lex is the original program, from 1975, in the modern day it's successor/implementation is the opensource [flex](https://github.com/westes/flex).
+Lex is the original program, from 1975, in the modern day it's successor/implementation is the open-source [flex](https://github.com/westes/flex).
A lex source file is divided into three parts:
@@ -1031,7 +1031,7 @@ Now when you run `./a.out`, if you enter a valid string, you'll see a blank line
The way we created that lexer and parser worked, but it wasn't very useful and has multiple issues we have to address:
-1. When entering a string that isn't matched at all, it gets printed back like nothing happend.
+1. When entering a string that isn't matched at all, it gets printed back like nothing happened.
For example, if we enter the letter `a` or any string which doesn't have `(`, `)`, `1`, `2`, `3`, `+` or `-` characters, it will just get printed back.
Ideally, we want to raise an error, so we're going to add one rule which catches any (non-special) character and throws an error with the built-in `yy_fatal_error`:
@@ -1191,7 +1191,7 @@ The way we created that lexer and parser worked, but it wasn't very useful and h
Of course, we should add some ifndef-def precompiler code, but that isn't relevant for now.
The way we're going to use trees in our grammar is as follows: for the `NUMBER` expression of `expr`, we'll create a node with the (char) value of the number and "return" it.
- For the expression of `expr` we'll "return" a node with the value of the math opeartor and left and right will be the returned values.
+ For the expression of `expr` we'll "return" a node with the value of the math operator and left and right will be the returned values.
Inside start, after we've parsed an `expr`, we'll print out the "returned" tree and free it's memory.
I'm putting returned in quotes, because (as mentioned), we'll just be setting the value of `$$`.
@@ -1362,7 +1362,7 @@ Perfect, we have a good looking tree!
## Appendix
-Originally I planned to put everything in one blogpost, but writing the front-end (this part) alone took me 2 and a half weeks of (inconsistent, I was suddenly hit with multiple exams and homeworks in uni) writing.
+Originally I planned to put everything in one blog post, but writing the front-end (this part) alone took me 2 and a half weeks of (inconsistent, I was suddenly hit with multiple exams and homeworks in uni) writing.
Additionally, while doing research, I found out the course actually skipped over a lot of important topics, which I'll first need to research.
Future parts will be linked right below the title at the very top, but it will probably be quite a while until I write them.