1import string,re
2from types import *
3
4data_type=["string", "int", "float",
5           "list", "map"]
6
7class Object:
8    def __init__(self, name="", type="", value=""):
9        self.name=name
10        self.type=type
11        self.value=value #for mapping: only attributes that are defined in object itself
12        self.attr_container_obj=None
13        self.definition=None
14        self.attr={} #for mappping: all attributes, including inherited ones
15        self.attr_list=[] #ditto, but in list format
16        self.debug=None
17    def append(self, value):
18        self.value.append(value)
19        value.attr_container_obj=self
20        if value.name:
21            self.attr[value.name]=value
22            self.attr_list.append(value)
23        if (value.name=="id" and self.attr.has_key("parents")) or \
24           (value.name=="parents" and self.attr.has_key("id")):
25            self.append(Object(name="children",value=[]))
26    def append_inherited(self, value):
27        self.attr[value.name]=value
28        self.attr_list.append(value)
29    def add_children(self, obj):
30        inst_obj=self.attr.get("children")
31        if not inst_obj: #checked earlier
32            raise SyntaxError, ("id attribute not defined and thus children neither",
33                                self.debug)
34        id=obj.attr.get("id")
35        if not id: #checked earlier
36            raise SyntaxError, ("id attribute not defined",
37                                obj.debug)
38        inst_obj.value.append(id.value)
39    def has_parent(self, dict, id):
40        """search parent hierarchy to find if any of it is same as type"""
41        id_obj=self.attr.get('id')
42        if not id_obj: #hmm.. attribute, look for parent type
43            type_obj=dict[self.name]
44            return type_obj.has_parent(dict,id)
45        if id_obj.value==id: return 1
46        for parent in self.attr['parents'].value:
47            par_obj=dict[parent]
48            if par_obj.has_parent(dict,id):
49                return 1
50        return 0
51
52    def __repr__(self):
53        return self.name+":"+self.type+":"+str(self.value)
54
55debug_reading=0
56class ParseDef:
57    """reads 'atlas def' files and created suitable 'web' for html/xml generators"""
58    def __init__(self):
59        self.id_dict={}
60        self.objects=Object(type="list",value=[])
61        self.lineno=0
62    def read_lines(self, fp, depth, parent_obj, line0=None):
63        """read object definitions in one level:
64           call itself recursively for sub objects"""
65        last_obj=None
66        while 1:
67            if line0: #line was already read, given as line0 argument
68                line=line0
69                line0=None
70                if debug_reading:
71                    print "???",line,self.lineno
72            else:
73                line=fp.readline()
74                self.lineno=self.lineno+1
75                if debug_reading:
76                    print "!!!",self.lineno,line,
77            if not line: break
78            if line[0] in '#\n': continue
79            space_count=len(re.match("( *)",line).group(1))
80            if space_count==len(line)-1: continue #Only spaces in line
81            #print " "*depth,depth,space_count,"{",line[:-1],"}",parent_obj
82            if space_count>depth: #sub object
83                if not last_obj:
84                    raise SyntaxError, ("Unexpected indentation",
85                                        (self.filename, self.lineno, space_count, line))
86                #current line belongs to sub object, lets call ourself
87                #it returns next line from our object
88                line0=self.read_lines(fp,space_count,last_obj,line)
89                last_obj=None
90                continue
91            if space_count<depth: #all objects in this level done
92                #and return line belonging to our parent
93                return line
94            #split into parts using ':' but not inside string
95            parts=[]
96            rest=line[space_count:-1]
97            while 1:
98                match=re.match("""([^"':]*):(.*)""",rest) #' (for xemacs highlight)
99                if match:
100                    parts.append(match.group(1))
101                    rest=match.group(2)
102                else:
103                    parts.append(rest)
104                    break
105            if len(parts)==3: #hmm.. probably name undefined
106                name,type,value=parts
107            elif len(parts)==2: #name and value defined, type undefined
108                name,value=parts
109                if len(value)==0:
110                    type="list" #guessing
111                else:
112                    type=""
113            else:
114                raise SyntaxError, ("Unexpected element numbers (things delimited with ':')",
115                                    (self.filename, self.lineno, space_count, line))
116            if type in ["list", "map"]: #new subojects
117                obj=last_obj=Object(name,type,value=[])
118            else:
119                #hack: reading several lines if """ string
120                if value[:3]=='"""' and not value[-3:]=='"""':
121                    value=value+"\n"
122                    while 1:
123                        line=fp.readline()
124                        self.lineno=self.lineno+1
125                        value=value+line
126                        if not line or string.find(line,'"""')>=0:
127                            break
128                try:
129                    evaled_value=eval(value)
130                except:
131                    print "Error at:",(self.filename, self.lineno, space_count, line)
132                    raise
133                obj=Object(name,type,evaled_value)
134                last_obj=None
135            parent_obj.append(obj)
136            obj.debug=(self.filename, self.lineno, space_count, line)
137    def read_file(self, filename):
138        """read one file"""
139        self.filename=filename
140        self.lineno=0
141        fp=open(filename)
142        self.read_lines(fp,0,self.objects)
143        fp.close()
144    def fill(self):
145        self.fill_id_dict()
146        self.fill_type()
147        self.fill_inherited_attributes()
148    def fill_id_dict(self):
149        """fill id_dict with all objects"""
150        for obj in self.objects.value:
151            id=None
152            for obj2 in obj.value:
153                if obj2.name=="id":
154                    id=obj2.value
155                    break
156            if not id:
157                raise SyntaxError, ("Id attribute is not specified for object",obj.debug)
158            if self.id_dict.has_key(id):
159                raise SyntaxError, ('Object with "'+id+'"-id already exists',obj.debug)
160            self.id_dict[id]=obj
161    def search_type(self, name):
162        """find type for this name"""
163        if name in data_type: return name
164        obj=self.id_dict[name]
165        if not obj.attr.has_key('parents'):
166            raise SyntaxError, ("Parents attribute is not specified for object",obj.debug)
167        parent_list=obj.attr['parents'].value
168        if not parent_list:
169            raise SyntaxError, ("Didn't found data_type for object",obj.debug)
170        return self.search_type(parent_list[0])
171    def fill_type_object(self, obj):
172        """recursively find types for all objects"""
173        if type(obj)!=InstanceType: return
174        if obj.name:
175            if not self.id_dict.has_key(obj.name):
176                raise SyntaxError, ('Name "'+obj.name+'" is not specified',obj.debug)
177            obj.type=self.search_type(obj.name)
178        if type(obj.value)==ListType:
179            for sub_obj in obj.value:
180                self.fill_type_object(sub_obj)
181    def fill_type(self):
182        self.fill_type_object(self.objects)
183    def fill_inherited_attributes_object(self, obj, p_obj,depth):
184        """recursively find attributes that are inherited from parents"""
185        if type(obj)!=InstanceType: return
186        if not p_obj.attr.has_key('parents'):
187            raise SyntaxError, ("Parents attribute is not specified for object",
188                                p_obj.debug)
189        if depth==1:
190            p_obj.add_children(obj)
191        #print obj.attr['id'].value,p_obj.attr['id'].value
192        for p_sub_obj in p_obj.value:
193            name=p_sub_obj.name
194            #print "-->",p_sub_obj.name
195            if not obj.attr.has_key(p_sub_obj.name):
196                obj.append_inherited(p_sub_obj)
197        for parent in p_obj.attr['parents'].value:
198            if not self.id_dict.has_key(parent):
199                raise SyntaxError, ("Parent \""+parent+"\" doesn't exist.",
200                                    obj.attr['parents'].debug)
201            self.fill_inherited_attributes_object(obj,self.id_dict[parent],depth+1)
202    def fill_inherited_attributes(self):
203        for obj in self.objects.value:
204            self.fill_inherited_attributes_object(obj,obj,0)
205
206
207def read_all_defs(filelist):
208    parser=ParseDef()
209    for file in filelist:
210        parser.read_file(file)
211    parser.fill()
212    #for item in parser.id_dict.items(): print item
213    return parser
214
215if __name__=="__main__":
216    parser=read_all_defs()
217