前回の結果は、BNF に構文解析の関数が対応しているのですが、もう少しちゃんと対応するようにできそうなので、さらに ChatGPT で調べてみます。
入力1
以下のように入力しました。
以下のように変更してください:
パースのとき文字列を直接渡すかわりに次の情報を保持するクラス(「パース情報」クラス)を作って渡してください:
- ソースの文字列
- ソースの文字列の(次の要素の)パースを開始する位置
- パースが成功したか失敗したか
- パースが成功したときの結果の代数的構造のオブジェクト
「パース情報」クラスでは true、false をオーバーロードして true は成功、false は失敗を表すようにしてください
「名前」に対応するクラスを作ってください
「名前」をパースする関数を作って、成功したときは「名前」オブジェクトを含む「パース情報」オブジェクトを返すようにしてください
この結果、パース関数の引数が「パース情報」オブジェクトになっていなかったので以下のように入力しました。
入力2: パース関数の引数は「パース情報」オブジェクトにしてください
この結果、ParseClause、ParseProgram がなくなっていたので以下のように入力しました。
入力3: ParseClause、ParseProgram も同様に作ってください
結果:
using System; using System.Collections.Generic; namespace PrologParser { public class ParseInfo<T> { public string Source { get; } public int Position { get; private set; } public bool Success { get; private set; } public T Result { get; private set; } public ParseInfo(string source, int position) { Source = source; Position = position; Success = false; } public void SetSuccess(T result, int newPosition) { Success = true; Result = result; Position = newPosition; } public static implicit operator bool(ParseInfo<T> parseInfo) => parseInfo.Success; } public class Name { public string Value { get; } public Name(string value) { Value = value; } public override string ToString() { return Value; } } public class Term { public string Type { get; } public string Value { get; } public List<Term> Args { get; } public Term(string type, string value, List<Term> args = null) { Type = type; Value = value; Args = args ?? new List<Term>(); } public override string ToString() { if (Type == "compound") return $"{Value}({string.Join(", ", Args)})"; return Value; } } public class Clause { public Term Head { get; } public List<Term> Body { get; } public Clause(Term head, List<Term> body) { Head = head; Body = body ?? new List<Term>(); } public override string ToString() { if (Body.Count > 0) return $"{Head} :- {string.Join(", ", Body)}."; return $"{Head}."; } } public static class PrologParser { public static void ParseName(ParseInfo<Name> parseInfo) { var source = parseInfo.Source; var startPosition = parseInfo.Position; if (startPosition >= source.Length || !char.IsLetter(source[startPosition])) { return; // Failure: Not a valid name start } int position = startPosition; while (position < source.Length && (char.IsLetterOrDigit(source[position]) || source[position] == '_')) { position++; } var nameValue = source.Substring(startPosition, position - startPosition); parseInfo.SetSuccess(new Name(nameValue), position); } public static void ParseTerm(ParseInfo<Term> parseInfo) { var source = parseInfo.Source; var startPosition = parseInfo.Position; var nameParseInfo = new ParseInfo<Name>(source, startPosition); ParseName(nameParseInfo); if (!nameParseInfo) return; // Failure: Not a valid term var name = nameParseInfo.Result; var position = nameParseInfo.Position; if (position < source.Length && source[position] == '(') { position++; var args = new List<Term>(); while (position < source.Length && source[position] != ')') { var argParseInfo = new ParseInfo<Term>(source, position); ParseTerm(argParseInfo); if (!argParseInfo) return; // Failure: Invalid argument args.Add(argParseInfo.Result); position = argParseInfo.Position; if (position < source.Length && source[position] == ',') { position++; // Skip comma } } if (position >= source.Length || source[position] != ')') return; // Failure: Missing closing parenthesis position++; // Skip closing parenthesis parseInfo.SetSuccess(new Term("compound", name.Value, args), position); return; } var termType = char.IsUpper(name.Value[0]) ? "variable" : "constant"; parseInfo.SetSuccess(new Term(termType, name.Value), position); } public static void ParseClause(ParseInfo<Clause> parseInfo) { var source = parseInfo.Source; var startPosition = parseInfo.Position; var headParseInfo = new ParseInfo<Term>(source, startPosition); ParseTerm(headParseInfo); if (!headParseInfo) return; // Failure: Invalid head var head = headParseInfo.Result; var position = headParseInfo.Position; if (position < source.Length && source[position] == ':') { position++; // Skip ':' if (position >= source.Length || source[position] != '-') return; // Failure: Expected ':-' position++; // Skip '-' var body = new List<Term>(); while (position < source.Length && source[position] != '.') { var bodyParseInfo = new ParseInfo<Term>(source, position); ParseTerm(bodyParseInfo); if (!bodyParseInfo) return; // Failure: Invalid body term body.Add(bodyParseInfo.Result); position = bodyParseInfo.Position; if (position < source.Length && source[position] == ',') { position++; // Skip comma } } if (position >= source.Length || source[position] != '.') return; // Failure: Missing terminating period position++; // Skip terminating period parseInfo.SetSuccess(new Clause(head, body), position); return; } if (position < source.Length && source[position] == '.') { position++; // Skip terminating period parseInfo.SetSuccess(new Clause(head, null), position); return; } return; // Failure: Missing terminating period or invalid syntax } public static void ParseProgram(ParseInfo<List<Clause>> parseInfo) { var source = parseInfo.Source; var position = parseInfo.Position; var clauses = new List<Clause>(); while (position < source.Length) { var clauseParseInfo = new ParseInfo<Clause>(source, position); ParseClause(clauseParseInfo); if (!clauseParseInfo) return; // Failure: Invalid clause clauses.Add(clauseParseInfo.Result); position = clauseParseInfo.Position; while (position < source.Length && char.IsWhiteSpace(source[position])) { position++; // Skip whitespace } } parseInfo.SetSuccess(clauses, position); } } class Program { static void Main(string[] args) { string source = "p(X, Y). q(a) :- r(b), s(c)."; var programParseInfo = new ParseInfo<List<Clause>>(source, 0); PrologParser.ParseProgram(programParseInfo); if (programParseInfo) { Console.WriteLine("Parsing succeeded:"); foreach (var clause in programParseInfo.Result) { Console.WriteLine(clause); } } else { Console.WriteLine("Parsing failed."); } } } }
かなり BNF に対応したものができました。まだ
- スペースの処理をパース関数の中で行っているのをどこでやるか検討
- リストの構文解析を追加
などがやりたいことですが ChatGPT でできるのかどうかはわかりません。

