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());
}
}
};
}