Примітка. Коли я використовував у заголовку "складний", я маю на увазі, що у виразі є багато операторів і операндів. Не те, що сам вираз складний.
Я нещодавно працював над простим компілятором для складання x86-64. Я закінчив головний передній кінець компілятора - лексер і аналізатор - і тепер можу створити абстрактне синтаксичне подання моєї програми. А оскільки моя мова буде статично набрана, я зараз роблю наступний етап: введіть перевірку вихідного коду. Однак я прийшов до проблеми, і не зміг самостійно її вирішити.
Розглянемо наступний приклад:
Аналізатор мого компілятора прочитав цей рядок коду:
int a = 1 + 2 - 3 * 4 - 5
І перетворив його в наступний AST:
=
/ \
a(int) \
-
/ \
- 5
/ \
+ *
/ \ / \
1 2 3 4
Тепер він повинен набрати перевірку AST. вона починається з першого типу перевірки =
оператора. Він спочатку перевіряє ліву частину оператора. Він бачить, що змінна a
оголошується як ціле число. Тепер він повинен переконатися, що вираз правої частини оцінюється на ціле число.
Я розумію, як це можна зробити, якби вираз був лише одним значенням, таким як 1
або 'a'
. Але як би це було зроблено для виразів із кількома значеннями та операндами - складним виразом - таким, як вище? Щоб правильно визначити значення виразу, видається, що перевіряючий тип фактично повинен був би виконати сам вираз і записати результат. Але це, очевидно, перемагає мету поділу фаз складання та виконання.
Єдиний інший спосіб, на який я думаю, це можна зробити, - це рекурсивно перевірити аркуш кожної підвираження в AST та перевірити, чи всі типи аркуша відповідають очікуваному типу оператора. Отже, починаючи з =
оператора, перевіряючий тип буде потім сканувати всі ліві сторони AST і перевіряти, що в аркушах є цілі числа. Потім він повторить це для кожного оператора в піддекспресії.
Я спробував дослідити цю тему в своїй копії "Книги Драконів" , але, схоже, не надто детально, і просто повторює те, що вже знаю.
Який звичайний метод застосовується, коли компілятор перевіряє вирази з багатьма операторами та операндами? Чи застосовується якийсь із згаданих нами методів? Якщо ні, то які методи та як саме вони би працювали?
double a = 7/2
намагатиметься інтерпретувати праву частину як подвійну, отже, спробує інтерпретувати чисельник та знаменник як подвійні та перетворити їх у разі потреби; в результаті a = 3.5
. Знизу вгору буде виконуватися ціле поділ і перетворюватися лише на останньому кроці (призначенні), так a = 3.0
.
int a = 1 + 2 - 3 * 4 - 5
аint a = 5 - ((4*3) - (1+2))