もう少し BNF に対応させてほしいので、さらに ChatGPT で書いてもらうことにします。
入力1
以下のように入力しました。
以下のように変更してください:
- すべての代数的構造のクラスを表すインターフェースを追加してください
- パース情報クラスの内容は変更不可にしてください
- パース関数の戻り値はパース情報オブジェクトにしてください
入力2
以下のように入力しました。
パース情報クラス内で文字列の処理ができるようにしてください:
- 文字列をパースする関数を追加する
- その関数では文字列または正規表現を指定することができる
- 前後のスペース類をスキップするかどうか指定できる
- 指定したものに一致したとき(成功したとき)文字列の分の位置を進める
- 成功したとき結果を名前オブジェクトとして保持するかどうか指定できる
- 指定したものに一致しなかったときは失敗とする
- パース情報クラス内のメンバーは変更せず新しいパース情報オブジェクトを返す
入力3
以下のように入力しました。
この ParseInfo クラスを使って PrologParser クラスの各パース関数を書き直してください
結果:
using System; using System.Collections.Generic; namespace PrologParser { public interface IPrologStructure { } public class Name : IPrologStructure { public string Value { get; } public Name(string value) { Value = value; } public override string ToString() { return Value; } } public class Term : IPrologStructure { 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 : IPrologStructure { 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 class ParseInfo<T> where T : IPrologStructure { public string Source { get; } public int Position { get; } public bool Success { get; } public T Result { get; } public ParseInfo(string source, int position, bool success, T result) { Source = source; Position = position; Success = success; Result = result; } public static implicit operator bool(ParseInfo<T> parseInfo) => parseInfo.Success; public ParseInfo<IPrologStructure> ParseString( string pattern, bool skipSpaces = true, bool captureAsName = false) { int position = Position; if (skipSpaces) { while (position < Source.Length && char.IsWhiteSpace(Source[position])) position++; } if (position >= Source.Length) { return new ParseInfo<IPrologStructure>(Source, Position, false, null); } string remainingSource = Source.Substring(position); var match = System.Text.RegularExpressions.Regex.Match(remainingSource, $"^{System.Text.RegularExpressions.Regex.Escape(pattern)}"); if (match.Success) { string matchedValue = match.Value; position += matchedValue.Length; IPrologStructure result = null; if (captureAsName) { result = new Name(matchedValue); } return new ParseInfo<IPrologStructure>(Source, position, true, result); } return new ParseInfo<IPrologStructure>(Source, Position, false, null); } } public static class PrologParser { public static ParseInfo<Name> ParseName(ParseInfo<IPrologStructure> info) { var parseResult = info.ParseString(@"\w+", skipSpaces: true, captureAsName: true); if (parseResult.Success && parseResult.Result is Name name) { return new ParseInfo<Name>(parseResult.Source, parseResult.Position, true, name); } return new ParseInfo<Name>(info.Source, info.Position, false, null); } public static ParseInfo<Term> ParseTerm(ParseInfo<IPrologStructure> info) { var nameInfo = ParseName(info); if (!nameInfo) return new ParseInfo<Term>(info.Source, info.Position, false, null); var name = nameInfo.Result; var position = nameInfo.Position; if (position < info.Source.Length && info.Source[position] == '(') { var args = new List<Term>(); position++; while (position < info.Source.Length && info.Source[position] != ')') { var termInfo = ParseTerm(new ParseInfo<IPrologStructure>(info.Source, position, true, null)); if (!termInfo) return new ParseInfo<Term>(info.Source, info.Position, false, null); args.Add(termInfo.Result); position = termInfo.Position; if (position < info.Source.Length && info.Source[position] == ',') { position++; } } if (position >= info.Source.Length || info.Source[position] != ')') return new ParseInfo<Term>(info.Source, info.Position, false, null); position++; return new ParseInfo<Term>(info.Source, position, true, new Term("compound", name.Value, args)); } var termType = char.IsUpper(name.Value[0]) ? "variable" : "constant"; return new ParseInfo<Term>(info.Source, nameInfo.Position, true, new Term(termType, name.Value)); } public static ParseInfo<Clause> ParseClause(ParseInfo<IPrologStructure> info) { var headInfo = ParseTerm(info); if (!headInfo) return new ParseInfo<Clause>(info.Source, info.Position, false, null); var head = headInfo.Result; var position = headInfo.Position; if (position < info.Source.Length && info.Source[position] == ':') { position++; if (position >= info.Source.Length || info.Source[position] != '-') return new ParseInfo<Clause>(info.Source, info.Position, false, null); position++; var body = new List<Term>(); while (position < info.Source.Length && info.Source[position] != '.') { var termInfo = ParseTerm(new ParseInfo<IPrologStructure>(info.Source, position, true, null)); if (!termInfo) return new ParseInfo<Clause>(info.Source, info.Position, false, null); body.Add(termInfo.Result); position = termInfo.Position; if (position < info.Source.Length && info.Source[position] == ',') { position++; } } if (position >= info.Source.Length || info.Source[position] != '.') return new ParseInfo<Clause>(info.Source, info.Position, false, null); position++; return new ParseInfo<Clause>(info.Source, position, true, new Clause(head, body)); } if (position < info.Source.Length && info.Source[position] == '.') { position++; return new ParseInfo<Clause>(info.Source, position, true, new Clause(head, null)); } return new ParseInfo<Clause>(info.Source, info.Position, false, null); } public static ParseInfo<List<Clause>> ParseProgram(ParseInfo<IPrologStructure> info) { var position = info.Position; var clauses = new List<Clause>(); while (position < info.Source.Length) { var clauseInfo = ParseClause(new ParseInfo<IPrologStructure>(info.Source, position, true, null)); if (!clauseInfo) return new ParseInfo<List<Clause>>(info.Source, info.Position, false, null); clauses.Add(clauseInfo.Result); position = clauseInfo.Position; while (position < info.Source.Length && char.IsWhiteSpace(info.Source[position])) { position++; } } return new ParseInfo<List<Clause>>(info.Source, position, true, clauses); } } }

