Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
ShaderParser.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections.Generic;
5 using System.Diagnostics;
6 using System.IO;
7 using System.Text;
8 
9 using Irony.Parsing;
10 using SiliconStudio.Shaders.Ast;
11 using SiliconStudio.Shaders.Grammar;
12 using SiliconStudio.Shaders.Utility;
14 
15 namespace SiliconStudio.Shaders.Parser
16 {
17  /// <summary>
18  /// Parser class.
19  /// </summary>
20  public class ShaderParser
21  {
22  private static readonly Dictionary<Type, ShaderLanguageData> LanguageDatas = new Dictionary<Type, ShaderLanguageData>();
23 
24  /// <summary>
25  /// Gets or sets the parser.
26  /// </summary>
27  /// <value>
28  /// The parser.
29  /// </value>
30  public Irony.Parsing.Parser Parser { get; private set; }
31 
32  /// <summary>
33  /// Gets the grammar.
34  /// </summary>
35  public ShaderGrammar Grammar{ get; private set; }
36 
37  /// <summary>
38  /// Gets or sets the language data.
39  /// </summary>
40  /// <value>
41  /// The language data.
42  /// </value>
43  public ShaderLanguageData LanguageData { get; private set; }
44 
45  /// <summary>
46  /// Gets the tokenizer.
47  /// </summary>
48  public Tokenizer Tokenizer { get; private set; }
49 
50  /// <summary>
51  /// Prevents a default instance of the <see cref="ShaderParser"/> class from being created.
52  /// </summary>
53  /// <param name="languageData">The language data.</param>
54  /// <param name="root">The root of the language.</param>
55  private ShaderParser(ShaderLanguageData languageData, NonTerminal root)
56  {
57  LanguageData = languageData;
58  Grammar = (ShaderGrammar)languageData.Grammar;
59  Tokenizer = new Tokenizer(languageData);
60  Parser = new Irony.Parsing.Parser(languageData, null, root);
61  }
62 
63  /// <summary>
64  /// Preprocesses and parses the specified source.
65  /// </summary>
66  /// <param name="source">The source.</param>
67  /// <param name="sourceFileName">Name of the source file.</param>
68  /// <param name="macros">The macros defined for the preprocessor.</param>
69  /// <param name="includeDirectories">The include directories used by the preprocessor..</param>
70  /// <returns>Result of parsing</returns>
71  public ParsingResult TryPreProcessAndParse(string source, string sourceFileName, ShaderMacro[] macros = null, params string[] includeDirectories)
72  {
73  // Use a default include handler
74  var defaultHandler = new DefaultIncludeHandler();
75 
76  if (includeDirectories != null)
77  defaultHandler.AddDirectories(includeDirectories);
78 
79  defaultHandler.AddDirectory(Environment.CurrentDirectory);
80 
81  var directoryName = Path.GetDirectoryName(sourceFileName);
82  if (!string.IsNullOrEmpty(directoryName))
83  defaultHandler.AddDirectory(directoryName);
84 
85  // Run the processor
86  var preprocessedSource = PreProcessor.Run(source, sourceFileName, macros, defaultHandler);
87 
88  // Parse the source
89  return Parse(preprocessedSource, sourceFileName);
90  }
91 
92  /// <summary>
93  /// Preprocesses and parses the specified source.
94  /// </summary>
95  /// <param name="source">The source.</param>
96  /// <param name="sourceFileName">Name of the source file.</param>
97  /// <param name="macros">The macros defined for the preprocessor.</param>
98  /// <param name="includeDirectories">The include directories used by the preprocessor..</param>
99  /// <returns>Result of parsing</returns>
100  public Shader PreProcessAndParse(string source, string sourceFileName, ShaderMacro[] macros = null, params string[] includeDirectories)
101  {
102  // Parse the source
103  var result = TryPreProcessAndParse(source, sourceFileName, macros, includeDirectories);
104  return Check(result, sourceFileName);
105  }
106 
107  /// <summary>
108  /// Gets the parser.
109  /// </summary>
110  /// <typeparam name="T"></typeparam>
111  /// <returns></returns>
112  public static ShaderParser GetParser<T>(NonTerminal root = null) where T : ShaderGrammar, new()
113  {
114  ShaderLanguageData languageData;
115  lock (LanguageDatas)
116  {
117  if (!LanguageDatas.TryGetValue(typeof(T), out languageData))
118  {
119  languageData = new ShaderLanguageData(new T());
120  LanguageDatas.Add(typeof(T), languageData);
121  }
122  }
123 
124  return new ShaderParser(languageData, root);
125  }
126 
127  /// <summary>
128  /// Gets the language.
129  /// </summary>
130  /// <typeparam name="T"></typeparam>
131  /// <returns></returns>
132  public static T GetGrammar<T>() where T : ShaderGrammar, new()
133  {
134  ShaderLanguageData languageData;
135  lock (LanguageDatas)
136  {
137  if (!LanguageDatas.TryGetValue(typeof(T), out languageData))
138  {
139  languageData = new ShaderLanguageData(new T());
140  LanguageDatas.Add(typeof(T), languageData);
141  }
142 
143  return (T)languageData.Grammar;
144  }
145  }
146 
147  /// <summary>
148  /// Parses the specified source code.
149  /// </summary>
150  /// <typeparam name="T">Type of the grammar</typeparam>
151  /// <param name="sourceCode">The source code.</param>
152  /// <param name="file">The file.</param>
153  /// <returns>A parsing result</returns>
154  public ParsingResult Parse(string sourceCode, string file)
155  {
156  var clock = new Stopwatch();
157  clock.Start();
158  var parseTree = Parser.Parse(sourceCode, file);
159  clock.Stop();
160 
161  var result = new ParsingResult
162  {
163  TimeToParse = clock.ElapsedMilliseconds,
164  TokenCount = parseTree.Tokens.Count,
165  };
166 
167  // Get the parsed node
168  if (parseTree.Root != null && parseTree.Root.AstNode != null)
169  {
170  result.Shader = (Shader)((IronyBrowsableNode)parseTree.Root.AstNode).Node;
171  }
172  else
173  {
174  result.HasErrors = true;
175  }
176 
177 
178  // Add messages from Irony
179  HandleMessages(parseTree, file, result);
180 
181  return result;
182  }
183 
184  /// <summary>
185  /// Parse a source code file and check that the result is valid.
186  /// </summary>
187  /// <param name="sourceCode"></param>
188  /// <param name="file"></param>
189  /// <returns></returns>
190  public Shader ParseAndCheck(string sourceCode, string file)
191  {
192  var result = Parse(sourceCode, file);
193  return Check(result, file);
194  }
195 
196  public static Shader Check(ParsingResult result, string sourceFileName)
197  {
198  // Throws an exception if there are any errors.
199  // Todo provide better handling
200  if (result.HasErrors)
201  {
202  var errorText = new StringBuilder();
203  errorText.AppendFormat("Unable to parse file [{0}]", sourceFileName).AppendLine();
204  foreach (var reportMessage in result.Messages)
205  {
206  errorText.AppendLine(reportMessage.ToString());
207  }
208  throw new InvalidOperationException(errorText.ToString());
209  }
210  return result.Shader;
211  }
212 
213  private static void HandleMessages(ParseTree parseTree, string file, ParsingResult result)
214  {
215  foreach (var parserMessage in parseTree.ParserMessages)
216  {
217  var level = new ReportMessageLevel();
218  switch (parserMessage.Level)
219  {
220  case ParserErrorLevel.Info:
221  level = ReportMessageLevel.Info;
222  break;
223  case ParserErrorLevel.Error:
224  level = ReportMessageLevel.Error;
225  break;
226  case ParserErrorLevel.Warning:
227  level = ReportMessageLevel.Warning;
228  break;
229  }
230 
231  result.Messages.Add(new ReportMessage(level, "", parserMessage.Message, new Ast.SourceSpan(SpanConverter.Convert(parserMessage.Location), 0)));
232 
233  if (parserMessage.Level != ParserErrorLevel.Info) result.HasErrors = true;
234  }
235  }
236  }
237 }
Methods used to create the Abstract Syntax Tree..
Default IncludeHandler implementation loading files from a set of predefined directories.
Macro to be used with PreProcessor.
Definition: ShaderMacro.cs:11
static Shader Check(ParsingResult result, string sourceFileName)
static SourceLocation Convert(Irony.Parsing.SourceLocation sourceLocation)
Abstract node.
Definition: Node.cs:15
ParsingResult TryPreProcessAndParse(string source, string sourceFileName, ShaderMacro[] macros=null, params string[] includeDirectories)
Preprocesses and parses the specified source.
Definition: ShaderParser.cs:71
readonly ParserMessageList ParserMessages
Definition: ParseTree.cs:128
Shader PreProcessAndParse(string source, string sourceFileName, ShaderMacro[] macros=null, params string[] includeDirectories)
Preprocesses and parses the specified source.
bool HasErrors
Gets or sets a value indicating whether this instance has errors.
Definition: LoggerResult.cs:29
IList< ReportMessage > Messages
Gets or sets the messages.
Definition: LoggerResult.cs:37
Describes a language.
Definition: LanguageData.cs:23
ReportMessageLevel
Level of a ReportMessage.
ParsingResult Parse(string sourceCode, string file)
Parses the specified source code.
Toplevel container of a shader parsing result.
Definition: Shader.cs:12
SiliconStudio.Shaders.Ast.SourceSpan SourceSpan
Definition: ShaderParser.cs:13
Shader ParseAndCheck(string sourceCode, string file)
Parse a source code file and check that the result is valid.
Grammar Grammar
The linked Grammar
Definition: LanguageData.cs:35