چندین پست قبل، در مورد نوشتن یک زبان برنامه سازی ، مطلبی نوشته بودم. در این مطلب قصد داریم یکی از بخشهای مهم هر زبان یعنی Parser رو بررسی کنیم. اگر یک جست و جوی ساده در اینترنت انجام دهید، حتما خواهید دید که اکثر آموزشهای ساخت زبان برنامه سازی، عملیات parse کردن داده رو با parser generator های موجود مثل bison و … انجام دادن. اما چرا اون رو خودمون ننویسیم؟
برای نوشتن یک Parser ساده تنها نیاز داریم به این که از Regex ها استفاده کنیم. البته، چیزی که در این مطلب نوشته میشه به هیچ وجه شما رو برنامه نویس نمیکنه، فقط ممکنه راه رو برای برنامه نویس شدن شما باز کنه.
خب، بریم سر اصل مطلب. میشه گفت تقریبا همه با زبان Lisp و Scheme و سینتکسشون آشنا میشن. این زبان ها سینتکس های جالبی دارن و از همه مهم تر این سینتکس ها نسبتا راحته، و در همان نتایج جست و جو، شما هزاران آموزش پیاده سازی Lisp و Scheme خواهید دید. یک عملیات ساده در Lisp و Scheme به این شکله :
(+ 1 2)
خب ساختار پرانتز مبنا، باعث میشه کار ما برای بازسازی عملیات های ساده در این زبان ها، بسیار بسیار ساده تر بشه. بیایم اول ببینیم که دیتا پارسر ما باید چه کنه؟
- باید این خط از برنامه بهش داده بشه
- باید اون رو تجزیه تحلیل کنه و انواع داده ای رو جدا کنه
- عملگر رو تشخیص بده
- عملیات رو انجام بده و خروجی مناسب رو بگردونه
خب، ما میخوایم نوع داده ایمون عدد باشه، حرف باشه، و بعد از اولین پرانتز، عملگر تعریف شه.
برای این کار، ابتدا دستورات زیر رو در نظر میگیریم :
( + 1 2 )
( display 'Hello')
خب، الان باید یه تابع کوچولو بنویسیم که نوع داده ای رو بهمون نشون بده. این تابع رو من توی روبی مینویسم و به این شکل در میاد :
def parse(str)
str = str.split(' ')
str[1] = str[1].to_sym
for i in str
if /[a-zA-Z]/ === i
puts "#{i} is STRING"
elsif /[0-9]/ === i
puts "#{i} is NUMBER"
elsif /[:a-z\+\-]/ === i
puts "#{i} is OPERATOR"
else
puts "#{i} is not defined}
end
end
end
ابتدا، تمام اعضای یک رشته که با فاصله از هم قرار گرفته اند را جدا کردیم (در واقع فاصله ای که در قطعه کد Scheme قرار داده شده، برای Parser ما مهم است) ، سپس با یک دستور شرطی نوع آن را بررسی کردیم. عضو شماره ۱ هر آرایه هم به یک Symbol تبدیل شده است (بعدها سمبل ها را بعنوان اوپراتور ها استفاده خواهیم نمود). بیاید این تابع را اجرا کنیم. آن هم با دو دستور داده شده.
نتیجه به این شکل خواهد بود :
همانگونه که شاهدید، پرانتز هارا نمیشناسد، display را هم به عنوان اوپراتور قبول ندارد، حال باید چه کرد؟ برای پرانتز ها باید دو شرط دیگر اضافه کنیم. پس برنامه ما به این شکل خواهد بود :
def parse(str)
str = str.split(' ')
str[1] = str[1].to_sym
for i in str
if /[a-zA-Z]/ === i
puts "#{i} is STRING"
elsif /[0-9]/ === i
puts "#{i} is NUMBER"
elsif /:[a-z\+\-]/ === i
puts "#{i} is OPERATOR"
elsif /\(/ === i
puts "#{i} is LPAREN"
elsif /\)/ === i
puts "#{i} is RPAREN"
else
puts "#{i} is not defined
end
end
end
خب، کد جدید را تست میکنیم وبا تست کد جدید، شما شاهد نتیجه درست خواهید بود.
در این مطلب قصد آن بود که شما ببینید چگونه میتوان فهمید که زبان Lisp ، بررسی میکند که یک داده از چه نوعی است. برای پردازش دستورات درون یک repl هم کافیست تا با یک حلقه بی نهایت، دستوری مشابه دستور زیر بنویسید :
while true
print "Lisp > "
cmd = gets.chomp
parse(cmd)
end
تبریک، شما یک دیتا پارسر نوشته اید. در مراحل بعدی، سعی میکنم تا توضیح دهم چگونه یک Lisp یا Scheme کوچک درست کنیم.