blob: fca8f42ada26ee634a48f101fef3d68e370a3c3d [file] [log] [blame]
/**
* <copyright>
*
* Copyright (c) 2012 Willink Transformations and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* E.D.Willink - initial API and implementation
*
* </copyright>
*/
/*
Convert a *.xtext file (CS resource) into an XBNF instance.
Feature references are resolved to EStructuralFeature references.
Cardinality is exposed as OneOrMore, ZeroOrOne, ZeroOrMore wrappers
Bugs:
No support for enum rules, predicates
*/
modeltype XTEXT uses 'http://www.eclipse.org/2008/Xtext';
modeltype ECORE uses 'http://www.eclipse.org/emf/2002/Ecore';
modeltype XBNF uses 'http://www.eclipse.org/ocl/XBNF';
modeltype XBNFwc uses 'http://www.eclipse.org/ocl/XBNFwithCardinality';
transformation xtext2xbnf(in xtext : XTEXT, out XBNF);
main() {
xtext.rootObjects()[XTEXT::Grammar]->map grammar2syntax();
}
mapping XTEXT::Grammar::grammar2syntax() : XBNF::Syntax {
/* var selfRules : OrderedSet(AbstractRule) = self.rules;
var selfRootElements : Set(AbstractElement) = selfRules.oclAsType(EObject).eAllContents().alternatives->asSet();
var allElements : Set(AbstractElement) = selfRootElements->closure(if oclIsKindOf(CompoundElement) then oclAsType(CompoundElement).elements else null endif)->union(selfRootElements);
var ruleCalls : Set(RuleCall) = allElements->select(oclIsKindOf(RuleCall)).oclAsType(RuleCall)->asSet();
var ruleCalls : Set(RuleCall) = allElements->select(oclIsKindOf(RuleCall)).oclAsType(RuleCall)->asSet();
var allRules : OrderedSet(AbstractRule) = ruleCalls.rule->asSet()->union(selfRules)->sortedBy(name);
var lexerRules : Sequence(TypedRule) = allRules->reject(oclIsKindOf(ParserRule))->map typedRule2rule();
var parserRules : Sequence(TypedRule) = allRules->select(oclIsKindOf(ParserRule))->map typedRule2rule(); */
var allGrammars : Sequence(Grammar) = self->asSet()->closure(usedGrammars->asSet())->asSequence()->prepend(self);
name := self.name;
-- var ruleCalls : Set(RuleCall) = self.oclAsType(EObject)->closure(eContents())->select(oclIsKindOf(RuleCall)).oclAsType(RuleCall)->asSet();
-- debug := XTEXT::Grammar.allInstances()->size().toString() + ' ' + ruleCalls->size().toString();
grammars := allGrammars->iterate(g ; acc : Sequence(XBNF::Grammar) = Sequence{} | acc->append(g.map grammar2lexerGrammar())->append(g.map grammar2parserGrammar()));
}
/*query gatherRules(knownRules : Set(AbstractRule), candidateRules : Set(AbstractRule)) : Set(AbstractRule)
{
var moreKnownRules = knownRules->union(candidateRules);
var moreCalls : Set(RuleCall) = candidateRules.oclAsType(EObject)->closure(eContents().oclAsType(EObject)->asSet())->select(oclIsKindOf(RuleCall)).oclAsType(RuleCall)->asSet();
var moreRules : Set(AbstractRule) = moreCalls.rule->asSet() - moreKnownRules;
var t = object XBNF::ParserGrammar { name = 'test '; };
-- var t1 = object XBNF::ParserGrammar { name = 'test1 ' + knownRules->size().toString(); };
-- var t2 = object XBNF::ParserGrammar { name = 'test2 ' + knownRules->size().toString() + '/' + candidateRules->size().toString() + ' => ' + moreRules->size().toString(); };
return moreKnownRules;
-- return if moreRules->isEmpty() then moreKnownRules else gatherRules(moreKnownRules, moreRules) endif;
}*/
mapping XTEXT::Grammar::grammar2lexerGrammar() : XBNF::LexerGrammar {
var lexerRules : Sequence(TypedRule) = self.rules->reject(oclIsKindOf(ParserRule))->map typedRule2rule();
name := self.name + 'Lexer';
rules := lexerRules;
goals := lexerRules->first()->asSequence();
}
mapping XTEXT::Grammar::grammar2parserGrammar() : XBNF::ParserGrammar {
var parserRules : Sequence(TypedRule) = self.rules->select(oclIsKindOf(ParserRule))->map typedRule2rule();
-- var noRules : Set(AbstractRule) = Set{};
-- var allParserRules : Set(AbstractRule) = gatherRules(noRules, self.rules->first().oclAsType(AbstractRule)->asSet());
name := self.name + 'Parser';
rules := parserRules->sortedBy(name);
goals := parserRules->first()->asSequence();
}
---------------------------------------------------------------------------------------
-- Map rule to rule
---------------------------------------------------------------------------------------
mapping XTEXT::AbstractRule::typedRule2rule() : XBNF::TypedRule
disjuncts EnumRule::enumRule2rule,
ParserRule::parserRule2rule,
TerminalRule::terminalRule2rule
{}
abstract mapping XTEXT::AbstractRule::abstractRule2rule_base() : XBNF::TypedRule
{
name := self.name;
type := self.type.classifier;
element := self.alternatives.element2element();
}
mapping XTEXT::EnumRule::enumRule2rule() : XBNF::TerminalRule
inherits AbstractRule::abstractRule2rule_base
{
kind := 'EnumRule';
}
mapping XTEXT::ParserRule::parserRule2rule() : XBNF::ParserRule
inherits AbstractRule::abstractRule2rule_base
{
kind := 'ParserRule';
}
mapping XTEXT::TerminalRule::terminalRule2rule() : XBNF::TerminalRule
inherits AbstractRule::abstractRule2rule_base
{
kind := 'TerminalRule';
}
---------------------------------------------------------------------------------------
-- Map element to element
---------------------------------------------------------------------------------------
helper XTEXT::AbstractElement::element2element() : XBNF::AbstractElement
{
return
if self.cardinality = '?' then object ZeroOrOne { debug := self.cardinality; element := self.element2element2(); }
else if self.cardinality = '+' then object OneOrMore { debug := self.cardinality; element := self.element2element2(); }
else if self.cardinality = '*' then object ZeroOrMore { debug := self.cardinality; element := self.element2element2(); }
else self.element2element2()
endif
endif
endif;
}
helper XTEXT::AbstractElement::element2element2() : XBNF::AbstractElement
{
return self.map abstractElement2element();
}
helper XTEXT::Assignment::element2element2() : XBNF::AbstractElement
{
var theRule : AbstractRule = self.terminal.oclAsType(RuleCall).rule;
var classifier : EClass = self.getClass();
var feature : EStructuralFeature = classifier.eAllStructuralFeatures->select(s | (s.name = self.feature) or (s.name.toUpperCase() = ('is' + self.feature).toUpperCase()))->any(true);
return self.terminal.map assignment2assignments(self, feature);
}
mapping XTEXT::AbstractElement::abstractElement2element() : XBNF::AbstractElement
disjuncts Action::action2element,
Alternatives::alternatives2element,
CharacterRange::characterRange2element,
CompoundElement::compoundElement2sequentialTerm,
EOF::eof2element,
EnumLiteralDeclaration::enumLiteralDeclaration2element,
Group::group2element,
Keyword::keyword2element,
NegatedToken::negatedToken2element,
RuleCall::ruleCall2element,
UnorderedGroup::unorderedGroup2element,
UntilToken::untilToken2element,
Wildcard::wildcard2element
{}
mapping XTEXT::Action::action2element() : XBNF::ActionAssignment
when { self.feature <> null }
{
var classifier : EClass = self.type.classifier.oclAsType(EClass);
debug := self.feature + ':' + classifier.name + self.operator + 'current';
feature := classifier.eAllStructuralFeatures->select(s | (s.name = self.feature) or (s.name.toUpperCase() = ('is' + self.feature).toUpperCase()))->any(true);
operator := self.operator;
type := self.type.classifier;
}
mapping XTEXT::Alternatives::alternatives2element() : XBNFwc::Alternatives {
debug := '|';
elements := self.elements->element2element();
}
mapping XTEXT::CharacterRange::characterRange2element() : XBNF::CharacterRange {
debug := '[' + self.left.value + '-' + self.right.value + ']';
left := self.left.value;
right := self.right.value;
}
mapping XTEXT::CompoundElement::compoundElement2sequentialTerm() : XBNFwc::Succession
{
elements := self.elements->element2element();
}
mapping XTEXT::EOF::eof2element() : XBNF::EOF {
debug := 'EOF';
}
mapping XTEXT::EnumLiteralDeclaration::enumLiteralDeclaration2element() : XBNF::RuleCall {
debug := 'EnumLiteralDeclaration';
}
mapping XTEXT::Group::group2element() : XBNF::RuleCall {
debug := 'Group';
}
mapping XTEXT::Keyword::keyword2element() : XBNF::Keyword {
debug := '"' + self.value + '"';
value := self.value;
}
mapping XTEXT::NegatedToken::negatedToken2element() : XBNF::NegatedToken {
debug := 'NegatedToken';
terminal := self.terminal.element2element();
}
mapping XTEXT::RuleCall::ruleCall2element() : XBNF::RuleCall {
debug := self.rule.name;
referredRule := self.rule.late resolveone(XBNF::AbstractRule);
}
mapping XTEXT::UnorderedGroup::unorderedGroup2element() : XBNF::RuleCall {
debug := 'UnorderedGroup';
}
mapping XTEXT::UntilToken::untilToken2element() : XBNF::UntilToken {
debug := 'UntilToken';
terminal := self.terminal.element2element();
}
mapping XTEXT::Wildcard::wildcard2element() : XBNF::Wildcard {
debug := 'Wildcard';
}
---------------------------------------------------------------------------------------
-- Map an Assignment.terminal to the appropriate assignnments
---------------------------------------------------------------------------------------
mapping XTEXT::AbstractElement::assignment2assignments(assignment : Assignment, theFeature : EStructuralFeature) : XBNF::AbstractElement
disjuncts Alternatives::alternatives2assignments,
CrossReference::crossReference2assignments,
Keyword::keyword2assignments,
RuleCall::ruleCall2assignments
{}
mapping XTEXT::Alternatives::alternatives2assignments(assignment : Assignment, theFeature : EStructuralFeature) : XBNFwc::Alternatives
{
elements := self.elements->map assignment2assignments(assignment, theFeature);
}
mapping XTEXT::CrossReference::crossReference2assignments(assignment : Assignment, theFeature : EStructuralFeature) : XBNF::RuleCallAssignment
{
var theRule : AbstractRule = self.terminal.oclAsType(RuleCall).rule;
debug := '#1 ' + theFeature.name + assignment.operator + theRule.name;
feature := theFeature;
operator := assignment.operator;
referredRule := theRule.late resolveone(XBNF::AbstractRule);
}
mapping XTEXT::Keyword::keyword2assignments(assignment : Assignment, theFeature : EStructuralFeature) : XBNF::KeywordAssignment
{
debug := '"' + self.value + '"';
feature := theFeature;
operator := assignment.operator;
value := self.value;
}
mapping XTEXT::RuleCall::ruleCall2assignments(assignment : Assignment, theFeature : EStructuralFeature) : XBNF::RuleCallAssignment
{
debug := '#2 ' + theFeature.name + assignment.operator + self.rule.name;
feature := theFeature;
operator := assignment.operator;
referredRule := self.rule.late resolveone(XBNF::AbstractRule);
}
---------------------------------------------------------------------------------------
-- Return the EClass that scopes feature assignments
---------------------------------------------------------------------------------------
query XTEXT::AbstractElement::getClass() : ECORE::EClass {
var container : EObject = self.oclAsType(EObject).eContainer();
return if container.oclIsKindOf(AbstractRule)
then container.oclAsType(AbstractRule).type.classifier.oclAsType(EClass)
else container.oclAsType(AbstractElement).getClass()
endif
}
query XTEXT::CompoundElement::getClass() : ECORE::EClass {
var container : EObject = self.oclAsType(EObject).eContainer();
return if (self.elements->first().oclIsKindOf(Action))
then self.elements->first().oclAsType(Action).type.classifier.oclAsType(EClass)
else if container.oclIsKindOf(AbstractRule)
then container.oclAsType(AbstractRule).type.classifier.oclAsType(EClass)
else container.oclAsType(AbstractElement).getClass()
endif endif
}