def testTrampoline(self): # trampolined factorial def fact(n, ans, cont): if n < 2: return bounce(cont, ans) else: return bounce(fact, n-1, n*ans, cont) ans = pogo_stick(bounce(fact, 5, 1, lambda d:d)) self.assertEqual(ans, 120) ans = pogo_stick(bounce(fact, 1, 1, lambda d:d)) self.assertEqual(ans, 1) # member search def memq(target, lst, cont): if lst == []: return bounce(cont, False) elif target == lst[0]: return bounce(cont, True) else: return bounce(memq, target, lst[1:], cont) ans = pogo_stick(bounce(memq, 3, [1, 2, 3, 4, 5], lambda d:d)) self.assertEqual(ans, True) ans = pogo_stick(bounce(memq, 3, [1, 2, 4, 5, 6], lambda d:d)) self.assertEqual(ans, False)
def parse_sexp(self, tokens, cont): """Parse a single S-expression. """ token_type = tokens[0][1] if token_type in (TOKEN_TYPE_BOOLEAN, TOKEN_TYPE_INTEGER, TOKEN_TYPE_FLOAT, TOKEN_TYPE_FRACTION, TOKEN_TYPE_COMPLEX, TOKEN_TYPE_STRING, TOKEN_TYPE_SYMBOL,): return bounce(self.parse_lexeme_datum, tokens, cont) elif token_type == TOKEN_TYPE_SINGLE_QUOTE: def make_quote(word): return bounce(cont, lib_list(tsym('quote'), word)) self.consume(tokens, TOKEN_TYPE_SINGLE_QUOTE) return bounce(self.parse_sexp, tokens, make_quote) elif token_type == TOKEN_TYPE_LPAREN: return bounce(self.parse_list, tokens, cont) else: raise SchemeError('bad expression syntax')
def memq(target, lst, cont): if lst == []: return bounce(cont, False) elif target == lst[0]: return bounce(cont, True) else: return bounce(memq, target, lst[1:], cont)
def done_rest(rest): if rest[0] == '(': if rest[1:-1] == '': return bounce(cont, '(' + first + ')') else: return bounce(cont, '(' + first + ' ' + rest[1:-1] + ')') else: return bounce(cont, '(' + first + ' . ' + rest + ')')
def _to_python_list(scmlist, pylist, cont): """Convert a Scheme list into a Python list. """ def got_first(first): nonlocal pylist pylist.append(first) return bounce(_to_python_list, scmlist[1], pylist, cont) if scmlist.isnil: return bounce(cont, pylist) if isinstance(scmlist[0], Pair): return bounce(_to_python_list, scmlist[0], [], got_first) pylist.append(scmlist[0]) return bounce(_to_python_list, scmlist[1], pylist, cont)
def _from_python_list(pylist, scmlist, cont): """Produce a Scheme list from a Python list. """ def got_first(first): nonlocal scmlist scmlist = Pair([first, scmlist]) return bounce(_from_python_list, pylist[1:], scmlist, cont) if pylist == []: return bounce(cont, scmlist) if isinstance(pylist[0], list): return bounce(_from_python_list, list(reversed(pylist[0])), NIL, got_first) scmlist = Pair([pylist[0], scmlist]) return bounce(_from_python_list, pylist[1:], scmlist, cont)
def parse_list(self, tokens, cont): """Parse a Scheme list. """ def done_rest(rest): self.consume(tokens, TOKEN_TYPE_RPAREN) return bounce(cont, rest) self.consume(tokens, TOKEN_TYPE_LPAREN) return bounce(self.parse_rest_sexps, tokens, done_rest)
def parse(self, tokens): """The interface. Returns the list of S-expressions generated from the given token list. """ self.sexps = [] while True: if len(tokens) == 0: return self.sexps self.sexps.append(pogo_stick(bounce(self.parse_sexp, tokens, lambda d:d)))
def done_first(first): def done_rest(rest): if rest[0] == '(': if rest[1:-1] == '': return bounce(cont, '(' + first + ')') else: return bounce(cont, '(' + first + ' ' + rest[1:-1] + ')') else: return bounce(cont, '(' + first + ' . ' + rest + ')') return bounce(_to_str, p[1], done_rest)
def _to_str(p, cont): """Give the neat string representation of a pair. """ def done_first(first): def done_rest(rest): if rest[0] == '(': if rest[1:-1] == '': return bounce(cont, '(' + first + ')') else: return bounce(cont, '(' + first + ' ' + rest[1:-1] + ')') else: return bounce(cont, '(' + first + ' . ' + rest + ')') return bounce(_to_str, p[1], done_rest) if not isinstance(p, Pair): return bounce(cont, str(p)) if p.isnil: return bounce(cont, '()') return bounce(_to_str, p[0], done_first)
def parse_rest_sexps(self, tokens, cont): """Parse the S-expressions in the list. The list may be a Scheme list or a dotted partial list. """ token_type = tokens[0][1] if token_type == TOKEN_TYPE_DOT: self.consume(tokens, TOKEN_TYPE_DOT) return bounce(self.parse_sexp, tokens, cont) elif token_type != TOKEN_TYPE_RPAREN: def done_first(first): nonlocal saved_first_exp saved_first_exp = first return bounce(self.parse_rest_sexps, tokens, done_rest) def done_rest(rest): return bounce(cont, cons(saved_first_exp, rest)) saved_first_exp = None return bounce(self.parse_sexp, tokens, done_first) else: return bounce(cont, NIL)
def parse_lexeme_datum(self, tokens, cont): token = tokens[0] token_type = token[1] if token_type == TOKEN_TYPE_BOOLEAN: self.consume(tokens, TOKEN_TYPE_BOOLEAN) if token[0] == '#t': return bounce(cont, True) else: return bounce(cont, False) elif token_type == TOKEN_TYPE_STRING: self.consume(tokens, TOKEN_TYPE_STRING) return bounce(cont, token[0][1:-1]) elif token_type == TOKEN_TYPE_SYMBOL: self.consume(tokens, TOKEN_TYPE_SYMBOL) return bounce(cont, tsym(token[0])) elif token_type == TOKEN_TYPE_INTEGER: self.consume(tokens, TOKEN_TYPE_INTEGER) return bounce(cont, int(token[0])) elif token_type == TOKEN_TYPE_FLOAT: self.consume(tokens, TOKEN_TYPE_FLOAT) return bounce(cont, float(token[0])) elif token_type == TOKEN_TYPE_FRACTION: self.consume(tokens, TOKEN_TYPE_FRACTION) numer, denom = token[0].split('/') return bounce(cont, float(numer) / float(denom)) elif token_type == TOKEN_TYPE_COMPLEX: self.consume(tokens, TOKEN_TYPE_COMPLEX) return bounce(cont, complex(token[0].replace('i', 'j'))) else: raise SchemeError(token, 'is not a lexeme datum')
def fact(n, ans, cont): if n < 2: return bounce(cont, ans) else: return bounce(fact, n-1, n*ans, cont)
def done_first(first): nonlocal saved_first_exp saved_first_exp = first return bounce(self.parse_rest_sexps, tokens, done_rest)
def got_first(first): nonlocal scmlist scmlist = Pair([first, scmlist]) return bounce(_from_python_list, pylist[1:], scmlist, cont)
def done_rest(rest): return bounce(cont, cons(saved_first_exp, rest))
def to_python_list(scmlist): return pogo_stick(bounce(_to_python_list, scmlist, [], lambda d:d))
def got_first(first): nonlocal pylist pylist.append(first) return bounce(_to_python_list, scmlist[1], pylist, cont)
def done_rest(rest): self.consume(tokens, TOKEN_TYPE_RPAREN) return bounce(cont, rest)
def to_str(p): return pogo_stick(bounce(_to_str, p, lambda d:d))
def make_quote(word): return bounce(cont, lib_list(tsym('quote'), word))
def from_python_list(pylist): return pogo_stick(bounce(_from_python_list, list(reversed(pylist)), NIL, lambda d:d))