Saturday, April 21, 2018

Ալգորիթմական լեզվի մասին

Նախաբան

Անցյալ դարի վերջերին միջնակարգ դպրոցի «Ինֆորմատիկա» առարկան, որի լրիվ անունն էր «Ինֆորմատիկայի և հաշվողական տեխնիկայի հիմունքներ», ամբողջությամբ նվիրված էր ծրագրավորմանը։ Իսկ քանի որ դպրոցների հիմնական մասում հմակարգիչներ չկային, մշակվել էր, ինֆորմատիկայի՝ այսպես կոչված «առանց ԷՀՄ»֊ի դասավանդման եղանակը։ Ալգորիթմական մտածելության ու ծրագրավորման հմտությունների զարգացման համար դասագրքերում օգտագործվում էին կա՛մ բլոկ֊սխեմաները, կա՛մ Ալգորիթմական լեզուն։ Առաջինի մասին երևի գիտեն բոլորը, քանի որ նույնիսկ ժամանակակից դասագրքերում դրանք հադիպում են հիմնականում ծրագրավորման լեզուների ղեկավարող կառուցվածքների նշանակությունը բացատրելու համար (այլ ոչ թե ալգորիթմները ներկայացնելու համար)։

Ալգորիթմական լեզուն, թերևս, մոռացվել է այն պատճառով, որ դպրոցներում դրա համար երբեք իրականացում (կոմպիլյատոր, ինկերպրետատոր կամ ծրագրավորման միջավայր) չի եղել։ Միգուցե անհետաքրքիր, անիմաստ կամ պարզապես անհարմար էր հայերեն ծառայողական բառերով լեզու օգտագործելը։ Օրինակ, երկու իրական թվերից մեծագույնը գտնելու ծրագիրը ալգորիթմական լեզվով կարելի է գրել այսպես․

ալգ իրկ մեծը(իրկ ա, բ)
  արգ ա, բ
սկիզբ
  եթե ա > բ
    ապա արժեք := ա
    այլապես արժեք := բ
  ավարտ
վերջ

Կամ, օրինակ, ամբողջ թվերի զանգվածում տրված արժեքի գծային որոնման ալգորիթմը կարելի է գրել մոտավորապես այսպես․

ալգ ամբ որոնել(ամբ գ, աղյուսակ տ[0:u], ք)
  արգ գ, տ, ք
սկիզբ
  թող ի սկսած 0 մինչև ք
  ցս
    եթե գ = տ[ի]
      ապա արժեք := ի
    ավարտ
  ցվ
վերջ

Ընդհանուր առմամբ ալգորիթմական լեզուն Ալգոլ (Algol) լեզվից ժառանգված մի գործիք էր՝ հարմարեցված ուսումնական նպատակների, և հատկապես «առանց ԷՀՄ» մոտեցմամբ դասավանդման համար։

Պետք է նշել, որ Մոսկվայի պետական համալսարանում ստեղծվել է ալգորիթմական լեզվի իրականացում՝ ռուսերեն ծառայողական բառերով։ Ոչ միայն իրականացում, այլև բավականին հարուստ ու հետաքրքիր ծրագրավորման միջավայր։ Արդեն XXI դարում, ինչ֊որ փորձեր եղան վերակենդանացնել այդ լեզուն КуМир համակարգի տեսքով, բայց չեմ կարծում, թե դա հառողություն կունենա։ Այսօր արդեն շատ են մեկը մեկից գեղեցիկ ու նորարարական ուսուցողական համակարգերը։ Դժվար թե որևէ մեկն ուզենա ծրագրավորում սովորել Scratch-ից, Python֊ից, Racket֊ից ու այլ ժամանակակից լեզուներից նախընտրելով Ալգորիթմական լեզուն կամ КуМир֊ը։ Ալգորիթմական լեզուն այսօր կարող է ունենալ միայն պատմական նշանակություն։

Ընթացիկ պլաններ

Իմ, այսպես ասած, անհատական զարգացման ծրագում վաղուց նախատեսել էի ուսումնասիրել Ջավա վիրտուալ մեքենայի (JVM) հրամանների համակարգը, ու գրել կոդի գեներատոր՝ որևէ պարզ լեզվի համար։ Սովորաբար որպես լեզու ընտրում եմ BASIC֊ի մի պարզեցված տարատեսակ, որում թողնում եմ մեկ կամ երկու պրիմիտիվ տիպեր, հիմնական ղեկավարող կառուցվածքներն ու ենթածրագրերը։ Այս անգամ հիշեցի Ալգորիթմական լեզվի մասին։ Ու քանի որ, մինչև JVM֊ի համար կոդի գեներատոր գրելը, պիտի գրեի ալգորիթմական լեզվի վերլուծիչ, որոշեցի, ձեռքի հետ, քչփորել նաև ANTLR4 parser generator֊ը։

Այսպիսով՝ ձևակերպվեցին հետևյալ խնդիրները․

  1. Կազմել ալգորիթմական լեզվի ֆորմալ քերականությունը, համոզվել, որ դրա համար հնարավոր է գրել քիչ֊թե շատ խելքը գլխին վերլուծիչ։ Խնդիրը սկզբից ինչ֊որ անհարմարություններ էր խոստանում, քանի որ Ալգորիթմական լեզուն հին դասագրքերում օգտագործված է որպես «թղթի վրայի» լեզու, և դասագիրք գրողներն ու թարգմանողները, կարծես թե, այնքան էլ չեն մտածել լեզվի ֆորմալ կողմի մասին։
  2. ANTLR4 գործիքի օգտագործմամբ գրել շարահյուսական վերլուծիչը։ Չնայած ANTLR4֊ի օգտագործումը շատ մոտ է Bison/Yacc գործիքների օգտագործմանը, այնուամենայնիվ, պետք է ժամանակ տրամադրել առանձնահատկությունները հասկանալու ու դրանց ընտելանալու համար։
  3. Սահմանել աբստրակտ քերականական ծառի հանգույցների դասերի հիերարխիան։ Բնականաբար, այդ դասերը պետք է հարմար լինեն ինչպես ANTLR4֊ի գեներացրած վերլուծիչում օգտագործելու համար, այնպես էլ դրանցից JVM կոդ գեներացնելու համար։
  4. Եվ հիմնականը՝ Apache BCEL գրադարանի օգտագործմամբ վերլուծության ծառից գեներացնել կոռեկտ *.class ֆայլ՝ Ջավա վիրտուալ մեքենայի բայթ֊կոդ։ Այս խնդիրը պիտի ամենաժամանակատարը լինի։
  5. Ամբողջ նախագիծն իրականացնել այնպես, որ այն օգտակար լինի վերը շարադրված նյութով հետաքրքրվողներին։

Քերականության մշակումը

Ալգորիթմական լեզվի քերականությունը կառուցում եմ հիմնականում ըստ հայերեն դասագրքի առաջին ու երկրորդ մասերում բերված օրինակների, երբեմն փորձում եմ հաշվի առնել նաև տեքստում տրված բացատրությունները։ Քերականությունը գրում եմ EBNF գրառմամբ՝ ANTLR4-ում իրականացված տարբերակով։ * ետածանցային (postfix) գործողությունը նշանակում է, որ տարրը կարող է կրկնվել զրո և ավելի անգամներ։ + ետածանցային գործողությունը կրկնությունները սահմանում է մեկ և ավելի անգամների համար։ ? ետածանցային գործողությունը ցույց է տալիս տարրի ոչ պարտադիր առկայությունը (զրո կամ մեկ անգամ)։ ( և ) փակագծերով տարրերը խմբավորվում են, իսկ | գործողությամբ նշվում են այնըտրանքները։ Քերականական կանոնների ոչ տերմինալային սիմվոլները պետք է սկսել փոքրատառով, իսկ տերմինալայինները՝ մեծատառով։ Քերակականական հավասարման ձախ ու աջ մասերն անջատվում են : նիշով, իսկ հավասարումն ավարտվում է ; նիշով։ Այս բաժինը նաև ANTLR4 գործիքի հետ աշխատանքի փորձի կուտակում է։

Սկսում եմ ամենախոշոր միավորից. Ալգորիթմական լեզվով գրված ծրագիրը ալգորիթմների հաջորդականություն է։ Ծրագիրը պարունակող ֆայլի սկզբում կարող են լինել դատարկ տողեր։ Այն տեղերում, որտեղ նոր տողի անցման նիշը պարտադիր չէ, ես օգտագործում եմ NL? գրառումը, իսկ որտեղ որ պարտադիր է՝ NL գրառումը։

grammar Alg0;

program
    : NL? algorithm*
    ;

Ալգորիթմը սկսվում է ալգ ծառայողական բառով, որին հաջորդում են վերադարձվող արժեքի տիպը, ալգորիթմի անունը, պարամետրերի ցուցակը և մարմինը։ Եթե ալգորիթմը արժեք չի վերադարձնելու, վերադարձվող արժեքի տիպը պետք է բաց թողնել։ Պարամետրերի ցուցակը նույնպես կարող է դատարկ լինել՝ (), ավելին՝ այն կարող է ընդհանրապես բացակայել։ Ալգորիթմի վերնագրի և մարմնի արանքում թվարկվում են ալգորիթմի արգումենտներն ու արդյունքները։ Մարմինը սկսվում է սկիզբ ծառայողական բառով և ավարտվում է վերջ բառով։ սկիզբ բառից հետո, նույն տողում, գրվում են ալգորիթմի լոկալ փոփոխականների հայտարարությունները։ Հայտարարություններից հետո գրվում են ալգորիթմի մարմնի հրամանները։

algorithm
    : 'ալգ' scalar? IDENT ('(' (parameter (',' parameter)*)? ')')? NL
      arguments? results?
      'սկիզբ' (declaration (',' declaration)*)? NL statement* 'վերջ' NL
    ;

Ալգորիթմական լեզվի ալգորիթմները կարող են վերադարձնել միայն պրիմիտիվ տիպի արժեքներ։ scalar ոչ տերմինալային սիմվոլը թվարկում է տրամաբանական, ամբողջաթիվ, իրական և տեքստային պրիմիտիվ տիպերը որոշող ծառայողական բառերը։

scalar
    : 'տրամ' | 'ամբ' | 'իրկ' | 'տեքստ'
    ;

Պարամետրի նկարագրությունը սկսվում է պրիմիտիվ տիպով, որին հետևում է պարամետրի անունների ցուցակ։

parameter
    : scalar paramName (',' paramName)*
    ;

Պարամետրի անունը կարող է լինել կամ իդենտիֆիկատոր՝ պարզ փոփոխականի անուն, կամ աղյուսակի անուն։ Վերջինս սկսվում է աղյուսակ բառով, դրան հետևում է իդենտիֆիկատոր և աղյուսակի չափողականությունների նկարագրությունը։

paramName
    : IDENT
    | 'աղյուսակ' IDENT '[' range (',' range)? ']'
    ;

range
    : (INTEGER | IDENT) ':' (INTEGER | IDENT)
    ;

Ալգորիթմական լեզուն հնարավորություն է տալիս սահմանել միայն միչափանի ու երկչափանի զանգվածներ՝ վեկտորներ և մատրիցներ։ Եվ հնարավորություն է տալիս նշելու տարրերի ինդեքսների միջակայքը։ Օրինակ, հետևյալը ամբողջ թվերի վեկտոր է, որի տարրերն ինդեքսավորվում են 1..8 թվերով․

ամբ աղյուսակ վ[1:8]

Գրքում բերված օրինակներում աղյուսակի ինդեքսների միջակայքը նշելիս հաստատունի հետ միասին օգտագործված է մի որևէ փոփոխական։ Հավանաբար ենթադրվել է, որ «աղյուսակ» օբյեկտից հնարավոր չի ստանալ ինդեքսների միջակայքը։ Օրինակ․

ալգ ֆ(ամբ N, իրկ աղյուսակ ա[2:N])
...

(Սա ավելորդ բան է ու իրականացման անհարմարություններ է ստեղծելու։ Այս մասը պետք է վերանայել ու ավելի հարմար գրառում մշակել։ Հաշվի առնելով նաև այն փաստը, որ աղյուսակները լինելու են ստատիկ և դրանք մոդելավորելու եմ Ջավայի օբյեկտներով, դրանց մասին ամբողջ ինֆորմացիան հնարավոր է լինելու ստանալ հենց աղյուսակի հղումից։)

Արգումենտների թվարկումը սկսվում է արգ բառով, որին հետևում են պարամետրերի ցուցակում թվարկված այն պարամետրերի անունները, որոնք ալգորիթմին են փոխանցվելու ըստ արժեքի (by value)։ Նոր տողից, արդ բառով սկսվում է արդյունք֊պարամետրերի թվարկում, դրան այնպիսիներն են, որոնք ալգորիթմին են փոխանցվելու հղումով (by reference)։

arguments
    : 'արգ' IDENT (',' IDENT)* NL
    ;

results
    : 'արդ' IDENT (',' IDENT)* NL
    ;

(Սա էլ է երևի ավելորդ բան։ Ռուսերեն ավելի ուշ հրատարակված դասագրքերում արգ ու արդ ծառայողական բառերը գրվում են հենց պարամետրերի ցուցակում՝ տիպից առաջ։)

Հիմա՝ ալգորիթմի մարմնի մասին։ Ինչպես արդեն նշեցի, այն սկսվում է սկիզբ բառով և ավարտվում է վերջ բառով։ սկիզբ բառի հետ նույն տողում սահմանվում են ալգորիթմի լոկալ փոփոխականները (կամ, դասագրքի տերմիններով, ժամանակավոր մեծությունները)։ Լոկալ փոփոխականների հայտարարման քերականությունը շատ նման է ալգորիթմի պարամետրերի քերականությանը։ Միակ բացառությունն այն է, որ զանգվածների ինդեքսների միջակայքերը պիտի լինեն հաստատուններ։

declaration
    : scalar declName (',' declName)*
    ;
    
declName
    : IDENT
    | 'աղյուսակ' IDENT '[' INTEGER ':' INTEGER ']'
    | 'աղյուսակ' IDENT '[' INTEGER ':' INTEGER ',' INTEGER ':' INTEGER ']'
    ;

Լոկալ փոփոխականների հայտարարություններին հաջորդում է հրամանների շարքը։ Ալգորիթմական լեզվի հրամանները կամ ղեկավարող կառուցվածքները, որքանով ես կարողացա ընդհանրացնել, հետևյալներն են․ վերագրում, ճյուղավորում, պայմանով ցիկլ, պարամետրով ցիկլ, ընտրություն և ենթածրագիր կանչ։

statement
    : assign | branch | condLoop | countLoop | select | algCall
    ;

Վերագրման հրամանը թույլ է տալիս := նշանի աջ կողմում գրված արտահայտությոն արժեքը վերագրել փոփոխականին համ զանգվածի տարրին։

assign
    : place ':=' expression NL
    ;

place
    : IDENT
    | IDENT '[' expression ']'
    | IDENT '[' expression ',' expression ']'
    ;

Ճյուղավորման հրամանը սկսվում է եթե բառով և ավարտվում է ավարտ բառով։ Եթե եթե բառին հաջորդող պայմանը ճիշտ է, ապա կատարվում է ապա բառին հաջորդող հրամանների շարքը։ Հակառակ դեպքում կատարվում են այլապես բառին հաջորդող հրամանները։ Հրամանի այլապես բլոկը կարող է բացակայել։

branch
    : 'եթե' expression NL 'ապա' NL? statement* ('այլապես' NL? statement*)? 'ավարտ' NL
    ;

Պայմանով ցիկլը սկսվում է մինչ բառով, որին հետևում է կրկնման պայմանը։ Այնուհետև, նոր տողից ցս (ցիկլի սկիզբ) և ցվ (ցիկլի վերջ) բառերի միջև գրվում են կրկնվող հրամանները։

condLoop
    : 'մինչ' expression NL 'ցս' NL? statement* 'ցվ' NL
    ;

Հաշվիչով ցիկլը սկսվում է թող բառով, որին հաջորդում է ցիկլի պարամետրը, ապա սկսած բառից հետո գրվում է հաշվիչի սկզբնական արժեքի արտահայտությունը, իսկ մինչև բառից հետո՝ հաշվիչի վերջնական արժեքի արտահայտությունը։ Եթե հաշվիչը պետք է փոխել ոչ թե 1, այլ մի որևէ այլ քայլով, ապա քայլ բառից հետո տրվում է այդ հատատունը։ Այս ցիկլի դեպքում նույնպես մարմինը գրվում է ցս և ցվ բառերի միջև։

countLoop
    : 'թող' IDENT 'սկսած' expression 'մինչև' expression
      ('քայլ' expression)? NL 'ցս' NL? statement* 'ցվ' NL
    ;

Եվ վերջին հրամանը՝ ալգորիթմի կանչը։ Սա ալգորիթմի անունն է, որին հետևում է արգումենտների ցուցակը։ Արգումենտների ցուցակը կարող է դատարկ լինել կամ բացակայել ընդհանրապես։

algCall
    : IDENT ('(' (expression (',' expression)*) ')')? NL
    ;

Ղեկավարող կառուցվածքների մասին այսքանը ես կարողացա դուրս բերել ձեռքիս տակ եղած օրինակներից։ Առ այս պահը չսահմանված են մնացել միայն արտահայտությունները։ Դասագրքում արտահայտությունների համար հիմնականում օգտագործված է ազատ, մաթեմատիկական գրառումը, սակայն պարզ է, որ ծրագրավորման լեզվի համար դա այնքան էլ հարմար չէ, ու պետք է օգտագործել ընդունված տեքստային գրառում։ Արդյունքում կառուցել եմ արտահայտությունների ստորև բերված քքերականությունը։ Այստեղ թվաբանական, համեմատման, տրամաբանական գործողություններն են, ինչպես նաև զանգվածի տարրին դիմելն ու ֆունկցիայի կանչը։

expression
    : simple
    | '(' expression ')'
    | IDENT '[' expression (',' expression)? ']'
    | IDENT '(' (expression (',' expression)*)? ')'
    | ('ոչ' | '-' | '+') expression
    | <assoc=right> expression '**' expression
    | expression ('*' | '/') expression
    | expression ('+' | '-') expression
    | expression ('>' | '>=' | '<' | '<=') expression
    | expression ('=' | '<>') expression
    | expression 'և' expression
    | expression 'կամ' expression
    ;

ANTLR4֊ը պահանջում է, որ արտահայտությունների քերականության մեջ գործողությունները գրվեն ըստ իրենց նախապատվությունների նվազման։ Այս դեպքում, օրինակ, աստիճան բարձրացնելու ** գործողությունն ամենաբարձր նախապատվություն ունեցող բինար գործողությունն է, իսկ ամնեացածր նախապատվություն ունեցողը տրամաբանական կամ գործողությունն է։ Պետք է նկատել նաև, որ <assoc=right> արտահայտությամ ** գործողության համար սահմանվել է աջ բաշխականություն։ Մյուս բինար գործողությունները ձախ֊բաշխական են։

Արտահայտությունների պարզ դեպքերն առանձնացրել եմ simple կանոնի մեջ։ Այստեղ են տեքստային, իրական ամբողջաթիվ ու տրամաբանական լիտերալները, ինչպես նաև պարզ փոփոխականը (IDENT

simple
    : TEXT
    | REAL
    | INTEGER
    | IDENT
    | 'ճիշտ'
    | 'կեղծ'
    ;

Կարծես թե վերջ։ Հիմաա ANTLR4 գործիքով այս քերականությունից պիտի ստանա Ջավա լեզվով գրված կոդ։

Փորձարկում

Բնականաբար, ես չեմ կարծում, թե հենց առաջին փորձից ամեն ինչ աշխատելու է․ կամ ինչ֊որ բան պակաս եմ գրել, կամ ինչ֊որ բան սխալ եմ հասկացել օրինակներից։ Համոզվելու համար պիտի փորձել։

Եվ այսպես, www.antlr.org կայքից ներբեռնում եմ գործիքի 4.7.1 տարբերակը պարունակող antlr-4.7.1-complete.jar ֆայլը ու առայժմ պատճենում եմ այն նույն պանակում, որտեղ քերականության ֆայլն է։ Ի դեպ, քերականությունը պարունակող ֆայլի անունը պետք է համընկնի grammar հրահանգով տրված անվան հետ (իմ դեպքում դա Alg0 է), իսկ ընդլայնումը պետք է լինի *.g4։

Քայլ առաջին։ Ջավայի միջոցով աշխատեցնում եմ ANTLR4 գործիքը․

$ java -cp .:antlr-4.7.1-complete.jar org.antlr.v4.Tool Alg0.g4

Ու միանգամից ստանում եմ հաղորդագրություններ բացթողումների մասին․

warning(125): Alg0.g4:6:6: implicit definition of token NL in parser
warning(125): Alg0.g4:10:20: implicit definition of token IDENT in parser
warning(125): Alg0.g4:29:7: implicit definition of token INTEGER in parser
warning(125): Alg0.g4:108:6: implicit definition of token TEXT in parser
warning(125): Alg0.g4:109:6: implicit definition of token REAL in parser

Իմաստն այն է, որ քերականության կանոններում օգտագործել եմ NL, IDENT, INTEGER, TEXT և REAL տերմինալային սիմվոլները, բայց դրանց տեսքը չեմ սահմանել։ Վերադառնում եմ Alg0.g4 ֆայլին ու դրա պոչից ավելացնում եմ հետևյալ մի քանի սահմանումները։

Իդենտիֆիկատորը հայերեն կամ լատիներեն փոքրատառով սկսվող և նույն տառերից ու թվանշաններց բաղկացած հաջորդականություն է։

IDENT
    : [ա-ևa-z][ա-ևa-z0-9]*
    ;

Իրական թիվը սահմանել եմ որպես . նիշը պարունակող թվանշանների հաջորդականություն։ Սա, իհարկե, լրիվ սահանումը չէ, բայց տվյալ գործի համար լրիվ հերիք է։

REAL
    : [0-9]+'.'[0-9]+?
    ;

Ամբողջ թիվը պարզապես թվանշանների հաջորդականություն է․

INTEGER
    : [0-9]+
    ;

Տեքստային լիտերալը " չակերտների մեջ առնված նիշերի հաջորդականություն է։ Այն չի կարող պարունակել " նիշը։

TEXT
    : '"'~('"')*'"'
    ;

Ալգորիթմական լեզվում ; նիշն ու նոր տողի անցման նիշը համարժեք են։

NL
    : [\n;]+
    ;

ANTLR4֊ի հետևյալ կանոնն էլ ասում է, որ բացատանիշերի հաջորդականությունը պետք է անտեսել։

WS : [ \t\r]+ -> skip
    ;

ANTLR4 գործիքի հաջորդ գործարկումն արդեն հաջող է անցնում, ու գեներացվում են *.java ֆայլերը, որոնք կարելի է կոմպիլյացնել ու ստանալ *.class ֆայլեր։ (Այդ գեներացված ֆայլերի մեջ են Alg0Lexer.java բառային վերլուծիչը, Alg0Parser.java շարահյուսական վերլուծիչը և այլն։ Դրանք բավականին կոկիկ ու ընթեռնելի ծրագրեր են, հետաքրքրության համար կարելի է բացել ու ուսումնասիրել։)

$ javac -cp .:antlr-4.7.1-complete.jar Alg0*.java

Իսկ ինչպե՞ս ստուգել։ ANTLR4֊ն իր մեջ պարունակում է TestRig կոչված ծրագիրը։ Ես դեռ լավ չեմ հասկանում, թե դա ինչ է, բայց կարող եմ ցույց տալ դրա հետ աշխատելու ձևը։ Բայց նախ պատրաստեմ մի օրինակ (դասագրքից), ու այն գրեմ case02.alg ֆայլում։

ալգ փոքրտարր(ամբ k, n, իրկ աղյուսակ a[k:n], ամբ l)
  արգ k, n, a
  արդ l
սկիզբ ամբ i, իրկ փոքր
  փոքր := a[k]
  l := k
  i := k + 1
  մինչ i <= n
  ցս
    եթե փոքր > a[i]
      ապա 
        փոքր := a[i]
        l := i
    ավարտ
    i := i + 1
  ցվ
վերջ

Հետո աշխատեցնում եմ արդեն TestRig֊ը։

$ java -cp .:antlr-4.7.1-complete.jar org.antlr.v4.gui.TestRig Alg0 program -tree < case02.alg

Հրամանում տրված -tree պարամետրը վերլուծության ծառն արտածում է Լիսպ֊ի ցուցակների տեսքով․

(program \n (algorithm ալգ փոքրտարր ( (parameter (scalar ամբ) (paramName k) , (paramName n)) , (parameter (scalar իրկ) (paramName աղյուսակ a [ (range k : n) ])) , (parameter (scalar ամբ) (paramName l)) ) \n (arguments արգ k , n , a \n) (results արդ l \n) սկիզբ (declaration (scalar ամբ) (declName i)) , (declaration (scalar իրկ) (declName փոքր)) \n (statement (assign (place փոքր) := (expression a [ (expression (simple k)) ]) \n)) (statement (assign (place l) := (expression (simple k)) \n)) (statement (assign (place i) := (expression (expression (simple k)) + (expression (simple 1))) \n)) (statement (condLoop մինչ (expression (expression (simple i)) <= (expression (simple n))) \n ցս \n (statement (branch եթե (expression (expression (simple փոքր)) > (expression a [ (expression (simple i)) ])) \n ապա \n (statement (assign (place փոքր) := (expression a [ (expression (simple i)) ]) \n)) (statement (assign (place l) := (expression (simple i)) \n)) ավարտ \n)) (statement (assign (place i) := (expression (expression (simple i)) + (expression (simple 1))) \n)) ցվ \n)) վերջ \n\n))

TestRig֊ին -tree֊ի փոխարեն -gui տալով վերլուծության ծառը կտեսնենք բացված գրաֆիկական պատուհանում։

Կարծես թե ամեն ինչ աշխատում է։ Բայց, կրկնեմ նորից, այս սահմանված քերականությունը պետքական է միայն բզբզելու, խաղալու, ինչ֊որ փորձեր անելու համար։ Քիչ թե շատ պետքական լեզու ստեղծելու համար պիտի ավելի լավ ուսումնասիրել ANTLR4֊ի վարքը՝ քերականությունը ավելի գրագետ սահմանելու համար։ Բացի այդ, դասագրքում եղած լեզուն արդեն հնացած է, պիտի վերանայել բոլոր կառուցվածքներն ու մշակել ծրագրեր գրելու ավելի հարմար լեզու։

Բայց այդ մասին, ինչպես ասում է կենդանի դասականը, հաջորդ դասին։