| OLD | NEW |
| (Empty) | |
| 1 library prolog; |
| 2 |
| 3 import '../common.dart'; |
| 4 import 'dart:async'; |
| 5 import 'dart:collection'; |
| 6 |
| 7 abstract class Node { |
| 8 const Node(); |
| 9 } |
| 10 |
| 11 abstract class Term extends Node { |
| 12 const Term(); |
| 13 } |
| 14 |
| 15 class Atom extends Term { |
| 16 final String text; |
| 17 const Atom(this.text); |
| 18 |
| 19 get hashCode => text.hashCode; |
| 20 |
| 21 operator ==(other) => text == other.text; |
| 22 |
| 23 static const Atom AND = const Atom(","); |
| 24 static const Atom OR = const Atom(";"); |
| 25 static const Atom IMPLY = const Atom(":-"); |
| 26 } |
| 27 |
| 28 class Compound extends Term { |
| 29 final Atom functor; |
| 30 final List<Term> arguments; |
| 31 const Compound(this.functor, [this.arguments = const <Term>[]]); |
| 32 bool get isAnd => functor == Atom.AND; |
| 33 bool get isOr => functor == Atom.OR; |
| 34 bool get isImply => functor == Atom.IMPLY; |
| 35 } |
| 36 |
| 37 class And extends Compound { |
| 38 const And(List<Term> arguments) : super(Atom.AND, arguments); |
| 39 } |
| 40 |
| 41 class Or extends Compound { |
| 42 const Or(List<Term> arguments) : super(Atom.OR, arguments); |
| 43 } |
| 44 |
| 45 class Clause extends Compound { |
| 46 Clause(Term head, [List<Term> body = const <Term>[]]) |
| 47 : super(Atom.IMPLY, <Term>[head]..addAll(body)); |
| 48 |
| 49 Atom get predicate { |
| 50 Term head = arguments.first; |
| 51 return head is Compound ? head.functor : head; |
| 52 } |
| 53 |
| 54 Term get head => arguments.first; |
| 55 |
| 56 Iterable<Term> get body => arguments.skip(1); |
| 57 } |
| 58 |
| 59 |
| 60 class Program { |
| 61 Map<Atom, List<Clause>> clauses = <Atom, List<Clause>>{}; |
| 62 |
| 63 void add(Clause clause) { |
| 64 assert(clause != null); |
| 65 clauses.putIfAbsent(clause.predicate, () => <Clause>[]).add(clause); |
| 66 } |
| 67 |
| 68 void addClause(Term head, List<Term> body) => add(new Clause(head, body)); |
| 69 |
| 70 Iterable<Clause> getClausesFor(Atom predicate) { |
| 71 return clauses.containsKey(predicate) |
| 72 ? clauses[predicate] |
| 73 : const <Clause>[]; |
| 74 } |
| 75 |
| 76 Program mergeWith(Program other) { |
| 77 clauses.addAll(other.clauses); |
| 78 return this; |
| 79 } |
| 80 } |
| 81 |
| 82 |
| 83 class ProgramEmitter { |
| 84 final EventSink<String> _sink; |
| 85 |
| 86 ProgramEmitter(this._sink); |
| 87 |
| 88 String _quoteAtom(Atom atom) { |
| 89 String text = atom.text; |
| 90 if (text == "" || |
| 91 text.contains(new RegExp(r'[^_0-9a-zA-Z]')) || |
| 92 text.indexOf(new RegExp('[_0-9A-Z]')) == 0) { |
| 93 return "'$text'"; |
| 94 } else { |
| 95 return text; |
| 96 } |
| 97 } |
| 98 |
| 99 void _emit(String text) => _sink.add(text); |
| 100 |
| 101 void _emitAtom(Atom atom, {quote: true}) { |
| 102 _emit(quote ? _quoteAtom(atom) : atom.text); |
| 103 } |
| 104 |
| 105 void _emitTerm(Term term) { |
| 106 if (term is Atom) { |
| 107 _emitAtom(term); |
| 108 } else if (term is Compound) { |
| 109 _emitAtom(term.functor); |
| 110 _open(); |
| 111 _emitList(term.arguments); |
| 112 _close(); |
| 113 } else { |
| 114 throw 'unsupported $term'; |
| 115 } |
| 116 } |
| 117 |
| 118 void _emitList(Iterable<Term> terms, {separator: ', '}) { |
| 119 if (terms.isEmpty) return; |
| 120 _emitTerm(terms.first); |
| 121 for (Term term in terms.skip(1)) { |
| 122 _emit(separator); |
| 123 _emitTerm(term); |
| 124 } |
| 125 } |
| 126 |
| 127 void _open() => _emit('('); |
| 128 |
| 129 void _close() => _emit(')'); |
| 130 |
| 131 void _space() => _emit(' '); |
| 132 |
| 133 void _endClause() => _emit('.\n'); |
| 134 |
| 135 void emitProgram(Program program) { |
| 136 for (Atom predicate in program.clauses.keys) { |
| 137 if (program.clauses[predicate] == null) print(predicate.text); |
| 138 for (Clause clause in program.clauses[predicate]) { |
| 139 _emitTerm(clause.head); |
| 140 Iterable<Term> body = clause.body; |
| 141 if (body.isNotEmpty) { |
| 142 _space(); |
| 143 _emitAtom(Atom.IMPLY, quote: false); |
| 144 _space(); |
| 145 _emitList(body); |
| 146 } |
| 147 _endClause(); |
| 148 } |
| 149 } |
| 150 } |
| 151 } |
| 152 |
| 153 |
| 154 /// Used to build a program from recorded relations and the element model. |
| 155 class ProgramBuilder { |
| 156 Program _program = new Program(); |
| 157 |
| 158 Map<String, dynamic> _usedNames = <String, dynamic>{}; |
| 159 |
| 160 bool _alreadyUsed(entity, String name) { |
| 161 return _usedNames.containsKey(name) && entity != _usedNames[name]; |
| 162 } |
| 163 |
| 164 Map<Element, Atom> _identifiers = <Element, Atom>{}; |
| 165 |
| 166 Queue<Function> _queue = new Queue<Function>(); |
| 167 |
| 168 void defer(f()) { |
| 169 _queue.add(f); |
| 170 } |
| 171 |
| 172 void _addFact(Atom predicate, List<Term> arguments) { |
| 173 _program.add(new Clause(new Compound(predicate, arguments))); |
| 174 } |
| 175 |
| 176 void _addClause(Clause clause) { |
| 177 _program.add(clause); |
| 178 } |
| 179 |
| 180 void _addContains(Atom container, Atom element) { |
| 181 _addFact(const Atom('contains'), <Atom>[container, element]); |
| 182 } |
| 183 |
| 184 Atom _elementPredicate(Element element) => new Atom(element.kind.id); |
| 185 |
| 186 // TODO(karlklose): Extract a strategy object for these two functions. |
| 187 Clause _elementDefinition(Element element, Atom reference) { |
| 188 Atom name = new Atom(element.name); |
| 189 Atom kind = new Atom(element.kind.id); |
| 190 List<Term> properties = <Term>[reference, name]; |
| 191 return new Clause(new Compound(kind, properties)); |
| 192 } |
| 193 |
| 194 String _getNameProposal(entity) { |
| 195 if (entity is CompilationUnitElement) { |
| 196 return 'file_${entity.script.readableUri.pathSegments.last}'; |
| 197 } else if (entity is LibraryElement) { |
| 198 return 'lib_${entity.canonicalUri.pathSegments.last}'; |
| 199 } else if (entity is Element) { |
| 200 return entity.name; |
| 201 } else { |
| 202 return entity.toString(); |
| 203 } |
| 204 } |
| 205 |
| 206 Atom _elementReference(Element element) { |
| 207 if (_identifiers.containsKey(element)) return _identifiers[element]; |
| 208 Atom reference = new Atom(_getName(element)); |
| 209 Element container = element.enclosingElement; |
| 210 if (container != null) { |
| 211 defer(() { |
| 212 _addContains(_elementReference(container), reference); |
| 213 _addClause(_elementDefinition(element, reference)); |
| 214 }); |
| 215 } |
| 216 return _identifiers[element] = reference; |
| 217 } |
| 218 |
| 219 String _makeUniqueName(entity, String base) { |
| 220 int nr = 0; |
| 221 String proposal = base; |
| 222 while (_alreadyUsed(entity, proposal)) { |
| 223 proposal = '$base$nr'; |
| 224 nr++; |
| 225 } |
| 226 _usedNames[proposal] = entity; |
| 227 return proposal; |
| 228 } |
| 229 |
| 230 String _getName(entity) => _makeUniqueName(entity, _getNameProposal(entity)); |
| 231 |
| 232 Term _terminize(object) { |
| 233 return object is Element ? _elementReference(object) |
| 234 : new Atom(object.toString()); |
| 235 } |
| 236 |
| 237 void addRelation(String relation, List elements) { |
| 238 List<Term> references = elements.map(_terminize).toList(); |
| 239 _addFact(new Atom(relation), references); |
| 240 while (_queue.isNotEmpty) { |
| 241 Function continuation = _queue.removeFirst(); |
| 242 continuation(); |
| 243 } |
| 244 } |
| 245 |
| 246 Program get program { |
| 247 Program result = _program; |
| 248 _program = null; |
| 249 return result; |
| 250 } |
| 251 } |
| OLD | NEW |