Skip to content

Commit

Permalink
Merge pull request #230 from TypeFox/dh/yang-processor
Browse files Browse the repository at this point in the history
yang tool progress
  • Loading branch information
dhuebner authored Nov 28, 2023
2 parents eb32abe + 2775519 commit de58f96
Show file tree
Hide file tree
Showing 32 changed files with 14,359 additions and 8 deletions.
11 changes: 11 additions & 0 deletions yang-lsp/.settings/org.eclipse.buildship.core.prefs
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
arguments=
auto.sync=false
build.scans.enabled=false
connection.arguments=
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
connection.gradle.user.home=null
connection.java.home=null
connection.jvm.arguments=
connection.project.dir=
eclipse.preferences.version=1
gradle.user.home=
java.home=
jvm.arguments=
offline.mode=false
override.workspace.settings=false
project.path=\:
show.console.view=false
show.executions.view=false
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
eclipse.preferences.version=1
encoding/<project>=UTF-8
Original file line number Diff line number Diff line change
Expand Up @@ -297,9 +297,11 @@ class YangContentProposalProvider extends IdeContentProposalProvider {
val modulePrefix = suffix.getSegment(i)
if (modulePrefix != scopeCtx.moduleName) {
val moduleName = suffix.getSegment(i)
val importPrefix = scopeCtx.importedModules.entrySet.findFirst[value.moduleName == moduleName].
key
name.append(importPrefix).append(":")
val importedByName = scopeCtx.importedModules.entrySet.findFirst[value.moduleName == moduleName]
if(importedByName !== null) {
val importPrefix = importedByName.key
name.append(importPrefix).append(":")
}
}
} else {
name.append(suffix.getSegment(i))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package io.typefox.yang.processor

import io.typefox.yang.processor.ProcessedDataTree.AccessKind
import io.typefox.yang.processor.ProcessedDataTree.ElementData
import io.typefox.yang.processor.ProcessedDataTree.HasStatements
import io.typefox.yang.processor.ProcessedDataTree.ListData
import io.typefox.yang.processor.ProcessedDataTree.ModuleData
import java.util.List
import io.typefox.yang.processor.ProcessedDataTree.ElementKind

class DataTreeSerializer {

def CharSequence serialize(ModuleData moduleData) {
'''
module: «moduleData.simpleName»
«FOR child : moduleData.children?:#[]»
«doSerialize(child, '', needsConnect(child, moduleData.children))»
«ENDFOR»
rpcs:
«FOR rpc : moduleData.rpcs?:#[]»
«doSerialize(rpc, '', needsConnect(rpc, moduleData.rpcs))»
«ENDFOR»
'''
}

dispatch def CharSequence doSerialize(HasStatements ele, String indent, boolean needsConnect) {
'''«indent» unsupported data type!'''
}

dispatch def CharSequence doSerialize(ElementData ele, String indent, boolean needsConnect) {
var accessString = " "
if(ele.getAccessKind() != AccessKind.not_set) {
accessString = ele.getAccessKind().name
}
val prefix = switch (ele.elementKind) {
case Case:
'--:'
default: {
'''«"----".substring(accessString.length)»«!accessString.trim.empty?accessString» '''
}
}
val label = switch (ele.elementKind) {
case Choice,
case Case: '''(«ele.name»)'''
default: {
ele.name
}
}
val keys = if (ele instanceof ListData) {
ele.keys.empty ? null : ''' [«ele.keys.join(', ')»]'''
}

val type = ele.getType
// no idea why this indentation is needed in pyang
val additionalIdent = if(ele.elementKind === ElementKind.Choice && prevSibling(ele)?.elementKind === ElementKind.Container) ' ' else ''
'''
«indent»«additionalIdent»+«prefix»«label»«ele.cardinality?.toString()»«keys»«IF type !== null» «type»«ENDIF»«IF ele.featureConditions !== null» {«ele.featureConditions.join(',')»}?«ENDIF»
«IF ele.children !== null»
«FOR child : ele.children»
«doSerialize(child, indent + (needsConnect?'| ':' '), needsConnect(child, ele.children))»
«ENDFOR»
«ENDIF»
'''
}

private def ElementData prevSibling(ElementData ele) {
val siblings = ele.parent?.children
if(siblings === null) {
return null
}
val eleIdx = siblings.indexOf(ele)
if(eleIdx > 0) {
val prevChild = siblings.get(eleIdx - 1)
if(prevChild instanceof ElementData) {
return prevChild
}
}
}

private def boolean needsConnect(HasStatements ele, List<HasStatements> siblings) {
if (siblings === null || siblings.last === ele) {
return false
}
return true
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package io.typefox.yang.processor;

import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import io.typefox.yang.processor.ProcessedDataTree.ElementIdentifier;
import io.typefox.yang.yang.Feature;

public class FeatureEvaluationContext {

private Map<String, Boolean> cache = Maps.newHashMap();

private Set<String> include = Sets.newHashSet(), exclude = Sets.newHashSet();

public FeatureEvaluationContext(List<String> includedFeatures, List<String> excludedFeatures) {
include.addAll(includedFeatures);
exclude.addAll(excludedFeatures);
}

public boolean isActive(Feature feature) {
ElementIdentifier featureModule = ProcessorUtility.moduleIdentifier(feature);
var featureQName = featureGlobalQName(featureModule, feature.getName());
if (cache.containsKey(featureQName)) {
return cache.get(featureQName);
}
var active = isActive(featureModule.name + ":", featureQName) && featureIfConditionsActive(feature);
cache.put(featureQName, active);
return active;
}

private boolean featureIfConditionsActive(Feature feature) {
return ProcessorUtility.checkIfFeatures(ProcessorUtility.findIfFeatures(feature), this);
}

private boolean isActive(String modulePrefix, String featureQName) {
// include <module>: means include none of <module> features.
if(include.contains(modulePrefix) && !include.contains(featureQName)) {
// include e.g. 'example-system-ext:' means any of example-system-ext module features should be included
return false;
}
return (include.isEmpty() || include.contains(featureQName)
|| !include.contains(modulePrefix)) // if <module>: not listed in include, all features are enabled
&& (exclude.isEmpty() || !(exclude.contains(featureQName) || exclude.contains(modulePrefix)));
}

private String featureGlobalQName(ElementIdentifier module, String featureName) {
return module.name + ":" + featureName;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package io.typefox.yang.processor;

import org.eclipse.xtext.nodemodel.util.NodeModelUtils;

import io.typefox.yang.processor.ProcessedDataTree.ElementIdentifier;
import io.typefox.yang.yang.BinaryOperation;
import io.typefox.yang.yang.Expression;
import io.typefox.yang.yang.Feature;
import io.typefox.yang.yang.FeatureReference;
import io.typefox.yang.yang.UnaryOperation;

public class FeatureExpressions {

public static abstract class FeatureCondition {

abstract boolean evaluate(FeatureEvaluationContext ctx);

public static FeatureCondition create(Expression exp) {
return build(exp);
}

private static FeatureCondition build(Expression exp) {
if (exp instanceof FeatureReference) {
return new IsFeatureCondition((FeatureReference) exp, ProcessorUtility.moduleIdentifier(exp));
} else if (exp instanceof UnaryOperation) {
return new IsFeatureCondition((FeatureReference) exp, true, ProcessorUtility.moduleIdentifier(exp));
} else if (exp instanceof BinaryOperation) {
BinaryOperation bin = (BinaryOperation) exp;
return new BinaryFeatureCondition(build(bin.getLeft()), build(bin.getRight()),
"and".equals(bin.getOperator()));
}
return null;
}
}

public static class IsFeatureCondition extends FeatureCondition {

final private Feature feature;
final private boolean not;
final private ElementIdentifier conditionModule;
final private String text;

public IsFeatureCondition(FeatureReference featureRef, ElementIdentifier conditionModule) {
this(featureRef, false, conditionModule);
}

public IsFeatureCondition(FeatureReference featureRef, boolean not, ElementIdentifier conditionModule) {
this.feature = featureRef.getFeature();
this.text = NodeModelUtils.getTokenText(NodeModelUtils.getNode(featureRef));
this.conditionModule = conditionModule;
this.not = not;
}

@Override
boolean evaluate(FeatureEvaluationContext ctx) {
return not ? !ctx.isActive(feature) : ctx.isActive(feature);
}

private String toStringValue = null;

@Override
public String toString() {
if (toStringValue == null) {
toStringValue = (not ? "!" : "") + this.text;
}
return toStringValue;
}
}

public static class BinaryFeatureCondition extends FeatureCondition {
final private FeatureCondition left, right;
final private boolean isAnd;

/**
* Creates an OR binary condition
*
* @param left
* @param right
*/
public BinaryFeatureCondition(FeatureCondition left, FeatureCondition right) {
this(left, right, false);
}

/**
* @param left
* @param right
* @param isAnd <code>true</code> if it's an AND binary condition. Default is
* <code>false</code>: OR
*/
public BinaryFeatureCondition(FeatureCondition left, FeatureCondition right, boolean isAnd) {
super();
this.left = left;
this.right = right;
this.isAnd = isAnd;
}

@Override
boolean evaluate(FeatureEvaluationContext ctx) {
return isAnd ? left.evaluate(ctx) && right.evaluate(ctx) : left.evaluate(ctx) || right.evaluate(ctx);
}

@Override
public String toString() {
return left.toString() + (isAnd ? " and " : " or ") + right.toString();
}
}
}
Loading

0 comments on commit de58f96

Please sign in to comment.