Ejemplo n.º 1
0
        void PrepareConstantBuilders([CanBeNull] ZilModelObject lastObject)
        {
            // builders and values for constants (which may refer to vocabulary,
            // routines, tables, objects, properties, or flags)
            foreach (var constant in Context.ZEnvironment.Constants)
            {
                IOperand value;
                if (constant.Name.StdAtom == StdAtom.LAST_OBJECT && lastObject != null)
                {
                    value = Objects[lastObject.Name];
                }
                else
                {
                    value = CompileConstant(constant.Value);
                }

                if (value == null)
                {
                    Context.HandleError(new CompilerError(
                                            constant,
                                            CompilerMessages.Nonconstant_Initializer_For_0_1_2,
                                            "constant",
                                            constant.Name,
                                            constant.Value.ToStringContext(Context, false)));
                    value = Game.Zero;
                }

                Constants.Add(constant.Name, Game.DefineConstant(constant.Name.Text, value));
            }
        }
Ejemplo n.º 2
0
        void PrepareObjectBuilders([CanBeNull] out ZilModelObject lastObject)
        {
            // builders for objects
            lastObject = null;

            string GetGlobalSymbolType(ZilAtom atom) =>
            Game.IsGloballyDefined(atom.Text, out var type) ? type : null;

            foreach (var obj in Context.ZEnvironment.ObjectsInDefinitionOrder(GetGlobalSymbolType))
            {
                lastObject = obj;
                Objects.Add(obj.Name, Game.DefineObject(obj.Name.Text));
                // builders for the rest of the properties and flags,
                // and vocabulary for names
                PreBuildObject(obj);
            }
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Looks through a <see cref="ZilModelObject"/>'s property definitions, creating the
        /// property builders, flag builders, and vocab word builders that it will need.
        /// </summary>
        /// <remarks>
        /// <para>This does not create the <see cref="IObjectBuilder"/> or add any data to it.</para>
        /// <para>It does, however, call the <c>PROPSPEC</c> routines for any properties that have
        /// custom handlers installed, and replaces the corresponding property definitions with
        /// whatever the handler returned.</para>
        /// </remarks>
        /// <param name="model">The object to examine.</param>
        void PreBuildObject([NotNull] ZilModelObject model)
        {
            var globalsByName   = Context.ZEnvironment.Globals.ToDictionary(g => g.Name);
            var propertiesSoFar = new HashSet <ZilAtom>();

            var preBuilders = new ComplexPropDef.ElementPreBuilders
            {
                CreateVocabWord = (atom, partOfSpeech, src) =>
                {
                    // ReSharper disable once SwitchStatementMissingSomeCases
                    switch (partOfSpeech.StdAtom)
                    {
                    case StdAtom.ADJ:
                    case StdAtom.ADJECTIVE:
                        Context.ZEnvironment.GetVocabAdjective(atom, src);
                        break;

                    case StdAtom.NOUN:
                    case StdAtom.OBJECT:
                        Context.ZEnvironment.GetVocabNoun(atom, src);
                        break;

                    case StdAtom.BUZZ:
                        Context.ZEnvironment.GetVocabBuzzword(atom, src);
                        break;

                    case StdAtom.PREP:
                        Context.ZEnvironment.GetVocabPreposition(atom, src);
                        break;

                    case StdAtom.DIR:
                        Context.ZEnvironment.GetVocabDirection(atom, src);
                        break;

                    case StdAtom.VERB:
                        Context.ZEnvironment.GetVocabVerb(atom, src);
                        break;

                    default:
                        Context.HandleError(new CompilerError(model, CompilerMessages.Unrecognized_0_1, "part of speech", partOfSpeech));
                        break;
                    }
                },

                ReserveGlobal = atom =>
                {
                    if (globalsByName.TryGetValue(atom, out var g))
                    {
                        g.StorageType = GlobalStorageType.Hard;
                    }
                }
            };

            // for detecting implicitly defined directions
            var directionPattern = Context.GetProp(
                Context.GetStdAtom(StdAtom.DIRECTIONS), Context.GetStdAtom(StdAtom.PROPSPEC)) as ComplexPropDef;

            // create property builders for all properties on this object as needed,
            // and set up P?FOO constants for them. also create vocabulary words for
            // SYNONYM and ADJECTIVE property values, and constants for FLAGS values.
            foreach (var prop in model.Properties)
            {
                using (DiagnosticContext.Push(prop.SourceLine))
                {
                    // the first element must be an atom identifying the property
                    if (!prop.IsCons(out var first, out var propBody) || !(first is ZilAtom atom))
                    {
                        Context.HandleError(new CompilerError(model, CompilerMessages.Property_Specification_Must_Start_With_An_Atom));
                        continue;
                    }

                    ZilAtom uniquePropertyName;

                    // exclude phony built-in properties
                    bool    phony;
                    bool?   isSynonym = null;
                    Synonym synonym   = null;

                    /* We also detect direction properties here, which are tricky for a few reasons:
                     * - They can be implicitly defined by a property spec that looks sufficiently direction-like.
                     * - (IN ROOMS) is not a direction, even if IN has been explicitly defined as a direction...
                     *   but (IN "string") is!
                     * - (FOO BAR) is not enough to implicitly define FOO as a direction, even if (DIR R:ROOM)
                     *   is a pattern for directions.
                     *
                     * Thus, there are a few ways to write a property that ZILF will recognize as a direction.
                     *
                     * If the property name has already been defined as one (e.g. by <DIRECTIONS>), you can either:
                     *   - Put two or more values after the property name: (NORTH TO FOREST), (NORTH 123 456)
                     *   - Put one value after the property name that isn't an atom: (NORTH "You can't go that way.")
                     *
                     * If it hasn't been defined as a direction, you can still implicitly define it right here:
                     *   - Put two or more values after the property name, *and* match the PROPDEF for DIRECTIONS:
                     *     (STARBOARD TO BRIG), (PORT SORRY "You can't jump that far.")
                     */

                    var isKnownDirectionName = Context.ZEnvironment.Directions.Contains(atom);

                    var isDirectionProp = isKnownDirectionName
                        ? propBody.HasLengthAtLeast(2) || !(propBody.IsEmpty || propBody.First is ZilAtom)
                        : propBody.HasLengthAtLeast(2) && directionPattern?.Matches(Context, prop) == true;

                    if (isDirectionProp)
                    {
                        // it's a direction
                        phony = false;

                        // could be a new implicitly defined direction
                        if (!isKnownDirectionName)
                        {
                            synonym = Context.ZEnvironment.Synonyms.FirstOrDefault(s => s.SynonymWord.Atom == atom);

                            if (synonym == null)
                            {
                                isSynonym = false;
                                Context.ZEnvironment.Directions.Add(atom);
                                Context.ZEnvironment.GetVocabDirection(atom, prop.SourceLine);
                                Context.SetPropDef(atom, directionPattern);
                                uniquePropertyName = atom;
                            }
                            else
                            {
                                isSynonym          = true;
                                uniquePropertyName = synonym.OriginalWord.Atom;
                            }
                        }
                        else
                        {
                            uniquePropertyName = atom;
                        }
                    }
                    else
                    {
                        // ReSharper disable once SwitchStatementMissingSomeCases
                        switch (atom.StdAtom)
                        {
                        case StdAtom.DESC:
                            phony = true;
                            uniquePropertyName = PseudoPropertyAtoms.Desc;
                            break;

                        case StdAtom.IN:
                            // (IN FOO) is a location, but (IN "foo") is a property
                            if (propBody.First is ZilAtom)
                            {
                                goto case StdAtom.LOC;
                            }
                            goto default;

                        case StdAtom.LOC:
                            phony = true;
                            uniquePropertyName = PseudoPropertyAtoms.Location;
                            break;

                        case StdAtom.FLAGS:
                            phony = true;
                            // multiple FLAGS definitions are OK
                            uniquePropertyName = null;
                            break;

                        default:
                            phony = false;
                            uniquePropertyName = atom;
                            break;
                        }
                    }

                    if (uniquePropertyName != null)
                    {
                        if (propertiesSoFar.Contains(uniquePropertyName))
                        {
                            Context.HandleError(new CompilerError(
                                                    prop,
                                                    CompilerMessages.Duplicate_0_Definition_1,
                                                    phony ? "pseudo-property" : "property",
                                                    atom.ToStringContext(Context, false)));
                        }
                        else
                        {
                            propertiesSoFar.Add(uniquePropertyName);
                        }
                    }

                    if (!phony && !Properties.ContainsKey(atom))
                    {
                        if (isSynonym == null)
                        {
                            synonym   = Context.ZEnvironment.Synonyms.FirstOrDefault(s => s.SynonymWord.Atom == atom);
                            isSynonym = (synonym != null);
                        }

                        if ((bool)isSynonym)
                        {
                            var origAtom = synonym.OriginalWord.Atom;
                            if (Properties.TryGetValue(origAtom, out var origPb) == false)
                            {
                                DefineProperty(origAtom);
                                origPb = Properties[origAtom];
                            }
                            Properties.Add(atom, origPb);

                            var pAtom = ZilAtom.Parse("P?" + atom.Text, Context);
                            Constants.Add(pAtom, origPb);

                            var origSpec = Context.GetProp(origAtom, Context.GetStdAtom(StdAtom.PROPSPEC));
                            Context.PutProp(atom, Context.GetStdAtom(StdAtom.PROPSPEC), origSpec);
                        }
                        else
                        {
                            DefineProperty(atom);
                        }
                    }

                    // check for a PROPSPEC
                    var propspec = Context.GetProp(atom, Context.GetStdAtom(StdAtom.PROPSPEC));
                    if (propspec != null)
                    {
                        if (propspec is ComplexPropDef complexDef)
                        {
                            // PROPDEF pattern
                            if (complexDef.Matches(Context, prop))
                            {
                                complexDef.PreBuildProperty(Context, prop, preBuilders);
                            }
                        }
                        else
                        {
                            // name of a custom property builder function
                            var form = new ZilForm(new[] { propspec, prop })
                            {
                                SourceLine = prop.SourceLine
                            };
                            var specOutput = (ZilObject)form.Eval(Context);

                            if (specOutput is ZilListoidBase list && list.StdTypeAtom == StdAtom.LIST &&
                                list.Rest is var customBody && !customBody.IsEmpty)
                            {
                                // replace the property body with the propspec's output
                                prop.Rest = customBody;
                            }
                            else
                            {
                                Context.HandleError(new CompilerError(model,
                                                                      CompilerMessages.PROPSPEC_For_Property_0_Returned_A_Bad_Value_1, atom, specOutput));
                            }
                        }
                    }