JavaScript’te Yorumlayıcı Tasarım Modelinin Gücü – JSManifest

JavaScript'te Yorumlayıcı Tasarım Modelinin Gücü – JSManifest

Bu yazıda JavaScript’te Yorumlayıcı Tasarım Modelini inceleyeceğiz. Bir yorumlayıcının yanı sıra bir kaynak kodun temel gramer gösterimlerini uygulayacağız. İstemci kodlarının kullanması için bir arayüz oluşturacağız (örneğin ayrıştırıcılar gibi)

Tasarım desenleri üç kategoriye ayrılır: Davranışsal, Yaratıcı ve Yapısal. Yorumlayıcı davranış grubuna aittir. Tüm kalıplar arasında yorumlayıcı, insanlar için en kafa karıştırıcı gibi görünüyor, ancak benim deneyimime göre, daha yüksek bir perspektifte (kutunun dışında) düşünmek, zihnimizdeki o ampulün aniden her şeyi aydınlatmasına yardımcı olacaktır.

Tercüman Kalıbını ne zaman uygulamamız gerekiyor?

Bazen, bir tercümana belirli bir bağlama göre nasıl yorum yapacağını söylemek için bir arayüze ihtiyaç duyduğumuz durumlarla karşılaşabiliriz. Bu model ayrıca SQL ayrıştırma, sembol işleme motorlarında vb. yaygın olarak kullanılır.

Bunu bir tür komut dosyası oluşturmak gibi düşünün “dil“.

Örneğin, diziler, dizinlerinden birinde daha fazla dizi içerebilen daha fazla dizi içerebilir, vb:

const items = [
  {
    profile: {
      username: 'bob',
      members: [
        {
          username: 'mike',
        },
        {
          username: 'sally123',
        },
        {
          username: 'panera',
          members: [
            {
              username: 'sonOfPanera',
            },
          ],
        },
      ],
    },
  },
]

Amaç, bir tür “dilbilgisitemsil bu çoğu durumda yetenekli özyineleme.

Yapısına baktığımızda dilbilgisi temsilindeki her bir parça ya bir bileşik ya da bir ağacın yaprağıdır. Daha yüksek bir perspektifte, bu gramer temsillerinin bileşik tasarım modelinin yapısını nasıl oluşturduğuna dikkat edin:

Daha fazla çocuk içeren bazı çocukların olduğuna ve derinliğin ihtiyaç duyduğu kadar arttığına dikkat edin. Bu, özyinelemenin çok önemli olduğu ve geçiş algoritmalarında gerekli hale geldiği yerdir.

Bu örüntüdeki ana katılımcı, yorumcunun kendisidir.

Yorumlayıcı ve Dilbilgisi Gösterimlerini Uygulama

Devam edelim ve yorumlayıcıyı oluşturalım. Tercümanın bir interpret kodumuzdan geçmekten ve bunlardan jeton üretmekten sorumlu olacak yöntem. Bu belirteçler, yorumlayıcının, ayrıştırıcılar gibi istemci kodunun anlaması için kombinatoryal ifadeler oluşturması gereken kuralları içerecektir:

İlk satıra bakalım (ilerken anlamayı kolaylaştırmak için bunu boş bir dizi olarak başlatacağız):

Bu satırı kodda okuyacak olsaydık, adı nasıl değiştiririz? items ile collection?

Satır satır okuyabilir ve şunun gibi bir şeyle yapabiliriz:

let srcCode = `const items = [`

let prevChar
let nextChar

for (let index = 0; index < srcCode.length; index++) {
  nextChar = srcCode[index + 1]

  const char = srcCode[index]

  if (
    char === 'i' &&
    nextChar === 't' &&
    srcCode[index + 2] === 'e' &&
    srcCode[index + 3] === 'm' &&
    srcCode[index + 4] === 's'
  ) {
    srcCode = srcCode.split('')
    srcCode = [
      ...srcCode.slice(0, index),
      'collection',
      ...srcCode.slice(index + 'items'.length),
    ].join('')
  }

  prevChar = char
}

Ancak, bu çok verimli değil çünkü önceki/sonraki dizinlere göz atmanın ve hangi satırın veya sütunun belirli ifadeler, bildirimler vb. ile başladığını söyleyebilmenin güvenilir bir yolu yok. Ayrıca bir yolumuz da yok. bildirici türünü güncelleyebilmek gibi faydalı şeyler yapmak için (var, let, const).

Yorumlayıcı modelini kullandığımızda, bu kodu alacak gerçek bir arayüz oluştururuz, dilbilgisinin bölümlerini temsil eden ilgili nesneleri birbirine bağlarız ve yorumlayıcıya bunlarla ne yapması gerektiği konusunda talimat veririz (unutmayın, bu nesnelerin her birinin kendi kuralları vardır). yorumlayıcıya rehberlik eden) ve ayrıştırıcılar gibi istemci kodu tarafından anlaşılabilen çıktılar üretir.

Yani, bu satıra geri dönersek:

Bunun yerine bir arayüz oluşturabiliriz. Şimdi, her sınıfı varsayılanı geçersiz kılmak gibi istediğimiz herhangi bir şeyi yapmak için özelleştirebildiğimiz için bu arayüzün bu kalıbın en güçlü parçası olduğunu unutmayın. toString yöntem:

class VariableDeclaration {
  constructor() {
    this.kind = null
    this.declarations = []
  }
  toString() {
    let output = ''

    output += `${this.kind} `

    this.declarations.forEach((declaration) => {
      output += declaration.toString()
    })

    return output
  }
}

class VariableDeclarator {
  constructor() {
    this.id = null
    this.init = null
  }
  interpret() {
    return this.init.interpret()
  }
  toString() {
    let output = ''
    output += this.id.toString()
    output += ' = '
    output += this.init.toString()
    return output
  }
}

class ArrayExpression {
  constructor() {
    this.elements = []
  }
  toString() {
    let output = ''

    output += '['

    this.elements.forEach((elem) => {
      output += elem.toString()
    })

    output += ']'

    return output
  }
}

class Identifier {
  constructor(name) {
    this.name = name
  }
  toString() {
    return this.name
  }
}

Mükemmel! Bu, Tercüman Tasarım Modeli iş başında! Artık gramerimiz için bazı temsillerimiz var.

Bu desen çok güçlü. Bu arayüz yerindeyken, tüm varsayılanların üzerine yazdığımızı görebiliriz. toString her sınıf için yöntemler structure for our domain.

Daha sonra, kaynak kodu almak ve bunları okumak için bir tercümana ihtiyacımız var. Tercümanımız, fiili uygulamada kullanılanların çok daha basitleştirilmiş bir versiyonu olacaktır. Bu gönderi uğruna, yalnızca bir astarımızı tam olarak temsil etmek için gerekli parçaları dahil ettim:

class Interpreter {
  interpret(srcCode = '') {
    let nodes = []
    let words = srcCode.split(/(s|r|n|t|=|.|]|[)/)

    for (let index = 0; index < words.length; index++) {
      const word = words[index]
      if (/var|let|const/.test(word)) {
        const kind = ['var', 'let', 'const'].find((char) => word.includes(char))
        const variableName = words[index + 2]

        words.shift()
        words.shift()
        words.shift()
        words.shift()
        words.shift()
        words.shift()
        words.shift()

        const declaration = new VariableDeclaration()
        const declarator = new VariableDeclarator()
        const variable = new Identifier(variableName)

        if (words[0] === '[') {
          declarator.init = new ArrayExpression()
        }

        declaration.kind = kind
        declaration.declarations = [declarator]
        declarator.id = variable

        nodes.push(declaration)
      }
    }

    return nodes
  }

  toString(nodes) {
    let output = ''

    for (const node of nodes) {
      output += node.toString()
    }

    return output
  }
}

Bu, yerinde olduğunda, müşteri kodu yorumlayıcımızı kullanabilir ve kod satırımızı değiştirebilir:

const interpreter = new Interpreter()
const interpreted = interpreter.interpret(srcCode)
const newCode = interpreter.toString(interpreted) 
const interpreter = new Interpreter()
const interpreted = interpreter.interpret(srcCode)

if (interpreted[0] instanceof VariableDeclaration) {
  interpreted[0].declarations[0].id.name = 'collection'
}

const newCode = interpreter.toString(interpreted) 

Bağlam

Bağlam nesneleri genellikle birlikte yorumlayıcılarda kullanılır. Eğer geçersiz kılabilirsek toString tüm dilbilgisi temsili nesnelerimizde, yararlı durum bilgilerini bir nesneye girerek yorumlayıcımızın gücünü kesinlikle artırabiliriz. context.

Örneğin, müşteri koduna atlama yeteneği verebiliriz. undefined dizi ifadesi çıktılarını oluştururken değerler:

dizi-ifade-manipülasyon-yorumlayıcı-tasarım-pattern.png

yorumlayıcı-tasarım-pattern-with-context.png

Diğer desenlerle ilgili ipuçları

  • Yorumlayıcı deseni, en çok bileşik yapılarla çalışırken güçlüdür. Başka bir deyişle, bileşik yapılarla uğraşırken, bileşik kalıbı yorumlayıcı kalıpla birleştirmek bir zorunluluktur.

  • Oluşturucu modelinde, bir dilin dilbilgisel temsilleri olarak hiyerarşik yapılar oluşturmak için bir oluşturucu kullanılabilir ve müşterinin bunları yorumlayıcı desenle kullanmasına izin verilir.

  • Soyut fabrika, karmaşık nesneler oluşturmak için kullanılabilir (örneğin javascript’te bu, çok sayıda ikili ifade içeren çok uzun bir işlev döndürme ifadesi olabilir)

  • Bileşik desen gibi, ziyaretçi ve yineleyici desen de yorumlayıcı ile güçlüdür. Ziyaretçiler genellikle yineleyici modelini kendi içinde uygulayan yinelemeli bir geçiş algoritması ile uygulanır. Tercümanlar, kompozit ağaçları geçmek için ziyaretçileri kullanabilir.

yorumlayıcı-tasarım-desen-ilişkileri-in-javascript.png

Çözüm

Okuduğunuz için teşekkür ederim ve gelecekte benden daha kaliteli yazılar gelmesini sabırsızlıkla bekliyoruz!

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.