Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
StringTemplateNode.cs
Go to the documentation of this file.
1 #region License
2 /* **********************************************************************************
3  * Copyright (c) Roman Ivantsov
4  * This source code is subject to terms and conditions of the MIT License
5  * for Irony. A copy of the license can be found in the License.txt file
6  * at the root of this distribution.
7  * By using this source code in any fashion, you are agreeing to be bound by the terms of the
8  * MIT License.
9  * You must not remove this notice from this software.
10  * **********************************************************************************/
11 #endregion
12 
13 using System;
14 using System.Collections.Generic;
15 using System.Linq;
16 using System.Text;
17 using Irony.Parsing;
18 using Irony.Interpreter;
19 
20 namespace Irony.Interpreter.Ast {
21  public class StringTemplateNode : AstNode {
22  #region embedded classes
23  enum SegmentType {
24  Text,
25  Expression
26  }
27  class TemplateSegment {
28  public SegmentType Type;
29  public string Text;
30  public AstNode ExpressionNode;
31  public int Position; //Position in raw text of the token for error reporting
32  public TemplateSegment(string text, AstNode node, int position) {
33  Type = node == null? SegmentType.Text : SegmentType.Expression;
34  Text = text;
35  ExpressionNode = node;
36  Position = position;
37  }
38  }
39  class SegmentList : List<TemplateSegment> { }
40  #endregion
41 
42  string _template;
43  string _tokenText; //used for locating error
44  StringTemplateSettings _templateSettings; //copied from Terminal.AstNodeConfig
45  SegmentList _segments = new SegmentList();
46 
47  public override void Init(ParsingContext context, ParseTreeNode treeNode) {
48  base.Init(context, treeNode);
49  _template = treeNode.Token.ValueString;
50  _tokenText = treeNode.Token.Text;
51  _templateSettings = treeNode.Term.AstNodeConfig as StringTemplateSettings;
52  ParseSegments(context);
53  AsString = "\"" + _template + "\" (templated string)";
54  }
55 
56  public override void EvaluateNode(EvaluationContext context, AstMode mode) {
57  switch (mode) {
58  case AstMode.Read:
59  var value = BuildString(context);
60  context.Data.Push(value);
61  break;
62  case AstMode.Write:
63  context.ThrowError(Resources.ErrAssignLiteralValue);
64  break;
65  }
66  }
67 
68  private void ParseSegments(ParsingContext context) {
69  var exprParser = new Parser(context.Language, _templateSettings.ExpressionRoot);
70  // As we go along the "value text" (that has all escapes done), we track the position in raw token text in the variable exprPosInTokenText.
71  // This position is position in original text in source code, including original escaping sequences and open/close quotes.
72  // It will be passed to segment constructor, and maybe used later to compute the exact position of runtime error when it occurs.
73  int currentPos = 0, exprPosInTokenText = 0;
74  while(true) {
75  var startTagPos = _template.IndexOf(_templateSettings.StartTag, currentPos);
76  if (startTagPos < 0) startTagPos = _template.Length;
77  var text = _template.Substring(currentPos, startTagPos - currentPos);
78  if (!string.IsNullOrEmpty(text))
79  _segments.Add(new TemplateSegment(text, null, 0)); //for text segments position is not used
80  if (startTagPos >= _template.Length)
81  break; //from while
82  //We have a real start tag, grab the expression
83  currentPos = startTagPos + _templateSettings.StartTag.Length;
84  var endTagPos = _template.IndexOf(_templateSettings.EndTag, currentPos);
85  if (endTagPos < 0) {
86  context.AddParserError(Resources.ErrNoEndTagInEmbExpr, _templateSettings.EndTag);//"No ending tag '{0}' found in embedded expression."
87  return;
88  }
89  var exprText = _template.Substring(currentPos, endTagPos - currentPos);
90  if(!string.IsNullOrEmpty(exprText)) {
91  //parse the expression
92  //_expressionParser.Context.Reset();
93 
94  var exprTree = exprParser.Parse(exprText);
95  if(exprTree.HasErrors()) {
96  //we use original search in token text instead of currentPos in template to avoid distortions caused by opening quote and escaped sequences
97  var baseLocation = this.Location + _tokenText.IndexOf(exprText);
98  context.CurrentParseTree.CopyMessages(exprTree.ParserMessages, baseLocation, Resources.ErrInvalidEmbeddedPrefix);
99  return;
100  }
101  //add the expression segment
102  exprPosInTokenText = _tokenText.IndexOf(_templateSettings.StartTag, exprPosInTokenText) + _templateSettings.StartTag.Length;
103  _segments.Add(new TemplateSegment(null, exprTree.Root.AstNode as AstNode, exprPosInTokenText));
104  //advance position beyond the expression
105  exprPosInTokenText += exprText.Length + _templateSettings.EndTag.Length;
106 
107  }//if
108  currentPos = endTagPos + _templateSettings.EndTag.Length;
109  }//while
110  }
111 
112  private object BuildString(EvaluationContext context) {
113  var initialStackCount = context.Data.Count;
114  string[] values = new string[_segments.Count];
115  for(int i = 0; i < _segments.Count; i++) {
116  var segment = _segments[i];
117  switch(segment.Type) {
118  case SegmentType.Text:
119  values[i] = segment.Text;
120  break;
121  case SegmentType.Expression:
122  values[i] = EvaluateExpression(context, segment);
123  context.Data.PopUntil(initialStackCount);
124  break;
125  }//else
126  }//for i
127  var result = string.Join(string.Empty, values);
128  return result;
129  }//method
130 
131  private string EvaluateExpression(EvaluationContext context, TemplateSegment segment) {
132  try {
133  segment.ExpressionNode.Evaluate(context, AstMode.Read);
134  var value = context.LastResult;
135  return value == null ? string.Empty : value.ToString();
136  } catch(RuntimeException ex) {
137  this.ErrorAnchor = this.Location + segment.Position + ex.Location;
138  throw ex.InnerException;
139  }
140 
141  }
142  }//class
143 }
static string ErrNoEndTagInEmbExpr
Looks up a localized string similar to No ending tag '{0}' found in embedded expression..
static string ErrAssignLiteralValue
Looks up a localized string similar to Invalide operation, attempt to assign constant or literal valu...
override void Init(ParsingContext context, ParseTreeNode treeNode)
override void EvaluateNode(EvaluationContext context, AstMode mode)
static string ErrInvalidEmbeddedPrefix
Looks up a localized string similar to Invalid embedded expression. .
readonly LanguageData Language