نوشتن یک Data Parser ساده

چندین پست قبل، در مورد نوشتن یک زبان برنامه سازی ، مطلبی نوشته بودم. در این مطلب قصد داریم یکی از بخشهای مهم هر زبان یعنی Parser رو بررسی کنیم. اگر یک جست و جوی ساده در اینترنت انجام دهید، حتما خواهید دید که اکثر آموزشهای ساخت زبان برنامه سازی، عملیات parse کردن داده رو با parser generator های موجود مثل bison و … انجام دادن. اما چرا اون رو خودمون ننویسیم؟

برای نوشتن یک Parser ساده تنها نیاز داریم به این که از Regex ها استفاده کنیم. البته، چیزی که در این مطلب نوشته میشه به هیچ وجه شما رو برنامه نویس نمی‌کنه، فقط ممکنه راه رو برای برنامه نویس شدن شما باز کنه.

خب، بریم سر اصل مطلب. میشه گفت تقریبا همه با زبان Lisp و Scheme و سینتکسشون آشنا میشن. این زبان ها سینتکس های جالبی دارن و از همه مهم تر این سینتکس ها نسبتا راحته، و در همان نتایج جست و جو، شما هزاران آموزش پیاده سازی Lisp و Scheme خواهید دید. یک عملیات ساده در Lisp و Scheme به این شکله :

کد   
(+ 1 2)

خب ساختار پرانتز مبنا، باعث میشه کار ما برای بازسازی عملیات های ساده در این زبان ها، بسیار بسیار ساده تر بشه. بیایم اول ببینیم که دیتا پارسر ما باید چه کنه؟

  1. باید این خط از برنامه بهش داده بشه
  2. باید اون رو تجزیه تحلیل کنه و انواع داده ای رو جدا کنه
  3. عملگر رو تشخیص بده
  4. عملیات رو انجام بده و خروجی مناسب رو بگردونه

خب، ما میخوایم نوع داده ایمون عدد باشه، حرف باشه، و بعد از اولین پرانتز، عملگر تعریف شه.

برای این کار، ابتدا دستورات زیر رو در نظر میگیریم :

کد   
( + 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 تبدیل شده است (بعدها سمبل ها را بعنوان اوپراتور ها استفاده خواهیم نمود). بیاید این تابع را اجرا کنیم. آن هم با دو دستور داده شده.

نتیجه به این شکل خواهد بود :

Screenshot from 2015-08-21 19:16:40

همانگونه که شاهدید، پرانتز هارا نمیشناسد، 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 کوچک درست کنیم.

 

Share

به بهانه سه سالگی!

امروز وقتی داشتم هاست و دامنه رو تمدید میکردم، به تاریخ اولین فاکتور نگاه کردم، مرداد ماه ۹۱ اولین فاکتور بود و چهارمین فاکتور رو در چهارمین سال فعالیتم در وبلاگ نویسی، پرداخت کردم. با خودم گفتم چقدر سریع گذشت! حالا نمیخوام وارد ناله ها و بحث های فلسفی و خسته کنندگی همیشگی بشم. بیاید به وبلاگ، یه طور دیگه تبریک بگیم! تاحالا فکر کردید چرا اولین رشته ای که در یادگیری برنامه نویسی چاپ میکنید Happy Birthday نیست؟ خب بیاید این رو امروز چاپ کنیم! چندین زبان متفاوت از زبان هایی که قبلا در موردش بحث کردیم، امروز سوژه پستمونن. زبان هایی که مدت زیادی سراغشون نرفته بودم، یا تازه باهاشون آشنا شدم. این هم میتونه بهانه خوبی باشه برای کمی کد زدن 🙂

۱. Lua

کد   
print('Happy Birthday!')

۲. Scheme

کد   
(display "Happy Birthday!")

۳. Erlang

کد   
io:fwrite("Happy Birthday").

۴. Javascript

کد   
alert("Happy Birthday");

۵. bash

کد   
echo "Happy Birthday"

بله و این هم زبان های جا افتاده و یا زبانهایی که برام نا آشنا بودن و تازه باهاشون آشنا شدم (البته فقط Scheme چنین حالتی رو برام داشت). توی این سه سال کلی بحث و مطلب و … از این بلاگ اومد بیرون، در کنارش، مدت نسبتا زیادی که به لطف مبین نت دوست داشتنی، نت درست و حسابی نداشتم، وبلاگ رول توییتر من رو هم داشت 😀 . حالا همه چیز رو به جای خود دارم استفاده میکنم، وبلاگ و توییتر و فیسبوک و … . یه سری کارها هم بود که قرار بود بعد از «کنکور» انجام بدم، خب الانم بعد کنکوره (تا زمانی که دوباره بخوام کنکور بدم برای ارشد 😀 ) و خب وقت زیاده برای انجامشون. چیزایی مثل پادکست و ویدئو کست و … . و در آخر هم تشکر میکنم از همه کسایی که این وبلاگ رو توی این ۳ سال، دنبال کردن و خوندن 🙂

 

Share