public IOperand CompileAsOperand([NotNull] IRoutineBuilder rb, [NotNull] ZilObject expr, [NotNull] ISourceLine src, [CanBeNull] IVariable suggestion = null) { expr = expr.Unwrap(Context); var constant = CompileConstant(expr, AmbiguousConstantMode.Pessimistic); if (constant != null) { return(constant); } switch (expr.StdTypeAtom) { case StdAtom.FORM: return(CompileForm(rb, (ZilForm)expr, true, suggestion ?? rb.Stack)); case StdAtom.ATOM: var atom = (ZilAtom)expr; if (Globals.ContainsKey(atom)) { Context.HandleError(new CompilerError(src, CompilerMessages.Bare_Atom_0_Interpreted_As_Global_Variable_Index, atom)); return(Globals[atom].Indirect); } if (SoftGlobals.ContainsKey(atom)) { Context.HandleError(new CompilerError( expr.SourceLine ?? src, CompilerMessages.Soft_Variable_0_May_Not_Be_Used_Here, atom)); } else { Context.HandleError(new CompilerError( expr.SourceLine ?? src, CompilerMessages.Bare_Atom_0_Used_As_Operand_Is_Not_A_Global_Variable, atom)); } return(Game.Zero); default: Context.HandleError(new CompilerError( expr.SourceLine ?? src, CompilerMessages.Expressions_Of_This_Type_Cannot_Be_Compiled)); return(Game.Zero); } }
bool VariableNameInUse([NotNull] ZilAtom atom) { return(Locals.ContainsKey(atom) || TempLocalNames.Contains(atom) || Globals.ContainsKey(atom) || SoftGlobals.ContainsKey(atom) || Constants.ContainsKey(atom) || Objects.ContainsKey(atom) || Routines.ContainsKey(atom)); }
/// <summary> /// Analyzes the set of defined global variables and, if there are too many to fit in Z-machine variables ("hard globals"), /// does the necessary planning and allocation to move some of them into a table ("soft globals"). /// </summary> /// <remarks> /// <para>This method sets <see cref="ZilGlobal.StorageType"/> for all globals, /// whether or not it ends up moving any of them.</para> /// <para>If it does decide to move some globals, it allocates the <see cref="SoftGlobalsTable"/> (<c>T?GLOBAL-VARS-TABLE</c>), /// assigns offsets within the table, and fills the table with the initial values.</para> /// </remarks> /// <param name="reservedGlobals">The number of hard globals that are reserved for other purposes (i.e. parser tables). /// Subtracting <paramref name="reservedGlobals"/> from 240 gives the number of hard globals that are actually available /// for storing <see cref="ZEnvironment.Globals"/>.</param> /// <param name="globalInitializers"></param> /// <exception cref="CompilerError"></exception> void DoFunnyGlobals(int reservedGlobals, Queue <System.Action> globalInitializers) { // if all the globals fit into Z-machine globals, no need for a table int remaining = 240 - reservedGlobals; if (Context.ZEnvironment.Globals.Count <= remaining) { foreach (var g in Context.ZEnvironment.Globals) { g.StorageType = GlobalStorageType.Hard; } return; } // reserve one slot for GLOBAL-VARS-TABLE remaining--; // in V3, the status line variables need to be Z-machine globals if (Context.ZEnvironment.ZVersion < 4) { foreach (var g in Context.ZEnvironment.Globals) { switch (g.Name.StdAtom) { case StdAtom.HERE: case StdAtom.SCORE: case StdAtom.MOVES: g.StorageType = GlobalStorageType.Hard; break; } } } // variables used as operands need to be Z-machine globals too var globalsByName = Context.ZEnvironment.Globals.ToDictionary(g => g.Name); foreach (var r in Context.ZEnvironment.Routines) { r.WalkRoutineForms(f => { var args = f.Rest; if (args != null && !args.IsEmpty) { // skip the first argument to operations that operate on a variable if (f.First is ZilAtom firstAtom) { switch (firstAtom.StdAtom) { case StdAtom.SET: case StdAtom.SETG: case StdAtom.VALUE: case StdAtom.GVAL: case StdAtom.LVAL: case StdAtom.INC: case StdAtom.DEC: case StdAtom.IGRTR_P: case StdAtom.DLESS_P: args = args.Rest; break; } } while (args != null && !args.IsEmpty) { if (args.First is ZilAtom atom && globalsByName.TryGetValue(atom, out var g)) { g.StorageType = GlobalStorageType.Hard; } args = args.Rest; } } }); } // determine which others to keep in Z-machine globals var lookup = Context.ZEnvironment.Globals.ToLookup(g => g.StorageType); var hardGlobals = new List <ZilGlobal>(remaining); if (lookup.Contains(GlobalStorageType.Hard)) { hardGlobals.AddRange(lookup[GlobalStorageType.Hard]); if (hardGlobals.Count > remaining) { throw new CompilerError( CompilerMessages.Too_Many_0_1_Defined_Only_2_Allowed, "hard globals", hardGlobals.Count, remaining); } } var softGlobals = new Queue <ZilGlobal>(Context.ZEnvironment.Globals.Count - hardGlobals.Count); if (lookup.Contains(GlobalStorageType.Any)) { foreach (var g in lookup[GlobalStorageType.Any]) { softGlobals.Enqueue(g); } } if (lookup.Contains(GlobalStorageType.Soft)) { foreach (var g in lookup[GlobalStorageType.Soft]) { softGlobals.Enqueue(g); } } while (hardGlobals.Count < remaining && softGlobals.Count > 0) { hardGlobals.Add(softGlobals.Dequeue()); } // assign final StorageTypes foreach (var g in hardGlobals) { g.StorageType = GlobalStorageType.Hard; } foreach (var g in softGlobals) { g.StorageType = GlobalStorageType.Soft; } // create SoftGlobals entries, fill table, and assign offsets int byteOffset = 0; var table = Game.DefineTable("T?GLOBAL-VARS-TABLE", false); var tableGlobal = Game.DefineGlobal("GLOBAL-VARS-TABLE"); tableGlobal.DefaultValue = table; Globals.Add(Context.GetStdAtom(StdAtom.GLOBAL_VARS_TABLE), tableGlobal); SoftGlobalsTable = tableGlobal; foreach (var g in softGlobals) { if (!g.IsWord) { var entry = new SoftGlobal { IsWord = false, Offset = byteOffset }; SoftGlobals.Add(g.Name, entry); var gSave = g; globalInitializers.Enqueue(() => table.AddByte(GetGlobalDefaultValue(gSave) ?? Game.Zero)); byteOffset++; } } if (byteOffset % 2 != 0) { byteOffset++; globalInitializers.Enqueue(() => table.AddByte(Game.Zero)); } foreach (var g in softGlobals) { if (g.IsWord) { var entry = new SoftGlobal { IsWord = true, Offset = byteOffset / 2 }; SoftGlobals.Add(g.Name, entry); var gSave = g; globalInitializers.Enqueue(() => table.AddShort(GetGlobalDefaultValue(gSave) ?? Game.Zero)); byteOffset += 2; } } }