using System; using System.Diagnostics; using System.IO; using System.Collections.Generic; using System.Linq; using System.Text; using System.Configuration; using miew.Enumerable; using miew.String; namespace agree { public abstract class ConfigFileReader { protected readonly Config gc; protected ConfigFileReader(Config gc) { this.gc = gc; } public abstract void ReadConfigFile(String file); }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// [Serializable] public class LkbConfig : ConfigFileReader { [NonSerialized] public static readonly Char[] one_space = { ' ' }; [NonSerialized] public static readonly Char[] sq_dq = { '\'', '\"' }; public LkbConfig(Config gc) : base(gc) { gc.types.top_type = "*top*"; gc.types.string_type = "string"; gc.types.typs_list = "*list*"; gc.types.typs_empty_list = "*null*"; gc.types.typs_diff_list = "diff-list"; gc.types.f_list_head = "first"; gc.types.f_list_tail = "rest"; gc.types.f_dlist_list = "list"; gc.types.f_dlist_last = "last"; gc.grammar.orth_path = new FsPath("stem"); gc.grammar.rule_args_path = new FsPath("args"); } [NonSerialized] String cur_file; [NonSerialized] readonly static Dictionary<String, Char> lisp_char_codes = new Dictionary<String, char> { {"space",' '}, {"fullwidth_question_mark",'\uFF1F'}, {"horizontal_ellipsis",'\u2026'}, {"fullwidth_full_stop",'\uFF0E'}, {"fullwidth_exclamation_mark",'\uFF01'}, {"black_circle",'\u25CF'}, {"fullwidth_comma",'\uFF0C'}, {"ideographic_full_stop",'\u3002'}, {"white_circle",'\u25CB'}, {"katakana_middle_dot",'\uFF65'}, {"ideographic_space",'\u3000'}, }; void SetGlobalVar(String param, String value) { if (value.Length > 0 && value[0] == '\'') value = value.Substring(1); if (value.Length > 0 && value[0] == '(' && value[value.Length - 1] == ')') value = value.Substring(1, value.Length - 2); if (param.Length == 0 || param[0] != '*' || param[param.Length - 1] != '*') { Debug.Print("Skipping unrecognized parameter: {0}", param); return; } param = param.Substring(1, param.Length - 2); switch (param) { case "start-symbol": if (gc.grammar.start_symbols == null) gc.grammar.start_symbols = new List<String>(); gc.grammar.start_symbols.Add(value); break; case "toptype": gc.types.top_type = value; break; case "string-type": gc.types.string_type = value; break; case "orth-path": gc.grammar.orth_path = new FsPath(value); break; case "list-type": gc.types.typs_list = value; break; case "list-tail": gc.types.f_list_tail = value; break; case "list-head": gc.types.f_list_head = value; break; case "empty-list-type": gc.types.typs_empty_list = value; break; case "diff-list-type": gc.types.typs_diff_list = value; break; case "diff-list-list": gc.types.f_dlist_list = value; break; case "diff-list-last": gc.types.f_dlist_last = value; break; case "deleted-daughter-features": { if (gc.parser.deleted_daughters == null) gc.parser.deleted_daughters = new List<String>(); gc.parser.deleted_daughters.AddRange(value.Split(one_space, StringSplitOptions.RemoveEmptyEntries)); } break; case "active-parsing-p": /* not relevant; ignored */ break; case "irregular-forms-only-p": gc.parser.IrregularFormsOnly = value == "t"; break; case "chart-packing-p": if (value == "t") gc.parser.packing = Config.Parser.PackingOpts.Full; else if (value == "nil") gc.parser.packing = Config.Parser.PackingOpts.None; break; case "packing-restrictor": { if (gc.parser.packing_restrictors == null) gc.parser.packing_restrictors = new List<String>(); gc.parser.packing_restrictors.AddRange(value.Split(one_space, StringSplitOptions.RemoveEmptyEntries)); } break; case "check-paths": { int ix = 0; while ((ix = value.IndexOf(" ", ix)) != -1) value = value.Remove(ix, 1); if (value.StartsWith("quote (") && value.EndsWith(")")) value = value.Substring(7, value.Length - 8); String[] all_paths = value.Split(new String[] { ") (" }, StringSplitOptions.None); if (all_paths.Length == 0) break; String s_tmp = all_paths[0]; if (s_tmp.Length > 0 && s_tmp[0] == '(') all_paths[0] = s_tmp.Substring(1); s_tmp = all_paths[all_paths.Length - 1]; if (s_tmp.Length > 0 && s_tmp.EndsWith(")")) all_paths[all_paths.Length - 1] = s_tmp.Remove(s_tmp.Length - 1); if (gc.parser.s_quick_check_paths==null) gc.parser.s_quick_check_paths = new List<string>(); gc.parser.s_quick_check_paths.AddRange(all_paths.Select(qcp => qcp.ExtractParenthesized())); } break; case "punctuation-characters": { foreach (String _s in value.Split(one_space, StringSplitOptions.RemoveEmptyEntries)) { if (_s == "append") continue; String s = _s; if (s.StartsWith("'(")) s = s.Substring(2); if (s.StartsWith(@"#\")) { if (s.Length > 3) s = s.TrimEnd(')'); s = s.Substring(2); if (s.Length == 1) gc.parser.punctuation_chars.Add(s[0]); else gc.parser.punctuation_chars.Add(lisp_char_codes[s]); } } } break; case "chart-limit": if (!int.TryParse(value, out gc.parser.chart_limit)) throw new Exception("Error: parsing 'chart-limit', expected an integer"); break; case "maximum-number-of-edges": if (!int.TryParse(value, out gc.parser.max_edges)) throw new Exception("Error: parsing 'maximum-number-of-edges', expected an integer"); break; case "key-daughter-path": gc.grammar.key_daughter_path = new FsPath(value); break; case "key-daughter-type": gc.grammar.typs_key_daughter = value; break; /// Node label configuration case "simple-tree-display": gc.nodeLabels.SimpleTreeDisplay = value == "t"; break; case "label-path": gc.nodeLabels.LabelPath = new FsPath(value); break; case "prefix-path": gc.nodeLabels.PrefixPath = new FsPath(value); break; case "suffix-path": gc.nodeLabels.SuffixPath = new FsPath(value); break; case "local-path": gc.nodeLabels.LocalPath = new FsPath(value); break; case "recursive-path": gc.nodeLabels.RecursivePath = new FsPath(value); break; case "label-fs-path": gc.nodeLabels.LabelFsPath = new FsPath(value); break; case "label-template-type": gc.nodeLabels.LabelTemplateType = value; break; default: Debug.Print("Ignoring option '*{0}* = {1}' in '{2}'", param, value, cur_file); break; } } public override void ReadConfigFile(String file) { if (!File.Exists(file)) return; using (StreamReader sr = new StreamReader(file)) { this.cur_file = file; String r_line, line = String.Empty; int line_no = 0; next_line: while ((r_line = sr.ReadLine()) != null) { line_no++; r_line = r_line.Trim(); if (r_line.Length == 0) continue; if (r_line == "#|") { while ((r_line = sr.ReadLine()) != null) { line_no++; if ((r_line = r_line.Trim()) == "|#") goto next_line; } throw new Exception("Error: end of file encountered looking for end of block comment '|#'"); } if (r_line[0] == ';') { if (line.Length > 0) throw new Exception(String.Format("Error: ';' in {0}, line {1}", file, line_no)); continue; } line += " " + r_line; int nest = 0; Char prev = default(Char); for (int i = 0; i < line.Length; i++) { if (line[i] == '(' && prev != '\\') nest++; if (line[i] == ')' && prev != '\\') { if (nest == 0) throw new Exception(String.Format("Error: ')' in {0}, line {1}", file, line_no)); nest--; } prev = line[i]; } // incomplete line if (nest != 0) continue; line = line.Trim(); if (line == String.Empty) continue; if (line[0] != '(' || line[line.Length - 1] != ')') throw new Exception(String.Format("Error: '{0}' in {1}, line {2}", line[0], file, line_no)); line = line.Substring(1, line.Length - 2); String[] rgs = line.ToLower().LispInsulatedSplit(' ').ToArray(); if (rgs[0] == "defparameter" || rgs[0] == "def-lkb-parameter") { if (rgs.Length > 4) throw new Exception(String.Format("Error: in {0}, line {1}; too many arguments for {2}", file, line_no, rgs[0])); if (rgs.Length < 3) throw new Exception(String.Format("Error: in {0}, line {1}; not enough arguments for {2}", file, line_no, rgs[0])); SetGlobalVar(rgs[1], rgs[2]); } else if (rgs[0] == "setf" || rgs[0] == "in-package") { } else throw new Exception(String.Format("Error: '{0}' in {1}, line {2}; expected 'defparameter', 'def-lkb-parameter', or 'setf'.", rgs[0], file, line_no)); line = String.Empty; } if (line.Length > 0) throw new Exception(String.Format("Error: unexpected end of file {0}; expected ')'.", file)); } } }; static class LispHelperExtension { public static IEnumerable<String> LispInsulatedSplit(this String s, Char ch_split) { int cb, i, i_last = 0; Char prev = default(Char); int nest = 0; bool f_q = false; for (i = 0; i < s.Length; i++) { Char ch = s[i]; if (ch == '"' && prev != '\\') f_q = !f_q; else if (ch == '(' && prev != '\\') nest++; else if (ch == ')' && prev != '\\' && nest > 0) nest--; else if (nest == 0 && !f_q && ch == ch_split) { if ((cb = i - i_last) > 0) yield return s.Substring(i_last, cb); i_last = i + 1; } prev = ch; } if ((cb = i - i_last) > 0) yield return s.Substring(i_last, cb); } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// [Serializable] public class PetGlobals : ConfigFileReader { [NonSerialized] public static readonly Char[] one_space = { ' ' }; [NonSerialized] public static readonly Char[] sq_dq = { '\'', '\"' }; public PetGlobals(Config gc) : base(gc) { gc.types.top_type = "top"; gc.types.string_type = "string"; gc.types.typs_list = "list"; gc.types.typs_empty_list = "nil"; gc.types.typs_diff_list = "difflist"; gc.types.f_list_head = "first"; gc.types.f_list_tail = "rest"; gc.types.f_dlist_list = "list"; gc.types.f_dlist_last = "last"; gc.grammar.orth_path = new FsPath("orth"); gc.grammar.rule_args_path = new FsPath("args"); String v = ConfigurationManager.AppSettings["opt_key"]; Config.Parser.KeyStrategy ks; if (v != null && Enum.TryParse<Config.Parser.KeyStrategy>(v, out ks)) gc.parser.ParsingStrategy = ks; } void SetGlobalVar(String file, int line_no, String[] rgs) { String kw = rgs[0].ToLower(); String arg = null; String[] args = null; if (rgs.Length > 1) { if (rgs[1] != ":=") throw new Exception(String.Format("Error: expected ':=' in {0}, line {1}", file, line_no)); if (rgs.Length == 2) throw new Exception(String.Format("Error: nothing specified on the right of ':=' in {0}, line {1}", file, line_no)); args = rgs.Skip(2).ToArray(); arg = args[0]; } else arg = rgs[0]; arg = String.Intern(arg.Trim(sq_dq).ToLower()); switch (kw) { case "encoding": break; case "irregular-forms-only": if (args != null) throw new Exception(String.Format("Error: 'irregular-forms-only' is a boolean option; in {0}, line {1}", file, line_no)); gc.parser.IrregularFormsOnly = true; break; case "case-sensitive": if (args != null) throw new Exception(String.Format("Error: 'case-sensitive' is a boolean option; in {0}, line {1}", file, line_no)); gc.parser.f_case_sensitive = true; break; case "lex-entries-can-fail": if (args != null) throw new Exception(String.Format("Error: 'lex-entries-can-fail' is a boolean option; in {0}, line {1}", file, line_no)); gc.parser.f_lex_entries_can_fail = true; break; case "unidirectional-chart-dependencies": if (args != null) throw new Exception(String.Format("Error: 'unidirectional-chart-dependencies' is a boolean option; in {0}, line {1}", file, line_no)); gc.parser.ChartDependencyScope = Config.Parser.DependencyScope.Unidirectional; break; case "chart-dependencies": gc.parser.ChartDependencyPaths = rgs.Skip(2).PairOff(); break; case "special-name-top": if (args.Length > 1) throw new Exception(String.Format("Error: cannot define more than one instance of the special 'top' type; in {0}, line {1}", file, line_no)); gc.types.top_type = arg; break; case "special-name-cons": /* don't know why this is needed */ break; case "special-name-symbol": /* don't know what this is for */ break; case "special-name-string": if (args.Length > 1) throw new Exception(String.Format("Error: cannot define more than one instance of the special 'string' type; in {0}, line {1}", file, line_no)); gc.types.string_type = arg; break; case "special-name-list": if (args.Length > 1) throw new Exception(String.Format("Error: cannot define more than one instance of the special 'list' type; in {0}, line {1}", file, line_no)); gc.types.typs_list = arg; break; case "special-name-nil": if (args.Length > 1) throw new Exception(String.Format("Error: cannot define more than one instance of the special 'empty list' type; in {0}, line {1}", file, line_no)); gc.types.typs_empty_list = arg; break; case "special-name-difflist": if (args.Length > 1) throw new Exception(String.Format("Error: cannot define more than one instance of the special 'difference list' type; in {0}, line {1}", file, line_no)); gc.types.typs_diff_list = arg; break; case "special-name-attr-first": if (args.Length > 1) throw new Exception(String.Format("Error: cannot define more than symbol for the special 'list head' feature; in {0}, line {1}", file, line_no)); gc.types.f_list_head = arg; break; case "special-name-attr-rest": if (args.Length > 1) throw new Exception(String.Format("Error: cannot define more than symbol for the special 'list tail' feature; in {0}, line {1}", file, line_no)); gc.types.f_list_tail = arg; break; case "special-name-attr-list": if (args.Length > 1) throw new Exception(String.Format("Error: cannot define more than symbol for the special 'difference list head' feature; in {0}, line {1}", file, line_no)); gc.types.f_dlist_list = arg; break; case "special-name-attr-last": if (args.Length > 1) throw new Exception(String.Format("Error: cannot define more than symbol for the special 'difference list tail' feature; in {0}, line {1}", file, line_no)); gc.types.f_dlist_last = arg; break; case "special-name-attr-args": /* don't know if/why this is different from rule-args-path */ break; case "rule-args-path": if (args.Length > 1) throw new Exception(String.Format("Error: cannot define more than symbol for the rule arguments path; in {0}, line {1}", file, line_no)); gc.grammar.rule_args_path = new FsPath(arg); break; case "keyarg-marker-path": if (args.Length > 1) throw new Exception(String.Format("Error: cannot define more than symbol for the key daughter path; in {0}, line {1}", file, line_no)); gc.grammar.key_daughter_path = new FsPath(arg); break; case "true-type": if (args.Length > 1) throw new Exception(String.Format("Error: cannot define more than symbol for the key daughter asserted type; in {0}, line {1}", file, line_no)); gc.grammar.typs_key_daughter = arg; break; case "orth-path": if (args.Length > 1) throw new Exception(String.Format("Error: cannot define more than one orthography path; in {0}, line {1}", file, line_no)); gc.grammar.orth_path = new FsPath(arg); break; case "head-dtr-path": if (args.Length > 1) throw new Exception(String.Format("Error: cannot define more than one head daughter path; in {0}, line {1}", file, line_no)); gc.grammar.head_dtr_path = arg; break; case "deleted-daughters": if (gc.parser.deleted_daughters == null) gc.parser.deleted_daughters = new List<String>(); gc.parser.deleted_daughters.AddRange(args.Select(s => s.ToLower())); break; case "packing-restrictor": if (gc.parser.packing_restrictors == null) gc.parser.packing_restrictors = new List<String>(); gc.parser.packing_restrictors.AddRange(args.Select(s => s.ToLower())); break; case "start-symbols": if (gc.grammar.start_symbols == null) gc.grammar.start_symbols = new List<String>(); gc.grammar.start_symbols.AddRange(args.Select(s => s.ToLower().TrimStart('$'))); break; case "spanning-only-rules": if (gc.parser.span_only_rules == null) gc.parser.span_only_rules = new List<String>(); gc.parser.span_only_rules.AddRange(args.Select(s => s.ToLower().TrimStart('$'))); break; case "punctuation-characters": gc.parser.punctuation_chars.UnionWith(arg); break; case "postload-lisp-files": case "preload-lisp-files": /* we will do our own MRS extraction */ break; case "pn-label-path": gc.nodeLabels.LabelPath = new FsPath(arg); break; case "pn-prefix-path": gc.nodeLabels.PrefixPath = new FsPath(arg); break; case "pn-suffix-path": gc.nodeLabels.SuffixPath = new FsPath(arg); break; case "pn-recursive-path": gc.nodeLabels.RecursivePath = new FsPath(arg); break; case "pn-local-path": gc.nodeLabels.LocalPath = new FsPath(arg); break; case "pn-label-fs-path": gc.nodeLabels.LabelFsPath = new FsPath(arg); break; case "pn-label-type": gc.nodeLabels.LabelTemplateType = arg; break; case "pn-meta-type": gc.nodeLabels.MetaTemplateType = arg; break; default: //Console.WriteLine("{0} {1}", kw, args.StringJoin("\t")); break; } } public override void ReadConfigFile(String file) { StringBuilder sb = new StringBuilder(); int line_no = 0; using (StreamReader sr = new StreamReader(file)) { String r_line; while ((r_line = sr.ReadLine()) != null) { line_no++; int ix = r_line.QuoteInsulatedIndexOf(';'); if (ix != -1) r_line = r_line.Remove(ix); r_line = r_line.Trim(); if (r_line != String.Empty) sb.Append(" ;" + line_no.ToString() + " " + r_line.Select(ch => ch < ' ' ? ' ' : ch).NewString()); } } foreach (String stmt in sb.ToString().QuoteInsulatedSplit('.')) { String cur_file_dir = Path.GetDirectoryName(file); String[] parts = stmt.Split(one_space, StringSplitOptions.RemoveEmptyEntries); while (parts.Length > 0 && parts[0][0] == ';') { line_no = int.Parse(parts[0].Substring(1)); parts = parts.Skip(1).ToArray(); } String kw = parts[0].ToLower(); if (kw == "include") { if (parts.Length != 2) throw new Exception(String.Format("Error: '{0}.' in {1}, line {2}", parts.StringJoin(" "), file, line_no)); String inc_file = parts[1].Trim(sq_dq); if (!Path.HasExtension(inc_file)) inc_file = Path.ChangeExtension(inc_file, ".set"); String try_file = Path.Combine(cur_file_dir, inc_file); if (File.Exists(try_file)) { ReadConfigFile(try_file); continue; } if (cur_file_dir != Environment.CurrentDirectory) { try_file = Path.Combine(Environment.CurrentDirectory, inc_file); if (File.Exists(try_file)) { ReadConfigFile(try_file); continue; } } throw new Exception(String.Format("Error: include file '{0}' not found in {1}, line {2}", inc_file, line_no)); } SetGlobalVar(file, line_no, parts.Where(s => s[0] != ';').ToArray()); } } }; }