ब्रेनफॉर्क भाषा के सिद्धांत ब्रेनफक के सिद्धांतों के समान हैं, एक अपवाद के साथ: एक अतिरिक्त निर्देश वाई जोड़ा जाता है, जो प्रक्रिया को कांटा करता है, माता-पिता में वर्तमान सेल को शून्य करता है, और बच्चे की प्रक्रिया में अगले सेल को बढ़ाता है। इस मामले में, बच्चे में सेल पॉइंटर को भी दाईं ओर स्थानांतरित किया जाता है।
पहले आप कोड पर एक नज़र डाल सकते हैं, और मैं नीचे टिप्पणी दूंगा
-module(bf). -export([start/1, code_server_loop/1, execute_loop/2]). start(ProgramString) -> Program = array:from_list(ProgramString), register(code, spawn(?MODULE, code_server_loop, [Program])), spawn(?MODULE, execute_loop, [{[], 0, []}, 0]). code_server_loop(Program) -> receive {get_token, Pid, Pos} -> reply(Pid, token, array:get(Pos, Program)), code_server_loop(Program); {get_left_brace, Pid, Pos} -> reply(Pid, left_brace, find_left_brace(Pos, Program)), code_server_loop(Program); {get_right_brace, Pid, Pos} -> reply(Pid, right_brace, find_right_brace(Pos, Program)), code_server_loop(Program); stop -> ok after 5000 -> self() ! stop, code_server_loop(Program) end. reply(Pid, ReplyType, Value) -> case Value of undefined -> Pid ! end_of_program; Value -> Pid ! {ReplyType, Value} end. find_left_brace(Pos, Program) -> find_left_brace(Pos - 1, Program, 0). find_left_brace(Pos, Program, Count) -> case array:get(Pos, Program) of $[ -> if Count == 0 -> Pos; true -> find_left_brace(Pos-1, Program, Count-1) end; $] -> find_left_brace(Pos-1, Program, Count+1); undefined -> undefined; _ -> find_left_brace(Pos-1, Program, Count) end. find_right_brace(Pos, Program) -> find_right_brace(Pos + 1, Program, 0). find_right_brace(Pos, Program, Count) -> case array:get(Pos, Program) of $] -> if Count == 0 -> Pos; true -> find_right_brace(Pos+1, Program, Count-1) end; $[ -> find_right_brace(Pos+1, Program, Count+1); undefined -> undefined; _ -> find_right_brace(Pos+1, Program, Count) end. get_code_server(MessageType, Position) -> code ! {MessageType, self(), Position}, receive end_of_program -> exit(normal); {_ReplyType, Reply} -> Reply end. get_token(Position) -> get_code_server(get_token, Position). get_left_brace(Position) -> get_code_server(get_left_brace, Position). get_right_brace(Position) -> get_code_server(get_right_brace, Position). execute_loop(Tape, CodePosition) -> Token = get_token(CodePosition), case execute_token(Token, Tape, CodePosition) of {skip, SkipPosition, NewTape} -> execute_loop(NewTape, SkipPosition); NewTape -> execute_loop(NewTape, CodePosition + 1) end. execute_token($., {_, C, _} = Tape, _) -> io:format("~c", [C]), Tape; execute_token($,, {L, _, R}, _) -> [C] = io:get_chars("> ", 1), {L, C, R}; execute_token($+, {L, C, R}, _) -> {L, C+1, R}; execute_token($-, {L, C, R}, _) -> {L, C-1, R}; execute_token($>, {L, C, []}, _) -> {[C|L], 0, []}; execute_token($>, {L, C, [RH|RT]}, _) -> {[C|L], RH, RT}; execute_token($<, {[], C, R}, _) -> {[], 0, [C|R]}; execute_token($<, {[LH|LT], C, R}, _) -> {LT, LH, [C|R]}; execute_token($[, {_, C, _} = Tape, Position) -> case C of 0 -> {skip, get_right_brace(Position) + 1, Tape}; _ -> Tape end; execute_token($], Tape, Position) -> {skip, get_left_brace(Position), Tape}; execute_token($Y, {L, _, R} = Tape, Position) -> fork(Tape, Position + 1), {L, 0, R}. fork({L, C, []}, Position) -> fork({L, C, [0]}, Position); fork({L, C, [RH|RT]}, Position) -> spawn(?MODULE, execute_loop, [{[C|L], RH+1, RT}, Position]).
कोड को प्रतीकात्मक रूप से निष्पादित किया जाता है। एएसटी बनाने के लिए यह महान (और इतना मुश्किल नहीं) होगा, जैसा कि बुध पर बीएफ कार्यान्वयन । लेकिन यह कांटा के लिए कोड की एक महत्वपूर्ण जटिलता का कारण होगा। और चूंकि व्याख्या की गति के पीछे कोई दौड़ नहीं है, इसलिए कार्यान्वयन सस्ता और गुस्सा है।
बाल प्रक्रिया के आरंभ को सरल बनाने के लिए, कोड को सभी प्रक्रियाओं के बीच साझा किया जाता है। कोड का सर्वर इसमें लगा हुआ है, जिसकी प्रक्रिया नाम कोड के तहत प्रारंभ फ़ंक्शन में दूसरी पंक्ति द्वारा पंजीकृत है। इस मामले में, दुभाषिया प्रक्रियाएं इसके लिए ग्राहक हैं। सर्वर एक विशिष्ट स्थिति में निर्देशों के लिए अनुरोधों को स्वीकार कर सकता है, साथ ही साथ बाएं और दाएं संबंधित ब्रैकेट की स्थिति को खोजने के लिए सहायक कार्य भी कर सकता है। निष्क्रियता के 5 सेकंड के बाद यह सर्वर स्वचालित रूप से बंद हो जाता है (यह स्पष्ट है कि दुभाषिया के सभी थ्रेड्स ने या तो एक रास्ता या किसी अन्य को समाप्त कर दिया, या कभी समाप्त नहीं किया)।
कोड की व्याख्या स्वयं काफी तुच्छ है: हम सर्वर से निर्देशों के लिए पूछते हैं, इसे निष्पादित करते हैं और निम्नलिखित के लिए पूछते हैं। और इसलिए, जब तक कि सर्वर हमें यह नहीं बताता कि कोई और कोड (end_of_program) नहीं है। फिर हम प्रक्रिया को पूरा करते हैं। मैंने xonix हैबराइज़र (धन्यवाद, धन्यवाद!) से कोशिकाओं के एक टेप को संग्रहीत करने की एक विधि उधार ली है: यह एक ट्यूपल है जिसमें वर्तमान से पहले कोशिकाओं की सूची, वर्तमान सेल और वर्तमान के बाद कोशिकाओं की एक सूची है। यह न केवल टेप के संभावित अनन्तता के साथ, बल्कि इसके साथ उपलब्ध भाषा टूल के साथ काम करने में आसानी के साथ काफी सुविधाजनक निकला।
सबसे महत्वपूर्ण बात, जिसके लिए यह सब लिखा गया था, केवल चार पंक्तियों में निहित है: निष्पादन की परिभाषा में अंतिम खंड_टोकन और कांटा। दरअसल, बच्चे की प्रक्रिया। और वहां सब कुछ काफी सरल है: हम अपनी कोशिकाओं के रिबन को थोड़ा बदलकर नई प्रक्रिया शुरू करेंगे।
एक प्रयोग के रूप में, आप इस तरह के कोड को चलाने की कोशिश कर सकते हैं (यह एक विस्तारित हल है):
Y[-<]++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.
और इसे कई बार चलाना बेहतर है और सुनिश्चित करें कि परिणाम हर बार अलग है: सभी क्योंकि थ्रेड्स के बीच कोई सिंक्रनाइज़ेशन नहीं है।
कोड, निश्चित रूप से, एक संदर्भ नहीं है। और प्रशिक्षण उद्देश्यों के लिए अधिक लिखा गया है । इसलिए मुझे नहीं लगता कि यह किसी के लिए उपयोगी होगा। लेकिन ब्लॉग को उपयुक्त चुना गया है।