#region Copyright // -------------------------------------------------------------------------------------------------------------------- // // Copyright (C) 2015 Ian Horswill // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in the // Software without restriction, including without limitation the rights to use, copy, // modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, // and to permit persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // -------------------------------------------------------------------------------------------------------------------- #endregion using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Text; using UnityEngine; namespace Prolog { /// /// Static class with utilities for interacting with Prolog. /// [Documentation("Static class with utilities for interacting with Prolog.")] public static class Prolog { public static string CurrentSourceFile; public static string CurrentSourceFileTrimmed { get { return TrimPath(CurrentSourceFile); } } public static int CurrentSourceLineNumber; public static TextWriter TraceOutput; /// /// File name extensions that are recognized as prolog source code. /// [Documentation("File name extensions that are recognized as prolog source code.")] public static string[] SourceFileExtensions = {".pl", ".p", ".prolog", ".pro"}; #region Parsing /// /// Converts English text string to Prolog-format list of words (symbols). /// public static Structure StringToWordList(string text, bool allowUnquote = false, List vars = null) { var b = new StringBuilder(); var words = new List(); bool unquoted = false; foreach (var c in text) { if (unquoted) { b.Append(c); if (c == ']') { b.Append('.'); var s = b.ToString(); if (vars == null) vars = new List(); words.Add(new ISOPrologReader(s, vars).ReadTermWithExistingVars()); b.Length = 0; unquoted = false; } } else switch (c) { case ' ': case '\t': if (b.Length > 0) { words.Add(GetLexicalItem(b.ToString())); b.Length = 0; } break; case '.': case '!': case ',': case '?': case '(': case ')': case '[': case ']': case '\'': case '"': if (b.Length > 0) { words.Add(GetLexicalItem(b.ToString())); b.Length = 0; } if (c == '[' && allowUnquote) { b.Append(c); unquoted = true; } else words.Add(GetLexicalItem(new string(c, 1))); break; default: b.Append(c); break; } } if (b.Length > 0) { words.Add(GetLexicalItem(b.ToString())); } return IListToPrologList(words); } static readonly Dictionary LexicalItems = new Dictionary(); public static Symbol GetLexicalItem(string p) { var l = p.ToLower(); Symbol s; if (LexicalItems.TryGetValue(l, out s)) return s; return Symbol.Intern(l); } public static bool IsLexicalItem(string s) { var l = s.ToLower(); return LexicalItems.ContainsKey(l); // || Symbol.IsInterned(l); } /// /// True if string is a prefix of some lexical item other than the string itself. /// public static bool IsPrefixOfDistinctLexicalItem(string s) { foreach (var pair in LexicalItems) { var item = pair.Key; if (item != s && item.StartsWith(s)) return true; } return false; } public static void RegisterLexicalItem(Symbol s) { LexicalItems[s.Name.ToLower()] = s; } /// /// List of enclitics (suffixes attached by a ') in English /// public static readonly string[] EnglishEnclitics = { "s", "m", "t", "d", "re", "ll","ve" }; /// /// Convert Prolog list of words into one text string. /// [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "wordList"), SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "WordList"), Documentation("Convert Prolog list of words into one text string.")] public static string WordListToString(Structure wordList) { var s = new StringBuilder(); string lastToken = null; foreach (var word in PrologListToIList(wordList)) { string str = word.ToString(); if (lastToken != null && char.IsLetterOrDigit(str[0]) && !(lastToken == "'" && EnglishEnclitics.Contains(str))) s.Append(' '); s.Append(str); lastToken = str; } return s.ToString(); } public static string DefaultSourceFileExtension = ".prolog"; #endregion #region List conversions /// /// Convert a scheme-format list (really a .NET IList) to a Prolog list. /// [Documentation("Convert a .NET IList to a Prolog list.")] // ReSharper disable once InconsistentNaming public static Structure IListToPrologList(IList schemeList) { if (schemeList == null) throw new ArgumentNullException("schemeList"); Structure listTail = null; for (int i = schemeList.Count - 1; i >= 0; i--) listTail = new Structure(Symbol.PrologListConstructor, schemeList[i], listTail); return listTail; } /// /// Convert a Prolog-format list into a Scheme list (really a .NET List[object]). /// [SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists"), Documentation("Convert a Prolog-format list into a .NET IList.")] public static List PrologListToIList(object prologList) { object current = Term.Deref(prologList); var schemeList = new List(PrologListLength(prologList)); while (current != null) { var t = current as Structure; if (t == null) throw new ArgumentException("List is not a proper list; it ends with: " + current); schemeList.Add(t.Argument(0)); current = t.Argument(1); } return schemeList; } /// /// Iterates over items in a Prolog list. /// [SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists"), Documentation("Convert a Prolog-format list into a .NET IList.")] public static IEnumerable PrologListItems(object prologList) { object current = Term.Deref(prologList); while (current != null) { var t = current as Structure; if (t == null) throw new ArgumentException("List is not a proper list; it ends with: " + current); yield return t.Argument(0); current = t.Argument(1); } } /// /// Convert a Prolog-format list into an object[] array. /// [Documentation("Convert a Prolog-format list into an object[].")] public static object[] PrologListToArray(object prologList) { object current = Term.Deref(prologList); var array = new object[PrologListLength(prologList)]; int index = 0; while (current != null) { var t = current as Structure; if (t == null) throw new ArgumentException("List is not a proper list; it ends with: " + current); array[index++] = t.Argument(0); current = t.Argument(1); } return array; } /// /// Converts an object[] array into a Prolog list. /// [Documentation("Converts an object[] array into a Prolog list.")] public static Structure ArrayToPrologList(T[] array) { if (array == null) throw new ArgumentNullException("array"); Structure result = null; for (int i = array.Length - 1; i >= 0; i--) result = new Structure(Symbol.PrologListConstructor, array[i], result); return result; } /// /// Computes the length of a prolog list. /// [Documentation("Finds the length of a prolog list.")] public static int PrologListLength(object prologList) { object current = Term.Deref(prologList); int index = 0; while (current != null) { var t = current as Structure; if (t == null) { if (current is LogicVariable) throw new ArgumentException("List is incomplete (i.e. it ends in a logic variable).", "prologList"); throw new ArgumentException("Argument is not a valid Prolog list", "prologList"); } if (t.IsFunctor(Symbol.PrologListConstructor, 2)) { index++; current = t.Argument(1); } else throw new ArgumentException("Argument is not a valid Prolog list", "prologList"); } return index; } /// /// Computes the length of a prolog list. /// [Documentation("Finds the length of a prolog list.")] public static object PrologListElement(object prologList, int index) { object current = Term.Deref(prologList); while (current != null) { var t = current as Structure; if (t == null) { if (current is LogicVariable) throw new ArgumentException("List is incomplete (i.e. it ends in a logic variable).", "prologList"); throw new ArgumentException("Argument is not a valid Prolog list", "prologList"); } if (t.IsFunctor(Symbol.PrologListConstructor, 2)) { if (index-- == 0) // Done. return t.Argument(0); current = t.Argument(1); } else throw new ArgumentException("Argument is not a valid Prolog list", "prologList"); } // Hit the end of the list throw new IndexOutOfRangeException(); } #endregion /// /// Returns the full pathname of the specified file. /// /// Path within the application directory /// Full pathname internal static string LoadFilePath(string path) { if (Path.GetExtension(path) == string.Empty) path = path + DefaultSourceFileExtension; #if DisableUnity return path; #else return Path.Combine(Application.dataPath, path); #endif } /// /// Returns the full pathname of the specified directory. /// /// Path within the application directory /// Full pathname internal static string LoadDirectoryPath(string path) { #if DisableUnity return path; #else return Path.Combine(Application.dataPath, path); #endif } /// /// Removes Applicaiton.dataPath from the beginning of path. /// /// Path to trim /// Trimmed version. public static string TrimPath(string path) { var prefix = Application.dataPath+Path.DirectorySeparatorChar; if (path.StartsWith(prefix)) return path.Substring(prefix.Length); return path; } } }