git @ Cat's Eye Technologies Dipple / master python / ambinate.py
master

Tree @master (Download .tar.gz)

ambinate.py @masterraw · history · blame

#!/usr/bin/env python

# ambinate.py: parameterized recursive {de,a}scent {parse,generato}r
# by Chris Pressey of Cat's Eye Technologies, sometime in
# 2015 or possibly earlier, see datestamp on:
# https://gist.github.com/cpressey/dd3f63eda91b33e429fa
#
# SPDX-FileCopyrightText: Chris Pressey, the original author of this work, has dedicated it to the public domain.
# For more information, please refer to <https://unlicense.org/>
# SPDX-License-Identifier: Unlicense

import random
import re
import sys

# Sentence ::= NounPhrase VerbPhrase ".".
# NounPhrase ::= Noun ["and" NounPhrase].
# Noun := "the" ("cat" | "dog" | "mat").
# VerbPhrase ::= "sat" "on" NounPhrase.

def sentence(g):
    noun_phrase(g)
    verb_phrase(g)
    g.term(".")
    g.done()

def noun_phrase(g):
    noun(g)
    r = g.test(["and", ""])
    if r == 'and':
        g.term('and')
        noun_phrase(g)

def noun(g):
    g.term("the")
    r = g.test(["cat", "dog", "mat"])
    if r == 'cat':
        g.term("cat")
    if r == 'dog':
        g.term("dog")
    if r == 'mat':
        g.term("mat")

def verb_phrase(g):
    g.term("sat")
    g.term("on")
    noun_phrase(g)


class RecursiveScent(object):
    """This is an interface"""
    def term(self, s):
        raise NotImplementedError

    def test(self, l):
        raise NotImplementedError

    def done(self):
        raise NotImplementedError


class Generator(RecursiveScent):
    def term(self, s):
        sys.stdout.write(s + ' ')

    def test(self, l):
        return random.choice(l)

    def done(self):
        sys.stdout.write('\n')


class Parser(RecursiveScent):
    def __init__(self, s):
        self.s = s
        self.token = None
        self._scan()
        self.errors = []

    def _scan(self):
        self.s = self.s.lstrip()
        match = re.match(r'^(\w+|\.)(.*?)$', self.s)
        if match:
            self.token = match.group(1)
            self.s = match.group(2)
        else:
            self.token = None
            self.s = ""

    def term(self, s):
        if self.token == s:
            self._scan()
        else:
            self.errors.append('expected "%s", found "%s"' % (s, self.token))

    def test(self, l):
        if self.token in l:
            return self.token
        elif "" in l:  # nullable
            return ""
        else:
            self.errors.append('"%s" not found in %s' % (self.token, l))

    def done(self):
        if self.errors:
            sys.stdout.write('errors: %s\n' % self.errors)
        else:
            sys.stdout.write('well-formed sentence.\n')

### MAIN ###

sentence(Generator())

s = 'the dog and the mat sat on the cat and the cat .'
sentence(Parser(s))

s = 'the dog and some cheese sat on the cat and the cat .'
sentence(Parser(s))