public VoidCall([NotNull] Compilation cc, [NotNull] IRoutineBuilder rb, [NotNull] ZilForm form) : this() { this.cc = cc; this.rb = rb; this.form = form; }
public ValueCall([NotNull] Compilation cc, [NotNull] IRoutineBuilder rb, [NotNull] ZilForm form, [NotNull] IVariable resultStorage) : this() { this.cc = cc; this.rb = rb; this.form = form; this.resultStorage = resultStorage; }
public PredCall([NotNull] Compilation cc, [NotNull] IRoutineBuilder rb, [NotNull] ZilForm form, [NotNull] ILabel label, bool polarity) : this() { this.cc = cc; this.rb = rb; this.form = form; this.label = label; this.polarity = polarity; }
static ZilFix TranslateType([NotNull] Context ctx, [NotNull] ZilAtom type) { // ReSharper disable once SwitchStatementMissingSomeCases switch (type.StdAtom) { case StdAtom.TADJ: type = ctx.GetStdAtom(StdAtom.ADJ); break; case StdAtom.TOBJECT: type = ctx.GetStdAtom(StdAtom.NOUN); break; case StdAtom.TPREP: type = ctx.GetStdAtom(StdAtom.PREP); break; case StdAtom.TDIR: type = ctx.GetStdAtom(StdAtom.DIR); break; case StdAtom.TVERB: type = ctx.GetStdAtom(StdAtom.VERB); break; } ZilFix classification; switch (type.StdAtom) { case StdAtom.BUZZ: case StdAtom.TBUZZ: case StdAtom.TZERO: classification = ZilFix.Zero; break; default: // call user-provided <GET-CLASSIFICATION type> var form = new ZilForm(new ZilObject[] { ctx.GetStdAtom(StdAtom.GET_CLASSIFICATION), type }); classification = (ZilObject)form.Eval(ctx) as ZilFix; if (classification == null) { throw new InterpreterError(InterpreterMessages._0_1_Must_Return_2, "NEW-ADD-WORD", "GET-CLASSIFICATION", "a FIX"); } break; } return(classification); }
internal void ExpandInPlace([NotNull] Context ctx) { IEnumerable <ZilObject> RecursiveExpandWithSplice(ZilObject zo) { ZilObject result; ZilObject SetSourceLine(ZilResult zr) { var newObj = (ZilObject)zr; newObj.SourceLine = zo.SourceLine; return(newObj); } switch (zo) { case ZilList list: result = new ZilList(list.SelectMany(RecursiveExpandWithSplice)); break; case ZilVector vector: result = new ZilVector(vector.SelectMany(RecursiveExpandWithSplice).ToArray()); break; case ZilForm form: ZilObject expanded; try { using (DiagnosticContext.Push(form.SourceLine)) { expanded = (ZilObject)form.Expand(ctx); } } catch (InterpreterError ex) { ctx.HandleError(ex); return(new[] { ctx.FALSE }); } if (expanded is IMayExpandAfterEvaluation expandAfter && expandAfter.ShouldExpandAfterEvaluation) { return(expandAfter.ExpandAfterEvaluation().AsResultSequence() .Select(SetSourceLine) .Select(xo => ReferenceEquals(xo, form) ? xo : new ZilMacroResult(xo))); } else if (!ReferenceEquals(expanded, form)) { expanded.SourceLine = zo.SourceLine; return(RecursiveExpandWithSplice(expanded) .Select(xo => new ZilMacroResult(xo))); } else { result = new ZilForm(form.SelectMany(RecursiveExpandWithSplice)); } break;
public ValuePredCall([NotNull] Compilation cc, [NotNull] IRoutineBuilder rb, [NotNull] ZilForm form, [NotNull] IVariable resultStorage, [NotNull] ILabel label, bool polarity) : this() { this.cc = cc; this.rb = rb; this.form = form; this.resultStorage = resultStorage; this.label = label; this.polarity = polarity; }
public void TestEVAL() { // most values eval to themselves TestHelpers.EvalAndAssert("<EVAL 123>", new ZilFix(123)); TestHelpers.EvalAndAssert("<EVAL \"hello\">", ZilString.FromString("hello")); var ctx = new Context(); TestHelpers.EvalAndAssert(ctx, "<EVAL +>", ctx.GetStdAtom(StdAtom.Plus)); TestHelpers.EvalAndAssert(ctx, "<EVAL <>>", ctx.FALSE); // lists eval to new lists formed by evaluating each element var list = new ZilList(new ZilObject[] { new ZilFix(1), new ZilForm(new ZilObject[] { ctx.GetStdAtom(StdAtom.Plus), new ZilFix(1), new ZilFix(1) }), new ZilFix(3) }); var expected = new ZilList(new ZilObject[] { new ZilFix(1), new ZilFix(2), new ZilFix(3) }); ctx.SetLocalVal(ctx.GetStdAtom(StdAtom.T), list); var actual = TestHelpers.Evaluate(ctx, "<EVAL .T>"); TestHelpers.AssertStructurallyEqual(expected, actual); // forms execute when evaluated var form = new ZilForm(new ZilObject[] { ctx.GetStdAtom(StdAtom.Plus), new ZilFix(1), new ZilFix(2) }); ctx.SetLocalVal(ctx.GetStdAtom(StdAtom.T), form); TestHelpers.EvalAndAssert(ctx, "<EVAL .T>", new ZilFix(3)); // must have 1-2 arguments TestHelpers.EvalAndCatch <ArgumentCountError>("<EVAL>"); TestHelpers.EvalAndCatch <ArgumentCountError>("<EVAL FOO BAR BAZ>"); // 2nd argument must be an ENVIRONMENT TestHelpers.EvalAndCatch <ArgumentTypeError>("<EVAL FOO BAR>"); TestHelpers.Evaluate(ctx, "<SET A 0>"); TestHelpers.Evaluate(ctx, "<DEFINE RIGHT (\"BIND\" E 'B \"AUX\" (A 1)) <EVAL .B .E>>"); TestHelpers.EvalAndAssert(ctx, "<RIGHT .A>", new ZilFix(0)); }
static bool CheckFormOrSegment([NotNull] Context ctx, [NotNull] ZilObject value, [NotNull] ZilForm form, bool segment, bool ignoreErrors) { var(first, rest) = form; // special forms // ReSharper disable once SwitchStatementMissingSomeCases switch ((first as ZilAtom)?.StdAtom) { case StdAtom.OR: return(rest.Any(subpattern => Check(ctx, value, subpattern, ignoreErrors))); case StdAtom.QUOTE: return(rest.First?.StructurallyEquals(value) ?? false); case StdAtom.PRIMTYPE when rest.First is ZilAtom primType: // special case for GVAL and LVAL, which can substitute for <PRIMTYPE ATOM> return (value.PrimType == ctx.GetTypePrim(primType) || primType.StdAtom == StdAtom.ATOM && (value.IsGVAL(out _) || value.IsLVAL(out _))); } // structure form: first pattern element is a DECL matched against the whole structure // (usually a type atom), remaining elements are matched against the structure elements if (first == null || !Check(ctx, value, first, ignoreErrors)) { return(false); } if (value is IStructure valueAsStructure) { // yay } else if (value is IProvideStructureForDeclCheck structProvider) { valueAsStructure = structProvider.GetStructureForDeclCheck(ctx); } else { return(false); } return(CheckElements(ctx, valueAsStructure, rest, segment, ignoreErrors)); }
public void MakeVerb(IWord word, ISourceLine location) { var nw = (NewParserWord)word; if (!nw.HasClass(verbClass)) { var form = new ZilForm(new ZilObject[] { ctx.GetStdAtom(StdAtom.MAKE_VERB_DATA) }); var verbData = (ZilObject)form.Eval(ctx); nw.VerbStuff = verbData; ctx.PutProp(verbData, ctx.GetStdAtom(StdAtom.VERB_STUFF_ID), nw.Inner); NewAddWord( nw.Atom, ctx.GetStdAtom(StdAtom.TVERB), verbData, ZilFix.Zero); } }
/* GetViaInner and SetViaInner disable DECL checking because user code may expect * property identifiers to be passed as FIXes instead of ATOMs. */ ZilObject GetViaInner(StdAtom accessor) { var oldCheckDecls = ctx.CheckDecls; try { ctx.CheckDecls = false; var form = new ZilForm(new ZilObject[] { ctx.GetStdAtom(accessor), Inner }); return((ZilObject)form.Eval(ctx)); } finally { ctx.CheckDecls = oldCheckDecls; } }
public static NewParserWord FromVword([NotNull] Context ctx, [NotNull] ZilHash vword) { var form = new ZilForm(new ZilObject[] { ctx.GetStdAtom(StdAtom.WORD_LEXICAL_WORD), vword }); if (!((ZilObject)form.Eval(ctx) is ZilString lexicalWord)) { throw new InterpreterError( InterpreterMessages._0_1_Must_Return_2, InterpreterMessages.NoFunction, "WORD-LEXICAL-WORD", "a string"); } var atom = ZilAtom.Parse(lexicalWord.Text, ctx); return(new NewParserWord(ctx, atom, vword)); }
internal static ZilForm SubstituteIfflagForm([NotNull] Context ctx, [NotNull] ZilForm form) { var body = form.Select(zo => { ZilObject value; switch (zo) { case ZilAtom atom when((value = ctx.GetCompilationFlagValue(atom)) != null): return(value); default: return(zo); } }); return(new ZilForm(body) { SourceLine = form.SourceLine }); }
/// <exception cref="InterpreterError">MAKE-VWORD did not return a VWORD.</exception> public IWord CreateWord(ZilAtom atom) { var form = new ZilForm(new ZilObject[] { ctx.GetStdAtom(StdAtom.MAKE_VWORD), ZilString.FromString(atom.Text), ZilFix.Zero, ZilFix.Zero }); if (!((ZilObject)form.Eval(ctx) is ZilHash vword) || vword.StdTypeAtom != StdAtom.VWORD) { throw new InterpreterError( InterpreterMessages._0_1_Must_Return_2, InterpreterMessages.NoFunction, "MAKE-VWORD", "a VWORD"); } return(new NewParserWord(ctx, atom, vword)); }
void SetViaInner(StdAtom accessor, ZilObject value) { var oldCheckDecls = ctx.CheckDecls; try { ctx.CheckDecls = false; var form = new ZilForm(new[] { ctx.GetStdAtom(accessor), Inner, value }); form.Eval(ctx); } finally { ctx.CheckDecls = oldCheckDecls; } }
public void TestEXPAND() { // most values expand to themselves TestHelpers.EvalAndAssert("<EXPAND 123>", new ZilFix(123)); TestHelpers.EvalAndAssert("<EXPAND \"hello\">", ZilString.FromString("hello")); var ctx = new Context(); TestHelpers.EvalAndAssert(ctx, "<EXPAND +>", ctx.GetStdAtom(StdAtom.Plus)); TestHelpers.EvalAndAssert(ctx, "<EXPAND <>>", ctx.FALSE); // lists expand to copies of themselves var list = new ZilList(new ZilObject[] { new ZilFix(1), new ZilFix(2), new ZilFix(3) }); ctx.SetLocalVal(ctx.GetStdAtom(StdAtom.T), list); var actual = TestHelpers.Evaluate(ctx, "<EXPAND .T>"); TestHelpers.AssertStructurallyEqual(list, actual); Assert.AreNotSame(list, actual); // forms execute when evaluated TestHelpers.Evaluate(ctx, "<DEFMAC FOO () <FORM BAR>>"); var expected = new ZilForm(new ZilObject[] { ZilAtom.Parse("BAR", ctx) }); TestHelpers.EvalAndAssert(ctx, "<EXPAND '<FOO>>", expected); TestHelpers.EvalAndAssert(ctx, "<EXPAND <FORM ,FOO>>", expected); // if the form doesn't contain a macro, it still executes TestHelpers.Evaluate(ctx, "<DEFINE BAR () 123>"); TestHelpers.EvalAndAssert(ctx, "<EXPAND '<BAR>>", new ZilFix(123)); TestHelpers.EvalAndAssert(ctx, "<EXPAND <FORM ,BAR>>", new ZilFix(123)); // must have 1 argument TestHelpers.EvalAndCatch <InterpreterError>("<EXPAND>"); TestHelpers.EvalAndCatch <InterpreterError>("<EXPAND FOO BAR>"); }
static ZilObject Dummy_FormArg(Context ctx, ZilForm form) { return(null); }
protected Frame([NotNull] Context ctx, [NotNull] ZilForm callingForm) { Context = ctx; Parent = ctx.TopFrame; SourceLine = callingForm.SourceLine; }
TellPattern(Token[] tokens, ZilForm outputForm) { this.tokens = tokens; this.outputForm = outputForm; }
internal ZilObject NewAddWord([NotNull] ZilAtom name, ZilAtom type, [CanBeNull] ZilObject value, [NotNull] ZilFix flags) { bool typeProvided; if (type == null) { typeProvided = false; type = ctx.GetStdAtom(StdAtom.TZERO); } else { typeProvided = true; } // find new CLASS by translating TYPE var classification = TranslateType(ctx, type); // create the word or merge into the existing one NewParserWord word; if (ctx.ZEnvironment.Vocabulary.TryGetValue(name, out var iword) == false) { // create it by calling user-provided <MAKE-VWORD name class flags> var form = new ZilForm(new ZilObject[] { ctx.GetStdAtom(StdAtom.MAKE_VWORD), ZilString.FromString(name.Text), classification, flags }); var vword = (ZilObject)form.Eval(ctx); if (vword.StdTypeAtom != StdAtom.VWORD) { throw new InterpreterError(InterpreterMessages._0_1_Must_Return_2, "NEW-ADD-WORD", "MAKE-VWORD", "a VWORD"); } word = NewParserWord.FromVword(ctx, (ZilHash)vword); ctx.ZEnvironment.Vocabulary.Add(name, word); } else { word = (NewParserWord)iword; // if old and new CLASS differ in the high bit, error (word class conflict) if ((word.Classification & 0x8000) != (classification.Value & 0x8000)) { throw new InterpreterError(InterpreterMessages._0_New_Classification_1_Is_Incompatible_With_Previous_2, "NEW-ADD-WORD", classification, word.Classification); } // merge new CLASS into the word var combinedClassification = word.Classification | classification.Value; if (ctx.ZEnvironment.ZVersion >= 4) { if (typeProvided && (combinedClassification & (dirClass | verbClass)) == (dirClass | verbClass) && (word.SemanticStuff != null || word.DirId != null) && value != null) { throw new InterpreterError(InterpreterMessages._0_Word_Would_Be_Overloaded, "NEW-ADD-WORD"); } } word.Classification = combinedClassification; // merge new FLAGS into the word word.Flags |= flags.Value; } // store flags if (flags.Value != 0) { var compFlag = ctx.GetCompilationFlagValue("WORD-FLAGS-IN-TABLE"); if (compFlag != null && compFlag.IsTrue) { // prepend .WORD .FLAGS to ,WORD-FLAGS-LIST var wordFlagsList = ctx.GetGlobalVal(ctx.GetStdAtom(StdAtom.WORD_FLAGS_LIST)) ?? new ZilList(null, null); if (wordFlagsList is ZilList list) { list = new ZilList(word.Inner, new ZilList(flags, list)); ctx.SetGlobalVal(ctx.GetStdAtom(StdAtom.WORD_FLAGS_LIST), list); } else { throw new InterpreterError( InterpreterMessages._0_Value_Of_1_Must_Be_2, "global", "WORD-FLAGS-LIST", "a list"); } } } if (value != null) { if (classification.Value == adjClass) { // store VALUE as word's ADJ-ID (V3) or SEMANTIC-STUFF (V4+) if (ctx.ZEnvironment.ZVersion >= 4) { word.SemanticStuff = value; } else { word.AdjId = value; } } else if (classification.Value == dirClass) { // store VALUE as word's DIR-ID word.DirId = value; } else { // store VALUE as word's SEMANTIC-STUFF word.SemanticStuff = value; } } return(word.Atom); }
internal IOperand CompileForm([NotNull] IRoutineBuilder rb, [NotNull] ZilForm form, bool wantResult, IVariable resultStorage) { using (DiagnosticContext.Push(form.SourceLine)) { var unwrapped = form.Unwrap(Context); if (!ReferenceEquals(unwrapped, form)) { switch (unwrapped) { case ZilForm newForm: form = newForm; break; default: return(wantResult ? CompileAsOperand(rb, unwrapped, form.SourceLine, resultStorage) : null); } } if (!(form.First is ZilAtom head)) { Context.HandleError(new CompilerError(form, CompilerMessages.FORM_Must_Start_With_An_Atom)); return(wantResult ? Game.Zero : null); } // built-in statements handled by ZBuiltins var zversion = Context.ZEnvironment.ZVersion; Debug.Assert(form.Rest != null); var argCount = form.Rest.Count(); if (wantResult) { // prefer the value version, then value+predicate, predicate, void if (ZBuiltins.IsBuiltinValueCall(head.Text, zversion, argCount)) { return(ZBuiltins.CompileValueCall(head.Text, this, rb, form, resultStorage)); } if (ZBuiltins.IsBuiltinValuePredCall(head.Text, zversion, argCount)) { var label1 = rb.DefineLabel(); resultStorage = resultStorage ?? rb.Stack; ZBuiltins.CompileValuePredCall(head.Text, this, rb, form, resultStorage, label1, true); rb.MarkLabel(label1); return(resultStorage); } if (ZBuiltins.IsBuiltinPredCall(head.Text, zversion, argCount)) { var label1 = rb.DefineLabel(); var label2 = rb.DefineLabel(); resultStorage = resultStorage ?? rb.Stack; ZBuiltins.CompilePredCall(head.Text, this, rb, form, label1, true); rb.EmitStore(resultStorage, Game.Zero); rb.Branch(label2); rb.MarkLabel(label1); rb.EmitStore(resultStorage, Game.One); rb.MarkLabel(label2); return(resultStorage); } if (ZBuiltins.IsBuiltinVoidCall(head.Text, zversion, argCount)) { ZBuiltins.CompileVoidCall(head.Text, this, rb, form); return(Game.One); } } else { // prefer the void version, then predicate, value, value+predicate // (predicate saves a cleanup instruction) if (ZBuiltins.IsBuiltinVoidCall(head.Text, zversion, argCount)) { ZBuiltins.CompileVoidCall(head.Text, this, rb, form); return(null); } if (ZBuiltins.IsBuiltinPredCall(head.Text, zversion, argCount)) { var dummy = rb.DefineLabel(); ZBuiltins.CompilePredCall(head.Text, this, rb, form, dummy, true); rb.MarkLabel(dummy); return(null); } if (ZBuiltins.IsBuiltinValueCall(head.Text, zversion, argCount)) { if (ZBuiltins.CompileValueCall(head.Text, this, rb, form, null) == rb.Stack) { rb.EmitPopStack(); } return(null); } if (ZBuiltins.IsBuiltinValuePredCall(head.Text, zversion, argCount)) { var label1 = rb.DefineLabel(); ZBuiltins.CompileValuePredCall(head.Text, this, rb, form, rb.Stack, label1, true); rb.MarkLabel(label1); rb.EmitPopStack(); return(null); } } // routine calls var obj = Context.GetZVal(Context.ZEnvironment.InternGlobalName(head)); while (obj is ZilConstant cnst) { obj = cnst.Value; } switch (obj) { case ZilRoutine rtn: // check argument count var args = form.Skip(1).ToArray(); if (args.Length < rtn.ArgSpec.MinArgCount || rtn.ArgSpec.MaxArgCount != null && args.Length > rtn.ArgSpec.MaxArgCount) { Context.HandleError(CompilerError.WrongArgCount( rtn.Name?.ToString() ?? "<unnamed routine>", new ArgCountRange(rtn.ArgSpec.MinArgCount, rtn.ArgSpec.MaxArgCount))); return(wantResult ? Game.Zero : null); } // compile routine call resultStorage = wantResult ? (resultStorage ?? rb.Stack) : null; using (var argOperands = CompileOperands(rb, form.SourceLine, args)) { rb.EmitCall(Routines[head], argOperands.AsArray(), resultStorage); } return(resultStorage); case ZilFalse _: // this always returns 0. we can eliminate the call if none of the arguments have side effects. var argsWithSideEffects = form.Skip(1).Where(HasSideEffects).ToArray(); if (argsWithSideEffects.Length <= 0) { return(Game.Zero); } resultStorage = wantResult ? (resultStorage ?? rb.Stack) : null; using (var argOperands = CompileOperands(rb, form.SourceLine, argsWithSideEffects)) { var operands = argOperands.AsArray(); if (operands.Any(o => o == rb.Stack)) { rb.EmitCall(Game.Zero, operands.Where(o => o == rb.Stack).ToArray(), resultStorage); } } return(resultStorage); default: // unrecognized if (!ZBuiltins.IsNearMatchBuiltin(head.Text, zversion, argCount, out var error)) { error = new CompilerError(CompilerMessages.Unrecognized_0_1, "routine or instruction", head); } Context.HandleError(error); return(wantResult ? Game.Zero : null); } } }
public ZilObject ToZilObject() { ZilObject result; StdAtom head; switch (Type) { case OutputElementType.Length: result = Fix ?? FALSE; break; case OutputElementType.Many: result = ZilString.FromString("MANY"); break; case OutputElementType.Adjective: head = StdAtom.ADJ; goto TwoElementForm; case OutputElementType.Byte: head = StdAtom.BYTE; goto TwoElementForm; case OutputElementType.Global: head = StdAtom.GLOBAL; goto TwoElementForm; case OutputElementType.Noun: head = StdAtom.NOUN; goto TwoElementForm; case OutputElementType.Object: head = StdAtom.OBJECT; goto TwoElementForm; case OutputElementType.Room: head = StdAtom.ROOM; goto TwoElementForm; case OutputElementType.String: head = StdAtom.STRING; goto TwoElementForm; case OutputElementType.Word: head = StdAtom.WORD; TwoElementForm: result = new ZilForm(new[] { GetStdAtom(head), (ZilObject)Fix ?? new ZilForm(new[] { GetStdAtom(StdAtom.LVAL), Variable }) }); break; case OutputElementType.Voc: result = new ZilForm(new[] { GetStdAtom(StdAtom.VOC), (ZilObject)Fix ?? new ZilForm(new[] { GetStdAtom(StdAtom.LVAL), Variable }), PartOfSpeech }); break; default: throw UnhandledCaseException.FromEnum(Type); } if (Constant != null) { result = new ZilList(new[] { Constant, result }); } return(result); }
/// <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)); } } }
static OutputElement ConvertOutputForm([NotNull] ZilForm form, [CanBeNull] ZilAtom constant) { // validate and parse if (!form.StartsWith(out ZilAtom head)) { throw new InterpreterError( form, InterpreterMessages.Element_0_Of_1_In_2_Must_Be_3, 1, "FORM", "PROPDEF pattern", "an atom"); } int length = 2; OutputElementType type; // ReSharper disable once SwitchStatementMissingSomeCases switch (head.StdAtom) { case StdAtom.BYTE: type = OutputElementType.Byte; break; case StdAtom.WORD: type = OutputElementType.Word; break; case StdAtom.OBJECT: type = OutputElementType.Object; break; case StdAtom.ROOM: type = OutputElementType.Room; break; case StdAtom.GLOBAL: type = OutputElementType.Global; break; case StdAtom.NOUN: type = OutputElementType.Noun; break; case StdAtom.ADJ: case StdAtom.ADJECTIVE: type = OutputElementType.Adjective; break; case StdAtom.VOC: type = OutputElementType.Voc; length = 3; break; case StdAtom.STRING: type = OutputElementType.String; break; default: throw new InterpreterError( form, InterpreterMessages.FORM_In_PROPDEF_Output_Pattern_Must_Be_BYTE_WORD_STRING_OBJECT_ROOM_GLOBAL_NOUN_ADJ_Or_VOC); } if (!form.HasLength(length)) { throw new InterpreterError( form, InterpreterMessages._0_FORM_In_PROPDEF_Output_Pattern_Must_Have_Length_1, head, length); } Debug.Assert(form.Rest?.Rest != null); ZilAtom outVariable; ZilFix outFix; if (form.StartsWith(out ZilAtom _, out ZilObject zo) && zo.IsLVAL(out var atom)) { outVariable = atom; outFix = null; }
public CallFrame([NotNull] Context ctx, [NotNull] ZilForm callingForm) : base(ctx, callingForm.SourceLine) { CallingForm = callingForm; }