لغة برمجة جديدة للهريس

منذ عدة سنوات جربت يدي في تطوير لغتي البرمجية. كنت أرغب في إنشاء لغة أبسط وأكثرها فاعلية وملاءمة.

في هذه المقالة ، أود أن أسلط الضوء على المراحل الرئيسية لعملي وأن أبدأ بوصف المفهوم الذي تم إنشاؤه للغة وتطبيقها الأول الذي أعمل عليه حاليًا.

سأقول مقدمًا أنني كتبت المشروع بالكامل في Free Pascal ، لأنه يمكن تجميع البرامج الموجودة به لعدد كبير من الأنظمة الأساسية ، ويقوم المترجم نفسه بإنتاج ثنائيات محسّنة للغاية (أقوم بتجميع كافة مكونات المشروع مع علامة O2).

وقت تشغيل اللغة


أول ما أتحدث عنه هو جهاز افتراضي اضطررت إلى كتابته لتشغيل التطبيقات المستقبلية بلغتي. قررت تطبيق بنية مكدس ، ربما ، لأنها كانت أسهل طريقة. لم أجد مقالًا عاديًا واحدًا عن كيفية القيام بذلك باللغة الروسية ، لذلك بعد قراءة المادة باللغة الإنجليزية ، جلست لتصميم وكتابة دراجتي. علاوة على ذلك ، سأقدم أفكاري والتطورات "المتقدمة" في هذا الشأن.

تنفيذ المكدس


من الواضح أن جهاز VM يقع على رأس المجموعة. في تنفيذي ، يعمل في كتل. في جوهرها ، هذه مجموعة بسيطة من المؤشرات ومتغير لتخزين فهرس الجزء العلوي من الرصة.
عند التهيئة ، يتم إنشاء صفيف مكون من 256 عنصرًا. إذا تم طرح المزيد من المؤشرات على المكدس ، فسيتم زيادة حجمه بواسطة العناصر 256 التالية. وفقا لذلك ، عند إزالة العناصر من المكدس ، يكون حجمها قابل للتعديل.

يستخدم VM عدة مكدسات:

  1. المكدس الرئيسي.
  2. كومة لتخزين نقاط العودة.
  3. كومة من جامع القمامة.
  4. معالج مكدس من try / catch / وأخيراً كتل.

الثوابت والمتغيرات


مع هذا ، كل شيء بسيط. تتم معالجة الثوابت في جزء صغير منفصل من التعليمات البرمجية وتتوفر في التطبيقات في المستقبل في عناوين ثابتة. المتغيرات هي مجموعة من المؤشرات ذات حجم معين ، ويتم الوصول إلى خلاياها عن طريق الفهرس - أي عنوان ثابت. يمكن وضع المتغيرات في الجزء العلوي من المكدس أو قراءتها من هناك. في الواقع ، لأن تقوم متغيراتنا في الأساس بتخزين المؤشرات على القيم في ذاكرة VM ، ثم العمل باستخدام المؤشرات الضمنية السائدة في اللغة.

جامع القمامة


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

التعامل مع محاولة / الصيد / وأخيرا كتل


كما هو الحال في أي لغة حديثة ، يعد التعامل مع الاستثناءات مكونًا مهمًا لها. يتم لف kernel من VM في كتلة try..catch ، والتي يمكن العودة إلى تنفيذ التعليمات البرمجية بعد التقاط استثناء عن طريق وضع القليل من المعلومات حوله على المكدس. في رمز التطبيق ، يمكنك تحديد كتل try / catch / وأخيراً من التعليمات البرمجية ، مع الإشارة إلى نقاط الإدخال التي يجب التقاطها (معالج الاستثناء) وأخيراً / end (نهاية الكتلة).

تعدد


يتم دعمه على مستوى VM. انها بسيطة ومريحة للاستخدام. إنه يعمل بدون نظام مقاطعة ، لذلك يجب تشغيل الرمز في عدة مؤشرات ترابط عدة مرات بشكل أسرع ، على التوالي.

مكتبات خارجية لـ VM


لا توجد طريقة للاستغناء عنها. يدعم VM عمليات الاستيراد ، تمامًا كما يتم تنفيذها بلغات أخرى. يمكنك كتابة جزء من الكود في Mash وجزء من الكود باللغات الأصلية ، ثم ربطهم معًا.

مترجم من اللغة عالية المستوى الهريس إلى bytecode ل VM


اللغة الوسيطة


لكتابة مترجم سريع من لغة معقدة إلى رمز VM ، قمت أولاً بتطوير لغة متوسطة. اتضح مشهد مرعب مثل المجمع ، والتي لا يوجد أي شعور خاص للنظر هنا. لا أستطيع إلا أن أقول إن المترجم في هذا المستوى يعالج معظم الثوابت والمتغيرات ويحسب عناوينها الثابتة وعناوين نقاط الدخول.

المترجم العمارة


اخترت ليس أفضل بنية للتنفيذ. لا يقوم المترجم بإنشاء شجرة رمز ، كما يلائم المترجمين الآخرين. وقال انه ينظر في بداية البناء. أي إذا كانت قطعة الشفرة التي تم تحليلها تبدو وكأنها "بينما <condition>:" ، فمن الواضح أن هذا هو وقت بناء الحلقة ويجب معالجتها على أنها حلقة loop. شيء مثل حالة التبديل معقدة.

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

رمز التحسين


هنا ، بالطبع ، يمكن تحقيقه بشكل أفضل (وسيتم تحقيقه ، ولكن لاحقًا ، مع وصول اليدين). حتى الآن ، يعرف المُحسِّن فقط كيفية قطع الكود والثوابت والاستيراد غير المستخدمة من التجميع. أيضا ، يتم استبدال عدة ثوابت بنفس القيمة بواحد. هذا كل شيء.

لغة الهريس


المفهوم الأساسي للغة


كانت الفكرة الرئيسية هي تطوير اللغة الأكثر وظيفية وبسيطة. وأعتقد أن التنمية تتواءم مع مهمتها مع اثارة ضجة.

كتل التعليمات البرمجية والإجراءات والوظائف


يتم فتح جميع الإنشاءات في اللغة بنقطتين : وتغلق مع المشغل النهائي .

يتم الإعلان عن الإجراءات والوظائف كـ proc و func ، على التوالي. يتم سرد الوسائط بين قوسين. تماما مثل معظم اللغات الأخرى.

يمكن أن تُرجع عبارة الإرجاع قيمة من دالة ما ، ويتيح لك بيان الفاصل الخروج من إجراء / وظيفة (إذا كانت خارج الحلقات).

مثال على الكود:

...

func summ(a, b):
  return a + b
end

proc main():
  println(summ(inputln(), inputln()))
end


  • : for..end, while..end, until..end
  • : if..[else..]end, switch..[case..end..][else..]end
  • : proc <>():… end, func <>():… end
  • Label & goto: <>:, jump <>
  • Enum .


, var .

:

a ?= 10
b ?= a + 20

var a = 10, b = a + 20

.


. Mash - . .. , , ( .. ), ().

, .

:

uses <bf>
uses <crt>

class MyClass:
  var a, b
  proc Create, Free
  func Summ
end

proc MyClass::Create(a, b):
  $a = new(a)
  $b = new(b)
end

proc MyClass::Free():
  Free($a, $b)
  $rem()
end

func MyClass::Summ():
  return $a + $b
end

proc main():
  x ?= new MyClass(10, 20)
  println(x->Summ())
  x->Free()
end

: 30.

:

uses <bf>
uses <crt>

class MyClass:
  var a, b
  proc Create, Free
  func Summ
end

proc MyClass::Create(a, b):
  $a = new(a)
  $b = new(b)
end

proc MyClass::Free():
  Free($a, $b)
  $rem()
end

func MyClass::Summ():
  return $a + $b
end

class MyNewClass(MyClass):
  func Summ
end

func MyNewClass::Summ():
  return ($a + $b) * 2
end

proc main():
  x ?= new MyNewClass(10, 20)
  println(x->Summ())
  x->Free()
end

: 60.

? !:

uses <bf>
uses <crt>

class MyClass:
  var a, b
  proc Create, Free
  func Summ
end

proc MyClass::Create(a, b):
  $a = new(a)
  $b = new(b)
end

proc MyClass::Free():
  Free($a, $b)
  $rem()
end

func MyClass::Summ():
  return $a + $b
end

class MyNewClass(MyClass):
  func Summ
end

func MyNewClass::Summ():
  return ($a + $b) * 2
end

proc main():
  x ?= new MyClass(10, 20)
  x->Summ ?= MyNewClass::Summ
  println(x->Summ())
  x->Free()
end

: 60.

:

uses <bf>
uses <crt>

class MyClass:
  var a, b
end

proc main():
  x ?= new MyClass
  println(BoolToStr(x->type == MyClass))
  x->rem()
  println(BoolToStr(typeof(3.14) == typeReal))
end

: true, true.


?= .
= .
. .
@<> — .
?<> — .
@= — .

:

uses <bf>
uses <crt>

proc main():
  var a = 10, b
  b ?= @a
  PrintLn(b)
  b ?= ?b
  PrintLn(b)
  b++
  PrintLn(a)
  InputLn()
end

: - , 10, 11.

Try..[catch..][finally..]end


:

uses <bf>
uses <crt>

proc main():
  println("Start")
  try:
    println("Trying to do something...")
    a ?= 10 / 0
  catch:
    println(getError())
  finally:
    println("Finally")
  end
  println("End")
  inputln()
end


GraalVM & Truffle. JIT , . , JIT GraalVM LLVM.


.


GitHub

, , .

Source: https://habr.com/ru/post/ar434966/


All Articles