CFG S识别哪些词-> 1 | S 0

问题描述

我正在处理这个练习:

是什么意思
S - > 1 | S 0

我发现这个问题特别具有挑战性,因为组合太多了,我不知道这些如何详尽列举所有可能性。首先,单词1、10、100、10100、100100是一些公认的单词。

解决方法

我发现这个问题特别具有挑战性,因为组合太多了,我不知道如何穷举所有可能性。

与大多数语法一样,可以生成无限个句子。但是实际上没有多少给定大小的组合,因为:

  • 语法是 linear (没有一个以上的非终结式语法)
  • 只有两个作品。

枚举所有可能的句子的简单方法是对可能的最左导数进行广度优先搜索,从仅由起始符号组成的句子形式开始。 (您可以使用最右边的派生,但最左边的似乎更自然。)

这是一个非常简单的操作,尤其是对于计算机而言,尽管在语法上如此简单,但是手工操作很容易:

  0   S            start symbol
  1   1            in (0) replace S->1
  2   S 0          in (0) replace S->S 0
  3   1 0          in (2) replace S->1
  4   S 0 0        in (2) replace S->S 0
  ...

有一些Javascript可以为任意上下文无关的语法生成句子(如果all参数为true,则可以生成句子)。 (语法分析器是原始的,但仍然缺少错误消息。但是它可用于正确输入的语法。明天我可能会再进行处理。)

function Grammar(s) {
  let tokens = /(\w+|"[^"]+"|'[^']*')|(:)|([|;])|(\S)/g
  let which = m => m.findIndex((v,i)=>v&&i)
  let grammar = new Map()
  grammar.start = undefined
  let put = (lhs,rhs) => {
    if (grammar.has(lhs)) grammar.get(lhs).push(rhs)
    else grammar.set(lhs,[rhs])
  }
  
  let lhs = undefined
  let rhs = []
  
  for (let m of s.matchAll(tokens)) {
    switch (which(m)) {
      case 1: rhs.push(m[1]); break; /* Symbol */
      case 2: if (lhs && rhs.length)
                put(lhs,rhs)
              else if (!lhs && rhs.length == 1) {
                if (!grammar.start)
                  grammar.start = rhs[0]
              }
              else break;
              lhs = rhs.pop()
              rhs = []
              break;
      case 3: if (lhs) {
                put(lhs,rhs)
                rhs = []
                if (m[3] == ';') lhs = undefined
              }
              break
      case 4: break
    }
  }
  if (lhs) put(lhs,rhs);
  return grammar
}

let grammar = Grammar(`
expr: atom
    | expr '+' expr
    | expr '*' expr
    | '-' expr
atom: NUM
    | '(' expr ')'
`)

function* derivationGenerator(grammar,all) {
  level = [ [grammar.start] ]
  while (level.length) {
    nextLevel = []
    for (const form of level) {
      let idx = form.findIndex(sym => grammar.has(sym))
      if (idx < 0 || all) yield form
      if (idx >= 0) {
        let before = form.slice(0,idx)
        let after = form.slice(idx+1)
        grammar.get(form[idx]).forEach(
          rhs => nextLevel.push(before.concat(rhs,after)))
      }
    }
    level = nextLevel
  }
}

function take(gen,n,handle) {
  for (let v of gen) {
    handle(v)
    if (--n <= 0) return gen
  }
}

take(derivationGenerator(grammar),10,form => console.log(...form))