1 # translation.py - Translate between Brainfuck commands and Dogeparty tokens
2 # Copyright (C) 2014 Rob Myers rob@robmyers.org
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17 ################################################################################
19 ################################################################################
24 ################################################################################
25 # Token translation and formatting
26 ################################################################################
28 # BF symbols to Dogeparty token names
29 SYMBOLS_TO_TOKENS = {'>':'INCP', '<':'DECP', '+':'INCB', '-':'DECB', '.':'PUTB',
30 ',':'GETB', '[':'JFOR', ']':'JBAK'}
32 # Dogeparty token names to BF symbols
33 TOKENS_TO_SYMBOLS = {v: k for k, v in SYMBOLS_TO_TOKENS.items()}
35 TOKENS = TOKENS_TO_SYMBOLS.keys()
37 # BF symbols and nothing else
38 TOKENS_RE = re.compile(r'[^><+\-.,[\]]+')
41 TOKEN_RUN_RE = re.compile(r'(>+|<+|\++|-+|\.+|,+|\[+|]+)')
44 INITIAL_COMMENT = re.compile(r'^\[[^\]]+]')
47 ################################################################################
48 # Parse source code and convert to token amounts
49 ################################################################################
51 def strip_non_token_characters(source):
52 """Return a copy of the sourc with all whitespace, newlines, letters,
53 numbers, and anything other than token characters removed from the
55 return re.sub(TOKENS_RE, '', source)
57 def remove_initial_comment(source):
58 """Remove the first [...] block from the code, it's just a comment"""
59 return re.sub(INITIAL_COMMENT, '', source)
61 def run_to_token(run):
62 """Convert a run of characters to a tokem count pair"""
63 return (SYMBOLS_TO_TOKENS[run[0]],
66 def code_to_token_runs(source):
67 """Convert program source to a list of token/run length pairs"""
68 runs = re.findall(TOKEN_RUN_RE, source)
69 return [run_to_token(run) for run in runs]
71 def source_to_tokens(source, strip_initial_comment):
72 """Clean the source code and represent the program as pairs of
74 source = strip_non_token_characters(source)
75 if strip_initial_comment:
76 source = remove_initial_comment(source)
77 return code_to_token_runs(source)
79 def write_transactions_csv(token_pairs, csvfile):
80 writer = csv.writer(csvfile)
81 for pair in token_pairs:
82 writer.writerow((pair[0], pair[1]))
84 def source_to_tokens_csv(source, csvfile, strip_initial_comment=True):
85 token_pairs = source_to_tokens(source, strip_initial_comment)
86 write_transactions_csv(token_pairs, csvfile)
89 ################################################################################
90 # Convert token amounts to source code
91 ################################################################################
93 def token_to_run(token_pair):
94 """Convert a token/count tuple to a string containting count instances of
96 return TOKENS_TO_SYMBOLS[token_pair[0]] * token_pair[1]
98 def token_runs_to_code(token_pairs):
99 """Convert a sequence of token/count tuples to a string of operator
102 for pair in token_pairs:
103 code += token_to_run(pair)
106 def json_token_amounts_to_pairs(tokens_json):
107 """Extract a list of (TOKEN,N) pairs from the json list of token sends"""
109 for send in tokens_json:
110 if send["status"] == "valid":
111 pairs.append((send["asset"], send["quantity"]))
115 ################################################################################
116 # Compare token lists
117 ################################################################################
119 class TokenList(object):
120 """A class to keep track of a list of tokens"""
123 """Create a new empty list"""
126 def append(self, token, count):
127 """Append the token count to the list"""
128 self.tokens.append((token, int(count)))
130 def from_json(self, json):
131 """Initialize the token list from a json rpc query response"""
133 self.append(send["asset"], send["quantity"])
135 def equals(self, other):
136 """Compare the token list with another token list.
137 Return True if the list is equal, false if not."""
139 num_tokens = len(self.tokens)
140 if num_tokens != len(other.tokens):
143 for i in range(0, num_tokens):
144 if self.tokens[i] != other.tokens[i]:
149 def is_subsequence_of(self, other):
150 """Check whether this is a proper subsequence of other.
151 An equal list is not a proper subsequence."""
153 num_tokens = len(self.tokens)
154 if num_tokens >= len(other.tokens):
157 for i in range(0, num_tokens):
158 if self.tokens[i] != other.tokens[i]: