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>"); }
public static ZilResult ISTRING(Context ctx, int count, [CanBeNull] ZilObject init = null) { if (count < 0) { throw new InterpreterError( InterpreterMessages._0_Expected_1, "ISTRING: arg 1", "a non-negative FIX"); } var contents = new List <char>(count); for (int i = 0; i < count; i++) { if (init != null) { var initResult = init.Eval(ctx); if (initResult.ShouldPass()) { return(initResult); } if (!((ZilObject)initResult is ZilChar ch)) { throw new InterpreterError(InterpreterMessages._0_Iterated_Values_Must_Be_CHARACTERs, "ISTRING"); } contents.Add(ch.Char); } else { contents.Add('\0'); } } return(ZilString.FromString(new string(contents.ToArray()))); }
static string TranslateString([NotNull] ZilString zstr, [NotNull] Context ctx, char crlfChar, StringSpacesMode spacesMode) { // strip CR/LF and ensure 1 space afterward, translate crlfChar to LF, // and collapse two spaces after '.' or crlfChar into one var sb = new StringBuilder(zstr.Text); char?last = null; bool sawDotSpace = false; var zversion = ctx.ZEnvironment.ZVersion; string DescribeChar(byte zscii) { switch (zscii) { case 8: return("backspace"); case 9: return("tab"); case 11: return("sentence space"); case 27: return("escape"); case var _ when zscii < 32: return("^" + (char)(zscii + 64)); case var _ when zscii < 127: return("'" + (char)zscii + "'"); default: return(null); } } for (int i = 0; i < sb.Length; i++) { char c = sb[i]; byte b = UnicodeTranslation.ToZscii(c); if (!StringEncoder.IsPrintable(b, zversion)) { var warning = new CompilerError(zstr, CompilerMessages.ZSCII_0_1_Cannot_Be_Safely_Printed_In_Zmachine_Version_2, b, DescribeChar(b), zversion); ctx.HandleError(warning); } switch (spacesMode) { case StringSpacesMode.CollapseAfterPeriod: if ((last == '.' || last == crlfChar) && c == ' ') { sawDotSpace = true; } else if (sawDotSpace && c == ' ') { sb.Remove(i--, 1); sawDotSpace = false; last = c; continue; } else { sawDotSpace = false; } break; case StringSpacesMode.CollapseWithSentenceSpace: if ((last == '.' || last == '?' || last == '!') && c == ' ') { sawDotSpace = true; } else if (sawDotSpace && c == ' ') { sb.Remove(i--, 1); sb[i] = SentenceSpaceChar; sawDotSpace = false; last = c; continue; } else { sawDotSpace = false; } break; } switch (c) { case '\r': sb.Remove(i--, 1); continue; case '\n': if (last == crlfChar) { sb.Remove(i--, 1); } else { sb[i] = ' '; } break; default: if (c == crlfChar) { sb[i] = '\n'; } break; } last = c; } return(sb.ToString()); }
public void ISTRING_Should_Evaluate_Initializer_Each_Time() { TestHelpers.EvalAndAssert("<SET X 64> <ISTRING 3 '<ASCII <SET X <+ .X 1>>>>", ZilString.FromString("ABC")); }
public void REST_Of_One_Character_String_Should_Be_Empty_String() { TestHelpers.EvalAndAssert("<REST \"x\">", ZilString.FromString("")); TestHelpers.EvalAndAssert("<REST <REST \"xx\">>", ZilString.FromString("")); }
public void TestUNPARSE() { TestHelpers.EvalAndAssert("<UNPARSE 123>", ZilString.FromString("123")); TestHelpers.EvalAndAssert("<UNPARSE '(\"FOO\" [BAR])>", ZilString.FromString("(\"FOO\" [BAR])")); }
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); }
public void TUPLE_Is_Included_In_ZilListBody() { var ctx = new Context(); var spec = ArgSpec.Parse("test", ZilAtom.Parse("FOO", ctx), null, new ZilObject[] { ZilString.FromString("TUPLE"), ZilAtom.Parse("A", ctx) }); TestHelpers.AssertStructurallyEqual( new ZilObject[] { ZilString.FromString("TUPLE"), ZilAtom.Parse("A", ctx) }, spec.AsZilListBody().ToArray()); }
public static ZilObject UNPARSE([NotNull] Context ctx, [NotNull] ZilObject arg) { // in MDL, this takes an optional second argument (radix), but we don't bother return(ZilString.FromString(arg.ToStringContext(ctx, false))); }
public static ZilObject SPNAME(Context ctx, [NotNull] ZilAtom atom) { return(ZilString.FromString(atom.Text)); }
public static ZilObject SUBSTRUC(Context ctx, [NotNull] IStructure from, int rest = 0, int?amount = null, [CanBeNull] IStructure dest = null) { if (amount != null) { var max = from.GetLength(rest + (int)amount); if (max != null && max.Value - rest < amount) { throw new InterpreterError( InterpreterMessages._0_1_Element1s_Requested_But_Only_2_Available, "SUBSTRUC", amount, max.Value - rest); } } else { amount = from.GetLength() - rest; } if (amount < 0) { throw new InterpreterError(InterpreterMessages._0_Negative_Element_Count, "SUBSTRUC"); } var fromObj = (ZilObject)from; var destObj = (ZilObject)dest; var primitive = (IStructure)fromObj.GetPrimitive(ctx); if (destObj != null) { // modify an existing structure if (destObj.PrimType != fromObj.PrimType) { throw new InterpreterError(InterpreterMessages._0_Destination_Must_Have_Same_Primtype_As_Source, "SUBSTRUC"); } int i; switch (dest) { case ZilListoidBase list: foreach (var item in primitive.Skip(rest).Take((int)amount)) { if (list.IsEmpty) { throw new InterpreterError(InterpreterMessages._0_Destination_Too_Short, "SUBSTRUC"); } Debug.Assert(list.Rest != null); list.First = item; list = list.Rest; } break; case ZilString str: // this is crazy inefficient, but works with ZilString and OffsetString // TODO: method on ZilString to do this more efficiently? for (i = 0; i < amount; i++) { str[i] = primitive[i + rest]; } break; case ZilVector vector: i = 0; foreach (var item in primitive.Skip(rest).Take((int)amount)) { if (i >= vector.GetLength()) { throw new InterpreterError(InterpreterMessages._0_Destination_Too_Short, "SUBSTRUC"); } vector[i++] = item; } break; default: throw new InterpreterError(InterpreterMessages._0_Destination_Type_Not_Supported_1, "SUBSTRUC", destObj.GetTypeAtom(ctx)); } return(destObj); } // no destination, return a new structure switch (fromObj.PrimType) { case PrimType.LIST: return(new ZilList(primitive.Skip(rest).Take((int)amount))); case PrimType.STRING: return(ZilString.FromString(((ZilString)primitive).Text.Substring(rest, (int)amount))); case PrimType.TABLE: throw new InterpreterError(InterpreterMessages._0_Primtype_TABLE_Not_Supported, "SUBSTRUC"); case PrimType.VECTOR: return(new ZilVector(((ZilVector)primitive).Skip(rest).Take((int)amount).ToArray())); default: throw UnhandledCaseException.FromEnum(fromObj.PrimType, "structured primtype"); } }
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); }