بديل أكثر تحملاً بلا حدود للغة البرمجة C.

بالنسبة لأولئك منكم الذين يتذكرون "Free" ، يعد Oak في الأساس نسخة أكثر قوة وعالية من هذا المشروع. الهدف من البلوط هو أن يكون مستوى مرتفع قدر الإمكان في الواجهة الأمامية ، ولكنه صغير ومنخفض المستوى في الواجهة الخلفية.
أنا خريج المدرسة الثانوية الطازجة وطالبة في الكلية أبحث عن عمل. إذا كنت تستمتع بمشاريعي ، فكر في دعمني عن طريق شراء قهوة لي!
مفتاح قابلية النقل المجنونة في Oak هو تنفيذها الخلفي المدمج بشكل لا يصدق. يمكن التعبير عن رمز الواجهة الخلفية لـ Oak في أقل من 100 خط من C. مثل هذا التنفيذ الصغير ممكن فقط بسبب مجموعة التعليمات الصغيرة للتمثيل الوسيط. يتكون IR Oak's IR فقط من 17 تعليمات مختلفة . هذا على قدم المساواة مع Brainfuck!
الواجهة الخلفية من البلوط ببساطة جدا. كل تعليمات تعمل على شريط الذاكرة . هذا الشريط هو في الأساس مجموعة ثابتة من عوامات الدقة المزدوجة.
let x : num = 5.25 ; ... let p : & num = & x ; `beginning of heap`
| | |
v v v
[ 0 , 0 , 0 , 5.25 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 3 , 0 , 0 , 0 , 0 , 0 , 0 , ... ]
^
|
`current location of the stack pointer`عندما يتم تعريف متغير في وظيفة ، يتم إعطاؤه موضعًا ثابتًا بالنسبة لمؤشر الأساس الحالي للماكينة الافتراضية. لذلك ، عندما يتم استدعاء وظيفة ، يتم تخصيص مساحة لمتغيرات الوظيفة على المكدس ، ويتم زيادة المؤشر الأساسي لاستخدام هذه المساحة الجديدة. بعد ذلك ، يحل المترجم فقط محل المتغير بعنوانه المضافة إلى إزاحة المؤشر الأساسي في بقية الكود!
بالإضافة إلى ذلك ، يعمل شريط الذاكرة كمكدس وكومة . بعد تعيين مساحة لجميع متغيرات البرنامج ، تبدأ الذاكرة المستخدمة في المكدس. ينمو المكدس ويتقلص مع البيانات في جميع أنحاء البرنامج: عندما يتم تلخيص رقمين ، على سبيل المثال ، يتم انتشارها من المكدس واستبدالها بالنتيجة. وبالمثل ، فإن الكومة تنمو وتقلل في جميع أنحاء البرنامج. ومع ذلك ، يتم استخدام الكومة للبيانات المخصصة ديناميكيًا : معلومات ذات بصمة ذاكرة غير معروفة في وقت الترجمة .
الآن بعد أن فهمت كيف تعمل الواجهة الخلفية لـ Oak بشكل أساسي ، إليك مجموعة التعليمات الكاملة!
| تعليمات | أثر جانبي |
|---|---|
push(n: f64); | ادفع رقمًا على المكدس. |
add(); | قم ببوب رقمين من المكدس ، وادفع مجموعهما. |
subtract(); | قم ببوب رقمين من المكدس. طرح الأول من الثاني ، وادفع النتيجة. |
multiply(); | انطلق رقمين من المكدس ، وادفع منتجهم. |
divide(); | قم ببوب رقمين من المكدس. قسّم الثاني على الأول ، وادفع النتيجة. |
sign(); | قم ببوب رقم من المكدس. إذا كان أكبر أو تساوي الصفر ، فدفع 1 ، وإلا ادفع -1 . |
allocate(); | قم ببث عدد من المكدس ، وأرجع مؤشرًا إلى هذا العدد من الخلايا الحرة على الكومة. |
free(); | قم ببث رقم من المكدس ، وانتقل إلى حيث يشير هذا الرقم في الذاكرة. انطلق رقمًا آخر من المكدس ، وحرر العديد من الخلايا في هذا الموقع في الذاكرة. |
store(size: i32); | قم ببث رقم من المكدس ، وانتقل إلى حيث يشير هذا الرقم في الذاكرة. ثم ، أرقام size البوب من المكدس. تخزين هذه الأرقام بترتيب عكسي في هذا الموقع في الذاكرة. |
load(size: i32); | قم ببث رقم من المكدس ، وانتقل إلى حيث يشير هذا الرقم في الذاكرة. ثم ، دفع size خلايا الذاكرة المتتالية على المكدس. |
call(fn: i32); | اتصل بوظيفة محددة من قبل المستخدم بواسطة معرف المترجم الخاص به. |
call_foreign_fn(name: String); | استدعاء وظيفة أجنبية باسمها في المصدر. |
begin_while(); | ابدأ حلقة الوقت. لكل تكرار ، قم ببث رقم من المكدس. إذا لم يكن الرقم صفرًا ، فابع الحلقة. |
end_while(); | بمناسبة نهاية حلقة الوقت. |
load_base_ptr(); | قم بتحميل المؤشر الأساسي لإطار المكدس الثابت ، والذي يكون دائمًا أقل من أو يساوي مؤشر المكدس. يتم تخزين المتغيرات بالنسبة إلى المؤشر الأساسي لكل وظيفة. لذلك ، فإن الوظيفة التي تحدد x: num و y: num ، x قد يتم تخزينها في base_ptr + 1 ، وقد يتم تخزين y في base_ptr + 2 . يتيح ذلك الوظائف لتخزين المتغيرات في الذاكرة ديناميكيًا وبنصت هناك حاجة إليها ، بدلاً من استخدام مواقع الذاكرة الثابتة. |
establish_stack_frame(arg_size: i32, local_scope_size: i32); | انطلق عدد arg_size عدد الخلايا قبالة المكدس وتخزينها بعيدًا. ثم ، اتصل على load_base_ptr لاستئناف إطار المكدس الأصل عند انتهاء هذه الوظيفة. Push local_scope_size عدد الأصفار على المكدس لإفساح المجال لمتغيرات الوظيفة. أخيرًا ، ادفع خلايا الحجة المخزنة مرة أخرى على المكدس حيث تم طلبها في الأصل. |
end_stack_frame(return_size: i32, local_scope_size: i32); | POP OFF return_size عدد الخلايا خارج المكدس وتخزينها بعيدًا. ثم ، pop local_scope_size عدد الخلايا من المكدس لتجاهل ذاكرة إطار المكدس. قم ببث قيمة من المكدس وتخزينها في المؤشر الأساسي لاستئناف إطار المكدس الأصل. أخيرًا ، ادفع خلايا قيمة الإرجاع المخزنة مرة أخرى على المكدس حيث تم طلبها في الأصل. |
باستخدام هذه التعليمات فقط ، فإن Oak قادر على تنفيذ تجريدات مستوى أعلى من C يمكن أن تقدمه !!! قد لا يبدو ذلك كثيرًا ، لكنه قوي جدًا بالنسبة للغة الصغيرة.
بناء جملة البلوط مستوحى بشدة من لغة برمجة الصدأ.
يتم الإعلان عن الوظائف مع الكلمة الرئيسية fn ، وهي متطابقة مع وظائف الصدأ ، باستثناء دلالات return . بالإضافة إلى ذلك ، يتم الإعلان عن الأنواع والثوابت المحددة للمستخدم مع type والكلمات const على التوالي.
على غرار سمات Rust الخارجية ، يقدم Oak العديد من أعلام وقت الترجمة. بعض هذه تظهر أدناه جنبا إلى جنب مع ميزات البلوط الأخرى.

فكيف بالضبط يعمل برنامج التحويل البرمجي البلوط؟
تسطيح الهياكل في وظائفها
putnumln(*bday.day) putnumln(*Date::day(&bday)) . هذه عملية بسيطة جدا.احسب حجم نوع كل عملية
// `3` is the size of the structure on the stack
fn Date :: new ( month : 1 , day : 1 , year : 1 ) -> 3 {
month ; day ; year
}
// self is a pointer to an item of size `3`
fn Date :: day ( self : & 3 ) -> & 1 { self + 1 }
fn main ( ) -> 0 {
let bday : 3 = Date :: new ( 5 , 14 , 2002 ) ;
}قم بحساب بصمة ذاكرة البرنامج بشكل ثابت
تحويل تعبيرات البلوط والبيانات إلى تعليمات IR مكافئة
هناك العديد من الظروف المختلفة التي تكون فيها مكالمة الطريقة صالحة. أساليب دائما تأخذ مؤشر إلى الهيكل كحجة . ومع ذلك ، لا يلزم أن يكون الكائن الذي يدعو إلى الطريقة مؤشرًا . على سبيل المثال ، الكود التالي صالح: let bday: Date = Date::new(); bday.print(); . لا يمكن استخدام المتغير bday مؤشرًا ، ولكن يمكن استخدام الطريقة .print() . هذا هو السبب.
عندما يرى المترجم مكالمة طريقة مسطحة ، يحتاج إلى إيجاد طريقة لتحويل "تعبير المثيل" إلى مؤشر. للمتغيرات ، هذا سهل: فقط أضف مرجعًا! على سبيل المثال ، تعبيرات هي مؤشرات بالفعل ، الأمر أسهل: لا تفعل أي شيء! بالنسبة لأي نوع آخر من التعبير ، فإنه أكثر مطوّلة قليلاً. يتسلل المترجم في متغير مخفي لتخزين التعبير ، ثم يجمع استدعاء الطريقة مرة أخرى باستخدام المتغير كتعبير مثيل. رائع جدا ، أليس كذلك؟
قم بتجميع تعليمات الأشعة تحت الحمراء للهدف
Target . إذا قمت بتنفيذ كل من تعليمات IR الخاصة بلغتك باستخدام السمة Target ، فيمكن أن يقوم OAK تلقائيًا بتجميع الطريق إلى لغة البرمجة أو التجميع الجديدة! نعم ، الأمر سهل كما يبدو! للسماح للمستخدمين بقراءة وثائق المكتبات والملفات دون الوصول إلى الإنترنت ، يوفر OAK المفوض الفرعي doc . يتيح ذلك للمؤلفين إضافة سمات الوثائق إلى التعليمات البرمجية الخاصة بهم لمساعدة المستخدمين الآخرين على فهم التعليمات البرمجية أو واجهة برمجة التطبيقات الخاصة بهم دون الاضطرار إلى البحث عن المصدر وقراءة التعليقات.
فيما يلي بعض رمز المثال.
# [ std ]
# [ header ( "This file tests Oak's doc subcommand." ) ]
# [ doc ( "This constant is a constant." ) ]
const CONSTANT = 3 ;
// No doc attribute
const TEST = CONSTANT + 5 ;
# [ doc ( "This structure represents a given date in time.
A Date object has three members:
|Member|Value|
|-|-|
|`month: num` | The month component of the date |
|`day: num` | The day component of the date |
|`year: num` | The year component of the date |" ) ]
struct Date {
let month : num , day : num , year : num ;
# [ doc ( "The constructor used to create a date." ) ]
fn new ( month : num , day : num , year : num ) -> Date {
return [ month , day , year ] ;
}
# [ doc ( "Print the date object to STDOUT" ) ]
fn print ( self : & Date ) {
putnum ( self ->month ) ; putchar ( '/' ) ;
putnum ( self ->day ) ; putchar ( '/' ) ;
putnumln ( self ->year ) ;
}
}
# [ doc ( "This function takes a number `n` and returns `n * n`, or `n` squared." ) ]
fn square ( n : num ) -> num {
return n * n
}
fn main ( ) {
let d = Date :: new ( 5 , 14 , 2002 ) ;
d . print ( ) ;
} وهنا مثال على الاستخدام للوحدة الفرعية doc لطباعة الوثائق المنسقة إلى المحطة.

للحصول على بناء التطوير الحالي ، استنساخ المستودع وتثبيته.
git clone https://github.com/adam-mcdaniel/oakc
cd oakc
cargo install -f --path . للحصول على بناء الإصدار الحالي ، قم بالتثبيت من Cates.io.
# Also works for updating oakc
cargo install -f oakcبعد ذلك ، يمكن تجميع ملفات البلوط باستخدام Oakc Binary.
oak c examples/hello_world.ok -c
main.exeC الخلفية - أي مترجم في مجلس التعاون الخليجي يدعم C99
العودة إلى الوراء - برنامج التحويل البرمجي Golang 1.14
TypeScript الخلفية - برنامج التحويل البرمجي TypeScript 3.9