Merge with MPC-HC 6d1472b2f18266d92e5bc068667de348c0cd3b3b.
[xy_vsfilter.git] / src / subtitles / libssf / File.cpp
blob5d6724924ca850b8cfcf423e449da295fb1fbb05
1 /*
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)
8 * any later version.
9 *
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
22 #include "stdafx.h"
23 #include "File.h"
25 #ifndef iswcsym
26 #define iswcsym(c) (iswalnum(c) || (c) == '_')
27 #endif
29 namespace ssf
31 File::File()
35 File::~File()
39 void File::Parse(InputStream& s, LPCWSTR predef)
41 Reference* pRef = CreateRootRef();
43 SetPredefined(true);
45 try {ParseDefs(WCharInputStream(predef), pRef);}
46 catch(Exception& e) {ASSERT(0); TRACE(_T("%s\n"), e.ToString());}
48 SetPredefined(false);
50 ParseDefs(s, pRef);
52 Commit();
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;
66 CStringW name;
68 int c = s.SkipWhiteSpace();
70 if(c == '*') {s.GetChar(); priority = PLow;}
71 else if(c == '!') {s.GetChar(); priority = PHigh;}
73 ParseTypes(s, types);
75 if(s.SkipWhiteSpace() == '#')
77 s.GetChar();
78 ParseName(s, name);
81 if(types.IsEmpty())
83 if(name.IsEmpty()) s.ThrowError(_T("syntax error"));
84 types.AddTail(L"?");
87 Reference* pRef = pParentRef;
89 while(types.GetCount() > 1)
90 pRef = CreateRef(CreateDef(pRef, types.RemoveHead()));
92 Definition* pDef = NULL;
94 if(!types.IsEmpty())
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);
105 s.GetChar();
108 void File::ParseTypes(InputStream& s, CAtlList<CStringW>& types)
110 types.RemoveAll();
112 CStringW str;
114 for(int c = s.SkipWhiteSpace(); iswcsym(c) || c == '.' || c == '@'; c = s.PeekChar())
116 c = s.GetChar();
118 if(c == '.')
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));
123 types.AddTail(str);
124 str.Empty();
126 else
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'"));
131 str += (WCHAR)c;
135 if(!str.IsEmpty())
137 types.AddTail(str);
141 void File::ParseName(InputStream& s, CStringW& name)
143 name.Empty();
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)
154 CStringW v;
156 int quote = s.SkipWhiteSpace();
157 if(quote != '"' && quote != '\'') s.ThrowError(_T("expected qouted string"));
158 s.GetChar();
160 for(int c = s.PeekChar(); c != Stream::EOS; c = s.PeekChar())
162 c = s.GetChar();
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"));
167 v += (WCHAR)c;
170 s.ThrowError(_T("unterminated quoted string"));
173 void File::ParseNumber(InputStream& s, Definition* pDef)
175 CStringW v, u;
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)
203 CStringW v;
205 int c = s.SkipWhiteSpace(L":=");
206 if(c != '{') s.ThrowError(_T("expected '{'"));
207 s.GetChar();
209 int depth = 0;
211 for(int c = s.PeekChar(); c != Stream::EOS; c = s.PeekChar())
213 c = s.GetChar();
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"));
219 v += (WCHAR)c;
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"));
233 if(c == '{')
235 s.GetChar();
236 ParseDefs(s, CreateRef(pParentDef));
238 else if(iswcsym(c))
240 CStringW str;
241 ParseName(s, str);
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);