Saturday, September 12, 2015

Ծրագրավորման լեզուն ընդլայնելու մասին

Վերջերս ես հանդիպեցի թե ինչպես են C++ լեզվում ներմուծել հայերեն ծառայողական բառեր։ Դա արվել էր, բնականաբար, նախապրոցեսորի (preprocessor) օգնությամբ։ Պարզապես ամեն մի բառի համար սահմանվել էր նրա համարժեք հայերեն տարբերակը, որն էլ նախամշակման ժամանակ փոխարինվում էր լեզվի իսկական ծառայողական բառով։ Դա ուներ մոտավորապես ուներ այսպիսի տեսք․

#define եթե if 
#define այլապես else 
#define մինչ while 
#define վերադարձնել return 
#define ամբողջ int

Եվ այս սահմանումներով, օրինակ, էվկլիդեսի ալգորիթմը կարելի է գրառել հետևյալ տեսքով․

ամբողջ euclid( ամբողջ n, ամբողջ m ) {
    մինչ( n != m ) {
        եթե( n > m )
            n %= m;
        այլապես
            m %= n;
    }
    վերադարձնել n + m;
}

Երբ հայերեն ծառայողական բառերի սահմանումներն ու դրանց օգտագործմամբ գրված ծրագիրը գրենք *.cpp ֆայլում, և clang++ կոմպիլյատորի նախապրոցեսորին խնդրենք մշակել այդ ֆայլը․

$ clang -E ex0.cpp

Ապա կստանանք «մաքուր» C++ լեզվով գրված կոդ։

int euclid( int n, int m ) {
    while( n != m ) {
        if( n > m )
            n %= m;
         else
            m %= n;
    }
    return n + m;
}

Պարզ է, որ եթե ուզում ենք ծրագրավորել հայերեն բառերով, ապա C++ լեզուն ավելի լավ տարբերակ չի առաջարկում․ կոմպիլյացիայից առաջ ծրագրի տեքստում փոփոխություններ կատարելու միակ հնարավոր եղանակը նախապրոցեսորի օգտագործումն է։ Պարզ է նաև, որ չենք կարող լեզվում նոր ղեկավարող կառուցվածքներ ավելացնել։ Օրինակ, ինչպե՞ս ավելացնել repeat տիպի կրկնման գործողություն։

repeat( 10 ) {
    std::cout << "Ողջո՜ւյն։\n";
}
* * *

Լրիվ այլ պատկեր է այն լեզուներում, որոնց ընդունված է ասել «ծրագրավորվող ծրագրավորման լեզուներ»։ Այդ դասի վառ ներկայացուցիչներ են Lisp ընտանիքի լեզուները՝ մակրոսների սահմանման իրենց հնարավորություններով։ «Ծրագրավորվող» լինելու հատկությամբ է օժտված նաև Tcl լեզուն, որում վերը բերված repeat կառուցվածքը սահմանելը ամենևին էլ բարդ բան չէ։ Ահա այն․

proc repeat { num body } {
    set result {}
    while { $num != 0 } {
        incr num -1
        set result [uplevel $body]
    }
    return $result
}

Նույնիսկ սրա հայերեն տարբերակի սահմանումն է բավականին հետաքրքիր։

proc կրկնել { count անգամ body } {
    if { ${անգամ} ne {անգամ} } then {
        error "Syntax error."
    }
 
    set result {}
    while { $count != 0 } {
        incr count -1
        set result [uplevel $body]
    }
    return $result
}

Այստեղ սահմանված է կրկնել անունով պրոցեդուրան, որը ունի երեք պարամետր։ Պարամետրերից առաջինը կրկնությունների քանակն է, երկրորդը կատարում է անգամ ծառայողական բառի դերը, իսկ երրորդը կրկնման հրամանի մարմինն է։ կրկնել պրոցեդուրայի մարմնում նախ ստուգում եմ, որ երկրորդ պարամետրի արժեքն անպայման լինի անգամ տողը։ Ահա նաև կիրառությունը․

կրկնել 5 անգամ {
    puts Hello!
}

Բայց ինչպե՞ս է սա աշխատում։ Չէ՞ որ Tcl լեզվի proc հրամանը պարզապես սահմանում է նոր ֆունկցիա, և, բոլորս էլ լավ գիտենք, որ ֆունկցիայի կանչի ժամանակ արգումենտները հաշվարկվում են ֆունկցիային փոխանցելուց առաջ։ Եվ, տվյալ դեպքում, «{ puts {Ողջո՜ւյն։} }» արգումենտի արժեքը պետք է հաշվարկվեր և պետք է մի անգամ արտածվեր «Ողջո՜ւյն։» տեքստը։

Բացատրությունը Tcl լեզվի միակ տիպի՝ տողի հաշվարկման կանոնների մեջ է։ Եթե տողը պարփակված է { և } ձևավոր փակագծերում, ապա այն փոխանցվում է այնպես, ինչպես կա (as-is), ոչ մի հաշվարկ չի կատարվում, ոչ մի ձևափոխություն չի կատարվում։ { և } փակագծերը տողը «պաշտպանում» են հաշվարկումից։ Եվ այդ պաշտպանությունը հնարավորություն է տալիս տողը դիտարկել որպես «ղեկավարող կառուցվածքի» բլոկ։

Օգտագործելով Tcl լեզվում պրոցեդուրաներ սահմանելու proc հրամանը և կոդի բլոկը ստեկի մեկ այլ կադրում հաշվարկելու uplevel հրամանը, կարելի է լեզուն ընդլայնել (համալրել) հայերեն ծառայողական բառեր ունեցող ղեկավարող կառուցվածքներով։ Եվ քանի որ նոր կառուցվածքները սահմանվելու են որպես պրոցեդուրաներ, ապա դա հնարավորություն է տալիս կատարել շարահյուսական և իմաստաբանական ստուգումներ։ Դրա օրինակ է վերը սահմանված կրկնել պրոցեդուրայում անգամ բառի առկայության ստուգումը։

* * *

Լեզուն ընդլայնելու (ասում են նաև՝ լեզվի մեջ նոր լեզու սահմանելու) էլ ավելի լայն ու հետաքրքիր հնարավորություններ են ընձեռնում Lisp ընտանիքի Common Lisp և Scheme լեզուները։ Բայց, ինչպես ասում էր Ֆ․ Դոստոևսկին, դա արդեն ուրիշ պատմության նյութ է։