1 # -*- coding: utf-8 -*-
3 # Copyright © 2014, 2015, 2016, 2017 Simon Forman
5 # This file is part of Thun.
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.
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.
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/>.
21 This module exports a single function for converting text to a joy
22 expression as well as a single Symbol class and a single Exception type.
24 The Symbol string class is used by the interpreter to recognize literals
25 by the fact that they are not Symbol objects.
30 term = int | float | string | '[' joy ']' | symbol
32 A Joy expression is a sequence of zero or more terms. A term is a
33 literal value (integer, float, string, or Joy expression) or a function
34 symbol. Function symbols are unquoted strings and cannot contain square
35 brackets. Terms must be separated by blanks, which can be omitted
36 around square brackets.
39 from re import Scanner
40 from .utils.stack import list_to_stack
43 #TODO: explain the details of float lits and strings.
46 SYMBOL = r'[•\w!@$%^&*()_+<>?|\/;:`~,.=-]+'
48 STRING_DOUBLE_QUOTED = r'"(?:[^"\\]|\\.)*"'
49 STRING_SINGLE_QUOTED = r"'(?:[^'\\]|\\.)*'"
54 '''A string class that represents Joy function names.'''
55 __repr__ = str.__str__
58 def text_to_expression(text):
59 '''Convert a string to a Joy expression.
61 When supplied with a string this function returns a Python datastructure
62 that represents the Joy datastructure described by the text expression.
63 Any unbalanced square brackets will raise a ParseError.
65 :param str text: Text to convert.
67 :raises ParseError: if the parse fails.
69 return _parse(_tokenize(text))
72 class ParseError(ValueError):
73 '''Raised when there is a error while parsing text.'''
77 '''Convert a text into a stream of tokens.
79 Converts function names to Symbols.
81 Raise ParseError (with some of the failing text) if the scan fails.
83 tokens, rest = _scanner.scan(text)
86 'Scan failed at position %i, %r'
87 % (len(text) - len(rest), rest[:10])
94 Return a stack/list expression of the tokens.
102 stack[-1].append(frame)
107 raise ParseError('Extra closing bracket.')
108 frame[-1] = list_to_stack(frame[-1])
112 raise ParseError('Unclosed bracket.')
113 return list_to_stack(frame)
117 (FLOAT, lambda _, token: float(token)),
118 (INT, lambda _, token: int(token)),
119 (SYMBOL, lambda _, token: Symbol(token)),
120 (BRACKETS, lambda _, token: token),
121 (STRING_DOUBLE_QUOTED, lambda _, token: token[1:-1].replace('\\"', '"')),
122 (STRING_SINGLE_QUOTED, lambda _, token: token[1:-1].replace("\\'", "'")),