然并卵:BF 科普 & BF 解釋器的 JS 實(shí)現(xiàn)

圖片來源:google 搜索

最近在 Codewars上做練習(xí)辣往,某道題的內(nèi)容是實(shí)現(xiàn)一個(gè) brainFuck(簡稱BF語言) 解釋器(c/python/js等等均可)教硫。動(dòng)手實(shí)踐的過程還是很有趣的涧尿,中間也遇到了各種各樣的問題百新,最終通過測試,代碼也比較接近目前的 JS 高分 solution矮瘟。這篇文章準(zhǔn)備聊聊相關(guān)的一些知識(shí)和實(shí)現(xiàn)的細(xì)節(jié)瞳脓。

“腦洞大開”的語言 —— BF 簡介

BrainFuck(后文以簡寫B(tài)F指代),單是名字就很容易讓人腦洞大開澈侠,有種不可描述的“哲學(xué)”韻味劫侧。所以如果你忍不住 google 一下相關(guān)圖片的話,你會(huì)可能搜到類似下面的圖片:

圖片來源:google 搜索

畫面是不是已經(jīng)很生動(dòng)了哨啃?

BF 字面上的含義已經(jīng)暗示了這是一種不太直觀和容易閱讀的語言烧栋,當(dāng)然,在當(dāng)下也不會(huì)是一種通用語言拳球。她屬于 Esolang(全稱 Esoteric programming language审姓,直譯:深?yuàn)W的編程語言) 的范疇。

BF誕生于上世紀(jì)30年代祝峻,曾運(yùn)用于早期的 PC(Amiga)魔吐,想詳細(xì)了解的童鞋可以瀏覽 維基百科

BF 在當(dāng)下有什么應(yīng)用場景呢莱找?

我想酬姆,對(duì)一個(gè)吃瓜群眾來說,了解了它奥溺,對(duì)寫作 逼格腦力 的提升是很有用的辞色。BF 具有極簡主義(搞設(shè)計(jì)的童鞋的不妨了解下下)和功能齊全(圖靈完全)的特點(diǎn),旨在為用戶帶來困惑和挑戰(zhàn)浮定,豐富勞動(dòng)人民的業(yè)余生活相满。

8 種運(yùn)算符及其操作

BF 作為一種極簡的計(jì)算機(jī)語言,僅有8種運(yùn)算符桦卒,分別為: < > + - , . [ ]立美,其功能對(duì)照如下表所示:

指令 含義
< 指針減一(指針左移)
> 指針加一(指針右移)
+ 指針指向的字節(jié)的值加一(當(dāng)前單元的數(shù)值+1)
- 指針指向的字節(jié)的值減一(當(dāng)前單元的數(shù)值-1)
, 輸入內(nèi)容到指針指向的單元(輸入一個(gè)字符,將其ASCII碼保存到當(dāng)前指針?biāo)竼卧?/td>
. 將指針指向的存儲(chǔ)單元的內(nèi)容作為字符輸出(將ASCII碼輸出為字符)
[ 如果指針指向的存儲(chǔ)單元為零闸盔,向后跳轉(zhuǎn)到對(duì)應(yīng)的 ] 指令處
] 如果指針指向的存儲(chǔ)單元不為零,向前跳轉(zhuǎn)到對(duì)應(yīng)的 [ 指令處

BF基于一個(gè)簡單的機(jī)器模型琳省,除了八個(gè)指令迎吵,這個(gè)機(jī)器還包括:一個(gè)以字節(jié)為單位躲撰、被初始化為零的數(shù)組、一個(gè)指向該數(shù)組的指針(初始時(shí)指向數(shù)組的第一個(gè)字節(jié))击费、以及用于輸入輸出的兩個(gè)字節(jié)流拢蛋。

對(duì) BF 比較有意思的比擬可以是這樣的:

  1. 如果把機(jī)器內(nèi)存看成是一個(gè)無限長的“小火車”(類似于ArrayList的數(shù)據(jù)結(jié)構(gòu)),每個(gè)車廂(存儲(chǔ)單元)里面的貨物默認(rèn)都是數(shù)字 0,列車上僅有一個(gè)列車員(數(shù)據(jù)指針)蔫巩;
  2. <> 相當(dāng)于列車員在車廂間進(jìn)行移動(dòng)谆棱,只有當(dāng)列車員在某節(jié)車廂時(shí),才能對(duì)車廂的貨物進(jìn)行操作圆仔;
  3. +- 相當(dāng)于列車員對(duì)當(dāng)前所在車廂的貨物進(jìn)行增減垃瞧;
  4. , 相當(dāng)于列車在裝貨,列車員將當(dāng)前所在車廂的貨物替換為貨運(yùn)站輸入的單批次貨物(一個(gè)字符的ASCII碼)坪郭;
  5. . 會(huì)將當(dāng)前車廂里的貨物名稱(單個(gè)字符)輸出來个从;
  6. [] 相當(dāng)于列車員在滿足條件的兩節(jié)車廂間來回移動(dòng);

這里要注意的是歪沃,數(shù)組的每個(gè)單元都是一個(gè)字節(jié)大朽氯瘛;- 命令允許溢出沪曙,它可以用 255 個(gè) + 命令來代替奕污。例如,當(dāng)某個(gè)存儲(chǔ)單元的值為 255 時(shí)液走,其執(zhí)行指令 + 的結(jié)果為 0碳默。類似的, 0 執(zhí)行指令 - 的結(jié)果為 255.

與通用語言的類比

據(jù)此育灸,BF的運(yùn)算符與通用語言的類比如下(以C語言為例):

BrainFuck C
< --ptr;
> ++ptr;
+ ++*ptr;
- --*ptr;
, *ptr = getchar();
. putchar(*ptr);
[ while (*ptr) {
] }

BF 解釋器的 JS 函數(shù)實(shí)現(xiàn)

代碼奉上:

function brainLuck(code, input) {             // @1
  const inputChars = input.split('');         // @2

  const codes = code.split('');               // @3
  let codeIdx = 0;

  const arr = [];                             // @4
  let arrIdx = 0;
  let outputStr = '';                         // @5

  while (codeIdx < code.length) {             // @6
    const ops = codes[codeIdx];

    const handleLeftBracket = () => {         // @7
      if (~~arr[arrIdx] === 0) {
        let cnt = 1;

        while (cnt) {
          codeIdx++;
          if (codes[codeIdx] === '[') {
            cnt += 1;
          }
          if (codes[codeIdx] === ']') {
            cnt -= 1;
          }
        }
      }
    };

    const handleRightBracket = () => {        // @8
      if (~~arr[arrIdx] !== 0) {
        let cnt = 1;

        while (cnt) {
          codeIdx--;
          if (codes[codeIdx] === ']') {
            cnt += 1;
          }
          if (codes[codeIdx] === '[') {
            cnt -= 1;
          }
        }
      }
    };

    switch (ops) {                            // @9
      case '>':
        arrIdx += 1;
        break;
      case '<':
        arrIdx -= 1;
        break;
      case '+':
        arr[arrIdx] = (~~arr[arrIdx] + 1) % 256;
        break;
      case '-':
        arr[arrIdx] = (~~arr[arrIdx] || 256) - 1;
        break;
      case ',':
        const iptChar = inputChars.shift();
        arr[arrIdx] = iptChar ? iptChar.charCodeAt(0) : arr[arrIdx];
        break;
      case '.':
        outputStr += String.fromCharCode(arr[arrIdx]);
        break;
      case '[':
        handleLeftBracket();
        break;
      case ']':
        handleRightBracket();
        break;
    }

    codeIdx++;                               // @10
  }

  return outputStr;                          // @11
}

實(shí)現(xiàn)思路闡述(與代碼中注釋的序號(hào)對(duì)應(yīng)):

(1) 我們實(shí)現(xiàn)了一個(gè)函數(shù) brainLuck 用以模擬 BF 語言的解釋執(zhí)行腻窒,函數(shù) brainLuck 的用例如下:

const code = ',+[-.,+]';
const input = 'Parksben' + String.fromCharCode(255);

const output = brainLuck(code, input);
console.log(output); // -> 'Parksben'

(2) 將輸入的字符串切割為單個(gè)字符,暫存進(jìn)數(shù)組 inputChars磅崭;

(3) 將 BF 程序切割為單個(gè)操作符儿子,方便遍歷每個(gè)指令,用 codeIdx 作為下標(biāo)進(jìn)行遍歷砸喻;

(4) 聲明一個(gè)數(shù)組 arr 用以模擬機(jī)器內(nèi)存柔逼,過程產(chǎn)生的數(shù)值存儲(chǔ)到此數(shù)組中;

(5) 用字符串 outputStr 存儲(chǔ)程序的輸出割岛;

(6) 遍歷 BF 運(yùn)算符愉适,對(duì)不同指令進(jìn)行相應(yīng)的操作;

(7) 方法 handleLeftBracket癣漆,用以匹配到與當(dāng)前 [ 對(duì)應(yīng)的 ](通過操作下標(biāo) codeIdx)维咸;

(8) 方法 handleRightBracket,用以匹配到與當(dāng)前 ] 對(duì)應(yīng)的 [(通過操作下標(biāo) codeIdx);

(9) 用以處理不同指令的 switch 語句癌蓖;

(10) codeIdx 加一瞬哼,以向前遍歷 codes;

(11) 程序輸出租副;

延伸閱讀

Brainfuck: a Programming Language or a Joke?

丹尼爾·克里斯托法尼的一些 BF 實(shí)例

深?yuàn)W的編程語言 - 維基百科

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末坐慰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子用僧,更是在濱河造成了極大的恐慌结胀,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件责循,死亡現(xiàn)場離奇詭異糟港,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)沼死,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門着逐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人意蛀,你說我怎么就攤上這事耸别。” “怎么了县钥?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵秀姐,是天一觀的道長。 經(jīng)常有香客問我若贮,道長省有,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任谴麦,我火速辦了婚禮蠢沿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘匾效。我一直安慰自己舷蟀,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布面哼。 她就那樣靜靜地躺著野宜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪魔策。 梳的紋絲不亂的頭發(fā)上匈子,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音闯袒,去河邊找鬼虎敦。 笑死游岳,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的其徙。 我是一名探鬼主播吭历,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼擂橘!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起摩骨,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤通贞,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后恼五,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體昌罩,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年灾馒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了茎用。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡睬罗,死狀恐怖轨功,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情容达,我是刑警寧澤古涧,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站花盐,受9級(jí)特大地震影響羡滑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜算芯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一柒昏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧熙揍,春花似錦职祷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至奖亚,卻和暖如春淳梦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背昔字。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國打工爆袍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留首繁,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓陨囊,卻偏偏與公主長得像弦疮,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蜘醋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 官網(wǎng) 中文版本 好的網(wǎng)站 Content-type: text/htmlBASH Section: User ...
    不排版閱讀 4,380評(píng)論 0 5
  • 緣起何時(shí)胁塞?緣散何處? 緣始緣盡压语,緣是無心人啸罢。 朝起夕終,伊儂南北胎食,憶忘情無扰才。 水入海,花終謝厕怜。 伊人一生孤衩匣,便是最...
    花夏依茗薇閱讀 284評(píng)論 0 4
  • 來到法國有許許多多的第一次,第一次吃各種各樣的起司粥航,第一次去貓咖啡琅捏,第一次説別人聽得懂的法語…… 最重要的是我第一...
    rampage_loki閱讀 248評(píng)論 0 0