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 
21 import string
22 from atlas import Object, Messages
23 from atlas.typemap import *
24 import encoder, decoder
25 
26 special_characters = "+[]()@#$=\n\r"
27 
28 class 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 
42 def get_encoder(stream_flag=None):
43  return Encoder(stream_flag)
44 
45 def gen_packed(obj):
46  return "[%s]\n" % map2packed(obj)
47 
48 type_name2packed_code = {"map": ("[", "]"),
49  "list": ("(", ")"),
50  "int": ("@", ""),
51  "float": ("#", ""),
52  "string": ("$", "")}
53 
54 
55 def 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 
76 def 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 
85 def 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.data 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.data = self.data + chr(eval("0x" + self.quote_data))
124  self.quote_on = 0
125  else:
126  self.data = self.data + 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.data = ""
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.data))
190 
191  def start_float(self):
192  self.start_value(self.end_float)
193  def end_float(self):
194  self.end_value(float(self.data))
195 
196  def start_string(self):
197  self.start_value(self.end_string)
198  def end_string(self):
199  self.end_value(self.data)
200 
201  def name_value(self):
202  self.name_stack[-1] = self.data
203  self.data = ""
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 
223 def get_parser():
224  packed_msg_parser=PackedParser()
225  return packed_msg_parser
226 
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
def start_value(self, mode)
Definition: packed.py:134
def setup(self, stream_flag=None)
Definition: decoder.py:23