BASIC ভাষা

aukimi-র ক্লাসিক BASIC ডায়ালেক্ট: টাইপ, স্ট্রাকচার, দুটো শর্টহ্যান্ড, এবং যে নিয়মগুলোতে সবাই আটকায়। এর পেছনে আছে 2 237টি কমান্ড।

একনজরে#

Aukimi একটি classic BASIC ডায়ালেক্ট নিয়ে আসে: একটি সম্পূর্ণ ভাষা, কোনো খেলনা ম্যাক্রো সিস্টেম নয়, টাইপড numerics, record, method, reference, structured error, coroutine এবং WebGPU compute সহ। এটি থাকে Engine-এর TXT ভিউপোর্ট মোডে এবং স্ক্রিপ্ট এডিটরের BASIC ভিউতে।

JavaScript-ই canonical স্ক্রিপ্টিং API থেকে যায়। BASIC একটি ঐচ্ছিক frontend: দুটোই একই প্রাইভেট dispatcher-এ কম্পাইল হয়, তাই একটি BASIC প্রোগ্রাম এবং একটি JavaScript প্রোগ্রাম একই ইঞ্জিনে গিয়ে পৌঁছায়। কোনোটাই দ্বিতীয় শ্রেণির নয়, আর কোনোটাই আলাদা runtime নয়।

যে নিয়মে সবাই আটকায়#

একটি কমান্ড কল সবসময় প্যারেনথিসিস বহন করে।

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

এটা স্টাইলের বিষয় নয়। এটাই কম্পাইলারকে একটি কমান্ড আর একটি ভেরিয়েবলের মধ্যে পার্থক্য করতে দেয়, আর এই কারণেই একটি বানান ভুল এখন কম্পাইল টাইমে ধরা পড়ে:

আপনি যা লেখেনআগে যা হতোএখন যা হয়
SpritePositon(1, 10, 20)পরিষ্কারভাবে কম্পাইল হতো, sprite নড়ত না, আর ব্যর্থতাটা পরে ... is not defined হিসেবে দেখা দিতকম্পাইল এরর, catalog-এ সবচেয়ে কাছাকাছি বানানটা প্রস্তাব করে
Blorp(1, 2)হুবহু তৈরি হওয়া JavaScript-এ পাঠিয়ে দেওয়া হতোকম্পাইল এরর
ceci nest pas du basicএকটি স্টেটমেন্ট হিসেবে emit হতোকম্পাইল এরর

একটি লেখার ভুল আগে নিজেকে একটি ইঞ্জিন bug হিসেবে দেখাত। 84-demo কর্পাসের বিপরীতে মেপে, guard-টি শূন্য false positive দিয়েছে এবং দুটো প্রকৃত নীরব ব্যর্থতা খুঁজে পেয়েছে, SetMusicSystemVolume এবং GetLightSensorExists, যা catalogue শুধু MusicSystemVolume এবং LightSensorExists হিসেবেই চেনে।

দুটো ফাঁক ইচ্ছাকৃতভাবে খোলা রাখা হয়েছে: console.log("x") এবং window.x = 1 তবুও পাস করে। এদের জন্য একটি guard লেখা হয়েছিল, মাপা হয়েছিল এবং প্রত্যাহার করা হয়েছে: BASIC এমন একটি কনটেক্সটের ভেতরে চলে যেটা এমন নাম সরবরাহ করে যা কম্পাইলার গণনা করতে পারে না, তাই একটি অজানা root প্রত্যাখ্যান করলে বৈধ প্রোগ্রামও বাতিল হয়ে যেত। সেখানে একটি false positive ফাঁকটার চেয়েও খারাপ।

সবচেয়ে সাধারণ বিস্ময়:

Print(...)স্ক্রিনে আঁকে, আর একটি প্রোগ্রাম এটাকে প্রতি ফ্রেমে কল করে
Console(...)এডিটরের কনসোলে লেখে, আর কিছুই করে না
Debug(...)মাংসপেশির স্মৃতির জন্য, PureBasic-এর নামে একই কমান্ড
ConsoleWarning, ConsoleError, ClearConsoleলগের বাকিটা

Print একটি ফ্রেম লুপে থাকলে কনসোলে যেকোনো প্রকৃত এরর ডুবিয়ে দেবে, এই কারণেই একটি flag-সহ একটির বদলে এই দুটো আলাদা কমান্ড।

টাইপ#

নিউমেরিক টাইপগুলো বাস্তব, আর এগুলো প্রতিটি টার্গেটে একইরকম আচরণ করে, এডিটর, HTML এক্সপোর্ট এবং Bevy/নেটিভ বিল্ডে:

  • সাইনড ও আনসাইনড 8, 16, 32 এবং 64-bit ইন্টিজার, deterministic coercion, integer DIV, এবং wrapping overflow সহ।
  • float32 এবং float64।
  • ক্লাসিক sigil-গুলো এখনও কাজ করে: name$ একটি string, speed# একটি float।

OPTION EXPLICIT opt-in, আর ক্লাসিক implicit ভেরিয়েবল ডিফল্টই থেকে যায় তাই ইমপোর্ট করা প্রোগ্রামও চলতে থাকে। এটি চালু করা সেই দুটো ফিচারের পূর্বশর্ত যা এটি ছাড়া নিরাপদ হতে পারে না, numeric typing এবং reference, কারণ একটি ভুল বানানে অ্যাসাইনমেন্ট নীরবে একটি ভেরিয়েবল তৈরি করলে দুটোই ব্যর্থ হয়ে যায়।

কাঠামো#

রেকর্ড#

TYPE একটি রেকর্ড ঘোষণা করে। এগুলো ইচ্ছামতো নেস্ট করা যায়, আর এদের value semantics আছে: একটি অ্যাসাইন করলে সেটি ডিপ-কপি হয়, নেস্টেড অ্যারে ও কালেকশনসহ।

মেথড#

METHOD একটি ফাংশনকে একটি instance-এর সাথে বাইন্ড করে, তার ভেতরে SELF সহ, নির্মাণের জন্য New এবং ধ্বংসের জন্য Delete। ডিপ কপিগুলো মেথড-নিরাপদ থেকে যায়।

রেফারেন্স#

REF এবং BYREF দুটো বানানে একই জিনিস, আর দুটোই একটি লাইভ read/write প্যারামিটার দেয়, একটি scalar, একটি record, একটি একক field, একটি array, একটি list cursor বা একটি map entry-র জন্য। রেফারেন্স কলের মধ্য দিয়ে এগিয়ে যায়, এবং সীমানায় fixed-width টাইপে coerce হয়।

হ্যান্ডেল#

@target একটি first-class রেফারেন্স নেয়। HANDLE এবং POINTER টাইপড রূপ, চেক করা read, write, validity ও release সহ, আর একটি মোছা রেকর্ডের হ্যান্ডেল ঝুলিয়ে না রেখে invalidate করা হয়।

নিয়ন্ত্রণ ও নিরাপত্তা#

Structured errors: TRY / CATCH / FINALLY, নেস্টেড, THROW, সুরক্ষিত RETHROW, এবং structured ও string উভয় catch সহ।

Conditional compilation: #IF / #ELSEIF / #ELSE / #ENDIF, নেস্টেড, feature flag এবং এডিটর, HTML, Bevy, native/wasm ও debug/release-এর জন্য explicit প্রোফাইলসহ। এটি প্রতিটি টার্গেটে include-এর আগে সমাধান করা হয়।

Rich functions: ডিফল্ট মান, OPTIONAL, একটি শেষ PARAMARRAY (বা ...), নিরাপদ arity overload, টাইপড রিটার্ন coercion, এবং ক্লোন করা record ও array রিটার্ন।

মাল্টি-ফাইল প্রোগ্রাম#

#INCLUDE, #INSERT এবং IncludeFile একটি প্রোগ্রামকে কয়েকটা ফাইলে ভাগ করে। এডিটর প্রজেক্ট মডিউল তৈরি ও ইমপোর্ট করে, include পাথ কমপ্লিট করে, dependency গ্রাফ ধরে রাখে, আর প্রতিটি রানটাইম টার্গেট এর জন্য তারযুক্ত।

Coroutine, এবং যা ইচ্ছাকৃতভাবে বাদ দেওয়া হয়েছে#

Cooperative task লেবেল-ভিত্তিক, টাইপড হ্যান্ডেল, একটি result ও error অবস্থা, yield-এ cancellation, এবং একটি task বা একটি গ্রুপের উপর await সহ। প্রতিটি টার্গেটে এগুলো গেম শিডিউলার শেয়ার করে।

শেয়ার্ড মেমোরিসহ থ্রেড ইচ্ছাকৃতভাবে বাদ দেওয়া হয়েছে। "এখনও নয়" নয়। যে cooperative scheduler নিয়ে আপনি যুক্তি করতে পারেন সেটা এমন একটা data race-এর চেয়ে ভালো যা আপনি পুনরুৎপাদন করতে পারবেন না।

WebGPU compute#

টাইপড f32 / i32 / u32 storage buffer, কাঁচা WGSL pipeline, frame-ordered dispatch এবং memblock readback, BASIC থেকে সত্যিকারের GPU compute।

⚠️ শুধু Engine। HTML টার্গেট একটি স্পষ্ট capability এরর দেয়, আর Bevy টার্গেট বিল্ডের আগেই অসামঞ্জস্যতা জানায়, প্রথম dispatch-এ ব্যর্থ হওয়া একটি বাইনারি তৈরি না করে।

একই সমস্যার দুটো শর্টহ্যান্ড#

কমান্ডগুলোর মধ্যে 597টির নাম Set/Get<Object><Property>: Sprite 66, Tween 58, Text 32, Edit 29। অবজেক্টের নামটা ফাংশনের নামের ভেতরেই থাকে, আর তার id প্রতিটি লাইনের প্রথম আর্গুমেন্ট। নিচের দুটো শর্টহ্যান্ডই সেই পুনরাবৃত্তি সরিয়ে দেয়, আর কোনোটাই নতুন API নয়: কম্পাইলার আসল কমান্ডের নাম পুনর্নির্মাণ করে এবং catalogue-এ সেটা খুঁজে বের করে।

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 একটা catalogue পরিবার বেছে নেয়।

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> থেকে, অথবা id অন্য কোথাও থেকে আসলে একটি explicit s AS Sprite থেকে। শুধু সেসব ভেরিয়েবল আবার লেখা হয় যাদের পরিবার জানা: record তাদের field ধরে রাখে, list তাদের method ধরে রাখে, SELF.field অস্পৃষ্ট থাকে।

⚠️ একটি typed প্যারামিটার চেনা যায় না। FUNCTION move(s AS Sprite) s-কে কোনো পরিবার দেয় না, pass-টি শুধু statement-স্তরের ঘোষণা ও assignment পড়ে। এখানে বলা হলো কারণ এটা একটা প্রকৃত সীমাবদ্ধতা, খুঁজে বের করার মতো কোনো bug নয়।

⚠️ catalogue প্রোপার্টিতে ক্রিয়াপদ বাদ দেয়, আর lifecycle-এ রাখে। SpritePosition লেখে, SpriteX পড়ে, আর কোনোটাই Set বা Get বহন করে না: পুরো catalogue-এ 4টা কমান্ড Set দিয়ে শুরু, আর একটাও Get দিয়ে শুরু হয় না। ক্রিয়াপদটা টিকে থাকে যেখানে কিছু জন্ম নেয় বা শেষ হয়: CreateSprite, LoadImage, DeleteSprite

এটা জানার মতো, কারণ একটা বানান ভুল একটা কম্পাইল এরর, আর Set… হলো সেই বানান ভুল যা নিয়ে মানুষ আসে। catalogue-ই কর্তৃত্ব, আর রেফারেন্স সেখান থেকেই তৈরি।

String interpolation, opt in#

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

{{ এবং }} আক্ষরিক braces লেখে।

⚠️ $ prefix বাধ্যতামূলক, আর এটা কোনো সাজসজ্জা নয়। প্রতিটি লিটারেলকে interpolate করা হাতে-লেখা JSON ভেঙে দিত, যা world API নিয়মিত নেয়:

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

সেখানে braces হলো ডেটা, কোনো ফাঁক নয়। একটি prefix-বিহীন স্ট্রিং যা কিছুই থাকুক না কেন অস্পৃষ্ট রেখে দেওয়া হয়।

নামকরণই দেখানো#

একটি ক্লাসিক প্রোগ্রাম শত শত অবজেক্ট তৈরি করে। শুধু যেগুলোর নাম দেওয়া হয় সেগুলোই Scene Graph-এ ঢোকে, SpriteName(id, "player") এবং তার সহোদররাই একটি অবজেক্টকে সেই ট্রি-তে বসায় যেখানে বাকি Engine সেটা দেখতে পারে। LightName এবং ParticlesName একটি light ও একটি emitter-এর জন্য একই কাজ করে।

টিউটোরিয়াল Name what your program creates এর মধ্য দিয়ে হাঁটিয়ে নিয়ে যায়: একই সিন, একবার ফাঁকা ট্রি নিয়ে আর একবার তার মধ্যে তিনটে এন্ট্রি নিয়ে।

কমান্ড catalogue#

2 237টা কমান্ড, 2 550টা পাবলিক overload, 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-র নিজস্ব সংযোজন, কনসোল কমান্ড, অবজেক্ট নেমিং, এবং WebGPU পাইপলাইন।

সম্পূর্ণ কমান্ড রেফারেন্স এদের সবগুলো তালিকাভুক্ত করে, প্রতিটি প্যারামিটার ও প্রতিটি overload সহ, এবং তার নিজস্ব সার্চ, এই পাতাটি যে catalogue গোনে সেখান থেকেই তৈরি, তাই এটা তার থেকে পিছিয়ে পড়তে পারে না। এডিটরের নিজস্ব API প্যানেলও সেই catalogue পড়ে।

এই পাতাটি ভাষা ডকুমেন্ট করে; সেটা কমান্ড ডকুমেন্ট করে।

সীমাবদ্ধতা ও সাধারণ সমস্যা#

  • একটি কমান্ডে প্যারেনথিসিস ঐচ্ছিক নয়। খালি PureBasic ফর্ম (Debug "x") প্রত্যাখ্যাত হয়, কারণ এটা variable/command অস্পষ্টতা আবার খুলে দেয় যার উপর কম্পাইল-টাইম guard নির্ভর করে।
  • একটি ইউজার ফাংশন একই নামের একটা catalogued কমান্ডের উপর জেতে, আর একটি ভেরিয়েবল একটা টাইপড ঘোষণায়, একটি অ্যাসাইনমেন্টের বামে, বা একটা পাথের root হিসেবে একটি কমান্ডের নাম শেয়ার করতে পারে।
  • ভেরিয়েবল ও ইউজার ফাংশন case-insensitive, আর তাদের symbol স্পেস সত্যিই catalogue-এরটা থেকে আলাদা।
  • Print কোনো লগ নয়। উপরে দেখুন; এটাই সেই ভুল যা একটা কনসোল ভরিয়ে দেয়।
  • Compute এক্সপোর্ট হয় না। এর উপর নির্ভর করার আগে fallback পাথ লিখুন।