1 Programming Language အသစ္ထြင္ၾကမယ္ (၆) 13th May 2009, 9:46 pm
sHa92
Founder
Function Support
ဘယ္ language မွာမဆို function ပါမွ တကယ္ program
ေကာင္းေကာင္းေရးလို႔ရမွာပါ။ ဒီေတာ့ ကၽြန္ေတာ္တို႔ language မွာလဲ function
မပါလို႔ ဘယ္ျဖစ္ပါ့မလဲ။ မဟုတ္ဘူးလား။ ဒီ chapter မွာကၽြန္ေတာ္တို႕
assembly language မွာ function support ကိုေရးၾကည့္ရေအာင္ပါ။
Assembly language မွာေတာ့ function အစား GOTO ကိုသုံးၿပီးေရးသြားလို႔ေတာ့
ရပါတယ္။ ဒါေပမဲ့ ခုကၽြန္ေတာ္တို႕က high level language အထိသြားမွာဆိုေတာ့၊
function support ကိုတခါတည္း assembly မွာကတည္းက ထည့္ထားလိုက္ပါမယ္။ ဒါမွ
high level language ကို compile လုပ္တဲ့အခါ ပိုလြယ္သြားမွာပါ။
ဒီေနရာမွာ function ဆိုတာ တကယ္ေတာ့ GOTO ပါဘဲ။ ေအာက္က C codes ကိုၾကည့္ပါ၊
လို႔ေျပာလို႔ရပါတယ္။ ဒါေပမဲ့ သူ႕မွာ အားနဲခ်က္တခုရွိပါတယ္။ Line no 151
ကိုၾကည့္ပါ။ Function ကေန return ျပန္ဖို႔အတြက္ သူ႕ကို ေခၚထားတဲ့ line
number (ဒီေနရာမွာ 111) ကိုသိဖို႔လိုပါတယ္။ ဒါမွ function ကေန ေခၚထားတဲ့
line ကို GOTO နဲ႔ return ျပန္လို႔ရမွာ မဟုတ္လား။ ဒါေၾကာင့္ ကၽြန္ေတာ္တို႔
function A() ကို line number 110 ကေန မေခၚဘဲ ဥပမာ၊ 200 ကေနေခၚရင္
မရပါဘူး။ ဒီအတြက္ 200 ကို return ျပန္ေအာင္ 151 က GOTO statement
ကိုျပန္ျပင္ေရးရပါမယ္။
ဒီအတြက္ solution က သူ႕ကိုေခၚထားတဲ့ line number ကို stack ေပၚမွာအရင္
save လုပ္ထားပါမယ္။ ၿပီးရင္ function ကေန return ျပန္တဲ့အခါ အဲဒီ stack
ေပၚက line number ကိုျပန္ဖတ္ၿပီး အဲဒီေနရာကို return ျပန္ေပးလိုက္ပါမယ္။
စဥ္းစားရတာ ရႈပ္သြားလား မသိဘူး ကဲ စဥ္းစားမေနဘဲ တခါတည္း
ေရးၾကည့္ရေအာင္ပါ။
Function ကို ေခၚဖို႔အတြက္ GOTO op-code အစား call ဆိုတဲ့ သတ္သတ္ op-code
တခုေဆာက္လိုက္ပါ။ သူလဲ GOTO လိုဘဲ သြားမဲ့ Line number ကို operand 0
ကေနယူပါမယ္။ GOTO နဲ႔ကြာတာက ေစာေစာက ေျပာသလို လက္ရွိ line number ကို
stack မွာအရင္သြား save လုပ္ထားပါမယ္။
ၿပီးရင္ return ဆိုတဲ့ op-code အသစ္ထပ္ထည့္ပါအုံးမယ္။ သူက ခုနက stack
ေပၚမွာ save လုပ္ထားတဲ့ line number ကိုျပန္သြားဖတ္ၿပီး အဲဒီ Line number
ကိုေခၚမွာ ျဖစ္ပါတယ္။ သူလဲ GOTO နဲ႔အတူတူပါဘဲ။ သြားရမဲ့ line number ကို
operand ကမယူဘဲ stack ေပၚကယူပါမယ္။ ဒီ op-code ႏွစ္ခုရွိရင္ function
ေရးလို႔ရပါၿပီ။ ေရးၾကည့္ရေအာင္။
ေရးဖို႔လိုပါတယ္။ ဒါမွ ဒီေကာင့္ကို Virtual Machine class
ကေနဖတ္လို႔ရမွာပါ။
stack ေပၚအရင္ သြားသိမ္းၿပီးမွာ operand 0 မွာေပးထားတဲ့ line number ကို
GOTO အတိုင္းဘဲ သြားခိုင္းပါတယ္။
Return op-code မွာေတာ့ stack ေပၚက ပထမ value ကိုအရင္ pop
လုပ္ၿပီးသိမ္းထားပါတယ္။ ဒါဟာ function ကေန return ျပန္မဲ့ data ျဖစ္ပါတယ္။
Stack ေပၚက သူ႕ေနာက္က data ကေတာ့ call op-code မွာသိမ္းထားတဲ့ return
ျပန္ရမဲ့ Line number ျဖစ္ပါတယ္။ သူ႕ကို pop လုပ္ၿပီး Program ရဲ႕ current
line number ကို set လုပ္လိုက္ပါတယ္။ ၿပီးရင္ ခုန save လုပ္ထားတဲ့ data
ကို stack ေပၚျပန္တင္ ေပးလိုက္ပါတယ္။
ဒီေနရာမွာ သတိထားရမွာက function ကေန value တခုခုကို stack တင္ကိုတင္ေပးမွ
ရပါမယ္။ ဆိုလိုတာက high-level language စကားနဲ႔ေျပာရရင္ ကၽြန္ေတာ္တို႕
language မွာ void function ေရးလို႔မရဘူးဆိုတဲ့ သေဘာပါ။ အနဲဆုံး return 0;
ေတာ့ ျပန္ကိုျပန္ေပးရပါမယ္။
ကဲၿပီးရင္ ေအာက္က C code ကို testing အေနနဲ႔ေရးၾကည့္ပါမယ္။
သတိထားမိလား မသိဘူး။ ဘာလို႔လဲဆိုေတာ့ ကၽြန္ေတာ္တို႔ function က parameter
ေတြလက္မခံေသးပါဘူး။ ဒါေၾကာင့္ parameter နဲ႔ local variables
ေတြလက္ခံေအာင္ေရးၾကည့္ရေအာင္ပါ။
ကၽြန္ေတာ္တို႕ function ထဲကို parameter ေတြထည့္ဖို႔ Stack
ကိုဘဲသုံးပါမယ္။ ပထမ၊ function ထဲကိုေပးခ်င္တဲ့ parameter ေတြကို stack
ေပၚကို function မေခၚခင္အရင္ pass လုပ္ေပးလုိက္ပါမယ္။ ၿပီးရင္ function
ထဲေရာက္မွ stack ေပၚက data ေတြကို Get Stack နဲ႔ Set Stack Op-code
ေတြသုံးၿပီး ျပန္ေခၚသုံးပါမယ္။ ဒီအတိုင္း အေပၚက code ကို global variable
မသုံးဘဲ parameter သုံးၿပီး ေရးၾကည့္ရေအာင္ပါ။
ဒါဆိုရင္ ကၽြန္ေတာ္တို႕ function က parameter ေတြကို stack
ကေနလက္ခံလို႔ရပါၿပီ။ ဒါေပမဲ့ stack ေပၚတင္ထားတဲ့ data ႏွစ္ခုကို (line
number 004 နဲ႔ 005 မွာ) ျပန္ၿပီး pop လုပ္လိုက္ပါတယ္။ ဒီလို function
တခုေခၚတိုင္း stack ေပၚအရင္ တင္ထားတဲ့ parameters ေတြကို pop
လိုက္လုပ္ေနရရင္ မလိုအပ္ဘဲ code ေတြမ်ားေနႏိုင္ပါတယ္။ ေရးရတဲ့ programmer
အတြက္လဲ မေကာင္းသလို op-code ေတြမ်ားေနရင္ run ရတာလဲ ေႏွးေနမွာပါ။ ဒီအတြက္
function ကေန return ျပန္ရင္ သူ႕အလိုအေလွ်ာက္ pop လုပ္ေပးလိုက္ပါမယ္။
ဒီလိုလုပ္ဖို႔ function ရဲ႕ scope ကိုသိဖို႔လိုပါလိမ့္မယ္။
Note: Scope ဆိုတာ stack ေပၚမွာ ရွိတဲ့ function နဲ႔ဘဲ သက္ဆိုင္တဲ့ data
ေတြကို ေခၚပါတယ္။ function အတြက္ stack ေပၚကို pass လုပ္ေပးလိုက္တဲ့
parameter ေတြ၊ function ထဲမွာ ရွိတဲ့ local variable ေတြဟာ function ရဲ႕
scope ေပါ့။ ဥပမာ၊ function ထဲမွာ local variable ၃ ခုရွိ္တယ္၊ parameter
၂ ခုလက္ခံတယ္ဆိုရင္ အဲဒီ function ရဲ႕ scope အရြယ္က ၅ ျဖစ္ပါတယ္။
ဒီေတာ့ function ရဲ႕ scope ကို သတ္မွတ္ေပးမဲ့ Op-code တခုထည့္လိုက္ပါမယ္။
သူ႕ကို OP_SCOPE လို႔ဘဲနာမည္ေပးထားပါမယ္။ set_stack တို႔၊ get_stack
သုံးၿပီး stack ေပၚက data ေတြယူတဲ့အခါ stack ေပၚမွာလက္ရွိ တင္ထားတဲ့ data
ေတြကို (function return address အပါအ၀င္) သိေနဖို႔လိုပါတယ္။ ဒီေတာ့ scope
ထဲက data ေတြကိုဘဲဖတ္လို႔၊ ေရးလို႔ရမဲ့ သပ္သပ္ Op-code ေတြသတ္မွတ္မယ္။
ဒါဆို တျခား data ေတြနဲ႔မေရာေတာ့ဘဲ scope ေပၚက data ေတြကိုဘဲ
ရွင္းရွင္းလင္းလင္း ဖတ္ႏိုင္တာေပါ့။ သူတို႔ကိုေတာ့ set_scope နဲ႔
get_scope လို႔ဘဲေခၚရေအာင္။ scope ရဲ႕ index (stack ရဲ႕ index မဟုတ္ပါ)
ကို operand 0 ကဘဲယူပါမယ္။ ဒီ Op-code ၃ ခုနဲ႔ constant ကို set လုပ္ဖို႔
put_stack လို put_scope ကို program မွာေၾကျငာလိုက္ပါမယ္။
ဘယ္ language မွာမဆို function ပါမွ တကယ္ program
ေကာင္းေကာင္းေရးလို႔ရမွာပါ။ ဒီေတာ့ ကၽြန္ေတာ္တို႔ language မွာလဲ function
မပါလို႔ ဘယ္ျဖစ္ပါ့မလဲ။ မဟုတ္ဘူးလား။ ဒီ chapter မွာကၽြန္ေတာ္တို႕
assembly language မွာ function support ကိုေရးၾကည့္ရေအာင္ပါ။
Assembly language မွာေတာ့ function အစား GOTO ကိုသုံးၿပီးေရးသြားလို႔ေတာ့
ရပါတယ္။ ဒါေပမဲ့ ခုကၽြန္ေတာ္တို႕က high level language အထိသြားမွာဆိုေတာ့၊
function support ကိုတခါတည္း assembly မွာကတည္းက ထည့္ထားလိုက္ပါမယ္။ ဒါမွ
high level language ကို compile လုပ္တဲ့အခါ ပိုလြယ္သြားမွာပါ။
ဒီေနရာမွာ function ဆိုတာ တကယ္ေတာ့ GOTO ပါဘဲ။ ေအာက္က C codes ကိုၾကည့္ပါ၊
- Code:
void A() { cout << "Hello";
};
A();
- Code:
// call the function A at line no 150110 goto 150
// function A()
150 TALK
// return from function
151 goto 111
............
လို႔ေျပာလို႔ရပါတယ္။ ဒါေပမဲ့ သူ႕မွာ အားနဲခ်က္တခုရွိပါတယ္။ Line no 151
ကိုၾကည့္ပါ။ Function ကေန return ျပန္ဖို႔အတြက္ သူ႕ကို ေခၚထားတဲ့ line
number (ဒီေနရာမွာ 111) ကိုသိဖို႔လိုပါတယ္။ ဒါမွ function ကေန ေခၚထားတဲ့
line ကို GOTO နဲ႔ return ျပန္လို႔ရမွာ မဟုတ္လား။ ဒါေၾကာင့္ ကၽြန္ေတာ္တို႔
function A() ကို line number 110 ကေန မေခၚဘဲ ဥပမာ၊ 200 ကေနေခၚရင္
မရပါဘူး။ ဒီအတြက္ 200 ကို return ျပန္ေအာင္ 151 က GOTO statement
ကိုျပန္ျပင္ေရးရပါမယ္။
ဒီအတြက္ solution က သူ႕ကိုေခၚထားတဲ့ line number ကို stack ေပၚမွာအရင္
save လုပ္ထားပါမယ္။ ၿပီးရင္ function ကေန return ျပန္တဲ့အခါ အဲဒီ stack
ေပၚက line number ကိုျပန္ဖတ္ၿပီး အဲဒီေနရာကို return ျပန္ေပးလိုက္ပါမယ္။
စဥ္းစားရတာ ရႈပ္သြားလား မသိဘူး ကဲ စဥ္းစားမေနဘဲ တခါတည္း
ေရးၾကည့္ရေအာင္ပါ။
Function ကို ေခၚဖို႔အတြက္ GOTO op-code အစား call ဆိုတဲ့ သတ္သတ္ op-code
တခုေဆာက္လိုက္ပါ။ သူလဲ GOTO လိုဘဲ သြားမဲ့ Line number ကို operand 0
ကေနယူပါမယ္။ GOTO နဲ႔ကြာတာက ေစာေစာက ေျပာသလို လက္ရွိ line number ကို
stack မွာအရင္သြား save လုပ္ထားပါမယ္။
ၿပီးရင္ return ဆိုတဲ့ op-code အသစ္ထပ္ထည့္ပါအုံးမယ္။ သူက ခုနက stack
ေပၚမွာ save လုပ္ထားတဲ့ line number ကိုျပန္သြားဖတ္ၿပီး အဲဒီ Line number
ကိုေခၚမွာ ျဖစ္ပါတယ္။ သူလဲ GOTO နဲ႔အတူတူပါဘဲ။ သြားရမဲ့ line number ကို
operand ကမယူဘဲ stack ေပၚကယူပါမယ္။ ဒီ op-code ႏွစ္ခုရွိရင္ function
ေရးလို႔ရပါၿပီ။ ေရးၾကည့္ရေအာင္။
- Code:
enum OpCode{ .......................
.......................
OP_CALL, // call function
OP_RETURN, // return from function
OP_END
};
ေရးဖို႔လိုပါတယ္။ ဒါမွ ဒီေကာင့္ကို Virtual Machine class
ကေနဖတ္လို႔ရမွာပါ။
- Code:
class Program {public:
vector InstructionList;
int nCurrent;
.......................
.......................
int GetCurrentInstruction() {
return nCurrent;
}
};
- Code:
void Run(Program& program) { while (true)
{
Instruction inst = program.Next();
switch (inst.code)
{
....................
....................
case OP_CALL:
program.PushStack( program.GetCurrentInstruction() );
program.SetInstruction( inst.operand[0] );
break;
case OP_RETURN:
Data1 = program.PopStack();
program.SetInstruction( program.PopStack() );
program.PushStack( Data1 );
break;
}
}
};
stack ေပၚအရင္ သြားသိမ္းၿပီးမွာ operand 0 မွာေပးထားတဲ့ line number ကို
GOTO အတိုင္းဘဲ သြားခိုင္းပါတယ္။
Return op-code မွာေတာ့ stack ေပၚက ပထမ value ကိုအရင္ pop
လုပ္ၿပီးသိမ္းထားပါတယ္။ ဒါဟာ function ကေန return ျပန္မဲ့ data ျဖစ္ပါတယ္။
Stack ေပၚက သူ႕ေနာက္က data ကေတာ့ call op-code မွာသိမ္းထားတဲ့ return
ျပန္ရမဲ့ Line number ျဖစ္ပါတယ္။ သူ႕ကို pop လုပ္ၿပီး Program ရဲ႕ current
line number ကို set လုပ္လိုက္ပါတယ္။ ၿပီးရင္ ခုန save လုပ္ထားတဲ့ data
ကို stack ေပၚျပန္တင္ ေပးလိုက္ပါတယ္။
ဒီေနရာမွာ သတိထားရမွာက function ကေန value တခုခုကို stack တင္ကိုတင္ေပးမွ
ရပါမယ္။ ဆိုလိုတာက high-level language စကားနဲ႔ေျပာရရင္ ကၽြန္ေတာ္တို႕
language မွာ void function ေရးလို႔မရဘူးဆိုတဲ့ သေဘာပါ။ အနဲဆုံး return 0;
ေတာ့ ျပန္ကိုျပန္ေပးရပါမယ္။
ကဲၿပီးရင္ ေအာက္က C code ကို testing အေနနဲ႔ေရးၾကည့္ပါမယ္။
- Code:
int a, int b;int Add() {
return a + b;
};
a = 10, b = 5;
cout << Add();
a = 45, b = 32;
cout << Add();
- Code:
// a is address 0, b is address 4000 put_mem 0 10
001 put_mem 4 5
002 call
003 num
004 put_mem 0 45
005 put_mem 4 32
006 call
007 num
008 end
// function A()
009 push_stack 0
010 push_stack 4
011 add
012 return
သတိထားမိလား မသိဘူး။ ဘာလို႔လဲဆိုေတာ့ ကၽြန္ေတာ္တို႔ function က parameter
ေတြလက္မခံေသးပါဘူး။ ဒါေၾကာင့္ parameter နဲ႔ local variables
ေတြလက္ခံေအာင္ေရးၾကည့္ရေအာင္ပါ။
ကၽြန္ေတာ္တို႕ function ထဲကို parameter ေတြထည့္ဖို႔ Stack
ကိုဘဲသုံးပါမယ္။ ပထမ၊ function ထဲကိုေပးခ်င္တဲ့ parameter ေတြကို stack
ေပၚကို function မေခၚခင္အရင္ pass လုပ္ေပးလုိက္ပါမယ္။ ၿပီးရင္ function
ထဲေရာက္မွ stack ေပၚက data ေတြကို Get Stack နဲ႔ Set Stack Op-code
ေတြသုံးၿပီး ျပန္ေခၚသုံးပါမယ္။ ဒီအတိုင္း အေပၚက code ကို global variable
မသုံးဘဲ parameter သုံးၿပီး ေရးၾကည့္ရေအာင္ပါ။
- Code:
// a is address 0, b is address 4000 put_stack 10
001 put_stack 5
002 call 13
003 num
004 pop_stack
005 pop_stack
006 put_stack 45
007 put_stack 32
008 call 13
009 num
010 pop_stack
011 pop_stack
012 end
// function A()
013 get_stack 2
014 get_stack 1
015 add
016 return
ဒါဆိုရင္ ကၽြန္ေတာ္တို႕ function က parameter ေတြကို stack
ကေနလက္ခံလို႔ရပါၿပီ။ ဒါေပမဲ့ stack ေပၚတင္ထားတဲ့ data ႏွစ္ခုကို (line
number 004 နဲ႔ 005 မွာ) ျပန္ၿပီး pop လုပ္လိုက္ပါတယ္။ ဒီလို function
တခုေခၚတိုင္း stack ေပၚအရင္ တင္ထားတဲ့ parameters ေတြကို pop
လိုက္လုပ္ေနရရင္ မလိုအပ္ဘဲ code ေတြမ်ားေနႏိုင္ပါတယ္။ ေရးရတဲ့ programmer
အတြက္လဲ မေကာင္းသလို op-code ေတြမ်ားေနရင္ run ရတာလဲ ေႏွးေနမွာပါ။ ဒီအတြက္
function ကေန return ျပန္ရင္ သူ႕အလိုအေလွ်ာက္ pop လုပ္ေပးလိုက္ပါမယ္။
ဒီလိုလုပ္ဖို႔ function ရဲ႕ scope ကိုသိဖို႔လိုပါလိမ့္မယ္။
Note: Scope ဆိုတာ stack ေပၚမွာ ရွိတဲ့ function နဲ႔ဘဲ သက္ဆိုင္တဲ့ data
ေတြကို ေခၚပါတယ္။ function အတြက္ stack ေပၚကို pass လုပ္ေပးလိုက္တဲ့
parameter ေတြ၊ function ထဲမွာ ရွိတဲ့ local variable ေတြဟာ function ရဲ႕
scope ေပါ့။ ဥပမာ၊ function ထဲမွာ local variable ၃ ခုရွိ္တယ္၊ parameter
၂ ခုလက္ခံတယ္ဆိုရင္ အဲဒီ function ရဲ႕ scope အရြယ္က ၅ ျဖစ္ပါတယ္။
ဒီေတာ့ function ရဲ႕ scope ကို သတ္မွတ္ေပးမဲ့ Op-code တခုထည့္လိုက္ပါမယ္။
သူ႕ကို OP_SCOPE လို႔ဘဲနာမည္ေပးထားပါမယ္။ set_stack တို႔၊ get_stack
သုံးၿပီး stack ေပၚက data ေတြယူတဲ့အခါ stack ေပၚမွာလက္ရွိ တင္ထားတဲ့ data
ေတြကို (function return address အပါအ၀င္) သိေနဖို႔လိုပါတယ္။ ဒီေတာ့ scope
ထဲက data ေတြကိုဘဲဖတ္လို႔၊ ေရးလို႔ရမဲ့ သပ္သပ္ Op-code ေတြသတ္မွတ္မယ္။
ဒါဆို တျခား data ေတြနဲ႔မေရာေတာ့ဘဲ scope ေပၚက data ေတြကိုဘဲ
ရွင္းရွင္းလင္းလင္း ဖတ္ႏိုင္တာေပါ့။ သူတို႔ကိုေတာ့ set_scope နဲ႔
get_scope လို႔ဘဲေခၚရေအာင္။ scope ရဲ႕ index (stack ရဲ႕ index မဟုတ္ပါ)
ကို operand 0 ကဘဲယူပါမယ္။ ဒီ Op-code ၃ ခုနဲ႔ constant ကို set လုပ္ဖို႔
put_stack လို put_scope ကို program မွာေၾကျငာလိုက္ပါမယ္။
- Code:
enum OpCode { OP_TALK,
..........................
..........................
OP_SCOPE,
OP_SET_SCOPE,
OP_GET_SCOPE,
OP_PUT_SCOPE,
OP_END
};
Last edited by SYKO on 13th May 2009, 10:01 pm; edited 1 time in total