Example #1
0
        /// <summary>
        /// Compiles a rule context transformation.
        /// </summary>
        /// <param name="transformation">The transformation.</param>
        /// <param name="builder">The builder.</param>
        /// <param name="glyphIdToRuleIndexCallback">The callback used to resolve rule index from glyph IDs.</param>
        /// <param name="createTransitionCallback">The callback used to create transitions.</param>
        /// <exception cref="System.InvalidOperationException">Transformations in transformation set don't share LookupFlags.</exception>
        private void CompileChainingRuleContextTransformation(
            IGlyphTransformationTable transformation,
            IStateMachineBuilder builder, Func <ushort, ushort> glyphIdToRuleIndexCallback,
            CreateContextTransformationTransitionDelegate <ushort> createTransitionCallback)
        {
            var table = (ChainingRuleContextTransformationTableBase)transformation;

            // Flatten the collection of rules and associate each rule with glyph received from the coverage table
            var rulesWithFirstGlyph = table
                                      .TransformationRules
                                      .Zip(table.Coverage.CoveredGlyphIds.Values)
                                      .SelectMany(ruleSetWithFirstGlyph => ruleSetWithFirstGlyph.Item1.Select(rule => Tuple.Create(rule, glyphIdToRuleIndexCallback(ruleSetWithFirstGlyph.Item2))));

            foreach (var ruleTuple in rulesWithFirstGlyph)
            {
                foreach (var transformationSet in ruleTuple.Item1.TransformationSets)
                {
                    var completeInputContext =
                        ruleTuple.Item1.Context
                        .Prepend(ruleTuple.Item2)
                        .ToList();

                    this.CompileSingleContextTransformation(
                        table,
                        builder,
                        transformationSet,
                        createTransitionCallback,
                        completeInputContext,
                        ruleTuple.Item1.Lookback,
                        ruleTuple.Item1.Lookahead);
                }
            }
        }
Example #2
0
        /// <summary>
        /// Compiles a context transformation for a single context.
        /// </summary>
        /// <typeparam name="TContextItem">The type of items in the context. This is arbitrary - it is only used to construct individual transitions in the context recognition section of the machine.</typeparam>
        /// <param name="transformation">The transformation.</param>
        /// <param name="builder">The builder.</param>
        /// <param name="subTransformationSet">The set of tranformation to execute on the matched context.</param>
        /// <param name="createTransitionCallback">The callback used to create transitions from individual items of the context. </param>
        /// <param name="context">The context.</param>
        /// <param name="lookbackContext">The lookback context (NULL if the transition is not chaining).</param>
        /// <param name="lookaheadContext">The lookahead context (NULL if the transition is not chaining). If lookaheadContext is defined, lookbackContext must be defined as well.</param>
        /// <exception cref="System.InvalidOperationException">Transformations in transformation set don't share LookupFlags.</exception>
        private void CompileSingleContextTransformation <TContextItem>(
            IGlyphTransformationTable transformation,
            IStateMachineBuilder builder,
            ContextTransformationSet subTransformationSet,
            CreateContextTransformationTransitionDelegate <TContextItem> createTransitionCallback,
            IEnumerable <TContextItem> context,
            IEnumerable <TContextItem> lookbackContext  = null,
            IEnumerable <TContextItem> lookaheadContext = null)
        {
            if (!subTransformationSet.Transformations.Any())
            {
                return;
            }

            var contextList          = context.ToList();
            var lookbackContextList  = lookbackContext == null ? new List <TContextItem>() : lookbackContext.Reverse().ToList();
            var lookaheadContextList = lookaheadContext == null ? new List <TContextItem>() : lookaheadContext.ToList();

            var completeContext = lookbackContextList.Append(contextList).Append(lookaheadContextList).ToList();

            var subMachineLookupFlags = subTransformationSet.Transformations.First().LookupFlags;

            // All the transformations in one transformation set must have the same lookup flags (they are supposed to come from
            // a single lookup).
            if (subTransformationSet.Transformations.Any(p => p.LookupFlags != subMachineLookupFlags))
            {
                throw new InvalidOperationException("Transformations in transformation set don't share LookupFlags.");
            }

            // The head must end pointing to the first glyph after end of the context
            var subBuilder = new SubMachineBuilder(subMachineLookupFlags);

            foreach (var subTransformation in subTransformationSet.Transformations)
            {
                this.CompileTransformation(subTransformation, subBuilder);
            }

            /* The path for each rule is constructed as follows:
             *  1) A chain of states which match the context (including lookback and lookahead context).
             *  2) State which positions the head to adhere to first glyph index of the transformation set
             *      and creates the context terminator glyph. The transition checking the last glyph of the context
             *      leads to this state. Additonal state is inserted before this one in case the table has defined a
             *      lookahead
             *  3) The sub-machine to be applied on the context. This state machine is modified to jump to 4 whenever
             *      it encounters context terminator.
             *  4) State which positions the head to end of the context and removes the context terminator. */

            // Part 1. Doesn't include the transition and state checking the last glyph (see part 2).
            var contextMatchPathSegment = completeContext
                                          .Take(completeContext.Count - 1)
                                          .Select(
                (contextMember, index) =>
                createTransitionCallback(
                    contextMember,
                    1,
                    transformation.LookupFlags,
                    null,
                    this.GetContextZoneForGlyphIndex(index, contextList, lookbackContextList, lookaheadContextList))).ToList();

            List <ITransition> subMachineInitializationPathSegment;

            if (lookaheadContextList.Count > 0)
            {
                // Part 2. Sub-machine initialization state.
                subMachineInitializationPathSegment = new List <ITransition>
                {
                    createTransitionCallback(
                        completeContext.Last(),
                        -lookaheadContextList.Count,
                        transformation.LookupFlags,
                        null,
                        this.GetContextZoneForGlyphIndex(completeContext.Count - 1, contextList, lookbackContextList, lookaheadContextList)),
                    new AlwaysTransition
                    {
                        HeadShift   = -contextList.Count,
                        LookupFlags = transformation.LookupFlags,
                        Action      = new SubstitutionAction
                        {
                            ReplacedGlyphCount  = 0,
                            ReplacementGlyphIds = new[] { SubMachineBuilder.ContextTerminatorGlyphId }
                        }
                    }
                };
            }
            else
            {
                // Part 2. Sub-machine initialization state.
                subMachineInitializationPathSegment = new List <ITransition>
                {
                    createTransitionCallback(
                        completeContext.Last(),
                        -contextList.Count,
                        transformation.LookupFlags,
                        new SubstitutionAction
                    {
                        ReplacementGlyphIds = new[] { SubMachineBuilder.ContextTerminatorGlyphId }
                    },
                        this.GetContextZoneForGlyphIndex(completeContext.Count - 1, contextList, lookbackContextList, lookaheadContextList))
                };
            }


            /* Part 3 a 4. Sub-machine iself + deinitialization state.
             * Including its entry state (to which lead all the internal transitions). */
            var subMachinePaths = subBuilder.GetPaths();

            // Assemble the paths and add them into the outer machine builder.
            foreach (var subMachinePath in subMachinePaths)
            {
                var assembledPath = contextMatchPathSegment.Append(subMachineInitializationPathSegment).Append(subMachinePath).ToList();

                builder.AddPath(assembledPath);
            }
        }