Skip to content

Commit

Permalink
Merge pull request #64 from kris7t/quoted-names
Browse files Browse the repository at this point in the history
Quoted names
  • Loading branch information
kris7t authored Oct 6, 2024
2 parents 6dbcac4 + 20d3913 commit c046de6
Show file tree
Hide file tree
Showing 30 changed files with 744 additions and 126 deletions.
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[versions]
antlr = "3.2" # To retain Xtext compatibility, this mustn't be increased
apiguardian = "1.1.2"
asm = "9.7"
asm = "9.7.1"
classgraph = "4.8.177"
eclipse-commands = "3.12.200"
eclipse-contenttype = "3.9.500"
Expand Down Expand Up @@ -43,7 +43,7 @@ jetbrainsAnnotations = "25.0.0"
jetty = "12.0.14"
jmh = "1.37"
jna = "5.15.0"
junit = "5.11.1"
junit = "5.11.2"
junit4 = "4.13.2"
lsp4j = "0.23.1"
mockito = "5.14.1"
Expand Down
2 changes: 1 addition & 1 deletion subprojects/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"@docusaurus/theme-classic": "^3.5.2",
"@docusaurus/theme-common": "^3.5.2",
"@docusaurus/theme-search-algolia": "^3.5.2",
"@fontsource-variable/jetbrains-mono": "^5.1.0",
"@fontsource-variable/jetbrains-mono": "^5.1.1",
"@fontsource-variable/open-sans": "^5.1.0",
"@fontsource/open-sans": "^5.1.0",
"@hpcc-js/wasm-zstd": "^1.2.1",
Expand Down
4 changes: 2 additions & 2 deletions subprojects/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"@emotion/serialize": "^1.3.2",
"@emotion/sheet": "^1.4.0",
"@emotion/styled": "^11.13.0",
"@fontsource-variable/jetbrains-mono": "^5.1.0",
"@fontsource-variable/jetbrains-mono": "^5.1.1",
"@fontsource-variable/open-sans": "^5.1.0",
"@fontsource/open-sans": "^5.1.0",
"@hpcc-js/wasm": "^2.22.3",
Expand All @@ -52,7 +52,7 @@
"@mui/icons-material": "^6.1.2",
"@mui/material": "^6.1.2",
"@mui/system": "^6.1.2",
"@mui/x-data-grid": "^7.18.0",
"@mui/x-data-grid": "^7.19.0",
"ansi-styles": "^6.2.1",
"csstype": "^3.1.3",
"d3": "^7.9.0",
Expand Down
38 changes: 30 additions & 8 deletions subprojects/frontend/src/graph/dotSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
* SPDX-License-Identifier: EPL-2.0
*/

import { escape } from 'lodash-es';

import type {
NodeMetadata,
RelationMetadata,
Expand All @@ -17,7 +19,7 @@ const CONTAINMENT_WEIGHT = 5;
const UNKNOWN_WEIGHT_FACTOR = 0.5;

function nodeName(graph: GraphStore, metadata: NodeMetadata): string {
const name = graph.getName(metadata);
const name = escape(graph.getName(metadata));
switch (metadata.kind) {
case 'INDIVIDUAL':
return `<b>${name}</b>`;
Expand All @@ -27,7 +29,7 @@ function nodeName(graph: GraphStore, metadata: NodeMetadata): string {
}

function relationName(graph: GraphStore, metadata: RelationMetadata): string {
const name = graph.getName(metadata);
const name = escape(graph.getName(metadata));
const { detail } = metadata;
if (detail.type === 'class' && detail.abstractClass) {
return `<i>${name}</i>`;
Expand Down Expand Up @@ -120,6 +122,23 @@ function computeNodeData(graph: GraphStore): NodeData[] {
return nodeData;
}

/**
* Escape an identifier so that it can be used as an SVG element `id` and as
* an `url(#)` reference to a clip path.
*
* While colons are allowed in such IDs, quotes and percent signs are not.
*
* @param name The name to escape.
* @returns The escaped name.
*/
function encodeName(name: string): string {
return encodeURIComponent(name)
.replaceAll('%3A', ':')
.replaceAll('_', '__')
.replaceAll("'", '_27')
.replaceAll('%', '_');
}

function createNodes(
graph: GraphStore,
nodeData: NodeData[],
Expand Down Expand Up @@ -154,22 +173,22 @@ function createNodes(
const classes = classList.join(' ');
const name = nodeName(graph, node);
const border = node.kind === 'INDIVIDUAL' ? 2 : 1;
const count = scopes ? ` ${data.count}` : '';
lines.push(`n${i} [id="${node.name}", class="${classes}", label=<
const count = scopes ? `&nbsp;${data.count}` : '';
const encodedNodeName = encodeName(node.name);
lines.push(`n${i} [id="${encodedNodeName}", class="${classes}", label=<
<table border="${border}" cellborder="0" cellspacing="0" style="rounded" bgcolor="white">
<tr><td cellpadding="4.5" width="32" bgcolor="green">${name}${count}</td></tr>`);
if (data.unaryPredicates.size > 0) {
lines.push(
'<hr/><tr><td cellpadding="4.5"><table fixedsize="TRUE" align="left" border="0" cellborder="0" cellspacing="0" cellpadding="1.5">',
);
data.unaryPredicates.forEach((value, relation) => {
const encodedRelationName = `${encodedNodeName},${encodeName(relation.name)}`;
lines.push(
`<tr>
<td><img src="#${value}"/></td>
<td width="1.5"></td>
<td align="left" href="#${value}" id="${node.name},${
relation.name
},label">${relationName(graph, relation)}</td>
<td align="left" href="#${value}" id="${encodedRelationName},label">${relationName(graph, relation)}</td>
</tr>`,
);
});
Expand Down Expand Up @@ -282,6 +301,7 @@ function createRelationEdges(
}

const tuples = partialInterpretation[relation.name] ?? [];
const encodedRelation = encodeName(relation.name);
tuples.forEach(([from, to, value]) => {
const isUnknown = value === 'UNKNOWN';
if (
Expand Down Expand Up @@ -336,7 +356,9 @@ function createRelationEdges(
edgeWeight *= UNKNOWN_WEIGHT_FACTOR;
}

const id = `${fromNode.name},${toNode.name},${relation.name}`;
const encodedFrom = encodeName(fromNode.name);
const encodedTo = encodeName(toNode.name);
const id = `${encodedFrom},${encodedTo},${encodedRelation}`;
const label = getEdgeLabel(name, containment, value);
lines.push(`n${from} -> n${to} [
id="${id}",
Expand Down
11 changes: 6 additions & 5 deletions subprojects/frontend/src/language/problem.grammar
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,9 @@ ModuleName { QualifiedName }

AggregatorName { QualifiedName }

QualifiedName[implicitCompletion=true] { "::"? identifier (QualifiedNameSeparator "::" identifier)* }
ID { identifier | QuotedID }

QualifiedName[implicitCompletion=true] { "::"? ID (QualifiedNameSeparator "::" ID)* }

StarMult { Star }

Expand Down Expand Up @@ -239,10 +241,9 @@ sep1<separator, content> { content (separator content)* }

exponential { int ("e" | "E") ("+" | "-")? int }

String {
"'" (![\\'\n] | "\\" ![\n] | "\\\n")+ "'" |
"\"" (![\\"\n] | "\\" (![\n] | "\n"))* "\""
}
String { "\"" (![\\"\n] | "\\" (![\n] | "\n"))* "\"" }

QuotedID { "'" (![\\'\n] | "\\" ![\n] | "\\\n")+ "'" }

SymbolicComparisonOp {
">" | ">=" | "<" | "<=" | "==" | "!=" |
Expand Down
14 changes: 7 additions & 7 deletions subprojects/frontend/src/language/problemLanguageSupport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ const parserWithMetadata = parser.configure({
'IntMult Real': t.number,
'StarMult/Star': t.number,
String: t.string,
'RelationName/QualifiedName': t.typeName,
'DatatypeName/QualifiedName': t.keyword,
'AggregatorName/QualifiedName': t.operatorKeyword,
'RuleName/QualifiedName': t.typeName,
'AtomNodeName/QualifiedName': t.atom,
'VariableName/QualifiedName': t.variableName,
'ModuleName/QualifiedName': t.typeName,
'RelationName!': t.typeName,
'DatatypeName!': t.keyword,
'AggregatorName!': t.operatorKeyword,
'RuleName!': t.typeName,
'AtomNodeName!': t.atom,
'VariableName!': t.variableName,
'ModuleName!': t.typeName,
'{ }': t.brace,
'( )': t.paren,
'[ ]': t.squareBracket,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
package tools.refinery.language.web.semantics.metadata;

import com.google.inject.Inject;
import org.eclipse.xtext.naming.IQualifiedNameConverter;
import org.eclipse.xtext.naming.QualifiedName;
import tools.refinery.language.ide.syntaxcoloring.TypeHashProvider;
import tools.refinery.language.semantics.NodeNameProvider;
import tools.refinery.language.semantics.ProblemTrace;
Expand All @@ -27,6 +29,9 @@ public class NodeMetadataFactory {
@Inject
private TypeHashProvider typeHashProvider;

@Inject
private IQualifiedNameConverter qualifiedNameConverter;

private ProblemTrace problemTrace;
private Concreteness concreteness;
private Interpretation<InferredType> typeInterpretation;
Expand All @@ -49,7 +54,8 @@ public NodeMetadata doCreateMetadata(int nodeId, String name, String simpleName,
public NodeMetadata createFreshlyNamedMetadata(int nodeId) {
var type = getType(nodeId);
var name = getName(type, nodeId);
return doCreateMetadata(name, name, type, NodeKind.IMPLICIT);
var escapedName = qualifiedNameConverter.toString(QualifiedName.create(name));
return doCreateMetadata(escapedName, escapedName, type, NodeKind.IMPLICIT);
}

private PartialRelation getType(int nodeId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ Identifier:
NonContainmentIdentifier | "contains" | "container";

NonContainmentIdentifier:
ID | "atom" | "multi" | "contained" | "problem" | "module" |
ID | QUOTED_ID | "atom" | "multi" | "problem" | "module" |
"datatype" | "aggregator" | "decision" | "propagation" | "shadow";

Real returns ecore::EDouble:
Expand All @@ -320,3 +320,10 @@ terminal EXPONENTIAL:
@Override
terminal SL_COMMENT:
('%' | '//') !('\n' | '\r')* ('\r'? '\n')?;

@Override
terminal STRING:
'"' ( '\\' . /* 'b'|'t'|'n'|'f'|'r'|'u'|'"'|"'"|'\\' */ | !('\\'|'"') )* '"';

terminal QUOTED_ID:
"'" ( '\\' . /* 'b'|'t'|'n'|'f'|'r'|'u'|'"'|"'"|'\\' */ | !('\\'|"'") )* "'";
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
import com.google.inject.Binder;
import com.google.inject.name.Names;
import org.eclipse.xtext.conversion.IValueConverterService;
import org.eclipse.xtext.conversion.impl.AbstractIDValueConverter;
import org.eclipse.xtext.documentation.impl.AbstractMultiLineCommentProvider;
import org.eclipse.xtext.findReferences.IReferenceFinder;
import org.eclipse.xtext.linking.ILinkingService;
import org.eclipse.xtext.naming.IQualifiedNameConverter;
import org.eclipse.xtext.naming.IQualifiedNameProvider;
Expand All @@ -28,16 +30,14 @@
import org.eclipse.xtext.validation.IDiagnosticConverter;
import org.eclipse.xtext.validation.IResourceValidator;
import org.eclipse.xtext.xbase.annotations.validation.DerivedStateAwareResourceValidator;
import tools.refinery.language.conversion.IDValueConverter;
import tools.refinery.language.conversion.ProblemValueConverterService;
import tools.refinery.language.linking.ProblemLinkingService;
import tools.refinery.language.naming.ProblemQualifiedNameProvider;
import tools.refinery.language.naming.ProblemQualifiedNameConverter;
import tools.refinery.language.parser.ProblemEcoreElementFactory;
import tools.refinery.language.parser.antlr.TokenSourceInjectingProblemParser;
import tools.refinery.language.resource.ProblemLocationInFileProvider;
import tools.refinery.language.resource.ProblemResource;
import tools.refinery.language.resource.ProblemResourceDescriptionManager;
import tools.refinery.language.resource.ProblemResourceDescriptionStrategy;
import tools.refinery.language.resource.*;
import tools.refinery.language.resource.state.ProblemDerivedStateComputer;
import tools.refinery.language.scoping.ProblemGlobalScopeProvider;
import tools.refinery.language.scoping.ProblemLocalScopeProvider;
Expand Down Expand Up @@ -81,6 +81,10 @@ public Class<? extends IValueConverterService> bindIValueConverterService() {
return ProblemValueConverterService.class;
}

public Class<? extends AbstractIDValueConverter> bindAbstractIDValueConverter() {
return IDValueConverter.class;
}

@Override
public Class<? extends ILinkingService> bindILinkingService() {
return ProblemLinkingService.class;
Expand Down Expand Up @@ -116,6 +120,10 @@ public Class<? extends IDerivedStateComputer> bindIDerivedStateComputer() {
return ProblemDerivedStateComputer.class;
}

public Class<? extends IReferenceFinder> bindIReferenceFinder() {
return ProblemReferenceFinder.class;
}

@Override
public Class<? extends ILocationInFileProvider> bindILocationInFileProvider() {
return ProblemLocationInFileProvider.class;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
*
* SPDX-License-Identifier: EPL-2.0
*/
package tools.refinery.language.conversion;

import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.conversion.impl.AbstractIDValueConverter;

import java.util.Set;

public class IDValueConverter extends AbstractIDValueConverter {
@Override
protected Set<String> computeValuesToEscape(Grammar grammar) {
return Set.of();
}

@Override
protected boolean mustEscape(String value) {
// Do not escape keywords with ^, because we use single quotes instead in {@link IdentifierValueConverter}.
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
*
* SPDX-License-Identifier: EPL-2.0
*/
package tools.refinery.language.conversion;

import com.google.inject.Inject;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.conversion.IValueConverter;
import org.eclipse.xtext.conversion.ValueConverterException;
import org.eclipse.xtext.nodemodel.INode;
import tools.refinery.language.naming.NamingUtil;
import tools.refinery.language.parser.antlr.IdentifierTokenProvider;
import tools.refinery.language.services.ProblemGrammarAccess;

import java.util.LinkedHashSet;
import java.util.Set;

public class IdentifierValueConverter implements IValueConverter<String> {
private final Set<String> keywords;
private final QUOTED_IDValueConverter quotedIdValueConverter;

@Inject
public IdentifierValueConverter(
ProblemGrammarAccess grammarAccess, QUOTED_IDValueConverter quotedIdValueConverter,
IdentifierTokenProvider identifierTokenProvider) {
this.quotedIdValueConverter = quotedIdValueConverter;
quotedIdValueConverter.setRule(grammarAccess.getQUOTED_IDRule());
keywords = new LinkedHashSet<>(GrammarUtil.getAllKeywords(grammarAccess.getGrammar()));
keywords.removeAll(identifierTokenProvider.getIdentifierKeywords());
}

@Override
public String toValue(String string, INode node) throws ValueConverterException {
if (string == null) {
return null;
}
if (NamingUtil.isQuoted(string)) {
return quotedIdValueConverter.toValue(string, node);
}
return string;
}

@Override
public String toString(String value) throws ValueConverterException {
if (value == null) {
throw new ValueConverterException("Identifier may not be null.", null, null);
}
if (NamingUtil.isSimpleId(value) && !keywords.contains(value)) {
return value;
}
return quotedIdValueConverter.toString(value);
}
}
Loading

0 comments on commit c046de6

Please sign in to comment.