कई वर्षों तक मैंने अपनी प्रोग्रामिंग भाषा को विकसित करने में अपना हाथ आजमाया। मैं अपनी राय में सबसे सरल, पूरी तरह कार्यात्मक और सुविधाजनक भाषा बनाना चाहता था।
इस लेख में मैं अपने काम के मुख्य चरणों को उजागर करना चाहता हूं और भाषा की निर्मित अवधारणा और इसके पहले कार्यान्वयन के बारे में बताना चाहता हूं, जिस पर मैं वर्तमान में काम कर रहा हूं।
मैं पहले से कहता हूं कि मैंने पूरी परियोजना को फ्री पास्कल में लिखा था, क्योंकि इस पर कार्यक्रमों को बड़ी संख्या में प्लेटफार्मों के लिए इकट्ठा किया जा सकता है, और कंपाइलर स्वयं बहुत अनुकूलित बायनेरिज़ का उत्पादन करता है (मैं ओ 2 ध्वज के साथ परियोजना के सभी घटकों को इकट्ठा करता हूं)।
भाषा रनटाइम
पहली बात करने के लिए एक आभासी मशीन है जिसे मुझे अपनी भाषा में भविष्य के अनुप्रयोगों को चलाने के लिए लिखना था। मैंने एक स्टैक आर्किटेक्चर को लागू करने का फैसला किया, शायद, क्योंकि यह सबसे आसान तरीका था। मुझे रूसी में ऐसा करने के लिए एक भी सामान्य लेख नहीं मिला, इसलिए अंग्रेजी भाषा की सामग्री को पढ़ने के बाद, मैं अपनी बाइक को डिजाइन करने और लिखने के लिए बैठ गया। इसके अलावा मैं इस मामले में अपने "उन्नत" विचारों और घटनाओं को दे दूंगा।
स्टैक कार्यान्वयन
जाहिर है, वीएम स्टैक के सिर पर है। मेरे कार्यान्वयन में, यह ब्लॉकों में काम करता है। संक्षेप में, यह पॉइंटर्स का एक सरल सरणी है और स्टैक के शीर्ष के सूचकांक को संग्रहीत करने के लिए एक चर है।
जब इसे प्रारंभ किया जाता है, तो 256 तत्वों का एक सरणी बनाया जाता है। यदि अधिक पॉइंटर्स को स्टैक पर फेंक दिया जाता है, तो इसका आकार अगले 256 तत्वों द्वारा बढ़ाया जाता है। तदनुसार, जब स्टैक से आइटम निकालते हैं, तो इसका आकार समायोज्य होता है।
एक वीएम कई स्टैक का उपयोग करता है:
- मुख्य ढेर।
- रिटर्न अंक जमा करने के लिए ढेर।
- कचरे के ढेर का ढेर।
- कोशिश / पकड़ / अंत में ब्लॉक के ढेर हैंडलर।
लगातार और चर
इसके साथ, सब कुछ सरल है। लगातार कोड के एक छोटे टुकड़े में संसाधित होते हैं और भविष्य में स्थैतिक पते पर अनुप्रयोगों में उपलब्ध होते हैं। चर एक निश्चित आकार के संकेत के एक सरणी हैं, इसकी कोशिकाओं तक पहुंच सूचकांक द्वारा की जाती है - अर्थात। स्थिर पता। वेरिएबल्स को स्टैक के शीर्ष पर रखा जा सकता है या वहां से पढ़ा जा सकता है। दरअसल, क्योंकि हमारे चर अनिवार्य रूप से वीएम की स्मृति में मानों को स्टोर करते हैं, फिर अंतर्निहित संकेत के साथ काम करते हैं जो भाषा में प्रबल होते हैं।
कचरा इकट्ठा करने वाला
मेरे वीएम में यह अर्ध-स्वचालित है। यानी कचरा उठाने वाले को बुलाने का फैसला डेवलपर करता है। यह सामान्य पॉइंटर काउंटर के अनुसार काम नहीं करता है, जैसा कि एक ही पायथन, पर्ल, रूबी, लुआ, आदि में होता है। यह एक मार्कर प्रणाली के माध्यम से कार्यान्वित किया जाता है। यानी जब यह समझा जाता है कि एक चर को एक अस्थायी मान सौंपा गया है, तो इस मान के लिए एक सूचक कचरा संग्राहक स्टैक में जोड़ा जाता है। भविष्य में, कलेक्टर जल्दी से पहले से तैयार बिंदुओं की सूची से गुजरता है।
कोशिश करना / पकड़ना / अंत में ब्लॉक करना
किसी भी आधुनिक भाषा में, अपवाद हैंडलिंग इसका एक महत्वपूर्ण घटक है। VM का कर्नेल एक try..catch ब्लॉक में लपेटा जाता है, जो स्टैक पर इसके बारे में थोड़ी जानकारी डालकर अपवाद को पकड़ने के बाद कोड के निष्पादन में वापस आ सकता है। एप्लिकेशन कोड में, आप कोड के प्रयास / कैच / अंत में ब्लॉक को निर्दिष्ट कर सकते हैं, प्रवेश बिंदुओं को पकड़ने के लिए (अपवाद हैंडलर) और अंत में / अंत (ब्लॉक का अंत) को दर्शाते हैं।
बहु सूत्रण
यह वीएम स्तर पर समर्थित है। यह उपयोग करने के लिए सरल और सुविधाजनक है। यह एक रुकावट प्रणाली के बिना काम करता है, इसलिए कोड को कई थ्रेड में क्रमशः कई बार तेज चलना चाहिए।
वीएम के लिए बाहरी पुस्तकालय
इसके बिना करने का कोई उपाय नहीं है। वीएम आयात का समर्थन करता है, ठीक उसी तरह जैसे अन्य भाषाओं में लागू किया जाता है। आप मैश में कोड का हिस्सा और देशी भाषाओं में कोड का हिस्सा लिख सकते हैं, फिर उन्हें एक साथ जोड़ सकते हैं।
वीएम के लिए उच्च-स्तरीय भाषा मैश से बाईटेकोड तक अनुवादक
मध्यवर्ती भाषा
एक जटिल भाषा से वीएम कोड में एक अनुवादक को जल्दी से लिखने के लिए, मैंने पहली बार एक मध्यवर्ती भाषा विकसित की। इसने एक असेंबलर जैसी डरावनी दृष्टि दिखाई, जिसके बारे में यहाँ विचार करने के लिए कोई विशेष समझ नहीं है। मैं केवल यह कह सकता हूं कि इस स्तर पर अनुवादक अधिकांश स्थिरांक, चर को संसाधित करता है, उनके स्थिर पते और प्रवेश बिंदुओं के पते की गणना करता है।
अनुवादक आर्किटेक्चर
मैंने कार्यान्वयन के लिए सर्वश्रेष्ठ वास्तुकला नहीं चुना। अनुवादक एक कोड ट्री का निर्माण नहीं करता है, जैसा कि अन्य अनुवादकों के साथ होता है। वह निर्माण की शुरुआत को देखता है। यानी यदि कोड का पार्स किया हुआ टुकड़ा "जबकि <स्थिति>:" जैसा दिखता है, तो यह स्पष्ट है कि यह लूप का निर्माण है और आपको लूप के निर्माण के दौरान इसे संसाधित करने की आवश्यकता है। एक जटिल स्विच-केस जैसा कुछ।
ऐसे स्थापत्य समाधान के लिए धन्यवाद, अनुवादक बहुत तेज नहीं था। हालांकि, इसके शोधन की सादगी में काफी वृद्धि हुई है। मैंने अपने कॉफी को ठंडा करने की तुलना में तेजी से आवश्यक डिजाइनों को जोड़ा। OOP के लिए पूर्ण समर्थन एक सप्ताह से भी कम समय में लागू किया गया था।
कोड अनुकूलन
यहां, निश्चित रूप से, इसे बेहतर तरीके से महसूस किया जा सकता है (और इसे महसूस किया जाएगा, लेकिन बाद में, जैसे ही हाथ पहुंचते हैं)। अब तक, ऑप्टिमाइज़र केवल यह जानता है कि असेंबली कोड, असेंबली और आयात को विधानसभा से कैसे काट दिया जाए। इसके अलावा, एक ही मूल्य वाले कई स्थिरांक एक से बदल दिए जाते हैं। वह सब है।
मैश भाषा
भाषा की मूल अवधारणा
मुख्य विचार सबसे कार्यात्मक और सरल भाषा विकसित करना था। मेरा मानना है कि विकास धमाके के साथ अपने काम को अंजाम देता है।
कोड ब्लॉक, प्रक्रियाएं और कार्य
भाषा में सभी निर्माण एक बृहदान्त्र के साथ खोले जाते हैं
: और
अंत ऑपरेटर के साथ बंद कर दिया जाता है।
प्रक्रियाओं और कार्यों को क्रमशः खरीद और कवक के रूप में घोषित किया जाता है। तर्क कोष्ठक में सूचीबद्ध हैं। ज्यादातर अन्य भाषाओं की तरह।
रिटर्न स्टेटमेंट किसी फ़ंक्शन से मान लौटा सकता है,
ब्रेक स्टेटमेंट आपको एक प्रक्रिया / फ़ंक्शन (यदि यह छोरों के बाहर है) से बाहर निकलने की अनुमति देता है।
कोड उदाहरण:
...
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, , .