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;
2using System.Collections.Generic;
7
8namespace Waher.Script.Xml
9{
13 public class XmlParser : IKeyWord
14 {
18 public XmlParser()
19 {
20 }
21
25 public string KeyWord => "<";
26
30 public string[] Aliases => null;
31
35 public string[] InternalKeywords => new string[0];
36
43 public bool TryParse(ScriptParser Parser, out ScriptNode Result)
44 {
45 Result = this.ParseDocument(Parser);
46 return !(Result is null);
47 }
48
49 private XmlScriptDocument ParseDocument(ScriptParser Parser)
50 {
51 List<XmlScriptProcessingInstruction> ProcessingInstructions = null;
52 int Start = Parser.Start;
53 int Pos;
54 char ch;
55
56 while (Parser.PeekNextChar() == '?')
57 {
58 Parser.NextChar();
59 Pos = Parser.Position;
60
61 while ((ch = Parser.NextChar()) != '?' && ch != 0)
62 ;
63
64 if (ch == 0)
65 return null;
66
67 if (ProcessingInstructions is null)
68 ProcessingInstructions = new List<XmlScriptProcessingInstruction>();
69
70 string Text = Parser.Expression.Script.Substring(Pos, Parser.Position - Pos - 1);
71
72 if (Parser.NextChar() != '>')
73 throw Parser.SyntaxError("> expected.");
74
75 ProcessingInstructions.Add(new XmlScriptProcessingInstruction(Text,
76 Start, Parser.Position - Start, Parser.Expression));
77
78 Parser.SkipWhiteSpace();
79
80 if (Parser.NextChar() != '<')
81 return null;
82
83 Start = Parser.Position;
84 }
85
86 Parser.UndoChar();
87 XmlScriptElement Root = this.ParseElement(Parser);
88 if (Root is null)
89 return null;
90
91 return new XmlScriptDocument(Root, ProcessingInstructions?.ToArray() ?? new XmlScriptProcessingInstruction[0],
92 Start, Parser.Position - Start, Parser.Expression);
93 }
94
95 private XmlScriptElement ParseElement(ScriptParser Parser)
96 {
97 if (Parser.NextChar() != '<')
98 throw Parser.SyntaxError("< expected.");
99
100 List<XmlScriptAttribute> Attributes = null;
101 XmlScriptAttribute Xmlns = null;
102 IElement ElementValue;
103 XmlScriptAttribute Attribute;
104 int ElementStart = Parser.Position;
105 string ElementName = ParseName(Parser);
106 string Value;
107 char ch;
108
109 Parser.SkipWhiteSpace();
110
111 while ((ch = Parser.PeekNextChar()) != 0)
112 {
113 if (ch == '>')
114 {
115 Parser.NextChar();
116
117 XmlScriptElement Element = new XmlScriptElement(ElementName, Xmlns, Attributes?.ToArray() ?? new XmlScriptAttribute[0],
118 ElementStart, Parser.Position - ElementStart, Parser.Expression);
119
120 while (true)
121 {
122 int Pos = Parser.Position;
123 int i = Parser.Expression.Script.IndexOf('<', Pos);
124 int Len;
125
126 if (i < 0)
127 throw Parser.SyntaxError("Open element.");
128
129 if (i > Pos)
130 {
131 Len = i - Pos;
132
133 Element.Add(new XmlScriptText(Parser.Expression.Script.Substring(Pos, Len),
134 Pos, Len, Parser.Expression));
135
136 Parser.SkipChars(Len);
137 }
138
139 switch (Parser.PeekNextChars(2))
140 {
141 case "</":
142 Parser.NextChar();
143 Parser.NextChar();
144
145 if (ParseName(Parser) != ElementName)
146 throw Parser.SyntaxError("Expected end of element " + ElementName);
147
148 if (Parser.NextChar() != '>')
149 throw Parser.SyntaxError("> expected.");
150
151 return Element;
152
153 case "<!":
154 if (Parser.IsNextChars("<!--"))
155 {
156 Parser.SkipChars(4);
157
158 Pos = Parser.Position;
159 i = Parser.Expression.Script.IndexOf("-->", Pos);
160 if (i < 0)
161 throw Parser.SyntaxError("Unterminated comment.");
162
163 Len = i - Pos;
164 Element.Add(new XmlScriptComment(Parser.Expression.Script.Substring(Pos, Len),
165 Pos, Len, Parser.Expression));
166
167 Parser.SkipChars(Len + 3);
168 }
169 else if (Parser.IsNextChars("<![CDATA["))
170 {
171 Parser.SkipChars(9);
172
173 Pos = Parser.Position;
174 i = Parser.Expression.Script.IndexOf("]]>", Pos);
175 if (i < 0)
176 throw Parser.SyntaxError("Unterminated CDATA construct.");
177
178 Len = i - Pos;
179 Element.Add(new XmlScriptCData(Parser.Expression.Script.Substring(Pos, Len),
180 Pos, Len, Parser.Expression));
181
182 Parser.SkipChars(Len + 3);
183 }
184 else
185 throw Parser.SyntaxError("Expected <!-- or <![CDATA[");
186
187 break;
188
189 case "<[":
190 Parser.SkipChars(2);
191
192 ScriptNode Node = Parser.ParseSequence();
193 Parser.SkipWhiteSpace();
194
195 if (!Parser.IsNextChars("]>"))
196 throw Parser.SyntaxError("]> expected.");
197
198 Parser.SkipChars(2);
199
200 if (!(Node is null))
201 Element.Add(new XmlScriptValue(Node, Node.Start, Node.Length, Node.Expression));
202 break;
203
204 case "<(":
205 Parser.SkipChars(2);
206
207 Node = Parser.ParseSequence();
208 Parser.SkipWhiteSpace();
209
210 if (!Parser.IsNextChars(")>"))
211 throw Parser.SyntaxError(")> expected.");
212
213 Parser.SkipChars(2);
214
215 Element.Add(new XmlScriptValue(Node, Node.Start, Node.Length, Node.Expression));
216 break;
217
218 default:
219 Element.Add(this.ParseElement(Parser));
220 break;
221 }
222 }
223 }
224 else if (ch == '/')
225 {
226 Parser.NextChar();
227 if (Parser.NextChar() != '>')
228 throw Parser.SyntaxError("> expected.");
229
230 return new XmlScriptElement(ElementName, Xmlns, Attributes?.ToArray() ?? new XmlScriptAttribute[0],
231 ElementStart, Parser.Position - ElementStart, Parser.Expression);
232 }
233 else if (char.IsLetter(ch) || ch == '_' || ch == ':')
234 {
235 int AttributeStart = Parser.Position;
236 string AttributeName = ParseName(Parser);
237
238 Parser.SkipWhiteSpace();
239 if (Parser.NextChar() != '=')
240 throw Parser.SyntaxError("= expected.");
241
242 bool Bak = Parser.CanSkipWhitespace;
243 Parser.CanSkipWhitespace = false;
244 ScriptNode Node = Parser.ParsePowers();
245 Parser.CanSkipWhitespace = Bak;
246
247 if (Node is ConstantElement Constant)
248 {
249 ElementValue = Constant.Constant;
250 Value = XmlScriptNode.EvaluateString(ElementValue);
251
252 Attribute = new XmlScriptAttributeString(AttributeName, Value,
253 AttributeStart, Parser.Position - AttributeStart, Parser.Expression);
254 }
255 else
256 {
257 Attribute = new XmlScriptAttributeScript(AttributeName, Node,
258 AttributeStart, Parser.Position - AttributeStart, Parser.Expression);
259 }
260
261 if (AttributeName == "xmlns")
262 Xmlns = Attribute;
263 else
264 {
265 if (Attributes is null)
266 Attributes = new List<XmlScriptAttribute>();
267
268 Attributes.Add(Attribute);
269 }
270 }
271 else if (char.IsWhiteSpace(ch))
272 Parser.NextChar();
273 else
274 throw Parser.SyntaxError("Invalid XML Element.");
275 }
276
277 return null;
278 }
279
280 private static string ParseName(ScriptParser Parser)
281 {
282 int Pos = Parser.Position;
283 char ch = Parser.PeekNextChar();
284
285 if (!(char.IsLetter(ch) || ch == '_' || ch == ':'))
286 throw Parser.SyntaxError("Name expected.");
287
288 while (Parser.NextChar() != 0)
289 {
290 ch = Parser.PeekNextChar();
291
292 if (char.IsLetterOrDigit(ch) || char.IsDigit(ch) ||
293 ch == '.' || ch == '-' || ch == '_' || ch == ':')
294 {
295 continue;
296 }
297
298 break;
299 }
300
301 return Parser.Expression.Script.Substring(Pos, Parser.Position - Pos);
302 }
303
304 }
305}
Base class for all types of elements.
Definition: Element.cs:13
string Script
Original script string.
Definition: Expression.cs:181
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.
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.
Parses an XML document
Definition: XmlParser.cs:14
bool TryParse(ScriptParser Parser, out ScriptNode Result)
Tries to parse a script node.
Definition: XmlParser.cs:43
string[] Aliases
Keyword aliases, if available, null if none.
Definition: XmlParser.cs:30
string KeyWord
Keyword associated with custom parser.
Definition: XmlParser.cs:25
string[] InternalKeywords
Any keywords used internally by the custom parser.
Definition: XmlParser.cs:35
XmlParser()
Parses an XML document
Definition: XmlParser.cs:18
Basic interface for all types of elements.
Definition: IElement.cs:20
Interface for keywords with custom parsing.
Definition: IKeyWord.cs:9