فالکن

**فالکُن**  یک زبان برنامه‌نویسی متن باز، ساده، سریع، قدرتمند و با یادگیری آسان است که از ۶ مدل برنامه‌نویسی امری، برنامه‌نویسی تابعی، برنامه‌نویسی شیءگرا، نمونه‌سازی اولیه، پیغام گرا و جدولی پشتیبانی می‌کند. این زبان توسط جیانکارلو نیکولی طراحی شد.

__TOC__

## تاریخچه
یک پروژه کوچک به نام هست، که در سال ۲۰۰۲ به منظور ایجاد یک ماشین مجازی کوچک و سریع آغاز شد بعد از مدتی به طراحی و ساخت زبان برنامه‌نویسی فالکن منجر شد.
جیانکارلو نیکولی، طراح فالکن، در مصاحبه‌ای می‌گوید: «من از زبان‌های زیادی از جمله C++، C، جاوا، اسمبلی، لیسپ، پرولوگ، کلیپر، دلفی، SQL «...» استفاده کرده‌ام؛ و نیاز به یک ابزار قابل انعطاف که نیازهای روزانه مرا در بر بگیرد و امکان بروز ایده‌های جدید را فراهم کند، را احساس کردم. «...» استفاده از یک مدل برنامه‌نویسی برای ماشین سودمند است. «...» اما استفاده از یک مدل خالص برای ذهن و فکر مناسب نیست. زیرا که در دنیای واقع هنگامی که از تعداد زیادی روندها و مدل‌های حل بتوان استفاده کرد، راه حل‌های بهتری ایجاد می‌شوند. «...» بنابراین فالکن ایجاد شد با ایده دارا بودن امکانات یک زبان شئ‌گرا خالص در حالی که یک زبان شئ‌گرا نیست، دارا بودن امکانات یک زبان امری خالص در حالی که یک زبان امری نیست و دارا بودن ساختارهای تابعی در حالی که یک زبان تابعی نیست. «...» اگر یک چیز دربارهٔ این زبان باشد که من به آن افتخار کنم، این است که این زبان به دلایل خارجی ایجاد نشد. بلکه به منظور حل مشکل یکپارچه سازی و مدیریت دسترسی در برنامه‌های کاربردی بزرگ از یک سو و نیاز به حل مسئله‌های پیچیده منطقی و مسئله‌ها با تغییرپذیری بالا از سوی دیگر، ایجاد شد.»

## تایپ‌های داده‌ای
* Nil: یک کلمه کلیدی به معنای هیچ مقدار.
* Integer: یک عدد صحیح ۶۴ بیتی.
* Numeric: یک عدد ممیز شناور ۶۴ بیتی.
* Range: یک ۳تایی از حد بالا، حد پایین و گام.
* MemBuf: جدول‌هایی از حافظه خام که هر خانه یک عدد صحیح بدون علامت ۱، ۲، ۳ یا ۴ بایتی است.
* Enum: نوعی تایپ شمارشی که شامل مجموعه‌ای از مقادیر ثابت به هم مرتبط است.
* Function: تابع (یک موجودیت قابل فراخوانی).
* String: یک رشته با طول متغیر از کاراکترهای UNICODE است.
* Array: یک دنباله با طول متغیر از عناصر هم نوع که می‌توانند در هنگام اجرا اضافه، حذف یا تغییر کنند.
* Dictionary: یک مجموعه با طول متغیر از دوتایی‌هایی به صورت (کلید، مقدار)؛ کلید می‌تواند از هر نوع عنصر زبان فالکن باشد.
* Object: نمونه‌هایی از کلاس‌ها
* Class: موجودیت‌هایی که می‌توانند نمونه‌هایی داشته باشند.
* Method: جفت‌هایی از نمونه‌ها و توابعی که می‌توانند روی آن نمونه‌ها عمل کنند.

## ساختارهای کنترلی
### ساختارهای انتخاب
**ساختار if/elif/else:** ساختار انتخاب if می‌تواند به صورت اختیاری دارای عبارت else یا تعدادی عبارت elif باشد. هر عبارت elif یک عبارت شرطی را ارزیابی می‌کند و در صورت درست بودن، دستورات زیر آن اجرا می‌شود در غیر این صورت عبارت elif بعدی در صورت وجود یا عبارت else بررسی می‌شود. یک عبارت if کامل مشابه زیر است:

 if expresion
  statements...
 elif expresion
  statements...
 elif expresion
  statements...
  /*other elifs*/
 else
  statements...
 end

یک روش دیگر برای اجرای یک تکه برنامه بر اساس ساختار if، استفاده از عملوند fast-if است که ساختار آن چنین است:

  ?  [: ]

 if a == true ? printl("A is true")

**ساختار switch:** هنگامی که نیاز به بررسی یک مقدار در مقابل مقادیر زیادی داریم از این ساختار استفاده می‌شود. تنها محدودیت این ساختار این است که عبارتی که مقدار آن بررسی می‌شود، باید رشته یا عدد صحیح باشد. وجود عبارت default اختیاری است. یک عبارت switch کامل مشابه زیر است:

 switch expresion
 case item [, item,.. , item]
  statements...
 case item [, item,.. , item]
  statements...
  /*other cases*/
 default
  statements...
 end

عناصری که برای انتخاب در مقابل caseها قرار می‌گیرند می‌توانند عددهای صحیح، رشته‌های حرفی، بازه‌های عددی، نام متغیر یا nil باشند.

**ساختار select:** مانند ساختار switch است با این تفاوت که تایپ متغیر را برای انتخاب بررسی می‌کند و نه مقدار آن را. یک عبارت select کامل مشابه زیر است:

 select variable
 case TypeSpec
  statements...
 case TypeSpec1, TypeSpec2,... , TypeSpecN
  statements...
  /*other cases*/
 default
  statements...
 end

### ساختارهای تکرار
**ساختار while:** مهم‌ترین ساختار تکرار این زبان است. شامل هیچ یا چند دستور است که می‌توانند بیش از یک بار اجرا شوند. break و continue دو دستور خاص هستند که در صورت قرار گرفت در بلوک ساختار while به ترتیب سبب پایان یافتن حلقه و انتقال بی درنگ کنترل برنامه به قسمت ارزیابی شرط حلقه می‌شوند؛ بنابراین با داشتن دستور break می‌توان حلقه بی‌نهایت نیز داشت. یک عبارت while مشابه زیر است:

 while expresion
  statements...
    [break]
  statements...
    [continue]
  statements...
 end

اگر بلوک while تنها شامل یک دستور باشد، می‌توان آن را با استفاده از یک علامت دونقطه خلاصه کرد. یک مثال می‌تواند چنین باشد:

 while var  ۱۰۰
  break
  end
    // do something here
  count +=۱
 end

**ساختار for/in:** این ساختار یک مجموعه از عناصر مانند عناصر آرایه، واژه‌نامه، لیست و.. را پیمایش می‌کند. با استفاده از دستور break می‌توان حلقه را در هر نقطه دلخواه پایان داد؛ و با دستور continue به عنصر بعدی در مجموعه رفت.
این ساختار را می‌توان روی رشته کاراکترها هم اعمال کرد که در این صورت روی کاراکترها از ابتدا تا انتها عمل می‌کند. علاوه بر بدنه اصلی بلاک for/in می‌تواند بلاک‌های خاصی با عناوین forfirst, forlast و formiddle داشته باشد که به ترتیب قبل از اولین عنصر، بعد از آخرین عنصر و بعد از هر عنصر به جز عنصر آخر اجرا می‌شوند.

 for variable [, variable,...] in collection
  statements...
    [break | continue]
  statements...

  forfirst
  … first time only statements...
  end

  formiddle
  … statements executed between element processing...
  end

  forlast
  … last time only statements...
  end
 end

## توابع
توابع با کلمه کلیدی function مشخص می‌شوند. در ادامه نام تابع و دو پرانتز باز و بسته که می‌توانند شامل یک لیست از هیچ یا چند پارامتر باشند قرار می‌گیرد. پارامترها به صورت عادی انتقال با مقدار هستند اما می‌توان با عملگر ‘$’ که در ادامه توضیح داده می‌شود، انتقال با آدرس نیز انجام داد. توابع می‌توانند مقداری برگردانند. در واقع توابعی که به صورت صریح مقداری برنمی‌گردانند مقدار nil برای آن‌ها در نظر گرفته می‌شود. درون توابع می‌توان توابع دیگر و خود تابع را صدا زد. قطعه‌ای از از برنامه که درون هیچ تابعی نیست به عنوان «برنامه اصلی» در نظر گرفته می‌شود. توابع به صورت جداگانه از برنامه اصلی نگه‌داری می‌شوند؛ بنابراین می‌توان توابع و برنامه اصلی را در هم آمیخت و قبل از تعریف یک تابع آن را صدا زد. در زیر مثالی از تعریف تابع آورده شده‌است:

 function square(x)
  y = x * x
  return y
 end

مانند سایر دستورات توابع نیز می‌توانند با استفاده از نشانگر دو نقطه «:» مانند زیر خلاصه شوند:

 function square(x): return x * x

## متغیرها
### دامنه متغیرها
دامنه متغیرها به صورت محلی و سراسری تعریف می‌شود. با تعریف تابع درون تابع می‌توان دامنه‌های درونی‌تری تعریف کرد که دامنه پدرشان را به ارث می‌برند.

### متغیرهای محلی و سراسری
هنگامی که یک متغیر برای اولین بار درون یک تابع مشخص می‌شود، آن اسم به صورت محلی در نظر گرفته می‌شود. یعنی حتی در صورت وجود یک متغیر با همان نام در برنامه اصلی، نسخه محلی آن در تابع استفاده می‌شود. با این روش از تغییر یک متغیر که ممکن است در جای دیگری استفاده شود، جلوگیری می‌شود.
می‌توان به متغیرهای سراسری، درون توابع دسترسی داشت اما در حالت معمول نمی‌توان مقدار آن‌ها را تغییر داد. به عنوان مثال در قطعه برنامه زیر مقدار ۱٫۴۱ چاپ می‌شود اما تغییر مقدار sqr در خط بعد تنها توسط خود تابع، که آن را تغییر داده قابل رویت است و مقدار sqr استفاده شده در بیرون تابع هنوز همان ۱٫۴۱ است.

 sqr = ۱٫۴۱

 function sqr(x)
  printl (" sqr was: ", sqr)
  sqr = x * x
  return sqr
 end

 number = sqr(8) * sqr

در صورت نیاز به تغییر مقدار یک متغیر سراسری درون یک تابع، بدون دریافت آن به عنوان پارامتر، می‌توان آن را با استفاده از کلمه کلیدی "global" وارد تابع کرد.
زبان فالکن با استفاده از یک ساختار قدرتمند به نام "static initializer" این امکان را می‌دهد که متغیرهایی که درون این ساختار تعریف می‌شوند، تنها یک بار در اولین اجرای تابع مقدار دهی اولیه شوند و از آن پس در هر بار صدا زده شدن تابع، مقدار قبلی آن‌ها استفاده شود.

### یک عنصر با بیش از یک نام
هر عنصری در زبان فالکن می‌تواند به صورت ایستا یا پویا، نام مستعار داشته باشد. نام مستعار در حالت ایستا (که به آن ارجاع هم گفته می‌شود) با استفاده از عملگر ‘$’ فراهم می‌شود. هر تغییری در اسم مستعار سبب تغییر د ر اسم اصلی نیز می‌شود. مانند مثال زیر:
 a = 0; b = $a
>b //is 0
 a++;>b       //is 1
 b++;>a       //is 2

با استفاده از اسم مستعار ایستا (ارجاع) می‌توان به توابع پارامترهای از نوع آدرس انتقال داد که در نتیجه مقدار آن‌ها درون تابع قابل تغییر خواهد کرد.

  function changer(data)
  data++
 end

 a = ۰
 changer(a)   //pass by value
> a //it's still 0
 changer($a)  //pass by reference
> a //it's 1

اسم مستعار پویا با استفاده از عملگر ‘#’ حاصل می‌شود. این عملگر می‌تواند بیش از یک بار در یک سطر اعمال شود. مثلاً در تکه برنامه زیر اولین استفاده روی متغیر “var” و دومین اعمال روی متغیرهای “var1”، “var2”، “var3” اعمال می‌شود.

 v1 = "Hello"; v2 = ""; v3 = "world"
 alias var
 for i in [1:4]
  var = v + toString(i)
>##alias
 end
 printl()

## مدل‌های برنامه‌نویسی
فالکن شش مدل برنامه‌نویسی اصلی را ترکیب کرد:
* امری
* تابعی
* شئ گرا
* نمونه‌سازی اولیه
* پیغام گرا
* برنامه‌نویسی جدولی

### مدل برنامه‌نویسی امری
برنامه‌نویسی امری از طریق تعریف و فراخوانی توابع به صورت کلاسیک، تأمین می‌شود. یک مجموعه از دستورات رویه‌ای مانند if, while, switch،... نیز پشتیبانی می‌شوند.

### برنامه‌نویسی تابعی
زبان فالکن یک موتور سنجش به نام sigma-reductor دارد که به برنامه نویسان اجازه می‌دهد برنامه‌هایی با مدل تابعی خالص بنویسند که تفاوتی با آنچه در زبان لیسپ دیده شده‌است، ندارد. دنباله‌های تابعی با زبان آرایه‌ای استاندارد نمایش داده می‌شوند. یعنی این دنباله‌ها می‌توانند به صورت پویا به وسیله خود برنامه در ارزیابی‌های مختلف یا در طول ارزیابی sigma-recuction ایجاد و بررسی شوند یا تغییر کنند. اگر عنصر ابتدای یک آرایه خود قابل فراخوانی باشد، می‌توان آن آرایه را فراخوانی کرد.
شیوه استفاده از مدل‌های برنامه‌نویسی مختلف این اجازه را می‌دهد که از مدل‌های دیگری مانند مدل امری و شئ‌گرا در دنباله‌های تابعی استفاده شود؛ یا بالعکس یعنی در مدل امری از مدل تابعی نیز استفاده شود.
.

### شئ‌گرا
فالکن مدل برنامه‌نویسی شئ‌گرا را با در نظر گرفتن کلاسها، وراثت چندگانه، مقداردهی صفات و ساخت نمونه از کلاس‌ها فراهم می‌کند. صورت عمومی تعریف یک کلاس به صورت زیر است:

 class class_name[ (param_list) ] [from inh1 [,inh2,... , inhN]]
    [static block]
    [properties declaration]
    [init block]
    [method list]
 end

ساختار نمونه‌های کلاس‌ها ثابت و غیرقابل تغییر است. اما با توجه به خصوصیت تابعی بودن فالکن، که به توابع به صورت یک گونه خاص از داده نگاه می‌کند، می‌توان به صفات یک نمونه از کلاس، یک داده ساده یا یک تابع مقداردهی کرد؛ بنابراین با مقداردهی یک صفت با دنباله تابعی، آن صفت یا خصوصیت برای آن نمونه، به صورت یک متد در می‌آید.
نمونه‌های کلاس‌ها می‌توانند در دنباله‌های تابعی ساخته شوند. از آنجا که ساخت یک نمونه از طریق صدا زدن نماد آن کلاس (نام کلاس) صورت می‌گیرد، اجرای یک دنباله تابعی که اولین عنصر آن نماد یک کلاس است، به ساخت یک نمونه از آن کلاس منجر می‌شود.
مدل شئ‌گرای فالکن امکان پیاده‌سازی چند مفهوم برای یک عملگر را می‌دهد. یعنی ایجاد کلاس‌هایی که عملگرهای منطقی و محاسباتی برای آن‌ها مفهوم خاصی دارند.

### نمونه‌سازی اولیه بر پایه شئ‌گرایی
مدل برنامه‌نویسی نمونه‌سازی اولیه شئ‌گرا مانند مدل شئ‌گرا است با این تفاوت که از مفهوم کلاس استفاده نمی‌شود و نمونه‌ها متعلق به کلاس خاصی نیستند و ساختار نمونه‌ها به صورت پویا می‌باشد. یعنی ساخت یک نمونه به معنای ایجاد یک کپی از ساختار یک نمونه اولیه‌است اما این عمل به معنای محدود کردن ساختار نمونه جدید به ساختار اولیه، در تمام طول عمر آن نیست.
به عنوان مثال آرایه‌ها می‌توانند نمونه‌ای از استفاده از مدل نمونه‌سازی اولیه باشند. قطعه برنامه زیر نشان می‌دهد که با استفاده از Binding می‌توان برای یک نمونه از آرایه یک تابع خاص را ایجاد کرد. یعنی ساختار آن را به صورت پویا تغییر داد:

 vect = ['alph', 'beta', 'gamma']
 vect.dump = function()
  for n in [0:self.len()]
>@"$(n): ", self[n]
  end
 end

 vect.dump()

### پیغام‌گرا
مدل برنامه نویشی پیغام‌گرا امکان فراخوانی غیرمستقیم (به جای فراخوانی مستقیم) یک یا چند شنونده را فراهم می‌کند. محتوای پیغام‌ها می‌تواند هر عنصر زبان مانند کلاس، دنباله‌های تابعی، جدول،... باشد و اختیاری است. شنونده‌ها می‌توانند دارای اولویت نباشند. در این صورت ترتیب دریافت پیام توسط شنونده‌ها اتفاقی است و شنونده‌ها می‌توانند برای دریافت پیغام با یکدیگر رقابت کنند و مانع از دسترسی سایر شنونده‌ها شوند. همچنین شنونده‌ها می‌توانند دارای اولویت باشند یعنی پیغام قبل از دریافت توسط شنونده‌ها با اولویت پایین‌تر حتماً توسط شنونده‌ها با اولویت بالاتر دریافت شده‌است. در این صورت شنونده‌ها می‌توانند در مراحل مرتب برای ساخت یک جواب مشترک با یکدیگر همکاری کنند.

### جدولی
به برنامه‌نویسی مدل جدولی می‌توان به صورت گسترشی از مدل شئ‌گرا نگاه کرد؛ که در آن هر کلاس با یک جدول که ستون‌های آن خصوصیات و هر ریف یک نمونه از آن است نمایش داده می‌شود. علاوه بر در کنار هم آوردن همه نمونه‌ها، جهت کار با سایر نمونه‌های جدول، مدل جدولی این امکان را فراهم می‌آورد که هر تغییر در جدول اصلی، به صورت پویا در همه نمونه‌های آن جدول منعکس شود.
در واقع هر جدول از تعدادی سطر که آرایه هستند و یک عنوان که معنای هر ستون را مشخص می‌کند، تشکیل شده‌است و داده‌های هر سطر می‌توانند هر عنصر زبان مانند یک جدول دیگر باشند. این آرایه‌ها که در سطرها قرار دارند، دو خصوصیت مهم دارند. اولاً طول آن‌ها ثابت و برابر تعداد ستون‌های جدول است. البته تا زمانی که تغییری در آرایه سبب تغییر طول آن نشود، آن تغییر ممکن است. ثانیاً این آرایه‌ها ستون‌های جدول را به ارث می‌برند. یعنی این آرایه‌ها می‌دانن که قسمتی از یک جدول هستند. این آرایه‌ها می‌توانند از داده‌ها و منطق مشخص جدول استفاده کنند یا از طریق binding منطق خاص خود را داشته باشند. یعنی سطری از جدول که انتخاب شده‌است می‌تواند قابلیت‌های عملیاتی خاص خود را داشته باشد.

## سایر ویژگی‌ها
### حالت استثنا
فالکن مدیریت حالت استثنا را با استفاده از عبارات raise, try و catch انجام می‌دهد. دستور raise می‌تواند هر نوع عنصر زبان مانند عدد، رشته، شئ و… را پرتاب کند. توابع کتابخانه‌ای معمولاً نمونه‌هایی از کلاس Error یا کلاس‌هایی که از آن مشتق شده‌اند را پرتاب می‌کنند.
دستور catch می‌تواند برای گرفتن هر عنصر زبان یا یک تایپ مشخص استفاده شود. کلاس‌هایی که توسط دستورات catch گرفته می‌شوند، معمولاً در یک سلسله مراتب قرار می‌گیرند. در نتیجه امکان اداره عمومی‌تر خطاها را فراهم می‌آورند. بلوک‌های try-catch می‌توانند درون سایر بلوک‌ها یا به صورت تو در تو نیز استفاده شوند. یک بلوک try-catch کامل می‌تواند به شکل زیر باشد (قسمت‌های درون کروشه اختیاری هستند):

 try
    [try statements]
 [catch [object_type] [in error_variable]]
    [catch statements]
 [catch [object_type] [in error_variable]]
    [catch statements]
 end

### همروندی
همروندی با وجود مفهوم thread فراهم است. برای اشتراک داده‌ها بین آن‌ها نیز از روش‌های مختلف اشتراک استفاده می‌شود.
همچنین مفهوم coroutine که نوعی همروندی سبک‌تر نسبت به thread است، در این زبان پشتیبانی می‌شود.

### گسترش صریح رشته
زبان فالکن دارای ‘@’ یک عملگر یگانی جهت گسترش صریح رشته‌های حرفی است. رشته‌های شامل کاراکتر ‘$’ که بعد از آن‌ها یک متغیر آمده‌است، با استفاده از این عمگر گسترش پیدا می‌کند. مانند مثال زیر:
 
 a = ۱۲۳۴۵۶٫۷۸۹
 v = "formatted as"
 s = "a = $a, $v $(a:12rg3,.2), rounded hex as $(a:C)"
 printl(@ s)

 a = 123456.789, formatted as 123,456.79, rounded hex as 0x1E241

که مقداری که چاپ می‌کند برابر است با:

سه کاراکتر ‘$’ استفاده شده به ترتیب بیانگر مقدار، مقدار که با پهنای ۱۲ راست‌چین شده و در گروه‌های ۳تایی قرار گرفته و با ‘,’ جدا شده و با دقت دو عدد اعشاری گرد شده، مقدار که در مبنای ۱۶ با پسوند ‘0x’ نمایش داده شده هستند که با عملگر ‘@’ گسترش پیدا کرده‌اند.

### بارگذاری پویای یک ماژول
فالکن امکان بارگذاری پویای ماژول‌ها را از دو طریق فراهم می‌کند. یکی استفاده از تابع include و دیگری استفاده از کلاس Reflexive Compiler.

### مجموعه ماژول‌های استاندارد Feather
از آنجا که زبان فالکن از برنامه‌نویسی به صورت ماژولار پشتیبانی می‌کند، فالکن با یک مجموعه ماژول‌های استاندارد به نام Feather همراه است. ماژول‌های این مجموعه در حال حاضر شامل:
* Reflexive Compiler: ماژول بارگذاری پویای سایر ماژول‌ها
* Configuration Parser: پشتیبانی از فایل parser می‌کند.
* MXML: یک parser خیلی سریع و ساده برای XML سازگار با XML 1.0 است.
* Process: واسط جهت پشتیبانی از فرایندهای بین چند platform است.
* Regular Expressions: واسط سازگار با کتابخانه عبارات منظم PCRE 7.x است.
* Socket: پشتیبانی از شبکه سازی با استفاده از BSD Sockets را انجام می‌دهد.
* Threading: پشتیبانی از ساختارهای چند ریسه‌ای
* ZLib: واسط برای روش‌های ساده فشرده‌سازی

## پیاده‌سازی
ماشین مجازی مرکزی و تمام ماژول‌های استاندارد با زبان برنامه‌نویسی ++C پیاده‌سازی شده‌اند. اما بعضی از ماژول‌های بسیار سطح پایین و عناصر موتور متنی با استفاده از زبان C و زبان اسمبلی نوشته شده‌اند. در نتیجه در مجموع کارایی بالایی دارد.

## قابلیت استفاده
زبان فالکن معمولاً پشتیبانی شده و در بین بخش‌های مختلف به روز نگه داشته می‌شود که در میان آن‌ها موارد زیر قرار دارند.
* اوبونتو
* فدرا
* جنتو لینوکس
* اسلک‌ور
* آرچ لینوکس
* مندریوا
* اپن سوزه
* لینوکس سوزه

## منابع

## پیوند به بیرون

* صفحه اصلی زبان فالکون
* مصاحبه با طراح زبان

رده:اجازه‌نامه‌های نرم‌افزاری آزاد و متن‌باز
رده:زبان‌های اسکریپت‌نویسی
رده:زبان‌های برنامه‌نویسی
رده:زبان‌های برنامه‌نویسی بر پایه پیش‌نمونه
رده:زبان‌های برنامه‌نویسی رویه‌ای
رده:زبان‌های برنامه‌نویسی ساخته‌شده در ۲۰۰۳ (میلادی)
رده:زبان‌های برنامه‌نویسی سطح بالا
رده:زبان‌های برنامه‌نویسی شیءگرا
رده:زبان‌های برنامه‌نویسی کلاس محور
رده:زبان‌های برنامه‌نویسی ماشینی پویا
رده:زبان‌های تابعی
رده:سامانه‌های توکار
رده:کامپایلرها و مفسرهای آزاد
رده:کتابخانه‌های رایانه‌ای آزاد
رده:نرم‌افزارهای چندسکویی