private Expression ConvertPathInterpolationExpression(ref ProcessedTagTemplateExpression source, FunctionScope escapes, QualifierSpaceId currentQualifierSpaceId, bool isRelativePath)
        {
            var expressions = EnumerateTemplateSpans(source.Head, source.TemplateSpans, escapes, currentQualifierSpaceId, isRelativePath: isRelativePath);

            // Expressions could be empty only for the case like p``;
            if (expressions == null || expressions.Count == 0)
            {
                return(null);
            }

            return(new InterpolatedPaths(expressions, isRelativePath, expressions[0].Location));
        }
        private Expression CurrentPathAtomInterpolationExpression(ref ProcessedTagTemplateExpression source, FunctionScope escapes, QualifierSpaceId currentQualifierSpaceId)
        {
            var expressions = EnumerateTaggedExpressionsForPathAtomInterpolation(source.Head, source.TemplateSpans, escapes, currentQualifierSpaceId);

            // Expressions could be empty only for the case like a``;
            if (expressions == null || expressions.Count == 0)
            {
                return(null);
            }

            return(ApplyExpression.Create(m_pathAtomInterpolateSelectorExpression, expressions.ToArray(), expressions[0].Location));
        }
        private List <Expression> EnumerateTemplateSpans(
            ITemplateLiteralFragment headNode,
            INodeArray <ITemplateSpan> templateSpans,
            FunctionScope escapes,
            QualifierSpaceId currentQualifierSpaceId,
            bool isRelativePath)
        {
            Contract.Requires(headNode != null);
            Contract.Requires(templateSpans != null);

            // Creating a list that will hold all potential expressions
            List <Expression> result = new List <Expression>((templateSpans.Length * 2) + 1);

            // Example: 'path/to/{x}/abc/{y}'.
            // - Head is 'path/to/'
            // - Spans:
            //   1. '{x}/abc/': expr 'x', literal '/abc/'
            //   2. '{y}': expr 'y', no literal.

            // For instance in this case: p`foo/${x}/${y}` the result equals p`foo`.combine(x).combine(y);
            // and for this case: p`${x}` the result equals x.
            // Note that  p`path/to/abc` equals as p`./path/to/abc`. Thus for p`${x}/path/to/abc`, x should evaluate to an absolute path,
            // and such a construct equals x.combine("path").combine("to"). combine("abc").
            string head = headNode.Text;

            if (!string.IsNullOrEmpty(head))
            {
                // Example: 'path/to/'
                if (!HasTailingPathSeparator(head))
                {
                    string message = I($"Path fragment '{head}' does not have a tailing path separator.");
                    RuntimeModelContext.Logger.ReportInvalidPathInterpolationExpression(
                        RuntimeModelContext.LoggingContext,
                        Location(headNode).AsLoggingLocation(),
                        message);
                    return(null);
                }

                if (!isRelativePath)
                {
                    // Tagged expression is expected to be an absolute path.
                    var convertedPathLiteral =
                        m_literalConverter.ConvertPathLiteral(
                            RemoveLeadingAndTailingPathSeparatorIfNeeded(head, isAbsolutePath: true),
                            Location(headNode));

                    if (convertedPathLiteral == null)
                    {
                        // Error has been reported.
                        return(null);
                    }

                    result.Add(convertedPathLiteral);
                }
                else
                {
                    // Tagged expression is expected to be a relative path.
                    var convertedRelativePathLiteral = m_literalConverter.ConvertRelativePathLiteral(
                        RemoveLeadingAndTailingPathSeparatorIfNeeded(head, isAbsolutePath: false),
                        Location(headNode));

                    if (convertedRelativePathLiteral == null)
                    {
                        // Error has been reported.
                        return(null);
                    }

                    result.Add(convertedRelativePathLiteral);
                }
            }

            for (int i = 0; i < templateSpans.Length; i++)
            {
                //// TODO: Currently fragment is string literal. This somehow defeats the purpose of paths.
                //// TODO: We need to find a syntactic representation of relative path that differs from string.
                var span = templateSpans[i];

                // Example: span is '{x}/abc/'.
                if (span.Expression != null)
                {
                    // Grab 'x' from '{x}/abc/'.
                    var convertedExpression = m_converter.ConvertExpression(span.Expression, escapes, currentQualifierSpaceId);
                    if (convertedExpression != null)
                    {
                        result.Add(convertedExpression);
                    }
                }

                // Fragment is '/abc/'.
                var fragment = span.Literal.Text;

                // For every expression (except last one), interpolated path should have a separator
                if (string.IsNullOrEmpty(fragment) && span.Expression != null)
                {
                    // Fragment is empty or consists only of whitespaces, but expression is present, e.g., span (2) -- '{y}'.
                    if (i == templateSpans.Length - 1)
                    {
                        // Last template span, nothing to do, separator could be empty.
                        continue;
                    }

                    // Not the last template span, thus needs a path separator.
                    string message = "Each path fragment in interpolated path literal should have a path separator between expressions.";
                    RuntimeModelContext.Logger.ReportInvalidPathInterpolationExpression(
                        RuntimeModelContext.LoggingContext,
                        Location(span.Literal).AsLoggingLocation(),
                        message);
                    return(null);
                }

                // Skip if fragment is only a separator, e.g., '{w}/{z}'.
                if (IsPathSeparator(fragment))
                {
                    continue;
                }

                // Fragments should start with path separator, e.g., '/abc/'.
                if (!HasLeadingPathSeparator(fragment))
                {
                    string message = I($"Path fragment '{fragment}' does not have a leading path separator.");
                    RuntimeModelContext.Logger.ReportInvalidPathInterpolationExpression(
                        RuntimeModelContext.LoggingContext,
                        Location(span.Literal).AsLoggingLocation(),
                        message);
                    return(null);
                }

                // All fragments except last one must have a trailing separator, e.g., '/abc/'.
                if (i != templateSpans.Length - 1 && !HasTailingPathSeparator(fragment))
                {
                    string message = I($"Path fragment '{fragment}' does not have a trailing path separator.");
                    RuntimeModelContext.Logger.ReportInvalidPathInterpolationExpression(
                        RuntimeModelContext.LoggingContext,
                        Location(span.Literal).AsLoggingLocation(),
                        message);
                    return(null);
                }

                // Remove '/' from '/abc/'.
                var    textFragment = RemoveLeadingAndTailingPathSeparatorIfNeeded(fragment, isAbsolutePath: false);
                string literal      = textFragment.Length == fragment.Length ? fragment : textFragment.ToString();
                result.Add(new StringLiteral(literal, LineInfo(span.Literal)));
            }

            return(result);
        }
        private Expression ConvertPathAtomInterpolation(ref ProcessedTagTemplateExpression source, FunctionScope escapes, QualifierSpaceId currentQualifierSpaceId)
        {
            if (source.Literal != null)
            {
                return(m_literalConverter.ConvertPathAtomLiteral(source.Literal, Location(source.TaggedTemplate)));
            }

            return(CurrentPathAtomInterpolationExpression(ref source, escapes, currentQualifierSpaceId));
        }
Beispiel #5
0
 /// <summary>
 /// Coerce source qualifier with a target one.
 /// </summary>
 public static bool CoerceQualifierValue(
     Context context,
     ModuleLiteral env,
     QualifierSpaceId sourceQualifierSpaceId,
     UninstantiatedModuleInfo targetModule,
     in UniversalLocation referencingLocation,
        private List <Expression> EnumerateTaggedExpressionsForStringInterpolation(ITemplateExpression template, FunctionScope escapes, QualifierSpaceId currentQualifierSpaceId)
        {
            // Creating a list that will hold all potential expressions
            List <Expression> result = new List <Expression>((template.TemplateSpans.Length * 2) + 1);

            var head = template.Head.Text;

            if (!string.IsNullOrEmpty(head))
            {
                result.Add(LiteralConverter.ConvertStringLiteral(head, Location(template.Head)));
            }

            for (int i = 0; i < template.TemplateSpans.Length; i++)
            {
                var span = template.TemplateSpans[i];
                if (span.Expression != null)
                {
                    var convertedExpression = m_converter.ConvertExpression(span.Expression, escapes, currentQualifierSpaceId);
                    if (convertedExpression != null)
                    {
                        result.Add(convertedExpression);
                    }
                }

                var fragment = span.Literal.Text;

                if (!string.IsNullOrEmpty(fragment))
                {
                    result.Add(LiteralConverter.ConvertStringLiteral(fragment, Location(template.Head)));
                }
            }

            return(result);
        }
        /// <summary>
        /// Method that converts tagged ast node into <see cref="Expression"/>.
        /// </summary>
        public Expression ConvertInterpolation(ITaggedTemplateExpression source, FunctionScope escapes, QualifierSpaceId currentQualifierSpaceId)
        {
            (var interpolationKind, ILiteralExpression literal, ITemplateLiteralFragment head, INodeArray <ITemplateSpan> templateSpans) = source;
            var tagTemplate = new ProcessedTagTemplateExpression(source, interpolationKind, literal, head, templateSpans);

            switch (interpolationKind)
            {
            case InterpolationKind.PathInterpolation:
                return(ConvertPathInterpolation(ref tagTemplate, escapes, currentQualifierSpaceId));

            case InterpolationKind.FileInterpolation:
                return(ConvertFileInterpolation(ref tagTemplate, escapes, currentQualifierSpaceId));

            case InterpolationKind.DirectoryInterpolation:
                return(ConvertDirectoryInterpolation(ref tagTemplate, escapes, currentQualifierSpaceId));

            case InterpolationKind.PathAtomInterpolation:
                return(ConvertPathAtomInterpolation(ref tagTemplate, escapes, currentQualifierSpaceId));

            case InterpolationKind.RelativePathInterpolation:
                return(ConvertRelativePathInterpolation(ref tagTemplate, escapes, currentQualifierSpaceId));

            default:
                throw Contract.AssertFailure(I($"Unknown interpolation kind '{interpolationKind}'."));
            }
        }
        /// <summary>
        /// Converts string interpolation expression like <code>let x = `${foo}`;</code>
        /// </summary>
        public Expression ConvertStringInterpolation(ITemplateExpression source, FunctionScope escapes, QualifierSpaceId currentQualifierSpaceId)
        {
            var taggedExpressions = EnumerateTaggedExpressionsForStringInterpolation(source, escapes, currentQualifierSpaceId);

            // There is one corner cases here:
            // If tagged expression is just a string literal, but has no expressions, we can just return it
            if (taggedExpressions.Count == 1)
            {
                if (taggedExpressions[0] is StringLiteral stringLiteral)
                {
                    return(stringLiteral);
                }
            }

            var applyExpression = ApplyExpression.Create(m_stringInterpolationSelectorExpression, taggedExpressions.ToArray(), LineInfo(source));

            return(new StringLiteralExpression(applyExpression, applyExpression.Location));
        }
Beispiel #9
0
        private static ObjectLiteral GetQualifierSpaceValue(EvaluationContext context, QualifierSpaceId qualifierSpaceId)
        {
            Contract.Requires(context != null);
            Contract.Requires(context.FrontEndContext.QualifierTable.IsValidQualifierSpaceId(qualifierSpaceId));

            var qualifierSpace = context.FrontEndContext.QualifierTable.GetQualifierSpace(qualifierSpaceId);
            var bindings       = new List <Binding>(qualifierSpace.Keys.Count);

            foreach (var kvp in qualifierSpace.AsDictionary)
            {
                var values = ArrayLiteral.CreateWithoutCopy(kvp.Value.Select(s => EvaluationResult.Create(s.ToString(context.StringTable))).ToArray(), default(LineInfo), AbsolutePath.Invalid);
                bindings.Add(new Binding(kvp.Key, values, default(LineInfo)));
            }

            return(ObjectLiteral.Create(bindings, default(LineInfo), AbsolutePath.Invalid));
        }
Beispiel #10
0
        private Expression ConvertDirectoryInterpolation(ref ProcessedTagTemplateExpression source, FunctionScope escapes, QualifierSpaceId currentQualifierSpaceId)
        {
            var pathExpression = ConvertPathInterpolation(ref source, escapes, currentQualifierSpaceId);

            if (pathExpression == null)
            {
                // Error occurred. Error was already logged
                return(null);
            }

            return(new DirectoryLiteralExpression(pathExpression, pathExpression.Location));
        }
Beispiel #11
0
 /// <summary>
 /// Writes a QualifierSpaceId
 /// </summary>
 public virtual void Write(QualifierSpaceId value)
 {
     Start <QualifierId>();
     WriteCompact(value.Id);
     End();
 }
        /// <nodoc />
        public UninstantiatedModuleInfo(SourceFile sourceFile, [NotNull] FileModuleLiteral fileModuleLiteral, QualifierSpaceId qualifierSpaceId)
            : this(sourceFile, qualifierSpaceId)
        {
            Contract.Requires(fileModuleLiteral != null, "fileModuleLiteral != null");

            FileModuleLiteral = fileModuleLiteral;
        }
        /// <nodoc />
        // Used only for semantic evaluation
        public UninstantiatedModuleInfo(SourceFile sourceFile, [NotNull] TypeOrNamespaceModuleLiteral typeOrNamespaceLiteral, QualifierSpaceId qualifierSpaceId)
            : this(sourceFile, qualifierSpaceId)
        {
            Contract.Requires(typeOrNamespaceLiteral != null, "typeOrNamespaceLiteral != null");

            TypeOrNamespaceTypeOrNamespaceLiteral = typeOrNamespaceLiteral;
        }
Beispiel #14
0
        /// <nodoc/>
        public WithQualifierExpression(Expression moduleReference, Expression qualifierExpression, QualifierSpaceId sourceQualifierSpaceId, QualifierSpaceId targetQualifierSpaceId, LineInfo location)
            : base(location)
        {
            Contract.Requires(moduleReference != null, "moduleReference != null");
            Contract.Requires(qualifierExpression != null, "qualifierExpression != null");

            ModuleReference        = moduleReference;
            QualifierExpression    = qualifierExpression;
            SourceQualifierSpaceId = sourceQualifierSpaceId;
            TargetQualifierSpaceId = targetQualifierSpaceId;
        }
Beispiel #15
0
        internal SourceFileParseResult(SourceFile sourceFile, FileModuleLiteral module, QualifierSpaceId qualifierSpaceId)
            : base(sourceFile)
        {
            Contract.Requires(sourceFile != null || module != null, "sourceFile or module should not be null");

            SourceFile       = sourceFile;
            Module           = module;
            QualifierSpaceId = qualifierSpaceId;
        }