Atlas 0.7.0
Networking protocol for the Worldforge system.
packed.py
1#Packed parser and generator
2#see forge/protocols/atlas/spec/packed_syntax.html
3
4#Copyright 2001 by Aloril
5
6#This library is free software; you can redistribute it and/or
7#modify it under the terms of the GNU Lesser General Public
8#License as published by the Free Software Foundation; either
9#version 2.1 of the License, or (at your option) any later version.
10
11#This library is distributed in the hope that it will be useful,
12#but WITHOUT ANY WARRANTY; without even the implied warranty of
13#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14#Lesser General Public License for more details.
15
16#You should have received a copy of the GNU Lesser General Public
17#License along with this library; if not, write to the Free Software
18#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20
21import string
22from atlas import Object, Messages
23from atlas.typemap import *
24import encoder, decoder
25
26special_characters = "+[]()@#$=\n\r"
27
28class PackedException(Exception): pass
29
31 begin_string = ""
32 middle_string = ""
33 end_string = ""
34 empty_end_string = ""
35
36 def encode1stream(self, object):
37 return string.join(to_string_and_type(object), "") + "\n"
38
39 encode1 = encode1stream
40
41
42def get_encoder(stream_flag=None):
43 return Encoder(stream_flag)
44
45def gen_packed(obj):
46 return "[%s]\n" % map2packed(obj)
47
48type_name2packed_code = {"map": ("[", "]"),
49 "list": ("(", ")"),
50 "int": ("@", ""),
51 "float": ("#", ""),
52 "string": ("$", "")}
53
54
55def to_string_and_type(value):
56 type_str = get_atlas_type(value)
57 if type_str == "string":
58 str_value = ""
59 for ch in value:
60 if ch in special_characters:
61 hex_str = hex(ord(ch))[2:]
62 if len(hex_str)<2: hex_str = "0" + hex_str
63 str_value = str_value + "+" + hex_str
64 else:
65 str_value = str_value + ch
66 elif type_str == "map":
67 str_value = map2packed(value)
68 elif type_str == "list":
69 str_value = list2packed(value)
70 else:
71 str_value = str(value)
72 end_type = type_name2packed_code[type_str][1]
73 type_str = type_name2packed_code[type_str][0]
74 return type_str, str_value, end_type
75
76def map2packed(obj):
77 """this encodes mappings"""
78 str_list = []
79 add_item = str_list.append
80 for name, value in list(obj.items()):
81 type_str, str_value, end_type = to_string_and_type(value)
82 add_item('%s%s=%s%s' % (type_str, name, str_value, end_type))
83 return string.join(str_list, "")
84
85def list2packed(lst):
86 str_list = []
87 add_item = str_list.append
88 for item in lst:
89 type_str, str_value, end_type = to_string_and_type(item)
90 add_item('%s%s%s' % (type_str, str_value, end_type))
91 return string.join(str_list,"")
92
93
94
95
97 def __init__(self, stream_flag=None):
98 """uses tree that start from root_obj, current route to leave
99 is kept in obj_stack"""
100 #Root object is never removed or visible for parser users,
101 #incoming objects are added to its value
102 self.root_obj = []
103 self.name_stack = []
104 self.obj_stack = [self.root_obj]
105 #resulting complete atlas 'messages' are added here
106 self.mode = "none"
107 self.quote_on = 0
108 self.setup(stream_flag=None)
109
110 def eos(self):
111 """end of stream"""
112 return not self.datadata and self.obj_stack == [[]]
113
114 def feed(self, msg):
115 for ch in msg:
116 if ch in special_characters:
117 self.character2method[ch](self)
118 elif self.quote_on:
119 self.quote_data = self.quote_data + ch
120 if ch not in string.digits + "abcdef":
121 raise PackedException("Illegal character in quoted string" + ch)
122 if len(self.quote_data)==2:
123 self.datadata = self.datadata + chr(eval("0x" + self.quote_data))
124 self.quote_on = 0
125 else:
126 self.datadata = self.datadata + ch
127
128 def exec_mode(self):
129 if self.mode!="none":
130 method = self.mode
131 self.mode = "none"
132 method()
133
134 def start_value(self, mode):
135 self.exec_mode()
136 self.mode = mode
137 self.name_stack.append("")
138
139 def end_value(self, value):
140 """put value into mapping/list"""
141 self.exec_mode()
142 self.datadata = ""
143 obj = self.obj_stack[-1]
144 name = self.name_stack.pop()
145 if name:
146 if not isinstance(obj,Object):
147 raise PackedException("attribute outside mapping (%s:%s)!" % \
148 (name, value))
149 setattr(obj, name, value)
150 else:
151 if type(obj)!=ListType:
152 raise PackedException("value mapping list (%s)!" % value)
153 obj.append(value)
154
155 def push_value(self, initial_value):
156 """for list/map: add to stack"""
157 self.obj_stack.append(initial_value)
158 def pop_value(self):
159 """for list/map: remove from stack"""
160 self.exec_mode()
161 obj = self.obj_stack.pop()
162 self.end_value(obj)
163 #moved here from end_map (because in sinuglar decoding also list possible for top level object
164 if len(self.obj_stack)==1:
165 obj=self.obj_stack[0][0]
166 self.msgList.append(obj)
167 del self.obj_stack[0][0]
168
169 #reactions to special characters
170 def quote(self):
171 self.quote_on = 1
172 self.quote_data = ""
173
174 def start_map(self):
175 self.start_value("none")
176 self.push_value(Object())
177 def end_map(self):
178 self.pop_value()
179
180 def start_list(self):
181 self.start_value("none")
182 self.push_value([])
183 def end_list(self):
184 self.pop_value()
185
186 def start_int(self):
187 self.start_value(self.end_int)
188 def end_int(self):
189 self.end_value(int(self.datadata))
190
191 def start_float(self):
192 self.start_value(self.end_float)
193 def end_float(self):
194 self.end_value(float(self.datadata))
195
196 def start_string(self):
197 self.start_value(self.end_string)
198 def end_string(self):
199 self.end_value(self.datadata)
200
201 def name_value(self):
202 self.name_stack[-1] = self.datadata
203 self.datadata = ""
204
205 def ignore(self):
206 "ignore character"
207 pass
208
209 character2method = {"+": quote,
210 "[": start_map,
211 "]": end_map,
212 "(": start_list,
213 ")": end_list,
214 "@": start_int,
215 "#": start_float,
216 "$": start_string,
217 "=": name_value,
218 "\n": ignore,
219 "\r": ignore}
220
221
222
223def get_parser():
224 packed_msg_parser=PackedParser()
225 return packed_msg_parser
226
def setup(self, stream_flag=None)
Definition: decoder.py:23
def start_value(self, mode)
Definition: packed.py:134
def __init__(self, stream_flag=None)
Definition: packed.py:97
def push_value(self, initial_value)
Definition: packed.py:155
def end_value(self, value)
Definition: packed.py:139