├── stdlib ├── newline.scm ├── print.scm ├── and.scm ├── for.scm ├── let-syntax.scm ├── let.scm └── letrec.scm ├── scheme ├── Globals.py ├── unquote.py ├── token.py ├── unquote_splicing.py ├── interfaces.py ├── __init__.py ├── quote.py ├── quasiquote.py ├── let_aster.py ├── cond.py ├── eval.py ├── quasisyntax.py ├── begin.py ├── Lambda.py ├── debug.py ├── IF.py ├── define_syntax.py ├── repl.py ├── syntax_expand.py ├── let.py ├── syntax_rules.py ├── procedure.py ├── syntax_case.py ├── case.py ├── callcc.py ├── parser.py ├── builtins.py ├── environment.py ├── syntax.py ├── symbol.py ├── symbol.pyx ├── macro.py ├── define.py ├── PatternMatcher.py ├── tests │ └── lispytest.py ├── utils.py ├── processer.py ├── processer.pyx └── jit.py ├── MANIFEST.in ├── scripts └── schemepy ├── encoding_test.py ├── setup.py ├── scheme_encode.py ├── README.md ├── lispytest.py └── LICENSE /stdlib/newline.scm: -------------------------------------------------------------------------------- 1 | (define (newline) (display "~n")) 2 | -------------------------------------------------------------------------------- /scheme/Globals.py: -------------------------------------------------------------------------------- 1 | import environment 2 | 3 | 4 | Globals = environment.Environment(None) 5 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include scheme *.scm 2 | recursive-include scheme *.pyx *.px[di] *.h *.c *.scm 3 | 4 | -------------------------------------------------------------------------------- /stdlib/print.scm: -------------------------------------------------------------------------------- 1 | (define (print . args) 2 | (for-each (lambda (x) (display x) (display " ")) args) 3 | (display "~n")) 4 | -------------------------------------------------------------------------------- /stdlib/and.scm: -------------------------------------------------------------------------------- 1 | (define-macro and (lambda args 2 | (if (null? args) #t 3 | (if (= (length args) 1) (car args) 4 | `(if ,(car args) (and ,@(cdr args)) #f))))) 5 | -------------------------------------------------------------------------------- /scripts/schemepy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import scheme, sys 3 | if len(sys.argv) == 1: 4 | exit(scheme.repl.repl()) 5 | 6 | txt=open(sys.argv[1]).read() 7 | exit(scheme.eval.Eval('(begin \n'+txt+')')) 8 | -------------------------------------------------------------------------------- /stdlib/for.scm: -------------------------------------------------------------------------------- 1 | (begin 2 | (define (for-each callable lst) 3 | (define ret (callable (car lst))) 4 | (if (cdr lst) (for-each callable (cdr lst)) ret)) 5 | 6 | 7 | (defmacro (for x in l . calls) (for-each (lambda (,x) ,@calls) ,l)) 8 | ) 9 | -------------------------------------------------------------------------------- /scheme/unquote.py: -------------------------------------------------------------------------------- 1 | __author__ = 'perkins' 2 | 3 | from scheme.Globals import Globals 4 | from scheme.begin import begin 5 | 6 | 7 | class unquote(begin): 8 | pass 9 | 10 | class unsyntax(begin): 11 | pass 12 | 13 | 14 | Globals['unquote'] = unquote() 15 | Globals['unsyntax'] = unsyntax() 16 | -------------------------------------------------------------------------------- /scheme/token.py: -------------------------------------------------------------------------------- 1 | from symbol import Symbol 2 | 3 | 4 | class Token(unicode): 5 | def setLine(self, l): 6 | self.line=l 7 | return self 8 | @property 9 | def symbol(self): 10 | """ 11 | 12 | 13 | :return: Symbol 14 | """ 15 | return Symbol(self).setLine(self.line) 16 | -------------------------------------------------------------------------------- /scheme/unquote_splicing.py: -------------------------------------------------------------------------------- 1 | __author__ = 'perkins' 2 | 3 | from scheme.Globals import Globals 4 | from scheme.begin import begin 5 | 6 | 7 | class unquote_splicing(begin): 8 | pass 9 | 10 | class unsyntax_splicing(begin): 11 | pass 12 | 13 | 14 | Globals['unquote-splicing'] = unquote_splicing() 15 | Globals['unsyntax-splicing'] = unsyntax_splicing() 16 | -------------------------------------------------------------------------------- /stdlib/let-syntax.scm: -------------------------------------------------------------------------------- 1 | (define-syntax let-syntax 2 | (syntax-rules () 3 | ((let-syntax () body ...) (begin body ...)) 4 | ((let-syntax ((name transformer)) body ...) (let () (begin (define-syntax name transformer) body ...))) 5 | ((let-syntax ((name transformer) more ...) body ...) (let () (begin (define-syntax name transformer) (let-syntax (more ...) body ...)))))) 6 | 7 | -------------------------------------------------------------------------------- /encoding_test.py: -------------------------------------------------------------------------------- 1 | #encoding: scheme_encode 2 | """lines beginning with ! execute i the scheme environment 3 | The encoding must be copied/linked to /encodings""" 4 | print 5 5 | 6 | !(define (double x) (+ x x)) 7 | 8 | for i in range(10): 9 | !(print (double 5)) 10 | 11 | 12 | 13 | if __name__=='__main__': 14 | exit( 15 | !(double 1) 16 | ) 17 | 18 | -------------------------------------------------------------------------------- /scheme/interfaces.py: -------------------------------------------------------------------------------- 1 | from zope import interface 2 | 3 | 4 | # noinspection PyMethodParameters,PyUnusedLocal 5 | class Procedure(interface.Interface): 6 | def __init__(ast, env): 7 | """""" 8 | 9 | def __call__(processer, params): 10 | """""" 11 | 12 | 13 | # noinspection PyMethodParameters,PyUnusedLocal 14 | class Macro(interface.Interface): 15 | def __init__(ast, env): 16 | """""" 17 | 18 | def __call__(processer, params): 19 | """""" 20 | 21 | -------------------------------------------------------------------------------- /scheme/__init__.py: -------------------------------------------------------------------------------- 1 | import begin 2 | import callcc 3 | import case 4 | import cond 5 | import define 6 | import environment 7 | import eval 8 | import Globals 9 | import IF 10 | import Lambda 11 | import macro 12 | import parser 13 | import procedure 14 | import processer 15 | import symbol 16 | import token 17 | import utils 18 | import quote 19 | import quasiquote 20 | import unquote 21 | import unquote_splicing 22 | import syntax_case 23 | import syntax_rules 24 | import define_syntax 25 | import quasisyntax 26 | 27 | from . import builtins 28 | 29 | p = processer.processer 30 | r = repl.repl 31 | -------------------------------------------------------------------------------- /scheme/quote.py: -------------------------------------------------------------------------------- 1 | __author__ = 'perkins' 2 | 3 | from zope.interface import implements 4 | 5 | from scheme.macro import Macro 6 | from scheme.Globals import Globals 7 | from utils import copy_with_quote 8 | 9 | 10 | class quote(object): 11 | implements(Macro) 12 | def __init__(self): 13 | pass 14 | def __call__(self, processer, params): 15 | if len(params) > 1: 16 | raise SyntaxError("quote accepts only 1 argument") 17 | processer.popStack(copy_with_quote(params)[0]) 18 | processer.stackPointer += 1 19 | return None 20 | 21 | 22 | Globals['quote'] = quote() -------------------------------------------------------------------------------- /scheme/quasiquote.py: -------------------------------------------------------------------------------- 1 | __author__ = 'perkins' 2 | 3 | from zope.interface import implements 4 | 5 | from scheme.macro import Macro 6 | from scheme.Globals import Globals 7 | from utils import copy_with_quasiquote 8 | 9 | 10 | class quasiquote(object): 11 | implements(Macro) 12 | def __init__(self): 13 | pass 14 | def __call__(self, processer, params): 15 | env = processer.cenv.parent 16 | if len(params) > 1: 17 | raise SyntaxError("quasiquote accepts only 1 argument") 18 | processer.popStack(copy_with_quasiquote(processer, env, params, o_stack=[])[0][0]) 19 | processer.stackPointer += 1 20 | return None 21 | 22 | 23 | Globals['quasiquote'] = quasiquote() -------------------------------------------------------------------------------- /scheme/let_aster.py: -------------------------------------------------------------------------------- 1 | __author__ = 'perkins' 2 | 3 | from scheme.macro import Macro 4 | from scheme.Globals import Globals 5 | from zope.interface import implements 6 | 7 | 8 | class let_aster(object): 9 | implements(Macro) 10 | def __init__(self): 11 | pass 12 | def __call__(self, processer, params): 13 | env = processer.cenv 14 | bindings = params[0] 15 | for binding in bindings: 16 | if len(binding[1:]) != 1: 17 | raise SyntaxError("let requires a list of pairs for its first argument") 18 | env[binding[0]] = processer.process([binding[1]], env) 19 | processer.process(params[1:], env) 20 | return 21 | 22 | 23 | Globals['let*'] = let_aster() -------------------------------------------------------------------------------- /scheme/cond.py: -------------------------------------------------------------------------------- 1 | __author__ = 'jeanie' 2 | 3 | from scheme.macro import Macro 4 | from scheme.Globals import Globals 5 | from zope.interface import implements 6 | 7 | 8 | class cond(object): 9 | implements(Macro) 10 | def __init__(self): 11 | pass 12 | def __call__(self, processer, params): 13 | env = processer.cenv 14 | for pair in params: 15 | if pair[0] == "else": 16 | return pair[1] 17 | if isinstance(pair[0], list): 18 | if processer.process([pair[0]], env): 19 | return pair[1] 20 | continue 21 | else: 22 | if pair[0].toObject(env): 23 | return pair[1] 24 | 25 | 26 | Globals['cond'] = cond() -------------------------------------------------------------------------------- /scheme/eval.py: -------------------------------------------------------------------------------- 1 | from Queue import Empty 2 | import cStringIO 3 | import parser 4 | 5 | import processer 6 | import utils 7 | 8 | 9 | p = processer.processer 10 | 11 | 12 | def Eval(obj, quotesExpanded=False, ccc=False): 13 | if isinstance(obj, (str, unicode)): 14 | obj = cStringIO.StringIO(obj) 15 | ast = parser.Parser(obj).ast 16 | try: 17 | ret = p.doProcess(ast, quotesExpanded=quotesExpanded, ccc=ccc) 18 | except Empty as e: 19 | # noinspection PyUnresolvedReferences 20 | ret = e.ret 21 | return ret 22 | 23 | 24 | def Exec(ast): 25 | try: 26 | ret = p.doProcess(utils.deepcopy(ast)) 27 | except Empty as e: 28 | # noinspection PyUnresolvedReferences 29 | ret = e.ret 30 | return ret 31 | -------------------------------------------------------------------------------- /stdlib/let.scm: -------------------------------------------------------------------------------- 1 | (begin 2 | (define-syntax let 3 | (syntax-rules () 4 | ((let ((name value) ...) body ...) ((lambda (name ...) body ...) value ...)) 5 | ((let function-name ((name value) ...) body ...) 6 | (let () 7 | (begin 8 | (define function-name (lambda (name ...) body ...)) 9 | (function-name value ...)))))) 10 | 11 | 12 | (define-syntax let* 13 | (syntax-rules () 14 | ((let* () body ...)(let () body ...)) 15 | ((let* ((name1 val1) (name2 val2) ...) body ...) 16 | (let ((name1 val1)) 17 | (let* ((name2 val2) ...) 18 | body ...))))) 19 | 20 | 21 | (define (print . args) 22 | (for-each (lambda (x) (display x) (display " ")) args) 23 | (display "~n")) 24 | 25 | ) 26 | -------------------------------------------------------------------------------- /stdlib/letrec.scm: -------------------------------------------------------------------------------- 1 | (define-syntax letrec 2 | (syntax-rules () 3 | ((letrec ((var1 init1) ...) body ...) 4 | (letrec "generate temp names" 5 | (var1 ...) 6 | () 7 | ((var1 init1) ...) 8 | body ...)) 9 | ((letrec "generate temp names" 10 | () 11 | (temp1 ...) 12 | ((var1 init1) ...) 13 | body ...) 14 | (let ((var1 ) ...) 15 | (let ((temp1 init1) ...) 16 | (set! var1 temp1) 17 | ... 18 | body ...))) 19 | ((letrec "generate temp names" 20 | (x y ...) 21 | (temp ...) 22 | ((var1 init1) ...) 23 | body ...) 24 | (letrec "generate temp names" 25 | (y ...) 26 | (newtemp temp ...) 27 | ((var1 init1) ...) 28 | body ...)))) 29 | -------------------------------------------------------------------------------- /scheme/quasisyntax.py: -------------------------------------------------------------------------------- 1 | __author__ = 'perkins' 2 | 3 | from zope.interface import implements 4 | 5 | from scheme.macro import Macro 6 | from scheme.Globals import Globals 7 | from utils import copy_with_quasisyntax 8 | from scheme.syntax import SyntaxSymbol 9 | 10 | class quasisyntax(object): 11 | implements(Macro) 12 | def __init__(self): 13 | pass 14 | def __call__(self, processer, params): 15 | env = processer.cenv.parent 16 | if len(params) > 1: 17 | raise SyntaxError("quasisyntax accepts only 1 argument") 18 | o = copy_with_quasisyntax(processer, env, params, o_stack=[]) 19 | o = SyntaxSymbol(o[0][0]).setSymbol(o[0][0]) 20 | processer.popStack(o,False) 21 | processer.stackPointer += 1 22 | return None 23 | 24 | 25 | Globals['quasisyntax'] = quasisyntax() 26 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, Extension 2 | try: 3 | from Cython.Build import cythonize 4 | except: 5 | cythonize=False 6 | 7 | 8 | import os 9 | 10 | 11 | 12 | setup(name='SchemePy', 13 | version='1.1.04', 14 | description='R5RS scheme interpreter, supporting hygenic macros and full call/cc', 15 | author='Logan Perkins', 16 | author_email='perkins@flyingjnl.net', 17 | url='https://github.com/perkinslr/schemepy', 18 | packages=['scheme'], 19 | ext_modules = cythonize("scheme/*.pyx") if cythonize else None, 20 | requires = ['zope.interface'], 21 | data_files = [[os.path.join('share','schemepy','stdlib'),[os.path.join('stdlib', i) for i in os.listdir('stdlib')]]], 22 | scripts = ['scripts/schemepy'], 23 | license = "LGPL", 24 | keywords = "scheme r5rs define-syntax call/cc", 25 | long_description=open('README.md').read(), 26 | 27 | ) 28 | 29 | 30 | -------------------------------------------------------------------------------- /scheme/begin.py: -------------------------------------------------------------------------------- 1 | __author__ = 'lperkins2' 2 | 3 | from zope.interface import implements 4 | 5 | from scheme.Globals import Globals 6 | from scheme.macro import Macro 7 | 8 | 9 | class begin(object): 10 | implements(Macro) 11 | def __init__(self): 12 | pass 13 | def __call__(self, processer, params): 14 | env = processer.cenv.parent 15 | retval = None 16 | for idx, param in enumerate(params): 17 | processer.stackPointer+=1 18 | icd = processer.callDepth 19 | processer.pushStack([param]) 20 | retval = processer.process([param], env, processer.callDepth) 21 | # while processer.callDepth > icd: 22 | # processer.popStackN() 23 | processer.popStack(retval) 24 | params[idx]=retval 25 | processer.popStack(retval) 26 | processer.stackPointer += 1 27 | return 28 | 29 | 30 | Globals['begin'] = begin() 31 | -------------------------------------------------------------------------------- /scheme/Lambda.py: -------------------------------------------------------------------------------- 1 | __author__ = 'perkins' 2 | 3 | from zope.interface import implements 4 | 5 | from scheme.macro import Macro, MacroSymbol 6 | from scheme.procedure import SimpleProcedure 7 | from processer import Globals 8 | import time 9 | import scheme 10 | from scheme import jit 11 | 12 | lambdas = [] 13 | 14 | 15 | class Lambda(object): 16 | implements(Macro) 17 | def __init__(self): 18 | pass 19 | def __call__(self, *a): 20 | 21 | if len(a) == 1: 22 | processer = scheme.processer.processer 23 | params = a[0] 24 | else: 25 | processer, params = a 26 | args = params[0] 27 | rest = params[1:] 28 | 29 | t = repr(time.time()) 30 | proc = SimpleProcedure([args] + rest, processer.cenv).setName("lambda:%s" % t) 31 | if jit.enabled and jit.lambdas_enabled: 32 | proc = jit.makeFunction(proc) 33 | ret = MacroSymbol('lambda:%s' % t).setEnv( 34 | {('lambda:%s' % t): proc}) 35 | # lambdas.append(ret) 36 | return ret 37 | 38 | 39 | Globals.Globals['lambda'] = Lambda() 40 | -------------------------------------------------------------------------------- /scheme/debug.py: -------------------------------------------------------------------------------- 1 | __author__ = 'perkins' 2 | 3 | DEBUG = False 4 | 5 | debug_settings={ 6 | 'pushStack':False, #watch increases in stack depth 7 | 'popStack':False, #watch decreses in stack depth 8 | 'discardedFrames':False, #save discarded frames, memory leak!!! 9 | 'repl':False, #prints more information on exceptions in the repl 10 | 'syntax':False, #prints informtion when doing syntax 11 | 'symbols':False, #prints line numbers in symbols' repr 12 | 'patternMatcher':False, #prints information when matching symbols 13 | 'tracebck':False, #returns extra informtion in ReportProcesser on errors 14 | 'jit-crash-on-error': False, #makes JIT errors raise exceptions 15 | 'jit': True, 16 | 'jit-one-opcode-per-line': False, 17 | } 18 | 19 | def getDebug(key): 20 | if key in debug_settings: 21 | return debug_settings[key] 22 | return False 23 | 24 | def setDebug(k, v): 25 | if k=='all': 26 | for k in debug_settings: 27 | debug_settings[k]=v 28 | else: 29 | debug_settings[k]=v 30 | 31 | 32 | 33 | def LOG(SECTION, *args): 34 | if not getDebug(SECTION): 35 | return 36 | print SECTION, 37 | for arg in args: 38 | print arg, 39 | print 40 | 41 | 42 | def setAll(b): 43 | for i in debug_settings: 44 | debug_settings[i]=b 45 | -------------------------------------------------------------------------------- /scheme/IF.py: -------------------------------------------------------------------------------- 1 | from scheme.symbol import Symbol 2 | 3 | 4 | __author__ = 'perkins' 5 | 6 | from scheme.macro import Macro 7 | from scheme.Globals import Globals 8 | from zope.interface import implements 9 | 10 | 11 | class IF(object): 12 | implements(Macro) 13 | def __init__(self): 14 | pass 15 | def __call__(self, processer, params): 16 | if (len(params)) > 3: 17 | raise SyntaxError("if accepts a maximum of 3 params") 18 | conditional = params[0] 19 | if_true = params[1] 20 | if_false = params[2] if len(params) == 3 else False 21 | env = processer.cenv 22 | if isinstance(conditional, list): 23 | old_stack_pointer = processer.stackPointer 24 | processer.stackPointer = 1 25 | processer.pushStack(conditional) 26 | # noinspection PyTypeChecker 27 | ret = processer.process([Symbol('Begin')] + [conditional], env) 28 | processer.popStack(ret) 29 | processer.stackPointer = old_stack_pointer 30 | if ret: 31 | return if_true 32 | else: 33 | return if_false 34 | else: 35 | if (isinstance(conditional, Symbol) and conditional.toObject(env)) or ( 36 | not isinstance(conditional, Symbol) and conditional): 37 | return if_true 38 | else: 39 | return if_false 40 | 41 | 42 | Globals['if'] = IF() 43 | -------------------------------------------------------------------------------- /scheme/define_syntax.py: -------------------------------------------------------------------------------- 1 | from __future__ import division, unicode_literals 2 | from scheme.symbol import Symbol 3 | from zope.interface import directlyProvides, implements, providedBy 4 | from scheme.macro import Macro 5 | from scheme.Globals import Globals 6 | from scheme.syntax import SyntaxSymbol 7 | __author__ = 'perkins' 8 | 9 | 10 | class DefinedSyntax(object): 11 | implements(Macro) 12 | def __init__(self, processer, transformer): 13 | if isinstance(transformer, Symbol): 14 | transformer=transformer.toObject(processer.cenv) 15 | self.transformer = transformer 16 | directlyProvides(transformer, Macro) 17 | def __call__(self, processer, params): 18 | syntax_object = SyntaxSymbol([processer.ast[0]] + params).setSymbol([processer.ast[0]] + params) 19 | osp = processer.stackPointer 20 | o = self.transformer(processer, [syntax_object]) 21 | if o is not None: 22 | processer.popStack(o) 23 | processer.stackPointer = osp 24 | 25 | 26 | class DefineSyntax(object): 27 | implements(Macro) 28 | def __init__(self): 29 | pass 30 | def __call__(self, processer, params): 31 | name = params[0] 32 | transformer = processer.process([params[1]], processer.cenv) 33 | processer.cenv.parent[name]=DefinedSyntax(processer, transformer) 34 | return processer.cenv.parent[name] 35 | 36 | 37 | 38 | 39 | 40 | 41 | Globals['define-syntax']=DefineSyntax() 42 | -------------------------------------------------------------------------------- /scheme/repl.py: -------------------------------------------------------------------------------- 1 | from Queue import Empty 2 | from scheme import debug 3 | 4 | 5 | __author__ = 'perkins' 6 | 7 | from scheme.parser import Parser 8 | import scheme.processer 9 | 10 | 11 | processer = scheme.processer.processer 12 | import sys 13 | 14 | Es=[] 15 | def repl(f=sys.stdin, prompt='schemepy> ', of=sys.stdout): 16 | global parser 17 | parser = Parser(f) 18 | while True: 19 | sys.stdout.write(prompt) 20 | try: 21 | ast = parser.ast 22 | except Exception as e: 23 | print e 24 | continue 25 | if ast: 26 | try: 27 | r = processer.doProcess(ast) 28 | except Empty as e: 29 | # noinspection PyUnresolvedReferences 30 | if hasattr(e, 'ret'): 31 | r = e.ret 32 | else: 33 | import traceback 34 | traceback.print_exc() 35 | raise e 36 | except Exception as e: 37 | if debug.getDebug('repl'): 38 | Es.append(e) 39 | import traceback 40 | print traceback.format_exc() 41 | print processer.ast 42 | if processer.children: 43 | print processer.children[-1].ast 44 | print scheme.processer.current_processer.ast 45 | r = e 46 | if r is not None and of: 47 | print >> of, r 48 | else: 49 | break 50 | -------------------------------------------------------------------------------- /scheme/syntax_expand.py: -------------------------------------------------------------------------------- 1 | from macro import Macro 2 | from zope.interface import providedBy 3 | from macro import Symbol 4 | from scheme.macro import SimpleMacro 5 | from scheme.define_syntax import DefinedSyntax 6 | import scheme 7 | def syntax_expand(processer, params): 8 | if not isinstance(params, list) or not params: 9 | return params 10 | o=[] 11 | first=params[0] 12 | if Macro in providedBy(first) or (isinstance(first, Symbol) and first.isBound(processer.env) and Macro in providedBy(first.toObject(processer.env))): 13 | processer.pushStack(params) 14 | if Macro not in providedBy(first): 15 | first = first.toObject(processer.env) 16 | if isinstance(first, (SimpleMacro, DefinedSyntax)): 17 | params = first(processer, params[1:]) 18 | 19 | params = processer.ast[-1] 20 | return params 21 | x = syntax_expand(processer, params) 22 | return x 23 | for i in params: 24 | if isinstance(i, list): 25 | o.append(syntax_expand(processer, i)) 26 | else: 27 | o.append(i) 28 | return o 29 | 30 | 31 | 32 | def expandObj(obj): 33 | processer = scheme.processer.Processer() 34 | processer.dumpStack() 35 | processer.ast=[None] 36 | ast = obj.ast[1] 37 | syn = syntax_expand(processer, ast) 38 | while syn!=ast: 39 | ast=syn 40 | processer.dumpStack() 41 | processer.ast=[None] 42 | syn = syntax_expand(processer, ast) 43 | obj.ast[1]=syn 44 | return syn 45 | -------------------------------------------------------------------------------- /scheme/let.py: -------------------------------------------------------------------------------- 1 | from scheme.debug import LOG 2 | from scheme.environment import Environment 3 | from scheme.procedure import SimpleProcedure 4 | 5 | 6 | __author__ = 'perkins' 7 | 8 | from scheme.macro import Macro 9 | from scheme.Globals import Globals 10 | from zope.interface import implements 11 | 12 | 13 | class let(object): 14 | implements(Macro) 15 | def __init__(self): 16 | pass 17 | def __call__(self, processer, params): 18 | env = processer.cenv 19 | print 18, env 20 | if isinstance(params[0], list): 21 | bindings = params[0] 22 | e = Environment(env) 23 | for binding in bindings: 24 | if len(binding[1:]) != 1: 25 | raise SyntaxError("let requires a list of pairs for its first argument") 26 | if isinstance(binding[1], list): 27 | b = binding[1] 28 | else: 29 | b = binding[1] 30 | 31 | processer.pushStack(b) 32 | r = processer.process(b, Environment(env), processer.initialCallDepth) 33 | processer.popStack(r) 34 | 35 | e[binding[0]] = r 36 | # processer.popStack(r) 37 | r = processer.process(params[1:], e, processer.callDepth) 38 | processer.popStack(r) 39 | processer.popStack(r) 40 | # processer.stackPointer+=1 41 | return 42 | name = params[0] 43 | bindings = params[1] 44 | vars_ = [i[0] for i in bindings] 45 | vals = [processer.process(i[1], Environment(env)) for i in bindings] 46 | proc = SimpleProcedure([vars_] + params[2:], env).setName(name) 47 | env[name] = proc 48 | LOG('LET', 32, [proc] + vals) 49 | ret = processer.process([[proc] + vals]) 50 | processer.popStack(ret) 51 | processer.stackPointer += 1 52 | return 53 | # {('lambda:%s' % t): SimpleProcedure([args] + rest, processer.cenv).setName("lambda:%s"%t)}) 54 | 55 | 56 | Globals['let'] = let() 57 | -------------------------------------------------------------------------------- /scheme/syntax_rules.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from zope.interface import implements, classProvides 4 | 5 | from scheme.macro import Macro 6 | from scheme.symbol import Symbol 7 | from scheme.environment import Environment, SyntaxEnvironment 8 | from scheme.syntax import SyntaxSymbol 9 | from scheme.PatternMatcher import PatternMatcher 10 | # from scheme.utils import syntax_copy_with_replacement 11 | 12 | 13 | from scheme.utils import transformCode 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | import scheme.debug 25 | 26 | 27 | class syntax_rules(object): 28 | implements(Macro) 29 | classProvides(Macro) 30 | def __init__(self, processer, ast): 31 | literals = ast[0] 32 | patterns = ast[1:] 33 | self.name = patterns[0][0][0] 34 | self.env = processer.cenv.parent 35 | self.literals = literals 36 | self.patterns = patterns 37 | def __call__(self, processer, params): 38 | params=params[0].toObject(processer.cenv) 39 | for pattern in self.patterns: 40 | template = pattern[1:] 41 | pattern = pattern[0] 42 | bindings = PatternMatcher(pattern, self.literals).match(params) 43 | if bindings is None: 44 | continue 45 | env = Environment(self.env) 46 | l = {} 47 | l.update(globals()) 48 | l.update(locals()) 49 | #import code 50 | #code.InteractiveConsole(locals=l).interact() 51 | transformedCode = transformCode(template, bindings, env, self) 52 | #osp = processer.stackPointer 53 | #processer.popStack(transformedCode) 54 | ##processer.ast = transformedCode 55 | #processer.stackPointer = osp 56 | if scheme.debug.getDebug('syntax'): 57 | print 56, transformedCode 58 | if len(transformedCode)==1: 59 | return transformedCode[0] 60 | return transformedCode 61 | raise SyntaxError("syntax-rules no case matching %r for %s" % (params, self.name)) 62 | 63 | 64 | import scheme.Globals 65 | 66 | 67 | scheme.Globals.Globals['syntax-rules'] = syntax_rules 68 | -------------------------------------------------------------------------------- /scheme/procedure.py: -------------------------------------------------------------------------------- 1 | from zope import interface 2 | 3 | 4 | # noinspection PyUnusedLocal 5 | from scheme.environment import Environment 6 | from scheme.symbol import Symbol 7 | from scheme.utils import deepcopy 8 | 9 | 10 | # noinspection PyMethodParameters,PyUnusedLocal 11 | class Procedure(interface.Interface): 12 | def __init__(ast, env): 13 | """""" 14 | 15 | def __call__(processer, params): 16 | """""" 17 | 18 | 19 | class SimpleProcedure(object): 20 | interface.implements(Procedure) 21 | def __init__(self, ast, env): 22 | self.ast = ast 23 | self.env = env 24 | self.name = None 25 | self.lineno = None 26 | def __call__(self, processer, args): 27 | retval = None 28 | env = Environment(self.env) 29 | if (isinstance(self.ast[0], list)): 30 | if '.' in self.ast[0]: 31 | iter_args = iter(args) 32 | for idx, item in enumerate(self.ast[0][:-2]): 33 | i = iter_args.next() 34 | env[item] = i 35 | env[self.ast[0][-1]] = list(iter_args) 36 | else: 37 | if (len(args) != len(self.ast[0])): 38 | raise TypeError("%r expected exactly %i arguments, got %i" % (self, len(self.ast[0]), len(args))) 39 | for idx, item in enumerate(self.ast[0]): 40 | i = args[idx] 41 | env[item] = i 42 | else: 43 | env[self.ast[0]] = args 44 | for i in self.ast[1:]: 45 | c = deepcopy([i]) 46 | processer.pushStack(c) 47 | icd = processer.callDepth 48 | retval = processer.process(c, env) 49 | while icd < processer.callDepth: 50 | processer.popStackN() 51 | processer.popStack(retval) 52 | if (isinstance(retval, Symbol)) and retval.isBound(env): 53 | return retval.toObject(env) 54 | return retval 55 | def setName(self, name): 56 | self.name = name 57 | if isinstance(name, Symbol): 58 | self.lineno = name.line 59 | return self 60 | def __repr__(self): 61 | if self.name: 62 | return '' % self.name 63 | return object.__repr__(self) 64 | -------------------------------------------------------------------------------- /scheme/syntax_case.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from Queue import Empty 3 | 4 | from zope.interface import implements, classProvides 5 | from scheme.procedure import Procedure 6 | from scheme.macro import Macro 7 | from scheme.symbol import Symbol 8 | from scheme.environment import Environment, SyntaxEnvironment 9 | from scheme.syntax import SyntaxSymbol 10 | from scheme.PatternMatcher import PatternMatcher 11 | # from scheme.utils import syntax_copy_with_replacement 12 | 13 | 14 | from scheme.utils import transformCode 15 | 16 | 17 | class syntax_case(object): 18 | implements(Macro) 19 | #classProvides(Macro) 20 | def __init__(self): 21 | #literals = ast[0] 22 | #patterns = ast[1:] 23 | #self.name = patterns[0][0][0] 24 | #self.env = processer.cenv.parent 25 | #self.literals = literals 26 | #self.patterns = patterns 27 | pass 28 | def __call__(self, processer, params): 29 | e = processer.cenv 30 | syntax_object = params[0] 31 | syntax_object = processer.process([syntax_object], e) 32 | syntax_list = syntax_object.toObject(e) 33 | while isinstance(syntax_list, SyntaxSymbol): 34 | syntax_list = syntax_list.toObject(e) 35 | literals = params[1] 36 | patterns = params[2:] 37 | for pattern in patterns: 38 | if len(pattern) == 2: 39 | template = pattern[1:] 40 | pattern = pattern[0] 41 | guard = True 42 | else: 43 | template = pattern[2:] 44 | guard = pattern[1] 45 | pattern = pattern[0] 46 | bindings = PatternMatcher(pattern, literals).match(syntax_list) 47 | if bindings is None: 48 | continue 49 | processer.pushStack([guard]) 50 | icd = processer.callDepth 51 | r = processer.process([guard], processer.cenv) 52 | while processer.callDepth > icd: 53 | processer.popStackN() 54 | processer.popStack(r) 55 | if not r: 56 | continue 57 | env = Environment(processer.cenv) 58 | transformedCode = transformCode(template, bindings, env, bindings) 59 | return transformedCode[0] 60 | raise SyntaxError("syntax-case no case matching %r" % (syntax_list)) 61 | 62 | 63 | import scheme.Globals 64 | 65 | 66 | scheme.Globals.Globals['syntax-case'] = syntax_case() 67 | 68 | O=[] 69 | -------------------------------------------------------------------------------- /scheme/case.py: -------------------------------------------------------------------------------- 1 | from scheme.symbol import Symbol 2 | 3 | 4 | __author__ = 'perkins' 5 | 6 | from scheme.macro import Macro 7 | from scheme.Globals import Globals 8 | from zope.interface import implements 9 | 10 | 11 | class switch(object): 12 | implements(Macro) 13 | def __init__(self): 14 | pass 15 | def __call__(self, processer, params): 16 | if isinstance(params[0], list): 17 | key = processer.process([params[0]], processer.cenv) 18 | else: 19 | key = params[0].toObject(processer.cenv) 20 | clauses = params[1:] 21 | ret = [] 22 | begun = False 23 | for clause in clauses: 24 | if clause[0] == 'else': 25 | begun = True 26 | if begun: 27 | if clause[-1] == 'break': 28 | ret.extend(clause[1:-1]) 29 | break; 30 | ret.extend(clause[1:]) 31 | else: 32 | if isinstance(clause[0], list): 33 | val = processer.process([clause[0]], processer.cenv) 34 | else: 35 | val = clause[0].toObject(processer.cenv) 36 | if key == val or (isinstance(val, list) and key in val): 37 | begun = True 38 | if clause[-1] == 'break': 39 | ret.extend(clause[1:-1]) 40 | break; 41 | ret.extend(clause[1:]) 42 | return [Symbol('begin')] + ret 43 | 44 | 45 | class case(object): 46 | implements(Macro) 47 | def __init__(self): 48 | pass 49 | def __call__(self, processer, params): 50 | if isinstance(params[0], list): 51 | key = processer.process([params[0]], processer.cenv) 52 | else: 53 | key = params[0].toObject(processer.cenv) 54 | clauses = params[1:] 55 | ret = [] 56 | for clause in clauses: 57 | if clause[0] == 'else': 58 | ret.extend(clause[1:]) 59 | else: 60 | if isinstance(clause[0], list): 61 | val = processer.process([clause[0]], processer.cenv) 62 | else: 63 | val = clause[0].toObject(processer.cenv) 64 | if key == val or (isinstance(val, list) and key in val): 65 | ret.extend(clause[1:]) 66 | break; 67 | return [Symbol('begin')] + ret 68 | 69 | 70 | Globals['switch'] = switch() 71 | Globals['case'] = case() -------------------------------------------------------------------------------- /scheme_encode.py: -------------------------------------------------------------------------------- 1 | import codecs 2 | import scheme 3 | import argparse 4 | import sys 5 | 6 | f=None 7 | if '--convert' in str(sys.argv): 8 | f = sys.stdout 9 | 10 | 11 | 12 | def scheme_encode(input, errors='strict', filename='', mode=0666): 13 | return (str(input), len(input)) 14 | 15 | 16 | First=True 17 | lisp=['(begin \n'] 18 | 19 | 20 | 21 | def scheme_decode(input, errors='strict'): 22 | global First, lisp 23 | acc=['import scheme;scheme.jit.enabled=True;scheme.debug.setAll(0);'] if First else [] 24 | First=False 25 | 26 | if not input: 27 | if len(lisp)==1: 28 | return (u'', 0) 29 | return (u"",0) 30 | length = len(input) 31 | # Deal with chunked reading, where we don't get 32 | # data containing a complete line of source 33 | if not input.endswith('\n'): 34 | length = input.rfind('\n') + 1 35 | input = input[:length] 36 | 37 | 38 | lines = input.split('\n') 39 | fli = None 40 | for l in (x.rstrip().replace('++', '-=-1').replace('--','-=1') for x in lines): 41 | if l.endswith(';'): 42 | l = l[:-1] 43 | if l.strip().startswith('!'): 44 | if fli is None: 45 | fli = len(l) - len(l.lstrip()) 46 | lisp.append(l.lstrip()[1:]) 47 | elif l.strip()=='': 48 | lisp.append(l) 49 | else: 50 | if len(lisp) - lisp.count('')>1: 51 | lisp.append('\n)\n') 52 | acc.append(' '*(fli or 0) +'scheme.eval.Eval(%r)'%('\n'.join([lisp.pop(0) for i in range(len(lisp))]))) 53 | fli = None 54 | lisp.append('(begin ') 55 | acc.append(l) 56 | if f: 57 | f.write(u'\n'.join(acc)+'\n') 58 | return u'exit()\n', length 59 | return (u'\n'.join(acc)+'\n', length) 60 | 61 | class Codec(codecs.Codec): 62 | def encode(self, input, errors='strict'): 63 | return scheme_encode(input, errors) 64 | def decode(self, input, errors='strict'): 65 | return scheme_decode(input, errors) 66 | 67 | class StreamWriter(Codec, codecs.StreamWriter): 68 | pass 69 | 70 | class StreamReader(Codec, codecs.StreamReader): 71 | pass 72 | 73 | def getregentry(): 74 | return codecs.CodecInfo( 75 | name='listp', 76 | encode=scheme_encode, 77 | decode=scheme_decode, 78 | # incrementalencoder=IncrementalEncoder, 79 | # incrementaldecoder=IncrementalDecoder, 80 | streamreader=StreamReader, 81 | streamwriter=StreamWriter, 82 | ) 83 | -------------------------------------------------------------------------------- /scheme/callcc.py: -------------------------------------------------------------------------------- 1 | from zope.interface import implements, providedBy 2 | 3 | from scheme.macro import Macro 4 | from scheme.procedure import Procedure 5 | from scheme.processer import Globals, processer as p 6 | from scheme.utils import callCCBounce 7 | from scheme.symbol import Symbol 8 | 9 | class CCC(Symbol): 10 | def isBound(self, *args): 11 | return True 12 | def getEnv(self, *args): 13 | return Globals.Globals 14 | def toObject(self, *args): 15 | return callcc() 16 | 17 | 18 | 19 | class callcc(object): 20 | implements(Procedure) 21 | def __init__(self): 22 | self.env = Globals.Globals 23 | def __call__(self, *args): 24 | if len(args) == 1: 25 | ast=args[0] 26 | processer = p 27 | else: 28 | processer, ast = args 29 | # raise Exception() 30 | continuation = processer.continuation 31 | continuation['initialCallDepth'] += 1 32 | continuation['targetCallDepth'] = processer.callDepth 33 | 34 | callback = callccCallback(continuation, self) 35 | # processer.popStack([ast[0], callback]) 36 | # processer.stackPointer-=1 37 | 38 | if Procedure in providedBy(ast[0]): 39 | processer.pushStack([[ast[0], callback]]) 40 | r = processer.process([[ast[0], callback]], processer.cenv) 41 | # processer.popStack(r) 42 | elif Macro in providedBy(ast[0]): 43 | r = ast[0](processer, [callback]) 44 | processer.pushStack(r) 45 | r = processer.process(r, processer.cenv) 46 | processer.popStack(r) 47 | else: 48 | r = ast[0](callback) 49 | return r 50 | 51 | 52 | class callccCallback(): 53 | implements(Procedure) 54 | def __init__(self, continuation, ccc): 55 | self.env = Globals 56 | self.continuation = continuation 57 | self.ccc=ccc 58 | def __call__(self, *args): 59 | if len(args) == 1: 60 | ast=args 61 | processer = p 62 | else: 63 | processer, ast = args 64 | processer.dumpStack() 65 | # if processer.callStack.queue: 66 | #processer.callStack.queue[-1][2]=self.continuation['stackPointer'] 67 | e = callCCBounce() 68 | e.continuation=self.continuation 69 | e.retval = ast[0] 70 | e.ccc=self.ccc 71 | #e.ret = processer.process(processer.ast, processer.cenv, 72 | # max(processer.initialCallDepth, self.continuation['initialCallDepth'] - 1)) 73 | #processer.dumpStack() 74 | # p.dumpStack() 75 | raise e 76 | 77 | 78 | Globals.Globals['call/cc'] = CCC('call/cc') 79 | -------------------------------------------------------------------------------- /scheme/parser.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | import re 3 | import cStringIO 4 | 5 | from symbol import Symbol 6 | from token import Token 7 | 8 | 9 | class Parser(object): 10 | tokenizer = r"""\s*(#`|#,@|#,|#'|,@|[('`,)]|"(?:[\\].|;|[^\\"])*"|;.*|[^\s('"`,;)]*)(.*)""" 11 | eof_object = Symbol('#') 12 | eol_object = Symbol('#') 13 | @classmethod 14 | def stringParser(cls, string): 15 | return cls(cStringIO.StringIO(string)) 16 | def __init__(self, _file): 17 | self.file = _file; 18 | self.line = u'' 19 | self.line_number = 0 20 | def gettokens(self): 21 | """Return the next token, reading new text into line buffer if needed.""" 22 | while True: 23 | if self.line == '\n' or self.line == '': 24 | self.line = self.file.readline().decode('utf-8') 25 | self.line_number += 1 26 | if (self.line_number == 1 or self.line_number == 2) and self.line.startswith('#!'): 27 | self.line = self.file.readline().decode('utf-8') 28 | self.line_number+=1 29 | if self.line == '': 30 | break 31 | 32 | # noinspection PyUnresolvedReferences 33 | token, self.line = re.match(self.tokenizer, self.line).groups() 34 | if token != '' and not token.startswith(';'): 35 | yield Token(token).setLine(self.line_number) 36 | if self.line == '\n' or self.line == '': 37 | yield self.eol_object 38 | yield self.eof_object 39 | tokens=property(gettokens) 40 | def getast(self): 41 | tokens = self.gettokens() 42 | o = [] 43 | 44 | for t in tokens: 45 | if t is self.eof_object: 46 | return o 47 | if t is self.eol_object: 48 | if o: 49 | return o 50 | continue 51 | o.append(self.read_ahead(t, tokens)) 52 | def read_ahead(self, token, tokens): 53 | if '(' == token: 54 | L = [] 55 | while True: 56 | token = tokens.next() 57 | if token is self.eof_object: 58 | raise SyntaxError('unexpected EOF in list') 59 | if token is self.eol_object: 60 | continue 61 | if token == ')': 62 | return L 63 | else: 64 | L.append(self.read_ahead(token, tokens)) 65 | elif ')' == token: 66 | raise SyntaxError('unexpected )') 67 | elif token is self.eol_object: 68 | raise SyntaxError('unexpected eol') 69 | else: 70 | return token.symbol 71 | ast=property(getast) 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /scheme/builtins.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | import os 3 | import sys 4 | import math 5 | import cmath 6 | import operator as op 7 | import scheme 8 | from zope.interface import providedBy 9 | from scheme import debug 10 | from scheme.Globals import Globals 11 | from scheme.macro import Macro 12 | from scheme.procedure import Procedure 13 | import cStringIO 14 | 15 | from scheme.debug import setDebug, getDebug 16 | 17 | cons = lambda x, y: [x] + y 18 | 19 | is_pair = lambda x: x != [] and isa(x, list) 20 | 21 | isa = isinstance 22 | 23 | 24 | def throw(e): 25 | raise e 26 | 27 | 28 | def last(o): 29 | if o: 30 | return o[-1] 31 | 32 | 33 | def car(x): 34 | return x[0] 35 | 36 | 37 | def schemeApply(callable_, args, kw={}): 38 | if Macro in providedBy(callable_) or Procedure in providedBy(callable_): 39 | return callable_(scheme.processer.current_processer, args) 40 | return callable_(*args, **kw) 41 | 42 | 43 | def List(*x): 44 | return list(x) 45 | 46 | def add_globals(env): 47 | """Add some Scheme standard procedures.""" 48 | import scheme.eval 49 | env.update(vars(math)) 50 | env.update(vars(cmath)) 51 | env.update({ 52 | 'list-type':list, 53 | 'globals':env, 54 | 'dict':dict, 55 | 'open-output-string': lambda: cStringIO.StringIO(), 56 | 'get-output-string': lambda ioObj: ioObj.getvalue(), 57 | '%': op.mod, 58 | 'procedure?': lambda x: Procedure in providedBy(x), 59 | 'set-debug': setDebug, 60 | 'throw': throw, 61 | 'Exception': Exception, 62 | 'type': lambda x: type(x), 63 | '**': op.pow, 64 | '+': op.add, '-': op.sub, '*': op.mul, '/': op.itruediv, 'not': op.not_, 65 | '>': op.gt, '<': op.lt, '>=': op.ge, '<=': op.le, '=': op.eq, 66 | 'equal?': op.eq, 'eq?': op.is_, 'length': len, 'cons': cons, 67 | 'car': car, 68 | 'cdr': lambda x: x[1:], 69 | 'append': op.add, 70 | 'list': List, 'list?': lambda x: isa(x, list), 71 | 'null?': lambda x: x == [], 72 | 'boolean?': lambda x: isa(x, bool), 'pair?': is_pair, 73 | 'port?': lambda x: isa(x, file), 'apply': schemeApply, 74 | 'len?': len, 75 | 'map': map, 76 | 'in': lambda x, y: x in y, 77 | 'bool': bool, 78 | 'eval': lambda *x: scheme.eval.Exec(x), 79 | 'execfile': scheme.eval.Eval, 80 | 'str':str, 81 | 'unicode':unicode, 82 | 'int':int, 83 | 'zip':zip, 84 | 'last': last, 85 | 'display': lambda x, port=sys.stdout: port.write(x.replace('~n', '\n') if isa(x, (str, unicode)) else str(x))}) 86 | from repl import repl 87 | import site 88 | package_locations = site.getsitepackages() 89 | installed = False 90 | for pl in package_locations: 91 | if __file__.startswith(pl): 92 | installed = True 93 | break 94 | if installed: 95 | p = os.path.join(sys.prefix,'share','schemepy','stdlib') 96 | else: 97 | p = os.path.join(*os.path.split(__file__)[:-2]+('stdlib',)) 98 | for scm in os.listdir(p): 99 | repl(open(os.path.join(p,scm)), '', None) 100 | return env 101 | 102 | 103 | add_globals(Globals) 104 | -------------------------------------------------------------------------------- /scheme/environment.py: -------------------------------------------------------------------------------- 1 | # noinspection PyTypeChecker 2 | 3 | import debug 4 | 5 | class Environment(dict): 6 | def __init__(self, parent, *args, **kw): 7 | """ 8 | 9 | :rtype : Environment 10 | """ 11 | self.parent = parent 12 | super(Environment, self).__init__(*args, **kw) 13 | def __call__(self, item): 14 | return self[item] 15 | def __setitem__(self, key, value): 16 | if isinstance(key, (list, tuple)): 17 | if len(key)==1: 18 | self[key[0]]=value 19 | return 20 | print key, value 21 | if len(key) != len(value): 22 | raise SyntaxError("Setting multiple symbols require the proper number of values") 23 | for idx, i in enumerate(key): 24 | self[i] = value[idx] 25 | return 26 | dict.__setitem__(self, key, value) 27 | def __repr__(self): 28 | import Globals 29 | if self is Globals.Globals: 30 | return '{GLOBALS}' 31 | return '' % (self.parent, dict.__repr__(self)) 32 | def __getitem__(self, item): 33 | if self.parent is not None: 34 | return super(Environment, self).__getitem__(item) if item in self else self.parent.__getitem__(item) 35 | return super(Environment, self).__getitem__(item) 36 | 37 | 38 | ses=[] 39 | 40 | class SyntaxEnvironment(dict): 41 | def __init__(self, *args, **kw): 42 | if debug.debug_settings['syntax']: 43 | ses.append(self) 44 | super(SyntaxEnvironment, self).__init__(*args, **kw) 45 | self.env=self 46 | parent = None 47 | def walk(self, pairs=False): 48 | items = self.iteritems() 49 | for key, value in items: 50 | if pairs: 51 | yield key, value 52 | else: 53 | yield key 54 | if isinstance(value, SyntaxEnvironment): 55 | for i in value.walk(pairs): 56 | yield i 57 | elif isinstance(value, list): 58 | for i in value: 59 | if isinstance(i, SyntaxEnvironment): 60 | for a in i.walk(pairs): 61 | yield a 62 | def __contains__(self, item): 63 | for i in self.walk(): 64 | if i == item: 65 | return True 66 | return False 67 | def get_all(self, item): 68 | if isinstance(item, list): 69 | o = [] 70 | for i in item: 71 | o.append(self.get_all(i)) 72 | return zip(*o) 73 | l = list(self.iget_all(item)) 74 | #if len(l) == 1: 75 | # return l[0] 76 | return l 77 | def iget_all(self, item): 78 | for i, v in self.walk(pairs=True): 79 | if i == item: 80 | yield v 81 | def __getitem__(self, item): 82 | o = [] 83 | found = False 84 | for i, v in self.walk(pairs=True): 85 | if i == item: 86 | found = True 87 | if i.ellipsis: 88 | o.extend(v) 89 | else: 90 | o.append(v) 91 | #return v 92 | return o 93 | def __setitem__(self, item, value): 94 | from scheme.PatternMatcher import PatternMatcher 95 | if isinstance(item, PatternMatcher): 96 | item.setValue(value) 97 | super(SyntaxEnvironment, self).__setitem__(item, value) 98 | -------------------------------------------------------------------------------- /scheme/syntax.py: -------------------------------------------------------------------------------- 1 | from __future__ import division, unicode_literals 2 | from Queue import Empty 3 | from scheme.symbol import Symbol 4 | from zope.interface import classProvides, implements, implementer, provider 5 | from scheme.macro import Macro, MacroSymbol 6 | from scheme.Globals import Globals 7 | __author__ = 'perkins' 8 | 9 | # noinspection PyAttributeOutsideInit 10 | @provider(Macro) 11 | class SyntaxSymbol(Symbol): 12 | def __init__(self, *args): 13 | self.line=0 14 | if len(args) > 1: 15 | processer, template = args 16 | self.setSymbol(template) 17 | self.setEnv(processer.cenv) 18 | def __new__(cls, *args): 19 | template = args[-1] 20 | return super(SyntaxSymbol, cls).__new__(cls, template) 21 | def setSyntax(self, transformer): 22 | self.transformer = transformer 23 | return self 24 | def setSymbol(self, symbol): 25 | self.symbol = symbol 26 | return self 27 | def setAltEnv(self, env): 28 | self.altEnv = env 29 | return self 30 | def toObject(self, env): 31 | # print 31, self, env 32 | if not isinstance(self.symbol, Symbol): 33 | return self.symbol 34 | try: 35 | possibleEnv = Symbol.getEnv(self, env) 36 | # print 36 37 | except NameError: 38 | if hasattr(self, 'altEnv'): 39 | # print 38 40 | try: 41 | possibleEnv = Symbol.getEnv(self.symbol, self.altEnv) 42 | # print 40, possibleEnv 43 | return Symbol.toObject(self.symbol, self.altEnv) 44 | except NameError: 45 | possibleEnv = None 46 | else: 47 | possibleEnv = None 48 | # print 44, possibleEnv 49 | if possibleEnv is not None: 50 | keys = possibleEnv.keys() 51 | if self in keys: 52 | possibleSymbol = keys[keys.index(self)] 53 | if isinstance(possibleSymbol, SyntaxSymbol) and possibleSymbol.transformer == self.transformer: 54 | return possibleEnv[self] 55 | try: 56 | return self.symbol.toObject(self.env) 57 | except NameError as e: 58 | if hasattr(self, 'altEnv'): 59 | # print 59 60 | return self.symbol.toObject(self.altEnv) 61 | if possibleEnv: 62 | import scheme.processer as p 63 | return MacroSymbol(self.symbol).setObj(possibleEnv[self]) 64 | raise e 65 | def getEnv(self, env): 66 | possibleEnv=None 67 | try: 68 | possibleEnv = Symbol.getEnv(self.symbol, env) 69 | except NameError: 70 | try: 71 | possibleEnv = Symbol.getEnv(self.symbol, self.altEnv) 72 | except NameError: 73 | pass 74 | if possibleEnv is not None: 75 | keys = possibleEnv.keys() 76 | if self in keys: 77 | possibleSymbol = keys[keys.index(self)] 78 | if isinstance(possibleSymbol, SyntaxSymbol) and possibleSymbol.transformer == self.transformer: 79 | return possibleEnv 80 | return self.symbol.getEnv(self.env) 81 | def setEnv(self, env): 82 | self.env = env 83 | return self 84 | def __repr__(self): 85 | return "" % Symbol.__repr__(self) 86 | 87 | @implementer(Macro) 88 | class syntax(object): 89 | def __init__(self, *args): 90 | pass 91 | def __call__(self, processer, template): 92 | o = SyntaxSymbol(processer, template[0]).setSymbol(template[0]) 93 | return o 94 | 95 | 96 | 97 | 98 | Globals['syntax']=syntax() 99 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | schemepy 2 | ======== 3 | 4 | Implementation of scheme in python supporting call/cc and hygienic macros 5 | 6 | Using schemepy 7 | --------- 8 | 9 | There are 3 basic ways to use schemepy. As a stand-alone scheme interpreter: 10 | 11 | $ /usr/bin/schemepy 12 | 13 | As a stand-alone REPL: 14 | 15 | $ /usr/bin/schemepy 16 | schemepy> 17 | 18 | 19 | Or from inside a python script 20 | 21 | import scheme 22 | scheme.repl.repl() 23 | schemepy> 24 | 25 | Or to run predefind strings from python 26 | 27 | import scheme 28 | scheme.eval.Eval(myString) 29 | #or 30 | scheme.eval.Eval(myFile) 31 | 32 | Eval will only execute the first statement in its input, so if you want compound inputs, wrap them with 33 | 34 | (begin ) 35 | 36 | The default environment setup is controlled in builtins.py, with 37 | additional definitions in builtins.scm (scheme/builtins.scm in the 38 | source, /usr/share/schemepy/stdlib/builtins.scm once installed). 39 | 40 | Scheme is sandboxed away from python, so only functions provided into 41 | the global Environment (scheme.Globals.Globals) or some other scheme 42 | environment can be accessed. Note that by default, the interpreter is 43 | given access to the file system and other sensitive functions. If you 44 | want to use it as a sandbox for user code, you need to strip out 45 | anything you don't want called. Also, getattr and getitem are 46 | undefined in the default environment. If you are running trusted 47 | code, you can simply add the standard getattr to the global 48 | Environment. If you are running user code, and want to provide 49 | getattr, write one that only allows access to approved data types: 50 | 51 | def safegetattr(obj, attr): 52 | if isinstance(obj, some_class): 53 | return getattr(obj, attr) 54 | raise TypeError("getattr only supports objects of type %r" % some_class) 55 | 56 | or similar. 57 | 58 | Differences from r5rs scheme 59 | --------- 60 | 61 | Macro expansion is mixed with code execution. Normally macro 62 | expansion would be done at compile time, but 63 | for simplicity each statement is expanded right before execution. By 64 | itself, the only effect this has is on performance. 65 | 66 | Macros are first-class objects. Normally, macros and normal 67 | procedures are essentially the same, except that macros' names are 68 | listed in a MacroTable, while procedures are listed in the normal 69 | variable table. I don't maintain a separate list of macros, so an 70 | object being a macro is recorded on the object itself. Normally, this 71 | isn't noticable, as any code which wouldn't generate errors in racket 72 | should produce the same output (This is done by making 73 | define-syntax take either a macro or a procedure and wrap it in a 74 | macro, which calls the wrapped object with the syntax and expects the 75 | return type to be syntax), but it does open the door to some things 76 | which scheme users won't expect. 77 | 78 | (define some-macro #f) 79 | (define (somefun) 80 | (define-syntax junk (lambda (x) #'(+ 1 2))) 81 | (set! some-macro junk) 82 | ) 83 | (somefun) (some-macro) 84 | ;3 85 | 86 | 87 | Tail recursion and general Tail-Call-Optimisation 88 | --------- 89 | 90 | Tail recursion is not handled differently from other tail calls; but, 91 | TCO is partially supported. Some calls recursively call process(), 92 | which breaks TCO, but most calls are properly TC optimised. 93 | 94 | 95 | 96 | Booleans 97 | --------- 98 | 99 | Truth values follow python's convention rather than scheme's (0, 100 | False, None, (), and '' are false, or anything which provides a 101 | __bool__ method which returns False). If you need scheme's behaviour, 102 | simply rewrite eq? and what not to check for identity against False. 103 | -------------------------------------------------------------------------------- /scheme/symbol.py: -------------------------------------------------------------------------------- 1 | # cython: profile=True 2 | 3 | from scheme import debug 4 | from scheme.Globals import Globals 5 | import re 6 | 7 | 8 | # noinspection PyAttributeOutsideInit 9 | class Symbol(unicode): 10 | line=0 11 | def setLine(self, line): 12 | self.line=line 13 | return self 14 | def toObject(self, env): 15 | if hasattr(self, 'cache'): 16 | ret = self.cache 17 | del (self.cache) 18 | return ret 19 | if len(self) > 0: 20 | if self[0] == self[-1] == '"': 21 | return self[1:-1] 22 | if '.' in self and (not self.replace('-', '').replace('.', '').replace('e', '').isdigit() 23 | and 'lambda:' not in self and '.' != self): 24 | lst = self.split('.') 25 | val = Symbol(lst[0]).toObject(env) 26 | for i in lst[1:]: 27 | val = Symbol('getattr').toObject(env)(val, i) 28 | return val 29 | 30 | if '[' in self: 31 | m = re.match('^(.+?)\[(.+?)\]$', self) 32 | if m is not None: 33 | obj, key = m.groups() 34 | val = Symbol('getitem').toObject(env)(Symbol(obj).toObject(env), Symbol(key).toObject(env)) 35 | return val 36 | while env is not None: 37 | if unicode(self) in env: 38 | return env[self] 39 | if hasattr(env, 'parent'): 40 | env = env.parent 41 | else: 42 | env = None 43 | if self.lstrip('-').isdigit(): 44 | return int(self) 45 | if self.replace('-', '').replace('.', '').replace('e', '').isdigit() and not self.startswith('e'): 46 | return float(self) 47 | 48 | if self == '#t': 49 | return True 50 | if self == '#f': 51 | return False 52 | try: 53 | return complex(self.replace('i', 'j', 1)) 54 | except: 55 | raise NameError(u"Symbol '%s' undefined" % self) 56 | def isBound(self, env, cache=True): 57 | try: 58 | if self.lstrip('-').isdigit(): 59 | return True 60 | if self.replace('-', '').replace('.', '').replace('e', '').isdigit() and not self.startswith('e'): 61 | return True 62 | if self == '#t': 63 | return True 64 | if self == '#f': 65 | return True 66 | try: 67 | complex(self.replace('i', 'j', 1)) 68 | return True 69 | except ValueError: 70 | pass 71 | if cache: 72 | self.cache = self.toObject(env) 73 | return True 74 | except NameError: 75 | return False 76 | except AttributeError: 77 | return False 78 | def getEnv(self, env): 79 | while env is not None: 80 | if unicode(self) in env: 81 | return env 82 | env = env.parent 83 | if self.lstrip('-').isdigit() or self.lstrip('-').replace('.', '').isdigit() or self[0] == self[-1] == '"' or \ 84 | self == '#t' or self == '#f': 85 | return Globals 86 | raise NameError(u"Symbol '%s' undefined in enclosing environments" % self) 87 | def __repr__(self): 88 | if debug.getDebug('symbols'): 89 | return '' % (self, self.line) 90 | return str(self) 91 | def __bool__(self): 92 | if self.isBound(None): 93 | return bool(self.toObject(Globals)) 94 | return True 95 | def __eq__(self, other): 96 | if not isinstance(other, (unicode, str)) and self.isBound(Globals, True): 97 | return self.toObject(Globals) == other 98 | return unicode.__eq__(self, other) 99 | def __add__(self, other): 100 | if (isinstance(other, Symbol)): 101 | return self.toObject(Globals) + other.toObject(Globals) 102 | if self.isBound(None): 103 | return self.toObject(None) + other 104 | return str(self) + other 105 | 106 | -------------------------------------------------------------------------------- /scheme/symbol.pyx: -------------------------------------------------------------------------------- 1 | # cython: profile=True 2 | 3 | from scheme import debug 4 | from scheme.Globals import Globals 5 | import re 6 | 7 | 8 | # noinspection PyAttributeOutsideInit 9 | class Symbol(unicode): 10 | line=0 11 | def setLine(self, line): 12 | self.line=line 13 | return self 14 | def toObject(self, env): 15 | if hasattr(self, 'cache'): 16 | ret = self.cache 17 | del (self.cache) 18 | return ret 19 | if len(self) > 0: 20 | if self[0] == self[-1] == '"': 21 | return self[1:-1] 22 | if '.' in self and (not self.replace('-', '').replace('.', '').replace('e', '').isdigit() 23 | and 'lambda:' not in self and '.' != self): 24 | lst = self.split('.') 25 | val = Symbol(lst[0]).toObject(env) 26 | for i in lst[1:]: 27 | val = Symbol('getattr').toObject(env)(val, i) 28 | return val 29 | 30 | if '[' in self: 31 | m = re.match('^(.+?)\[(.+?)\]$', self) 32 | if m is not None: 33 | obj, key = m.groups() 34 | val = Symbol('getitem').toObject(env)(Symbol(obj).toObject(env), Symbol(key).toObject(env)) 35 | return val 36 | while env is not None: 37 | if unicode(self) in env: 38 | return env[self] 39 | if hasattr(env, 'parent'): 40 | env = env.parent 41 | else: 42 | env = None 43 | if self.lstrip('-').isdigit(): 44 | return int(self) 45 | if self.replace('-', '').replace('.', '').replace('e', '').isdigit() and not self.startswith('e'): 46 | return float(self) 47 | 48 | if self == '#t': 49 | return True 50 | if self == '#f': 51 | return False 52 | try: 53 | return complex(self.replace('i', 'j', 1)) 54 | except: 55 | raise NameError(u"Symbol '%s' undefined" % self) 56 | def isBound(self, env, cache=True): 57 | try: 58 | if self.lstrip('-').isdigit(): 59 | return True 60 | if self.replace('-', '').replace('.', '').replace('e', '').isdigit() and not self.startswith('e'): 61 | return True 62 | if self == '#t': 63 | return True 64 | if self == '#f': 65 | return True 66 | try: 67 | complex(self.replace('i', 'j', 1)) 68 | return True 69 | except ValueError: 70 | pass 71 | if cache: 72 | self.cache = self.toObject(env) 73 | return True 74 | except NameError: 75 | return False 76 | except AttributeError: 77 | return False 78 | def getEnv(self, env): 79 | while env is not None: 80 | if unicode(self) in env: 81 | return env 82 | env = env.parent 83 | if self.lstrip('-').isdigit() or self.lstrip('-').replace('.', '').isdigit() or self[0] == self[-1] == '"' or \ 84 | self == '#t' or self == '#f': 85 | return Globals 86 | raise NameError(u"Symbol '%s' undefined in enclosing environments" % self) 87 | def __repr__(self): 88 | if debug.getDebug('symbols'): 89 | return '' % (self, self.line) 90 | return str(self) 91 | def __bool__(self): 92 | if self.isBound(None): 93 | return bool(self.toObject(Globals)) 94 | return True 95 | def __eq__(self, other): 96 | if not isinstance(other, (unicode, str)) and self.isBound(Globals, True): 97 | return self.toObject(Globals) == other 98 | return unicode.__eq__(self, other) 99 | def __add__(self, other): 100 | if (isinstance(other, Symbol)): 101 | return self.toObject(Globals) + other.toObject(Globals) 102 | if self.isBound(None): 103 | return self.toObject(None) + other 104 | return str(self) + other 105 | 106 | -------------------------------------------------------------------------------- /scheme/macro.py: -------------------------------------------------------------------------------- 1 | from zope.interface import providedBy 2 | 3 | from scheme.debug import LOG 4 | from scheme.procedure import Procedure 5 | from scheme.utils import copy_with_quasiquote, deepcopy 6 | import scheme 7 | 8 | __author__ = 'perkins' 9 | 10 | from zope import interface 11 | from scheme.symbol import Symbol 12 | from scheme.environment import Environment 13 | import scheme 14 | 15 | 16 | 17 | # noinspection PyUnusedLocal,PyMethodParameters 18 | class Macro(interface.Interface): 19 | def __init__(ast, env): 20 | """""" 21 | 22 | def __call__(processer, params): 23 | """""" 24 | 25 | 26 | class MacroSymbol(Symbol): 27 | def getEnv(self, env): 28 | if hasattr(self, 'env'): 29 | return self.env 30 | raise NameError("MacroSymbol has no associated environment") 31 | def setObj(self, obj): 32 | # noinspection PyAttributeOutsideInit 33 | self.obj = obj 34 | return self 35 | def toObject(self, env): 36 | # self.env.parent=env if env is not None else scheme.Globals.Globals 37 | if hasattr(self, 'obj'): 38 | return self.obj 39 | e = env 40 | while e is not None: 41 | if e is self.env: 42 | return Symbol.toObject(self, env) 43 | if hasattr(e, 'parent'): 44 | e = e.parent 45 | else: 46 | e = None 47 | return Symbol.toObject(self, self.env) 48 | def setEnv(self, env): 49 | # noinspection PyAttributeOutsideInit 50 | self.env = Environment(env) 51 | if not hasattr(self.env.parent, 'parent'): 52 | self.env.parent = Environment(scheme.Globals.Globals, env) 53 | # self.env.parent = env.parent if env is not None and hasattr(env, 'parent') else scheme.Globals.Globals 54 | return self 55 | def __call__(self, *args): 56 | from jit import isaProcedure 57 | f = self.toObject({}) 58 | if args and isinstance(args[0], scheme.processer.Processer): 59 | processer,ast=args 60 | else: 61 | processer=scheme.processer.current_processer 62 | ast=list(args) 63 | if isaProcedure(f): 64 | return f(processer, ast) 65 | return f(*ast) 66 | 67 | 68 | class SimpleMacro(object): 69 | interface.implements(Macro) 70 | @classmethod 71 | def wrappedMacro(cls, proc, env): 72 | while (isinstance(proc, Symbol)): 73 | proc = proc.toObject(env) 74 | pb = providedBy(proc) 75 | if Macro in pb: 76 | return proc 77 | if Procedure in pb: 78 | return cls(None, env, proc).setName(proc.name) 79 | # return cls(proc.ast, proc.env).setName(proc.name) 80 | return cls(None, env, proc) 81 | def __init__(self, ast, env, wrapped=None): 82 | self.ast = ast 83 | self.env = env 84 | self.name = None 85 | self.wrapped = wrapped 86 | # noinspection PyUnusedLocal 87 | def __call__(self, processer, args): 88 | if self.wrapped: 89 | if Procedure in providedBy(self.wrapped): 90 | return self.wrapped(processer, args) 91 | return self.wrapped(args) 92 | retval = None 93 | env = Environment(self.env) 94 | if (isinstance(self.ast[0], list)): 95 | # if len(self.ast[0])==1: 96 | # env[self.ast[0][0]] = [Symbol('quote'), args] 97 | if '.' in self.ast[0]: 98 | idx = -1 99 | item = None 100 | for idx, item in enumerate(self.ast[0][:-2]): 101 | i = args[idx] 102 | env[item] = i 103 | env[self.ast[0][-1]] = args[idx + 1:] 104 | else: 105 | if len(self.ast[0]) != len(args): 106 | raise SyntaxError( 107 | "Macro %r requires exactly %i args, %i given" % (self, len(self.ast[0]), len(args))) 108 | for idx, item in enumerate(self.ast[0]): 109 | i = args[idx] 110 | env[item] = i 111 | else: 112 | env[self.ast[0]] = [Symbol('quote'), args] 113 | o = [] 114 | retval = copy_with_quasiquote(processer, env, deepcopy(self.ast[1:]), o_stack=o)[0] 115 | LOG("macro", retval) 116 | retval = processer.process(retval, processer.cenv) 117 | processer.popStack(retval) 118 | return 119 | def setName(self, name): 120 | self.name = name 121 | return self 122 | def __repr__(self): 123 | if self.name: 124 | return '' % self.name 125 | return object.__repr__(self) 126 | -------------------------------------------------------------------------------- /scheme/define.py: -------------------------------------------------------------------------------- 1 | from zope.interface import implements 2 | 3 | from scheme.macro import Macro, SimpleMacro 4 | from scheme.procedure import SimpleProcedure 5 | from processer import Globals 6 | from scheme.symbol import Symbol 7 | from scheme import jit 8 | 9 | class define(object): 10 | implements(Macro) 11 | def __init__(self): 12 | pass 13 | def __call__(self, processer, params): 14 | if not isinstance(params[0], list): 15 | env = processer.cenv.parent 16 | name = params[0] 17 | if not isinstance(name, Symbol): 18 | name = processer.callStack.queue[-1][0][0][1] 19 | processer.stackPointer=2 20 | initialCallDepth = processer.callDepth 21 | processer.pushStack(params[1:]) 22 | value = processer.process(params[1:], env) 23 | while initialCallDepth < processer.callDepth: 24 | processer.popStack(value) 25 | env[name] = value 26 | if processer.callDepth: 27 | processer.popStack(value) 28 | else: 29 | name = params[0][0] 30 | args = params[0][1:] 31 | rest = params[1:] 32 | env = processer.cenv.parent if processer.cenv is not Globals.Globals else Globals.Globals 33 | if isinstance(name, list): 34 | x = name 35 | o = [] 36 | while isinstance(x, list): 37 | o.append(x) 38 | x = x[0] 39 | name = o[-1] 40 | retval = [Symbol('define'), name, [Symbol('lambda'), args, [Symbol('begin')] + rest]] 41 | processer.process(retval, env) 42 | else: 43 | env[name] = SimpleProcedure([args] + rest, env).setName(name) 44 | if jit.enabled: 45 | f = jit.makeFunction(env[name]) 46 | if f: 47 | env[name]=f 48 | processer.popStack(None) 49 | # processer.ast[processer.stackPointer]=None 50 | processer.stackPointer += 1 51 | return None 52 | 53 | 54 | class Set(object): 55 | implements(Macro) 56 | def __init__(self): 57 | pass 58 | def __call__(self, processer, params): 59 | if not isinstance(params[0], list): 60 | env = processer.cenv.parent 61 | name = params[0] 62 | initialCallDepth=processer.callDepth 63 | processer.pushStack(params[1:]) 64 | processer.stackPointer = 2 65 | value = processer.process(params[1:], env) 66 | while initialCallDepth < processer.callDepth: 67 | processer.popStackN() 68 | processer.ast[2] = value 69 | processer.ast[0] = Symbol('set!') 70 | if not name.isBound(env): 71 | raise NameError("Name %s unbound in enclosing namespaces" % name) 72 | name.getEnv(env)[name] = value 73 | else: 74 | name = params[0][0] 75 | args = params[0][1:] 76 | rest = params[1:] 77 | env = processer.cenv.parent if processer.cenv is not Globals.Globals else Globals.Globals 78 | if isinstance(name, list): 79 | x = name 80 | o = [] 81 | while isinstance(x, list): 82 | o.append(x) 83 | x = x[0] 84 | name = x 85 | if not name.isBound(env): 86 | raise NameError("Name %s unbound in enclosing namespaces" % name) 87 | retval = [Symbol('define'), o[-1], [Symbol('lambda'), args, [Symbol('begin')] + rest]] 88 | 89 | processer.process(retval, env) 90 | else: 91 | if not name.isBound(env): 92 | raise NameError("Name %s unbound in enclosing namespaces" % name) 93 | env[name] = SimpleProcedure([args] + rest, env).setName(name) 94 | if jit.enabled: 95 | f = jit.makeFunction(env[name]) 96 | if f: 97 | env[name]=f 98 | processer.popStack(None) 99 | # processer.ast[processer.stackPointer]=None 100 | processer.stackPointer += 1 101 | return None 102 | 103 | 104 | class defmacro(object): 105 | implements(Macro) 106 | def __init__(self): 107 | pass 108 | def __call__(self, processer, params): 109 | if len(params) == 2 and not isinstance(params[0], list) and ( 110 | (isinstance(params[1], list) and params[1][0] == 'lambda') or not (isinstance(params[1], list))): 111 | name = params[0] 112 | env = processer.cenv.parent if processer.cenv is not Globals.Globals else Globals.Globals 113 | proc = processer.process([params[1]], env) 114 | env[name] = SimpleMacro.wrappedMacro(proc, env) 115 | 116 | else: 117 | name = params[0][0] 118 | args = params[0][1:] 119 | rest = params[1:] 120 | env = processer.cenv.parent if processer.cenv is not Globals.Globals else Globals.Globals 121 | env[name] = SimpleMacro([args] + rest, env).setName(name) 122 | # processer.ast[processer.stackPointer]=None 123 | processer.popStack(None) 124 | processer.stackPointer += 1 125 | return None 126 | 127 | 128 | Globals.Globals['define'] = define() 129 | Globals.Globals['set!'] = Set() 130 | Globals.Globals['define-macro'] = Globals.Globals['defmacro'] = defmacro() 131 | -------------------------------------------------------------------------------- /lispytest.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python2.7 2 | # Cribbed from Peter Norvig's scheme interpreter in python, http://norvig.com/lispy2.html 3 | 4 | 5 | # ############### Tests for lis.py and lispy.py 6 | import scheme.symbol 7 | import scheme.procedure 8 | 9 | 10 | lis_tests = [ 11 | ("(quote (testing 1 (2.0) -3.14e159))", ['testing', '1', ['2.0'], '-3.14e159']), 12 | ("(+ 2 2)", 4), 13 | ("(+ (* 2 100) (* 1 10))", 210), 14 | ("(if (> 6 5) (+ 1 1) (+ 2 2))", 2), 15 | ("(if (< 6 5) (+ 1 1) (+ 2 2))", 4), 16 | ("(define x 3)", None), ("x", 3), ("(+ x x)", 6), 17 | ("(begin (define x 1) (set! x (+ x 1)) (+ x 1))", 3), 18 | ("((lambda (x) (+ x x)) 5)", 10), 19 | ("(define twice (lambda (x) (* 2 x)))", None), ("(twice 5)", 10), 20 | ("(define compose (lambda (f g) (lambda (x) (f (g x)))))", None), 21 | ("((compose list twice) 5)", [10]), 22 | ("(define repeat (lambda (f) (compose f f)))", None), 23 | ("((repeat twice) 5)", 20), ("((repeat (repeat twice)) 5)", 80), 24 | ("(define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))", None), 25 | ("(fact 3)", 6), 26 | ("(fact 50)", 30414093201713378043612608166064768844377641568960512000000000000), 27 | ("(define abs (lambda (n) ((if (> n 0) + -) 0 n)))", None), 28 | ("(list (abs -3) (abs 0) (abs 3))", [3, 0, 3]), 29 | ("""(define combine (lambda (f) 30 | (lambda (x y) 31 | (if (null? x) (quote ()) 32 | (f (list (car x) (car y)) 33 | ((combine f) (cdr x) (cdr y)))))))""", None), 34 | ("(define zip (combine cons))", None), 35 | ("(zip (list 1 2 3 4) (list 5 6 7 8))", [[1, 5], [2, 6], [3, 7], [4, 8]]), 36 | ("""(define riff-shuffle (lambda (deck) (begin 37 | (define take (lambda (n seq) (if (<= n 0) (quote ()) (cons (car seq) (take (- n 1) (cdr seq)))))) 38 | (define drop (lambda (n seq) (if (<= n 0) seq (drop (- n 1) (cdr seq))))) 39 | (define mid (lambda (seq) (/ (length seq) 2))) 40 | ((combine append) (take (mid deck) deck) (drop (mid deck) deck)))))""", None), 41 | ("(riff-shuffle (list 1 2 3 4 5 6 7 8))", [1, 5, 2, 6, 3, 7, 4, 8]), 42 | ("((repeat riff-shuffle) (list 1 2 3 4 5 6 7 8))", [1, 3, 5, 7, 2, 4, 6, 8]), 43 | ("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1, 2, 3, 4, 5, 6, 7, 8]), 44 | ("(and (begin 0) 6)", 0), 45 | ] 46 | 47 | lispy_tests = [ 48 | ("()", SyntaxError), 49 | ("(set! x)", IndexError), 50 | ("(define 3 4)", None), 51 | ("(define 3 (- 3 1))", None), 52 | ("(quote 1 2)", SyntaxError), 53 | ("(if 1 2 3 4)", SyntaxError), 54 | ("(lambda (x))", scheme.procedure.SimpleProcedure), 55 | ("""(if (= 1 1) (define-macro (a b) a) 56 | (define-macro a (quote b)))""", None), 57 | ("(define (twice x) (* 2 x))", None), 58 | ("(twice 2)", 4), 59 | ("(twice 2 2)", TypeError), 60 | ("(define lyst (lambda items items))", None), 61 | ("(lyst 1 2 3 (+ 2 2))", [1, 2, 3, 4]), 62 | ("(if 1 2)", 2), 63 | ("(if (= 3 4) 2)", False), 64 | ("(define ((account bal) amt) (set! bal (+ bal amt)) bal)", None), 65 | ("(define a1 (account 100))", None), 66 | ("(a1 0)", 100), ("(a1 10)", 110), ("(a1 10)", 120), 67 | ("""(define (newton guess function derivative epsilon) 68 | (define guess2 (- guess (/ (function guess) (derivative guess)))) 69 | (if (< (abs (- guess guess2)) epsilon) guess2 70 | (newton guess2 function derivative epsilon)))""", None), 71 | ("""(define (square-root a) 72 | (newton 1 (lambda (x) (- (* x x) a)) (lambda (x) (* 2 x)) 1e-8))""", None), 73 | ("(> (square-root 200.) 14.14213)", True), 74 | ("(< (square-root 200.) 14.14215)", True), 75 | ("(= (square-root 200.) (sqrt 200.))", True), 76 | ("""(define (sum-squares-range start end) 77 | (define (sumsq-acc start end acc) 78 | (if (> start end) acc (sumsq-acc (+ start 1) end (+ (* start start) acc)))) 79 | (sumsq-acc start end 0))""", None), 80 | 81 | ("(call/cc (lambda (throw) (+ 5 (* (throw 1) 10 )))) ;; throw", 1), 82 | # ("(sum-squares-range 1 3000)", 9004500500), ## Tests tail recursion 83 | ("(call/cc (lambda (throw) (+ 5 (* 10 1)))) ;; do not throw", 15), 84 | ("""(call/cc (lambda (throw) 85 | (+ 5 (* 10 (call/cc (lambda (escape) (* 100 (escape 3)))))))) ; 1 level""", 35), 86 | ("""(call/cc (lambda (throw) 87 | (+ 5 (* 10 (call/cc (lambda (escape) (* 100 (throw 3)))))))) ; 2 levels""", 3), 88 | ("""(call/cc (lambda (throw) 89 | (+ 5 (* 10 (call/cc (lambda (escape) (* 100 1))))))) ; 0 levels""", 1005), 90 | ("(* 1i 1i)", -1), ("(sqrt -1)", 1j), 91 | ("(let ((a 1) (b 2)) (+ a b))", 3), 92 | ("(let ((a 1) (b 2 3)) (+ a b))", SyntaxError), 93 | ("(and 1 2 3)", 3), ("(and (> 2 1) 2 3)", 3), ("(and)", True), 94 | ("(and (> 2 1) (> 2 3))", False), 95 | ( 96 | "(define-macro (unless . args) (display 'xyzzy) (display args) " 97 | "(quasiquote (if (not ,(car args)) (begin ,@(cdr args))))) ; test `", 98 | None), 99 | ("(unless (= 2 (+ 1 1)) (display 2) 3 4)", False), 100 | (r'(unless (= 4 (+ 1 1)) (display 2) (display "\n") 3 4)', 4), 101 | ("(quote x)", 'x'), 102 | ("(quote (1 2 three))", ['1', '2', 'three']), 103 | ("'x", 'x'), 104 | ("'(one 2 3)", ['one', '2', '3']), 105 | ("(define L (list 1 2 3))", None), 106 | ("`(testing ,@L testing)", ['testing', 1, 2, 3, 'testing']), 107 | ("`(testing ,L testing)", ['testing', [1, 2, 3], 'testing']), 108 | ("`,@L", IndexError), 109 | ("""'(1 ;test comments ' 110 | ;skip this line 111 | 2 ; more ; comments ; ) ) 112 | 3) ; final comment""", ['1', '2', '3']), 113 | ('''(defmacro (for x in l . calls) (for-each (lambda (,x) ,@calls) ,l))''', None), 114 | ('''(define tst (lambda (x) (syntax (+ 1 2))))''', None), 115 | ('''(tst 5)''', ['+','1','2']), 116 | ('''(define-syntax tst (lambda (x) (syntax (+ 1 2))))''', None), 117 | ('''(tst 5)''', 3), 118 | ('''(define-syntax when 119 | (lambda (x) 120 | (syntax-case x () 121 | ((_ test e e* ...) 122 | (syntax (if test (begin e e* ...)))))))''', None), 123 | ('''(when (< 0 5) "true")''', "true"), 124 | ('''(when (> 0 5) "true")''', False), 125 | ('''(let-syntax ((s (lambda (x) #'(+ 1 2)))) (+ 5 (s)))''', 8), 126 | # ('''(for x in '(1 2 3) x)''', 3) 127 | ] 128 | 129 | 130 | def test(tests, name=''): 131 | import scheme.Globals 132 | "For each (exp, expected) test case, see if eval(parse(exp)) == expected." 133 | fails = 0 134 | for (x, expected) in tests: 135 | try: 136 | result = Eval(x) 137 | if isinstance(result, scheme.symbol.Symbol): 138 | if result.isBound(scheme.Globals.Globals): 139 | result = result.toObject(scheme.Globals.Globals) 140 | print x, '=>', repr(result) 141 | ok = (result == expected) or expected is None 142 | if not ok and type(expected) == type: 143 | if isinstance(result, expected): 144 | ok = True 145 | except Exception as e: 146 | p.dumpStack() 147 | print x, '=raises=>', type(e).__name__, e 148 | ok = type(expected) == type and issubclass(expected, Exception) and isinstance(e, expected) 149 | if not ok: 150 | fails += 1 151 | print 'FAIL!!! Expected', expected 152 | print '%s %s: %d out of %d tests fail.' % ('*' * 45, name, fails, len(tests)) 153 | 154 | 155 | if __name__ == '__main__': 156 | from scheme.eval import Eval, p 157 | test(lis_tests + lispy_tests, 'lispy.py') 158 | -------------------------------------------------------------------------------- /scheme/PatternMatcher.py: -------------------------------------------------------------------------------- 1 | from __future__ import division, unicode_literals 2 | from scheme.environment import SyntaxEnvironment 3 | import scheme.debug 4 | 5 | __author__ = 'perkins' 6 | 7 | O=[] 8 | 9 | class PatternMatcher(object): 10 | def __eq__(self, other): 11 | """ 12 | 13 | :param other: One of ['.' sym] [sym '...'] [sym] 14 | :return: boolean 15 | """ 16 | return self.pattern == other 17 | def setValue(self, val): 18 | self.value = val 19 | return self 20 | def __repr__(self): 21 | return '' % (self.pattern, self.literals, self.dot, self.ellipsis) 22 | def __init__(self, pattern, literals, dot=False, ellipsis=False): 23 | self.pattern = pattern 24 | self.literals = literals 25 | self.ellipsis = ellipsis 26 | self.dot = dot 27 | self.idx = 0 28 | self.value = None 29 | def __iter__(self): 30 | ei_pattern = enumerate(iter(self.pattern)) 31 | for self.idx, i in ei_pattern: 32 | if isinstance(i, list): 33 | if len(self.pattern) > self.idx + 1 and self.pattern[self.idx + 1] == '...': 34 | while True: 35 | move_on = yield PatternMatcher(i, self.literals, False, True) 36 | if move_on: 37 | yield "OK" 38 | self.idx, i = ei_pattern.next() 39 | break 40 | else: 41 | yield PatternMatcher(i, self.literals) 42 | elif i == '.': 43 | self.idx, i = ei_pattern.next() 44 | if isinstance(i, list): 45 | raise SyntaxError(". must be followed by a name") 46 | yield PatternMatcher(i, self.literals, True) 47 | elif len(self.pattern) > self.idx + 1 and self.pattern[self.idx + 1] == '...': 48 | yield PatternMatcher(i, self.literals, False, True) 49 | self.idx, i = ei_pattern.next() 50 | else: 51 | yield PatternMatcher(i, self.literals) 52 | def match(self, params): 53 | try: 54 | return self.__match([params]) 55 | except SyntaxError: 56 | pass 57 | except TypeError: 58 | pass 59 | except AttributeError: 60 | pass 61 | if scheme.debug.getDebug('patternMatcher'): 62 | import traceback 63 | traceback.print_exc() 64 | def __match(self, params): 65 | """ 66 | Literals match literals 67 | Lists match lists (recursive invocation) 68 | Pattern tokens match lists or symbols 69 | ... makes the previous element match 0 or more times 70 | ... continues to consume elements from pattern until a non-matching param is encountered 71 | . makes the following element consume all following params 72 | :param params: [Symbol] 73 | :return: dictionary of pattern variables or False if not a matching pattern 74 | """ 75 | o = SyntaxEnvironment() 76 | O.append(o) 77 | if not isinstance(self.pattern, list): # we're a single element 78 | if self.dot: 79 | if not params: 80 | o[self] = [] 81 | else: 82 | o[self] = [params[0]] + params[1] 83 | params.pop() 84 | params.pop() 85 | return o 86 | elif self.ellipsis: 87 | o[self] = [params[0]] + params[1] 88 | params.pop() 89 | params.pop() 90 | return o 91 | else: 92 | if not params: 93 | ''' 94 | We need a symbol or a list 95 | ''' 96 | raise SyntaxError() 97 | if self.pattern in self.literals: 98 | if params[0] != self.pattern: 99 | raise SyntaxError() 100 | return SyntaxEnvironment() 101 | if isinstance(params, list): 102 | r = SyntaxEnvironment({self: params[0]}) 103 | return r 104 | else: 105 | return SyntaxEnvironment({self: params}) 106 | else: 107 | if not params: 108 | raise SyntaxError() 109 | params = params[0][:] 110 | ipattern = iter(self) 111 | for patternElement in ipattern: 112 | if patternElement.ellipsis: 113 | # This element needs to match 0+ sub-elements of params (non-greedy), 114 | # so we work backward from the right till this is the last sub-pattern 115 | reversed_pattern = self.pattern[self.idx + 2:] 116 | rpm = PatternMatcher(reversed_pattern, self.literals) 117 | reversed_pattern = list(rpm) 118 | reversed_pattern.reverse() 119 | for rpatternElement in reversed_pattern: 120 | if rpatternElement.ellipsis: 121 | raise SyntaxError() 122 | if rpatternElement.dot: 123 | o[rpatternElement] = [] 124 | continue 125 | if params: 126 | paramElement = [params.pop(-1)] 127 | else: 128 | paramElement = [] 129 | v = rpatternElement.__match(paramElement) 130 | o.update(v) 131 | # We've matched all the params to the right 132 | # without error, whatever's left is given to this pattern 133 | if not params: 134 | o.update({patternElement: params}) 135 | try: 136 | x = ipattern.send(1) 137 | except StopIteration: 138 | x = 'OK' 139 | if x != 'OK': 140 | raise SyntaxError(x) 141 | break 142 | if isinstance(patternElement.pattern, list): 143 | matches = [] 144 | while True: 145 | if params: 146 | lastParam = False 147 | paramElement = [params.pop(0)] 148 | else: 149 | lastParam = True 150 | paramElement = [] 151 | try: 152 | matches.append(patternElement.__match(paramElement)) 153 | except SyntaxError: 154 | if not lastParam: 155 | params.insert(0, paramElement) 156 | try: 157 | ipattern.send(True) 158 | except StopIteration: 159 | break 160 | break; 161 | 162 | o.update({patternElement: matches}) 163 | break; 164 | else: 165 | l = [params.pop(0), params] 166 | r = patternElement.__match(l) 167 | if not l: 168 | params = [] 169 | o.update(r) 170 | 171 | break 172 | if params: 173 | if isinstance(params, list): 174 | paramElement = [params.pop(0), params] 175 | else: 176 | paramElement=[params] 177 | params=[] 178 | else: 179 | paramElement = [] 180 | o.update(patternElement.__match(paramElement)) 181 | if not paramElement: 182 | params = [] 183 | if params: 184 | raise SyntaxError() 185 | return o 186 | -------------------------------------------------------------------------------- /scheme/tests/lispytest.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python2.7 2 | # Cribbed from Peter Norvig's scheme interpreter in python, http://norvig.com/lispy2.html 3 | 4 | 5 | # ############### Tests for lis.py and lispy.py 6 | import scheme.symbol 7 | import scheme.procedure 8 | import types 9 | 10 | 11 | lis_tests = [ 12 | ("(quote (testing 1 (2.0) -3.14e159))", ['testing', '1', ['2.0'], '-3.14e159']), 13 | ("(+ 2 2)", 4), 14 | ("(+ (* 2 100) (* 1 10))", 210), 15 | ("(if (> 6 5) (+ 1 1) (+ 2 2))", 2), 16 | ("(if (< 6 5) (+ 1 1) (+ 2 2))", 4), 17 | ("(define x 3)", None), ("x", 3), ("(+ x x)", 6), 18 | ("(begin (define x 1) (set! x (+ x 1)) (+ x 1))", 3), 19 | ("((lambda (x) (+ x x)) 5)", 10), 20 | ("(define twice (lambda (x) (* 2 x)))", None), ("(twice 5)", 10), 21 | ("(define compose (lambda (f g) (lambda (x) (f (g x)))))", None), 22 | ("((compose list twice) 5)", [10]), 23 | ("(define repeat (lambda (f) (compose f f)))", None), 24 | ("((repeat twice) 5)", 20), ("((repeat (repeat twice)) 5)", 80), 25 | ("(define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))", None), 26 | ("(fact 3)", 6), 27 | ("(fact 50)", 30414093201713378043612608166064768844377641568960512000000000000), 28 | ("(define abs (lambda (n) ((if (> n 0) + -) 0 n)))", None), 29 | ("(list (abs -3) (abs 0) (abs 3))", [3, 0, 3]), 30 | ("""(define combine (lambda (f) 31 | (lambda (x y) 32 | (if (null? x) (quote ()) 33 | (f (list (car x) (car y)) 34 | ((combine f) (cdr x) (cdr y)))))))""", None), 35 | ("(define zip (combine cons))", None), 36 | ("(zip (list 1 2 3 4) (list 5 6 7 8))", [[1, 5], [2, 6], [3, 7], [4, 8]]), 37 | ("""(define riff-shuffle (lambda (deck) (begin 38 | (define take (lambda (n seq) (if (<= n 0) (quote ()) (cons (car seq) (take (- n 1) (cdr seq)))))) 39 | (define drop (lambda (n seq) (if (<= n 0) seq (drop (- n 1) (cdr seq))))) 40 | (define mid (lambda (seq) (/ (length seq) 2))) 41 | ((combine append) (take (mid deck) deck) (drop (mid deck) deck)))))""", None), 42 | ("(riff-shuffle (list 1 2 3 4 5 6 7 8))", [1, 5, 2, 6, 3, 7, 4, 8]), 43 | ("((repeat riff-shuffle) (list 1 2 3 4 5 6 7 8))", [1, 3, 5, 7, 2, 4, 6, 8]), 44 | ("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1, 2, 3, 4, 5, 6, 7, 8]), 45 | ("(and (begin 0) 6)", 0), 46 | ] 47 | 48 | lispy_tests = [ 49 | ("()", SyntaxError), 50 | ("(set! x)", IndexError), 51 | ("(define 3 4)", None), 52 | ("(define 3 (- 3 1))", None), 53 | ("(quote 1 2)", SyntaxError), 54 | ("(if 1 2 3 4)", SyntaxError), 55 | ("(lambda (x))", scheme.procedure.SimpleProcedure), 56 | ("""(if (= 1 1) (define-macro (a b) a) 57 | (define-macro a (quote b)))""", None), 58 | ("(define (twice x) (* 2 x))", None), 59 | ("(twice 2)", 4), 60 | ("(twice 2 2)", TypeError), 61 | ("(define lyst (lambda items items))", None), 62 | ("(lyst 1 2 3 (+ 2 2))", [1, 2, 3, 4]), 63 | ("(if 1 2)", 2), 64 | ("(if (= 3 4) 2)", False), 65 | ("(define ((account bal) amt) (set! bal (+ bal amt)) bal)", None), 66 | ("(define a1 (account 100))", None), 67 | ("(a1 0)", 100), ("(a1 10)", 110), ("(a1 10)", 120), 68 | ("""(define (newton guess function derivative epsilon) 69 | (define guess2 (- guess (/ (function guess) (derivative guess)))) 70 | (if (< (abs (- guess guess2)) epsilon) guess2 71 | (newton guess2 function derivative epsilon)))""", None), 72 | ("""(define (square-root a) 73 | (newton 1 (lambda (x) (- (* x x) a)) (lambda (x) (* 2 x)) 1e-8))""", None), 74 | ("(> (square-root 200.) 14.14213)", True), 75 | ("(< (square-root 200.) 14.14215)", True), 76 | ("(= (square-root 200.) (sqrt 200.))", True), 77 | ("""(define (sum-squares-range start end) 78 | (define (sumsq-acc start end acc) 79 | (if (> start end) acc (sumsq-acc (+ start 1) end (+ (* start start) acc)))) 80 | (sumsq-acc start end 0))""", None), 81 | 82 | ("(call/cc (lambda (throw) (+ 5 (* (throw 1) 10 )))) ;; throw", 1), 83 | # ("(sum-squares-range 1 3000)", 9004500500), ## Tests tail recursion 84 | ("(call/cc (lambda (throw) (+ 5 (* 10 1)))) ;; do not throw", 15), 85 | ("""(call/cc (lambda (throw) 86 | (+ 5 (* 10 (call/cc (lambda (escape) (* 100 (escape 3)))))))) ; 1 level""", 35), 87 | ("""(call/cc (lambda (throw) 88 | (+ 5 (* 10 (call/cc (lambda (escape) (* 100 (throw 3)))))))) ; 2 levels""", 3), 89 | ("""(call/cc (lambda (throw) 90 | (+ 5 (* 10 (call/cc (lambda (escape) (* 100 1))))))) ; 0 levels""", 1005), 91 | ("(* 1i 1i)", -1), ("(sqrt -1)", 1j), 92 | ("(let ((a 1) (b 2)) (+ a b))", 3), 93 | ("(let ((a 1) (b 2 3)) (+ a b))", SyntaxError), 94 | ("(and 1 2 3)", 3), ("(and (> 2 1) 2 3)", 3), ("(and)", True), 95 | ("(and (> 2 1) (> 2 3))", False), 96 | ( 97 | "(define-macro (unless . args) (display 'xyzzy) (display args) " 98 | "(quasiquote (if (not ,(car args)) (begin ,@(cdr args))))) ; test `", 99 | None), 100 | ("(unless (= 2 (+ 1 1)) (display 2) 3 4)", False), 101 | (r'(unless (= 4 (+ 1 1)) (display 2) (display "\n") 3 4)', 4), 102 | ("(quote x)", 'x'), 103 | ("(quote (1 2 three))", ['1', '2', 'three']), 104 | ("'x", 'x'), 105 | ("'(one 2 3)", ['one', '2', '3']), 106 | ("(define L (list 1 2 3))", None), 107 | ("`(testing ,@L testing)", ['testing', 1, 2, 3, 'testing']), 108 | ("`(testing ,L testing)", ['testing', [1, 2, 3], 'testing']), 109 | ("`,@L", IndexError), 110 | ("""'(1 ;test comments ' 111 | ;skip this line 112 | 2 ; more ; comments ; ) ) 113 | 3) ; final comment""", ['1', '2', '3']), 114 | ('''(defmacro (for x in l . calls) (for-each (lambda (,x) ,@calls) ,l))''', None), 115 | ('''(define tst (lambda (x) (syntax (+ 1 2))))''', None), 116 | ('''(tst 5)''', ['+','1','2']), 117 | ('''(define-syntax tst (lambda (x) (syntax (+ 1 2))))''', None), 118 | ('''(tst 5)''', 3), 119 | ('''(define-syntax when 120 | (lambda (x) 121 | (syntax-case x () 122 | ((_ test e e* ...) 123 | (syntax (if test (begin e e* ...)))))))''', None), 124 | ('''(when (< 0 5) "true")''', "true"), 125 | ('''(when (> 0 5) "true")''', False), 126 | # ('''(let-syntax ((s (lambda (x) #'(+ 1 2)))) (+ 5 (s)))''', 8), 127 | ('''(let ((a 4)) (+ a a))''', 8), 128 | ('''(let ((a (+ 5 6))) (+ a a))''', 22), 129 | ('''(let ((a (+ 5 6)) (b 11)) (+ a b) b)''', 11), 130 | ('''(for x in '(1 2 3) x)''', 3), 131 | ('''(let () 3)''', 3), 132 | ('''(let loop ((i 1)) i)''', 1), 133 | ('''(let loop ((i 1)) (if (= 10 i) #t (loop (+ i 1))))''', True), 134 | ] 135 | 136 | 137 | def test(tests, name=''): 138 | import scheme.Globals 139 | "For each (exp, expected) test case, see if eval(parse(exp)) == expected." 140 | fails = 0 141 | passes = 0 142 | for idx, (x, expected) in enumerate(tests): 143 | try: 144 | print idx, x, 145 | result = Eval(x) 146 | if isinstance(result, scheme.symbol.Symbol): 147 | if result.isBound(scheme.Globals.Globals): 148 | result = result.toObject(scheme.Globals.Globals) 149 | print '=>', repr(result) 150 | ok = (result == expected) or expected is None 151 | if not ok and type(expected) == type or type(expected) == types.ClassType: 152 | ok = isinstance(result, expected) 153 | if not ok and expected == scheme.procedure.SimpleProcedure: 154 | ok = isinstance(result, types.FunctionType) 155 | except Exception as e: 156 | p.dumpStack() 157 | print x, '=raises=>', type(e).__name__, e 158 | ok = type(expected) == type and issubclass(expected, Exception) and isinstance(e, expected) 159 | if not ok: 160 | fails += 1 161 | print 'FAIL!!! Expected', expected 162 | print '%s %s: %d out of %d tests fail.' % ('*' * 45, name, fails, len(tests)) 163 | 164 | 165 | if __name__ == '__main__': 166 | import sys 167 | if 'jit' in sys.argv: 168 | scheme.jit.enabled=True 169 | if 'lambdajit' not in sys.argv: 170 | scheme.jit.lambdas_enabled = False 171 | if 'unsafejit' in sys.argv: 172 | scheme.jit.unsafe_enabled = True 173 | from scheme.eval import Eval, p 174 | import time 175 | st = time.time() 176 | if sys.argv[-1].isdigit(): 177 | num = int(sys.argv[-1]) 178 | else: 179 | num=1 180 | for i in xrange(num): 181 | test(lis_tests + lispy_tests, 'lispy.py') 182 | et = time.time() 183 | print et-st 184 | 185 | -------------------------------------------------------------------------------- /scheme/utils.py: -------------------------------------------------------------------------------- 1 | from scheme.environment import Environment 2 | from scheme.symbol import Symbol 3 | 4 | 5 | class callCCBounce(Exception): 6 | pass 7 | 8 | 9 | def deepcopy(lst): 10 | if isinstance(lst, dict) and not isinstance(lst, Environment): 11 | d = {} 12 | for i in lst: 13 | d[i] = deepcopy(lst[i]) 14 | return d 15 | if isinstance(lst, tuple): 16 | lst = list(lst) 17 | if not isinstance(lst, list): 18 | return lst 19 | o = [] 20 | for i in lst: 21 | o.append(deepcopy(i)) 22 | return o 23 | 24 | 25 | def copy_with_replacement(lst, **vals): 26 | if not isinstance(lst, list): 27 | if isinstance(lst, Symbol) and lst in vals: 28 | return vals[lst] 29 | return lst 30 | o = [] 31 | for i in lst: 32 | o.append(copy_with_replacement(i, **vals)) 33 | return o 34 | 35 | 36 | def copy_with_quote(lst): 37 | from scheme.macro import MacroSymbol 38 | if not isinstance(lst, list): 39 | if isinstance(lst, Symbol): 40 | if lst.isBound(None): 41 | return MacroSymbol(lst).setEnv({lst: lst.toObject(None)}) 42 | return MacroSymbol(lst).setEnv({lst: lst}) 43 | return lst 44 | o = [] 45 | for i in lst: 46 | o.append(copy_with_quote(i)) 47 | return o 48 | 49 | def copy_with_quasisyntax(processer, env, lst, last_lst=None, last_idx=None, o_stack=None): 50 | from scheme.unquote import unsyntax 51 | from scheme.unquote_splicing import unsyntax_splicing 52 | from scheme.macro import MacroSymbol 53 | from scheme.syntax import SyntaxSymbol 54 | from scheme.Globals import Globals 55 | if not isinstance(lst, list): 56 | if isinstance(lst, Symbol): 57 | if last_idx == 0 and lst.isBound(Globals) and isinstance(lst.toObject(Globals), unsyntax): 58 | qq_target = last_lst.pop(last_idx + 1) 59 | retval = processer.process([qq_target], env) 60 | return retval, True 61 | if lst == '#,': 62 | qq_target = last_lst.pop(last_idx + 1) 63 | retval = processer.__class__(processer).process([qq_target], env) 64 | return retval, False 65 | if last_idx == 0 and lst.isBound(Globals) and isinstance(lst.toObject(Globals), unsyntax_splicing): 66 | qq_target = last_lst.pop(last_idx + 1) 67 | retval = processer.__class__(processer).process([qq_target], env) 68 | o_stack.extend(retval) 69 | return retval, 2 70 | if lst == '#,@': 71 | qq_target = last_lst.pop(last_idx + 1) 72 | 73 | retval = processer.__class__(processer).process([qq_target], env) 74 | return retval, 3 75 | return lst, False 76 | return SyntaxSymbol(lst).setSymbol(lst), False 77 | return lst, False 78 | o = [] 79 | o_stack.append(o) 80 | for idx, i in enumerate(lst): 81 | r = copy_with_quasisyntax(processer, env, i, lst, idx, o_stack) 82 | if r[1] == 3: 83 | o.extend(r[0]) 84 | continue 85 | if r[1] == 2: 86 | return r[0], 3 87 | elif r[1]: 88 | return r[0], False 89 | else: 90 | o.append(r[0]) 91 | return o, False 92 | 93 | 94 | def copy_with_quasiquote(processer, env, lst, last_lst=None, last_idx=None, o_stack=None): 95 | from scheme.unquote import unquote 96 | from scheme.unquote_splicing import unquote_splicing 97 | from scheme.macro import MacroSymbol 98 | from scheme.Globals import Globals 99 | if not isinstance(lst, list): 100 | if isinstance(lst, Symbol): 101 | if last_idx == 0 and lst.isBound(Globals) and isinstance(lst.toObject(Globals), unquote): 102 | qq_target = last_lst.pop(last_idx + 1) 103 | retval = processer.process([qq_target], env) 104 | return retval, True 105 | if lst == ',': 106 | qq_target = last_lst.pop(last_idx + 1) 107 | processer.pushStack(qq_target) 108 | retval = processer.process([qq_target], env) 109 | processer.popStack(retval, wrap=False) 110 | return retval, False 111 | if last_idx == 0 and lst.isBound(Globals) and isinstance(lst.toObject(Globals), unquote_splicing): 112 | qq_target = last_lst.pop(last_idx + 1) 113 | processer.pushStack(qq_target) 114 | retval = processer.process([qq_target], env) 115 | processer.popStack(retval, wrap=False) 116 | o_stack.extend(retval) 117 | return retval, 2 118 | if lst == ',@': 119 | qq_target = last_lst.pop(last_idx + 1) 120 | processer.pushStack(qq_target) 121 | retval = processer.process([qq_target], env) 122 | processer.popStack(retval, wrap=False) 123 | return retval, 3 124 | return MacroSymbol(lst).setEnv({lst: lst}), False 125 | return lst, False 126 | o = [] 127 | o_stack.append(o) 128 | for idx, i in enumerate(lst): 129 | r = copy_with_quasiquote(processer, env, i, lst, idx, o_stack) 130 | if r[1] == 3: 131 | o.extend(r[0]) 132 | continue 133 | if r[1] == 2: 134 | return r[0], 3 135 | elif r[1]: 136 | return r[0], False 137 | else: 138 | o.append(r[0]) 139 | return o, False 140 | 141 | 142 | def symbols_to_values(lst, env): 143 | if not isinstance(lst, list): 144 | if isinstance(lst, Symbol): 145 | return lst.toObject(env) 146 | return lst 147 | o = [] 148 | for i in lst: 149 | o.append(symbols_to_values(i, env)) 150 | return o 151 | 152 | 153 | def expand_syntax(lst): 154 | if "#'" in str(lst) or '#`' in str(lst): 155 | for idx, this in enumerate(lst): 156 | if this == "#'": 157 | quoteTarget = lst.pop(idx + 1) 158 | if quoteTarget == "#'": 159 | def getQuoteTarget(): 160 | qt = lst.pop(idx + 1) 161 | if qt == "#'": 162 | return [Symbol('syntax'), getQuoteTarget()] 163 | return qt 164 | quoteTarget = [Symbol('syntax'), getQuoteTarget()] 165 | lst[idx] = [Symbol('syntax'), quoteTarget] 166 | elif this == "#`": 167 | quoteTarget = lst.pop(idx + 1) 168 | if quoteTarget == "#`": 169 | def getQuoteTarget(): 170 | qt = lst.pop(idx + 1) 171 | if qt == "`": 172 | return [Symbol('quasisyntax'), getQuoteTarget()] 173 | return qt 174 | quoteTarget = [Symbol('quasisyntax'), getQuoteTarget()] 175 | lst[idx] = [Symbol('quasisyntax'), quoteTarget] 176 | elif isinstance(this, list): 177 | expand_quotes(this) 178 | return lst 179 | 180 | 181 | 182 | def expand_quotes(lst): 183 | if "'" in str(lst) or "`" in str(lst): 184 | for idx, this in enumerate(lst): 185 | if this == "'": 186 | quoteTarget = lst.pop(idx + 1) 187 | if quoteTarget == "'": 188 | def getQuoteTarget(): 189 | qt = lst.pop(idx + 1) 190 | if qt == "'": 191 | return [Symbol('quote'), getQuoteTarget()] 192 | return qt 193 | quoteTarget = [Symbol('quote'), getQuoteTarget()] 194 | lst[idx] = [Symbol('quote'), quoteTarget] 195 | elif this == "`": 196 | quoteTarget = lst.pop(idx + 1) 197 | if quoteTarget == "`": 198 | def getQuoteTarget(): 199 | qt = lst.pop(idx + 1) 200 | if qt == "`": 201 | return [Symbol('quasiquote'), getQuoteTarget()] 202 | return qt 203 | quoteTarget = [Symbol('quasiquote'), getQuoteTarget()] 204 | lst[idx] = [Symbol('quasiquote'), quoteTarget] 205 | elif isinstance(this, list): 206 | expand_quotes(this) 207 | return expand_syntax(lst) 208 | 209 | 210 | def getUniqueSymbol(c): 211 | import time, hashlib 212 | return Symbol(str(c) + hashlib.md5(str(time.time())).hexdigest()) 213 | 214 | B=[] 215 | 216 | def transformCode(code, bindings, env, transformer, localSymbols = None): 217 | """ 218 | Recursive function to build the transformed code 219 | :param code: code to transform 220 | :param bindings: Environment which details which symbols should not be looked up in the macro's environment 221 | :param env: Environment to store macro-variables and to use for macro-variable lookup 222 | :param transformer: the transformer for which the SyntaxSymbols are generated 223 | :return: transformed code 224 | """ 225 | # B.append(bindings) 226 | from scheme.macro import MacroSymbol 227 | if localSymbols is None: 228 | localSymbols = {} 229 | if not isinstance(code, list): 230 | if code in bindings: 231 | return bindings[code] 232 | try: 233 | code.toObject(env) 234 | return MacroSymbol(code).setEnv(env) 235 | except: 236 | pass 237 | if code not in localSymbols: 238 | localSymbols[code]=getUniqueSymbol(code) 239 | return localSymbols[code] 240 | o=[] 241 | itercode=iter(enumerate(code)) 242 | for idx, c in itercode: 243 | if isinstance(c, list): 244 | newC=transformCode(c, bindings, env, transformer, localSymbols) 245 | o.append(newC) 246 | else: 247 | if len(code) > idx + 1 and code[idx+1]=='...': 248 | itercode.next() 249 | tmp = bindings[c]#.get_all(c) 250 | #if isinstance(tmp, list): 251 | o.extend(tmp) 252 | #else: 253 | # o.append(tmp) 254 | continue 255 | if c in bindings: 256 | #o.append(bindings.get_all(c)) 257 | o.extend(bindings[c]) 258 | continue 259 | try: 260 | c.toObject(env) 261 | o.append(MacroSymbol(c).setEnv(env)) 262 | continue 263 | except Exception as e: 264 | #print e 265 | pass 266 | if c not in localSymbols: 267 | localSymbols[c]=getUniqueSymbol(c) 268 | o.append(localSymbols[c]) 269 | return o 270 | 271 | -------------------------------------------------------------------------------- /scheme/processer.py: -------------------------------------------------------------------------------- 1 | from scheme import Globals 2 | from scheme.syntax_rules import SyntaxSymbol 3 | from scheme.utils import callCCBounce 4 | from scheme.environment import Environment 5 | from scheme.procedure import Procedure, SimpleProcedure 6 | from scheme.macro import Macro, MacroSymbol 7 | from scheme.utils import deepcopy, expand_quotes 8 | import scheme.utils 9 | from zope.interface import providedBy 10 | from scheme.symbol import Symbol 11 | from Queue import LifoQueue, Empty 12 | from scheme import debug 13 | from scheme.debug import LOG 14 | 15 | current_processer = None 16 | discarded_frames = [] 17 | 18 | 19 | class Processer(object): 20 | def __init__(self, parent=None): 21 | # if current_processer: 22 | # raise Exception() 23 | self.children = [] 24 | self.parent = parent 25 | if parent: 26 | parent.children.append(self) 27 | self.callStack = LifoQueue() 28 | self.callDepth = 0 29 | self.env = Globals.Globals 30 | self.ast = None 31 | self.stackPointer = 0 32 | self.cenv = None 33 | self.initialCallDepth = 0 34 | self.shouldAbort = None 35 | def getContinuation(self): 36 | if self.parent: 37 | pc = self.parent.continuation 38 | else: 39 | pc = dict(callDepth=0, callStack=[], initialCallDepth=0) 40 | return dict(env=self.cenv, callDepth=self.callDepth + pc['callDepth'], 41 | callStack=deepcopy(self.callStack.queue) + pc['callStack'], 42 | initialCallDepth=self.initialCallDepth + pc['initialCallDepth'], stackPointer=self.stackPointer) 43 | def setContinuation(self, args): 44 | continuation, retval = args 45 | self.callStack.queue[:] = deepcopy(continuation['callStack']) 46 | self.callDepth = continuation['callDepth'] 47 | self.cenv = continuation['env'] 48 | self.stackPointer = continuation['stackPointer'] 49 | self.popStack(retval) 50 | continuation = property(getContinuation, setContinuation) 51 | def pushStackN(self): 52 | self.callStack.put((self.ast, self.cenv, self.stackPointer, 0)) 53 | self.callDepth += 1 54 | def popStackN(self): 55 | self.ast, self.cenv, self.stackPointer, garbage = self.callStack.get_nowait() 56 | self.callDepth -= 1 57 | def pushStack(self, ast): 58 | if debug.getDebug('pushStack'): 59 | import traceback 60 | traceback.print_stack() 61 | print 'push', self.ast, self.stackPointer 62 | self.callStack.put((self.ast, self.cenv, self.stackPointer, 1)) 63 | self.ast = ast 64 | self.cenv = Environment(self.cenv) 65 | self.stackPointer = 0 66 | self.callDepth += 1 67 | def popStack(self, retval, wrap=True): 68 | if debug.getDebug('popStack'): 69 | import traceback 70 | 71 | 72 | traceback.print_stack() 73 | print 'pop', self.ast, retval, self.stackPointer, 74 | if len(self.callStack.queue): 75 | print self.callStack.queue[-1][0], self.callStack.queue[-1][2] 76 | print 77 | print 78 | if isinstance(retval, Symbol) and wrap: 79 | if isinstance(retval, SyntaxSymbol): 80 | retval = MacroSymbol(retval).setObj(retval) 81 | else: 82 | retval = MacroSymbol(retval).setEnv({retval: retval.toObject(self.cenv)}) 83 | if debug.getDebug('discardedFrames'): 84 | discarded_frames.append((self.ast, self.cenv, self.stackPointer)) 85 | try: 86 | self.ast, self.cenv, self.stackPointer, rv = self.callStack.get_nowait() 87 | except Empty as e: 88 | e.ret = e.retval = self.ast[-1] 89 | raise e 90 | self.callDepth -= 1 91 | if rv: 92 | if self.stackPointer >= len(self.ast): 93 | self.popStack(retval) 94 | self.ast[self.stackPointer] = retval 95 | LOG('popStack', self.ast, self.stackPointer) 96 | def dumpStack(self): 97 | while self.callDepth > 0 and self.callStack.queue: 98 | self.popStackN() 99 | self.stackPointer = 0 100 | self.cenv = None 101 | self.initialCallDepth = 0 102 | self.ast = None 103 | self.callDepth = 0 104 | tcd = 0 105 | def doProcess(self, _ast, env=None, callDepth=None, ccc=False, quotesExpanded=False): 106 | if not ccc: 107 | self.dumpStack() 108 | try: 109 | return self.process(_ast, env, callDepth, quotesExpanded) 110 | except callCCBounce as e: 111 | print >> open('/tmp/ccc.log', 'a'), (e.continuation, e.retval) 112 | # noinspection PyUnresolvedReferences 113 | continuation = e.continuation 114 | callDepth = self.callDepth 115 | icd = self.initialCallDepth 116 | self.dumpStack() 117 | self.callStack.queue = deepcopy(continuation['callStack']) 118 | self.callDepth = continuation['callDepth'] 119 | self.initialCallDepth = 0 120 | LOG('call/cc bounce', 109, self.ast) 121 | self.ast, self.cenv, self.stackPointer, rv = self.callStack.get_nowait() 122 | # self.callStack.queue.pop(0) 123 | #self.callDepth-=1 124 | LOG('call/cc bounce', self.ast) 125 | seek = True 126 | while True: 127 | for i in xrange(len(self.ast)): 128 | if isinstance(self.ast[i], list): 129 | if isinstance(self.ast[i], list) and self.ast[i][0] is e.ccc: 130 | self.ast[i] = e.retval 131 | self.stackPointer = i + 1 132 | #a = self.ast 133 | #self.popStackN() 134 | #self.ast=a 135 | seek = False 136 | break 137 | if not seek: 138 | break 139 | self.ast, self.cenv, self.stackPointer, rv = self.callStack.get_nowait() 140 | 141 | self.tcd += 1 142 | while self.callDepth > 1: 143 | try: 144 | self.doProcess(self.ast, self.cenv, 0, True) 145 | except TypeError as e3: 146 | if not callable(self.ast[0]): 147 | try: 148 | self.popStack(self.ast[-1]) 149 | except Empty as e5: 150 | if hasattr(e5, 'ret'): 151 | return e5.ret 152 | if hasattr(e5, 'retval'): 153 | return e5.retval 154 | raise e5 155 | #return self.doProcess(self.ast, self.cenv, 0, True) 156 | else: 157 | raise e3 158 | print 157, self.ast, self.stackPointer, self.callStack.queue, self.callDepth 159 | except Empty as e4: 160 | if hasattr(e4, 'ret'): 161 | return e4.ret 162 | if hasattr(e4, 'retval'): 163 | return e4.retval 164 | raise e4 165 | return self.ast[-1] 166 | except Empty as e1: 167 | if hasattr(e1, 'ret'): 168 | return e1.ret 169 | if hasattr(e1, 'retval'): 170 | self.dumpStack() 171 | return e1.retval 172 | raise e1 173 | def process(self, _ast, env=None, callDepth=None, quotesExpanded=True): 174 | global current_processer 175 | current_processer = self 176 | if _ast == [[]]: 177 | raise SyntaxError() 178 | """ 179 | 180 | 181 | :rtype : object 182 | :param _ast: 183 | :param env: Environment 184 | :return: 185 | """ 186 | try: 187 | if callDepth is not None: 188 | self.initialCallDepth = callDepth 189 | else: 190 | 191 | self.initialCallDepth = self.callDepth 192 | 193 | if env is None: 194 | self.cenv = self.env 195 | else: 196 | self.cenv = env 197 | self.ast = _ast 198 | if not quotesExpanded: 199 | self.ast = expand_quotes(self.ast) 200 | self.stackPointer = 0; 201 | if not isinstance(self.ast, list): 202 | if isinstance(self.ast, Symbol): 203 | this = self.ast.toObject(self.cenv) 204 | else: 205 | this = self.ast 206 | if self.callDepth: 207 | self.popStack(this) 208 | else: 209 | return this 210 | if len(self.ast) == 1 and not isinstance(self.ast[0], list): 211 | if isinstance(self.ast[0], Symbol): 212 | this = self.ast[0].toObject(self.cenv) 213 | else: 214 | this = self.ast[0] 215 | if self.callDepth > self.initialCallDepth: 216 | self.popStack(this) 217 | else: 218 | return this 219 | while True: 220 | if self.shouldAbort: 221 | raise self.shouldAbort 222 | if self.stackPointer >= len(self.ast) and self.callDepth <= self.initialCallDepth: 223 | return self.ast[-1] 224 | if self.stackPointer >= len(self.ast): 225 | for idx, i in enumerate(self.ast): 226 | if isinstance(i, Symbol) and i.isBound(self.cenv): 227 | self.ast[idx] = i.toObject(self.cenv) 228 | initial_call_depth = self.initialCallDepth 229 | if isinstance(self.ast[0], Symbol): 230 | self.ast[0] = self.ast[0].toObject(self.cenv) 231 | if isinstance(self.ast[0], SimpleProcedure): 232 | this = self.ast[0] 233 | args = self.ast[1:] 234 | params = deepcopy(this.ast[0]) 235 | e = Environment(this.env) 236 | if isinstance(params, list): 237 | if '.' in params: 238 | iter_args = iter(args) 239 | for idx, item in enumerate(params[:-2]): 240 | e[item] = iter_args.next() 241 | e[params[-1]] = list(iter_args) 242 | else: 243 | if (isinstance(args, list) and len(args) != len(params)): 244 | raise TypeError("%r expected exactly %i arguments, got %i" % ( 245 | self.ast[0], len(params), len(args))) 246 | if (not isinstance(args, list) and 1 != len(params)): 247 | raise TypeError("%r expected exactly %i arguments, got %i" % ( 248 | self.ast[0], len(params), len(args))) 249 | iter_args = iter(args) 250 | for idx, item in enumerate(params): 251 | e[item] = iter_args.next() 252 | else: 253 | e[params] = args 254 | self.popStackN() 255 | self.pushStack(deepcopy([Symbol('last'), [Symbol('list')] + this.ast[1:]])) 256 | self.cenv = Environment(e) 257 | continue 258 | elif Procedure in providedBy(self.ast[0]): 259 | r = self.ast[0](self, self.ast[1:]) 260 | self.popStack(r) 261 | elif Macro in providedBy(self.ast[0]): 262 | r = self.ast[0](self, self.ast[1:]) 263 | if r is None: 264 | continue 265 | self.popStack(r) 266 | else: 267 | r = self.ast[0](*self.ast[1:]) 268 | self.popStack(r) 269 | self.initialCallDepth = initial_call_depth 270 | self.stackPointer += 1 271 | continue 272 | this = self.ast[self.stackPointer] 273 | if isinstance(this, SyntaxSymbol): 274 | this = this.toObject(self.cenv) 275 | if isinstance(this, list): 276 | self.pushStack(this) 277 | continue 278 | if isinstance(this, Symbol) and this.isBound(self.cenv): 279 | t = this.toObject(self.cenv) 280 | while isinstance(t, Symbol) and t.isBound(self.cenv): 281 | t = t.toObject(self.cenv) 282 | else: 283 | t = this 284 | if self.stackPointer == 0 and Macro in providedBy(t): 285 | initial_call_depth = self.initialCallDepth 286 | r = t(self, self.ast[1:]) 287 | self.initialCallDepth = initial_call_depth 288 | if r is None: 289 | continue 290 | if isinstance(r, SyntaxSymbol): 291 | self.popStack(r) 292 | elif not isinstance(r, list): 293 | r1 = [lambda *x: r] 294 | # self.ast[:] = r1 295 | self.popStack(r1) 296 | else: 297 | self.ast[:] = r 298 | continue 299 | if isinstance(this, Symbol) and this.isBound(self.cenv): 300 | self.ast[self.stackPointer] = this.toObject(self.cenv) 301 | self.stackPointer += 1 302 | except Empty as e: 303 | if hasattr(e, 'ret'): 304 | return e.ret 305 | return self.ast[-1] 306 | 307 | 308 | processer = Processer() 309 | -------------------------------------------------------------------------------- /scheme/processer.pyx: -------------------------------------------------------------------------------- 1 | from scheme import Globals 2 | from scheme.syntax_rules import SyntaxSymbol 3 | from scheme.utils import callCCBounce 4 | from scheme.environment import Environment 5 | from scheme.procedure import Procedure, SimpleProcedure 6 | from scheme.macro import Macro, MacroSymbol 7 | from scheme.utils import deepcopy, expand_quotes 8 | import scheme.utils 9 | from zope.interface import providedBy 10 | from scheme.symbol import Symbol 11 | from Queue import LifoQueue, Empty 12 | from scheme import debug 13 | from scheme.debug import LOG 14 | 15 | current_processer = None 16 | discarded_frames = [] 17 | 18 | 19 | class Processer(object): 20 | def __init__(self, parent=None): 21 | # if current_processer: 22 | # raise Exception() 23 | self.children = [] 24 | self.parent = parent 25 | if parent: 26 | parent.children.append(self) 27 | self.callStack = LifoQueue() 28 | self.callDepth = 0 29 | self.env = Globals.Globals 30 | self.ast = None 31 | self.stackPointer = 0 32 | self.cenv = None 33 | self.initialCallDepth = 0 34 | self.shouldAbort = None 35 | def getContinuation(self): 36 | if self.parent: 37 | pc = self.parent.continuation 38 | else: 39 | pc = dict(callDepth=0, callStack=[], initialCallDepth=0) 40 | return dict(env=self.cenv, callDepth=self.callDepth + pc['callDepth'], 41 | callStack=deepcopy(self.callStack.queue) + pc['callStack'], 42 | initialCallDepth=self.initialCallDepth + pc['initialCallDepth'], stackPointer=self.stackPointer) 43 | def setContinuation(self, args): 44 | continuation, retval = args 45 | self.callStack.queue[:] = deepcopy(continuation['callStack']) 46 | self.callDepth = continuation['callDepth'] 47 | self.cenv = continuation['env'] 48 | self.stackPointer = continuation['stackPointer'] 49 | self.popStack(retval) 50 | continuation = property(getContinuation, setContinuation) 51 | def pushStackN(self): 52 | self.callStack.put((self.ast, self.cenv, self.stackPointer, 0)) 53 | self.callDepth += 1 54 | def popStackN(self): 55 | self.ast, self.cenv, self.stackPointer, garbage = self.callStack.get_nowait() 56 | self.callDepth -= 1 57 | def pushStack(self, ast): 58 | if debug.getDebug('pushStack'): 59 | import traceback 60 | traceback.print_stack() 61 | print 'push', self.ast, self.stackPointer 62 | self.callStack.put((self.ast, self.cenv, self.stackPointer, 1)) 63 | self.ast = ast 64 | self.cenv = Environment(self.cenv) 65 | self.stackPointer = 0 66 | self.callDepth += 1 67 | def popStack(self, retval, wrap=True): 68 | if debug.getDebug('popStack'): 69 | import traceback 70 | 71 | 72 | traceback.print_stack() 73 | print 'pop', self.ast, retval, self.stackPointer, 74 | if len(self.callStack.queue): 75 | print self.callStack.queue[-1][0], self.callStack.queue[-1][2] 76 | print 77 | print 78 | if isinstance(retval, Symbol) and wrap: 79 | if isinstance(retval, SyntaxSymbol): 80 | retval = MacroSymbol(retval).setObj(retval) 81 | else: 82 | retval = MacroSymbol(retval).setEnv({retval: retval.toObject(self.cenv)}) 83 | if debug.getDebug('discardedFrames'): 84 | discarded_frames.append((self.ast, self.cenv, self.stackPointer)) 85 | try: 86 | self.ast, self.cenv, self.stackPointer, rv = self.callStack.get_nowait() 87 | except Empty as e: 88 | e.ret = e.retval = self.ast[-1] 89 | raise e 90 | self.callDepth -= 1 91 | if rv: 92 | if self.stackPointer >= len(self.ast): 93 | self.popStack(retval) 94 | self.ast[self.stackPointer] = retval 95 | LOG('popStack', self.ast, self.stackPointer) 96 | def dumpStack(self): 97 | while self.callDepth > 0 and self.callStack.queue: 98 | self.popStackN() 99 | self.stackPointer = 0 100 | self.cenv = None 101 | self.initialCallDepth = 0 102 | self.ast = None 103 | self.callDepth = 0 104 | tcd = 0 105 | def doProcess(self, _ast, env=None, callDepth=None, ccc=False, quotesExpanded=False): 106 | if not ccc: 107 | self.dumpStack() 108 | try: 109 | return self.process(_ast, env, callDepth, quotesExpanded) 110 | except callCCBounce as e: 111 | print >> open('/tmp/ccc.log', 'a'), (e.continuation, e.retval) 112 | # noinspection PyUnresolvedReferences 113 | continuation = e.continuation 114 | callDepth = self.callDepth 115 | icd = self.initialCallDepth 116 | self.dumpStack() 117 | self.callStack.queue = deepcopy(continuation['callStack']) 118 | self.callDepth = continuation['callDepth'] 119 | self.initialCallDepth = 0 120 | LOG('call/cc bounce', 109, self.ast) 121 | self.ast, self.cenv, self.stackPointer, rv = self.callStack.get_nowait() 122 | # self.callStack.queue.pop(0) 123 | #self.callDepth-=1 124 | LOG('call/cc bounce', self.ast) 125 | seek = True 126 | while True: 127 | for i in xrange(len(self.ast)): 128 | if isinstance(self.ast[i], list): 129 | if isinstance(self.ast[i], list) and self.ast[i][0] is e.ccc: 130 | self.ast[i] = e.retval 131 | self.stackPointer = i + 1 132 | #a = self.ast 133 | #self.popStackN() 134 | #self.ast=a 135 | seek = False 136 | break 137 | if not seek: 138 | break 139 | self.ast, self.cenv, self.stackPointer, rv = self.callStack.get_nowait() 140 | 141 | self.tcd += 1 142 | while self.callDepth > 1: 143 | try: 144 | self.doProcess(self.ast, self.cenv, 0, True) 145 | except TypeError as e3: 146 | if not callable(self.ast[0]): 147 | try: 148 | self.popStack(self.ast[-1]) 149 | except Empty as e5: 150 | if hasattr(e5, 'ret'): 151 | return e5.ret 152 | if hasattr(e5, 'retval'): 153 | return e5.retval 154 | raise e5 155 | #return self.doProcess(self.ast, self.cenv, 0, True) 156 | else: 157 | raise e3 158 | print 157, self.ast, self.stackPointer, self.callStack.queue, self.callDepth 159 | except Empty as e4: 160 | if hasattr(e4, 'ret'): 161 | return e4.ret 162 | if hasattr(e4, 'retval'): 163 | return e4.retval 164 | raise e4 165 | return self.ast[-1] 166 | except Empty as e1: 167 | if hasattr(e1, 'ret'): 168 | return e1.ret 169 | if hasattr(e1, 'retval'): 170 | self.dumpStack() 171 | return e1.retval 172 | raise e1 173 | def process(self, _ast, env=None, callDepth=None, quotesExpanded=True): 174 | global current_processer 175 | current_processer = self 176 | if _ast == [[]]: 177 | raise SyntaxError() 178 | """ 179 | 180 | 181 | :rtype : object 182 | :param _ast: 183 | :param env: Environment 184 | :return: 185 | """ 186 | try: 187 | if callDepth is not None: 188 | self.initialCallDepth = callDepth 189 | else: 190 | 191 | self.initialCallDepth = self.callDepth 192 | 193 | if env is None: 194 | self.cenv = self.env 195 | else: 196 | self.cenv = env 197 | self.ast = _ast 198 | if not quotesExpanded: 199 | self.ast = expand_quotes(self.ast) 200 | self.stackPointer = 0; 201 | if not isinstance(self.ast, list): 202 | if isinstance(self.ast, Symbol): 203 | this = self.ast.toObject(self.cenv) 204 | else: 205 | this = self.ast 206 | if self.callDepth: 207 | self.popStack(this) 208 | else: 209 | return this 210 | if len(self.ast) == 1 and not isinstance(self.ast[0], list): 211 | if isinstance(self.ast[0], Symbol): 212 | this = self.ast[0].toObject(self.cenv) 213 | else: 214 | this = self.ast[0] 215 | if self.callDepth > self.initialCallDepth: 216 | self.popStack(this) 217 | else: 218 | return this 219 | while True: 220 | if self.shouldAbort: 221 | raise self.shouldAbort 222 | if self.stackPointer >= len(self.ast) and self.callDepth <= self.initialCallDepth: 223 | return self.ast[-1] 224 | if self.stackPointer >= len(self.ast): 225 | for idx, i in enumerate(self.ast): 226 | if isinstance(i, Symbol) and i.isBound(self.cenv): 227 | self.ast[idx] = i.toObject(self.cenv) 228 | initial_call_depth = self.initialCallDepth 229 | if isinstance(self.ast[0], Symbol): 230 | self.ast[0] = self.ast[0].toObject(self.cenv) 231 | if isinstance(self.ast[0], SimpleProcedure): 232 | this = self.ast[0] 233 | args = self.ast[1:] 234 | params = deepcopy(this.ast[0]) 235 | e = Environment(this.env) 236 | if isinstance(params, list): 237 | if '.' in params: 238 | iter_args = iter(args) 239 | for idx, item in enumerate(params[:-2]): 240 | e[item] = iter_args.next() 241 | e[params[-1]] = list(iter_args) 242 | else: 243 | if (isinstance(args, list) and len(args) != len(params)): 244 | raise TypeError("%r expected exactly %i arguments, got %i" % ( 245 | self.ast[0], len(params), len(args))) 246 | if (not isinstance(args, list) and 1 != len(params)): 247 | raise TypeError("%r expected exactly %i arguments, got %i" % ( 248 | self.ast[0], len(params), len(args))) 249 | iter_args = iter(args) 250 | for idx, item in enumerate(params): 251 | e[item] = iter_args.next() 252 | else: 253 | e[params] = args 254 | self.popStackN() 255 | self.pushStack(deepcopy([Symbol('last'), [Symbol('list')] + this.ast[1:]])) 256 | self.cenv = Environment(e) 257 | continue 258 | elif Procedure in providedBy(self.ast[0]): 259 | r = self.ast[0](self, self.ast[1:]) 260 | self.popStack(r) 261 | elif Macro in providedBy(self.ast[0]): 262 | r = self.ast[0](self, self.ast[1:]) 263 | if r is None: 264 | continue 265 | self.popStack(r) 266 | else: 267 | r = self.ast[0](*self.ast[1:]) 268 | self.popStack(r) 269 | self.initialCallDepth = initial_call_depth 270 | self.stackPointer += 1 271 | continue 272 | this = self.ast[self.stackPointer] 273 | if isinstance(this, SyntaxSymbol): 274 | this = this.toObject(self.cenv) 275 | if isinstance(this, list): 276 | self.pushStack(this) 277 | continue 278 | if isinstance(this, Symbol) and this.isBound(self.cenv): 279 | t = this.toObject(self.cenv) 280 | while isinstance(t, Symbol) and t.isBound(self.cenv): 281 | t = t.toObject(self.cenv) 282 | else: 283 | t = this 284 | if self.stackPointer == 0 and Macro in providedBy(t): 285 | initial_call_depth = self.initialCallDepth 286 | r = t(self, self.ast[1:]) 287 | self.initialCallDepth = initial_call_depth 288 | if r is None: 289 | continue 290 | if isinstance(r, SyntaxSymbol): 291 | self.popStack(r) 292 | elif not isinstance(r, list): 293 | r1 = [lambda *x: r] 294 | # self.ast[:] = r1 295 | self.popStack(r1) 296 | else: 297 | self.ast[:] = r 298 | continue 299 | if isinstance(this, Symbol) and this.isBound(self.cenv): 300 | self.ast[self.stackPointer] = this.toObject(self.cenv) 301 | self.stackPointer += 1 302 | except Empty as e: 303 | if hasattr(e, 'ret'): 304 | return e.ret 305 | return self.ast[-1] 306 | 307 | 308 | processer = Processer() 309 | -------------------------------------------------------------------------------- /scheme/jit.py: -------------------------------------------------------------------------------- 1 | enabled=False 2 | lambdas_enabled=True 3 | unstable_enabled=False 4 | from types import CodeType as code, FunctionType as function, BuiltinFunctionType 5 | from procedure import SimpleProcedure 6 | from symbol import Symbol 7 | import scheme.debug 8 | import operator as op 9 | import warnings 10 | from scheme.Globals import Globals 11 | import dis 12 | import time 13 | import sys 14 | from procedure import Procedure 15 | from zope.interface import providedBy 16 | from itertools import count 17 | from macro import MacroSymbol 18 | from scheme.environment import Environment 19 | globals().update(dis.opmap) 20 | 21 | basic_ops = {op.add:BINARY_ADD,op.mul:BINARY_MULTIPLY,op.sub:BINARY_SUBTRACT,op.itruediv:BINARY_TRUE_DIVIDE} 22 | compare_ops = (op.lt, op.le, op.eq, op.ne, op.gt, op.ge, op.contains, object(), op.is_, object(), object(), object()) 23 | 24 | labelidx=count() 25 | 26 | def lookup_value(v, e): 27 | if isinstance(v, Symbol): 28 | if v.isBound(e): 29 | return v.toObject(e) 30 | return v 31 | return e[v] 32 | 33 | def isaProcedure(obj): 34 | return Procedure in providedBy(obj) or isinstance(obj, MacroSymbol) 35 | 36 | 37 | def get_constants(c, s=None): 38 | if s is None: 39 | s=set() 40 | for i in c: 41 | if isinstance(i, list): 42 | get_constants(i,s) 43 | continue 44 | if isinstance(i, Symbol): 45 | if i.isBound({}): 46 | s.add(i.toObject({})) 47 | continue 48 | if isinstance(i, (int,long,str,unicode,float,complex)): 49 | s.add(i) 50 | return s 51 | 52 | def get_symbols(c, s=None): 53 | if s is None: 54 | s=set() 55 | for i in c: 56 | if isinstance(i, list): 57 | get_symbols(i,s) 58 | continue 59 | if isinstance(i, Symbol): 60 | if i.isBound({}): 61 | s.add(i.toObject({})) 62 | continue 63 | s.add(i) 64 | return s 65 | 66 | def get_locals(c, s=None, e=None): 67 | if s is None: 68 | s=set() 69 | if c: 70 | f = c[0] 71 | if isinstance(f, Symbol) and f.isBound(e): 72 | f=f.toObject(e) 73 | if isinstance(f, scheme.define.define): 74 | what = c[1] 75 | if isinstance(what, list): 76 | return None 77 | s.add(what) 78 | return s 79 | for i in c: 80 | if isinstance(i, list): 81 | if get_locals(i,s,e) is None: 82 | return None 83 | continue 84 | return s 85 | 86 | 87 | def analyze(obj, env=None, p=None): 88 | '''Determines if the jit may be capable of rewriting a SimpleProcedure to python bytecode. Returns some basic information if it can be rewritten''' 89 | if p and env is None: 90 | env=p.env 91 | while isinstance(obj, Symbol): 92 | if not obj.isBound(env): 93 | return False 94 | obj=obj.toObject(env) 95 | if not isinstance(obj, SimpleProcedure): 96 | return False 97 | ast = obj.ast 98 | params=ast[0] 99 | flags = 67 100 | c = ast[1:] 101 | constants = get_constants(c).union() 102 | symbols = get_symbols(c) 103 | localnames = get_locals(c,e=obj.env) 104 | if localnames is None: 105 | return False 106 | localnames = localnames.difference(params) 107 | names = (symbols - constants).difference(params).difference(localnames) 108 | nlocals = len(params)+len(localnames) 109 | 110 | name = str(obj.name) 111 | firstlineno = obj.lineno or 0 112 | if not isinstance(params, list): 113 | flags|=4 114 | varargs = [params] 115 | params=[params] 116 | argcount=len(params)-1 117 | names.add('list') 118 | else: 119 | argcount = len(params) 120 | varargs = False 121 | varnames = params+list(localnames) 122 | filename='' 123 | return argcount, nlocals, flags, tuple(constants)+(obj.env,[None]), names, varnames, filename, name, firstlineno, varargs 124 | 125 | 126 | class Loader(object): 127 | def __init__(self, constants, varnames, names, env, bytecode, stackptr): 128 | self.constants = constants 129 | self.varnames = varnames 130 | self.names = names 131 | self.env = env 132 | self.bytecode = bytecode 133 | self.nonlocals = [] 134 | self.stackptr = stackptr 135 | def storeItem(self, item, bytecode=None): 136 | if bytecode is None: 137 | bytecode = self.bytecode 138 | item = str(item) 139 | idx = self.varnames.index(item) 140 | bytecode.append(STORE_FAST) 141 | bytecode.append(idx%256) 142 | bytecode.append(idx//256) 143 | def loadExtraConstant(self, o, bytecode=None): 144 | if bytecode is None: 145 | bytecode = self.bytecode 146 | if o in self.constants: 147 | return self.loadItem(o,bytecode) 148 | if o not in self.nonlocals: 149 | self.nonlocals.append(o) 150 | idx=len(self.constants)+self.nonlocals.index(o) 151 | bytecode.add(LOAD_CONST,idx%256,idx//256) 152 | def loadItem(self, o, bytecode=None): 153 | constants = self.constants 154 | varnames = self.varnames 155 | names = self.names 156 | if bytecode is None: 157 | bytecode = self.bytecode 158 | if str(o) in varnames: 159 | o=str(o) 160 | elif not isinstance(o, Symbol): 161 | pass 162 | elif isinstance(o, MacroSymbol): 163 | pass 164 | elif o.isBound({}): 165 | o = o.toObject({}) 166 | if o in constants: 167 | idx = constants.index(o) 168 | bytecode.append(LOAD_CONST) 169 | bytecode.append(idx%256) 170 | bytecode.append(idx//256) 171 | elif o in varnames: 172 | idx = varnames.index(o) 173 | bytecode.append(LOAD_FAST) 174 | bytecode.append(idx%256) 175 | bytecode.append(idx//256) 176 | elif o in names: 177 | if not isinstance(o, Symbol): 178 | o=names[names.index(o)] 179 | if o.isBound(Globals) and o.toObject(Globals) is o.toObject(self.env): 180 | idx = names.index(o) 181 | bytecode.append(LOAD_GLOBAL) 182 | bytecode.append(idx%256) 183 | bytecode.append(idx//256) 184 | else: 185 | self.loadItem(lookup_value) 186 | bytecode.append(LOAD_CONST) 187 | if o not in self.nonlocals: 188 | self.nonlocals.append(o) 189 | idx = len(self.constants)+self.nonlocals.index(o) 190 | bytecode.append(idx%256) 191 | bytecode.append(idx//256) 192 | self.loadItem(self.env) 193 | self.stackptr+3 194 | self.stackptr-3 195 | bytecode.add(CALL_FUNCTION,2,0) 196 | #raise ValueError("Trying to load nonlocal value: %r"%o) 197 | else: 198 | if isinstance(o, MacroSymbol): 199 | return self.loadItem(o.toObject({}), bytecode) 200 | print 156,constants 201 | print 157,varnames 202 | print 158,names 203 | if unstable_enabled: 204 | print type(o) 205 | warnings.warn("Trying to load unknown value: %r, assuming constant"%o) 206 | self.loadExtraConstant(o,bytecode) 207 | else: 208 | raise ValueError("Trying to load unknown value: %r"%o) 209 | 210 | class label(object): 211 | def __init__(self, name): 212 | self.name=name 213 | 214 | class goto(object): 215 | def __init__(self, label): 216 | self.label=label 217 | 218 | class Bytecode(list): 219 | def __init__(self, lineno=0): 220 | self.lineno = lineno 221 | self.opcodeno = 0 222 | self.lnotab = [] 223 | self.labels = {} 224 | def add(self, *a): 225 | for i in a: 226 | self.append(i) 227 | def get_lnotab(self): 228 | return str.join('', [chr(i) for i in self.lnotab]) 229 | def nextLine(self, lineno): 230 | if self.lineno > lineno: 231 | return 232 | oc_offset = len(self) - self.opcodeno 233 | self.opcodeno = len(self) 234 | lno_offset = lineno - self.lineno 235 | self.lineno = lineno 236 | while oc_offset > 255: 237 | self.lnotab.append(255) 238 | self.lnotab.append(0) 239 | oc_offset -= 255 240 | while lno_offset > 255: 241 | lno_offset-=255 242 | self.lnotab.append(0) 243 | self.lnotab.append(255) 244 | self.lnotab.append(oc_offset) 245 | self.lnotab.append(lno_offset) 246 | def append(self, value): 247 | if scheme.debug.debug_settings['jit-one-opcode-per-line']: 248 | self.nextLine(self.lineno+1) 249 | if isinstance(value, label): 250 | self.labels[value.name]=len(self) 251 | return 252 | super(Bytecode, self).append(value) 253 | if isinstance(value, goto): 254 | self.append(0) 255 | def flatten(self): 256 | o=[] 257 | itr = iter(self) 258 | for i in itr: 259 | if isinstance(i, int): 260 | o.append(chr(i)) 261 | elif isinstance(i, goto): 262 | itr.next() 263 | idx = self.labels[i.label] 264 | o.append(chr(idx%256)) 265 | o.append(chr(idx//256)) 266 | return str.join('', o) 267 | 268 | class StackPointer(object): 269 | def __repr__(self): 270 | return ""%(self.ptr,self.maxptr) 271 | def __init__(self): 272 | self.ptr = 0 273 | self.maxptr = 0 274 | def __add__(self, other): 275 | self.ptr+=other 276 | if self.ptr > self.maxptr: 277 | self.maxptr = self.ptr 278 | return self 279 | def __sub__(self, other): 280 | self.ptr-=other 281 | return self 282 | def __cmp__(self, other): 283 | return cmp(self.ptr, other) 284 | 285 | def write_code(obj, env=None, p=None): 286 | tmp = analyze(obj, env, p) 287 | if not tmp: 288 | return False 289 | argcount, nlocals, flags, constants, names, varnames, filename, name, firstlineno, varargs = tmp 290 | if None in constants: 291 | constants.remove(None) 292 | stackptr = StackPointer() 293 | constants=(None,lookup_value,isaProcedure,scheme.processer.processer)+tuple(constants) 294 | names=("pushStack",)+tuple(names) 295 | varnames=list(varnames) 296 | c = iter(obj.ast[1:]) 297 | bytecode = Bytecode(lineno=obj.lineno or 0) 298 | loader = Loader(constants, varnames, names, obj.env, bytecode, stackptr) 299 | def write_nested_code(statement): 300 | if not isinstance(statement, list): 301 | if isinstance(statement, Symbol): 302 | bytecode.nextLine(statement.line) 303 | stackptr + 1 304 | loader.loadItem(statement) 305 | return True 306 | func = statement[0] 307 | while isinstance(func, Symbol): 308 | bytecode.nextLine(func.line) 309 | if func.isBound(obj.env): 310 | func = func.toObject(obj.env) 311 | else: 312 | break 313 | if func in basic_ops.keys(): 314 | isp = stackptr.ptr 315 | for l in statement[1:]: 316 | if isinstance(l, list): 317 | if not write_nested_code(l): 318 | return False 319 | continue 320 | stackptr + 1 321 | loader.loadItem(l) 322 | while stackptr > isp + 1: 323 | stackptr - 1 324 | bytecode.append(basic_ops[func]) 325 | return True 326 | elif func in compare_ops: 327 | isp = stackptr.ptr 328 | istatement = iter(statement[1:]) 329 | lidx = labelidx.next() 330 | for l in istatement: 331 | if isinstance(l, list): 332 | if not write_nested_code(l): 333 | return False 334 | else: 335 | stackptr + 1 336 | loader.loadItem(l) 337 | if stackptr > isp + 1: 338 | stackptr + 1 339 | if istatement.__length_hint__(): 340 | bytecode.append(DUP_TOP) 341 | bytecode.append(ROT_THREE) 342 | bytecode.append(COMPARE_OP) 343 | stackptr - 1 344 | bytecode.append(compare_ops.index(func)) 345 | bytecode.append(0) 346 | if istatement.__length_hint__(): 347 | bytecode.append(JUMP_IF_FALSE_OR_POP) 348 | bytecode.append(goto('false%i'%lidx)) 349 | stackptr - 1 350 | else: 351 | bytecode.append(JUMP_ABSOLUTE) 352 | bytecode.append(goto('false%i'%lidx)) 353 | bytecode.append(label('false%i'%lidx)) 354 | bytecode.append(ROT_TWO) 355 | bytecode.append(POP_TOP) 356 | stackptr - 1 357 | bytecode.append(label('false%i'%lidx)) 358 | return True 359 | elif isinstance(func, (BuiltinFunctionType, function, type)): 360 | isp = stackptr.ptr 361 | istatement = iter(statement[1:]) 362 | loader.loadItem(statement[0]) 363 | for l in statement[1:]: 364 | if isinstance(l, list): 365 | if not write_nested_code(l): 366 | return False 367 | continue 368 | stackptr + 1 369 | loader.loadItem(l) 370 | bytecode.add(CALL_FUNCTION, len(statement[1:]), 0) 371 | return True 372 | elif isinstance(func, scheme.define.define): 373 | var = statement[1] 374 | if isinstance(var, list): 375 | return False 376 | val = statement[2] 377 | if isinstance(val, list): 378 | if not write_nested_code(val): 379 | return False 380 | else: 381 | loader.loadItem(val) 382 | stackptr +1 383 | bytecode.add(DUP_TOP) 384 | bytecode.add(DUP_TOP) 385 | loader.storeItem(var) 386 | loader.loadItem(obj.env) 387 | loader.loadExtraConstant(var) 388 | bytecode.add(ROT_THREE,ROT_THREE,ROT_TWO) 389 | bytecode.add(STORE_MAP) 390 | bytecode.add(POP_TOP) 391 | return True 392 | elif isinstance(func, scheme.IF.IF): 393 | lidx = labelidx.next() 394 | if not write_nested_code(statement[1]): 395 | return False 396 | bytecode.add(JUMP_IF_FALSE_OR_POP, goto('iffalse%i'%lidx)) 397 | if not write_nested_code(statement[2]): 398 | return False 399 | bytecode.add(JUMP_ABSOLUTE, goto('end%i'%lidx)) 400 | bytecode.add(label('iffalse%i'%lidx)) 401 | if len(statement)==4: 402 | bytecode.add(POP_TOP) 403 | if not write_nested_code(statement[3]): 404 | return False 405 | bytecode.add(label('end%i'%lidx)) 406 | return True 407 | elif isinstance(func, scheme.begin.begin): 408 | c = iter(statement[1:]) 409 | isp = stackptr.ptr 410 | for statement in c: 411 | while stackptr > isp: 412 | bytecode.append(POP_TOP) 413 | stackptr-1 414 | if not isinstance(statement, list): 415 | if isinstance(statement, Symbol): 416 | bytecode.nextLine(statement.line) 417 | stackptr + 1 418 | loader.loadItem(statement) 419 | if c.__length_hint__() > 0: 420 | bytecode.append(POP_TOP) 421 | stackptr - 1 422 | continue 423 | if not write_nested_code(statement): 424 | return False 425 | return True 426 | elif isaProcedure(func): 427 | ls = len(statement)-1 428 | loader.loadItem(func) 429 | if func is not obj: 430 | loader.loadItem(scheme.processer.processer) 431 | bytecode.add(DUP_TOP) 432 | bytecode.add(LOAD_ATTR,names.index("pushStack")%256,names.index("pushStack")//256) 433 | loader.loadItem([None]) 434 | bytecode.add(CALL_FUNCTION,1,0) 435 | bytecode.add(POP_TOP) 436 | for arg in statement[1:]: 437 | if not write_nested_code(arg): 438 | return False 439 | if func is not obj: 440 | bytecode.add(BUILD_LIST, ls%256,ls//256) 441 | bytecode.add(CALL_FUNCTION, 2, 0) 442 | else: 443 | bytecode.add(CALL_FUNCTION, ls, 0) 444 | stackptr-(ls-1) 445 | return True 446 | elif isinstance(func, Symbol): 447 | #not currently resolvable, so we calculate it's value at runtime. If it's a Procedure or special form, we'll apply it. Only use these if unstable optimizations enabled 448 | gidx=labelidx.next() 449 | if unstable_enabled: 450 | ls = len(statement)-1 451 | loader.loadItem(isaProcedure) 452 | stackptr+1 453 | loader.loadItem(func) 454 | stackptr+1 455 | bytecode.add(DUP_TOP,ROT_THREE,CALL_FUNCTION,1,0) 456 | bytecode.add(POP_JUMP_IF_TRUE) 457 | bytecode.add(goto("applyProcedure%i"%gidx)) 458 | #it's a function 459 | for arg in statement[1:]: 460 | if not write_nested_code(arg): 461 | return False 462 | bytecode.add(CALL_FUNCTION, ls, 0) 463 | stackptr-(ls-1) 464 | bytecode.add(JUMP_ABSOLUTE, goto("end%i"%gidx)) 465 | bytecode.add(label("applyProcedure%i"%gidx)) 466 | loader.loadItem(scheme.processer.processer) 467 | for arg in statement[1:]: 468 | if not write_nested_code(arg): 469 | return False 470 | bytecode.add(BUILD_LIST,ls%256,ls//256) 471 | stackptr-(ls-1) 472 | bytecode.add(CALL_FUNCTION,2,0) 473 | stackptr-3 474 | bytecode.add(label('end%i'%gidx)) 475 | 476 | 477 | return True 478 | else: 479 | return False 480 | elif isinstance(func, list): 481 | if not write_nested_code(func): 482 | return False 483 | for arg in statement[1:]: 484 | if not write_nested_code(arg): 485 | return False 486 | bytecode.add(CALL_FUNCTION, len(statement)-1, 0) 487 | stackptr - (len(statement)-2) 488 | return True 489 | elif isinstance(func, scheme.Lambda.Lambda): 490 | if unstable_enabled and lambdas_enabled: 491 | p = scheme.processer.processer 492 | p.pushStackN() 493 | p.cenv=Environment(p.cenv) 494 | print 470, 'statically compiling and linking lambda' 495 | f = func(p, statement[1:]).toObject({}) 496 | try: 497 | if isinstance(f, SimpleProcedure): 498 | f = makeFunction(f) 499 | finally: 500 | p.popStackN() 501 | loader.loadExtraConstant(f) 502 | return True 503 | return False 504 | else: 505 | print type(func) 506 | return False 507 | if varargs: 508 | for vararg in varargs: 509 | loader.loadItem('list-type') 510 | loader.loadItem(vararg) 511 | bytecode.append(CALL_FUNCTION) 512 | bytecode.append(1) 513 | bytecode.append(0) 514 | loader.storeItem(vararg) 515 | for statement in c: 516 | while stackptr > 0: 517 | bytecode.append(POP_TOP) 518 | stackptr-=1 519 | if not isinstance(statement, list): 520 | if isinstance(statement, Symbol): 521 | bytecode.nextLine(statement.line) 522 | stackptr + 1 523 | loader.loadItem(statement) 524 | if c.__length_hint__() > 0: 525 | bytecode.append(POP_TOP) 526 | stackptr -= 1 527 | continue 528 | if not write_nested_code(statement): 529 | return False 530 | if not bytecode: 531 | loader.loadItem(None) 532 | bytecode.append(RETURN_VALUE) 533 | c = code(argcount, nlocals, stackptr.maxptr, flags, bytecode.flatten(), constants+tuple(loader.nonlocals), tuple(str(i) for i in names), tuple(str(i) for i in varnames), filename, name, firstlineno, bytecode.get_lnotab()) 534 | if scheme.debug.debug_settings['jit']: 535 | print stackptr 536 | return c 537 | 538 | 539 | def makeFunction(obj, env=None, p=None): 540 | from syntax_expand import expandObj 541 | if scheme.debug.debug_settings['jit-crash-on-error']: 542 | expandObj(obj) 543 | c = write_code(obj, env, p) 544 | else: 545 | try: 546 | expandObj(obj) 547 | c = write_code(obj, env, p) 548 | except: 549 | return obj 550 | if not c: 551 | return obj 552 | if scheme.debug.debug_settings['jit']: 553 | import dis 554 | print obj.ast 555 | dis.dis(c) 556 | return function(c, p.env if p else Globals) 557 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | (This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.) 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | {description} 474 | Copyright (C) {year} {fullname} 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 489 | USA 490 | 491 | Also add information on how to contact you by electronic and paper mail. 492 | 493 | You should also get your employer (if you work as a programmer) or your 494 | school, if any, to sign a "copyright disclaimer" for the library, if 495 | necessary. Here is a sample; alter the names: 496 | 497 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 498 | library `Frob' (a library for tweaking knobs) written by James Random 499 | Hacker. 500 | 501 | {signature of Ty Coon}, 1 April 1990 502 | Ty Coon, President of Vice 503 | 504 | That's all there is to it! 505 | --------------------------------------------------------------------------------