Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
XmlParser.cs
1using System;
6
7namespace Waher.Script.Xml
8{
12 public class XmlParser : IKeyWord
13 {
17 public XmlParser()
18 {
19 }
20
24 public string KeyWord => "<";
25
29 public string[] Aliases => null;
30
34 public string[] InternalKeywords => Array.Empty<string>();
35
42 public bool TryParse(ScriptParser Parser, out ScriptNode Result)
43 {
44 Result = this.ParseDocument(Parser);
45 return !(Result is null);
46 }
47
48 private XmlScriptDocument ParseDocument(ScriptParser Parser)
49 {
50 ChunkedList<XmlScriptProcessingInstruction> ProcessingInstructions = null;
51 int Start = Parser.Start;
52 int Pos;
53 char ch;
54
55 while (Parser.PeekNextChar() == '?')
56 {
57 Parser.NextChar();
58 Pos = Parser.Position;
59
60 while ((ch = Parser.NextChar()) != '?' && ch != 0)
61 ;
62
63 if (ch == 0)
64 return null;
65
66 if (ProcessingInstructions is null)
67 ProcessingInstructions = new ChunkedList<XmlScriptProcessingInstruction>();
68
69 string Text = Parser.Expression.Script.Substring(Pos, Parser.Position - Pos - 1);
70
71 if (Parser.NextChar() != '>')
72 throw Parser.SyntaxError("> expected.");
73
74 ProcessingInstructions.Add(new XmlScriptProcessingInstruction(Text,
75 Start, Parser.Position - Start, Parser.Expression));
76
77 Parser.SkipWhiteSpace();
78
79 if (Parser.NextChar() != '<')
80 return null;
81
82 Start = Parser.Position;
83 }
84
85 Parser.UndoChar();
86 XmlScriptElement Root = this.ParseElement(Parser);
87 if (Root is null)
88 return null;
89
90 return new XmlScriptDocument(Root, ProcessingInstructions?.ToArray() ?? Array.Empty<XmlScriptProcessingInstruction>(),
91 Start, Parser.Position - Start, Parser.Expression);
92 }
93
94 private XmlScriptElement ParseElement(ScriptParser Parser)
95 {
96 if (Parser.NextChar() != '<')
97 throw Parser.SyntaxError("< expected.");
98
99 ChunkedList<XmlScriptAttribute> Attributes = null;
100 XmlScriptAttribute Xmlns = null;
101 IElement ElementValue;
102 XmlScriptAttribute Attribute;
103 int ElementStart = Parser.Position;
104 string ElementName = ParseName(Parser);
105 string Value;
106 char ch;
107
108 Parser.SkipWhiteSpace();
109
110 while ((ch = Parser.PeekNextChar()) != 0)
111 {
112 if (ch == '>')
113 {
114 Parser.NextChar();
115
116 XmlScriptElement Element = new XmlScriptElement(ElementName, Xmlns, Attributes?.ToArray() ?? Array.Empty<XmlScriptAttribute>(),
117 ElementStart, Parser.Position - ElementStart, Parser.Expression);
118 XmlScriptWildcard LastWildCard = null;
119 XmlScriptNode NewChild;
120
121 while (true)
122 {
123 int Pos = Parser.Position;
124 int i = Parser.Expression.Script.IndexOf('<', Pos);
125 int Len;
126
127 if (i < 0)
128 throw Parser.SyntaxError("Open element.");
129
130 if (i > Pos)
131 {
132 Len = i - Pos;
133
134 Element.Add(NewChild = new XmlScriptText(Parser.Expression.Script.Substring(Pos, Len),
135 Pos, Len, Parser.Expression));
136
137 Parser.SkipChars(Len);
138
139 if (!(LastWildCard is null) && !NewChild.IsWhitespace)
140 {
141 LastWildCard.Next = NewChild;
142 LastWildCard = null;
143 }
144 }
145
146 NewChild = null;
147
148 switch (Parser.PeekNextChars(2))
149 {
150 case "</":
151 Parser.NextChar();
152 Parser.NextChar();
153
154 if (ParseName(Parser) != ElementName)
155 throw Parser.SyntaxError("Expected end of element " + ElementName);
156
157 if (Parser.NextChar() != '>')
158 throw Parser.SyntaxError("> expected.");
159
160 return Element;
161
162 case "<!":
163 if (Parser.IsNextChars("<!--"))
164 {
165 Parser.SkipChars(4);
166
167 Pos = Parser.Position;
168 i = Parser.Expression.Script.IndexOf("-->", Pos);
169 if (i < 0)
170 throw Parser.SyntaxError("Unterminated comment.");
171
172 Len = i - Pos;
173 Element.Add(NewChild = new XmlScriptComment(Parser.Expression.Script.Substring(Pos, Len),
174 Pos, Len, Parser.Expression));
175
176 Parser.SkipChars(Len + 3);
177 }
178 else if (Parser.IsNextChars("<![CDATA["))
179 {
180 Parser.SkipChars(9);
181
182 Pos = Parser.Position;
183 i = Parser.Expression.Script.IndexOf("]]>", Pos);
184 if (i < 0)
185 throw Parser.SyntaxError("Unterminated CDATA construct.");
186
187 Len = i - Pos;
188 Element.Add(NewChild = new XmlScriptCData(Parser.Expression.Script.Substring(Pos, Len),
189 Pos, Len, Parser.Expression));
190
191 Parser.SkipChars(Len + 3);
192 }
193 else
194 throw Parser.SyntaxError("Expected <!-- or <![CDATA[");
195
196 break;
197
198 case "<[":
199 Parser.SkipChars(2);
200
201 ScriptNode Node = Parser.ParseSequence();
202 Parser.SkipWhiteSpace();
203
204 if (!Parser.IsNextChars("]>"))
205 throw Parser.SyntaxError("]> expected.");
206
207 Parser.SkipChars(2);
208
209 if (!(Node is null))
210 Element.Add(NewChild = new XmlScriptValue(Node, Node.Start, Node.Length, Node.Expression));
211 break;
212
213 case "<(":
214 Parser.SkipChars(2);
215
216 Node = Parser.ParseSequence();
217 Parser.SkipWhiteSpace();
218
219 if (!Parser.IsNextChars(")>"))
220 throw Parser.SyntaxError(")> expected.");
221
222 Parser.SkipChars(2);
223
224 if (!(Node is null))
225 Element.Add(NewChild = new XmlScriptValue(Node, Node.Start, Node.Length, Node.Expression));
226 break;
227
228 case "<*":
229 Parser.SkipChars(2);
230 if (Parser.PeekNextChar() != '>')
231 throw Parser.SyntaxError("> expected.");
232
233 Parser.NextChar();
234
235 if (LastWildCard is null)
236 Element.Add(LastWildCard = new XmlScriptWildcard(Pos, Parser.Position - Pos, Parser.Expression));
237 continue;
238
239 default:
240 Element.Add(NewChild = this.ParseElement(Parser));
241 break;
242 }
243
244 if (!(LastWildCard is null) && !(NewChild is null) && !NewChild.IsWhitespace)
245 {
246 LastWildCard.Next = NewChild;
247 LastWildCard = null;
248 }
249 }
250 }
251 else if (ch == '/')
252 {
253 Parser.NextChar();
254 if (Parser.NextChar() != '>')
255 throw Parser.SyntaxError("> expected.");
256
257 return new XmlScriptElement(ElementName, Xmlns, Attributes?.ToArray() ?? Array.Empty<XmlScriptAttribute>(),
258 ElementStart, Parser.Position - ElementStart, Parser.Expression);
259 }
260 else if (char.IsLetter(ch) || ch == '_' || ch == ':')
261 {
262 int AttributeStart = Parser.Position;
263 string AttributeName = ParseName(Parser);
264
265 Parser.SkipWhiteSpace();
266 if (Parser.NextChar() != '=')
267 throw Parser.SyntaxError("= expected.");
268
269 bool Bak = Parser.CanSkipWhitespace;
270 Parser.CanSkipWhitespace = false;
271 ScriptNode Node = Parser.ParsePowers();
272 Parser.CanSkipWhitespace = Bak;
273
274 if (Node is ConstantElement Constant)
275 {
276 ElementValue = Constant.Constant;
277 Value = XmlScriptNode.EvaluateString(ElementValue);
278
279 Attribute = new XmlScriptAttributeString(AttributeName, Value,
280 AttributeStart, Parser.Position - AttributeStart, Parser.Expression);
281 }
282 else
283 {
284 Attribute = new XmlScriptAttributeScript(AttributeName, Node,
285 AttributeStart, Parser.Position - AttributeStart, Parser.Expression);
286 }
287
288 if (AttributeName == "xmlns")
289 Xmlns = Attribute;
290 else
291 {
292 if (Attributes is null)
293 Attributes = new ChunkedList<XmlScriptAttribute>();
294
295 Attributes.Add(Attribute);
296 }
297 }
298 else if (ch == '*')
299 {
300 int AttributeStart = Parser.Position;
301 Parser.NextChar();
302
303 if (Attributes is null)
304 Attributes = new ChunkedList<XmlScriptAttribute>();
305
306 Attributes.Add(new XmlScriptAttributeWildcard(AttributeStart,
307 Parser.Position - AttributeStart, Parser.Expression));
308 }
309 else if (char.IsWhiteSpace(ch))
310 Parser.NextChar();
311 else
312 throw Parser.SyntaxError("Invalid XML Element.");
313 }
314
315 return null;
316 }
317
318 private static string ParseName(ScriptParser Parser)
319 {
320 int Pos = Parser.Position;
321 char ch = Parser.PeekNextChar();
322
323 if (!(char.IsLetter(ch) || ch == '_' || ch == ':'))
324 throw Parser.SyntaxError("Name expected.");
325
326 while (Parser.NextChar() != 0)
327 {
328 ch = Parser.PeekNextChar();
329
330 if (char.IsLetterOrDigit(ch) || char.IsDigit(ch) ||
331 ch == '.' || ch == '-' || ch == '_' || ch == ':')
332 {
333 continue;
334 }
335
336 break;
337 }
338
339 return Parser.Expression.Script.Substring(Pos, Parser.Position - Pos);
340 }
341
342 }
343}
A chunked list is a linked list of chunks of objects of type T .
Definition: ChunkedList.cs:54
void Add(T Item)
Adds an item to the collection.
Definition: ChunkedList.cs:272
Base class for all types of elements.
Definition: Element.cs:14
string Script
Original script string.
Definition: Expression.cs:207
Represents a constant element value.
Base class for all nodes in a parsed script tree.
Definition: ScriptNode.cs:69
int Length
Length of expression covered by node.
Definition: ScriptNode.cs:101
Expression Expression
Expression of which the node is a part.
Definition: ScriptNode.cs:177
int Start
Start position in script expression.
Definition: ScriptNode.cs:92
Script parser, for custom parsers.
Definition: ScriptParser.cs:10
SyntaxException SyntaxError(string Message)
Returns a Syntax Error Exception object.
ScriptNode ParsePowers()
Parses powers.
bool IsNextChars(string Token)
If the next characters to be parsed is a given token.
int Start
Start position in expression
Definition: ScriptParser.cs:28
int Position
Current parsing position.
Definition: ScriptParser.cs:38
Expression Expression
Expression being parsed.
Definition: ScriptParser.cs:43
void UndoChar()
Undoes a character in the parsing of an expression.
Definition: ScriptParser.cs:82
string PeekNextChars(int NrChars)
Returns the next given number of characters to be parsed, without moving the position forward one cha...
Definition: ScriptParser.cs:92
void SkipChars(int NrChars)
Skips a predefined number of characters.
char PeekNextChar()
Returns the next character to be parsed, without moving the position forward one character....
bool CanSkipWhitespace
If whitespace can be skipped (true), or if it has semantic meaning to the custom parser (false).
Definition: ScriptParser.cs:49
void SkipWhiteSpace()
If current position is whitespace, moves the current position forward to the first non-whitespace cha...
char NextChar()
Returns the next character to be parsed, and moves the position forward one character....
ScriptNode ParseSequence()
Parses a sequence of statements.
Abstract base class for XML Script attribute nodes.
XML Script attribute node, whose value is defined by script.
XML Script attribute node, whose value is defined by script.
XML Script attribute wildcard node, used in pattern matching to match any attribute or sequence of at...
Represents an script-based XML document.
Base class for all XML Script nodes in a parsed script tree.
static string EvaluateString(ScriptNode Node, Variables Variables)
Evaluates a script node to a string.
virtual bool IsWhitespace
If the node represents whitespace.
Parses an XML document
Definition: XmlParser.cs:13
bool TryParse(ScriptParser Parser, out ScriptNode Result)
Tries to parse a script node.
Definition: XmlParser.cs:42
string[] Aliases
Keyword aliases, if available, null if none.
Definition: XmlParser.cs:29
string KeyWord
Keyword associated with custom parser.
Definition: XmlParser.cs:24
string[] InternalKeywords
Any keywords used internally by the custom parser.
Definition: XmlParser.cs:34
XmlParser()
Parses an XML document
Definition: XmlParser.cs:17
Basic interface for all types of elements.
Definition: IElement.cs:21
Interface for keywords with custom parsing.
Definition: IKeyWord.cs:9