OSDN Git Service

Switch back to spaces for indentation.
[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 .library import NotAnIntError, StackUnderflowError
31
32
33 class UnknownSymbolError(KeyError): pass
34
35
36 def joy(stack, expression, dictionary, viewer=None):
37     '''Evaluate a Joy expression on a stack.
38
39   This function iterates through a sequence of terms which are either
40   literals (strings, numbers, sequences of terms) or function symbols.
41   Literals are put onto the stack and functions are looked up in the
42   dictionary and executed.
43
44     The viewer is a function that is called with the stack and expression
45     on every iteration, its return value is ignored.
46
47     :param stack stack: The stack.
48     :param stack expression: The expression to evaluate.
49     :param dict dictionary: A ``dict`` mapping names to Joy functions.
50     :param function viewer: Optional viewer function.
51     :rtype: (stack, (), dictionary)
52
53     '''
54     while expression:
55
56         if viewer: viewer(stack, expression)
57
58         term, expression = expression
59         if isinstance(term, Symbol):
60             try:
61                 term = dictionary[term]
62             except KeyError:
63                 raise UnknownSymbolError(term)
64             stack, expression, dictionary = term(stack, expression, dictionary)
65         else:
66             stack = term, stack
67
68     if viewer: viewer(stack, expression)
69     return stack, expression, dictionary
70
71
72 def run(text, stack, dictionary, viewer=None):
73     '''
74     Return the stack resulting from running the Joy code text on the stack.
75
76     :param str text: Joy code.
77     :param stack stack: The stack.
78     :param dict dictionary: A ``dict`` mapping names to Joy functions.
79     :param function viewer: Optional viewer function.
80     :rtype: (stack, (), dictionary)
81
82     '''
83     expression = text_to_expression(text)
84     return joy(stack, expression, dictionary, viewer)
85
86
87 def repl(stack=(), dictionary=None):
88     '''
89     Read-Evaluate-Print Loop
90
91     Accept input and run it on the stack, loop.
92
93     :param stack stack: The stack.
94     :param dict dictionary: A ``dict`` mapping names to Joy functions.
95     :rtype: stack
96
97     '''
98     if dictionary is None:
99         dictionary = {}
100     try:
101         while True:
102             print()
103             print(stack_to_string(stack), '<-top')
104             print()
105             try:
106                 text = input('joy? ')
107             except (EOFError, KeyboardInterrupt):
108                 break
109             try:
110                 stack, _, dictionary = run(text, stack, dictionary)
111             except:
112                 print_exc()
113     except:
114         print_exc()
115     print()
116     return stack
117
118
119 def interp(stack=(), dictionary=None):
120     '''
121     Simple REPL with no extra output, suitable for use in scripts.
122     '''
123     if dictionary is None:
124         dictionary = {}
125     try:
126         while True:
127             try:
128                 text = input()
129             except (EOFError, KeyboardInterrupt):
130                 break
131             try:
132                 stack, _, dictionary = run(text, stack, dictionary)
133             except UnknownSymbolError as sym:
134                 print('Unknown:', sym)
135             except StackUnderflowError:
136                 print('Not enough values on stack.')
137             except NotAnIntError:
138                 print('Not an integer.')
139             except:
140                 print_exc()
141             print(stack_to_string(stack))
142     except:
143         print_exc()
144     return stack