var a = 10 + 1. Принципы работы компилятора. Часть 1

Компилятор — это программа которая транслирует высокоуровневый код в низкоуровневый код целевой машины.

Компилятор

Далее, на этой простой строчке мы рассмотрим теоретические основы его работы.

Строка var a = 10 + 1 может быть записана несколькими способами: c переносом строк, или с переносом строк и комментариями внутри, или еще как-то.

var a = 10 + 
1;
var /*iable*/ a = 10 + 
1;

Этот код нужно привести в порядок, т.е. поудалять символы переноса и комментарии.

const str = `var /*iable*/ a = 10 
+ 1`;
const str2 = []
for(var i = 0; i <= str.length; i++) {
const char = str.charAt(i)
  if (!/\n/.test(char)) {
str2.push(char)
}
}
console.log(str2.join(''))
"var /*iable*/ a = 10 + 1"

Первый этап выполнен — избавились от лишних переносов строк. Осталось разобраться с комментариями.

Комментарии начинаются с символов /* и заканчиваются символами */. Нужно немного изменить код и проверить что str.charAt(i) === '/' и str.charAt(i+1) === '*' — таким образом мы найдем начало комментария.

const str = `var /*iable*/ a = 10 
+ 1`;
const str2 = []
for(var i = 0; i <= str.length; i++) {
const char = str.charAt(i)
  if (/\n/.test(char)) {
continue
} else if (char === '/' && str.charAt(i + 1) === '*') {
while(!(str.charAt(i) === '*' && str.charAt(i + 1) === '/')) {
i++
}
i++ //skip last '/'
continue
}
  str2.push(char)
}
console.log(str2.join(''))
"var a = 10 + 1"

Теперь наша строка приведена в более-менее читаемый вид. Осталось только понять семантику написанного.

В любом туториала по жс, в главе о переменых, будет такое предложение:

чтобы объявить переменную, используйте ключевое слово VAR, за которой следует имя переменной
var a;

И дальше вы уже знаете, что var это ключевое слово. Следуя этой логике можно сказать что a это идентификатор, 10 и 1 числа, а + — оператор. Точно так же как в школе мы определяли грамматические категории слов в предложении, определяя их часть речи, мы даем каждой значащей последовательности некую меку: идентификатор, ключевое слово, число или оператор.

Давайте выразим это в коде, заодно немного его отрефакторим.

В результате мы получим следующее представление исходной строки:

[{
lexeme: "var",
type: "keyword"
}, {
lexeme: "a",
type: "id"
}, {
lexeme: "=",
type: "op"
}, {
lexeme: "10",
type: "num"
}, {
lexeme: "+",
type: "op"
}, {
lexeme: "1",
type: "num"
}]

Каждый елемент в этом массиве называется токеном. Каждый токен это структура, или обьект, который хранит в себе лексему и ее тип. Лексема, в свою очередь, это некая значащая последовательность символов. К примеру, лексема var имеет тип keyword.

Схематически, работу нашего кода можно представить так:

Схема работы кода выше

Данная схема может распознать только числа и операторы. Но по аналогии, можно добавить распознавание строк, идентификаторов и регулярных выражений.

Здесь давайте остановимся и просуммируем то что мы сделали на текущий момент:

— Очислити строку от пробельных символов
 — Удалили коментарии
 — Разбили строку на токены

Оказывается, что это как раз то, чем занимается Лексический Анализатор, который является первой стадией, или фазой, работы компилятора.

Всего таких фаз 6: лексический, cинтаксический и семантический анализаторы, генерация промежуточного кода, оптимизация и генерация кода:

Схема компилятора

В следующей части мы рассмотрим работу Синтаксического анализатора.

Оригинал и продолжение тут — https://tich.io/channels/article/var-a-10-1/

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.