ภาษา BASIC

ภาษาถิ่น BASIC แบบคลาสสิกของ aukimi: ชนิดข้อมูล โครงสร้าง สองรูปแบบย่อ และกฎที่ดักทุกคนได้ มีคำสั่งอยู่เบื้องหลังถึง 2,237 คำสั่ง

สรุปในประโยคเดียว#

aukimi มาพร้อมภาษาถิ่น BASIC แบบคลาสสิก: ภาษาเต็มรูปแบบ ไม่ใช่ระบบแมโครของเล่น มีชนิดตัวเลขที่กำหนดได้ เรคคอร์ด เมธอด การอ้างอิง ข้อผิดพลาดแบบมีโครงสร้าง โครูทีน และการคำนวณด้วย WebGPU มันอยู่ในโหมดวิวพอร์ต TXT ของ Engine และในมุมมอง BASIC ของตัวแก้ไขสคริปต์

JavaScript ยังคงเป็น API สคริปต์หลักที่เป็นทางการ BASIC คือ ฟรอนต์เอนด์ ทางเลือก: ทั้งสองคอมไพล์ไปสู่ตัวจัดการภายในตัวเดียวกัน ดังนั้นโปรแกรม BASIC และโปรแกรม JavaScript จึงเข้าถึงเอนจินตัวเดียวกันทุกประการ ไม่มีอะไรเป็นชนชั้นรอง และไม่มีอะไรเป็นรันไทม์แยกต่างหาก

กฎที่ดักทุกคนได้#

การเรียกคำสั่งต้องมีวงเล็บเสมอ

mode = ClipSpaceMode()
SpritePosition(player, 320, 180)

นี่ไม่ใช่เรื่องสไตล์ มันคือสิ่งที่ทำให้ตัวคอมไพเลอร์แยกคำสั่งออกจากตัวแปรได้ และ เป็นเหตุผลที่การพิมพ์ผิดจะถูกจับได้ตั้งแต่ตอนคอมไพล์:

คุณเขียนสิ่งที่เคยเกิดขึ้นสิ่งที่เกิดขึ้นตอนนี้
SpritePositon(1, 10, 20)คอมไพล์ผ่านฉลุย สไปรต์ไม่ขยับ แล้วความล้มเหลวก็โผล่มาทีหลังเป็น ... is not definedข้อผิดพลาดตอนคอมไพล์ พร้อมคำแนะนำการสะกดที่ใกล้เคียงที่สุดในแคตตาล็อก
Blorp(1, 2)ถูกส่งต่อตรง ๆ ลงใน JavaScript ที่สร้างขึ้นข้อผิดพลาดตอนคอมไพล์
ceci nest pas du basicถูกปล่อยออกมาเป็นคำสั่งหนึ่งข้อผิดพลาดตอนคอมไพล์

ความผิดพลาดจาก การเขียน เคยแสดงตัวเป็นบั๊กของ เอนจิน เมื่อวัดกับชุดตัวอย่าง สาธิต 84 ชิ้น ตัวป้องกันนี้ไม่พบผลบวกลวงแม้แต่รายการเดียว และพบความล้มเหลวแบบเงียบ จริงสองรายการ คือ SetMusicSystemVolume และ GetLightSensorExists ซึ่งแคตตาล็อก รู้จักเพียงในชื่อ MusicSystemVolume และ LightSensorExists

มีช่องโหว่สองจุดที่เปิดทิ้งไว้โดยตั้งใจ: console.log("x") และ window.x = 1 ยังคงผ่านได้ มีการเขียนตัวป้องกันสำหรับสิ่งเหล่านี้ วัดผลแล้ว และ ถอนออก: BASIC ทำงานภายในบริบทที่มีชื่อต่าง ๆ ที่ตัวคอมไพเลอร์ไม่สามารถระบุได้ทั้งหมด การปฏิเสธ รูทที่ไม่รู้จักจึงปฏิเสธโปรแกรมที่ถูกต้องไปด้วย ผลบวกลวงตรงนี้แย่กว่าช่องโหว่นั้นเสียอีก

ความประหลาดใจที่พบบ่อยที่สุดเพียงหนึ่งเดียว:

Print(...)วาดลงบนหน้าจอ และโปรแกรมจะเรียกมันทุกเฟรม
Console(...)เขียนลงในคอนโซลของตัวแก้ไข และไม่ทำอะไรอื่นเลย
Debug(...)คำสั่งเดียวกันภายใต้ชื่อของ PureBasic เพื่อความคุ้นเคย
ConsoleWarning, ConsoleError, ClearConsoleส่วนที่เหลือของล็อก

Print ในลูปเฟรมจะกลบข้อผิดพลาดจริงในคอนโซลจนมิด นี่คือเหตุผลที่ทั้งสองคำสั่งแยก จากกันแทนที่จะเป็นคำสั่งเดียวพร้อมแฟล็ก

ชนิดข้อมูล#

ชนิดตัวเลขเป็นของจริง และมีพฤติกรรมเหมือนกันทุกประการบนทุกเป้าหมาย ทั้งตัวแก้ไข การส่งออกเป็น HTML และการบิลด์แบบ Bevy/native:

  • จำนวนเต็มแบบมีเครื่องหมายและไม่มีเครื่องหมาย 8, 16, 32 และ 64 บิต พร้อมการ แปลงค่าแบบกำหนดแน่นอน DIV แบบจำนวนเต็ม และการล้นแบบ wrapping
  • float32 และ float64
  • สัญลักษณ์คลาสสิกยังใช้ได้: name$ คือสตริง speed# คือ float

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

โครงสร้าง#

เรคคอร์ด#

TYPE ประกาศเรคคอร์ด มันซ้อนกันได้ตามใจ และมี ความหมายเชิงค่า (value semantics): การกำหนดค่าจะทำสำเนาลึก (deep-copy) รวมถึงอาร์เรย์และคอลเลกชัน ที่ซ้อนอยู่ภายในด้วย

เมธอด#

METHOD ผูกฟังก์ชันเข้ากับอินสแตนซ์ โดยมี SELF อยู่ภายใน มี New สำหรับสร้าง และ Delete สำหรับทำลาย การทำสำเนาลึกยังคงปลอดภัยกับเมธอด

การอ้างอิง#

REF และ BYREF คือสิ่งเดียวกันภายใต้สองชื่อสะกด และทั้งคู่ให้พารามิเตอร์แบบ อ่าน/เขียนสด สำหรับค่าสเกลาร์ เรคคอร์ด ฟิลด์เดียว อาร์เรย์ ตัวชี้ตำแหน่งของลิสต์ หรือรายการในแมป การอ้างอิงส่งต่อผ่านการเรียกฟังก์ชัน และแปลงเป็นชนิดความกว้างคงที่ ที่ขอบเขต

แฮนเดิล#

@target รับการอ้างอิงชั้นหนึ่ง HANDLE และ POINTER คือรูปแบบที่มีชนิดกำกับ พร้อมการอ่าน เขียน ตรวจสอบความถูกต้อง และปล่อยที่มีการตรวจสอบ และแฮนเดิลไปยัง เรคคอร์ดที่ถูกลบไปแล้วจะถูกทำให้ใช้งานไม่ได้ แทนที่จะปล่อยให้ค้างเป็น dangling

การควบคุมและความปลอดภัย#

ข้อผิดพลาดแบบมีโครงสร้าง: TRY / CATCH / FINALLY ซ้อนกันได้ พร้อม THROW, RETHROW ที่มีการป้องกัน และการจับได้ทั้งแบบมีโครงสร้างและแบบสตริง

การคอมไพล์แบบมีเงื่อนไข: #IF / #ELSEIF / #ELSE / #ENDIF ซ้อนกันได้ พร้อมแฟล็กคุณสมบัติและโปรไฟล์ที่ชัดเจนสำหรับตัวแก้ไข, HTML, Bevy, native/wasm และ debug/release มันถูกแก้ไข ก่อน การรวมไฟล์ (include) เสมอ บนทุกเป้าหมาย

ฟังก์ชันที่ครบครัน: ค่าเริ่มต้น OPTIONAL, PARAMARRAY ตัวสุดท้าย (หรือ ...), การโอเวอร์โหลดจำนวนอาร์กิวเมนต์ที่ปลอดภัย การแปลงชนิดค่าที่คืนกลับ และ การคืนค่าเรคคอร์ดกับอาร์เรย์ที่ถูกโคลน

โปรแกรมหลายไฟล์#

#INCLUDE, #INSERT และ IncludeFile ทำให้โปรแกรมหนึ่งแยกเป็นหลายไฟล์ ตัวแก้ไข สร้างและนำเข้าโมดูลของโปรเจกต์ เติมพาธ include ให้อัตโนมัติ เก็บกราฟของการพึ่งพา และทุกเป้าหมายรันไทม์รองรับสิ่งนี้อยู่แล้ว

โครูทีน และสิ่งที่ถูกตัดออกโดยตั้งใจ#

งานแบบ cooperative ใช้ label เป็นฐาน มีแฮนเดิลที่มีชนิดกำกับ สถานะผลลัพธ์และ ข้อผิดพลาด การยกเลิก ที่จุด yield และการรอทั้งงานเดี่ยวหรือกลุ่มงาน มันแชร์ ตัวจัดตารางเวลาของเกมบนทุกเป้าหมาย

เธรดที่แชร์หน่วยความจำถูกตัดออกโดยตั้งใจ ไม่ใช่ "ยังไม่ทำ" แต่ตัดออกจริง ๆ ตัวจัดตารางเวลาแบบ cooperative ที่คุณให้เหตุผลได้ ดีกว่าการแย่งข้อมูล (data race) ที่คุณทำซ้ำไม่ได้

การคำนวณด้วย WebGPU#

storage buffer แบบ f32 / i32 / u32 ที่มีชนิดกำกับ WGSL pipeline แบบดิบ การส่งงานตามลำดับเฟรม และการอ่านค่ากลับจาก memblock คือการคำนวณด้วย GPU จริง จาก BASIC

⚠️ ใช้ได้เฉพาะ Engine เท่านั้น เป้าหมาย HTML จะแจ้งข้อผิดพลาดเรื่องขีดความสามารถ ที่ชัดเจน และเป้าหมาย Bevy จะรายงานความเข้ากันไม่ได้ ก่อน การบิลด์ แทนที่จะ สร้างไบนารีที่ล้มเหลวตอนส่งงานครั้งแรก

สองรูปแบบย่อสำหรับปัญหาเดียวกัน#

597 คำสั่งจากทั้งหมดมีชื่อในรูปแบบ Set/Get<Object><Property>: Sprite 66, Tween 58, Text 32, Edit 29 ชื่อของอ็อบเจ็กต์อยู่ในชื่อฟังก์ชัน และ id ของมันคือ อาร์กิวเมนต์แรกของทุกบรรทัด รูปแบบย่อทั้งสองด้านล่างช่วยลดความซ้ำซ้อนนี้ และไม่มี อันไหนเป็น API ใหม่: ตัวคอมไพเลอร์สร้างชื่อคำสั่งจริงขึ้นใหม่แล้วค้นหาในแคตตาล็อก

WITH <family>, ภายในบล็อก#

WITH SPRITE s
  \position 100, 200      ` SpritePosition(s, 100, 200)
  \size 64, 64            ` SpriteSize(s, 64, 64)
  x# = \x                 ` SpriteX(s)
ENDWITH

เมื่ออยู่ต้นคำสั่ง \prop จะ เขียน ที่อื่นทุกที่มันจะ อ่าน ตำแหน่งเป็นตัวตัดสิน จึงไม่มีสัญกรณ์ที่สองต้องจำ WITH hero โทเค็นเดียว ยังคงเป็นบล็อกเรคคอร์ด WITH SPRITE s เลือกกลุ่มแคตตาล็อก

s.position(...), ที่อื่นทั้งหมด#

s = CreateSprite(img)     ` the family is DEDUCED from the creation command
s.position(100, 200)      ` SpritePosition(s, 100, 200)
s.visible = 1             ` SpriteVisible(s, 1)
x# = s.x                  ` SpriteX(s)

กลุ่มมาจาก Create<F> / Load<F> / Clone<F> หรือจาก s AS Sprite ที่ประกาศ ชัดเจน เมื่อ id มาจากที่อื่น มีเพียงตัวแปรที่ รู้กลุ่มแน่นอน เท่านั้นที่จะถูกเขียนใหม่: เรคคอร์ดยังคงใช้ฟิลด์ของตัวเอง ลิสต์ยังคงใช้เมธอดของตัวเอง SELF.field ไม่ถูกแตะต้อง

⚠️ พารามิเตอร์ที่มีชนิดกำกับจะไม่ถูกจดจำ FUNCTION move(s AS Sprite) ไม่ได้ ให้กลุ่มกับ s การตรวจสอบนี้อ่านเฉพาะการประกาศและการกำหนดค่าระดับ statement เท่านั้น บอกไว้ตรงนี้เพราะมันเป็นข้อจำกัดจริง ไม่ใช่บั๊กที่คุณควรไล่ตามหา

⚠️ แคตตาล็อกตัดคำกริยาออกในคุณสมบัติ (properties) แต่คงไว้ในวงจรชีวิต (lifecycle) SpritePosition เขียนค่า SpriteX อ่านค่า และไม่มีตัวไหนมี Set หรือ Get นำหน้า: มีเพียง 4 คำสั่ง ในแคตตาล็อกทั้งหมดที่ขึ้นต้นด้วย Set และ ไม่มีสักคำสั่งเดียว ที่ขึ้นต้นด้วย Get คำกริยายังอยู่ในจุดที่มีสิ่งใดสิ่งหนึ่งเกิดขึ้น หรือสิ้นสุดลง: CreateSprite, LoadImage, DeleteSprite

นั่นควรค่าแก่การรู้ไว้ เพราะการสะกดผิดคือข้อผิดพลาดตอนคอมไพล์ และ Set… คือ การสะกดผิดที่คนมักจะมาด้วย แคตตาล็อกคือแหล่งอ้างอิงที่แท้จริง และ หน้าเอกสารอ้างอิง ถูกสร้างขึ้นจากมันโดยตรง

การแทรกค่าในสตริง (string interpolation) แบบเลือกใช้เอง#

Print($"score: {points}")        ` "score: " + Str(points)

{{ และ }} ใช้เขียนวงเล็บปีกกาตัวจริง

⚠️ คำนำหน้า $ เป็นสิ่งจำเป็น ไม่ใช่ของประดับ การแทรกค่าในทุกลิเทอรัลเคยทำให้ JSON ที่เขียนด้วยมือพัง ซึ่ง world API ใช้เป็นประจำ:

CreateTerrain("island", "{\"preset\":\"island\",\"size\":256}")

ตรงนี้วงเล็บปีกกาคือข้อมูล ไม่ใช่ช่องว่างสำหรับแทรกค่า สตริงที่ไม่มีคำนำหน้าจะไม่ถูก แตะต้องไม่ว่าจะมีอะไรอยู่ในนั้น

การตั้งชื่อคือการแสดงตัว#

โปรแกรมคลาสสิกสร้างอ็อบเจ็กต์เป็นร้อย ๆ มีเพียงตัวที่ถูกตั้งชื่อเท่านั้นที่จะเข้าสู่ Scene Graph SpriteName(id, "player") และคำสั่งพี่น้องของมันคือสิ่งที่นำอ็อบเจ็กต์ เข้าไปในโครงสร้างต้นไม้ที่ส่วนที่เหลือของ Engine มองเห็นได้ LightName และ ParticlesName ทำแบบเดียวกันสำหรับไฟและตัวปล่อยอนุภาค

บทเรียน ตั้งชื่อสิ่งที่โปรแกรมของคุณสร้างขึ้น พาไปดูเรื่องนี้: ฉากเดียวกัน ครั้งหนึ่งมีโครงสร้างต้นไม้ว่างเปล่า และอีกครั้งมีสามรายการอยู่ในนั้น

แคตตาล็อกคำสั่ง#

2,237 คำสั่ง, 2,550 โอเวอร์โหลดสาธารณะ, 43 หมวดหมู่ หมวดที่ใหญ่ที่สุด:

3D286Studio106File61
Core200Input95Memblock43
Tweening149Input-Raw943D Particles41
3D Physics144Multiplayer91World40
Sprite135Text68Particles38
Platform1112D Physics66Sound37

บวกกับ JSON (36), Image (35), Skeleton (34), XML (34), Music (29), Preference (27), Extras (21), HTTP (21), String (21), Math (19), Regular Expression (19), Video (18), Dictionary (17), Benchmarking (15), Compute (14), Date (13), Maths (12), Time (12), StringBuilder (9), Compression (7), Font (8), Error (4), Array (3), Cipher (2), Sort (2)

Studio และ Compute เป็นสิ่งที่ aukimi เพิ่มเข้ามาเองในภาษาถิ่นคลาสสิก คือ คำสั่งคอนโซล การตั้งชื่ออ็อบเจ็กต์ และ pipeline ของ WebGPU

หน้าเอกสารอ้างอิงคำสั่งฉบับสมบูรณ์ รวบรวม ทั้งหมด พร้อมทุกพารามิเตอร์และทุกโอเวอร์โหลด มีระบบค้นหาของตัวเอง สร้างขึ้นจาก แคตตาล็อกเดียวกับที่หน้านี้นับ จึงไม่มีทางล้าหลังกว่ากัน แผงคำสั่ง API ในตัวแก้ไขเอง ก็อ่านแคตตาล็อกนั้นเช่นกัน

หน้านี้บันทึกเรื่อง ภาษา หน้านั้นบันทึกเรื่อง คำสั่ง

ข้อจำกัดและปัญหาที่พบบ่อย#

  • วงเล็บไม่ใช่ตัวเลือกในคำสั่ง รูปแบบ PureBasic แบบเปล่า ๆ (Debug "x") ถูกปฏิเสธ เพราะมันเปิดความคลุมเครือระหว่างตัวแปรกับคำสั่งขึ้นมาใหม่ ซึ่งเป็นสิ่งที่ ตัวป้องกันตอนคอมไพล์ต้องอาศัยอยู่
  • ฟังก์ชันของผู้ใช้ชนะคำสั่งในแคตตาล็อกที่ชื่อเดียวกัน และตัวแปรอาจใช้ชื่อร่วม กับคำสั่งได้ในบริบทของการประกาศชนิด ด้านซ้ายของการกำหนดค่า หรือเป็นรูทของพาธ
  • ตัวแปรและฟังก์ชันของผู้ใช้ไม่สนใจตัวพิมพ์เล็กใหญ่ และพื้นที่ชื่อของมันแยกจาก แคตตาล็อกอย่างแท้จริง
  • Print ไม่ใช่ล็อก ดูด้านบน มันคือความผิดพลาดที่ทำให้คอนโซลเต็มไปด้วยข้อความ
  • Compute ไม่รองรับการส่งออก เขียนเส้นทางสำรองไว้ก่อนที่คุณจะพึ่งพามัน