-1

I am dealing with this exercise: What are the words of

S - > 1 | S 0

I find the problem particularly challenging because there are so many combinations and I have no clue these how to exhaustively enumerate all possibilities. As a starter, the words 1, 10, 100, 10100, 100100 are some of the recognized words.

zell
  • 9,830
  • 10
  • 62
  • 115
  • 1
    How are you getting `10100` or `100100` as possible words? Your only options are to start with a `1`, or tack a `0` on the end, so this is equivalent to the regular expression `10*`. – jasonharper Sep 14 '20 at 18:34

1 Answers1

1

I find the problem particularly challenging because there are so many combinations and I have no clue these how to exhaustively enumerate all possibilities.

As with most grammars, an infinite set of sentences can be produced. But there really aren't very many combinations of any given size, because:

  • The grammar is linear (no production has more than one non-terminal)
  • There are only two productions.

The simple way to enumerate all possible sentences is to a breadth-first search over the possible leftmost derivations, starting with a sentential form consisting only of the start symbol. (You could use rightmost derivations, but leftmost seems more natural.)

That's a very simple operation, particularly for a computer, although in a grammar this simple, it's easy to do by hand:

  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
  ...

Here's a bit of Javascript which can generate sentences (or sentential forms if the all argument is true) for arbitrary context-free grammars. (The grammar parser is primitive, and still lacks error messages. But it works on correctly entered grammars. I might work on it some more tomorrow.)

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))
rici
  • 234,347
  • 28
  • 237
  • 341