budde377/Part

View on GitHub
dart/part/lib/function_string_parser.dart

Summary

Maintainability
Test Coverage
library function_string_parser;

@MirrorsUsed(targets:const['user_settings', 'site_classes', 'core', 'elements', 'List', 'Map', 'String', 'num', 'int', 'double', 'bool'])
import "dart:mirrors";
import "dart:math" as Math;
import "core.dart";


Function _firstNotNull(Iterable<Function> l) => (String s) => l.fold(null, (prev, Function f) => prev != null ? prev : f(s));

_matchFirstNotNull(Pattern p, String s, f(Match, String)) => _firstNotNull(p.allMatches(s).map((Match m) => (String s) => f(m, s)))(s);

_FSProgram _parseFS(String s) => _parseFSProgram(s.trim());

_FSProgram _parseFSProgram(String s) =>
_firstNotNull([_parseFSCompositeFunctionCall, _parseFSFunctionCall])(s);


_FSCompositeFunctionCall _parseFSCompositeFunctionCall(String s) =>
_matchFirstNotNull(new RegExp(r"\.[\[\.]"), s, (Match m, String s) {
  var t = _parseFSTarget(s.substring(0, m.start).trim());
  if (t == null) {
    return null;
  }
  var f = _parseFSCompositeFunction(s.substring(m.start).trim());
  if (f == null) {
    return null;
  }
  return new _FSCompositeFunctionCall(t, f);
});

_FSTarget _parseFSTarget(String string) =>
_firstNotNull([_parseFSType, _parseFSFunctionCall])(string);

/*
 * <type>                       = <name> | [a-zA-Z_][A-Za-z0-9_\]+[a-zA-Z_]
 */
_FSType _parseFSType(String string) {

  var n = _parseFSName(string);
  if (n != null) {
    return n;
  }

  return _matchFirstNotNull(r"\", string, (Match m, String s) {
    var t = _parseFSType(s.substring(0, m.start).trim());
    if (t == null) {
      return null;
    }

    var n = _parseFSName(s.substring(m.start + 1).trim());

    if (n == null) {
      return null;
    }

    return new _FSTypeBackslashName(t, n);
  });

}

_FSName _parseFSName(String string) => new RegExp(r"^[a-zA-Z_][A-Za-z0-9_]*$", caseSensitive:false).hasMatch(string) ? new _FSName(string) : null;

_FSNotStartingWithUnderscoreName _parseFSNotStartingWithUnderscoreName(String string) {
  if (string.isEmpty) {
    return null;
  }
  if (!new RegExp(r"[a-zA-Z0-9]").hasMatch(string.substring(0, 1))) {
    return null;
  }
  if (string.length == 1) {
    return new _FSNotStartingWithUnderscoreName(string);
  }

  if (_parseFSName(string.substring(1)) == null) {
    return null;
  }

  return new _FSNotStartingWithUnderscoreName(string);

}
/*
  * <composite_function>         = .<function_chain> | <composite_function>.<function_chain>
*/
_FSCompositeFunction _parseFSCompositeFunction(String string) =>
_matchFirstNotNull(new RegExp(r"\.[\[\.]"), string, (Match m, String s) {
  var c = _parseFSFunctionChain(s.substring(m.start + 1).trim());
  if (c == null) {
    return null;
  }
  if (m.start == 0) {
    return new _FSFunctionChainCompositeFunction(c);
  }

  var cf = _parseFSCompositeFunction(s.substring(0, m.start).trim());

  if (cf == null) {
    return null;
  }
  return new _FSFunctionCompositeFunction(cf, c);

});


/*
 * <function_chain>             = <function_chain><function> | <function>
 */

_FSFunctionChain _parseFSFunctionChain(String s) {
  var f = _parseFSFunction(s);
  if (f != null) {
    return f;
  }
  return _matchFirstNotNull(".", s, (Match m, s) {
    var f = _parseFSFunction(s.substring(m.start).trim());
    if (f == null) {
      return null;
    }

    var fc = _parseFSFunctionChain(s.substring(0, m.start).trim());

    if (fc == null) {
      return null;
    }
    return new _FSChainFunctionChain(fc, f);

  });


}


/*
 * <function_call>              = <target><function>
 */

_FSFunctionCall _parseFSFunctionCall(String s) =>
_matchFirstNotNull(new RegExp(r"[\.\[]"), s, (Match m, String s) {
  var t = _parseFSTarget(s.substring(0, m.start).trim());
  if (t == null) {
    return null;
  }
  var f = _parseFSFunction(s.substring(m.start).trim());
  if (f == null) {
    return null;
  }

  return new _FSFunctionCall(t, f);

});

/*
 * <scalar>                     = true | false | null | <num> | <string>
 */
_FSScalar _parseFSScalar(String s) => _firstNotNull([_parseFSBool, _parseFSNull, _parseFSNum, _parseFSString])(s);


_FSBoolScalar _parseFSBool(String s) => s.toLowerCase() == "true" ? new _FSBoolScalar(true) : s.toLowerCase() == "false" ? new _FSBoolScalar(false) : null;


_FSNullScalar _parseFSNull(String s) => s.toLowerCase() == "null" ? new _FSNullScalar() : null;

/*
 * <num>                        = [+-]? <integer> | <float>
 */
_FSNumScalar _parseFSNum(String s) {
  var sign = 1;
  if (s.startsWith("+")) {
    s = s.substring(1).trim();
  } else if (s.startsWith("-")) {
    s = s.substring(1).trim();
    sign = -1;
  }

  _FSNumScalar n = _firstNotNull([_parseFSInteger, _parseFSFloat])(s);
  if (n == null) {
    return null;
  }
  return n.mul(sign);
}
/*
 * <integer>                    = <octal> | <decimal> | <hexadecimal> | <binary>
 */
_FSNumScalar _parseFSInteger(String s) => _firstNotNull([_parseFSOctal, _parseFSDecimal, _parseFSHexadecimal, _parseFSBinary])(s);

/*
 * <float>                      = <double_number> | <exp_double_number>
 */
_FSNumScalar _parseFSFloat(String s) => _firstNotNull([_parseFSDouble, _parseFSExpDouble])(s);

/*
 * <double_number>              = [0-9]*[\.][0-9]*
 */
_FSDoubleNumScalar _parseFSDouble(String s) {
  var m = new RegExp(r"^[0-9]*[\.][0-9]*$").firstMatch(s);
  if (m == null) {
    return null;
  }

  return new _FSDoubleNumScalar(double.parse(m[0]));
}


/*
 * <exp_double_number>          = ([0-9]+|[0-9]*[\.][0-9]*)[eE][+-]?[0-9]+
 */
_FSDoubleNumScalar _parseFSExpDouble(String s) {
  var m = new RegExp(r"^([0-9]+|[0-9]*[\.][0-9]*)[eE][+-]?[0-9]+$").firstMatch(s);
  if (m == null) {
    return null;
  }

  return new _FSDoubleNumScalar(double.parse(m[0]));
}

_FSNumScalar _parseBase(String s, RegExp p, int base) {
  var m = p.firstMatch(s);
  if (m == null) {
    return null;
  }

  return new _FSIntNumScalar(int.parse(m[1], radix:base));
}

/*
 * <octal>                      = 0[0-7]+
 */
_FSNumScalar _parseFSOctal(String s) => _parseBase(s, new RegExp(r"^0([0-7]+)$"), 8);
/*
 * <decimal>                    = [0-9]+
 */
_FSNumScalar _parseFSDecimal(String s) => _parseBase(s, new RegExp(r"^([0-9]+)$"), 10);
/*
 * <hexadecimal>                = 0x[0-9A-Fa-f]
 */
_FSNumScalar _parseFSHexadecimal(String s) => _parseBase(s, new RegExp(r"^0x([0-9a-f]+)$", caseSensitive:false), 16);
/*
 * <binary>                     = 0b[0-1]+
 */
_FSNumScalar _parseFSBinary(String s) => _parseBase(s, new RegExp(r"^0b([0-1]+)$"), 2);

_FSStringScalar _parseFSString(String s) {
  if (!s.startsWith("'") && !s.startsWith('"')) {
    return null;
  }
  var divider = s.substring(0, 1);
  if (!s.endsWith(divider)) {
    return null;
  }


  var i = s.replaceAll(r"\\", "x").replaceAll(r"\" + divider, "x");

  if (!i.startsWith(divider) || !i.endsWith(divider)) {
    return null;
  }

  if (i.substring(1, i.length - 1).contains(divider)) {
    return null;
  }
  if (divider == '"') {
    var map = {
        "n": "\n",
        "r": "\r",
        "t": "\t",
        "v": "\v",
        "e": "\e",
        "f": "\f"
    };
    map.forEach((String key, String value) {
      s = s.replaceAllMapped(new RegExp(r"([^\\])\\" + key), (Match m) => "${m[1]}$value");
    });
    s = s.replaceAllMapped(new RegExp(r"([^\\])\\([0-7]{1,3})"), (Match m) => "${m[1]}" + new String.fromCharCode(int.parse(m[2], radix:8)));
    s = s.replaceAllMapped(new RegExp(r"([^\\])\\x([0-9A-Fa-f]{1,2})"), (Match m) => "${m[1]}" + new String.fromCharCode(int.parse(m[2], radix:16)));
  }
  s = s.replaceAllMapped(new RegExp(r"([^\\])\\\\" + divider), (Match m) => "${m[1]}$divider");
  s = s.replaceAll(r"\\", r"\");
  return new _FSStringScalar(s.substring(1, s.length - 1));
}

/*
 * <function>                   = .<name>(<arg_list>) | .<name> () | \[<scalar>\]
 */
_FSFunction _parseFSFunction(String s) {

  var scalar;
  if (s.startsWith("[") && s.endsWith("]") && (scalar = _parseFSScalarArrayProgram(s.substring(1, s.length - 1).trim())) != null) {
    return new _FSArrayAccessFunction(scalar);
  }


  if (!s.startsWith(".")) {
    return null;
  }

  s = s.substring(1).trim();

  if (!s.endsWith(")")) {
    return null;
  }
  var first = s.indexOf("(");
  if (first < 0) {
    return null;
  }

  var n = _parseFSName(s.substring(0, first).trim());
  if (n == null) {
    return null;
  }

  var a = s.substring(first + 1, s.length - 1).trim();
  if (a.isEmpty) {
    return new _FSNoArgumentFunction(n);
  }

  var args = _parseFSArguments(s.substring(first + 1, s.length - 1).trim());

  if (args == null) {
    return null;
  }

  return new _FSArgumentFunction(n, args);


}

/*
 * <arg_list>                   = <sap> | <sap>, <arg_list> | <named_arg_list>
 */
_FSArgument _parseFSArguments(String s) {
  var sap = _parseFSScalarArrayProgram(s);
  if (sap != null) {
    return new _FSArgument(sap);
  }

  var nal = _parseFSNamedArguments(s);
  if (nal != null) {
    return nal;
  }

  return _matchFirstNotNull(",", s, (Match m, String s) {
    var sap = _parseFSScalarArrayProgram(s.substring(0, m.start).trim());
    if (sap == null) {
      return null;
    }

    var args = _parseFSArguments(s.substring(m.start + 1).trim());

    if (args == null) {
      return null;
    }
    return new _FSArguments(sap, args);

  });

}

/*
 * <named_arg_list>             = <named_arg> | <named_arg>, <named_arg_list>
 */
_FSNamedArgument _parseFSNamedArguments(String s) {
  var arg = _parseFSNamedArgument(s);
  if (arg != null) {
    return arg;
  }

  return _matchFirstNotNull(",", s, (Match m, String s) {
    var arg = _parseFSNamedArgument(s.substring(0, m.start).trim());
    if (arg == null) {
      return null;
    }

    var args = _parseFSNamedArguments(s.substring(m.start + 1).trim());

    if (args == null) {
      return null;
    }
    return new _FSNamedArguments(arg.name, arg.value, args);
  });

}

/*
 * <named_arg>                  = <name_nswu> : <sap>
 */
_FSNamedArgument _parseFSNamedArgument(String s) {
  var first_pos = s.indexOf(":");
  if (first_pos < 0) {
    return null;
  }

  var name = _parseFSNotStartingWithUnderscoreName(s.substring(0, first_pos).trim());
  if (name == null) {
    return null;
  }

  var sap = _parseFSScalarArrayProgram(s.substring(first_pos + 1).trim());
  if (sap == null) {
    return null;
  }

  return new _FSNamedArgument(name, sap);

}

/*
 * <sap>                        = <scalar> | <array> | <program>
 */
_FSScalarArrayProgram _parseFSScalarArrayProgram(String s) => _firstNotNull([_parseFSScalar, _parseFSArray, _parseFSProgram])(s);

/*
 * <array>                      = \[ <array_entries>\]
 */

_FSArray _parseFSArray(String s) => _firstNotNull([_parseFSEmptyArray, _parseFSNonEmptyArray])(s);

_FSEmptyArray _parseFSEmptyArray(String s) {
  if (!s.startsWith("[") || !s.endsWith("]")) {
    return null;
  }
  if (s.substring(1, s.length - 1).trim() != "") {
    return null;
  }

  return new _FSEmptyArray();
}

_FSNonEmptyArray _parseFSNonEmptyArray(String s) {
  if (!s.startsWith("[") || !s.endsWith("]")) {
    return null;
  }

  var entries = _parseFSAllArrayEntries(s.substring(1, s.length - 1).trim());

  if (entries == null) {
    return null;
  }

  return new _FSNonEmptyArray(entries);

}
/*
 * <all_array_entries>          = <array_entries> | <named_array_entries>
 */
_FSArrayEntry _parseFSAllArrayEntries(String s) => _firstNotNull([_parseFSArrayEntries, _parseFSNamedArrayEntries])(s);

/*
 * <array_entries>              = <sap> | <sap>, <all_array_entries>
 */
_FSArrayEntry _parseFSArrayEntries(String s) {
  var sap = _parseFSScalarArrayProgram(s);
  if (sap != null) {
    return new _FSArrayEntry(sap);
  }

  return _matchFirstNotNull(",", s, (Match m, String s) {
    var sap = _parseFSScalarArrayProgram(s.substring(0, m.start).trim());
    if (sap == null) {
      return null;
    }

    var entries = _parseFSAllArrayEntries(s.substring(m.start + 1).trim());
    if (entries == null) {
      return null;
    }
    return new _FSArrayEntries(sap, entries);
  });

}
/*
 * <named_array_entries>        = <array_named_entry> | <array_named_entry>, <all_array_entries>
 */
_FSNamedArrayEntry _parseFSNamedArrayEntries(String s) {
  var ne = _parseFSNamedArrayEntry(s);
  if (ne != null) {
    return ne;
  }

  return _matchFirstNotNull(",", s, (Match m, String s) {
    var ne = _parseFSNamedArrayEntry(s.substring(0, m.start).trim());
    if (ne == null) {
      return null;
    }

    var entries = _parseFSAllArrayEntries(s.substring(m.start + 1).trim());
    if (entries == null) {
      return null;
    }
    return new _FSNamedArrayEntries(ne.key, ne.value, entries);
  });
}
/*
 * <array_named_entry>          = <scalar> => <sap>
 */
_FSNamedArrayEntry _parseFSNamedArrayEntry(String s) {
  var first_index = s.indexOf("=>");
  if (first_index < 0) {
    return null;
  }

  var scalar = _parseFSScalar(s.substring(0, first_index).trim());

  if (scalar == null) {
    return null;
  }

  var sap = _parseFSScalarArrayProgram(s.substring(first_index + 2).trim());
  if (sap == null) {
    return null;
  }
  return new _FSNamedArrayEntry(scalar, sap);

}

abstract class _FSTarget {


}


abstract class _FSType extends _FSTarget {

  String get value;

}

/*
 * <type>                       = <name> | <type>\<name>
 */
class _FSTypeBackslashName extends _FSType {

  final _FSType type;
  final _FSName name;

  String get value => type.value + r"\" + name.value;

  _FSTypeBackslashName(this.type, this.name);


  String toString() => type.toString() + "\\" + name.toString();
}

abstract class _FSProgram extends _FSScalarArrayProgram {
  final _FSTarget target;

  _FSProgram(this.target);

  List<_FSFunctionCall> toFunctionCalls();

  dynamic compute(dynamic computer(_FSProgram)) => computer(this);

}

class _FSCompositeFunctionCall extends _FSProgram implements _FSTarget {
  final _FSCompositeFunction function;

  List<_FSFunctionCall> toFunctionCalls() => function.toFunctionCalls(this.target);

  _FSCompositeFunctionCall(_FSTarget target, this.function) : super(target);

  String toString() => target.toString() + function.toString();

}

class _FSFunctionCall extends _FSProgram implements _FSTarget {

  final _FSFunction function;

  _FSFunctionCall(_FSTarget target, this.function) : super(target);

  String toString() => target.toString() + function.toString();

  List<_FSFunctionCall> toFunctionCalls() => [this];

}

/*
 <composite_function>         = .<function_chain> | <composite_function>.<function_chain>
 */

abstract class _FSCompositeFunction {

  List<_FSFunctionCall> toFunctionCalls(_FSTarget target);

}

class _FSFunctionCompositeFunction extends _FSCompositeFunction {
  final _FSFunctionChain function;
  final _FSCompositeFunction composite;

  _FSFunctionCompositeFunction(this.composite, this.function);

  String toString() => composite.toString() + "." + function.toString();

  List<_FSFunctionCall> toFunctionCalls(_FSTarget target) {
    var l = composite.toFunctionCalls(target);
    l.add(function.toFunctionCall(target));
    return l;
  }

}


class _FSFunctionChainCompositeFunction extends _FSCompositeFunction {
  final _FSFunctionChain function;

  _FSFunctionChainCompositeFunction(this.function);

  String toString() => "." + function.toString();

  List<_FSFunctionCall> toFunctionCalls(_FSTarget target) => [function.toFunctionCall(target)];

}

abstract class _FSFunctionChain {

  _FSFunctionCall toFunctionCall(_FSTarget target);

}

class _FSChainFunctionChain extends _FSFunctionChain {
  final _FSChainFunctionChain chain;
  final _FSFunction function;

  _FSChainFunctionChain(this.chain, this.function);

  String toString() => chain.toString() + function.toString();

  _FSFunctionCall toFunctionCall(_FSTarget target) => new _FSFunctionCall(chain.toFunctionCall(target), function);

}

abstract class _FSFunction extends _FSFunctionChain {

  _FSFunctionCall toFunctionCall(_FSTarget target) => new _FSFunctionCall(target, this);

}

abstract class _FSNamedFunction extends _FSFunction {

  final _FSName name;

  _FSNamedFunction(this.name);

  String toString() => "." + name.toString();

  List<_FSArgument> get argumentList;

  List<_FSArgument> get positionalArgumentList;

  Map<String, _FSArgument> get namedArgumentMap;

  List<dynamic> computedPositionalArgumentList(dynamic converter(_FSProgram));

  Map<String, dynamic> computedNamedArgumentMap(dynamic converter(_FSProgram));

}

class _FSNoArgumentFunction extends _FSNamedFunction {

  _FSNoArgumentFunction(_FSName name) : super(name);

  String toString() => super.toString() + "()";

  List<_FSArgument> get argumentList => [];

  List<_FSArgument> get positionalArgumentList => [];

  Map<String, _FSArgument> get namedArgumentMap => {
  };

  List<dynamic> computedPositionalArgumentList(dynamic converter(_FSProgram)) => positionalArgumentList;

  Map<String, dynamic> computedNamedArgumentMap(dynamic converter(_FSProgram)) => namedArgumentMap;
}

class _FSArgumentFunction extends _FSNamedFunction {

  final _FSArgument argument;

  _FSArgumentFunction(_FSName name, this.argument) : super(name);

  String toString() => super.toString() + "(" + argument.toString() + ")";

  List<_FSArgument> get argumentList => argument.toArgumentList();

  List<_FSArgument> get positionalArgumentList {
    var l = argumentList;
    l.removeWhere((_FSArgument a) => a is _FSNamedArgument);
    l.map((_FSArgument a) => a.value);
    return l;
  }

  Map<String, _FSArgument> get namedArgumentMap {
    var l = argumentList;
    l.removeWhere((_FSArgument a) => a is! _FSNamedArgument);
    var m = new Map.fromIterable(l, key:(_FSNamedArgument a) => a.name.value, value:(_FSNamedArgument a) => a.value);

    return m;

  }


  List<dynamic> computedPositionalArgumentList(dynamic converter(_FSProgram)) => positionalArgumentList.map((_FSArgument a) => a.value.compute(converter)).toList();

  Map<String, dynamic> computedNamedArgumentMap(dynamic converter(_FSProgram)) {
    var m = namedArgumentMap;
    return new Map.fromIterables(m.keys, m.values.map((_FSArgument a) => a.value.compute(converter)));
  }
}


class _FSArrayAccessFunction extends _FSFunction {

  final _FSScalarArrayProgram scalar;


  _FSArrayAccessFunction(this.scalar);

  String toString() => "[" + scalar.toString() + "]";

}

class _FSArgument {

  final _FSScalarArrayProgram value;

  _FSArgument(this.value);

  String toString() => value.toString();

  List<_FSArgument> toArgumentList() => [this];

}

class _FSArguments extends _FSArgument {

  final _FSArgument argument;

  _FSArguments(_FSScalarArrayProgram value, this.argument) : super(value);

  String toString() => super.toString() + ", " + argument.toString();

  List<_FSArgument> toArgumentList() {
    var l = argument.toArgumentList();
    l.insert(0, new _FSArgument(this.value));
    return l;
  }

}

class _FSNamedArgument extends _FSArgument {
  final _FSNotStartingWithUnderscoreName name;

  _FSNamedArgument(this.name, _FSScalarArrayProgram value) : super(value);

  String toString() => name.toString() + " : " + value.toString();

  List<_FSArgument> toArgumentList() => [this];

}

class _FSNamedArguments extends _FSNamedArgument implements _FSArguments {
  final _FSNamedArgument argument;

  _FSNamedArguments(_FSNotStartingWithUnderscoreName name, _FSScalarArrayProgram value, this.argument) : super(name, value);

  String toString() => super.toString() + ", " + argument.toString();

  List<_FSArgument> toArgumentList() {
    var l = argument.toArgumentList();
    l.insert(0, new _FSNamedArgument(this.name, this.value));
    return l;
  }
}


class _FSName extends _FSType {

  final String value;

  _FSName(this.value);

  String toString() => "n{$value}";
}

class _FSNotStartingWithUnderscoreName {
  final String value;

  _FSNotStartingWithUnderscoreName(this.value) ;

  String toString() => "nwou{$value}";
}


abstract class _FSScalarArrayProgram {

  dynamic compute(dynamic computer(_FSProgram));

}


abstract class _FSArray extends _FSScalarArrayProgram {

  List<_FSArrayEntry> get entries;

  bool get isList;

  bool get isMap;

}

class _FSNonEmptyArray extends _FSArray {
  final _FSArrayEntry entry;

  List<_FSArrayEntry> get entries => entry.toEntryList();

  bool get isList => entries.every((_FSArrayEntry e) => e is! _FSNamedArrayEntry);

  bool get isMap => !isList;

  Map computeMap(dynamic computer(_FSProgram)) {

    var resultMap = {
    };

    var i = 0;

    entries.forEach((_FSArrayEntry entry) {
      if (entry is _FSNamedArrayEntry) {
        _FSNamedArrayEntry e = entry;
        if (e.key is _FSIntNumScalar) {
          i = Math.max(i, e.key.value);
        }
        resultMap[e.key.value] = e.value.compute(computer);
      } else {
        _FSArrayEntry e = entry;
        resultMap[i] = e.value.compute(computer);
        i++;
      }

    });

    return resultMap;
  }

  List computeList(dynamic computer(_FSProgram)) => computeMap(computer).values.toList();

  dynamic compute(dynamic computer(_FSProgram)) => isList ? computeList(computer) : computeMap(computer);

  _FSNonEmptyArray(this.entry);

  String toString() => "[" + entry.toString() + "]";

}

class _FSEmptyArray extends _FSArray {

  dynamic compute(dynamic computer(_FSProgram)) => [];

  String toString() => "[]";

  List<_FSArrayEntry> get entries => [];

  bool get isList => true;

  bool get isMap => false;


}

class _FSArrayEntry {

  final _FSScalarArrayProgram value;

  _FSArrayEntry(this.value);

  String toString() => value.toString();

  List<_FSArrayEntry> toEntryList() => [this];

}

class _FSArrayEntries extends _FSArrayEntry {

  _FSArrayEntry entry;

  _FSArrayEntries(_FSScalarArrayProgram value, this.entry): super(value);

  String toString() => super.toString() + ", " + entry.toString();

  List<_FSArrayEntry> toEntryList() {
    var l = entry.toEntryList();
    l.insert(0, new _FSArrayEntry(value));
    return l;
  }

}


class _FSNamedArrayEntry extends _FSArrayEntry {
  final _FSScalar key;

  _FSNamedArrayEntry(this.key, _FSScalarArrayProgram value) : super(value);

  String toString() => key.toString() + " : " + super.toString();

}

class _FSNamedArrayEntries extends _FSNamedArrayEntry implements _FSArrayEntries {
  _FSArrayEntry entry;

  _FSNamedArrayEntries(_FSScalar key, _FSScalarArrayProgram value, this.entry) : super(key, value);

  String toString() => super.toString() + ", " + entry.toString();

  List<_FSArrayEntry> toEntryList() {
    var l = entry.toEntryList();
    l.insert(0, new _FSNamedArrayEntry(key, value));
    return l;
  }
}

abstract class _FSScalar extends _FSScalarArrayProgram {

  get value;

}


class _FSBoolScalar extends _FSScalar {
  final bool value;

  _FSBoolScalar(this.value);

  String toString() => "b{$value}";

  bool compute(dynamic computer(_FSProgram)) => value;

}

class _FSNullScalar extends _FSScalar {

  final value = null;

  compute(dynamic computer(_FSProgram)) => null;
}

abstract class _FSNumScalar extends _FSScalar {
  final num value;

  _FSNumScalar(this.value);

  _FSNumScalar mul(int);

}


class _FSIntNumScalar implements _FSNumScalar {

  final int value;

  _FSIntNumScalar(this.value);

  String toString() => "int{$value}";

  _FSIntNumScalar mul(int) => new _FSIntNumScalar(int * value);

  int compute(dynamic computer(_FSProgram)) => value;
}


class _FSDoubleNumScalar implements _FSNumScalar {

  final double value;

  _FSDoubleNumScalar(this.value);

  String toString() => "float{$value}";

  _FSDoubleNumScalar mul(int) => new _FSDoubleNumScalar(int * value);

  double compute(dynamic computer(_FSProgram)) => value;

}

class _FSStringScalar extends _FSScalar {
  final String value;

  _FSStringScalar(this.value);

  String toString() => "str{$value}";

  String compute(dynamic computer(_FSProgram)) => value;

}

abstract class _RegisterFunction {

}

class _RegisterNamedFunction implements _RegisterFunction {


  final String name;
  final List positionalArguments;
  final Map<Symbol, dynamic> namedArguments;

  _RegisterNamedFunction(this.name, this.positionalArguments, this.namedArguments);

  _RegisterNamedFunction.from(_FSNamedFunction f, dynamic convert(_FSProgram)) : this(f.name.value, f.computedPositionalArgumentList(convert), () {
    var m = f.computedNamedArgumentMap(convert);
    return new Map.fromIterable(m.keys, key:MirrorSystem.getSymbol, value:(String s) => m[s]);
  }());

}

class _RegisterArrayAccessFunction implements _RegisterFunction {

  final key;

  _RegisterArrayAccessFunction(this.key);

  _RegisterArrayAccessFunction.from(_FSArrayAccessFunction f, dynamic convert(_FSProgram)) : this(f.scalar.compute(convert));

}


abstract class RegisterHandler {

  Object get instance;

  String get type;

  final Register register;

  bool canRunFunction(String type, _RegisterFunction f, [instance= null]);

  dynamic runFunction(String type, _RegisterFunction f, [instance= null]);

  void remove() {
    register.handlers.remove(this);
  }

  RegisterHandler(this.register);

  String toString() => "RegisterHandler: $type";

}

class TypeRegisterHandler extends RegisterHandler {

  final Type typeT;

  final ClassMirror mirror;

  final String type;

  Object instance;


  TypeRegisterHandler(Register register, Type type, [Object this.instance = null]) : super(register),
  this.typeT = type,
  this.type = MirrorSystem.getName(reflectType(type).simpleName),
  this.mirror = reflectType(type);


  bool _isGetterSetter(_RegisterNamedFunction f) => _getterSetterSymbol(f) != null;


  Symbol _getterSetterSymbol(_RegisterNamedFunction f) {

    if (f.name.length <= 3) {
      return null;
    }
    var setter = false;

    var s1, s2;
    var string = f.name.substring(3);
    if (f.name.startsWith('get')) {

      s1 = MirrorSystem.getSymbol(string[0].toUpperCase() + string.substring(1));
      s2 = MirrorSystem.getSymbol(string[0].toLowerCase() + string.substring(1));

    } else if (f.name.startsWith('set')) {
      s1 = MirrorSystem.getSymbol(string[0].toUpperCase() + string.substring(1) + "=");
      s2 = MirrorSystem.getSymbol(string[0].toLowerCase() + string.substring(1) + "=");
      setter = true;

    } else {
      return null;
    }

    MethodMirror mMirror;
    var s;
    if (mirror.instanceMembers.containsKey(s1)) {
      mMirror = mirror.instanceMembers[s1];
      s = s1;
    } else if (mirror.instanceMembers.containsKey(s2)) {
      mMirror = mirror.instanceMembers[s2];
      s = s2;
    } else {
      return null;
    }


    if (!((setter && mMirror.isSetter) || (!setter && mMirror.isGetter))) {
      return null;
    }

    return setter ? (MirrorSystem.getSymbol((s == s1 ? string[0].toUpperCase() : string[0].toLowerCase()) + string.substring(1))) : s;
  }

  bool canRunFunction(String type, _RegisterFunction function, [instance=null]) {

    instance = instance == null ? this.instance : instance;

    if (instance == null) {
      return false;
    }

    if (type != this.type) {
      return false;
    }
    var mirror = reflect(instance).type; //TODO: Fix when Issue 13440 is done

    if (function is _RegisterArrayAccessFunction) {
      return mirror.instanceMembers.containsKey(MirrorSystem.getSymbol("[]"));

    } else if (function is _RegisterNamedFunction) {
      _RegisterNamedFunction f = function;
      var symbol = MirrorSystem.getSymbol(f.name);
      if (!mirror.instanceMembers.containsKey(symbol)) {
        return _isGetterSetter(function);
      }

      MethodMirror mMirror = mirror.declarations[symbol];
      if (!mMirror.isRegularMethod) {
        return false;
      }

      return true;
    }
    return false;
  }

  dynamic runFunction(String type, _RegisterFunction function, [instance=null]) {

    instance = instance == null ? this.instance : instance;

    if (function is _RegisterArrayAccessFunction) {
      _RegisterArrayAccessFunction f = function;
      return reflect(instance).invoke(MirrorSystem.getSymbol("[]"), [f.key]).reflectee;
    } else {
      _RegisterNamedFunction f = function;
      var s = _getterSetterSymbol(f);
      if (s != null) {
        if (f.name.startsWith('get')) {
          return reflect(instance).getField(s).reflectee;
        }
        return reflect(instance).setField(s, f.positionalArguments[0]).reflectee;

      }
      return reflect(instance).invoke(MirrorSystem.getSymbol(f.name), f.positionalArguments, f.namedArguments).reflectee;
    }
  }


}


class AliasRegisterHandler extends RegisterHandler {


  final String type;

  final RegisterHandler target;

  AliasRegisterHandler(Register register, this.type, this.target) : super(register);

  Object get instance => target.instance;

  bool canRunFunction(String type, _RegisterFunction function, [Object instance]) =>
  target.canRunFunction(type == this.type ? target.type : type, function, instance);

  dynamic runFunction(String type, _RegisterFunction function, [Object instance]) =>
  target.runFunction(type == this.type ? target.type : type, function, instance);


  void removeWithTarget() {
    super.remove();
    target.remove();
  }

}

class SimpleRegisterHandler extends RegisterHandler {


  Object instance;

  final String type;

  final Map<String, Function> functions = new Map<String, Function>();

  Function arrayAccessFunction;

  SimpleRegisterHandler(Register register, this.type, [this.instance = null]) : super(register);

  bool canRunFunction(String type, _RegisterFunction function, [instance]) {
    if (type != this.type) {
      return false;
    }

    if (function is _RegisterNamedFunction) {
      _RegisterNamedFunction f = function;
      return functions.containsKey(f.name);
    }

    if (function is _RegisterArrayAccessFunction) {
      return arrayAccessFunction != null;
    }
    return false;
  }

  dynamic runFunction(String type, _RegisterFunction function, [instance]) {

    instance = instance == null ? this.instance : instance;

    if (function is _RegisterNamedFunction) {
      _RegisterNamedFunction f = function;
      var posArgs = f.positionalArguments;
      posArgs.insert(0, instance);
      return Function.apply(functions[f.name], posArgs, f.namedArguments);
    } else {
      _RegisterArrayAccessFunction f = function;
      return arrayAccessFunction(instance, f.key);
    }
  }


}

class Register {


  static Register _cache;

  final List<RegisterHandler> handlers = new List<RegisterHandler>();

  factory Register() => _cache == null ? _cache = new Register._internal() : _cache;


  Register._internal(){
    addType(String);
    addType(List);
    addType(num);
    addType(int);
    addType(double);
    addType(Map);
    addType(bool);
  }


  AliasRegisterHandler addAlias(String type, RegisterHandler targetHandler) {
    var h = new AliasRegisterHandler(this, type, targetHandler);
    handlers.add(h);
    return h;
  }

  TypeRegisterHandler addType(Type t, [Object instance= null]) {

    var h = new TypeRegisterHandler(this, t, instance);
    handlers.add(h);
    return h;
  }

  SimpleRegisterHandler add(String type, [Object instance= null]) {
    var h = new SimpleRegisterHandler(this, type, instance);
    handlers.add(h);
    return h;
  }


  dynamic _runRegisterFunction(List<String> types, _RegisterFunction function, [instance = null]) {
    var f = handlers.fold(null, (prev, RegisterHandler handler) {
      if (prev != null) {
        return prev;
      }

      var type = types.firstWhere((String s) => handler.canRunFunction(s, function, instance), orElse:() => null);
      if (type == null) {
        return null;
      }

      return () => handler.runFunction(type, function, instance);

    });

    return f == null ? null : f();
  }

  dynamic _runFunction(List<String> types, _FSFunction function, [instance = null]) {
    if (function is _FSNamedFunction) {
      return _runRegisterFunction(types, new _RegisterNamedFunction.from(function, _runProgram), instance);
    } else {
      return _runRegisterFunction(types, new _RegisterArrayAccessFunction.from(function, _runProgram), instance);
    }
  }

  dynamic _runFunctionCall(_FSFunctionCall f, [Object instance = null]) {
    if (f.target is _FSType) {
      _FSType t = f.target;
      return _runFunction([t.value], f.function, instance);
    }

    if (f.target is _FSFunctionCall) {
      var i = _runFunctionCall(f.target);
      return _runFunction(_typesFromInstance(i), f.function, i);
    }

    return null;
  }

  List<String> _buildType(ClassMirror mirror) {
    var l = [MirrorSystem.getName(mirror.simpleName)];
    l.addAll(mirror.superinterfaces.expand(_buildType).toList());

    if (mirror.superclass != null) {
      l.addAll(_buildType(mirror.superclass));
    }
    return l.fold([], (List<String> l, String s) {
      if (!l.contains(s)) {
        l.add(s);
      }
      return l;

    });

  }

  List<String> _typesFromInstance(Object instance) {
    if (instance == null) {
      return [];
    }
    return _buildType(reflect(instance).type);
  }


  dynamic runFunctionString(String s) => _runProgram(_parseFS(s));

  dynamic _runProgram(_FSProgram program) {

    if (program == null) {
      debug("Program was null");
      return null;
    }

    debug("Running program: $program");
    debugger.insertTab();
    var calls = program.toFunctionCalls();
    var lastReturn = null;
    calls.forEach((_FSFunctionCall c) {
      lastReturn = _runFunctionCall(c);
    });
    debugger.removeTab();
    debug("Result: $lastReturn");
    return lastReturn;
  }

}


Register get register => new Register();