السلطان
  1. الرئيسية
  2. التوثيق
  3. المرجع

التنسيق

صفحات التوثيق

التنسيق

تنسق std.fmt.fmt (المكتبة.تنسيق.fmt) سلسلة تنسيق مع وسائطها. وهي تتبع std::fmt في Rust (format! وwrite! وprint! وDisplay وDebug ولغة مواصفات التنسيق المصغرة) وfmt في Go (Sprintf وFprintf وPrintf). تستعمل سلاسل التنسيق صياغة Rust وتنتج مخرجات Rust، فسلسلة التنسيق المكتوبة لـ Rust تعطي النص نفسه، إلا في الفروق المذكورة في هذه الصفحة. تغطي الصفحة الواجهة البرمجية، وقواعد سلسلة التنسيق، والمنسقات المخصصة، والفحص وقت الترجمة، وهي موجهة إلى المبرمجين الذين ينسقون النصوص وإلى من يعملون على std.fmt.

استورد المكتبة.تنسيق.fmt;

عرف الرئيسية(): عدد {
    دع الاسم: نص = «سلطان»;
    طابق نسق(«{:<8}|{:>5}|{:.2}», [قيمة_تنسيق.نص(&الاسم), قيمة_تنسيق.عدد(42), قيمة_تنسيق.ع64(3.14159)]) مع
    | نتيجة.نجح(السطر) -> { دع ر: نتيجة طبيعي, خطأ_تنسيق = اطبع_سطرا_منسقا(«{}», [قيمة_تنسيق.نص(&السطر)]); }
    | نتيجة.خطأ(_) -> { أرجع 1; };
    أرجع 0;
}

يطبع البرنامج سلطان | 42|3.14. وسلسلة التنسيق حرفية، فيفحصها المترجم مقابل الوسائط قبل تشغيل البرنامج. وتوجد أمثلة أخرى في examples/{en,ar}/fmt/ (formatting وtable وdisplay).

الوسائط

لا توجد في السلطان دوال متغيرة عدد الوسائط (variadic) ولا سمات (traits)، ولذلك تكون الوسائط متجهًا من قيم قيمة_تنسيق، مع منشئ واحد لكل نوع.

المنشئ يحمل
عدد، طبيعي عدد، طبيعي
ص8 … ص64، ط8 … ط64 الأعداد الصحيحة ذات العرض الثابت؛ والعرض مهم في x وo وb للقيم السالبة
ع32، ع64 الأعداد العائمة
منطقي، حرف القيم المنطقية والحروف
نص &نص: مستعار، ولا يُنسخ أبدًا
بايتات &متجه ط8
أعداد، نصوص &متجه عدد، &متجه نص (للتصحيح فقط)
قائمة &متجه قيمة_تنسيق: أي قائمة، ومنها القوائم المتداخلة (للتصحيح فقط)
مخصص دالة(&متغير منسق): نتيجة طبيعي, خطأ_تنسيق، أي Display وDebug لنوع يعرّفه المستخدم

النصوص والمتجهات مستعارة، فلا ينسخ التنسيق شيئًا ويحتفظ المستدعي بقيمه. ويُمرر متجه الوسائط بالقيمة، ونقله لا يكلف شيئًا. وهو في العادة حرفي متجه داخل الاستدعاء نفسه، وهذا يتيح للمترجم أيضًا فحص سلسلة التنسيق مقابله.

الواجهة البرمجية

الدالة Rust / Go
نسق(القالب, الوسائط): نتيجة نص, خطأ_تنسيق format! / fmt.Sprintf
نسق_في(&متغير م, القالب, الوسائط): نتيجة طبيعي, خطأ_تنسيق write! إلى String، أو إلى Formatter داخل منسق مخصص
اكتب_منسقا(&الوجهة, الكتابة, القالب, الوسائط) write! على io::Write / fmt.Fprintf (أي كاتب<ح> من std.io)
اكتب_منسقا_بمنسق(&متغير م, &الوجهة, الكتابة, القالب, الوسائط) مثل السابق، مع إعادة استعمال مخزن منسق واحد
اطبع_منسقا، اطبع_سطرا_منسقا، اطبع_خطأ_منسقا، اطبع_سطر_خطأ_منسقا print!, println!, eprint!, eprintln! / fmt.Printf
سجل_منسقا(&المسجل, المستوى, القالب, الوسائط) (std.io.log) log::info! / log.Printf

تعيد دوال الكتابة والطباعة عدد البايتات المكتوبة. وتحمل أسماء الطباعة الإنجليزية اللاحقة _fmt لأن print وprintln اسمان لدالتين في std.io أصلًا، والبرنامج الذي يستورد الاثنتين كان سيجد الأسماء ملتبسة.

المنسق (منسق) مخزن مخرجات واحد. وتتيح أنشئ_منسقا وأنشئ_منسقا_بسعة ونص_المنسق (نسخة) وخذ_نص_المنسق (نقل إلى الخارج بلا نسخ) وطول_المنسق وامسح_المنسق (تحتفظ بالمخزن) للحلقة أن تعيد استعمال منسق واحد، كما تفعل اكتب_منسقا_بمنسق.

سلسلة التنسيق

format_string := text [ maybe_format text ] *
maybe_format  := '{' '{' | '}' '}' | format
format        := '{' [ argument ] [ ':' format_spec ] [ ws ] * '}'
argument      := integer
format_spec   := [[fill]align][sign]['#']['0'][width]['.' precision]type
fill          := character
align         := '<' | '^' | '>'
sign          := '+' | '-'
width         := count
precision     := count | '*'
type          := '' | '?' | 'x?' | 'X?' | 'x' | 'X' | 'o' | 'b' | 'e' | 'E'
count         := integer | integer '$'

هذه قواعد Rust بدون الوسائط المسماة. يأخذ {} الوسيط التالي، ويأخذ {N} الوسيط N، و{{ و}} قوسان حرفيان. ويجب استعمال كل وسيط، كما في Rust.

العنصر السلوك
الحشو والمحاذاة {:<8} {:^8} {:>8} {:*^9}. يُحاذى النص إلى اليسار افتراضيًا والأعداد إلى اليمين. وعند التوسيط يوضع حرف الحشو الفردي بعد القيمة.
الإشارة {:+} يطبع الإشارة دائمًا. و- مقبول ويُتجاهل. ولا تكون لـ NaN إشارة أبدًا.
# الصيغة البديلة: البادئات 0x و0o و0b. والتصحيح المنسق ({:#?}) يضع كل مدخل من القائمة في سطر خاص به، بإزاحة أربع مسافات.
0 الحشو بالأصفار مع مراعاة الإشارة: {:08} لـ -42 هو -0000042، و{:#010x} لـ 255 هو 0x000000ff. ويُتجاهل حينئذ الحشو والمحاذاة. والحشو بالأصفار ينطبق على NaN وinf أيضًا، كما في Rust.
العرض والدقة حرفيات أو وسائط. يأخذ {:1$} العرض من الوسيط 1، و{:.2$} الدقة من الوسيط 2، ويأخذ {:.*} الدقة من الوسيط التالي قبل القيمة. وكلاهما لا يتجاوز 65535 (u16 في Rust)، ويجب أن يكون الوسيط عددًا صحيحًا غير سالب.
الدقة تقتطع النص، وتقرّب الأعداد العائمة، وتتجاهلها الأعداد الصحيحة. ويستعمل التقريب القيمة الدقيقة، مع التقريب إلى الزوجي عند المنتصف، كما يفعل Rust: {:.1} لـ 0.25 هو 0.2 ولـ 0.35 هو 0.3.

الأنواع

النوع الأعداد الصحيحة الأعداد العائمة النص غير ذلك
{} عشري أقصر صيغة تُقرأ إلى القيمة نفسها، بالصيغة الموضعية (1e21 يطبع كل أرقامه، و-0.0 هو -0) كما هو true/false؛ والحرف؛ والبايتات نصًا بترميز UTF-8
{:?} عشري الأقصر؛ بالصيغة الأسية تحت 1e-4 ومن 1e16؛ مع .0 للقيمة الصحيحة (1.0، 1e16) بين علامتي تنصيص ومع تهريب القوائم بصيغة [a, b]؛ والحرف بين علامتي تنصيص
{:x} {:X} ست عشري؛ والقيمة السالبة بمتممها الثنائي بعرضها البايتات أرقامًا ست عشرية (%x في Go)
{:o} {:b} ثماني، ثنائي
{:e} {:E} الصيغة الأسية (1.234e3) الصيغة الأسية، الأقصر أو بدقة محددة (1.2345e3، و{:.2e} هو 1.23e3)
{:x?} {:X?} التصحيح بالست عشري، وداخل القوائم أيضًا

النوع الذي لا ينطبق على الوسيط ({:x} لنص، أو {} لقائمة) هو الخطأ نوع_غير_مناسب.

تأتي أرقام الأعداد العائمة من std.text.float: أقصر عدد عشري يُقرأ إلى القيمة نفسها، أو، مع الدقة، القيمة الدقيقة مقرّبة إلى الزوجي عند المنتصف (أرقام_ع64_بخانات_كسرية، أرقام_ع64_بخانات_معنوية). وكلاهما يجرب Grisu (بنمطي الأقصر والعدد المحدد) ويرجع إلى خوارزمية الأعداد الكبيرة الدقيقة حين لا يستطيع Grisu الجزم، وهي استراتيجية flt2dec في Rust (Grisu ثم Dragon).

عرض النص والعربية

يُحسب عرض النص ودقته بعناقيد الحروف الممتدة (extended grapheme clusters) (UAX #29، std.unicode.grapheme). فالحرف مع حركاته محرف واحد، ولذلك يضيف {:>6} لـ «مُحَمَّد» (8 قيم Unicode و4 عناقيد) حرفي حشو، ولا تفصل الدقة أبدًا حرفًا عن علاماته. والنص ASCII الخالي من CR يأخذ مسارًا سريعًا. أما Rust فيعد قيم Unicode المفردة (scalar values)؛ ويتفق الأسلوبان في النص الخالي من العلامات المركبة. وزوج اللام ألف عنقودان مع أن الخطوط ترسمهما رسمًا واحدًا.

لا تدرج std.fmt علامات اتجاه. والمستدعي هو من يعزل النص ثنائي الاتجاه حيث يحتاج إلى ذلك، باستعمال std.text.bidi.

نص التصحيح

يضع {:?} النص بين علامتي تنصيص ويهرّب \t \r \n \\ \0 وعلامة التنصيص. ويكتب محارف التحكم، ومحارف التنسيق (ومنها علامتا الاتجاه U+200E وU+200F، ومحارف العزل)، ومحارف الفصل (عدا المسافة)، ومحارف الاستعمال الخاص، والمحارف غير المعينة بصيغة \u{…}، كما تفعل escape_debug في Rust. وتُهرَّب العلامة المركبة إذا كانت في بداية القيمة. ويهرّب Rust أيضًا العلامة الواقعة بعد حرف داخل النص، أما std.fmt فتبقيها، فتبقى الحركات العربية مقروءة. ولا ينطبق العرض على نص التصحيح ولا على الحروف، كما في Rust.

المنسقات المخصصة

يحمل الوسيط مخصص دالة مغلقة تكتب في المنسق، مثل impl Display وimpl Debug في Rust.

استورد المكتبة.تنسيق.fmt;

نوع نقطة = { س: عدد, ص: عدد };

عرف اعرض_نقطة(ن: &نقطة, م: &متغير منسق): نتيجة طبيعي, خطأ_تنسيق {
    إذا (المنسق_للتصحيح(م)) {
        أرجع نسق_في(م, «نقطة {{ س: {}, ص: {} }}», [قيمة_تنسيق.عدد((*ن).س), قيمة_تنسيق.عدد((*ن).ص)]);
    };
    أرجع نسق_في(م, «({}, {})», [قيمة_تنسيق.عدد((*ن).س), قيمة_تنسيق.عدد((*ن).ص)]);
}

عرف الرئيسية(): عدد {
    دع ن: نقطة = { س: 1, ص: 2 };
    دع ر: نتيجة طبيعي, خطأ_تنسيق = اطبع_سطرا_منسقا(«{} {:?}», [
        قيمة_تنسيق.مخصص(دالة(م: &متغير منسق): نتيجة طبيعي, خطأ_تنسيق -> اعرض_نقطة(&ن, م)),
        قيمة_تنسيق.عدد(3)
    ]);
    أرجع 0;
}

تستطيع الدالة المغلقة قراءة مواصفة الموضع المحجوز (placeholder) بـ عرض_المنسق ودقة_المنسق وحشو_المنسق ومحاذاة_المنسق والمنسق_بديل والمنسق_بإشارة والمنسق_بأصفار وصيغة_المنسق والمنسق_للتصحيح. وتكتب بـ نسق_في واكتب_نصا_في_المنسق واكتب_حرفا_في_المنسق، وتراعي العرض والمحاذاة والدقة بـ احش_نصا (Formatter::pad في Rust). وكما في Rust، فالمخرجات المكتوبة بدون احش_نصا تتجاهل العرض.

تُستعاد مواصفة المنسق بعد الدالة المغلقة، فيحصل العنصر التالي من القائمة على مواصفة القائمة. والعنصر المخصص في قائمة مطبوعة بالتنسيق المنسق يُزاح سطرًا سطرًا.

ما يمكن أن تلتقطه الدالة المغلقة المخصصة

لا تلتقط الدوال المغلقة في السلطان إلا القيم البسيطة: الأعداد والقيم المنطقية والتعدادات والسجلات المكونة منها، والدوال المغلقة الأخرى. ولا يمكن التقاط قيمة تملك نصوصًا أو متجهات. نسّقها بدالتها الخاصة في منسق، ثم مرّر خذ_نص_المنسق وسيطًا من نوع نص، فهو ينقل النص إلى الخارج بلا نسخ.

التقاط مرجع، بحيث يستعير المنسق &ن لقيمة تملك نصًا، غير مدعوم لأنه لا يمكن جعله سليمًا. فقيمة الدالة المغلقة بيئة معدودة المراجع، ونوع دالتها دالة(...) لا يحمل عمرًا (lifetime)، والدوال المغلقة تنتقل بحرية عبر المعاملات والحقول والمتجهات والقيم المعادة. وكان مدقق الملكية سيضطر إلى معاملة كل قيمة دالة مغلقة على أنها قد تحمل استعارة، وكل نوع دالة على أنه مقيد بعمر، كما يفعل Rust مع impl Fn + 'a. والتقاط &ن الذي يُقبل دون ذلك قد يعيش أطول من ن.

الفحص وقت الترجمة

تُفحص سلسلة التنسيق الحرفية وقت الترجمة، كما تفحص format_args! في Rust وstd::format في C++20 سلاسلهما. وينطبق الفحص على استدعاء دالة تنسيق من المكتبة القياسية (دوال std.fmt.fmt وstd.io.log التي يكون آخر معاملين فيها القالب والوسائط) تكون سلسلة التنسيق فيه حرفية.

في مثل هذا الاستدعاء، يضيف الخفض قبل الاستدعاء التزامًا وقت الترجمة: استدعاءً لمدقق std.fmt تحقق_قالب_تنسيق_وقت_الترجمة بالحرفي، ومعه منشئ كل عنصر حين تكون الوسائط حرفي متجه (msir/lowering/expressions.sn، أصدر_تحقق_تنسيق_خفض). ويقيّمه منسق المترجم بالمفسر كأي استدعاء وقت الترجمة ثم يزيله (driver/compile_time.sn). وينسق المدقق السلسلة في منسق مؤقت بوسائط بديلة من تلك الأنواع، عبر المحلل نفسه المستعمل وقت التشغيل، فلا يمكن أن يختلف فحص وقت الترجمة عن فحص وقت التشغيل. وتتشارك فحوص الترجمة الواحدة سياق مفسر واحدًا، فتكون تكلفة كل استدعاء هي تنسيق سلسلة واحدة.

الاستدعاء ما يُفحص وقت الترجمة
نسق(«{} {:x}», [قيمة_تنسيق.نص(&ن), قيمة_تنسيق.عدد(1)]) كل شيء: الصياغة، وعدد الوسائط، والفهارس الموضعية، واستعمال كل وسيط، وملاءمة النوع لكل موضع محجوز (x للأعداد الصحيحة والبايتات فقط، وe للأعداد، و? للقوائم)، وكون وسائط العرض والدقة أعدادًا صحيحة
عنصر ليس استدعاء منشئ ([أ] مع أ: قيمة_تنسيق) يُعد، ولا يُفحص نوعه
وسائط مربوطة بـ دع (نسق(«{} {}», الوسائط)) الصياغة فقط؛ والوسائط وقت التشغيل
سلسلة تنسيق مبنية وقت التشغيل (نسق(القالب, الوسائط)) لا شيء؛ وينطبق فحص وقت التشغيل الموصوف أدناه

سلسلة التنسيق المرفوضة هي SULTANC-FORMAT-STRING / سلسلة_تنسيق_غير_صالحة، مع سبب لكل خطأ (انظر الأخطاء)، ومع موضع الوسيط في أخطاء الوسائط، بالعربية والإنجليزية. ويشير التشخيص إلى البايت المخالف داخل الحرفي ({ الموضع المحجوز، أو الحرف الخاطئ)، أو إلى الحرفي كله في حالة الوسيط غير المستعمل أو الحرفي الذي يحتوي تهريبًا:

خطأ[SULTANC-FORMAT-STRING]: سلسلة تنسيق غير صالحة — موضع وسيط التنسيق بدءا من الصفر هو 1. نوع التنسيق في الموضع لا يناسب الوسيط الذي ينسقه.
  --> main.sn:11:18..11:19
    |
 11 |     طابق نسق(«{} {:X}», [قيمة_تنسيق.عدد(1), قيمة_تنسيق.نص(&الاسم)]) مع
    |                  ^
    |

والاختبارات هي tests/{ar,en}/stdlib/fmt/compile_check.sn وreject_format_*.sn.

الأخطاء

سلسلة التنسيق غير الحرفية تُحلَّل وتُفحص مقابل الوسائط وقت التشغيل، كما تفحص fmt في Go كل سلسلة تنسيق، وكما تفعل std::vformat في C++. وكل خطأ قيمة من خطأ_تنسيق. وعند الخطأ يُترك المنسق كما كان، ولا يُكتب شيء ولا يُطبع.

المتغير الحمولة خطأ الترجمة المقابل في Rust
قوس_غير_مغلق إزاحة { expected }
قوس_إغلاق_منفرد الإزاحة unmatched } found
مواصفة_غير_صالحة الإزاحة invalid format string
نوع_غير_معروف الإزاحة unknown format trait
وسيط_مسمى الإزاحة (الوسائط المسماة غير مدعومة)
وسيط_مفقود فهرس الوسيط invalid reference to positional argument
وسيط_غير_مستخدم فهرس الوسيط argument never used
نوع_غير_مناسب فهرس الوسيط the trait is not implemented
عدد_غير_صالح فهرس الوسيط expected usize
فشل_الكتابة حالة الكاتب لا يوجد
فشل_مخصص لا شيء fmt::Error

وقت الترجمة، تكون الأخطاء التسعة الأولى أسباب SULTANC-FORMAT-STRING: format.unclosed-brace وformat.unmatched-close-brace وformat.invalid-spec وformat.unknown-type وformat.named-argument وformat.missing-argument وformat.unused-argument وformat.mismatched-type وformat.invalid-count.

ويحوّل std.error الخطأ بـ فشل_من_خطأ_تنسيق؛ انظر الأخطاء.

Rust وGo

Rust Go std.fmt
format!("{} {}", a, b) fmt.Sprintf("%v %v", a, b) نسق(«{} {}», [قيمة_تنسيق.عدد(أ), قيمة_تنسيق.عدد(ب)])
write!(w, …) fmt.Fprintf(w, …) اكتب_منسقا(&الوجهة, الكتابة, …) / نسق_في(&متغير م, …)
println!(…) / eprintln!(…) fmt.Println, fmt.Printf اطبع_سطرا_منسقا(…) / اطبع_سطر_خطأ_منسقا(…)
{:>8} {:<8} {:^8} %8v %-8v مثلها
{:08.3} %08.3f مثلها
{:x} {:#X} {:o} {:b} {:e} %x %#X %o %b %e مثلها
{:?} {:#?} %q, %v, %+v التصحيح؛ والتصحيح المنسق
{:.*} {:1$} %.*f %*d مثلها
impl Display for T String() string وسيط مخصص
الفحص وقت الترجمة go vet فحص سلسلة التنسيق الحرفية وقت الترجمة (SULTANC-FORMAT-STRING)؛ وخطأ_تنسيق وقت التشغيل للسلسلة المبنية وقت التشغيل
لا يوجد %!d(string=x) في المخرجات خطأ، بلا مخرجات

اختبارات المواصفة، tests/{en,ar}/stdlib/fmt/spec_rust.sn، مولدة من مخرجات rustc نفسه لسلاسل التنسيق والوسائط نفسها (tools/generate/generate_fmt_tests.py).

الأداء

يستعمل كل استدعاء مخزن مخرجات واحدًا (أو مخزن المنسق المعاد استعماله)، يُحجز مرة واحدة للنص الحرفي والوسائط. وتُكتب الأعداد الصحيحة والنصوص والحروف والقيم المنطقية فيه مباشرة. ويحتاج الحشو إلى عرض المحتوى، ويُقاس للنص غير ASCII الذي له عرض أو دقة بتقطيع العناقيد. وتخصص الأعداد العائمة ذاكرة لأرقامها. وتشغيل 10^5 دورة من التنسيق المختلط لا يغير عدد البايتات الحية في المخصص (tests/{en,ar}/stdlib/fmt/rss_flat.sn).

القيود

  • الوسائط المسماة ({الاسم}) غير مدعومة.
  • الدوال المغلقة المخصصة لا تلتقط إلا القيم البسيطة.
  • أنواع الوسائط الخاصة بالتصحيح (أعداد ونصوص وقائمة) ترفض مواضع العرض ({}).

وتوجد أيضًا std.text.format (نسق_نصا، نسق_عددا، …) مجموعةً من الدوال المساعدة الأقدم؛ أما الشيفرة الجديدة فتستعمل std.fmt.

انظر أيضًا