2 * Copyright (C) 2003-2006 Gabest
3 * http://www.gabest.org
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GNU Make; see the file COPYING. If not, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
26 #define iswcsym(c) (iswalnum(c) || (c) == '_')
39 void File::Parse(InputStream
& s
, LPCWSTR predef
)
41 Reference
* pRef
= CreateRootRef();
45 try {ParseDefs(WCharInputStream(predef
), pRef
);}
46 catch(Exception
& e
) {ASSERT(0); TRACE(_T("%s\n"), e
.ToString());}
54 if(s
.PeekChar() != Stream::EOS
)
56 TRACE(_T("Warning: parsing ended before EOF!\n"));
60 void File::ParseDefs(InputStream
& s
, Reference
* pParentRef
)
62 while(s
.SkipWhiteSpace(L
";") != '}' && s
.PeekChar() != Stream::EOS
)
64 NodePriority priority
= PNormal
;
65 CAtlList
<CStringW
> types
;
68 int c
= s
.SkipWhiteSpace();
70 if(c
== '*') {s
.GetChar(); priority
= PLow
;}
71 else if(c
== '!') {s
.GetChar(); priority
= PHigh
;}
75 if(s
.SkipWhiteSpace() == '#')
83 if(name
.IsEmpty()) s
.ThrowError(_T("syntax error"));
87 Reference
* pRef
= pParentRef
;
89 while(types
.GetCount() > 1)
90 pRef
= CreateRef(CreateDef(pRef
, types
.RemoveHead()));
92 Definition
* pDef
= NULL
;
95 pDef
= CreateDef(pRef
, types
.RemoveHead(), name
, priority
);
97 c
= s
.SkipWhiteSpace(L
":=");
99 if(c
== '"' || c
== '\'') ParseQuotedString(s
, pDef
);
100 else if(iswdigit(c
) || c
== '+' || c
== '-') ParseNumber(s
, pDef
);
101 else if(pDef
->IsType(L
"@")) ParseBlock(s
, pDef
);
102 else ParseRefs(s
, pDef
);
108 void File::ParseTypes(InputStream
& s
, CAtlList
<CStringW
>& types
)
114 for(int c
= s
.SkipWhiteSpace(); iswcsym(c
) || c
== '.' || c
== '@'; c
= s
.PeekChar())
120 if(str
.IsEmpty()) s
.ThrowError(_T("'type' cannot be an empty string"));
121 if(!iswcsym(s
.PeekChar())) s
.ThrowError(_T("unexpected dot after type '%s'"), CString(str
));
128 if(str
.IsEmpty() && iswdigit(c
)) s
.ThrowError(_T("'type' cannot start with a number"));
129 if((!str
.IsEmpty() || !types
.IsEmpty()) && c
== '@') s
.ThrowError(_T("unexpected @ in 'type'"));
141 void File::ParseName(InputStream
& s
, CStringW
& name
)
145 for(int c
= s
.SkipWhiteSpace(); iswcsym(c
); c
= s
.PeekChar())
147 if(name
.IsEmpty() && iswdigit(c
)) s
.ThrowError(_T("'name' cannot start with a number"));
148 name
+= (WCHAR
)s
.GetChar();
152 void File::ParseQuotedString(InputStream
& s
, Definition
* pDef
)
156 int quote
= s
.SkipWhiteSpace();
157 if(quote
!= '"' && quote
!= '\'') s
.ThrowError(_T("expected qouted string"));
160 for(int c
= s
.PeekChar(); c
!= Stream::EOS
; c
= s
.PeekChar())
163 if(c
== quote
) {pDef
->SetAsValue(Definition::string
, v
); return;}
164 if(c
== '\n') s
.ThrowError(_T("qouted string terminated unexpectedly by a new-line character"));
165 if(c
== '\\') c
= s
.GetChar();
166 if(c
== Stream::EOS
) s
.ThrowError(_T("qouted string terminated unexpectedly by EOS"));
170 s
.ThrowError(_T("unterminated quoted string"));
173 void File::ParseNumber(InputStream
& s
, Definition
* pDef
)
177 for(int c
= s
.SkipWhiteSpace(); iswxdigit(c
) || wcschr(L
"+-.x:", c
); c
= s
.PeekChar())
179 if((c
== '+' || c
== '-') && !v
.IsEmpty()
180 || (c
== '.' && (v
.IsEmpty() || v
.Find('.') >= 0 || v
.Find('x') >= 0))
181 || (c
== 'x' && v
!= '0')
182 || (c
>= 'a' && c
<= 'f' || c
>= 'A' && c
<= 'F') && v
.Find(L
"0x") != 0
183 || (c
== ':' && v
.IsEmpty()))
185 s
.ThrowError(_T("unexpected character '%c' in number"), (TCHAR
)c
);
188 v
+= (WCHAR
)s
.GetChar();
191 if(v
.IsEmpty()) s
.ThrowError(_T("invalid number"));
193 for(int c
= s
.SkipWhiteSpace(); iswcsym(c
); c
= s
.PeekChar())
195 u
+= (WCHAR
)s
.GetChar();
198 pDef
->SetAsNumber(v
, u
);
201 void File::ParseBlock(InputStream
& s
, Definition
* pDef
)
205 int c
= s
.SkipWhiteSpace(L
":=");
206 if(c
!= '{') s
.ThrowError(_T("expected '{'"));
211 for(int c
= s
.PeekChar(); c
!= Stream::EOS
; c
= s
.PeekChar())
214 if(c
== '}' && depth
== 0) {pDef
->SetAsValue(Definition::block
, v
); return;}
215 if(c
== '\\') {v
+= (WCHAR
)c
; c
= s
.GetChar();}
216 else if(c
== '{') depth
++;
217 else if(c
== '}') depth
--;
218 if(c
== Stream::EOS
) s
.ThrowError(_T("block terminated unexpectedly by EOS"));
222 s
.ThrowError(_T("unterminated block"));
225 void File::ParseRefs(InputStream
& s
, Definition
* pParentDef
, LPCWSTR term
)
227 int c
= s
.SkipWhiteSpace();
231 if(pParentDef
->IsValue()) s
.ThrowError(_T("cannot mix references with other values"));
236 ParseDefs(s
, CreateRef(pParentDef
));
243 // TODO: allow spec references: parent.<type>, self.<type>, child.<type>
245 Definition
* pDef
= GetDefByName(str
);
246 if(!pDef
) s
.ThrowError(_T("cannot find definition of '%s'"), CString(str
));
248 if(!pParentDef
->IsVisible(pDef
)) s
.ThrowError(_T("cannot access '%s' from here"), CString(str
));
250 pParentDef
->AddTail(pDef
);
252 else if(!wcschr(term
, c
) && c
!= Stream::EOS
)
254 s
.ThrowError(_T("unexpected character '%c'"), (TCHAR
)c
);
257 c
= s
.SkipWhiteSpace();
259 while(!wcschr(term
, c
) && c
!= Stream::EOS
);