CoffeeScript 解釋器模式

2022-06-29 17:15 更新

解釋器模式

問題

其他人需要以控制方式運(yùn)行你的一部分代碼。相對(duì)地,你選擇的語言不能以一種簡潔的方式表達(dá)問題域。

解決方案

使用解釋器模式來創(chuàng)建一個(gè)你翻譯為特定代碼的領(lǐng)域特異性語言(domain-specific language)。

我們來做個(gè)假設(shè),例如用戶希望在你的應(yīng)用程序中執(zhí)行數(shù)學(xué)運(yùn)算。你可以讓他們正向運(yùn)行代碼來演算指令(eval)但這會(huì)讓他們運(yùn)行任意代碼。相反,你可以提供一個(gè)小型的“堆棧計(jì)算器(stack calculator)”語言,用來做單獨(dú)分析,以便只運(yùn)行數(shù)學(xué)運(yùn)算,同時(shí)報(bào)告更有用的錯(cuò)誤信息。

class StackCalculator
    parseString: (string) ->
        @stack = [ ]
        for token in string.split /\s+/
            @parseToken token

        if @stack.length > 1
            throw "Not enough operators: numbers left over"
        else
            @stack[0]

    parseToken: (token, lastNumber) ->
        if isNaN parseFloat(token) # Assume that anything other than a number is an operator
            @parseOperator token
        else
            @stack.push parseFloat(token)

    parseOperator: (operator) ->
        if @stack.length < 2
            throw "Can't operate on a stack without at least 2 items"

        right = @stack.pop()
        left = @stack.pop()

        result = switch operator
            when "+" then left + right
            when "-" then left - right
            when "*" then left * right
            when "/"
                if right is 0
                    throw "Can't divide by 0"
                else
                    left / right
            else
                throw "Unrecognized operator: #{operator}"

        @stack.push result

calc = new StackCalculator

calc.parseString "5 5 +" # => { result: 10 }

calc.parseString "4.0 5.5 +" # => { result: 9.5 }

calc.parseString "5 5 + 5 5 + *" # => { result: 100 }

try
    calc.parseString "5 0 /"
catch error
    error # => "Can't divide by 0"

try
    calc.parseString "5 -"
catch error
    error # => "Can't operate on a stack without at least 2 items"

try
    calc.parseString "5 5 5 -"
catch error
    error # => "Not enough operators: numbers left over"

try
    calc.parseString "5 5 5 foo"
catch error
    error # => "Unrecognized operator: foo"

討論

作為一種替代編寫我們自己的解釋器的選擇,你可以將現(xiàn)有的CoffeeScript解釋器與更自然的(更容易理解的)表達(dá)自己的算法的正常方式相結(jié)合。

class Sandwich
    constructor: (@customer, @bread='white', @toppings=[], @toasted=false)->

white = (sw) ->
    sw.bread = 'white'
    sw

wheat = (sw) ->
    sw.bread = 'wheat'
    sw

turkey = (sw) ->
    sw.toppings.push 'turkey'
    sw

ham = (sw) ->
    sw.toppings.push 'ham'
    sw

swiss = (sw) ->
    sw.toppings.push 'swiss'
    sw

mayo = (sw) ->
    sw.toppings.push 'mayo'
    sw

toasted = (sw) ->
    sw.toasted = true
    sw

sandwich = (customer) ->
    new Sandwich customer

to = (customer) ->
    customer

send = (sw) ->
    toastedState = sw.toasted and 'a toasted' or 'an untoasted'

    toppingState = ''
    if sw.toppings.length > 0
        if sw.toppings.length > 1
            toppingState = " with #{sw.toppings[0..sw.toppings.length-2].join ', '} and #{sw.toppings[sw.toppings.length-1]}"
        else
            toppingState = " with #{sw.toppings[0]}"
    "#{sw.customer} requested #{toastedState}, #{sw.bread} bread sandwich#{toppingState}"

send sandwich to 'Charlie' # => "Charlie requested an untoasted, white bread sandwich"
send turkey sandwich to 'Judy' # => "Judy requested an untoasted, white bread sandwich with turkey"
send toasted ham turkey sandwich to 'Rachel' # => "Rachel requested a toasted, white bread sandwich with turkey and ham"
send toasted turkey ham swiss sandwich to 'Matt' # => "Matt requested a toasted, white bread sandwich with swiss, ham and turkey"

這個(gè)實(shí)例可以允許功能層實(shí)現(xiàn)返回修改后的對(duì)象,從而外函數(shù)可以依次修改它。示例通過借用動(dòng)詞和介詞的用法,把自然語法提供給結(jié)構(gòu),當(dāng)被正確使用時(shí),會(huì)像自然語句一樣結(jié)束。這樣,利用CoffeeScript語言技能和你現(xiàn)有的語言技能可以幫助你關(guān)于捕捉代碼的問題。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)