अपनी भाषा में एक कंपाइलर लिखना


204

सहज रूप से, ऐसा लगेगा कि भाषा के लिए संकलक Fooको फू में ही नहीं लिखा जा सकता। विशेष रूप से, भाषा के लिए पहला संकलक Fooफू में नहीं लिखा जा सकता है, लेकिन बाद के किसी भी संकलक के लिए लिखा जा सकता है Foo

लेकिन क्या यह वास्तव में सच है? मुझे एक ऐसी भाषा के बारे में पढ़ने की कुछ बहुत अस्पष्ट याद है जिसका पहला संकलक "स्वयं" में लिखा गया था। क्या यह संभव है और यदि ऐसा हो तो कैसे?



यह एक बहुत पुराना प्रश्न है, लेकिन कहते हैं कि मैंने जावा में भाषा फू के लिए एक दुभाषिया लिखा है। फिर भाषा फू के साथ, मैंने लिखा कि यह स्वयं व्याख्याकार है। फू को अभी भी जेआरई की आवश्यकता होगी?
जॉर्ज ज़ेवियर

जवाबों:


231

इसे "बूटस्ट्रैपिंग" कहा जाता है। आपको पहले किसी अन्य भाषा (आमतौर पर जावा या सी) में अपनी भाषा के लिए एक कंपाइलर (या दुभाषिया) का निर्माण करना होगा। एक बार जब यह हो जाता है, तो आप भाषा फू में संकलक का एक नया संस्करण लिख सकते हैं। आप संकलक को संकलित करने के लिए पहले बूटस्ट्रैप संकलक का उपयोग करते हैं, और फिर इस संकलित संकलक का उपयोग बाकी सब कुछ (स्वयं के भविष्य के संस्करणों सहित) को संकलित करने के लिए करते हैं।

अधिकांश भाषाएं वास्तव में इस फैशन में बनाई गई हैं, आंशिक रूप से क्योंकि भाषा डिजाइनर उस भाषा का उपयोग करना पसंद करते हैं जिसे वे बना रहे हैं, और इसलिए भी कि एक गैर-तुच्छ संकलक अक्सर भाषा के "पूर्ण" के लिए एक उपयोगी बेंचमार्क के रूप में कार्य करता है।

इसका एक उदाहरण स्काला होगा। इसका पहला संकलन पिज्जा में बनाया गया था, जो मार्टिन ओडस्की की एक प्रयोगात्मक भाषा थी। संस्करण 2.0 के अनुसार, कंपाइलर पूरी तरह से स्काला में फिर से लिखा गया था। उस बिंदु से, पुराने पिज्जा कंपाइलर को पूरी तरह से खारिज किया जा सकता है, इस तथ्य के कारण कि नए स्काला संकलक का उपयोग भविष्य के पुनरावृत्तियों के लिए खुद को संकलित करने के लिए किया जा सकता है।


शायद एक मूर्खतापूर्ण प्रश्न: यदि आप अपने कंपाइलर को माइक्रोप्रोसेसर के किसी अन्य आर्किटेक्चर में पोर्ट करना चाहते हैं, तो बूटस्ट्रैपिंग को उस आर्किटेक्चर के लिए काम करने वाले कंपाइलर से पुनः आरंभ करना चाहिए। क्या यह सही है? यदि यह सही है, तो इसका मतलब है कि पहले संकलक को रखना बेहतर है क्योंकि यह आपके संकलक को अन्य आर्किटेक्चर में पोर्ट करने के लिए उपयोगी हो सकता है (विशेषकर यदि कुछ 'सार्वभौमिक भाषा' जैसे C में लिखा गया है)?
पियर्सनटी

2
@ पिएरटोन्टी आमतौर पर नए माइक्रोप्रोसेसर के लिए कंपाइलर बैकेंड को पुनः प्राप्त करना आसान होगा।
bstpierre


76

मुझे याद है कि एक सॉफ्टवेयर इंजीनियरिंग रेडियो पॉडकास्ट सुन रहा था, जिसमें डिक गेब्रियल ने LISP में नंगे-हड्डियों के संस्करण को कागज पर लिखकर और मशीन कोड में हाथ जोड़कर मूल LISP दुभाषिया को बूटस्ट्रैप करने की बात की थी । तब से, LISP की बाकी विशेषताएं LISP के साथ व्याख्या और लिखी गईं।


सब कुछ बहुत कुछ के साथ एक

47

पिछले उत्तरों में एक जिज्ञासा जोड़ना।

यहां लिनक्स से स्क्रैच मैनुअल से एक उद्धरण है , उस चरण पर जहां कोई अपने स्रोत से जीसीसी संकलक का निर्माण शुरू करता है। (लिनक्स फ्रैच स्क्रैच लिनक्स को स्थापित करने का एक तरीका है जो वितरण को स्थापित करने से बिल्कुल अलग है, जिसमें आपको वास्तव में लक्ष्य प्रणाली के हर एक बाइनरी को संकलित करना होगा ।)

make bootstrap

'बूटस्ट्रैप' लक्ष्य केवल जीसीसी को संकलित नहीं करता है, बल्कि इसे कई बार संकलित करता है। यह पहले दौर में संकलित कार्यक्रमों का उपयोग खुद को दूसरी बार संकलित करने के लिए करता है, और फिर तीसरी बार। फिर यह इन दूसरे और तीसरे संकलन की तुलना यह सुनिश्चित करने के लिए करता है कि यह स्वयं को दोष रहित बना सके। इसका मतलब यह भी है कि इसे सही तरीके से संकलित किया गया था।

'बूटस्ट्रैप' लक्ष्य का उपयोग इस तथ्य से प्रेरित होता है कि टारगेट सिस्टम के टूलचैन के निर्माण के लिए संकलक जो उपयोग करता है, वह लक्ष्य कंपाइलर का एक ही संस्करण नहीं हो सकता है। उस तरीके से आगे बढ़ना निश्चित है, लक्ष्य प्रणाली में, एक संकलक जो खुद को संकलित कर सकता है।


12
"आपको वास्तव में लक्ष्य प्रणाली के हर एक बाइनरी को संकलित करना होगा" और फिर भी आपको एक जीसी बाइनरी के साथ शुरू करना होगा जो आपको कहीं से मिला है, क्योंकि स्रोत खुद को संकलित नहीं कर सकता है। मुझे आश्चर्य है कि यदि आपने प्रत्येक जीसी बाइनरी के वंश का पता लगाया है जो कि प्रत्येक क्रमिक जीसीपी को फिर से इकट्ठा करने के लिए इस्तेमाल किया गया था, तो क्या आप केएंडआर के मूल सी कंपाइलर पर वापस आ जाएंगे?
रोबर्ट

43

जब आप C के लिए अपना पहला संकलक लिखते हैं, तो आप इसे किसी अन्य भाषा में लिखते हैं। अब, आपके पास सी के लिए एक कंपाइलर है, कहते हैं, असेंबलर। आखिरकार, आप उस स्थान पर आएंगे जहां आपको तार को पार्स करना होगा, विशेष रूप से दृश्यों से बचना होगा। आप \nदशमलव कोड 10 (और \r13, आदि) के साथ चरित्र में बदलने के लिए कोड लिखेंगे ।

इसके बाद कंपाइलर तैयार हो जाता है, आप इसे सी में फिर से लागू करना शुरू कर देंगे। इस प्रक्रिया को " बूटस्ट्रैपिंग " कहा जाता है ।

स्ट्रिंग पार्सिंग कोड बन जाएगा:

...
if (c == 92) { // backslash
    c = getc();
    if (c == 110) { // n
        return 10;
    } else if (c == 92) { // another backslash
        return 92;
    } else {
        ...
    }
}
...

जब यह संकलित होता है, तो आपके पास एक बाइनरी होता है जो '\ n' को समझता है। इसका मतलब है कि आप स्रोत कोड को बदल सकते हैं:

...
if (c == '\\') {
    c = getc();
    if (c == 'n') {
        return '\n';
    } else if (c == '\\') {
        return '\\';
    } else {
        ...
    }
}
...

तो यह जानकारी कहाँ है कि 'n' 13 का कोड है? यह बाइनरी में है! यह डीएनए की तरह है: इस बाइनरी के साथ सी सोर्स कोड को संकलित करना इस जानकारी को विरासत में मिलेगा। यदि संकलक खुद को संकलित करता है, तो वह इस ज्ञान को अपने वंश को पारित करेगा। इस बिंदु से, अकेले स्रोत से देखने का कोई तरीका नहीं है कि कंपाइलर क्या करेगा।

यदि आप किसी प्रोग्राम के स्रोत में वायरस छिपाना चाहते हैं, तो आप इसे इस तरह से कर सकते हैं: एक संकलक का स्रोत प्राप्त करें, वह फ़ंक्शन ढूंढें जो फ़ंक्शन संकलित करता है और इसे इस एक के साथ प्रतिस्थापित करता है:

void compileFunction(char * name, char * filename, char * code) {
    if (strcmp("compileFunction", name) == 0 && strcmp("compile.c", filename) == 0) {
        code = A;
    } else if (strcmp("xxx", name) == 0 && strcmp("yyy.c", filename) == 0) {
        code = B;
    }

    ... code to compile the function body from the string in "code" ...
}

दिलचस्प हिस्से ए और बी हैं compileFunctionवायरस के लिए स्रोत कोड है, शायद किसी तरह से एन्क्रिप्ट किया गया है ताकि परिणामी बाइनरी को खोजने से स्पष्ट न हो। यह सुनिश्चित करता है कि संकलक को संकलित करने से वायरस इंजेक्शन कोड संरक्षित होगा।

बी उस फ़ंक्शन के लिए समान है जिसे हम अपने वायरस से बदलना चाहते हैं। उदाहरण के लिए, यह स्रोत फ़ाइल "login.c" में "लॉगिन" फ़ंक्शन हो सकता है जो संभवतः लिनक्स कर्नेल से है। हम इसे एक ऐसे संस्करण से बदल सकते हैं जो सामान्य पासवर्ड के अलावा रूट खाते के लिए पासवर्ड "जोशुआ" को स्वीकार करेगा।

यदि आप इसे संकलित करते हैं और इसे बाइनरी के रूप में फैलाते हैं, तो स्रोत को देखकर वायरस को खोजने का कोई तरीका नहीं होगा।

विचार का मूल स्रोत: https://web.archive.org/web/20070714062657/http://www.acm.org/classics/sep95/


1
वायरस संक्रमित संकलक लिखने के बारे में दूसरी छमाही का क्या मतलब है? :)
मवेलपंड

3
@mhvelplund बस इस ज्ञान को फैलाना कि बूटस्ट्रैपिंग आपको कैसे मार सकती है।
आरोन दिगुल्ला

19

आप अपने आप में एक कंपाइलर नहीं लिख सकते क्योंकि आपके पास अपने शुरुआती स्रोत कोड को संकलित करने के लिए कुछ भी नहीं है। इसे हल करने के लिए दो दृष्टिकोण हैं।

सबसे कम पसंदीदा निम्नलिखित है। आप भाषा के न्यूनतम सेट के लिए कोडांतरक (yuck) में एक न्यूनतम संकलक लिखते हैं और फिर उस संकलक का उपयोग भाषा की अतिरिक्त विशेषताओं को लागू करने के लिए करते हैं। जब तक आप अपने लिए सभी भाषा सुविधाओं के साथ एक कंपाइलर रखते हैं, तब तक अपना रास्ता बनाएं। एक दर्दनाक प्रक्रिया जो आमतौर पर केवल तब होती है जब आपके पास कोई अन्य विकल्प नहीं होता है।

पसंदीदा दृष्टिकोण एक क्रॉस कंपाइलर का उपयोग करना है। आप लक्ष्य मशीन पर चलने वाले आउटपुट को बनाने के लिए किसी अलग मशीन पर मौजूदा संकलक का पिछला छोर बदलते हैं। तो फिर तुम एक अच्छा पूर्ण संकलक और लक्ष्य मशीन पर काम कर रहा है। इसके लिए सबसे लोकप्रिय सी भाषा है, क्योंकि इसमें बहुत सारे मौजूदा कंपाइलर हैं जो प्लग करने योग्य बैक एंड हैं जिन्हें स्वैप किया जा सकता है।

एक छोटा ज्ञात तथ्य यह है कि GNU C ++ कंपाइलर में एक कार्यान्वयन है जो केवल C सबसेट का उपयोग करता है। इसका कारण यह है कि आमतौर पर एक नई टारगेट मशीन के लिए सी कंपाइलर को ढूंढना आसान होता है जो आपको इसके बाद पूर्ण GNU C ++ कंपाइलर बनाने की अनुमति देता है। आपने अब लक्ष्य मशीन पर C ++ कंपाइलर रखने के लिए अपने आप को बूट किया है।


14

आम तौर पर, आपको पहले काम करने वाले कंपाइलर के एक काम (यदि आदिम) में कटौती करने की आवश्यकता है - तो आप इसे स्वयं-होस्टिंग बनाने के बारे में सोचना शुरू कर सकते हैं। यह वास्तव में कुछ लंगूरों में एक महत्वपूर्ण मील का पत्थर माना जाता है।

"मोनो" से मुझे जो याद है, उससे यह संभावना है कि उन्हें काम करने के लिए कुछ चीजों को जोड़ने की आवश्यकता होगी: मोनो टीम यह इंगित करती रहती है कि कुछ चीजें बस संभव नहीं हैं Reflection.Emit; बेशक, एमएस टीम उन्हें गलत साबित कर सकती है।

इसके कुछ वास्तविक फायदे हैं: यह शुरुआत के लिए काफी अच्छी इकाई परीक्षण है! और आपके पास चिंता करने के लिए केवल एक ही भाषा है (अर्थात यह संभव है कि एक C # विशेषज्ञ को C C ++ का अधिक ज्ञान न हो; लेकिन अब आपका C # कंपाइलर ठीक कर सकता है)। लेकिन मुझे आश्चर्य है कि अगर यहां काम पर पेशेवर गर्व की मात्रा नहीं है: वे बस चाहते हैं कि यह स्व-होस्टिंग हो।

काफी संकलक नहीं है, लेकिन मैं हाल ही में एक सिस्टम पर काम कर रहा हूं जो स्वयं होस्टिंग है; कोड जनरेटर का उपयोग कोड जनरेटर को उत्पन्न करने के लिए किया जाता है ... इसलिए यदि स्कीमा बदलता है तो मैं इसे केवल खुद पर चलाता हूं: नया संस्करण। यदि कोई बग है, तो मैं अभी पिछले संस्करण पर वापस जाता हूं और फिर से कोशिश करता हूं। बहुत सुविधाजनक है, और बनाए रखने के लिए बहुत आसान है।


अपडेट १

मैंने सिर्फ एंडर्स के इस वीडियो को पीडीसी पर देखा है , और (लगभग एक घंटे में) वह कुछ और वैध कारण देता है - सभी एक सेवा के रूप में संकलक के बारे में। सिर्फ रिकार्ड के लिए।


4

यहाँ एक डंप है (वास्तव में खोज करने के लिए कठिन विषय):

यह भी PyPy और Rubinius का विचार है :

(मुझे लगता है कि यह फोर्थ पर भी लागू हो सकता है , लेकिन मुझे फोर्थ के बारे में कुछ भी नहीं पता है।)


माना जाता है कि लघुउत्पाद से संबंधित लेख का पहला लिंक वर्तमान में स्पष्ट उपयोगी और तत्काल जानकारी के बिना एक पृष्ठ की ओर इशारा कर रहा है।
नबंर

1

GNAT, GNU Ada संकलक को पूरी तरह से निर्मित होने के लिए Ada संकलक की आवश्यकता होती है। इसे एक प्लेटफॉर्म पर पोर्ट करते समय दर्द हो सकता है जहां कोई जीएनएटी बाइनरी आसानी से उपलब्ध नहीं है।


1
मैं क्यों नहीं देख रहा हूँ? ऐसा कोई नियम नहीं है कि आपको एक से अधिक बार बूटस्ट्रैप करना पड़े (जैसे कि हर नए प्लेटफॉर्म के लिए), आप करंट के साथ भी क्रॉसपॉली कर सकते हैं।
मार्को वैन डे वोर्ट

1

दरअसल, अधिकांश कंपाइलर्स उस भाषा में लिखे जाते हैं जिसे वे ऊपर लिखे कारणों के लिए संकलित करते हैं।

पहला बूटस्ट्रैप कंपाइलर आमतौर पर C, C ++ या असेंबली में लिखा जाता है।


1

मोनो परियोजना C # संकलक लंबे समय से "स्व-होस्टेड" है, इसका मतलब यह है कि यह सी # में ही लिखा गया है।

मुझे पता है कि संकलक को शुद्ध सी कोड के रूप में शुरू किया गया था, लेकिन एक बार ईसीएमए की "बुनियादी" विशेषताओं को लागू करने के बाद उन्होंने सी # में संकलक को फिर से लिखना शुरू कर दिया।

मुझे उसी भाषा में संकलक लिखने के फायदों के बारे में पता नहीं है, लेकिन मुझे यकीन है कि इसे कम से कम उन विशेषताओं के साथ करना होगा जो भाषा स्वयं प्रस्तुत कर सकती है (सी, उदाहरण के लिए, ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग का समर्थन नहीं करता है) ।

आप अधिक जानकारी यहाँ पा सकते हैं ।


1

मैंने अपने आप में एसएलआईसी (सिस्टम के कार्यान्वयन के लिए भाषाओं की प्रणाली) लिखा। फिर हाथ ने इसे विधानसभा में संकलित किया। SLIC के लिए बहुत कुछ है क्योंकि यह पाँच उप-भाषाओं का एकल संकलक था:

  • SYNTAX पार्सर प्रोग्रामिंग भाषा PPL
  • जनरेटर LISP 2 आधारित पेड़ रेंगने PSEUDO कोड पीढ़ी भाषा
  • आईएसओ इन सीक्वेंस, PSEUDO कोड, अनुकूलन भाषा
  • PSEUDO मैक्रो असेंबली कोड प्रोड्यूसिंग लैंग्वेज की तरह।
  • MACHOP विधानसभा-मशीन अनुदेश भाषा को परिभाषित करना।

एसएलआईसी सीडब्ल्यूआईसी (कंपाइलर फॉर राइटिंग एंड इंप्लीमेंटिंग कंपाइलर्स) से प्रेरित था। अधिकांश संकलक विकास पैकेजों के विपरीत, SLIC और CWIC ने विशेषज्ञ, डोमेन विशिष्ट, भाषाओं के साथ कोड पीढ़ी को संबोधित किया। SLIC ने ISO, PSEUDO और MACHOP सब-लैंग्वेज को जोड़ने वाली CWIC कोड कोड फैली हुई है, जो टारगेट-क्रॉलिंग जेनरेटर लैंग्वेज से अलग टारगेट मशीन बारीकियों को अलग करती है।

LISP 2 पेड़ और सूची

LISP 2 आधारित जनरेटर भाषा की गतिशील मेमोरी प्रबंधन प्रणाली एक प्रमुख घटक है। वर्गाकार कोष्ठक में दर्शाई गई भाषा में सूचियाँ व्यक्त की जाती हैं, इसके अवयव अल्पविराम से अलग हो जाते हैं अर्थात एक तीन तत्व [a, b, c] सूची।

पेड़:

     ADD
    /   \
  MPY     3
 /   \
5     x

उन सूचियों का प्रतिनिधित्व करते हैं जिनकी पहली प्रविष्टि एक नोड ऑब्जेक्ट है:

[ADD,[MPY,5,x],3]

पेड़ों को आमतौर पर शाखाओं से पहले अलग नोड के साथ प्रदर्शित किया जाता है:

ADD[MPY[5,x],3]

LISP 2 आधारित जनरेटर कार्यों के साथ अनपर्सिंग

एक जनरेटर फ़ंक्शन (अनपेक्षित) => क्रिया> जोड़े का एक नामित सेट है ...

<NAME>(<unparse>)=><action>;
      (<unparse>)=><action>;
            ...
      (<unparse>)=><action>;

विषम अभिव्यक्तियाँ ऐसे परीक्षण हैं जो पेड़ के पैटर्न और / या वस्तु प्रकारों से मेल खाती हैं और उन्हें अलग करती हैं और उन भागों को स्थानीय चर को संसाधित करने के लिए इसकी प्रक्रियात्मक कार्रवाई द्वारा निर्दिष्ट करती हैं। विभिन्न तर्क प्रकारों को लेते हुए एक अतिभारित फ़ंक्शन की तरह। सिवाय () => ... परीक्षणों के क्रम में कोड किए गए हैं। पहली सफल अनपेक्षित अपनी इसी क्रिया को अंजाम देती है। अनपेक्षित अभिव्यक्तियाँ परीक्षण विहीन हैं। ADD [x, y] दो शाखाओं से मेल खाता है ADD ट्री अपनी शाखाओं को स्थानीय चर x और y को सौंपता है। कार्रवाई एक साधारण अभिव्यक्ति हो सकती है या एक .BEGIN ...। बाउंड कोड ब्लॉक। मैं आज सी शैली {...} ब्लॉकों का उपयोग करूंगा। पेड़ मिलान, [], अनुचित नियम कार्रवाई के लिए लौटे परिणाम (ओं) को पारित करने वाले जनरेटर को बुला सकते हैं:

expr_gen(ADD[expr_gen(x),expr_gen(y)])=> x+y;

विशेष रूप से उपरोक्त expr_gen अनपेक्षित दो शाखा ADD ट्री से मेल खाता है। परीक्षण पैटर्न के भीतर एक पेड़ शाखा में रखा गया एक भी तर्क जनरेटर उस शाखा के साथ बुलाया जाएगा। इसकी तर्क सूची हालांकि स्थानीय चर निर्दिष्ट वस्तुएं हैं। अनपेक्षित के ऊपर एक दो शाखा निर्दिष्ट होती है ADD ट्री डिस्सैम्प, प्रत्येक शाखा को expr_gen को दबाने वाली पुनरावर्ती। बाईं शाखा वापसी को स्थानीय चर x में रखा गया। इसी तरह वापसी शाखा को yr रिटर्न ऑब्जेक्ट के साथ expr_gen को दिया गया। ऊपर एक संख्यात्मक अभिव्यक्ति मूल्यांकनकर्ता का हिस्सा हो सकता है। वैक्टर नाम के शॉर्टकट फीचर्स थे जो नोड स्ट्रिंग के बजाय ऊपर थे, एक वेक्टर के नोड्स को इसी क्रिया के वेक्टर के साथ इस्तेमाल किया जा सकता था:

expr_gen(#node[expr_gen(x),expr_gen(y)])=> #action;

  node:   ADD, SUB, MPY, DIV;
  action: x+y, x-y, x*y, x/y;

        (NUMBER(x))=> x;
        (SYMBOL(x))=> val:(x);

उपरोक्त अधिक पूर्ण अभिव्यक्ति मूल्यांकक expr_gen की बाईं शाखा से x पर लौटाता है और दाईं शाखा से y तक। एक्स और वाई पर किए गए संबंधित एक्शन वेक्टर वापस आ गए। अंतिम अनपेक्षित => एक्शन जोड़े संख्यात्मक और प्रतीक वस्तुओं से मेल खाते हैं।

प्रतीक और प्रतीक गुण

प्रतीकों का नाम विशेषता हो सकता है। वैल: (x) x में निहित प्रतीक ऑब्जेक्ट की वैल विशेषता तक पहुँचता है। एक सामान्यीकृत प्रतीक तालिका स्टैक SLIC का हिस्सा है। SYMBOL तालिका को धक्का दिया जा सकता है और कार्यों के लिए स्थानीय प्रतीक प्रदान किया जा सकता है। नव निर्मित प्रतीक को शीर्ष प्रतीक तालिका में सूचीबद्ध किया गया है। सिंबल लुकअप शीर्ष तालिका से पहले स्टैक के नीचे प्रतीक तालिका स्टैक को खोजता है।

मशीन स्वतंत्र कोड जनरेट करना

SLIC की जनरेटर भाषा PSEUDO निर्देश ऑब्जेक्ट का उत्पादन करती है, जो उन्हें एक कोड कोड सूची में जोड़ देती है। A .FLUSH अपनी PSEUDO कोड सूची को प्रत्येक PSEUDO निर्देश को सूची से हटाने और इसे कॉल करने के लिए चलाने का कारण बनता है। निष्पादन के बाद एक PSEUDO ऑब्जेक्ट्स मेमोरी जारी की जाती है। PSEUDO और जनरेटर कार्यों की प्रक्रियात्मक निकाय मूल रूप से अपने आउटपुट को छोड़कर एक ही भाषा है। PSEUDO को असेंबली मैक्रोज़ के रूप में कार्य करने के लिए कहा जाता है जो मशीन को स्वतंत्र कोड अनुक्रमिकता प्रदान करता है। वे पेड़ रेंगने वाले जनरेटर भाषा से बाहर विशिष्ट लक्ष्य मशीन का एक पृथक्करण प्रदान करते हैं। PSEUDO MACHOP फ़ंक्शन को आउटपुट मशीन कोड में कहते हैं। MACHOPs का उपयोग असेंबली छद्म ऑप्स को परिभाषित करने के लिए किया जाता है (जैसे dc, कॉन्स्टेंट आदि को परिभाषित करना) और मशीन इंस्ट्रक्शन या एक परिवार जैसे कि वॉन्टेड एंट्री का उपयोग करके निर्देश दिया जाता है। वे बस निर्देश बनाने के लिए अपने मापदंडों को बिट क्षेत्रों के अनुक्रम में बदल देते हैं। MACHOP कॉल असेंबली की तरह दिखती हैं और जब कंपाइल लिस्टिंग में असेंबली दिखाई जाती है तो उसके लिए खेतों की प्रिंट फॉर्मेटिंग प्रदान करती है। उदाहरण कोड में मैं c शैली टिप्पणी का उपयोग कर रहा हूं जिसे आसानी से जोड़ा जा सकता है लेकिन मूल भाषाओं में नहीं था। MACHOPs एक बिट एड्रेसेबल मेमोरी में कोड तैयार कर रहे हैं। SLIC लिंकर कंपाइलर का आउटपुट हैंडल करता है। डीईसी -10 उपयोगकर्ता मोड निर्देश के लिए एक MACHOP vectored प्रविष्टि का उपयोग कर: MACHOPs एक बिट एड्रेसेबल मेमोरी में कोड तैयार कर रहे हैं। SLIC लिंकर कंपाइलर का आउटपुट हैंडल करता है। डीईसी -10 उपयोगकर्ता मोड निर्देश के लिए एक MACHOP vectored प्रविष्टि का उपयोग कर: MACHOPs एक बिट एड्रेसेबल मेमोरी में कोड तैयार कर रहे हैं। SLIC लिंकर कंपाइलर का आउटपुट हैंडल करता है। डीईसी -10 उपयोगकर्ता मोड निर्देश के लिए एक MACHOP vectored प्रविष्टि का उपयोग कर:

.MACHOP #opnm register,@indirect offset (index): // Instruction's parameters.
.MORG 36, O(18): $/36; // Align to 36 bit boundary print format: 18 bit octal $/36
O(9):  #opcd;          // Op code 9 bit octal print out
 (4):  register;       // 4 bit register field appended print
 (1):  indirect;       // 1 bit appended print
 (4):  index;          // 4 bit index register appended print
O(18): if (#opcd&&3==1) offset // immediate mode use value else
       else offset/36;         // memory address divide by 36
                               // to get word address.
// Vectored entry opcode table:
#opnm := MOVE, MOVEI, MOVEM, MOVES, MOVS, MOVSI, MOVSM, MOVSS,
         MOVN, MOVNI, MOVNM, MOVNS, MOVM, MOVMI, MOVMM, MOVMS,
         IMUL, IMULI, IMULM, IMULB, MUL,  MULI,  MULM,  MULB,
                           ...
         TDO,  TSO,   TDOE,  TSOE,  TDOA, TSOA,  TDON,  TSON;
// corresponding opcode value:
#opcd := 0O200, 0O201, 0O202, 0O203, 0O204, 0O205, 0O206, 0O207,
         0O210, 0O211, 0O212, 0O213, 0O214, 0O215, 0O216, 0O217,
         0O220, 0O221, 0O222, 0O223, 0O224, 0O225, 0O226, 0O227,
                           ...
         0O670, 0O671, 0O672, 0O673, 0O674, 0O675, 0O676, 0O677;

.MORG 36, O (18): $ / 36; ऑक्टल में 18 बिट्स के $ 36 / शब्द शब्द पते के स्थान को 36 बिट सीमा पर स्थान संरेखित करता है। 9 बिट opcd, 4 बिट रजिस्टर, अप्रत्यक्ष बिट और 4 बिट इंडेक्स रजिस्टर संयुक्त हैं और मुद्रित हैं जैसे कि एक एकल 18 बिट फ़ील्ड। 18 बिट पता / 36 या तत्काल मूल्य आउटपुट है और ऑक्टल में मुद्रित होता है। एक MOVEI उदाहरण r1 = 1 और r2 = 2 के साथ प्रिंट आउट:

400020 201082 000005            MOVEI r1,5(r2)

कंपाइलर असेंबली ऑप्शन के साथ आपको कंपाइल लिस्टिंग में जनरेट असेंबली कोड मिलता है।

इसे एक साथ लिंक करें

एसएलआईसी लिंकर को एक पुस्तकालय के रूप में आपूर्ति की जाती है जो लिंकिंग और प्रतीक प्रस्तावों को संभालती है। लक्ष्य विशेष आउटपुट लोड फ़ाइल स्वरूपण हालांकि लक्ष्य मशीनों के लिए लिखा जाना चाहिए और लिंकर लाइब्रेरी लाइब्रेरी के साथ जोड़ा जाना चाहिए।

जनरेटर की भाषा एक फ़ाइल में पेड़ों को लिखने और उन्हें पढ़ने के लिए एक मल्टीपास कंपाइलर को लागू करने की अनुमति देने में सक्षम है।

कोड पीढ़ी और उत्पत्ति की लघु गर्मी

मैं यह सुनिश्चित करने के लिए कोड पीढ़ी से पहले चला गया हूं कि यह समझा जाता है कि SLIC एक वास्तविक संकलक था। एसएलआईसी 1960 के दशक के अंत में सीडब्ल्यूआईसी (कंपाइलर फॉर राइटिंग एंड इंप्लीमेंटिंग कंपाइलर) द्वारा विकसित किया गया था। CWIC में केवल SYNTAX और GENERATOR भाषाएँ थीं, जो GENERATOR की भाषा में सांख्यिक बाइट कोड का उत्पादन करती थीं। बाइट कोड रखा गया था या नामांकित वर्गों के साथ जुड़े मेमोरी बफ़र्स में (CWIC प्रलेखन में प्रयुक्त शब्द) को एक .FLUSH स्टेटमेंट द्वारा लिखा गया था। सीडब्ल्यूआईसी पर एक एसीएम पेपर एसीएम अभिलेखागार से उपलब्ध है।

एक प्रमुख प्रोग्रामिंग भाषा को सफलतापूर्वक लागू करना

1970 के दशक के अंत में SLIC का उपयोग COBOL क्रॉस कंपाइलर लिखने के लिए किया गया था। लगभग 3 महीने में पूरा हो गया ज्यादातर एक प्रोग्रामर द्वारा। मैंने आवश्यकतानुसार प्रोग्रामर के साथ थोड़ा काम किया। एक अन्य प्रोग्रामर ने लक्ष्य TI-990 मिनी-कंप्यूटर के लिए रनटाइम लाइब्रेरी और MACHOPs लिखा। उस COBOL कंपाइलर ने असेंबली में काफी अधिक लाइनें संकलित कीं, फिर DEC-10 देशी COBOL कंपाइलर को असेंबली में लिखा गया।

एक संकलक के लिए अधिक तो आमतौर पर के बारे में बात की

खरोंच से एक कंपाइलर लिखने का एक बड़ा हिस्सा रन टाइम लाइब्रेरी है। आपको एक प्रतीक तालिका की आवश्यकता है। आपको इनपुट और आउटपुट चाहिए। डायनेमिक मेमोरी मैनेजमेंट आदि यह आसानी से एक कंपाइलर के लिए रनटाइम लाइब्रेरी लिखने और फिर कंपाइलर लिखने के काम आ सकता है। लेकिन एसएलआईसी के साथ कि रनटाइम लाइब्रेरी एसएलआईसी में विकसित सभी कंपाइलरों के लिए आम है। ध्यान दें कि दो रनटाइम लाइब्रेरी हैं। भाषा की एक (उदाहरण के लिए COBOL) लक्ष्य मशीन। अन्य संकलक रनटाइम लाइब्रेरी है।

मुझे लगता है कि मैंने स्थापित किया है कि ये पार्सर जनरेटर नहीं थे। इसलिए अब बैक एंड की थोड़ी समझ के साथ मैं पार्सर प्रोग्रामिंग लैंग्वेज समझा सकता हूं।

पार्सर प्रोग्रामिंग भाषा

पार्सर को सरल समीकरणों के रूप में लिखे गए सूत्र का उपयोग करके लिखा जाता है।

<name> <formula type operator> <expression> ;

सबसे निचले स्तर पर भाषा का तत्व चरित्र है। टोकन भाषा के पात्रों के एक सबसेट से बनते हैं। चरित्र वर्णों का उपयोग उन वर्णों के नाम को परिभाषित और परिभाषित करने के लिए किया जाता है। वर्ण को परिभाषित करने वाला वर्ण बृहदान्त्र (:) वर्ण है। वर्ण जो वर्ग के सदस्य हैं, को परिभाषा के दाईं ओर कोडित किया गया है। मुद्रण योग्य वर्णों को 'एकल' स्ट्रिंग्स में संलग्न किया गया है। Nonprinting और विशेष वर्णों को उनके संख्यात्मक अध्यादेश द्वारा दर्शाया जा सकता है। क्लास के सदस्यों को एक विकल्प द्वारा अलग किया जाता है | ऑपरेटर। एक वर्ग सूत्र अर्धविराम के साथ समाप्त होता है। वर्ण वर्ग में पहले से परिभाषित वर्ग शामिल हो सकते हैं:

/*  Character Class Formula                                    class_mask */
bin: '0'|'1';                                                // 0b00000010
oct: bin|'2'|'3'|'4'|'5'|'6'|'7';                            // 0b00000110
dgt: oct|'8'|'9';                                            // 0b00001110
hex: dgt|'A'|'B'|'C'|'D'|'E'|'F'|'a'|'b'|'c'|'d'|'e'|'f';    // 0b00011110
upr:  'A'|'B'|'C'|'D'|'E'|'F'|'G'|'H'|'I'|'J'|'K'|'L'|'M'|
      'N'|'O'|'P'|'Q'|'R'|'S'|'T'|'U'|'V'|'W'|'X'|'Y'|'Z';   // 0b00100000
lwr:  'a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'i'|'j'|'k'|'l'|'m'|
      'n'|'o'|'p'|'q'|'r'|'s'|'t'|'u'|'v'|'w'|'x'|'y'|'z';   // 0b01000000
alpha:  upr|lwr;                                             // 0b01100000
alphanum: alpha|dgt;                                         // 0b01101110

Skip_class 0b00000001 पूर्वनिर्धारित है, लेकिन एक स्किप_क्लास को परिभाषित करने में ओवरराइड हो सकता है।

संक्षेप में: एक वर्ण वर्ग विकल्प की एक सूची है जो केवल एक चरित्र स्थिरांक, एक वर्ण का क्रम या पहले से परिभाषित वर्ण वर्ग हो सकता है। जैसा कि मैंने चरित्र वर्गों को लागू किया है: वर्ग सूत्र को एक वर्ग बिट मुखौटा सौंपा गया है। (ऊपर टिप्पणियों में दिखाया गया है) किसी भी अक्षर के शाब्दिक या क्रमिक होने वाले किसी भी वर्ग सूत्र को एक वर्ग बिट आवंटित किया जाता है। एक मुखौटा सम्मिलित वर्ग (एस) के क्लास मास्क (ओं) को एक साथ आवंटित बिट (यदि कोई हो) के साथ जोड़कर बनाया गया है। वर्ण वर्गों से एक वर्ग तालिका बनाई जाती है। एक चरित्र के अध्यादेश द्वारा अनुक्रमणित प्रविष्टि में बिट्स होते हैं जो चरित्र के वर्ग की सदस्यता का संकेत देते हैं। कक्षा परीक्षण इनलाइन किया जाता है। ईएक्स में चरित्र के क्रम के साथ एक IA-86 कोड उदाहरण वर्ग परीक्षण दिखाता है:

test    byte ptr [eax+_classmap],dgt

द्वारा पीछा किया:

jne      <success>

या

je       <failure>

IA-86 निर्देश कोड उदाहरणों का उपयोग किया जाता है क्योंकि मुझे लगता है कि IA-86 निर्देश आज अधिक व्यापक रूप से ज्ञात हैं। अपने वर्ग के मुखौटे का मूल्यांकन करने वाला वर्ग नाम गैर-विनाशकारी रूप से है, जो वर्णिक (ईएक्सएक्स) वर्णों द्वारा अनुक्रमित कक्षा-तालिका के साथ है। एक गैर-शून्य परिणाम वर्ग सदस्यता को इंगित करता है। (EAX को अल (EAX के निम्न 8 बिट) को छोड़कर शून्य किया गया है जिसमें वर्ण है)।

इन पुराने कंपाइलरों में टोकन कुछ अलग थे। मुख्य शब्दों को टोकन के रूप में नहीं समझाया गया था। वे बस पार्सर भाषा में उद्धृत स्ट्रिंग स्थिरांक से मेल खाते थे। कोट किए गए तार सामान्य रूप से नहीं रखे जाते हैं। संशोधक का उपयोग किया जा सकता है। A + स्ट्रिंग का मिलान करता रहता है। (अर्थात + '-' एक अक्षर से मेल खाता है जब चरित्र सफल होता है), ऑपरेशन, (यानी, 'ई') स्ट्रिंग को टोकन में सम्मिलित करता है। पहला मैच होने तक SKIP_CLASS के प्रमुख पात्रों को छोड़ कर टोकन फॉर्मूला को व्हाइट स्पेस द्वारा नियंत्रित किया जाता है। ध्यान दें कि एक स्पष्ट स्किप_क्लास कैरेक्टर मैच स्किपिंग को रोक देगा जिससे स्किप को स्किप_क्लास कैरेक्टर के साथ शुरू किया जा सकेगा। स्ट्रिंग टोकन फार्मूला एक एकल उद्धरण क्विडड चरित्र या एक डबल उद्धृत स्ट्रिंग से मेल खाते स्किप_क्लास वर्णों को छोड़ देता है। ब्याज एक "उद्धृत स्ट्रिंग के भीतर" वर्ण का मिलान है:

string .. (''' .ANY ''' | '"' $(-"""" .ANY | """""","""") '"') MAKSTR[];

पहला वैकल्पिक किसी एकल उद्धरण उद्धृत चरित्र से मेल खाता है। सही विकल्प एक दोहरे उद्धरण उद्धृत स्ट्रिंग से मेल खाता है जिसमें एक एकल "चरित्र का प्रतिनिधित्व करने के लिए एक साथ दो" चरित्र का उपयोग करते हुए दोहरे उद्धरण वर्ण शामिल हो सकते हैं। यह सूत्र अपनी परिभाषा में प्रयुक्त तारों को परिभाषित करता है। आंतरिक सही विकल्प '' '$' (- "" "" .ANY | "" "" "", "" "") "" एक दोहरे उद्धरण उद्धृत स्ट्रिंग से मेल खाता है। हम एक दोहरे उद्धरण "वर्ण" से मेल खाने के लिए एकल 'उद्धृत वर्ण का उपयोग कर सकते हैं। हालांकि दोहरे "उद्धृत स्ट्रिंग के भीतर यदि हम एक" वर्ण का उपयोग करना चाहते हैं तो हमें दो "वर्णों का उपयोग करना चाहिए। उदाहरण के लिए एक उद्धरण को छोड़कर किसी भी पात्र से मेल खाते हुए आंतरिक बाएँ विकल्प में:

-"""" .ANY

आगे एक नकारात्मक झांकना - "" "" का उपयोग किया जाता है जब सफल होता है ("वर्ण से मेल नहीं खा रहा होता है") तो .ANY वर्ण (जो "वर्ण नहीं हो सकता क्योंकि -" "" उस संभावना को समाप्त कर देता है)। सही विकल्प पर चल रहा है - "" "" एक "चरित्र से मेल खाता है और असफल होना सही विकल्प था:"

"""""",""""

दो एकल वर्णों के साथ उन्हें प्रतिस्थापित करने के लिए दो "वर्णों का मिलान करने की कोशिश करता है," "" "" "थ्व सिंगल" वर्ण डालने के लिए। दोनों आंतरिक विकल्प समापन स्ट्रिंग उद्धरण वर्ण से मेल खाते हैं और MAKSTR [] को एक स्ट्रिंग ऑब्जेक्ट बनाने के लिए बुलाया गया है। $ अनुक्रम, लूप, जबकि सफल, ऑपरेटर एक अनुक्रम के मिलान में उपयोग किया जाता है। टोकन सूत्र अग्रणी स्किप क्लास वर्ण (श्वेत स्थान) को छोड़ दें। एक बार पहला मैच होने के बाद स्किप_क्लास स्किपिंग अक्षम कर दी जाती है। हम [] का उपयोग करके अन्य भाषाओं में प्रोग्राम किए गए कार्यों को कॉल कर सकते हैं। [], MAKBIN [], MAKOCT [], MAKHEX [], MAKFLOAT [], और MAKINT [] को लाइब्रेरी फंक्शन की आपूर्ति की जाती है, जो एक मिलान टोकन स्ट्रिंग को एक टाइप की गई वस्तु में बदल देती है। नीचे दिया गया फॉर्मूला काफी जटिल टोकन पहचान को दिखाता है:

number .. "0B" bin $bin MAKBIN[]        // binary integer
         |"0O" oct $oct MAKOCT[]        // octal integer
         |("0H"|"0X") hex $hex MAKHEX[] // hexadecimal integer
// look for decimal number determining if integer or floating point.
         | ('+'|+'-'|--)                // only - matters
           dgt $dgt                     // integer part
           ( +'.' $dgt                  // fractional part?
              ((+'E'|'e','E')           // exponent  part
               ('+'|+'-'|--)            // Only negative matters
               dgt(dgt(dgt|--)|--)|--)  // 1 2 or 3 digit exponent
             MAKFLOAT[] )               // floating point
           MAKINT[];                    // decimal integer

उपरोक्त संख्या टोकन सूत्र पूर्णांक और फ्लोटिंग पॉइंट संख्या को पहचानता है। - विकल्प हमेशा सफल होते हैं। गणना में संख्यात्मक वस्तुओं का उपयोग किया जा सकता है। सूत्र की सफलता पर टोकन ऑब्जेक्ट को पार्स स्टैक पर धकेल दिया जाता है। प्रतिपादक सीसा (+ 'E' | 'e', ​​'E') में दिलचस्प है। हम हमेशा MAKEFLOAT [] के लिए एक अपरकेस ई की इच्छा रखते हैं। लेकिन हम 'ई' की जगह एक कम केस 'ई' का इस्तेमाल करते हैं।

आपने वर्ण वर्ग और टोकन फॉर्मूला की संगति देखी होगी। पार्सिंग फॉर्मूला जारी है कि बैकट्रैकिंग विकल्प और ट्री कंस्ट्रक्शन ऑपरेटरों को जोड़ना। बैकट्रैकिंग और नॉन-बैकट्रैकिंग वैकल्पिक ऑपरेटरों को अभिव्यक्ति स्तर के भीतर नहीं मिलाया जा सकता है। आपके पास ((b। B \ c) गैर-बैकट्रैकिंग नहीं हो सकती है withe \ backtracking विकल्प। (a \ b \ c), (a | b | c) और ((a | b) \ c) मान्य हैं। ए \ बैकट्रैकिंग विकल्प पार्स स्थिति को उसके बाएं विकल्प का प्रयास करने से पहले बचाता है और विफलता पर सही विकल्प का प्रयास करने से पहले पार्स राज्य को पुनर्स्थापित करता है। विकल्पों के अनुक्रम में पहला सफल विकल्प समूह को संतुष्ट करता है। आगे के विकल्पों का प्रयास नहीं किया गया है। फैक्टरिंग और समूहीकरण एक निरंतर आगे बढ़ाने के लिए प्रदान करता है। इससे पहले कि वह अपने बाएं विकल्प का प्रयास करे, बैकट्रैक विकल्प पार्स की एक सहेजी हुई स्थिति बनाता है। जब पार्स आंशिक मैच कर सकता है और तब विफल हो जाता है, तो पीछे की आवश्यकता होती है:

(a b | c d)\ e

उपर्युक्त में यदि कोई रिटर्न विफलता वैकल्पिक सीडी का प्रयास करता है। यदि c c के बाद विफलता वापस आती है तो वैकल्पिक विकल्प का प्रयास किया जाएगा। यदि एक सफल और बी विफल हो जाता है तो पार्स वील को पीछे हटा दिया जाता है और ई प्रयास किया जाता है। इसी तरह एक असफल c सफल और b में विफल रहता है कि पार्स को पीछे हटा दिया गया है और वैकल्पिक e लिया गया है। Backtracking एक सूत्र के भीतर सीमित नहीं है। यदि कोई भी पार्सिंग फॉर्मूला किसी भी समय आंशिक मैच करता है और फिर विफल हो जाता है तो पार्स को शीर्ष बैकट्रैक और इसके विकल्प पर ले जाया जाता है। एक संकलित विफलता हो सकती है यदि कोड आउटपुट हो गया है तो बैकट्रैक बनाया गया था। संकलन शुरू करने से पहले एक बैकट्रैक सेट किया गया है। असफलता लौटना या उसमें पीछे आना एक संकलक विफलता है। बैकट्रैक ढेर हो गए हैं। हम नकारात्मक का उपयोग कर सकते हैं - और सकारात्मक? झांकना / आगे देखने के लिए परिचालकों के परीक्षण के बिना पार्स को आगे बढ़ाएं। स्ट्रिंग परीक्षण होना एक ऐसी झलक है जिसके आगे केवल इनपुट स्थिति को सहेजने और रीसेट करने की आवश्यकता है। आगे एक नज़र एक पार्सिंग अभिव्यक्ति होगी जो विफल होने से पहले एक आंशिक मैच बनाती है। आगे एक नज़र बैकट्रैकिंग का उपयोग करके लागू किया गया है।

Parser भाषा न तो LL या LR पार्सर है। लेकिन एक पुनरावर्ती सभ्य पार्सर लिखने के लिए एक प्रोग्रामिंग भाषा जिसमें आप पेड़ निर्माण का कार्यक्रम करते हैं:

:<node name> creates a node object and pushes it onto the node stack.
..           Token formula create token objects and push them onto 
             the parse stack.
!<number>    pops the top node object and top <number> of parstack 
             entries into a list representation of the tree. The 
             tree then pushed onto the parse stack.
+[ ... ]+    creates a list of the parse stack entries created 
             between them:
              '(' +[argument $(',' argument]+ ')'
             could parse an argument list. into a list.

एक सामान्य रूप से प्रयुक्त पार्सिंग उदाहरण एक अंकगणितीय अभिव्यक्ति है:

Exp = Term $(('+':ADD|'-':SUB) Term!2); 
Term = Factor $(('*':MPY|'/':DIV) Factor!2);
Factor = ( number
         | id  ( '(' +[Exp $(',' Exp)]+ ')' :FUN!2
               | --)
         | '(' Exp ')" )
         (^' Factor:XPO!2 |--);

एक पाश का उपयोग करके एक्सप और टर्म एक बाएं हाथ का पेड़ बनाता है। सही पुनरावृत्ति का उपयोग करने वाला कारक एक दाहिने हाथ का पेड़ बनाता है:

d^(x+5)^3-a+b*c => ADD[SUB[EXP[EXP[d,ADD[x,5]],3],a],MPY[b,c]]

              ADD
             /   \
          SUB     MPY
         /   \   /   \
      EXP     a b     c
     /   \
    d     EXP     
         /   \
      ADD     3
     /   \
    x     5

यहाँ cc संकलक का एक सा है, c शैली टिप्पणियों के साथ SLIC का एक अद्यतन संस्करण। फ़ंक्शन प्रकार (व्याकरण, टोकन, चरित्र वर्ग, जनरेटर, PSEUDO, या MACHOP उनकी आईडी के बाद उनके प्रारंभिक सिंटैक्स द्वारा निर्धारित किए जाते हैं। इन टॉप-डाउन पार्सर के साथ आप एक प्रोग्राम को परिभाषित करने वाले सूत्र के साथ शुरू करते हैं:

program = $((declaration            // A program is a sequence of
                                    // declarations terminated by
            |.EOF .STOP)            // End Of File finish & stop compile
           \                        // Backtrack: .EOF failed or
                                    // declaration long-failed.
             (ERRORX["?Error?"]     // report unknown error
                                    // flagging furthest parse point.
              $(-';' (.ANY          // find a ';'. skiping .ANY
                     | .STOP))      // character: .ANY fails on end of file
                                    // so .STOP ends the compile.
                                    // (-';') failing breaks loop.
              ';'));                // Match ';' and continue

declaration =  "#" directive                // Compiler directive.
             | comment                      // skips comment text
             | global        DECLAR[*1]     // Global linkage
             |(id                           // functions starting with an id:
                ( formula    PARSER[*1]     // Parsing formula
                | sequencer  GENERATOR[*1]  // Code generator
                | optimizer  ISO[*1]        // Optimizer
                | pseudo_op  PRODUCTION[*1] // Pseudo instruction
                | emitor_op  MACHOP[*1]     // Machine instruction
                )        // All the above start with an identifier
              \ (ERRORX["Syntax error."]
                 garbol);                    // skip over error.

// ध्यान दें कि पेड़ बनाने के दौरान आईडी को कैसे बंद किया जाता है और बाद में संयुक्त किया जाता है।

formula =   ("==" syntax  :BCKTRAK   // backtrack grammar formula
            |'='  syntax  :SYNTAX    // grammar formula.
            |':'  chclass :CLASS     // character class define
            |".." token   :TOKEN     // token formula
              )';' !2                // Combine node name with id 
                                     // parsed in calling declaration 
                                     // formula and tree produced
                                     // by the called syntax, token
                                     // or character class formula.
                $(-(.NL |"/*") (.ANY|.STOP)); Comment ; to line separator?

chclass = +[ letter $('|' letter) ]+;// a simple list of character codes
                                     // except 
letter  = char | number | id;        // when including another class

syntax  = seq ('|' alt1|'\' alt2 |--);

alt1    = seq:ALT!2 ('|' alt1|--);  Non-backtrack alternative sequence.

alt2    = seq:BKTK!2 ('\' alt2|--); backtrack alternative sequence

seq     = +[oper $oper]+;

oper    = test | action | '(' syntax ')' | comment; 

test    = string | id ('[' (arg_list| ,NILL) ']':GENCALL!2|.EMPTY);

action  = ':' id:NODE!1
        | '!' number:MAKTREE!1
        | "+["  seq "]+" :MAKLST!1;

//     C style comments
comment  = "//" $(-.NL .ANY)
         | "/*" $(-"*/" .ANY) "*/";

ध्यान दें कि कैसे पार्सर भाषा टिप्पणी और त्रुटि पुनर्प्राप्ति को संभालती है।

मुझे लगता है कि मैंने सवाल का जवाब दे दिया है। SLICs उत्तराधिकारी का एक बड़ा हिस्सा लिखने के बाद, cc भाषा अपने आप में यहाँ है। अभी तक इसके लिए कोई कंपाइलर नहीं है। लेकिन मैं इसे असेंबली कोड, नग्न asm c या c ++ फ़ंक्शन में संकलित कर सकता हूं।


0

हां, आप उस भाषा में किसी भाषा के लिए एक कंपाइलर लिख सकते हैं। नहीं, आपको बूटस्ट्रैप के लिए उस भाषा के लिए पहले संकलक की आवश्यकता नहीं है।

आपको बूटस्ट्रैप के लिए जो आवश्यक है, वह भाषा का कार्यान्वयन है। यह एक संकलक या एक दुभाषिया हो सकता है।

ऐतिहासिक रूप से, भाषाओं को आमतौर पर व्याख्या की गई भाषाओं या संकलित भाषाओं के रूप में समझा जाता था। इंटरप्रिटर्स केवल पूर्व के लिए लिखे गए थे और कंपाइलर केवल बाद के लिए लिखे गए थे। इसलिए आमतौर पर यदि एक कंपाइलर किसी भाषा के लिए लिखा जाने वाला होता है, तो पहला कंपाइलर किसी अन्य भाषा में इसे बूटस्ट्रैप करने के लिए लिखा जाएगा, फिर, वैकल्पिक रूप से, कंपाइलर को विषय भाषा के लिए फिर से लिखा जाएगा। लेकिन इसके बजाय किसी अन्य भाषा में दुभाषिया लिखना एक विकल्प है।

यह सिर्फ सैद्धांतिक नहीं है। मैं वर्तमान में ऐसा खुद कर रहा हूं। मैं एक भाषा के लिए एक संकलक पर काम कर रहा हूं, सामन, कि मैंने खुद को विकसित किया। मैंने पहले C में एक साल्मन कंपाइलर बनाया और अब मैं सैल्मन में कंपाइलर लिख रहा हूं, इसलिए मैं सैल्मन कंपाइलर को बिना किसी अन्य भाषा में लिखे सैल्मन के लिए कंपाइलर के बिना काम करवा सकता हूं।


-1

हो सकता है कि आप BNF का वर्णन करते हुए BNF लिख सकते हैं ।


4
आप वास्तव में (यह उतना मुश्किल भी नहीं है), लेकिन इसका एकमात्र व्यावहारिक अनुप्रयोग पार्सर जनरेटर में होगा।
डैनियल स्पाइवाक

वास्तव में मैंने लीमा पार्सर जनरेटर का उत्पादन करने के लिए उस पद्धति का उपयोग किया है। मेटाग्राममार का एक प्रतिबंधित, सरलीकृत, सारणीबद्ध प्रतिनिधित्व एक सरल पुनरावर्ती-वंशीय पार्सर के माध्यम से जाता है। फिर, लीम व्याकरणों की भाषा के लिए एक पार्सर उत्पन्न करता है, और फिर यह उस पार्सर का उपयोग करता है जिसे व्याकरण पढ़ने के लिए वास्तव में किसी के लिए पार्सर उत्पन्न करने में रुचि है। इसका मतलब है कि मुझे यह जानने की ज़रूरत नहीं है कि मैंने जो लिखा है उसे कैसे लिखा जाए। यह जादू जैसा लगता है।
इयान

वास्तव में आप ऐसा नहीं कर सकते, क्योंकि बीएनएफ स्वयं का वर्णन नहीं कर सकता। आपको एक वैरिएंट की आवश्यकता है जैसे कि याक में उपयोग किया जाता है जहां गैर-टर्मिनल प्रतीकों को उद्धृत नहीं किया जाता है।
लोर्न

1
Bnf को परिभाषित करने के लिए आप bnf का उपयोग नहीं कर सकते क्योंकि <> को पहचाना नहीं जा सकता। EBNF ने तय किया कि भाषा के निरंतर स्ट्रिंग टोकन को उद्धृत करके।
GK
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.