DSL على Swift: كيفية إنشاء لغة برمجة خاصة بك في أمسية

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: عندما تكون العمومية هي العدو

\n\n

تخيل أنك تكتب كودًا لتهيئة الشبكة، أو تحليل السجلات (Logs)، أو وصف قواعد العمل. في كل مرة - تركيبات متشابهة، كومة من الأقواس، عوامل شرطية متداخلة، وقراءة مؤلمة لكود الآخرين. هل هذا مألوف؟

\n\n

في مثل هذه الحالات، تأتي اللغات الموجهة للمجال (DSL) للإنقاذ. هذا ليس سحرًا ولا إعادة اختراع العجلة - إنها طريقة لجعل الكود قابلاً للقراءة لدرجة أنه يوثق نفسه بنفسه. في هذه المقالة، سنشرح كيفية إنشاء DSL خاص بك على Swift - قوي، أنيق، ولا يتطلب مترجمًا (Compiler).

\n\n

DSL على Swift ليس مجرد موضة عابرة في تطوير iOS. إنها أداة تُستخدم في SwiftUI وCombine وحتى في مكتبات الخلفية (Backend). ستتعلم تصميم لغات تحل مشاكل محددة: من تحليل البيانات إلى واجهات المستخدم. هيا بنا!

\n\n

ما هو DSL ولماذا يحتاجه المطور؟

\n\n

Domain-Specific Language هي لغة مصممة خصيصًا لمجال ضيق واحد. على عكس اللغة العامة (مثل Swift أو Python)، تتحدث DSL بلغة مجال عملك. على سبيل المثال، بدلاً من:

\n\n
let config = NetworkConfig()\nconfig.host = "api.example.com"\nconfig.port = 443\nconfig.timeout = 30
\n\n

تكتب:

\n\n
let config = networkConfig {\n    host("api.example.com")\n    port(443)\n    timeout(30)\n}
\n\n

الخيار الثاني يُقرأ كتعليمات بلغة بشرية. هذا هو هدف DSL - تقليل العبء المعرفي وجعل الكود موثقًا ذاتيًا.

\n\n

في Swift، لإنشاء DSL غالبًا ما يُستخدم:

\n
    \n
  • Result Builders - ميزة جديدة في Swift 5.4+، تسمح ببناء هياكل متداخلة من خلال بناء جملة تصريحي (Declarative Syntax).
  • \n
  • السلاسل الوظيفية (Functional Chains) - نمط البناء (Builder Pattern) الكلاسيكي.
  • \n
  • العوامل (Operators) - تحميل العوامل الزائد (Operator Overloading) لإنشاء واجهات سلسة (Fluent Interfaces).
  • \n
\n\n

لكن دعنا ننتقل مباشرة إلى العمل. سننشئ DSL لتحليل ملفات CSV - وهي مهمة تعذب كل مطور مرة واحدة على الأقل في حياته.

\n\n

مثال 1: DSL بسيط لتهيئة الشبكة

\n\n

لنبدأ بمثال أساسي يوضح الآلية. لنفترض أننا بحاجة لوصف طلبات HTTP. بدلاً من كومة من المعاملات - DSL موجز.

\n\n
// تعريف هيكل الطلب\nstruct HTTPRequest {\n    var method: String = "GET"\n    var path: String = "/"\n    var headers: [String: String] = [:]\n    var body: Data? = nil\n}\n\n// إنشاء DSL عبر Result Builder\n@resultBuilder\nstruct RequestBuilder {\n    static func buildBlock(_ components: HTTPRequestModifier...) -> HTTPRequest {\n        var request = HTTPRequest()\n        for mod in components {\n            mod.apply(&request)\n        }\n        return request\n    }\n}\n\n// معدّلات الطلب\nprotocol HTTPRequestModifier {\n    func apply(_ request: inout HTTPRequest)\n}\n\nstruct MethodModifier: HTTPRequestModifier {\n    let method: String\n    func apply(_ request: inout HTTPRequest) {\n        request.method = method\n    }\n}\n\nstruct PathModifier: HTTPRequestModifier {\n    let path: String\n    func apply(_ request: inout HTTPRequest) {\n        request.path = path\n    }\n}\n\n// دوال مساعدة\nfunc method(_ m: String) -> HTTPRequestModifier { MethodModifier(method: m) }\nfunc path(_ p: String) -> HTTPRequestModifier { PathModifier(path: p) }\n\n// الاستخدام\n@RequestBuilder\nfunc buildGetRequest() -> HTTPRequest {\n    method("GET")\n    path("/users")\n}\n\nlet request = buildGetRequest()\nprint(request.method, request.path) // GET /users
\n\n

هذا الكود هو أساس أي DSL على Swift. @resultBuilder يجمع كل التعبيرات داخل الكتلة ويحولها إلى هيكل واحد. المهم: كل عنصر هو معدّل يغير الحالة.

\n\n

الآن دعنا نعقد المهمة: نضيف التداخل والشروط.

المدونات

توصيات الكتب