OSDN Git Service

Merge branch 'master' of git.osdn.net:/gitroot/joypy/Thun
[joypy/Thun.git] / joy / joy.py
1 # -*- coding: utf-8 -*-
2 #
3 #    Copyright © 2014, 2015, 2017, 2018 Simon Forman
4 #
5 #    This file is part of Thun
6 #
7 #    Thun is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU General Public License as published by
9 #    the Free Software Foundation, either version 3 of the License, or
10 #    (at your option) any later version.
11 #
12 #    Thun is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU General Public License for more details.
16 #
17 #    You should have received a copy of the GNU General Public License
18 #    along with Thun.  If not see <http://www.gnu.org/licenses/>.
19 #
20 '''
21 This module implements an interpreter for a dialect of Joy that
22 attempts to stay very close to the spirit of Joy but does not precisely
23 match the behaviour of the original version(s) written in C.
24
25 '''
26 from builtins import input
27 from traceback import print_exc
28 from .parser import text_to_expression, ParseError, Symbol
29 from .utils.stack import stack_to_string
30 from .utils.errors import (
31     NotAListError,
32     NotAnIntError,
33     StackUnderflowError,
34     )
35
36
37 class UnknownSymbolError(KeyError): pass
38
39
40 def joy(stack, expression, dictionary, viewer=None):
41     '''Evaluate a Joy expression on a stack.
42
43   This function iterates through a sequence of terms which are either
44   literals (strings, numbers, sequences of terms) or function symbols.
45   Literals are put onto the stack and functions are looked up in the
46   dictionary and executed.
47
48     The viewer is a function that is called with the stack and expression
49     on every iteration, its return value is ignored.
50
51     :param stack stack: The stack.
52     :param stack expression: The expression to evaluate.
53     :param dict dictionary: A ``dict`` mapping names to Joy functions.
54     :param function viewer: Optional viewer function.
55     :rtype: (stack, (), dictionary)
56
57     '''
58     while expression:
59
60         if viewer: viewer(stack, expression)
61
62         term, expression = expression
63         if isinstance(term, Symbol):
64             try:
65                 term = dictionary[term]
66             except KeyError:
67                 raise UnknownSymbolError(term)
68             stack, expression, dictionary = term(stack, expression, dictionary)
69         else:
70             stack = term, stack
71
72     if viewer: viewer(stack, expression)
73     return stack, expression, dictionary
74
75
76 def run(text, stack, dictionary, viewer=None):
77     '''
78     Return the stack resulting from running the Joy code text on the stack.
79
80     :param str text: Joy code.
81     :param stack stack: The stack.
82     :param dict dictionary: A ``dict`` mapping names to Joy functions.
83     :param function viewer: Optional viewer function.
84     :rtype: (stack, (), dictionary)
85
86     '''
87     expression = text_to_expression(text)
88     return joy(stack, expression, dictionary, viewer)
89
90
91 def repl(stack=(), dictionary=None):
92     '''
93     Read-Evaluate-Print Loop
94
95     Accept input and run it on the stack, loop.
96
97     :param stack stack: The stack.
98     :param dict dictionary: A ``dict`` mapping names to Joy functions.
99     :rtype: stack
100
101     '''
102     if dictionary is None:
103         dictionary = {}
104     try:
105         while True:
106             print()
107             print(stack_to_string(stack), '<-top')
108             print()
109             try:
110                 text = input('joy? ')
111             except (EOFError, KeyboardInterrupt):
112                 break
113             try:
114                 stack, _, dictionary = run(text, stack, dictionary)
115             except:
116                 print_exc()
117     except:
118         print_exc()
119     print()
120     return stack
121
122
123 def interp(stack=(), dictionary=None):
124     '''
125     Simple REPL with no extra output, suitable for use in scripts.
126     '''
127     if dictionary is None:
128         dictionary = {}
129     try:
130         while True:
131             try:
132                 text = input()
133             except (EOFError, KeyboardInterrupt):
134                 break
135             try:
136                 stack, _, dictionary = run(text, stack, dictionary)
137             except UnknownSymbolError as sym:
138                 print('Unknown:', sym)
139             except StackUnderflowError as e:
140                 print(e)  # 'Not enough values on stack.'
141             except NotAnIntError:
142                 print('Not an integer.')
143             except NotAListError as e:
144                 print(e)  # 'Not a list.'
145             except:
146                 print_exc()
147             print(stack_to_string(stack))
148     except:
149         print_exc()
150     return stack