ภาษาโปรแกรมใหม่ Mash

เป็นเวลาหลายปีที่ฉันพยายามพัฒนาภาษาโปรแกรมของตัวเอง ในความคิดของฉัน ฉันต้องการสร้างภาษาที่เรียบง่าย ใช้งานได้เต็มรูปแบบ และสะดวกที่สุดเท่าที่จะเป็นไปได้

ในบทความนี้ ฉันต้องการเน้นขั้นตอนหลักของงานของฉัน และเริ่มต้นด้วยการอธิบายแนวคิดที่สร้างขึ้นของภาษาและการใช้งานครั้งแรกซึ่งฉันกำลังดำเนินการอยู่

ขอบอกล่วงหน้าเลยว่าผมเขียนทั้งโปรเจ็กต์ด้วย Free Pascal เพราะ... โปรแกรมบนนั้นสามารถประกอบได้สำหรับแพลตฟอร์มจำนวนมากและคอมไพเลอร์เองก็สร้างไบนารีที่ได้รับการปรับปรุงให้เหมาะสมที่สุด (ฉันรวบรวมส่วนประกอบทั้งหมดของโปรเจ็กต์ด้วยแฟล็ก O2)

รันไทม์ภาษา

ก่อนอื่น คุ้มค่าที่จะพูดถึงเครื่องเสมือนที่ฉันต้องเขียนเพื่อรันแอปพลิเคชันในอนาคตในภาษาของฉัน ฉันตัดสินใจใช้สถาปัตยกรรมสแต็ก เพราะอาจเป็นวิธีที่ง่ายที่สุด ฉันไม่พบบทความปกติเกี่ยวกับวิธีการทำเช่นนี้ในภาษารัสเซีย ดังนั้นหลังจากทำความคุ้นเคยกับเนื้อหาภาษาอังกฤษแล้ว ฉันจึงนั่งลงเพื่อออกแบบและเขียนจักรยานของตัวเอง ต่อไปผมจะนำเสนอแนวคิดและพัฒนาการ “ขั้นสูง” ของผมในเรื่องนี้

การใช้งานสแต็ก

แน่นอนว่าที่ด้านบนของ VM คือสแต็ก ในการใช้งานของฉันมันทำงานเป็นบล็อก โดยพื้นฐานแล้ว นี่คืออาร์เรย์ของพอยน์เตอร์แบบธรรมดาและเป็นตัวแปรสำหรับจัดเก็บดัชนีที่ด้านบนของสแต็ก
เมื่อเริ่มต้นแล้ว จะมีการสร้างอาร์เรย์ที่มีองค์ประกอบ 256 รายการ หากมีการพุชพอยน์เตอร์ลงบนสแต็กมากขึ้น ขนาดของมันจะเพิ่มขึ้นอีก 256 องค์ประกอบถัดไป ดังนั้นเมื่อลบองค์ประกอบออกจากสแต็ก ขนาดขององค์ประกอบจะถูกปรับขนาด

VM ใช้หลายสแต็ก:

  1. กองหลัก
  2. กองสำหรับเก็บคะแนนคืน
  3. กองเก็บขยะ.
  4. ลอง/จับ/บล็อกตัวจัดการสแต็กในที่สุด

ค่าคงที่และตัวแปร

อันนี้ง่าย ค่าคงที่ได้รับการจัดการในโค้ดเล็กๆ ที่แยกจากกัน และพร้อมใช้งานในแอปพลิเคชันในอนาคตผ่านที่อยู่แบบคงที่ ตัวแปรคืออาร์เรย์ของพอยน์เตอร์ในขนาดที่กำหนด การเข้าถึงเซลล์จะดำเนินการโดยดัชนี - เช่น ที่อยู่แบบคงที่ คุณสามารถผลักตัวแปรไปที่ด้านบนของสแต็กหรืออ่านจากตรงนั้นได้ จริงๆแล้วเพราะว่า แม้ว่าตัวแปรของเราจะจัดเก็บพอยน์เตอร์เป็นค่าในหน่วยความจำ VM เป็นหลัก แต่ภาษาก็ถูกครอบงำโดยการทำงานกับพอยน์เตอร์โดยนัย

คนเก็บขยะ

ใน VM ของฉันมันเป็นแบบกึ่งอัตโนมัติ เหล่านั้น. นักพัฒนาเองตัดสินใจว่าเมื่อใดจะโทรหาคนเก็บขยะ มันใช้งานไม่ได้กับตัวนับพอยน์เตอร์ทั่วไป เช่นเดียวกับใน Python, Perl, Ruby, Lua ฯลฯ ดำเนินการผ่านระบบมาร์กเกอร์ เหล่านั้น. เมื่อตัวแปรตั้งใจที่จะกำหนดค่าชั่วคราว ตัวชี้ไปยังค่านี้จะถูกเพิ่มลงในสแต็กของตัวรวบรวมขยะ ในอนาคตนักสะสมจะวิ่งผ่านรายการพอยน์เตอร์ที่เตรียมไว้อย่างรวดเร็ว

การจัดการลอง/จับ/บล็อกในที่สุด

เช่นเดียวกับภาษาสมัยใหม่ การจัดการข้อยกเว้นถือเป็นองค์ประกอบที่สำคัญ แกน VM ถูกรวมไว้ในบล็อก try..catch ซึ่งสามารถกลับไปใช้โค้ดได้หลังจากตรวจพบข้อยกเว้นโดยการพุชข้อมูลบางอย่างเกี่ยวกับมันลงบนสแต็ก ในโค้ดแอปพลิเคชัน คุณสามารถกำหนดบล็อก try/catch/finally ของโค้ด โดยระบุจุดเริ่มต้นที่ catch (ตัวจัดการข้อยกเว้น) และสุดท้าย/สิ้นสุด (จุดสิ้นสุดของบล็อก)

มัลติเธรด

ได้รับการรองรับในระดับ VM ใช้งานง่ายและสะดวก มันทำงานโดยไม่มีระบบขัดจังหวะ ดังนั้นโค้ดควรดำเนินการในหลายเธรดเร็วขึ้นหลายเท่าตามลำดับ

ไลบรารีภายนอกสำหรับ VM

ไม่มีทางทำได้หากไม่มีสิ่งนี้ VM รองรับการนำเข้า เช่นเดียวกับการใช้งานในภาษาอื่นๆ คุณสามารถเขียนโค้ดบางส่วนใน Mash และเขียนโค้ดบางส่วนในภาษาท้องถิ่น จากนั้นจึงเชื่อมโยงเป็นโค้ดเดียว

เครื่องมือแปลจากภาษา Mash ระดับสูงเป็น bytecode สำหรับ VM

ภาษาระดับกลาง

หากต้องการเขียนนักแปลจากภาษาที่ซับซ้อนเป็นโค้ด VM อย่างรวดเร็ว ฉันจึงพัฒนาภาษาระดับกลางก่อน ผลลัพธ์ที่ได้คือภาพที่น่าสยดสยองเหมือนการประกอบซึ่งไม่มีประเด็นใดเป็นพิเศษในการพิจารณาที่นี่ ฉันจะบอกว่าในระดับนี้นักแปลจะประมวลผลค่าคงที่และตัวแปรส่วนใหญ่ คำนวณที่อยู่คงที่และที่อยู่ของจุดเริ่มต้น

สถาปัตยกรรมนักแปล

ฉันไม่ได้เลือกสถาปัตยกรรมที่ดีที่สุดสำหรับการนำไปปฏิบัติ นักแปลไม่ได้สร้างแผนผังโค้ดเหมือนที่นักแปลคนอื่นๆ ทำ เขามองไปที่จุดเริ่มต้นของโครงสร้าง เหล่านั้น. หากส่วนของโค้ดที่ถูกแยกวิเคราะห์ดูเหมือน “ while <condition>:” แสดงว่านี่คือโครงสร้าง while loop และจำเป็นต้องได้รับการประมวลผลเป็นโครงสร้าง while 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 <name>():... end, func <name>():... end
  • ป้ายกำกับ & ไปที่: <name>:, ข้าม <name>
  • การแจงนับและอาร์เรย์คงที่

ตัวแปร

นักแปลสามารถระบุได้โดยอัตโนมัติ หรือหากนักพัฒนาเขียน var ก่อนที่จะกำหนด

ตัวอย่างโค้ด:

a ?= 10
b ?= a + 20

var a = 10, b = a + 20

รองรับตัวแปรโกลบอลและโลคัล

OOP

เรามาถึงหัวข้อที่อร่อยที่สุดแล้ว 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

จะส่งออก: จริง, จริง

เกี่ยวกับตัวดำเนินการมอบหมายและตัวชี้ที่ชัดเจน

ตัวดำเนินการ ?= ใช้เพื่อกำหนดตัวแปรตัวชี้ให้กับค่าในหน่วยความจำ
ตัวดำเนินการ = เปลี่ยนค่าในหน่วยความจำโดยใช้ตัวชี้จากตัวแปร
และตอนนี้เล็กน้อยเกี่ยวกับตัวชี้ที่ชัดเจน ฉันเพิ่มพวกเขาเข้าไปในภาษาเพื่อให้มีอยู่
@<variable> — นำตัวชี้ที่ชัดเจนไปยังตัวแปร
?<variable> — รับตัวแปรตามตัวชี้
@= — กำหนดค่าให้กับตัวแปรโดยใช้พอยน์เตอร์ที่ชัดเจน

รหัสตัวอย่าง:

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

ลอง..[จับ..][สุดท้าย..]จบ

รหัสตัวอย่าง:

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 ดังนั้นในแง่ของประสิทธิภาพ ปัจจุบันสามารถแข่งขันกับ Python ได้เท่านั้น ฉันหวังว่าฉันจะสามารถใช้การคอมไพล์ JIT โดยใช้ GraalVM หรือ LLVM ได้

ที่เก็บ

คุณสามารถเล่นกับการพัฒนาและติดตามโครงการได้ด้วยตัวเอง

เว็บไซต์
พื้นที่เก็บข้อมูลบน GitHub

ขอบคุณที่อ่านจนจบถ้าคุณทำ

ที่มา: will.com

เพิ่มความคิดเห็น