Смекни!
smekni.com

Обчислення иразів у програмуванні (стр. 4 из 4)

Щоб добути знак операції чи дужку, досить узяти поточний символ.

Добування сталих та імен (елементів типу real і st8) опишемо функціями відповідно getcon і getnam.

Побудуємо функції getcon і getnam так, щоб після виконання їх виклику поточним був символ, наступний за останнім символом добутої лексеми. У такому разі до обробки знаків операцій і дужок додамо указання переходу до наступного поточного символу tempc := getc. Імена, що не є іменами функцій із масиву Namf, будемо вважати помилковими лексемами. Якщо лексема подається змінною lx типу Tlx, то залежно від першого символу лексеми потрібно виконати такі дії:

<D> ® lx.stl := con; lx.numb := getcon;

<L> ® lx.name := getnam;

iflx.name представлено в Namf then lx.stl := nam

else lx.stl := err;

'+', '*', '-', '/' ® lx.stl := ops; lx.sig := tempc; tempc := getc;

'(', ')' ® lx.stl := par; lx.prt := tempc; tempc := getc;

інше ® lx.stl := err; lx.wrlx := tempc; tempc := getc;

В усіх випадках повертається значення true – ознака наявності лексеми. За символу finch повертається false. Наведена залежність є основою функції getlx:

function getlx ( var lx : Tlx ) : boolean;

begin

getlx := true; tempc := getbglx;

if tempc in Bcon then

begin

lx.stl := con; lx.numb := getcon

end

else

if tempc in Bnam then

begin

lx.name := getnam;

if isfn ( lx.name ) then lx.stl := nam

else lx.stl := err

end

else

if tempc in Bops then

begin

lx.stl := ops; lx.sig := tempc; tempc := getc

end

else

if tempc in Bpar then

begin

lx.stl := par; lx.prt := tempc; tempc := getc

end

else

if tempc = finch then

getlx := false

else

begin

lx.stl := err; lx.wrlx := tempc; tempc := getc

end

end;

Функція isfn перевірки, чи представлено ім'я lx.name в масиві Namf, залишається для самостійної розробки.

9.4. Допоміжні підпрограми

Алгоритм процедури getbglx дуже простий: поки поточний символ не потрапив у множину Blex перших символів лексем, викликається функція getc для одержання нового поточного символу. Якщо при цьому вираз вичерпується, то наступним поточним вважається "фіктивний символ" finch.

function getbglx : char;

begin

whilenot ((tempc in Blex )or( tempc = finch ) ) do tempc := getc;

getbglx := tempc

end;

Функція getcon задає читання символів сталої з образу та побудову за ними відповідного значення типу real. Нехай синтаксис сталої задається метавиразом <D> { <D> } [ '.' { <D> } ]. Розглянемо побудову значення типу real за сталою. Цифри до крапки задають цілу частину числа. Значення чергової цифри додається до результату обробки попередніх цифр, помноженого на 10. Перед обробкою першої цифри результатом є 0. Крапка пропускається, а значення цифр праворуч від неї множаться на відповідні від'ємні степені числа 10 і додаються до числа:

function getcon : real;

var v, d : real;

begin

v := 0; d := 1;

repeat

v := v*10 + ord(tempc) - ord('0'); tempc := getc;

until not (tempc in Bcon);

if tempc = '.' then tempc := getc;

while tempc in Bcon do

begin

d := d/10; v := v + ( ord(tempc) - ord('0') )*d; tempc := getc

end;

{сталу прочитано; поточним є символ, наступний за її останнім}

getcon := v

end;

Запишемо функцію getcon у інший спосіб, який реально застосовується в побудові підпрограм лексичного аналізу в системах програмування. Обробка чергового символу залежить від того, чи є він цифрою в цілій або дробовій частині сталої, крапкою або символом після сталої.

Додамо змінну cp типу Tcp=(ip, fp, out), елементи якого позначають місця поточного символу tempc в цілій (ip) та дробовій (fp) частині або за межами сталої (out). Спочатку cp=ip. Залежність її наступного значення від попереднього та від поточного символу tc подамо таблицею, в якій стрілка ліворуч відмічає початкове значення ip (табл.20.1).

Нехай змінна v зберігає результат обробки попередніх цифр, d – від'ємні степені числа 10 (спочатку v=0, d=1). Нехай num(<D>) позначає вираз ord(<D>)-ord('0'). Подамо обробку поточного символу tempc й змінювання значень cp таблицею 20.2. Відсутність присвоювань змінній cp у деяких клітинах табл. 20.2 означає, що її значення не змінюється.

За наведеною таблицею функція getcon записується з уживанням оператора case майже механічно:

function getcon : real;

type Tcp = ( ip, fp, out );

var v, d : real; cp : Tcp;

begin

v := 0; d := 1; cp := ip;

while cp <> out do

case cp of

ip : case tempc of

'0'..'9' : begin

v := v*10 + ord(tempc) - ord('0');

tempc := getc

end;

'.' : begin

cp := fp; tempc := getc

end

else cp := out

end;

fp : case tempc of

'0'..'9' : begin

d := d/10;

v := v + (ord(tempc) - ord('0'))*d;

tempc := getc

end

else cp := out

end

end; { оператора case cp of та циклу while}

getcon := v

end

Функція getnam записується аналогічно й залишається для самостійної розробки.

9.5. Читання символів

Нарешті ми уточнимо, як читаються символи виразу з тексту, написавши функцію getc добування наступного символу.

Її розробку почнемо з уточнення задання виразу. Нехай вираз записано в текстовому файлі, у кількох рядках, довжини яких не більше 80. Ознакою кінця виразу є кінець файла. Суміжні лексеми відокремлюються довільними послідовностями пропусків, можливо, порожніми.

Скористаємося обмеженням на довжину рядків тексту та організуємо читання тексту не окремими символами, а рядками. Черговий рядок стає значенням змінної рядкового типу Str, яка називається образом вхідного рядка, або буфером. Саме з буфера символи по одному добуваються за викликів функції getc.

Функцію getc разом із іншими необхідними означеннями помістимо в окремий модуль Inbuf. Створюючи цей модуль, ми повністю відокремлюємо обробку символів виразу від їх конкретного джерела – файла на диску, клавіатури чи ще чогось.

Додамо указання використання модуля Inbuf до модуля Glx.

Для роботи з буфером, тобто змінною buf типу Str, додамо змінні bufl, bufp та tempc, що зберігатимуть відповідно довжину буфера (кількість символів), позицію в ньому, якою закінчується оброблена частина виразу, та її останній, або поточний символ. Означимо ще сталу finch = chr(0), яка стає значенням поточного символу при закінченні виразу. Стала finch та змінна tempc експортуватимуться з модуля, і за його межами рядок "буде видно крізь віконце tempc".

Перенесемо означення імен finch і tempc з модуля Glx до модуля Inbuf.

Ініціалізацію змінних модуля задає процедура bufinit, виклик якої записано в розділі ініціалізації. Вона також забезпечує можливість задати ім'я файла, з якого треба читати вираз. Функція newln описує заповнення буфера новим вхідним рядком та повернення його першого символу.

Модуль Inbuf має такий загальний вигляд:

Unit Inbuf ( input, output );

Interface

const finch=chr(0);

var tempc : char;

function getc : char;

Implementation

const bufml = 80;

type Str=string[bufml];

var buf : Str;

bufl, bufp : integer;

f : text; nam : Str;

procedure bufinit;

begin

buf := ''; {спочатку буфер – порожній рядок}

bufl := 0; bufp := 0;

tempc := ' '; {штучний пропуск перед початком першого рядка}

writeln('Уведіть ім''я текстового файла з виразом'); readln(nam);

assign(f, nam); reset(f)

end;

function newln : char; … end;

function getc; … end;

Begin

bufinit

End.

Наведемо, нарешті, функції getc і newln.

function getc : char;

begin

bufp := bufp + 1;

if bufp <= bufl then tempc := buf[bufp]

else { рядок вичерпано } tempc := newln;

getc := tempc

end;

При виконанні функції newln у разі наявності наступного рядка повертається пропуск. Він штучно додається перед першим символом рядка, аби той не продовжував лексему в попередньому рядку. У разі кінця файла повертається finch – ознака закінчення виразу:

function newln : char;

begin

if eof(f) then tempc := finch

else

begin

readln (f, buf );

bufp := 0; bufl := length ( buf );

tempc := ' '

end;

newln := tempc

end