| Index: sdk/lib/_internal/compiler/implementation/prolog/prolog.dart
|
| diff --git a/sdk/lib/_internal/compiler/implementation/prolog/prolog.dart b/sdk/lib/_internal/compiler/implementation/prolog/prolog.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e85f75f91966ffddaa8b30164c88450f10d7925d
|
| --- /dev/null
|
| +++ b/sdk/lib/_internal/compiler/implementation/prolog/prolog.dart
|
| @@ -0,0 +1,251 @@
|
| +library prolog;
|
| +
|
| +import '../common.dart';
|
| +import 'dart:async';
|
| +import 'dart:collection';
|
| +
|
| +abstract class Node {
|
| + const Node();
|
| +}
|
| +
|
| +abstract class Term extends Node {
|
| + const Term();
|
| +}
|
| +
|
| +class Atom extends Term {
|
| + final String text;
|
| + const Atom(this.text);
|
| +
|
| + get hashCode => text.hashCode;
|
| +
|
| + operator ==(other) => text == other.text;
|
| +
|
| + static const Atom AND = const Atom(",");
|
| + static const Atom OR = const Atom(";");
|
| + static const Atom IMPLY = const Atom(":-");
|
| +}
|
| +
|
| +class Compound extends Term {
|
| + final Atom functor;
|
| + final List<Term> arguments;
|
| + const Compound(this.functor, [this.arguments = const <Term>[]]);
|
| + bool get isAnd => functor == Atom.AND;
|
| + bool get isOr => functor == Atom.OR;
|
| + bool get isImply => functor == Atom.IMPLY;
|
| +}
|
| +
|
| +class And extends Compound {
|
| + const And(List<Term> arguments) : super(Atom.AND, arguments);
|
| +}
|
| +
|
| +class Or extends Compound {
|
| + const Or(List<Term> arguments) : super(Atom.OR, arguments);
|
| +}
|
| +
|
| +class Clause extends Compound {
|
| + Clause(Term head, [List<Term> body = const <Term>[]])
|
| + : super(Atom.IMPLY, <Term>[head]..addAll(body));
|
| +
|
| + Atom get predicate {
|
| + Term head = arguments.first;
|
| + return head is Compound ? head.functor : head;
|
| + }
|
| +
|
| + Term get head => arguments.first;
|
| +
|
| + Iterable<Term> get body => arguments.skip(1);
|
| +}
|
| +
|
| +
|
| +class Program {
|
| + Map<Atom, List<Clause>> clauses = <Atom, List<Clause>>{};
|
| +
|
| + void add(Clause clause) {
|
| + assert(clause != null);
|
| + clauses.putIfAbsent(clause.predicate, () => <Clause>[]).add(clause);
|
| + }
|
| +
|
| + void addClause(Term head, List<Term> body) => add(new Clause(head, body));
|
| +
|
| + Iterable<Clause> getClausesFor(Atom predicate) {
|
| + return clauses.containsKey(predicate)
|
| + ? clauses[predicate]
|
| + : const <Clause>[];
|
| + }
|
| +
|
| + Program mergeWith(Program other) {
|
| + clauses.addAll(other.clauses);
|
| + return this;
|
| + }
|
| +}
|
| +
|
| +
|
| +class ProgramEmitter {
|
| + final EventSink<String> _sink;
|
| +
|
| + ProgramEmitter(this._sink);
|
| +
|
| + String _quoteAtom(Atom atom) {
|
| + String text = atom.text;
|
| + if (text == "" ||
|
| + text.contains(new RegExp(r'[^_0-9a-zA-Z]')) ||
|
| + text.indexOf(new RegExp('[_0-9A-Z]')) == 0) {
|
| + return "'$text'";
|
| + } else {
|
| + return text;
|
| + }
|
| + }
|
| +
|
| + void _emit(String text) => _sink.add(text);
|
| +
|
| + void _emitAtom(Atom atom, {quote: true}) {
|
| + _emit(quote ? _quoteAtom(atom) : atom.text);
|
| + }
|
| +
|
| + void _emitTerm(Term term) {
|
| + if (term is Atom) {
|
| + _emitAtom(term);
|
| + } else if (term is Compound) {
|
| + _emitAtom(term.functor);
|
| + _open();
|
| + _emitList(term.arguments);
|
| + _close();
|
| + } else {
|
| + throw 'unsupported $term';
|
| + }
|
| + }
|
| +
|
| + void _emitList(Iterable<Term> terms, {separator: ', '}) {
|
| + if (terms.isEmpty) return;
|
| + _emitTerm(terms.first);
|
| + for (Term term in terms.skip(1)) {
|
| + _emit(separator);
|
| + _emitTerm(term);
|
| + }
|
| + }
|
| +
|
| + void _open() => _emit('(');
|
| +
|
| + void _close() => _emit(')');
|
| +
|
| + void _space() => _emit(' ');
|
| +
|
| + void _endClause() => _emit('.\n');
|
| +
|
| + void emitProgram(Program program) {
|
| + for (Atom predicate in program.clauses.keys) {
|
| + if (program.clauses[predicate] == null) print(predicate.text);
|
| + for (Clause clause in program.clauses[predicate]) {
|
| + _emitTerm(clause.head);
|
| + Iterable<Term> body = clause.body;
|
| + if (body.isNotEmpty) {
|
| + _space();
|
| + _emitAtom(Atom.IMPLY, quote: false);
|
| + _space();
|
| + _emitList(body);
|
| + }
|
| + _endClause();
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +/// Used to build a program from recorded relations and the element model.
|
| +class ProgramBuilder {
|
| + Program _program = new Program();
|
| +
|
| + Map<String, dynamic> _usedNames = <String, dynamic>{};
|
| +
|
| + bool _alreadyUsed(entity, String name) {
|
| + return _usedNames.containsKey(name) && entity != _usedNames[name];
|
| + }
|
| +
|
| + Map<Element, Atom> _identifiers = <Element, Atom>{};
|
| +
|
| + Queue<Function> _queue = new Queue<Function>();
|
| +
|
| + void defer(f()) {
|
| + _queue.add(f);
|
| + }
|
| +
|
| + void _addFact(Atom predicate, List<Term> arguments) {
|
| + _program.add(new Clause(new Compound(predicate, arguments)));
|
| + }
|
| +
|
| + void _addClause(Clause clause) {
|
| + _program.add(clause);
|
| + }
|
| +
|
| + void _addContains(Atom container, Atom element) {
|
| + _addFact(const Atom('contains'), <Atom>[container, element]);
|
| + }
|
| +
|
| + Atom _elementPredicate(Element element) => new Atom(element.kind.id);
|
| +
|
| + // TODO(karlklose): Extract a strategy object for these two functions.
|
| + Clause _elementDefinition(Element element, Atom reference) {
|
| + Atom name = new Atom(element.name);
|
| + Atom kind = new Atom(element.kind.id);
|
| + List<Term> properties = <Term>[reference, name];
|
| + return new Clause(new Compound(kind, properties));
|
| + }
|
| +
|
| + String _getNameProposal(entity) {
|
| + if (entity is CompilationUnitElement) {
|
| + return 'file_${entity.script.readableUri.pathSegments.last}';
|
| + } else if (entity is LibraryElement) {
|
| + return 'lib_${entity.canonicalUri.pathSegments.last}';
|
| + } else if (entity is Element) {
|
| + return entity.name;
|
| + } else {
|
| + return entity.toString();
|
| + }
|
| + }
|
| +
|
| + Atom _elementReference(Element element) {
|
| + if (_identifiers.containsKey(element)) return _identifiers[element];
|
| + Atom reference = new Atom(_getName(element));
|
| + Element container = element.enclosingElement;
|
| + if (container != null) {
|
| + defer(() {
|
| + _addContains(_elementReference(container), reference);
|
| + _addClause(_elementDefinition(element, reference));
|
| + });
|
| + }
|
| + return _identifiers[element] = reference;
|
| + }
|
| +
|
| + String _makeUniqueName(entity, String base) {
|
| + int nr = 0;
|
| + String proposal = base;
|
| + while (_alreadyUsed(entity, proposal)) {
|
| + proposal = '$base$nr';
|
| + nr++;
|
| + }
|
| + _usedNames[proposal] = entity;
|
| + return proposal;
|
| + }
|
| +
|
| + String _getName(entity) => _makeUniqueName(entity, _getNameProposal(entity));
|
| +
|
| + Term _terminize(object) {
|
| + return object is Element ? _elementReference(object)
|
| + : new Atom(object.toString());
|
| + }
|
| +
|
| + void addRelation(String relation, List elements) {
|
| + List<Term> references = elements.map(_terminize).toList();
|
| + _addFact(new Atom(relation), references);
|
| + while (_queue.isNotEmpty) {
|
| + Function continuation = _queue.removeFirst();
|
| + continuation();
|
| + }
|
| + }
|
| +
|
| + Program get program {
|
| + Program result = _program;
|
| + _program = null;
|
| + return result;
|
| + }
|
| +}
|
|
|