From ed77dc580416d64b09d2028fa273fbc9faba8d8d Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Wed, 7 Feb 2024 00:37:30 -0800 Subject: [PATCH 01/35] Serialize AST. --- .../main/java/org/lflang/LinguaFranca.xtext | 4 +- .../src/main/java/org/lflang/ast/ToSExpr.java | 744 ++++++++++++++++++ .../lflang/generator/IntegratedBuilder.java | 2 +- core/src/main/resources/lib/c/reactor-c | 2 +- .../lsp/LFLanguageServerExtension.java | 19 + 5 files changed, 767 insertions(+), 4 deletions(-) create mode 100644 core/src/main/java/org/lflang/ast/ToSExpr.java diff --git a/core/src/main/java/org/lflang/LinguaFranca.xtext b/core/src/main/java/org/lflang/LinguaFranca.xtext index d4a892d9e8..7f9e74d491 100644 --- a/core/src/main/java/org/lflang/LinguaFranca.xtext +++ b/core/src/main/java/org/lflang/LinguaFranca.xtext @@ -221,7 +221,7 @@ Watchdog: 'watchdog' name=ID '(' timeout=Expression ')' ('->' effects+=VarRefOrModeTransition (',' effects+=VarRefOrModeTransition)*)? code=Code; - + STP: 'STP' '(' value=Expression ')' code=Code; @@ -232,7 +232,7 @@ Instantiation: (attributes+=Attribute)* name=ID '=' 'new' (widthSpec=WidthSpec)? reactorClass=[ReactorDecl] ('<' typeArgs+=Type (',' typeArgs+=Type)* '>')? '(' - (parameters+=Assignment (',' parameters+=Assignment)*)? + (parameters+=Assignment (',' parameters+=Assignment)*)? ')' (('at' host=Host ';') | ';'?); Connection: diff --git a/core/src/main/java/org/lflang/ast/ToSExpr.java b/core/src/main/java/org/lflang/ast/ToSExpr.java new file mode 100644 index 0000000000..cc1bc798d8 --- /dev/null +++ b/core/src/main/java/org/lflang/ast/ToSExpr.java @@ -0,0 +1,744 @@ +package org.lflang.ast; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.emf.ecore.EObject; + +import org.lflang.ast.ToSExpr.SExpr; +import org.lflang.ast.ToSExpr.SList.SAtom; +import org.lflang.lf.Action; +import org.lflang.lf.Array; +import org.lflang.lf.ArraySpec; +import org.lflang.lf.Assignment; +import org.lflang.lf.AttrParm; +import org.lflang.lf.Attribute; +import org.lflang.lf.BracedListExpression; +import org.lflang.lf.BracketListExpression; +import org.lflang.lf.BuiltinTriggerRef; +import org.lflang.lf.Code; +import org.lflang.lf.CodeExpr; +import org.lflang.lf.Connection; +import org.lflang.lf.Deadline; +import org.lflang.lf.Element; +import org.lflang.lf.Expression; +import org.lflang.lf.Host; +import org.lflang.lf.IPV4Host; +import org.lflang.lf.IPV6Host; +import org.lflang.lf.Import; +import org.lflang.lf.ImportedReactor; +import org.lflang.lf.Initializer; +import org.lflang.lf.Input; +import org.lflang.lf.Instantiation; +import org.lflang.lf.KeyValuePair; +import org.lflang.lf.KeyValuePairs; +import org.lflang.lf.Literal; +import org.lflang.lf.Method; +import org.lflang.lf.MethodArgument; +import org.lflang.lf.Mode; +import org.lflang.lf.Model; +import org.lflang.lf.NamedHost; +import org.lflang.lf.Output; +import org.lflang.lf.Parameter; +import org.lflang.lf.ParameterReference; +import org.lflang.lf.Port; +import org.lflang.lf.Preamble; +import org.lflang.lf.Reaction; +import org.lflang.lf.Reactor; +import org.lflang.lf.ReactorDecl; +import org.lflang.lf.STP; +import org.lflang.lf.Serializer; +import org.lflang.lf.StateVar; +import org.lflang.lf.TargetDecl; +import org.lflang.lf.Time; +import org.lflang.lf.Timer; +import org.lflang.lf.TriggerRef; +import org.lflang.lf.Type; +import org.lflang.lf.TypeParm; +import org.lflang.lf.TypedVariable; +import org.lflang.lf.VarRef; +import org.lflang.lf.Variable; +import org.lflang.lf.Watchdog; +import org.lflang.lf.WidthSpec; +import org.lflang.lf.WidthTerm; +import org.lflang.lf.util.LfSwitch; + +public class ToSExpr extends LfSwitch { + +// private SExpr sList(String name, EObject... parts) { +// var ret = new ArrayList(); +// ret.add(new SAtom(name)); +// for (EObject part : parts) { +// ret.add(doSwitch(part)); +// } +// return new SList(ret); +// } + + private SExpr sList(String name, Object... parts) { + var ret = new ArrayList(); + ret.add(new SAtom(name)); + for (var part : parts) { + if (part instanceof List) { + throw new IllegalArgumentException("List was not expected"); + } + if (part instanceof EObject object) { + ret.add(doSwitch(object)); + } else if (part instanceof SExpr sExpr) { + ret.add(sExpr); + } else { + ret.add(new SAtom(String.valueOf(part))); + } + } + return new SList(ret); + } + + private SExpr sList(String name, List parts) { + var ret = new ArrayList(); + ret.add(new SAtom(name)); + for (EObject part : parts) { + ret.add(doSwitch(part)); + } + return new SList(ret); + } + +// private SExpr sList(String name, SExpr... parts) { +// var ret = new ArrayList(); +// ret.add(new SAtom(name)); +// ret.addAll(Arrays.asList(parts)); +// return new SList(ret); +// } + + @Override + public SExpr caseModel(Model object) { +// Model: +// target=TargetDecl +// (imports+=Import)* +// (preambles+=Preamble)* +// (reactors+=Reactor)+ +// ; + return sList("model", + doSwitch(object.getTarget()), + sList("preambles", object.getPreambles()), + sList("imports", object.getImports()), + sList("reactors", object.getReactors()) + ); + } + + @Override + public SExpr caseImport(Import object) { +// Import: 'import' reactorClasses+=ImportedReactor (',' reactorClasses+=ImportedReactor)* 'from' importURI=STRING ';'?; + return sList("import", new SAtom(object.getImportURI()), sList("reactors", object.getReactorClasses())); + } + + @Override + public SExpr caseReactorDecl(ReactorDecl object) { +// ReactorDecl: Reactor | ImportedReactor; + return sList("reactor-decl", + object.getName() + ); + } + + @Override + public SExpr caseImportedReactor(ImportedReactor object) { +// ImportedReactor: reactorClass=[Reactor] ('as' name=ID)?; + return sList("imported-reactor", object.getName(), object.getReactorClass().getName(), object.getReactorClass().hashCode()); + } + + @Override + public SExpr caseReactor(Reactor object) { +// Reactor: +// {Reactor} (attributes+=Attribute)* ((federated?='federated' | main?='main')? & realtime?='realtime'?) 'reactor' (name=ID)? +// ('<' typeParms+=TypeParm (',' typeParms+=TypeParm)* '>')? +// ('(' parameters+=Parameter (',' parameters+=Parameter)* ')')? +// ('at' host=Host)? +// ('extends' (superClasses+=[ReactorDecl] (',' superClasses+=[ReactorDecl])*))? +// '{' +// ( (preambles+=Preamble) +// | (stateVars+=StateVar) +// | (methods+=Method) +// | (inputs+=Input) +// | (outputs+=Output) +// | (timers+=Timer) +// | (actions+=Action) +// | (watchdogs+=Watchdog) +// | (instantiations+=Instantiation) +// | (connections+=Connection) +// | (reactions+=Reaction) +// | (modes+=Mode) +// )* '}'; + return sList("reactor", + new SAtom(object.getName()), + new SAtom(String.valueOf(object.hashCode())), + sList("attributes", object.getAttributes()), + sList("is-main", object.isMain()), + sList("is-federated", object.isFederated()), + sList("is-realtime", object.isRealtime()), + sList("typeParms", object.getTypeParms()), + sList("parameters", object.getParameters()), + sList("host", object.getHost()), + sList("extends", object.getSuperClasses()), + sList("preambles", object.getPreambles()), + sList("state", object.getStateVars()), + sList("methods", object.getMethods()), + sList("inputs", object.getInputs()), + sList("outputs", object.getOutputs()), + sList("timers", object.getTimers()), + sList("actions", object.getActions()), + sList("watchdogs", object.getWatchdogs()), + sList("instantiations", object.getInstantiations()), + sList("connections", object.getConnections()), + sList("reactions", object.getReactions()), + sList("modes", object.getModes()) + ); + } + + @Override + public SExpr caseTypeParm(TypeParm object) { +// TypeParm: +// literal=TypeExpr | code=Code + return sList("type-parm", object.getLiteral(), object.getCode()); + } + + @Override + public SExpr caseTargetDecl(TargetDecl object) { +// TargetDecl: +// 'target' name=ID (config=KeyValuePairs)? ';'?; + return sList("target-decl", object.getName(), object.getConfig()); + } + + @Override + public SExpr caseStateVar(StateVar object) { +// StateVar: +// (attributes+=Attribute)* +// (reset?='reset')? 'state' name=ID +// (':' type=Type)? +// init=Initializer? +// ';'? +// ; + return sList("state", object.getName(), + sList("attributes", object.getAttributes()), + sList("is-reset", object.isReset()), + object.getType(), object.getInit()); + } + + @Override + public SExpr caseInitializer(Initializer object) { +// Initializer: +// parens?='(' (exprs+=Expression (',' exprs+=Expression)* (trailingComma?=',')?)? ')' +// | braces?='{' (exprs+=Expression (',' exprs+=Expression)* (trailingComma?=',')?)? '}' +// | assign?='=' exprs+=Expression +// ; + return sList("initializer", + sList("exprs", object.getExprs()), + sList("is-braces", object.isBraces()), + sList("is-parens", object.isParens()), + sList("is-assign", object.isAssign()), + sList("is-trailing-comma", object.isTrailingComma()) + ); + } + + @Override + public SExpr caseMethod(Method object) { +// const?='const'? 'method' name=ID +// '(' (arguments+=MethodArgument (',' arguments+=MethodArgument)*)? ')' +// (':' return=Type)? +// code=Code +// ';'? + return sList("method", + object.getName(), + sList("is-const", object.isConst()), + sList("arguments", object.getArguments()), + sList("return", object.getReturn()), + object.getCode() + ); + } + + @Override + public SExpr caseMethodArgument(MethodArgument object) { +// MethodArgument: +// name=ID (':' type=Type)? + return sList("method-argument", object.getName(), sList("type", object.getType())); + } + + @Override + public SExpr caseInput(Input object) { +// Input: +// (attributes+=Attribute)* mutable?='mutable'? 'input' (widthSpec=WidthSpec)? name=ID (':' type=Type)? ';'?; + return sList("input", + object.getName(), + sList("attributes", object.getAttributes()), + sList("is-mutable", object.isMutable()), + object.getWidthSpec(), + object.getType() + ); + } + + @Override + public SExpr caseOutput(Output object) { +// Output: +// (attributes+=Attribute)* 'output' (widthSpec=WidthSpec)? name=ID (':' type=Type)? ';'?; + return sList("output", + object.getName(), + sList("attributes", object.getAttributes()), + object.getWidthSpec(), + object.getType() + ); + } + + @Override + public SExpr caseTimer(Timer object) { +// Timer: +// (attributes+=Attribute)* 'timer' name=ID ('(' offset=Expression (',' period=Expression)? ')')? ';'?; + return sList("timer", + object.getName(), + sList("attributes", object.getAttributes()), + sList("offset", object.getOffset()), + sList("period", object.getPeriod()) + ); + } + + @Override + public SExpr caseMode(Mode object) { +// Mode: +// {Mode} (initial?='initial')? 'mode' (name=ID)? +// '{' ( +// (stateVars+=StateVar) | +// (timers+=Timer) | +// (actions+=Action) | +// (watchdogs+=Watchdog) | +// (instantiations+=Instantiation) | +// (connections+=Connection) | +// (reactions+=Reaction) +// )* '}'; + return sList("mode", + object.getName(), + sList("is-initial", object.isInitial()), + sList("state", object.getStateVars()), + sList("timers", object.getTimers()), + sList("actions", object.getActions()), + sList("watchdogs", object.getWatchdogs()), + sList("instantiations", object.getInstantiations()), + sList("connections", object.getConnections()), + sList("reactions", object.getReactions()) + ); + } + + @Override + public SExpr caseAction(Action object) { +// Action: +// (attributes+=Attribute)* +// (origin=ActionOrigin)? 'action' name=ID +// ('(' minDelay=Expression (',' minSpacing=Expression (',' policy=STRING)? )? ')')? +// (':' type=Type)? ';'?; + return sList("action", + object.getName(), + sList("attributes", object.getAttributes()), + object.getOrigin(), + sList("min-delay", object.getMinDelay()), + sList("min-spacing", object.getMinSpacing()), + sList("policy", object.getPolicy()), + object.getType() + ); + } + + @Override + public SExpr caseReaction(Reaction object) { +// Reaction: +// (attributes+=Attribute)* +// (('reaction') | mutation ?= 'mutation') +// (name=ID)? +// ('(' (triggers+=TriggerRef (',' triggers+=TriggerRef)*)? ')') +// ( => sources+=VarRef (',' sources+=VarRef)*)? +// ('->' effects+=VarRefOrModeTransition (',' effects+=VarRefOrModeTransition)*)? +// (code=Code)? (stp=STP)? (deadline=Deadline)? (delimited?=';')? +// ; + return sList( + "reaction", + object.getName(), + sList("attributes", object.getAttributes()), + sList("is-mutation", object.isMutation()), + sList("triggers", object.getTriggers()), + sList("sources", object.getSources()), + sList("effects", object.getEffects()), + object.getCode(), + object.getStp(), + object.getDeadline(), + sList("is-delimited", object.isDelimited()) + ); + } + + @Override + public SExpr caseTriggerRef(TriggerRef object) { +// TriggerRef: +// BuiltinTriggerRef | VarRef; +// return sList("trigger-ref", object.getVariable()); + throw new RuntimeException("not implemented"); + } + + @Override + public SExpr caseBuiltinTriggerRef(BuiltinTriggerRef object) { +// BuiltinTriggerRef: +// type = BuiltinTrigger; + return sList("builtin-trigger-ref", object.getType()); + } + + @Override + public SExpr caseDeadline(Deadline object) { +// Deadline: +// 'deadline' '(' delay=Expression ')' code=Code; + return sList("deadline", object.getDelay(), object.getCode()); + } + + @Override + public SExpr caseWatchdog(Watchdog object) { +// Watchdog: +// 'watchdog' name=ID '(' timeout=Expression ')' +// ('->' effects+=VarRefOrModeTransition (',' effects+=VarRefOrModeTransition)*)? +// code=Code; + return sList("watchdog", + object.getName(), + sList("timeout", object.getTimeout()), + sList("effects", object.getEffects()), + object.getCode() + ); + } + + @Override + public SExpr caseSTP(STP object) { +// STP: +// 'STP' '(' value=Expression ')' code=Code; + return sList("stp", object.getValue(), object.getCode()); + } + + @Override + public SExpr casePreamble(Preamble object) { +// Preamble: +// (visibility=Visibility)? 'preamble' code=Code; + return sList("preamble", object.getVisibility(), object.getCode()); + } + + @Override + public SExpr caseInstantiation(Instantiation object) { +// Instantiation: +// (attributes+=Attribute)* +// name=ID '=' 'new' (widthSpec=WidthSpec)? +// reactorClass=[ReactorDecl] ('<' typeArgs+=Type (',' typeArgs+=Type)* '>')? '(' +// (parameters+=Assignment (',' parameters+=Assignment)*)? +// ')' (('at' host=Host ';') | ';'?); + return sList("instantiation", + object.getName(), + sList("attributes", object.getAttributes()), + object.getWidthSpec(), + object.getReactorClass(), + sList("typeArgs", object.getTypeArgs()), + sList("parameters", object.getParameters()), + object.getHost() + ); + } + + @Override + public SExpr caseConnection(Connection object) { +// Connection: +// ((leftPorts += VarRef (',' leftPorts += VarRef)*) +// | ( '(' leftPorts += VarRef (',' leftPorts += VarRef)* ')' iterated ?= '+'?)) +// ('->' | physical?='~>') +// rightPorts += VarRef (',' rightPorts += VarRef)* +// ('after' delay=Expression)? +// (serializer=Serializer)? +// ';'? + return sList("connection", + sList("left-ports", object.getLeftPorts()), + sList("right-ports", object.getRightPorts()), + sList("is-iterated", object.isIterated()), + sList("is-physical", object.isPhysical()), + sList("delay", object.getDelay()), + object.getSerializer() + ); + } + + @Override + public SExpr caseSerializer(Serializer object) { +// Serializer: +// 'serializer' type=STRING +// ; + return sList("serializer", object.getType()); + } + + @Override + public SExpr caseAttribute(Attribute object) { +// Attribute: +// '@' attrName=ID ('(' (attrParms+=AttrParm (',' attrParms+=AttrParm)* ','?)? ')')? +// ; + return sList("attribute", object.getAttrName(), sList("attr-parms", object.getAttrParms())); + } + + @Override + public SExpr caseAttrParm(AttrParm object) { +// AttrParm: +// (name=ID '=')? value=Literal; + return sList("attr-parm", object.getName(), object.getValue()); + } + + @Override + public SExpr caseKeyValuePairs(KeyValuePairs object) { +// KeyValuePairs: +// {KeyValuePairs} '{' (pairs+=KeyValuePair (',' (pairs+=KeyValuePair))* ','?)? '}'; + return sList("key-value-pairs", object.getPairs()); + } + + @Override + public SExpr caseKeyValuePair(KeyValuePair object) { +// KeyValuePair: +// name=(Kebab|STRING) ':' value=Element; + return sList("pair", object.getName(), object.getValue()); + } + + @Override + public SExpr caseArray(Array object) { +// Array: // todo allow empty array in grammar, replace with validator error +// '[' elements+=Element (',' (elements+=Element))* ','? ']'; + return sList("array", object.getElements()); + } + + @Override + public SExpr caseElement(Element object) { +// Element: +// keyvalue=KeyValuePairs +// | array=Array +// | literal=Literal +// | (time=INT unit=TimeUnit) +// | id=Path; + return sList("element", object.getKeyvalue(), object.getArray(), object.getLiteral(), sList("time", object.getTime(), object.getUnit()), object.getId()); + } + + @Override + public SExpr caseTypedVariable(TypedVariable object) { +// TypedVariable: +// Port | Action +// ; + throw new RuntimeException("not implemented"); + } + + @Override + public SExpr caseVariable(Variable object) { +// Variable: +// TypedVariable | Timer | Mode | Watchdog; + throw new RuntimeException("not implemented"); + } + + @Override + public SExpr caseVarRef(VarRef object) { +// VarRef: +// (variable=[Variable] | container=[Instantiation] '.' variable=[Variable] +// | interleaved?='interleaved' '(' (variable=[Variable] | container=[Instantiation] '.' variable=[Variable]) ')') ('as' (alias=ID))? +// ; + return sList("var-ref", object.getVariable(), object.getContainer(), sList("is-interleaved", object.isInterleaved()), sList("alias", object.getAlias())); + } + + @Override + public SExpr caseAssignment(Assignment object) { +// Assignment: +// lhs=[Parameter] +// rhs=AssignmentInitializer +// ; + return sList("assignment", object.getLhs(), object.getRhs()); + } + + @Override + public SExpr caseParameter(Parameter object) { +// Parameter: +// (attributes+=Attribute)* +// name=ID (':' type=Type)? +// init=Initializer? +// ; + return sList("parameter", + object.getName(), + sList("attributes", object.getAttributes()), + object.getType(), + object.getInit() + ); + } + + @Override + public SExpr caseExpression(Expression object) { +// Expression: +// {Literal} literal=Literal +// | Time +// | ParameterReference +// | {CodeExpr} code=Code +// | BracedListExpression +// | BracketListExpression +// ; + throw new RuntimeException("not implemented"); +// return sList("expression", object.getLiteral(), object.getTime(), object.getParameter(), object.getCode(), object.getBracedListExpression(), object.getBracketListExpression()); + } + + @Override + public SExpr caseBracedListExpression(BracedListExpression object) { +// BracedListExpression: +// '{' {BracedListExpression} (items+=Expression (',' items+=Expression)*)? ','? '}' +// ; + return sList("braced-list-expression", object.getItems()); + } + + @Override + public SExpr caseBracketListExpression(BracketListExpression object) { +// BracketListExpression: +// '[' {BracketListExpression} (items+=Expression (',' items+=Expression)*)? ','? ']' +// ; + return sList("bracket-list-expression", object.getItems()); + } + + @Override + public SExpr caseParameterReference(ParameterReference object) { +// ParameterReference: +// parameter=[Parameter] +// ; + return sList("parameter-reference", object.getParameter()); + } + + @Override + public SExpr caseTime(Time object) { +// Time: +// (interval=INT unit=TimeUnit) +// ; + return sList("time", object.getInterval(), object.getUnit()); + } + + @Override + public SExpr casePort(Port object) { +// Port: +// Input | Output; + throw new RuntimeException("not implemented"); + } + + @Override + public SExpr caseType(Type object) { +// Type: +// time?='time' (arraySpec=ArraySpec)? +// | id=DottedName ('<' typeArgs+=Type (',' typeArgs+=Type)* '>')? (stars+='*')* (arraySpec=ArraySpec)? +// | code=Code +// ; + return sList("type", object.getId(), sList("stars", object.getStars().size()), object.getArraySpec(), object.getCode(), sList("is-time", object.isTime())); + } + + @Override + public SExpr caseArraySpec(ArraySpec object) { +// ArraySpec: +// '[' ( ofVariableLength?=']' | length=INT ']' ); + return sList("array-spec", sList("length", object.getLength()), sList("is-of-variable-length", object.isOfVariableLength())); + } + + @Override + public SExpr caseWidthSpec(WidthSpec object) { +// WidthSpec: +// '[' ( ofVariableLength?=']' | (terms+=WidthTerm) ('+' terms+=WidthTerm)* ']' ); + return sList("width-spec", sList("terms", object.getTerms()), sList("is-of-variable-length", object.isOfVariableLength())); + } + + @Override + public SExpr caseWidthTerm(WidthTerm object) { +// WidthTerm: +// width=INT +// | parameter=[Parameter] +// | 'widthof(' port=VarRef ')' +// | code=Code; + return sList("width-term", sList("width", object.getWidth()), object.getParameter(), sList("width-of", object.getPort()), object.getCode()); + } + + @Override + public SExpr caseIPV4Host(IPV4Host object) { +// IPV4Host: +// (user=Kebab '@')? addr=IPV4Addr (':' port=INT)? +// ; + return sList("ipv4-host", sList("user", object.getUser()), object.getAddr(), sList("port", object.getPort())); + } + + @Override + public SExpr caseIPV6Host(IPV6Host object) { +// IPV6Host: +// ('[' (user=Kebab '@')? addr=IPV6Addr ']' (':' port=INT)?) +// ; + return sList("ipv6-host", sList("user", object.getUser()), object.getAddr(), sList("port", object.getPort())); + } + + @Override + public SExpr caseNamedHost(NamedHost object) { +// NamedHost: +// (user=Kebab '@')? addr=HostName (':' port=INT)? +// ; + return sList("named-host", sList("user", object.getUser()), object.getAddr(), sList("port", object.getPort())); + } + + @Override + public SExpr caseHost(Host object) { +// Host: +// IPV4Host | IPV6Host | NamedHost +// ; + // return super.caseHost(object); + throw new RuntimeException("not implemented"); + } + + @Override + public SExpr caseCode(Code object) { +// Code: +// // {Code} '{=' (tokens+=Token)* '=}' +// {Code} '{=' body=Body '=}' +// ; + return sList("code", object.getBody()); + } + + @Override + public SExpr caseLiteral(Literal object) { +// Literal: +// STRING | CHAR_LIT | SignedFloat | SignedInt | Boolean +// ; + return sList("literal", object.getLiteral()); + } + + @Override + public SExpr caseCodeExpr(CodeExpr object) { + return doSwitch(object.getCode()); + } + + @Override + public SExpr defaultCase(EObject object) { + throw new RuntimeException("this should be unreachable"); + } + + public interface SExpr {} + public record SList(List parts) implements SExpr { + @Override + public String toString() { + var ret = new StringBuilder(); + ret.append('('); + var flat = parts.stream().allMatch(it -> it instanceof SAtom); + var first = true; + for (var part : parts) { + if (!first) { + ret.append(','); + if (!flat) { + ret.append('\n'); + } else { + ret.append(' '); + } + } + var s = part.toString(); + if (!flat && !first) { + s = s.indent(1); + } + ret.append(s); + first = false; + } + ret.append(')'); + return ret.toString(); + } + public record SAtom(String s) implements SExpr { + + @Override + public String toString() { + return "\"" + s.replaceAll("\"", "\uD83C\uDCA0") + "\""; + } + } + } +} diff --git a/core/src/main/java/org/lflang/generator/IntegratedBuilder.java b/core/src/main/java/org/lflang/generator/IntegratedBuilder.java index 1e88a15817..832279dc98 100644 --- a/core/src/main/java/org/lflang/generator/IntegratedBuilder.java +++ b/core/src/main/java/org/lflang/generator/IntegratedBuilder.java @@ -123,7 +123,7 @@ private GeneratorResult doGenerate( * @param uri The URI of a Lingua Franca file. * @return The resource corresponding to {@code uri}. */ - private Resource getResource(URI uri) { + public Resource getResource(URI uri) { return resourceSetProvider.get().getResource(uri, true); } diff --git a/core/src/main/resources/lib/c/reactor-c b/core/src/main/resources/lib/c/reactor-c index 7427d987d7..4a6b4a233d 160000 --- a/core/src/main/resources/lib/c/reactor-c +++ b/core/src/main/resources/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 7427d987d725111141139819c5341921edd0fef1 +Subproject commit 4a6b4a233df05fd58775fbcb6f7564f944f408f1 diff --git a/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java b/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java index 18de51f482..9b6edf4375 100644 --- a/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java +++ b/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java @@ -1,6 +1,7 @@ package org.lflang.diagram.lsp; import java.util.ArrayList; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import org.eclipse.emf.common.util.URI; import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; @@ -10,6 +11,7 @@ import org.eclipse.xtext.ide.server.ILanguageServerExtension; import org.lflang.LFRuntimeModule; import org.lflang.LFStandaloneSetup; +import org.lflang.ast.ToSExpr; import org.lflang.generator.GeneratorResult; import org.lflang.generator.GeneratorResult.Status; import org.lflang.generator.IntegratedBuilder; @@ -40,6 +42,23 @@ public void setClient(LanguageClient client) { this.client = client; } + @JsonRequest("parser/ast") + public CompletableFuture getAst(String uri) { + return CompletableFuture.supplyAsync(() -> { + URI parsedUri; + try { + parsedUri = URI.createFileURI(new java.net.URI(uri).getPath()); + } catch (java.net.URISyntaxException e) { + System.err.println(e); + return "LF language server failed to get AST because the URI was invalid"; + } + var model = builder.getResource(parsedUri).getContents().get(0); // FIXME: if the resource has syntax errors this should fail + var toSExpr = new ToSExpr(); + var sExpr = toSExpr.doSwitch(model); + return sExpr.toString(); + }); + } + /** * Handle a request for a complete build of the Lingua Franca file specified by {@code uri}. * From bd393a48d454879117cb00a507466c444cfdb4ae Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Wed, 7 Feb 2024 18:49:35 -0800 Subject: [PATCH 02/35] Add metadata and format. --- .../src/main/java/org/lflang/ast/ToSExpr.java | 1463 +++++++++-------- .../lsp/LFLanguageServerExtension.java | 32 +- 2 files changed, 814 insertions(+), 681 deletions(-) diff --git a/core/src/main/java/org/lflang/ast/ToSExpr.java b/core/src/main/java/org/lflang/ast/ToSExpr.java index cc1bc798d8..d37cdf70cd 100644 --- a/core/src/main/java/org/lflang/ast/ToSExpr.java +++ b/core/src/main/java/org/lflang/ast/ToSExpr.java @@ -2,11 +2,18 @@ import java.util.ArrayList; import java.util.List; - +import java.util.Optional; +import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; - +import org.eclipse.xtext.nodemodel.INode; +import org.eclipse.xtext.nodemodel.util.NodeModelUtils; +import org.eclipse.xtext.util.LineAndColumn; import org.lflang.ast.ToSExpr.SExpr; +import org.lflang.ast.ToSExpr.SList.Metadata; import org.lflang.ast.ToSExpr.SList.SAtom; +import org.lflang.ast.ToSExpr.SList.Sym; +import org.lflang.generator.Position; +import org.lflang.generator.Range; import org.lflang.lf.Action; import org.lflang.lf.Array; import org.lflang.lf.ArraySpec; @@ -65,680 +72,802 @@ public class ToSExpr extends LfSwitch { -// private SExpr sList(String name, EObject... parts) { -// var ret = new ArrayList(); -// ret.add(new SAtom(name)); -// for (EObject part : parts) { -// ret.add(doSwitch(part)); -// } -// return new SList(ret); -// } - - private SExpr sList(String name, Object... parts) { - var ret = new ArrayList(); - ret.add(new SAtom(name)); - for (var part : parts) { - if (part instanceof List) { - throw new IllegalArgumentException("List was not expected"); - } - if (part instanceof EObject object) { - ret.add(doSwitch(object)); - } else if (part instanceof SExpr sExpr) { - ret.add(sExpr); - } else { - ret.add(new SAtom(String.valueOf(part))); - } + @Override + protected SExpr doSwitch(int classifierID, EObject theEObject) { + var node = NodeModelUtils.getNode(theEObject); + var range = getLfRange(theEObject); + var metadata = + new Metadata( + theEObject.eResource().getURI(), + node.getTotalOffset(), + range.getStartInclusive().getZeroBasedLine(), + range.getStartInclusive().getZeroBasedColumn(), + range.getEndExclusive().getZeroBasedLine(), + range.getEndExclusive().getZeroBasedColumn()); + var ret = super.doSwitch(classifierID, theEObject); + ret.setMetadata(metadata); + return ret; + } + + private static Range getLfRange(EObject astNode) { + final INode node = NodeModelUtils.getNode(astNode); + final LineAndColumn oneBasedLfLineAndColumn = + NodeModelUtils.getLineAndColumn(node, node.getTotalOffset()); + Position lfStart = + Position.fromOneBased( + oneBasedLfLineAndColumn.getLine(), oneBasedLfLineAndColumn.getColumn()); + return new Range(lfStart, lfStart.plus(node.getText())); + } + + private SExpr sym(String name) { + return new SAtom<>(new Sym(name)); + } + + private SExpr sList(String name, Object... parts) { + var ret = new ArrayList(); + ret.add(sym(name)); + for (var part : parts) { + if (part instanceof List) { + throw new IllegalArgumentException("List was not expected"); + } + if (part instanceof EObject object) { + ret.add(doSwitch(object)); + } else if (part instanceof SExpr sExpr) { + ret.add(sExpr); + } else { + ret.add(new SAtom<>(String.valueOf(part))); + } + } + return new SList(ret); + } + + private SExpr sList(String name, List parts) { + var ret = new ArrayList(); + ret.add(sym(name)); + for (EObject part : parts) { + ret.add(doSwitch(part)); + } + return new SList(ret); + } + + @Override + public SExpr caseModel(Model object) { + // Model: + // target=TargetDecl + // (imports+=Import)* + // (preambles+=Preamble)* + // (reactors+=Reactor)+ + // ; + return sList( + "model", + doSwitch(object.getTarget()), + sList("preambles", object.getPreambles()), + sList("imports", object.getImports()), + sList("reactors", object.getReactors())); + } + + @Override + public SExpr caseImport(Import object) { + // Import: 'import' reactorClasses+=ImportedReactor (',' + // reactorClasses+=ImportedReactor)* 'from' importURI=STRING ';'?; + return sList( + "import", + new SAtom<>(object.getImportURI()), + sList("reactors", object.getReactorClasses())); + } + + @Override + public SExpr caseReactorDecl(ReactorDecl object) { + // ReactorDecl: Reactor | ImportedReactor; + return sList("reactor-decl", object.getName()); + } + + @Override + public SExpr caseImportedReactor(ImportedReactor object) { + // ImportedReactor: reactorClass=[Reactor] ('as' name=ID)?; + return sList( + "imported-reactor", + object.getName(), + object.getReactorClass().getName(), + object.getReactorClass().hashCode()); + } + + @Override + public SExpr caseReactor(Reactor object) { + // Reactor: + // {Reactor} (attributes+=Attribute)* ((federated?='federated' | main?='main')? & + // realtime?='realtime'?) 'reactor' (name=ID)? + // ('<' typeParms+=TypeParm (',' typeParms+=TypeParm)* '>')? + // ('(' parameters+=Parameter (',' parameters+=Parameter)* ')')? + // ('at' host=Host)? + // ('extends' (superClasses+=[ReactorDecl] (',' superClasses+=[ReactorDecl])*))? + // '{' + // ( (preambles+=Preamble) + // | (stateVars+=StateVar) + // | (methods+=Method) + // | (inputs+=Input) + // | (outputs+=Output) + // | (timers+=Timer) + // | (actions+=Action) + // | (watchdogs+=Watchdog) + // | (instantiations+=Instantiation) + // | (connections+=Connection) + // | (reactions+=Reaction) + // | (modes+=Mode) + // )* '}'; + return sList( + "reactor", + new SAtom<>(object.getName()), + new SAtom<>(object.hashCode()), + sList("attributes", object.getAttributes()), + sList("is-main", object.isMain()), + sList("is-federated", object.isFederated()), + sList("is-realtime", object.isRealtime()), + sList("typeParms", object.getTypeParms()), + sList("parameters", object.getParameters()), + sList("host", object.getHost()), + sList("extends", object.getSuperClasses()), + sList("preambles", object.getPreambles()), + sList("state", object.getStateVars()), + sList("methods", object.getMethods()), + sList("inputs", object.getInputs()), + sList("outputs", object.getOutputs()), + sList("timers", object.getTimers()), + sList("actions", object.getActions()), + sList("watchdogs", object.getWatchdogs()), + sList("instantiations", object.getInstantiations()), + sList("connections", object.getConnections()), + sList("reactions", object.getReactions()), + sList("modes", object.getModes())); + } + + @Override + public SExpr caseTypeParm(TypeParm object) { + // TypeParm: + // literal=TypeExpr | code=Code + return sList("type-parm", object.getLiteral(), object.getCode()); + } + + @Override + public SExpr caseTargetDecl(TargetDecl object) { + // TargetDecl: + // 'target' name=ID (config=KeyValuePairs)? ';'?; + return sList("target-decl", object.getName(), object.getConfig()); + } + + @Override + public SExpr caseStateVar(StateVar object) { + // StateVar: + // (attributes+=Attribute)* + // (reset?='reset')? 'state' name=ID + // (':' type=Type)? + // init=Initializer? + // ';'? + // ; + return sList( + "state", + object.getName(), + sList("attributes", object.getAttributes()), + sList("is-reset", object.isReset()), + object.getType(), + object.getInit()); + } + + @Override + public SExpr caseInitializer(Initializer object) { + // Initializer: + // parens?='(' (exprs+=Expression (',' exprs+=Expression)* (trailingComma?=',')?)? ')' + // | braces?='{' (exprs+=Expression (',' exprs+=Expression)* (trailingComma?=',')?)? + // '}' + // | assign?='=' exprs+=Expression + // ; + return sList( + "initializer", + sList("exprs", object.getExprs()), + sList("is-braces", object.isBraces()), + sList("is-parens", object.isParens()), + sList("is-assign", object.isAssign()), + sList("is-trailing-comma", object.isTrailingComma())); + } + + @Override + public SExpr caseMethod(Method object) { + // const?='const'? 'method' name=ID + // '(' (arguments+=MethodArgument (',' arguments+=MethodArgument)*)? ')' + // (':' return=Type)? + // code=Code + // ';'? + return sList( + "method", + object.getName(), + sList("is-const", object.isConst()), + sList("arguments", object.getArguments()), + sList("return", object.getReturn()), + object.getCode()); + } + + @Override + public SExpr caseMethodArgument(MethodArgument object) { + // MethodArgument: + // name=ID (':' type=Type)? + return sList("method-argument", object.getName(), sList("type", object.getType())); + } + + @Override + public SExpr caseInput(Input object) { + // Input: + // (attributes+=Attribute)* mutable?='mutable'? 'input' (widthSpec=WidthSpec)? name=ID + // (':' type=Type)? ';'?; + return sList( + "input", + object.getName(), + sList("attributes", object.getAttributes()), + sList("is-mutable", object.isMutable()), + object.getWidthSpec(), + object.getType()); + } + + @Override + public SExpr caseOutput(Output object) { + // Output: + // (attributes+=Attribute)* 'output' (widthSpec=WidthSpec)? name=ID (':' type=Type)? + // ';'?; + return sList( + "output", + object.getName(), + sList("attributes", object.getAttributes()), + object.getWidthSpec(), + object.getType()); + } + + @Override + public SExpr caseTimer(Timer object) { + // Timer: + // (attributes+=Attribute)* 'timer' name=ID ('(' offset=Expression (',' + // period=Expression)? ')')? ';'?; + return sList( + "timer", + object.getName(), + sList("attributes", object.getAttributes()), + sList("offset", object.getOffset()), + sList("period", object.getPeriod())); + } + + @Override + public SExpr caseMode(Mode object) { + // Mode: + // {Mode} (initial?='initial')? 'mode' (name=ID)? + // '{' ( + // (stateVars+=StateVar) | + // (timers+=Timer) | + // (actions+=Action) | + // (watchdogs+=Watchdog) | + // (instantiations+=Instantiation) | + // (connections+=Connection) | + // (reactions+=Reaction) + // )* '}'; + return sList( + "mode", + object.getName(), + sList("is-initial", object.isInitial()), + sList("state", object.getStateVars()), + sList("timers", object.getTimers()), + sList("actions", object.getActions()), + sList("watchdogs", object.getWatchdogs()), + sList("instantiations", object.getInstantiations()), + sList("connections", object.getConnections()), + sList("reactions", object.getReactions())); + } + + @Override + public SExpr caseAction(Action object) { + // Action: + // (attributes+=Attribute)* + // (origin=ActionOrigin)? 'action' name=ID + // ('(' minDelay=Expression (',' minSpacing=Expression (',' policy=STRING)? )? ')')? + // (':' type=Type)? ';'?; + return sList( + "action", + object.getName(), + sList("attributes", object.getAttributes()), + object.getOrigin(), + sList("min-delay", object.getMinDelay()), + sList("min-spacing", object.getMinSpacing()), + sList("policy", object.getPolicy()), + object.getType()); + } + + @Override + public SExpr caseReaction(Reaction object) { + // Reaction: + // (attributes+=Attribute)* + // (('reaction') | mutation ?= 'mutation') + // (name=ID)? + // ('(' (triggers+=TriggerRef (',' triggers+=TriggerRef)*)? ')') + // ( => sources+=VarRef (',' sources+=VarRef)*)? + // ('->' effects+=VarRefOrModeTransition (',' effects+=VarRefOrModeTransition)*)? + // (code=Code)? (stp=STP)? (deadline=Deadline)? (delimited?=';')? + // ; + return sList( + "reaction", + object.getName(), + sList("attributes", object.getAttributes()), + sList("is-mutation", object.isMutation()), + sList("triggers", object.getTriggers()), + sList("sources", object.getSources()), + sList("effects", object.getEffects()), + object.getCode(), + object.getStp(), + object.getDeadline(), + sList("is-delimited", object.isDelimited())); + } + + @Override + public SExpr caseTriggerRef(TriggerRef object) { + // TriggerRef: + // BuiltinTriggerRef | VarRef; + // return sList("trigger-ref", object.getVariable()); + throw new RuntimeException("not implemented"); + } + + @Override + public SExpr caseBuiltinTriggerRef(BuiltinTriggerRef object) { + // BuiltinTriggerRef: + // type = BuiltinTrigger; + return sList("builtin-trigger-ref", object.getType()); + } + + @Override + public SExpr caseDeadline(Deadline object) { + // Deadline: + // 'deadline' '(' delay=Expression ')' code=Code; + return sList("deadline", object.getDelay(), object.getCode()); + } + + @Override + public SExpr caseWatchdog(Watchdog object) { + // Watchdog: + // 'watchdog' name=ID '(' timeout=Expression ')' + // ('->' effects+=VarRefOrModeTransition (',' effects+=VarRefOrModeTransition)*)? + // code=Code; + return sList( + "watchdog", + object.getName(), + sList("timeout", object.getTimeout()), + sList("effects", object.getEffects()), + object.getCode()); + } + + @Override + public SExpr caseSTP(STP object) { + // STP: + // 'STP' '(' value=Expression ')' code=Code; + return sList("stp", object.getValue(), object.getCode()); + } + + @Override + public SExpr casePreamble(Preamble object) { + // Preamble: + // (visibility=Visibility)? 'preamble' code=Code; + return sList("preamble", object.getVisibility(), object.getCode()); + } + + @Override + public SExpr caseInstantiation(Instantiation object) { + // Instantiation: + // (attributes+=Attribute)* + // name=ID '=' 'new' (widthSpec=WidthSpec)? + // reactorClass=[ReactorDecl] ('<' typeArgs+=Type (',' typeArgs+=Type)* '>')? '(' + // (parameters+=Assignment (',' parameters+=Assignment)*)? + // ')' (('at' host=Host ';') | ';'?); + return sList( + "instantiation", + object.getName(), + sList("attributes", object.getAttributes()), + object.getWidthSpec(), + object.getReactorClass(), + sList("typeArgs", object.getTypeArgs()), + sList("parameters", object.getParameters()), + object.getHost()); + } + + @Override + public SExpr caseConnection(Connection object) { + // Connection: + // ((leftPorts += VarRef (',' leftPorts += VarRef)*) + // | ( '(' leftPorts += VarRef (',' leftPorts += VarRef)* ')' iterated ?= '+'?)) + // ('->' | physical?='~>') + // rightPorts += VarRef (',' rightPorts += VarRef)* + // ('after' delay=Expression)? + // (serializer=Serializer)? + // ';'? + return sList( + "connection", + sList("left-ports", object.getLeftPorts()), + sList("right-ports", object.getRightPorts()), + sList("is-iterated", object.isIterated()), + sList("is-physical", object.isPhysical()), + sList("delay", object.getDelay()), + object.getSerializer()); + } + + @Override + public SExpr caseSerializer(Serializer object) { + // Serializer: + // 'serializer' type=STRING + // ; + return sList("serializer", object.getType()); + } + + @Override + public SExpr caseAttribute(Attribute object) { + // Attribute: + // '@' attrName=ID ('(' (attrParms+=AttrParm (',' attrParms+=AttrParm)* ','?)? ')')? + // ; + return sList("attribute", object.getAttrName(), sList("attr-parms", object.getAttrParms())); + } + + @Override + public SExpr caseAttrParm(AttrParm object) { + // AttrParm: + // (name=ID '=')? value=Literal; + return sList("attr-parm", object.getName(), object.getValue()); + } + + @Override + public SExpr caseKeyValuePairs(KeyValuePairs object) { + // KeyValuePairs: + // {KeyValuePairs} '{' (pairs+=KeyValuePair (',' (pairs+=KeyValuePair))* ','?)? '}'; + return sList("key-value-pairs", object.getPairs()); + } + + @Override + public SExpr caseKeyValuePair(KeyValuePair object) { + // KeyValuePair: + // name=(Kebab|STRING) ':' value=Element; + return sList("pair", object.getName(), object.getValue()); + } + + @Override + public SExpr caseArray(Array object) { + // Array: // todo allow empty array in grammar, replace with validator error + // '[' elements+=Element (',' (elements+=Element))* ','? ']'; + return sList("array", object.getElements()); + } + + @Override + public SExpr caseElement(Element object) { + // Element: + // keyvalue=KeyValuePairs + // | array=Array + // | literal=Literal + // | (time=INT unit=TimeUnit) + // | id=Path; + return sList( + "element", + object.getKeyvalue(), + object.getArray(), + object.getLiteral(), + sList("time", object.getTime(), object.getUnit()), + object.getId()); + } + + @Override + public SExpr caseTypedVariable(TypedVariable object) { + // TypedVariable: + // Port | Action + // ; + throw new RuntimeException("not implemented"); + } + + @Override + public SExpr caseVariable(Variable object) { + // Variable: + // TypedVariable | Timer | Mode | Watchdog; + throw new RuntimeException("not implemented"); + } + + @Override + public SExpr caseVarRef(VarRef object) { + // VarRef: + // (variable=[Variable] | container=[Instantiation] '.' variable=[Variable] + // | interleaved?='interleaved' '(' (variable=[Variable] | container=[Instantiation] '.' + // variable=[Variable]) ')') ('as' (alias=ID))? + // ; + return sList( + "var-ref", + object.getVariable(), + object.getContainer(), + sList("is-interleaved", object.isInterleaved()), + sList("alias", object.getAlias())); + } + + @Override + public SExpr caseAssignment(Assignment object) { + // Assignment: + // lhs=[Parameter] + // rhs=AssignmentInitializer + // ; + return sList("assignment", object.getLhs(), object.getRhs()); + } + + @Override + public SExpr caseParameter(Parameter object) { + // Parameter: + // (attributes+=Attribute)* + // name=ID (':' type=Type)? + // init=Initializer? + // ; + return sList( + "parameter", + object.getName(), + sList("attributes", object.getAttributes()), + object.getType(), + object.getInit()); + } + + @Override + public SExpr caseExpression(Expression object) { + // Expression: + // {Literal} literal=Literal + // | Time + // | ParameterReference + // | {CodeExpr} code=Code + // | BracedListExpression + // | BracketListExpression + // ; + throw new RuntimeException("not implemented"); + // return sList("expression", object.getLiteral(), object.getTime(), + // object.getParameter(), object.getCode(), object.getBracedListExpression(), + // object.getBracketListExpression()); + } + + @Override + public SExpr caseBracedListExpression(BracedListExpression object) { + // BracedListExpression: + // '{' {BracedListExpression} (items+=Expression (',' items+=Expression)*)? ','? '}' + // ; + return sList("braced-list-expression", object.getItems()); + } + + @Override + public SExpr caseBracketListExpression(BracketListExpression object) { + // BracketListExpression: + // '[' {BracketListExpression} (items+=Expression (',' items+=Expression)*)? ','? ']' + // ; + return sList("bracket-list-expression", object.getItems()); + } + + @Override + public SExpr caseParameterReference(ParameterReference object) { + // ParameterReference: + // parameter=[Parameter] + // ; + return sList("parameter-reference", object.getParameter()); + } + + @Override + public SExpr caseTime(Time object) { + // Time: + // (interval=INT unit=TimeUnit) + // ; + return sList("time", object.getInterval(), object.getUnit()); + } + + @Override + public SExpr casePort(Port object) { + // Port: + // Input | Output; + throw new RuntimeException("not implemented"); + } + + @Override + public SExpr caseType(Type object) { + // Type: + // time?='time' (arraySpec=ArraySpec)? + // | id=DottedName ('<' typeArgs+=Type (',' typeArgs+=Type)* '>')? (stars+='*')* + // (arraySpec=ArraySpec)? + // | code=Code + // ; + return sList( + "type", + object.getId(), + sList("stars", object.getStars().size()), + object.getArraySpec(), + object.getCode(), + sList("is-time", object.isTime())); + } + + @Override + public SExpr caseArraySpec(ArraySpec object) { + // ArraySpec: + // '[' ( ofVariableLength?=']' | length=INT ']' ); + return sList( + "array-spec", + sList("length", object.getLength()), + sList("is-of-variable-length", object.isOfVariableLength())); + } + + @Override + public SExpr caseWidthSpec(WidthSpec object) { + // WidthSpec: + // '[' ( ofVariableLength?=']' | (terms+=WidthTerm) ('+' terms+=WidthTerm)* ']' ); + return sList( + "width-spec", + sList("terms", object.getTerms()), + sList("is-of-variable-length", object.isOfVariableLength())); + } + + @Override + public SExpr caseWidthTerm(WidthTerm object) { + // WidthTerm: + // width=INT + // | parameter=[Parameter] + // | 'widthof(' port=VarRef ')' + // | code=Code; + return sList( + "width-term", + sList("width", object.getWidth()), + object.getParameter(), + sList("width-of", object.getPort()), + object.getCode()); + } + + @Override + public SExpr caseIPV4Host(IPV4Host object) { + // IPV4Host: + // (user=Kebab '@')? addr=IPV4Addr (':' port=INT)? + // ; + return sList( + "ipv4-host", + sList("user", object.getUser()), + object.getAddr(), + sList("port", object.getPort())); + } + + @Override + public SExpr caseIPV6Host(IPV6Host object) { + // IPV6Host: + // ('[' (user=Kebab '@')? addr=IPV6Addr ']' (':' port=INT)?) + // ; + return sList( + "ipv6-host", + sList("user", object.getUser()), + object.getAddr(), + sList("port", object.getPort())); + } + + @Override + public SExpr caseNamedHost(NamedHost object) { + // NamedHost: + // (user=Kebab '@')? addr=HostName (':' port=INT)? + // ; + return sList( + "named-host", + sList("user", object.getUser()), + object.getAddr(), + sList("port", object.getPort())); + } + + @Override + public SExpr caseHost(Host object) { + // Host: + // IPV4Host | IPV6Host | NamedHost + // ; + // return super.caseHost(object); + throw new RuntimeException("not implemented"); + } + + @Override + public SExpr caseCode(Code object) { + // Code: + // // {Code} '{=' (tokens+=Token)* '=}' + // {Code} '{=' body=Body '=}' + // ; + return sList("code", object.getBody()); + } + + @Override + public SExpr caseLiteral(Literal object) { + // Literal: + // STRING | CHAR_LIT | SignedFloat | SignedInt | Boolean + // ; + return sList("literal", object.getLiteral()); + } + + @Override + public SExpr caseCodeExpr(CodeExpr object) { + return doSwitch(object.getCode()); + } + + @Override + public SExpr defaultCase(EObject object) { + throw new RuntimeException("this should be unreachable"); + } + + public abstract static class SExpr { + private Optional m = Optional.empty(); + + protected abstract String display(); + + public void setMetadata(Metadata m) { + if (this.m.isPresent()) { + throw new IllegalStateException("the metadata can only be set once"); + } else { + this.m = Optional.of(m); + } + } + + @Override + public String toString() { + String metadata = m.map(Metadata::toString).orElse("()"); + return String.format("(%s\n%s)", metadata, display()); + } + } + + public class SList extends SExpr { + private final List parts; + + public SList(List parts) { + this.parts = parts; + } + + protected String display() { + var ret = new StringBuilder(); + ret.append('('); + var flat = parts.stream().allMatch(it -> it instanceof SAtom); + var first = true; + for (var part : parts) { + if (!first) { + ret.append(','); + if (!flat) { + ret.append('\n'); + } else { + ret.append(' '); + } } - return new SList(ret); - } - - private SExpr sList(String name, List parts) { - var ret = new ArrayList(); - ret.add(new SAtom(name)); - for (EObject part : parts) { - ret.add(doSwitch(part)); + var s = part.toString(); + if (!flat && !first) { + s = s.indent(1); } - return new SList(ret); - } - -// private SExpr sList(String name, SExpr... parts) { -// var ret = new ArrayList(); -// ret.add(new SAtom(name)); -// ret.addAll(Arrays.asList(parts)); -// return new SList(ret); -// } - - @Override - public SExpr caseModel(Model object) { -// Model: -// target=TargetDecl -// (imports+=Import)* -// (preambles+=Preamble)* -// (reactors+=Reactor)+ -// ; - return sList("model", - doSwitch(object.getTarget()), - sList("preambles", object.getPreambles()), - sList("imports", object.getImports()), - sList("reactors", object.getReactors()) - ); - } - - @Override - public SExpr caseImport(Import object) { -// Import: 'import' reactorClasses+=ImportedReactor (',' reactorClasses+=ImportedReactor)* 'from' importURI=STRING ';'?; - return sList("import", new SAtom(object.getImportURI()), sList("reactors", object.getReactorClasses())); - } - - @Override - public SExpr caseReactorDecl(ReactorDecl object) { -// ReactorDecl: Reactor | ImportedReactor; - return sList("reactor-decl", - object.getName() - ); - } - - @Override - public SExpr caseImportedReactor(ImportedReactor object) { -// ImportedReactor: reactorClass=[Reactor] ('as' name=ID)?; - return sList("imported-reactor", object.getName(), object.getReactorClass().getName(), object.getReactorClass().hashCode()); - } - - @Override - public SExpr caseReactor(Reactor object) { -// Reactor: -// {Reactor} (attributes+=Attribute)* ((federated?='federated' | main?='main')? & realtime?='realtime'?) 'reactor' (name=ID)? -// ('<' typeParms+=TypeParm (',' typeParms+=TypeParm)* '>')? -// ('(' parameters+=Parameter (',' parameters+=Parameter)* ')')? -// ('at' host=Host)? -// ('extends' (superClasses+=[ReactorDecl] (',' superClasses+=[ReactorDecl])*))? -// '{' -// ( (preambles+=Preamble) -// | (stateVars+=StateVar) -// | (methods+=Method) -// | (inputs+=Input) -// | (outputs+=Output) -// | (timers+=Timer) -// | (actions+=Action) -// | (watchdogs+=Watchdog) -// | (instantiations+=Instantiation) -// | (connections+=Connection) -// | (reactions+=Reaction) -// | (modes+=Mode) -// )* '}'; - return sList("reactor", - new SAtom(object.getName()), - new SAtom(String.valueOf(object.hashCode())), - sList("attributes", object.getAttributes()), - sList("is-main", object.isMain()), - sList("is-federated", object.isFederated()), - sList("is-realtime", object.isRealtime()), - sList("typeParms", object.getTypeParms()), - sList("parameters", object.getParameters()), - sList("host", object.getHost()), - sList("extends", object.getSuperClasses()), - sList("preambles", object.getPreambles()), - sList("state", object.getStateVars()), - sList("methods", object.getMethods()), - sList("inputs", object.getInputs()), - sList("outputs", object.getOutputs()), - sList("timers", object.getTimers()), - sList("actions", object.getActions()), - sList("watchdogs", object.getWatchdogs()), - sList("instantiations", object.getInstantiations()), - sList("connections", object.getConnections()), - sList("reactions", object.getReactions()), - sList("modes", object.getModes()) - ); - } - - @Override - public SExpr caseTypeParm(TypeParm object) { -// TypeParm: -// literal=TypeExpr | code=Code - return sList("type-parm", object.getLiteral(), object.getCode()); - } - - @Override - public SExpr caseTargetDecl(TargetDecl object) { -// TargetDecl: -// 'target' name=ID (config=KeyValuePairs)? ';'?; - return sList("target-decl", object.getName(), object.getConfig()); - } - - @Override - public SExpr caseStateVar(StateVar object) { -// StateVar: -// (attributes+=Attribute)* -// (reset?='reset')? 'state' name=ID -// (':' type=Type)? -// init=Initializer? -// ';'? -// ; - return sList("state", object.getName(), - sList("attributes", object.getAttributes()), - sList("is-reset", object.isReset()), - object.getType(), object.getInit()); - } - - @Override - public SExpr caseInitializer(Initializer object) { -// Initializer: -// parens?='(' (exprs+=Expression (',' exprs+=Expression)* (trailingComma?=',')?)? ')' -// | braces?='{' (exprs+=Expression (',' exprs+=Expression)* (trailingComma?=',')?)? '}' -// | assign?='=' exprs+=Expression -// ; - return sList("initializer", - sList("exprs", object.getExprs()), - sList("is-braces", object.isBraces()), - sList("is-parens", object.isParens()), - sList("is-assign", object.isAssign()), - sList("is-trailing-comma", object.isTrailingComma()) - ); - } - - @Override - public SExpr caseMethod(Method object) { -// const?='const'? 'method' name=ID -// '(' (arguments+=MethodArgument (',' arguments+=MethodArgument)*)? ')' -// (':' return=Type)? -// code=Code -// ';'? - return sList("method", - object.getName(), - sList("is-const", object.isConst()), - sList("arguments", object.getArguments()), - sList("return", object.getReturn()), - object.getCode() - ); - } - - @Override - public SExpr caseMethodArgument(MethodArgument object) { -// MethodArgument: -// name=ID (':' type=Type)? - return sList("method-argument", object.getName(), sList("type", object.getType())); - } - - @Override - public SExpr caseInput(Input object) { -// Input: -// (attributes+=Attribute)* mutable?='mutable'? 'input' (widthSpec=WidthSpec)? name=ID (':' type=Type)? ';'?; - return sList("input", - object.getName(), - sList("attributes", object.getAttributes()), - sList("is-mutable", object.isMutable()), - object.getWidthSpec(), - object.getType() - ); - } - - @Override - public SExpr caseOutput(Output object) { -// Output: -// (attributes+=Attribute)* 'output' (widthSpec=WidthSpec)? name=ID (':' type=Type)? ';'?; - return sList("output", - object.getName(), - sList("attributes", object.getAttributes()), - object.getWidthSpec(), - object.getType() - ); - } - - @Override - public SExpr caseTimer(Timer object) { -// Timer: -// (attributes+=Attribute)* 'timer' name=ID ('(' offset=Expression (',' period=Expression)? ')')? ';'?; - return sList("timer", - object.getName(), - sList("attributes", object.getAttributes()), - sList("offset", object.getOffset()), - sList("period", object.getPeriod()) - ); - } - - @Override - public SExpr caseMode(Mode object) { -// Mode: -// {Mode} (initial?='initial')? 'mode' (name=ID)? -// '{' ( -// (stateVars+=StateVar) | -// (timers+=Timer) | -// (actions+=Action) | -// (watchdogs+=Watchdog) | -// (instantiations+=Instantiation) | -// (connections+=Connection) | -// (reactions+=Reaction) -// )* '}'; - return sList("mode", - object.getName(), - sList("is-initial", object.isInitial()), - sList("state", object.getStateVars()), - sList("timers", object.getTimers()), - sList("actions", object.getActions()), - sList("watchdogs", object.getWatchdogs()), - sList("instantiations", object.getInstantiations()), - sList("connections", object.getConnections()), - sList("reactions", object.getReactions()) - ); - } - - @Override - public SExpr caseAction(Action object) { -// Action: -// (attributes+=Attribute)* -// (origin=ActionOrigin)? 'action' name=ID -// ('(' minDelay=Expression (',' minSpacing=Expression (',' policy=STRING)? )? ')')? -// (':' type=Type)? ';'?; - return sList("action", - object.getName(), - sList("attributes", object.getAttributes()), - object.getOrigin(), - sList("min-delay", object.getMinDelay()), - sList("min-spacing", object.getMinSpacing()), - sList("policy", object.getPolicy()), - object.getType() - ); - } - - @Override - public SExpr caseReaction(Reaction object) { -// Reaction: -// (attributes+=Attribute)* -// (('reaction') | mutation ?= 'mutation') -// (name=ID)? -// ('(' (triggers+=TriggerRef (',' triggers+=TriggerRef)*)? ')') -// ( => sources+=VarRef (',' sources+=VarRef)*)? -// ('->' effects+=VarRefOrModeTransition (',' effects+=VarRefOrModeTransition)*)? -// (code=Code)? (stp=STP)? (deadline=Deadline)? (delimited?=';')? -// ; - return sList( - "reaction", - object.getName(), - sList("attributes", object.getAttributes()), - sList("is-mutation", object.isMutation()), - sList("triggers", object.getTriggers()), - sList("sources", object.getSources()), - sList("effects", object.getEffects()), - object.getCode(), - object.getStp(), - object.getDeadline(), - sList("is-delimited", object.isDelimited()) - ); - } - - @Override - public SExpr caseTriggerRef(TriggerRef object) { -// TriggerRef: -// BuiltinTriggerRef | VarRef; -// return sList("trigger-ref", object.getVariable()); - throw new RuntimeException("not implemented"); - } - - @Override - public SExpr caseBuiltinTriggerRef(BuiltinTriggerRef object) { -// BuiltinTriggerRef: -// type = BuiltinTrigger; - return sList("builtin-trigger-ref", object.getType()); - } - - @Override - public SExpr caseDeadline(Deadline object) { -// Deadline: -// 'deadline' '(' delay=Expression ')' code=Code; - return sList("deadline", object.getDelay(), object.getCode()); - } - - @Override - public SExpr caseWatchdog(Watchdog object) { -// Watchdog: -// 'watchdog' name=ID '(' timeout=Expression ')' -// ('->' effects+=VarRefOrModeTransition (',' effects+=VarRefOrModeTransition)*)? -// code=Code; - return sList("watchdog", - object.getName(), - sList("timeout", object.getTimeout()), - sList("effects", object.getEffects()), - object.getCode() - ); - } - - @Override - public SExpr caseSTP(STP object) { -// STP: -// 'STP' '(' value=Expression ')' code=Code; - return sList("stp", object.getValue(), object.getCode()); - } - - @Override - public SExpr casePreamble(Preamble object) { -// Preamble: -// (visibility=Visibility)? 'preamble' code=Code; - return sList("preamble", object.getVisibility(), object.getCode()); - } - - @Override - public SExpr caseInstantiation(Instantiation object) { -// Instantiation: -// (attributes+=Attribute)* -// name=ID '=' 'new' (widthSpec=WidthSpec)? -// reactorClass=[ReactorDecl] ('<' typeArgs+=Type (',' typeArgs+=Type)* '>')? '(' -// (parameters+=Assignment (',' parameters+=Assignment)*)? -// ')' (('at' host=Host ';') | ';'?); - return sList("instantiation", - object.getName(), - sList("attributes", object.getAttributes()), - object.getWidthSpec(), - object.getReactorClass(), - sList("typeArgs", object.getTypeArgs()), - sList("parameters", object.getParameters()), - object.getHost() - ); - } - - @Override - public SExpr caseConnection(Connection object) { -// Connection: -// ((leftPorts += VarRef (',' leftPorts += VarRef)*) -// | ( '(' leftPorts += VarRef (',' leftPorts += VarRef)* ')' iterated ?= '+'?)) -// ('->' | physical?='~>') -// rightPorts += VarRef (',' rightPorts += VarRef)* -// ('after' delay=Expression)? -// (serializer=Serializer)? -// ';'? - return sList("connection", - sList("left-ports", object.getLeftPorts()), - sList("right-ports", object.getRightPorts()), - sList("is-iterated", object.isIterated()), - sList("is-physical", object.isPhysical()), - sList("delay", object.getDelay()), - object.getSerializer() - ); - } - - @Override - public SExpr caseSerializer(Serializer object) { -// Serializer: -// 'serializer' type=STRING -// ; - return sList("serializer", object.getType()); - } - - @Override - public SExpr caseAttribute(Attribute object) { -// Attribute: -// '@' attrName=ID ('(' (attrParms+=AttrParm (',' attrParms+=AttrParm)* ','?)? ')')? -// ; - return sList("attribute", object.getAttrName(), sList("attr-parms", object.getAttrParms())); - } - - @Override - public SExpr caseAttrParm(AttrParm object) { -// AttrParm: -// (name=ID '=')? value=Literal; - return sList("attr-parm", object.getName(), object.getValue()); - } - - @Override - public SExpr caseKeyValuePairs(KeyValuePairs object) { -// KeyValuePairs: -// {KeyValuePairs} '{' (pairs+=KeyValuePair (',' (pairs+=KeyValuePair))* ','?)? '}'; - return sList("key-value-pairs", object.getPairs()); - } - - @Override - public SExpr caseKeyValuePair(KeyValuePair object) { -// KeyValuePair: -// name=(Kebab|STRING) ':' value=Element; - return sList("pair", object.getName(), object.getValue()); - } - - @Override - public SExpr caseArray(Array object) { -// Array: // todo allow empty array in grammar, replace with validator error -// '[' elements+=Element (',' (elements+=Element))* ','? ']'; - return sList("array", object.getElements()); - } - - @Override - public SExpr caseElement(Element object) { -// Element: -// keyvalue=KeyValuePairs -// | array=Array -// | literal=Literal -// | (time=INT unit=TimeUnit) -// | id=Path; - return sList("element", object.getKeyvalue(), object.getArray(), object.getLiteral(), sList("time", object.getTime(), object.getUnit()), object.getId()); - } - - @Override - public SExpr caseTypedVariable(TypedVariable object) { -// TypedVariable: -// Port | Action -// ; - throw new RuntimeException("not implemented"); - } - - @Override - public SExpr caseVariable(Variable object) { -// Variable: -// TypedVariable | Timer | Mode | Watchdog; - throw new RuntimeException("not implemented"); - } - - @Override - public SExpr caseVarRef(VarRef object) { -// VarRef: -// (variable=[Variable] | container=[Instantiation] '.' variable=[Variable] -// | interleaved?='interleaved' '(' (variable=[Variable] | container=[Instantiation] '.' variable=[Variable]) ')') ('as' (alias=ID))? -// ; - return sList("var-ref", object.getVariable(), object.getContainer(), sList("is-interleaved", object.isInterleaved()), sList("alias", object.getAlias())); - } - - @Override - public SExpr caseAssignment(Assignment object) { -// Assignment: -// lhs=[Parameter] -// rhs=AssignmentInitializer -// ; - return sList("assignment", object.getLhs(), object.getRhs()); - } - - @Override - public SExpr caseParameter(Parameter object) { -// Parameter: -// (attributes+=Attribute)* -// name=ID (':' type=Type)? -// init=Initializer? -// ; - return sList("parameter", - object.getName(), - sList("attributes", object.getAttributes()), - object.getType(), - object.getInit() - ); - } - - @Override - public SExpr caseExpression(Expression object) { -// Expression: -// {Literal} literal=Literal -// | Time -// | ParameterReference -// | {CodeExpr} code=Code -// | BracedListExpression -// | BracketListExpression -// ; - throw new RuntimeException("not implemented"); -// return sList("expression", object.getLiteral(), object.getTime(), object.getParameter(), object.getCode(), object.getBracedListExpression(), object.getBracketListExpression()); - } - - @Override - public SExpr caseBracedListExpression(BracedListExpression object) { -// BracedListExpression: -// '{' {BracedListExpression} (items+=Expression (',' items+=Expression)*)? ','? '}' -// ; - return sList("braced-list-expression", object.getItems()); - } - - @Override - public SExpr caseBracketListExpression(BracketListExpression object) { -// BracketListExpression: -// '[' {BracketListExpression} (items+=Expression (',' items+=Expression)*)? ','? ']' -// ; - return sList("bracket-list-expression", object.getItems()); - } - - @Override - public SExpr caseParameterReference(ParameterReference object) { -// ParameterReference: -// parameter=[Parameter] -// ; - return sList("parameter-reference", object.getParameter()); - } - - @Override - public SExpr caseTime(Time object) { -// Time: -// (interval=INT unit=TimeUnit) -// ; - return sList("time", object.getInterval(), object.getUnit()); - } - - @Override - public SExpr casePort(Port object) { -// Port: -// Input | Output; - throw new RuntimeException("not implemented"); - } - - @Override - public SExpr caseType(Type object) { -// Type: -// time?='time' (arraySpec=ArraySpec)? -// | id=DottedName ('<' typeArgs+=Type (',' typeArgs+=Type)* '>')? (stars+='*')* (arraySpec=ArraySpec)? -// | code=Code -// ; - return sList("type", object.getId(), sList("stars", object.getStars().size()), object.getArraySpec(), object.getCode(), sList("is-time", object.isTime())); - } - - @Override - public SExpr caseArraySpec(ArraySpec object) { -// ArraySpec: -// '[' ( ofVariableLength?=']' | length=INT ']' ); - return sList("array-spec", sList("length", object.getLength()), sList("is-of-variable-length", object.isOfVariableLength())); - } - - @Override - public SExpr caseWidthSpec(WidthSpec object) { -// WidthSpec: -// '[' ( ofVariableLength?=']' | (terms+=WidthTerm) ('+' terms+=WidthTerm)* ']' ); - return sList("width-spec", sList("terms", object.getTerms()), sList("is-of-variable-length", object.isOfVariableLength())); - } - - @Override - public SExpr caseWidthTerm(WidthTerm object) { -// WidthTerm: -// width=INT -// | parameter=[Parameter] -// | 'widthof(' port=VarRef ')' -// | code=Code; - return sList("width-term", sList("width", object.getWidth()), object.getParameter(), sList("width-of", object.getPort()), object.getCode()); - } - - @Override - public SExpr caseIPV4Host(IPV4Host object) { -// IPV4Host: -// (user=Kebab '@')? addr=IPV4Addr (':' port=INT)? -// ; - return sList("ipv4-host", sList("user", object.getUser()), object.getAddr(), sList("port", object.getPort())); + ret.append(s); + first = false; + } + ret.append(')'); + return ret.toString(); } - @Override - public SExpr caseIPV6Host(IPV6Host object) { -// IPV6Host: -// ('[' (user=Kebab '@')? addr=IPV6Addr ']' (':' port=INT)?) -// ; - return sList("ipv6-host", sList("user", object.getUser()), object.getAddr(), sList("port", object.getPort())); - } + public static class SAtom extends SExpr { + private final T x; - @Override - public SExpr caseNamedHost(NamedHost object) { -// NamedHost: -// (user=Kebab '@')? addr=HostName (':' port=INT)? -// ; - return sList("named-host", sList("user", object.getUser()), object.getAddr(), sList("port", object.getPort())); - } + public SAtom(T x) { + this.x = x; + } - @Override - public SExpr caseHost(Host object) { -// Host: -// IPV4Host | IPV6Host | NamedHost -// ; - // return super.caseHost(object); - throw new RuntimeException("not implemented"); - } - - @Override - public SExpr caseCode(Code object) { -// Code: -// // {Code} '{=' (tokens+=Token)* '=}' -// {Code} '{=' body=Body '=}' -// ; - return sList("code", object.getBody()); - } - - @Override - public SExpr caseLiteral(Literal object) { -// Literal: -// STRING | CHAR_LIT | SignedFloat | SignedInt | Boolean -// ; - return sList("literal", object.getLiteral()); - } - - @Override - public SExpr caseCodeExpr(CodeExpr object) { - return doSwitch(object.getCode()); + public String display() { + if (x instanceof String s) { + return "\"" + s.replaceAll("\"", "\uD83C\uDCA0") + "\""; + } else { + return String.valueOf(x); + } + } } - @Override - public SExpr defaultCase(EObject object) { - throw new RuntimeException("this should be unreachable"); + public record Sym(String s) { + @Override + public String toString() { + return s; + } } - public interface SExpr {} - public record SList(List parts) implements SExpr { - @Override - public String toString() { - var ret = new StringBuilder(); - ret.append('('); - var flat = parts.stream().allMatch(it -> it instanceof SAtom); - var first = true; - for (var part : parts) { - if (!first) { - ret.append(','); - if (!flat) { - ret.append('\n'); - } else { - ret.append(' '); - } - } - var s = part.toString(); - if (!flat && !first) { - s = s.indent(1); - } - ret.append(s); - first = false; - } - ret.append(')'); - return ret.toString(); - } - public record SAtom(String s) implements SExpr { - - @Override - public String toString() { - return "\"" + s.replaceAll("\"", "\uD83C\uDCA0") + "\""; - } - } + public record Metadata( + URI uri, int offset, int startLine, int startCol, int endLine, int endCol) { + @Override + public String toString() { + return String.format( + "(\"%s\" %d %d %d %d %d)", + uri.toFileString(), offset, startLine, startCol, endLine, endCol); + } } + } } diff --git a/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java b/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java index 9b6edf4375..cabf6ce0f6 100644 --- a/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java +++ b/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java @@ -1,7 +1,6 @@ package org.lflang.diagram.lsp; import java.util.ArrayList; -import java.util.Optional; import java.util.concurrent.CompletableFuture; import org.eclipse.emf.common.util.URI; import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; @@ -44,19 +43,24 @@ public void setClient(LanguageClient client) { @JsonRequest("parser/ast") public CompletableFuture getAst(String uri) { - return CompletableFuture.supplyAsync(() -> { - URI parsedUri; - try { - parsedUri = URI.createFileURI(new java.net.URI(uri).getPath()); - } catch (java.net.URISyntaxException e) { - System.err.println(e); - return "LF language server failed to get AST because the URI was invalid"; - } - var model = builder.getResource(parsedUri).getContents().get(0); // FIXME: if the resource has syntax errors this should fail - var toSExpr = new ToSExpr(); - var sExpr = toSExpr.doSwitch(model); - return sExpr.toString(); - }); + return CompletableFuture.supplyAsync( + () -> { + URI parsedUri; + try { + parsedUri = URI.createFileURI(new java.net.URI(uri).getPath()); + } catch (java.net.URISyntaxException e) { + System.err.println(e); + return "LF language server failed to get AST because the URI was invalid"; + } + var model = + builder + .getResource(parsedUri) + .getContents() + .get(0); // FIXME: if the resource has syntax errors this should fail + var toSExpr = new ToSExpr(); + var sExpr = toSExpr.doSwitch(model); + return sExpr.toString(); + }); } /** From 0b366adbefb44b9a3b6bca591c592d388e2236d2 Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Thu, 8 Feb 2024 01:05:20 -0800 Subject: [PATCH 03/35] Fix various problems found while running on tests This includes stack overflows (eObjects that refer to their ancestors, creating cycles) and pruning out noise such as null values and empty lists. --- .../src/main/java/org/lflang/ast/ToSExpr.java | 169 +++++++++++++++--- 1 file changed, 146 insertions(+), 23 deletions(-) diff --git a/core/src/main/java/org/lflang/ast/ToSExpr.java b/core/src/main/java/org/lflang/ast/ToSExpr.java index d37cdf70cd..accb2cd46d 100644 --- a/core/src/main/java/org/lflang/ast/ToSExpr.java +++ b/core/src/main/java/org/lflang/ast/ToSExpr.java @@ -1,5 +1,9 @@ package org.lflang.ast; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -9,6 +13,7 @@ import org.eclipse.xtext.nodemodel.util.NodeModelUtils; import org.eclipse.xtext.util.LineAndColumn; import org.lflang.ast.ToSExpr.SExpr; +import org.lflang.ast.ToSExpr.SList.Fingerprint; import org.lflang.ast.ToSExpr.SList.Metadata; import org.lflang.ast.ToSExpr.SList.SAtom; import org.lflang.ast.ToSExpr.SList.Sym; @@ -72,8 +77,35 @@ public class ToSExpr extends LfSwitch { + /** + * The eObjects in the syntax tree on the path from the root up to and including the current + * eObject. + */ + private final List callStack = new ArrayList<>(); + @Override - protected SExpr doSwitch(int classifierID, EObject theEObject) { + public SExpr doSwitch(EObject theEObject) { + callStack.add(theEObject); + var ret = doSwitchHelper(theEObject); + callStack.remove(callStack.size() - 1); + return ret; + } + + private boolean inside(Class tClass) { + return callStack.size() >= 2 && tClass.isInstance(callStack.get(callStack.size() - 2)); + } + + private boolean inVarRef() { + // uses variable, typedvariable, timer, mode, watchdog, port, input, output, action + return inside(VarRef.class); + } + + // private boolean inReaction() { + // // uses triggerref + // return inside(Reaction.class); + // } + + private SExpr doSwitchHelper(EObject theEObject) { var node = NodeModelUtils.getNode(theEObject); var range = getLfRange(theEObject); var metadata = @@ -84,7 +116,7 @@ protected SExpr doSwitch(int classifierID, EObject theEObject) { range.getStartInclusive().getZeroBasedColumn(), range.getEndExclusive().getZeroBasedLine(), range.getEndExclusive().getZeroBasedColumn()); - var ret = super.doSwitch(classifierID, theEObject); + var ret = super.doSwitch(theEObject); ret.setMetadata(metadata); return ret; } @@ -103,10 +135,40 @@ private SExpr sym(String name) { return new SAtom<>(new Sym(name)); } + private SExpr fingerprint(EObject eObject) { + try { + var md = MessageDigest.getInstance("SHA"); + var range = + getLfRange( + eObject); // No two distinct syntactic elements can have the exact same source code + // range + var locBuffer = ByteBuffer.allocate(16); // 4 32-bit ints + locBuffer.putInt(range.getStartInclusive().getZeroBasedLine()); + locBuffer.putInt(range.getStartInclusive().getZeroBasedColumn()); + locBuffer.putInt(range.getEndExclusive().getZeroBasedLine()); + locBuffer.putInt(range.getEndExclusive().getZeroBasedColumn()); + md.update(locBuffer.array()); + md.update(eObject.eResource().getURI().toString().getBytes(StandardCharsets.UTF_8)); + md.update( + NodeModelUtils.getNode(eObject) + .getText() + .getBytes( + StandardCharsets + .UTF_8)); // FIXME: this is probably redundant, but I am leaving it in for + // good measure until we validate this machinery more rigorously + return new SAtom<>(new Fingerprint(md.digest())); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("should be impossible; SHA is a supported algorithm"); + } + } + private SExpr sList(String name, Object... parts) { var ret = new ArrayList(); ret.add(sym(name)); for (var part : parts) { + if (part == null) { + continue; + } if (part instanceof List) { throw new IllegalArgumentException("List was not expected"); } @@ -127,6 +189,9 @@ private SExpr sList(String name, List parts) { for (EObject part : parts) { ret.add(doSwitch(part)); } + if (ret.size() == 1) { + return null; + } return new SList(ret); } @@ -168,8 +233,9 @@ public SExpr caseImportedReactor(ImportedReactor object) { return sList( "imported-reactor", object.getName(), + fingerprint(object), object.getReactorClass().getName(), - object.getReactorClass().hashCode()); + fingerprint(object.getReactorClass())); } @Override @@ -198,7 +264,7 @@ public SExpr caseReactor(Reactor object) { return sList( "reactor", new SAtom<>(object.getName()), - new SAtom<>(object.hashCode()), + fingerprint(object), sList("attributes", object.getAttributes()), sList("is-main", object.isMain()), sList("is-federated", object.isFederated()), @@ -298,9 +364,13 @@ public SExpr caseInput(Input object) { // Input: // (attributes+=Attribute)* mutable?='mutable'? 'input' (widthSpec=WidthSpec)? name=ID // (':' type=Type)? ';'?; + if (inVarRef()) { + return sList("input", object.getName(), fingerprint(object)); + } return sList( "input", object.getName(), + fingerprint(object), sList("attributes", object.getAttributes()), sList("is-mutable", object.isMutable()), object.getWidthSpec(), @@ -312,9 +382,13 @@ public SExpr caseOutput(Output object) { // Output: // (attributes+=Attribute)* 'output' (widthSpec=WidthSpec)? name=ID (':' type=Type)? // ';'?; + if (inVarRef()) { + return sList("output", object.getName(), fingerprint(object)); + } return sList( "output", object.getName(), + fingerprint(object), sList("attributes", object.getAttributes()), object.getWidthSpec(), object.getType()); @@ -325,9 +399,13 @@ public SExpr caseTimer(Timer object) { // Timer: // (attributes+=Attribute)* 'timer' name=ID ('(' offset=Expression (',' // period=Expression)? ')')? ';'?; + if (inVarRef()) { + return sList("timer", object.getName()); + } return sList( "timer", object.getName(), + fingerprint(object), sList("attributes", object.getAttributes()), sList("offset", object.getOffset()), sList("period", object.getPeriod())); @@ -346,17 +424,22 @@ public SExpr caseMode(Mode object) { // (connections+=Connection) | // (reactions+=Reaction) // )* '}'; - return sList( - "mode", - object.getName(), - sList("is-initial", object.isInitial()), - sList("state", object.getStateVars()), - sList("timers", object.getTimers()), - sList("actions", object.getActions()), - sList("watchdogs", object.getWatchdogs()), - sList("instantiations", object.getInstantiations()), - sList("connections", object.getConnections()), - sList("reactions", object.getReactions())); + if (inVarRef()) { + return sList("mode", object.getName(), fingerprint(object)); + } else { + return sList( + "mode", + object.getName(), + fingerprint(object), + sList("is-initial", object.isInitial()), + sList("state", object.getStateVars()), + sList("timers", object.getTimers()), + sList("actions", object.getActions()), + sList("watchdogs", object.getWatchdogs()), + sList("instantiations", object.getInstantiations()), + sList("connections", object.getConnections()), + sList("reactions", object.getReactions())); + } } @Override @@ -366,9 +449,13 @@ public SExpr caseAction(Action object) { // (origin=ActionOrigin)? 'action' name=ID // ('(' minDelay=Expression (',' minSpacing=Expression (',' policy=STRING)? )? ')')? // (':' type=Type)? ';'?; + if (inVarRef()) { + return sList("action", object.getName(), fingerprint(object)); + } return sList( "action", object.getName(), + fingerprint(object), sList("attributes", object.getAttributes()), object.getOrigin(), sList("min-delay", object.getMinDelay()), @@ -406,7 +493,6 @@ public SExpr caseReaction(Reaction object) { public SExpr caseTriggerRef(TriggerRef object) { // TriggerRef: // BuiltinTriggerRef | VarRef; - // return sList("trigger-ref", object.getVariable()); throw new RuntimeException("not implemented"); } @@ -430,9 +516,13 @@ public SExpr caseWatchdog(Watchdog object) { // 'watchdog' name=ID '(' timeout=Expression ')' // ('->' effects+=VarRefOrModeTransition (',' effects+=VarRefOrModeTransition)*)? // code=Code; + if (inVarRef()) { + return sList("watchdog", object.getName(), fingerprint(object)); + } return sList( "watchdog", object.getName(), + fingerprint(object), sList("timeout", object.getTimeout()), sList("effects", object.getEffects()), object.getCode()); @@ -465,7 +555,7 @@ public SExpr caseInstantiation(Instantiation object) { object.getName(), sList("attributes", object.getAttributes()), object.getWidthSpec(), - object.getReactorClass(), + sList("reactor", object.getReactorClass().getName(), fingerprint(object.getReactorClass())), sList("typeArgs", object.getTypeArgs()), sList("parameters", object.getParameters()), object.getHost()); @@ -548,7 +638,13 @@ public SExpr caseElement(Element object) { object.getKeyvalue(), object.getArray(), object.getLiteral(), - sList("time", object.getTime(), object.getUnit()), + object.getTime() == 0 + && (object.getKeyvalue() != null + || object.getArray() != null + || object.getLiteral() != null + || object.getId() != null) + ? null + : sList("time", object.getTime(), object.getUnit()), object.getId()); } @@ -791,8 +887,15 @@ public abstract static class SExpr { protected abstract String display(); public void setMetadata(Metadata m) { + // noinspection StatementWithEmptyBody if (this.m.isPresent()) { - throw new IllegalStateException("the metadata can only be set once"); + // It is actually possible for doSwitch to be + // invoked twice, so this can be called redundantly + // in non-error cases. Don't ask me why. It creates + // redundant work, but this hasn't been shown to a + // performance bottleneck, so it's fine. + // throw new IllegalStateException("tried to set the metadata twice for object " + + // display()); } else { this.m = Optional.of(m); } @@ -800,8 +903,11 @@ public void setMetadata(Metadata m) { @Override public String toString() { - String metadata = m.map(Metadata::toString).orElse("()"); - return String.format("(%s\n%s)", metadata, display()); + return m.map(metadata -> String.format("(%s\n%s)", metadata, display().indent(2).stripTrailing())) + .orElseGet(() -> { + var indented = display().indent(1); + return String.format("(()%s)", indented.stripTrailing()); + }); } } @@ -819,7 +925,6 @@ protected String display() { var first = true; for (var part : parts) { if (!first) { - ret.append(','); if (!flat) { ret.append('\n'); } else { @@ -828,7 +933,7 @@ protected String display() { } var s = part.toString(); if (!flat && !first) { - s = s.indent(1); + s = s.indent(1).stripTrailing(); } ret.append(s); first = false; @@ -860,6 +965,24 @@ public String toString() { } } + public record Fingerprint(byte[] digest) { + @Override + public String toString() { + var ret = new StringBuilder(); + ret.append("#u8("); + var first = true; + for (byte c : digest) { + if (!first) { + ret.append(' '); + } + ret.append(c); + first = false; + } + ret.append(")"); + return ret.toString(); + } + } + public record Metadata( URI uri, int offset, int startLine, int startCol, int endLine, int endCol) { @Override From 1f6f0ae0057a257f94a91b93de47b73e54fb2480 Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Thu, 8 Feb 2024 14:16:41 -0800 Subject: [PATCH 04/35] Superficial tweaks. --- core/src/main/java/org/lflang/ast/ToSExpr.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/lflang/ast/ToSExpr.java b/core/src/main/java/org/lflang/ast/ToSExpr.java index accb2cd46d..f83ba79a10 100644 --- a/core/src/main/java/org/lflang/ast/ToSExpr.java +++ b/core/src/main/java/org/lflang/ast/ToSExpr.java @@ -177,7 +177,7 @@ private SExpr sList(String name, Object... parts) { } else if (part instanceof SExpr sExpr) { ret.add(sExpr); } else { - ret.add(new SAtom<>(String.valueOf(part))); + ret.add(new SAtom<>(part)); } } return new SList(ret); @@ -206,8 +206,8 @@ public SExpr caseModel(Model object) { return sList( "model", doSwitch(object.getTarget()), - sList("preambles", object.getPreambles()), sList("imports", object.getImports()), + sList("preambles", object.getPreambles()), sList("reactors", object.getReactors())); } @@ -903,7 +903,7 @@ public void setMetadata(Metadata m) { @Override public String toString() { - return m.map(metadata -> String.format("(%s\n%s)", metadata, display().indent(2).stripTrailing())) + return m.map(metadata -> String.format("(%s\n%s)", metadata, display().indent(1).stripTrailing())) .orElseGet(() -> { var indented = display().indent(1); return String.format("(()%s)", indented.stripTrailing()); @@ -952,6 +952,8 @@ public SAtom(T x) { public String display() { if (x instanceof String s) { return "\"" + s.replaceAll("\"", "\uD83C\uDCA0") + "\""; + } else if (x instanceof Boolean b) { + return b ? "#t" : "#f"; } else { return String.valueOf(x); } @@ -975,7 +977,7 @@ public String toString() { if (!first) { ret.append(' '); } - ret.append(c); + ret.append(Byte.toUnsignedInt(c)); first = false; } ret.append(")"); From 94fa12f53eb4161e7efe093c91b4a756d8623095 Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Mon, 4 Mar 2024 15:10:50 -0800 Subject: [PATCH 05/35] Allow to accept JSON The JSON is not being used yet. The first step is to make sure the JSON RPC messages are being passed along properly. --- .../lsp/LFLanguageServerExtension.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java b/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java index cabf6ce0f6..6fc5d2c85b 100644 --- a/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java +++ b/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java @@ -66,18 +66,18 @@ public CompletableFuture getAst(String uri) { /** * Handle a request for a complete build of the Lingua Franca file specified by {@code uri}. * - * @param uri the URI of the LF file of interest + * @param uriAndJson the URI of the LF file of interest * @return A message describing the outcome of the build process. */ @JsonRequest("generator/build") - public CompletableFuture build(String uri) { + public CompletableFuture build(String[] uriAndJson) { if (client == null) return CompletableFuture.completedFuture( "Please wait for the Lingua Franca language server to be fully initialized."); return CompletableFuture.supplyAsync( () -> { try { - return buildWithProgress(client, uri, true).getUserMessage(); + return buildWithProgress(client, uriAndJson, true).getUserMessage(); } catch (Exception e) { return "An internal error occurred:\n" + e; } @@ -88,27 +88,27 @@ public CompletableFuture build(String uri) { * Handles a request for the most complete build of the specified Lingua Franca file that can be * done in a limited amount of time. * - * @param uri the URI of the LF file of interest + * @param uriAndJson the URI of the LF file of interest */ @JsonNotification("generator/partialBuild") - public void partialBuild(String uri) { + public void partialBuild(String[] uriAndJson) { if (client == null) return; - buildWithProgress(client, uri, false); + buildWithProgress(client, uriAndJson, false); } /** * Completely build the specified LF program and provide information that is sufficient to run it. * - * @param uri The URI of the LF program to be built. + * @param uriAndJson The URI of the LF program to be built. * @return An array consisting of the directory in which the execute command should be executed, * the program of the execute command, and the arguments of the execute command. */ @JsonNotification("generator/buildAndRun") - public CompletableFuture buildAndRun(String uri) { + public CompletableFuture buildAndRun(String[] uriAndJson) { return new CompletableFuture() .completeAsync( () -> { - var result = buildWithProgress(client, uri, true); + var result = buildWithProgress(client, uriAndJson, true); if (!result.getStatus().equals(Status.COMPILED)) return null; LFCommand cmd = result.getContext().getFileConfig().getCommand(); ArrayList ret = new ArrayList<>(); @@ -120,10 +120,10 @@ public CompletableFuture buildAndRun(String uri) { /** Describes a build process that has a progress. */ private GeneratorResult buildWithProgress( - LanguageClient client, String uri, boolean mustComplete) { + LanguageClient client, String[] uriAndJson, boolean mustComplete) { URI parsedUri; try { - parsedUri = URI.createFileURI(new java.net.URI(uri).getPath()); + parsedUri = URI.createFileURI(new java.net.URI(uriAndJson[0]).getPath()); } catch (java.net.URISyntaxException e) { // This error will appear as a silent failure to most users, but that is acceptable because // this error From 72caff7801fb3091a3dcc3240b5aa482ba50a014 Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Tue, 5 Mar 2024 16:23:09 -0800 Subject: [PATCH 06/35] Partially beanify --- .../lsp/LFLanguageServerExtension.java | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java b/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java index 6fc5d2c85b..a29579db58 100644 --- a/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java +++ b/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java @@ -104,11 +104,11 @@ public void partialBuild(String[] uriAndJson) { * the program of the execute command, and the arguments of the execute command. */ @JsonNotification("generator/buildAndRun") - public CompletableFuture buildAndRun(String[] uriAndJson) { + public CompletableFuture buildAndRun(BuildArgs uriAndJson) { return new CompletableFuture() .completeAsync( () -> { - var result = buildWithProgress(client, uriAndJson, true); + var result = buildWithProgress(client, new String[]{uriAndJson.getUri(), uriAndJson.getJson()}, true); if (!result.getStatus().equals(Status.COMPILED)) return null; LFCommand cmd = result.getContext().getFileConfig().getCommand(); ArrayList ret = new ArrayList<>(); @@ -145,4 +145,27 @@ private GeneratorResult buildWithProgress( } return result; } + + private static class BuildArgs { + + private String uri; + + private String json; + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public String getJson() { + return json; + } + + public void setJson(String json) { + this.json = json; + } + } } From 04723be35bac86207dfa3e578a2b714330bb1711 Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Tue, 5 Mar 2024 16:37:57 -0800 Subject: [PATCH 07/35] Get beanification to work properly --- .../org/lflang/diagram/lsp/BuildArgs.java | 44 +++++++++++++++++++ .../lsp/LFLanguageServerExtension.java | 25 +---------- 2 files changed, 46 insertions(+), 23 deletions(-) create mode 100644 lsp/src/main/java/org/lflang/diagram/lsp/BuildArgs.java diff --git a/lsp/src/main/java/org/lflang/diagram/lsp/BuildArgs.java b/lsp/src/main/java/org/lflang/diagram/lsp/BuildArgs.java new file mode 100644 index 0000000000..586b2e597a --- /dev/null +++ b/lsp/src/main/java/org/lflang/diagram/lsp/BuildArgs.java @@ -0,0 +1,44 @@ +package org.lflang.diagram.lsp; + +import java.util.Objects; + +public class BuildArgs { + + private String uri; + + private String json; + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public String getJson() { + return json; + } + + public void setJson(String json) { + this.json = json; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BuildArgs buildArgs = (BuildArgs) o; + return Objects.equals(uri, buildArgs.uri) + && Objects.equals(json, buildArgs.json); + } + + @Override + public int hashCode() { + return Objects.hash(uri, json); + } +} diff --git a/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java b/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java index a29579db58..e1efbd4576 100644 --- a/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java +++ b/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java @@ -1,8 +1,10 @@ package org.lflang.diagram.lsp; import java.util.ArrayList; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import org.eclipse.emf.common.util.URI; +import org.eclipse.lsp4j.generator.JsonRpcData; import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; import org.eclipse.lsp4j.services.LanguageClient; @@ -145,27 +147,4 @@ private GeneratorResult buildWithProgress( } return result; } - - private static class BuildArgs { - - private String uri; - - private String json; - - public String getUri() { - return uri; - } - - public void setUri(String uri) { - this.uri = uri; - } - - public String getJson() { - return json; - } - - public void setJson(String json) { - this.json = json; - } - } } From 26ce31130f5d0b337c4f180e09805d6b9225499b Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Tue, 5 Mar 2024 16:48:05 -0800 Subject: [PATCH 08/35] Fully beanify and propagate json to code generator --- .../lflang/generator/IntegratedBuilder.java | 15 ++++++++---- .../lsp/LFLanguageServerExtension.java | 24 +++++++++---------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/org/lflang/generator/IntegratedBuilder.java b/core/src/main/java/org/lflang/generator/IntegratedBuilder.java index 832279dc98..6559c1221e 100644 --- a/core/src/main/java/org/lflang/generator/IntegratedBuilder.java +++ b/core/src/main/java/org/lflang/generator/IntegratedBuilder.java @@ -1,5 +1,6 @@ package org.lflang.generator; +import com.google.gson.JsonParser; import com.google.inject.Inject; import com.google.inject.Provider; import java.nio.file.Path; @@ -54,6 +55,7 @@ public interface ReportProgress { */ public GeneratorResult run( URI uri, + String json, boolean mustComplete, ReportProgress reportProgress, CancelIndicator cancelIndicator) { @@ -70,7 +72,7 @@ public GeneratorResult run( if (cancelIndicator.isCanceled()) return GeneratorResult.CANCELLED; if (messageReporter.getErrorsOccurred()) return GeneratorResult.FAILED; reportProgress.apply("Generating code...", VALIDATED_PERCENT_PROGRESS); - return doGenerate(uri, mustComplete, reportProgress, cancelIndicator); + return doGenerate(uri, json, mustComplete, reportProgress, cancelIndicator); } /* ------------------------- PRIVATE METHODS ------------------------- */ @@ -100,6 +102,7 @@ private void validate(URI uri, MessageReporter messageReporter) { */ private GeneratorResult doGenerate( URI uri, + String json, boolean mustComplete, ReportProgress reportProgress, CancelIndicator cancelIndicator) { @@ -109,7 +112,7 @@ private GeneratorResult doGenerate( mustComplete ? Mode.LSP_SLOW : LFGeneratorContext.Mode.LSP_MEDIUM, cancelIndicator, reportProgress, - getArgs(), + getArgs(json), resource, fileAccess, fileConfig -> new LanguageServerMessageReporter(resource.getContents().get(0))); @@ -137,7 +140,11 @@ static DiagnosticSeverity convertSeverity(Severity severity) { } /** Return arguments to feed to the code generator. Currently, no arguments are being set. */ - protected GeneratorArguments getArgs() { - return GeneratorArguments.none(); + protected GeneratorArguments getArgs(String jsonString) { + if (jsonString == null || jsonString.isBlank()) { + return GeneratorArguments.none(); + } + var json = JsonParser.parseString(jsonString).getAsJsonObject(); + return new GeneratorArguments(false, null, false, json, false, false, null, List.of()); } } diff --git a/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java b/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java index e1efbd4576..9ef865a97f 100644 --- a/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java +++ b/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java @@ -68,18 +68,18 @@ public CompletableFuture getAst(String uri) { /** * Handle a request for a complete build of the Lingua Franca file specified by {@code uri}. * - * @param uriAndJson the URI of the LF file of interest + * @param args the URI of the LF file of interest * @return A message describing the outcome of the build process. */ @JsonRequest("generator/build") - public CompletableFuture build(String[] uriAndJson) { + public CompletableFuture build(BuildArgs args) { if (client == null) return CompletableFuture.completedFuture( "Please wait for the Lingua Franca language server to be fully initialized."); return CompletableFuture.supplyAsync( () -> { try { - return buildWithProgress(client, uriAndJson, true).getUserMessage(); + return buildWithProgress(client, args, true).getUserMessage(); } catch (Exception e) { return "An internal error occurred:\n" + e; } @@ -90,27 +90,27 @@ public CompletableFuture build(String[] uriAndJson) { * Handles a request for the most complete build of the specified Lingua Franca file that can be * done in a limited amount of time. * - * @param uriAndJson the URI of the LF file of interest + * @param args the URI of the LF file of interest */ @JsonNotification("generator/partialBuild") - public void partialBuild(String[] uriAndJson) { + public void partialBuild(BuildArgs args) { if (client == null) return; - buildWithProgress(client, uriAndJson, false); + buildWithProgress(client, args, false); } /** * Completely build the specified LF program and provide information that is sufficient to run it. * - * @param uriAndJson The URI of the LF program to be built. + * @param args The URI of the LF program to be built. * @return An array consisting of the directory in which the execute command should be executed, * the program of the execute command, and the arguments of the execute command. */ @JsonNotification("generator/buildAndRun") - public CompletableFuture buildAndRun(BuildArgs uriAndJson) { + public CompletableFuture buildAndRun(BuildArgs args) { return new CompletableFuture() .completeAsync( () -> { - var result = buildWithProgress(client, new String[]{uriAndJson.getUri(), uriAndJson.getJson()}, true); + var result = buildWithProgress(client, args, true); if (!result.getStatus().equals(Status.COMPILED)) return null; LFCommand cmd = result.getContext().getFileConfig().getCommand(); ArrayList ret = new ArrayList<>(); @@ -122,10 +122,10 @@ public CompletableFuture buildAndRun(BuildArgs uriAndJson) { /** Describes a build process that has a progress. */ private GeneratorResult buildWithProgress( - LanguageClient client, String[] uriAndJson, boolean mustComplete) { + LanguageClient client, BuildArgs args, boolean mustComplete) { URI parsedUri; try { - parsedUri = URI.createFileURI(new java.net.URI(uriAndJson[0]).getPath()); + parsedUri = URI.createFileURI(new java.net.URI(args.getUri()).getPath()); } catch (java.net.URISyntaxException e) { // This error will appear as a silent failure to most users, but that is acceptable because // this error @@ -141,7 +141,7 @@ private GeneratorResult buildWithProgress( GeneratorResult result = null; try { result = - builder.run(parsedUri, mustComplete, progress::report, progress.getCancelIndicator()); + builder.run(parsedUri, args.getJson(), mustComplete, progress::report, progress.getCancelIndicator()); } finally { progress.end(result == null ? "An internal error occurred." : result.getUserMessage()); } From 9eb3058567e2c01cee07c878a6671e0330a7aaac Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Tue, 5 Mar 2024 20:58:20 -0800 Subject: [PATCH 09/35] Support parsing the tracing property from JSON --- .../main/java/org/lflang/target/property/TracingProperty.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/lflang/target/property/TracingProperty.java b/core/src/main/java/org/lflang/target/property/TracingProperty.java index b47793c717..ae392864f9 100644 --- a/core/src/main/java/org/lflang/target/property/TracingProperty.java +++ b/core/src/main/java/org/lflang/target/property/TracingProperty.java @@ -52,7 +52,7 @@ public TracingOptions fromAst(Element node, MessageReporter reporter) { @Override protected TracingOptions fromString(String string, MessageReporter reporter) { - throw new UnsupportedOperationException("Not supported yet."); + return new TracingOptions(Boolean.parseBoolean(string)); } @Override From bd7af8a65ab3691ac52b2a6eac366d9f67d886a4 Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Wed, 6 Mar 2024 22:22:49 -0800 Subject: [PATCH 10/35] Pass all CMake args even in docker build Previously we had separate CMake commands depending on whether we are in Docker or not and consequently compilation options were getting ignored in Docker mode --- .../src/main/java/org/lflang/ast/ToSExpr.java | 12 ++-- .../generator/docker/CDockerGenerator.java | 11 +++- .../target/property/DockerProperty.java | 9 ++- .../main/java/org/lflang/util/LFCommand.java | 6 +- .../org/lflang/diagram/lsp/BuildArgs.java | 57 +++++++++---------- .../lsp/LFLanguageServerExtension.java | 9 ++- 6 files changed, 64 insertions(+), 40 deletions(-) diff --git a/core/src/main/java/org/lflang/ast/ToSExpr.java b/core/src/main/java/org/lflang/ast/ToSExpr.java index f83ba79a10..b08a66a455 100644 --- a/core/src/main/java/org/lflang/ast/ToSExpr.java +++ b/core/src/main/java/org/lflang/ast/ToSExpr.java @@ -903,11 +903,13 @@ public void setMetadata(Metadata m) { @Override public String toString() { - return m.map(metadata -> String.format("(%s\n%s)", metadata, display().indent(1).stripTrailing())) - .orElseGet(() -> { - var indented = display().indent(1); - return String.format("(()%s)", indented.stripTrailing()); - }); + return m.map( + metadata -> String.format("(%s\n%s)", metadata, display().indent(1).stripTrailing())) + .orElseGet( + () -> { + var indented = display().indent(1); + return String.format("(()%s)", indented.stripTrailing()); + }); } } diff --git a/core/src/main/java/org/lflang/generator/docker/CDockerGenerator.java b/core/src/main/java/org/lflang/generator/docker/CDockerGenerator.java index ab04e6cfd0..392b2e3422 100644 --- a/core/src/main/java/org/lflang/generator/docker/CDockerGenerator.java +++ b/core/src/main/java/org/lflang/generator/docker/CDockerGenerator.java @@ -2,6 +2,7 @@ import org.eclipse.xtext.xbase.lib.IterableExtensions; import org.lflang.generator.LFGeneratorContext; +import org.lflang.generator.c.CCompiler; import org.lflang.target.Target; import org.lflang.target.property.BuildCommandsProperty; import org.lflang.util.StringUtil; @@ -84,11 +85,19 @@ protected String generateRunForBuildDependencies() { /** Return the default compile command for the C docker container. */ protected String generateCompileCommand() { + var ccompile = + new CCompiler( + context.getTargetConfig(), + context.getFileConfig(), + context.getErrorReporter(), + context.getTargetConfig().target == Target.C); return String.join( "\n", "RUN set -ex && \\", "mkdir bin && \\", - "cmake -DCMAKE_INSTALL_BINDIR=./bin -S src-gen -B bin && \\", + String.format( + "%s -DCMAKE_INSTALL_BINDIR=./bin -S src-gen -B bin && \\", + ccompile.compileCmakeCommand()), "cd bin && \\", "make all"); } diff --git a/core/src/main/java/org/lflang/target/property/DockerProperty.java b/core/src/main/java/org/lflang/target/property/DockerProperty.java index 1dc47724fe..e494419f67 100644 --- a/core/src/main/java/org/lflang/target/property/DockerProperty.java +++ b/core/src/main/java/org/lflang/target/property/DockerProperty.java @@ -58,7 +58,14 @@ public DockerOptions fromAst(Element node, MessageReporter reporter) { @Override protected DockerOptions fromString(String string, MessageReporter reporter) { - throw new UnsupportedOperationException("Not supported yet."); + if (string.equalsIgnoreCase("true")) { + return new DockerOptions(true); + } else if (string.equalsIgnoreCase("false")) { + return new DockerOptions(false); + } else { + throw new UnsupportedOperationException( + "Docker options other than \"true\" and \"false\" are not supported."); + } } @Override diff --git a/core/src/main/java/org/lflang/util/LFCommand.java b/core/src/main/java/org/lflang/util/LFCommand.java index 8bed14916e..97325f8e9e 100644 --- a/core/src/main/java/org/lflang/util/LFCommand.java +++ b/core/src/main/java/org/lflang/util/LFCommand.java @@ -95,7 +95,11 @@ public File directory() { /** Get a String representation of the stored command */ public String toString() { - return String.join(" ", processBuilder.command()); + return String.join( + " ", + (Iterable) + () -> + processBuilder.command().stream().map(it -> it.replace("'", "'\"'\"'")).iterator()); } /** diff --git a/lsp/src/main/java/org/lflang/diagram/lsp/BuildArgs.java b/lsp/src/main/java/org/lflang/diagram/lsp/BuildArgs.java index 586b2e597a..80a78214f4 100644 --- a/lsp/src/main/java/org/lflang/diagram/lsp/BuildArgs.java +++ b/lsp/src/main/java/org/lflang/diagram/lsp/BuildArgs.java @@ -4,41 +4,40 @@ public class BuildArgs { - private String uri; + private String uri; - private String json; + private String json; - public String getUri() { - return uri; - } + public String getUri() { + return uri; + } - public void setUri(String uri) { - this.uri = uri; - } + public void setUri(String uri) { + this.uri = uri; + } - public String getJson() { - return json; - } + public String getJson() { + return json; + } - public void setJson(String json) { - this.json = json; - } + public void setJson(String json) { + this.json = json; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - BuildArgs buildArgs = (BuildArgs) o; - return Objects.equals(uri, buildArgs.uri) - && Objects.equals(json, buildArgs.json); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hash(uri, json); + if (o == null || getClass() != o.getClass()) { + return false; } + BuildArgs buildArgs = (BuildArgs) o; + return Objects.equals(uri, buildArgs.uri) && Objects.equals(json, buildArgs.json); + } + + @Override + public int hashCode() { + return Objects.hash(uri, json); + } } diff --git a/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java b/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java index 9ef865a97f..4b75417126 100644 --- a/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java +++ b/lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageServerExtension.java @@ -1,10 +1,8 @@ package org.lflang.diagram.lsp; import java.util.ArrayList; -import java.util.Objects; import java.util.concurrent.CompletableFuture; import org.eclipse.emf.common.util.URI; -import org.eclipse.lsp4j.generator.JsonRpcData; import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; import org.eclipse.lsp4j.services.LanguageClient; @@ -141,7 +139,12 @@ private GeneratorResult buildWithProgress( GeneratorResult result = null; try { result = - builder.run(parsedUri, args.getJson(), mustComplete, progress::report, progress.getCancelIndicator()); + builder.run( + parsedUri, + args.getJson(), + mustComplete, + progress::report, + progress.getCancelIndicator()); } finally { progress.end(result == null ? "An internal error occurred." : result.getUserMessage()); } From 7e0b7cbf5fffb48d55700740b54f89b743a378ca Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Mon, 11 Mar 2024 16:04:25 -0700 Subject: [PATCH 11/35] Python target supports TracePluginProperty --- core/src/main/java/org/lflang/target/Target.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/org/lflang/target/Target.java b/core/src/main/java/org/lflang/target/Target.java index 0156866890..811220be53 100644 --- a/core/src/main/java/org/lflang/target/Target.java +++ b/core/src/main/java/org/lflang/target/Target.java @@ -643,6 +643,7 @@ public void initialize(TargetConfig config) { SchedulerProperty.INSTANCE, SingleThreadedProperty.INSTANCE, TracingProperty.INSTANCE, + TracePluginProperty.INSTANCE, WorkersProperty.INSTANCE); case Rust -> config.register( BuildTypeProperty.INSTANCE, From a4b5ffdf9d73cf769471e9a96209e089f44579ef Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Mon, 18 Mar 2024 21:56:46 -0700 Subject: [PATCH 12/35] Allow trace plugin to be specified in LF syntax --- .../lflang/target/property/TracePluginProperty.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/lflang/target/property/TracePluginProperty.java b/core/src/main/java/org/lflang/target/property/TracePluginProperty.java index 2d150b9fab..cd6eadac76 100644 --- a/core/src/main/java/org/lflang/target/property/TracePluginProperty.java +++ b/core/src/main/java/org/lflang/target/property/TracePluginProperty.java @@ -1,6 +1,7 @@ package org.lflang.target.property; import org.lflang.MessageReporter; +import org.lflang.ast.ASTUtils; import org.lflang.lf.Element; import org.lflang.target.TargetConfig; import org.lflang.target.property.TracePluginProperty.TracePluginOptions; @@ -23,8 +24,13 @@ public TracePluginOptions initialValue() { @Override protected TracePluginOptions fromAst(Element node, MessageReporter reporter) { - reporter.at(node).error(TargetConfig.NOT_IN_LF_SYNTAX_MESSAGE); - return null; + var s = ASTUtils.elementToSingleString(node); + if (s != null) { + return new TracePluginOptions(s); + } else { + reporter.at(node).error("Expected a string literal"); + return null; + } } @Override From c4f61c9e7115f490e5b1e7a7ceb85eaa74794f6e Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Mon, 18 Mar 2024 22:13:34 -0700 Subject: [PATCH 13/35] Clean up after rebase --- core/src/main/java/org/lflang/ast/ToSExpr.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/org/lflang/ast/ToSExpr.java b/core/src/main/java/org/lflang/ast/ToSExpr.java index b08a66a455..49074626c9 100644 --- a/core/src/main/java/org/lflang/ast/ToSExpr.java +++ b/core/src/main/java/org/lflang/ast/ToSExpr.java @@ -21,13 +21,13 @@ import org.lflang.generator.Range; import org.lflang.lf.Action; import org.lflang.lf.Array; -import org.lflang.lf.ArraySpec; import org.lflang.lf.Assignment; import org.lflang.lf.AttrParm; import org.lflang.lf.Attribute; import org.lflang.lf.BracedListExpression; import org.lflang.lf.BracketListExpression; import org.lflang.lf.BuiltinTriggerRef; +import org.lflang.lf.CStyleArraySpec; import org.lflang.lf.Code; import org.lflang.lf.CodeExpr; import org.lflang.lf.Connection; @@ -329,11 +329,8 @@ public SExpr caseInitializer(Initializer object) { // ; return sList( "initializer", - sList("exprs", object.getExprs()), - sList("is-braces", object.isBraces()), - sList("is-parens", object.isParens()), - sList("is-assign", object.isAssign()), - sList("is-trailing-comma", object.isTrailingComma())); + sList("expr", object.getExpr()), + sList("is-assign", object.isAssign())); } @Override @@ -769,13 +766,13 @@ public SExpr caseType(Type object) { "type", object.getId(), sList("stars", object.getStars().size()), - object.getArraySpec(), + object.getCStyleArraySpec(), object.getCode(), sList("is-time", object.isTime())); } @Override - public SExpr caseArraySpec(ArraySpec object) { + public SExpr caseCStyleArraySpec(CStyleArraySpec object) { // ArraySpec: // '[' ( ofVariableLength?=']' | length=INT ']' ); return sList( From e2240ae276557eb1a063f1089136878d759184e7 Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Mon, 25 Mar 2024 17:26:27 -0700 Subject: [PATCH 14/35] Provide comma-separated list of federate names --- .../lflang/federated/extensions/CExtension.java | 4 ++-- .../federated/extensions/CExtensionUtils.java | 15 +++++++++------ .../federated/extensions/FedTargetExtension.java | 6 ++++-- .../lflang/federated/extensions/TSExtension.java | 2 +- .../lflang/federated/generator/FedEmitter.java | 5 +++-- .../lflang/federated/generator/FedGenerator.java | 4 +++- .../federated/generator/FedTargetEmitter.java | 6 ++++-- core/src/main/resources/lib/c/reactor-c | 2 +- 8 files changed, 27 insertions(+), 17 deletions(-) diff --git a/core/src/main/java/org/lflang/federated/extensions/CExtension.java b/core/src/main/java/org/lflang/federated/extensions/CExtension.java index 28010a90ac..87a19e29d4 100644 --- a/core/src/main/java/org/lflang/federated/extensions/CExtension.java +++ b/core/src/main/java/org/lflang/federated/extensions/CExtension.java @@ -76,14 +76,14 @@ public class CExtension implements FedTargetExtension { @Override public void initializeTargetConfig( LFGeneratorContext context, - int numOfFederates, + List federateNames, FederateInstance federate, FederationFileConfig fileConfig, MessageReporter messageReporter, RtiConfig rtiConfig) throws IOException { - CExtensionUtils.handleCompileDefinitions(federate, numOfFederates, rtiConfig, messageReporter); + CExtensionUtils.handleCompileDefinitions(federate, federateNames, rtiConfig, messageReporter); generateCMakeInclude(federate, fileConfig); diff --git a/core/src/main/java/org/lflang/federated/extensions/CExtensionUtils.java b/core/src/main/java/org/lflang/federated/extensions/CExtensionUtils.java index c5adf0a810..70dd5688bb 100644 --- a/core/src/main/java/org/lflang/federated/extensions/CExtensionUtils.java +++ b/core/src/main/java/org/lflang/federated/extensions/CExtensionUtils.java @@ -7,6 +7,8 @@ import java.util.HashMap; import java.util.List; import java.util.regex.Pattern; +import java.util.stream.Collectors; + import org.lflang.InferredType; import org.lflang.MessageReporter; import org.lflang.ast.ASTUtils; @@ -184,7 +186,7 @@ static boolean isSharedPtrType(InferredType type, CTypes types) { public static void handleCompileDefinitions( FederateInstance federate, - int numOfFederates, + List federateNames, RtiConfig rtiConfig, MessageReporter messageReporter) { @@ -198,7 +200,7 @@ public static void handleCompileDefinitions( if (federate.targetConfig.get(AuthProperty.INSTANCE)) { definitions.put("FEDERATED_AUTHENTICATED", ""); } - definitions.put("NUMBER_OF_FEDERATES", String.valueOf(numOfFederates)); + definitions.put("NUMBER_OF_FEDERATES", String.valueOf(federateNames.size())); definitions.put("EXECUTABLE_PREAMBLE", ""); definitions.put("FEDERATE_ID", String.valueOf(federate.id)); @@ -206,7 +208,7 @@ public static void handleCompileDefinitions( handleAdvanceMessageInterval(federate); - initializeClockSynchronization(federate, rtiConfig, messageReporter); + initializeClockSynchronization(federate, rtiConfig, federateNames, messageReporter); } private static void handleAdvanceMessageInterval(FederateInstance federate) { @@ -235,7 +237,7 @@ static boolean clockSyncIsOn(FederateInstance federate, RtiConfig rtiConfig) { * href="https://github.com/icyphy/lingua-franca/wiki/Distributed-Execution#clock-synchronization">Documentation */ public static void initializeClockSynchronization( - FederateInstance federate, RtiConfig rtiConfig, MessageReporter messageReporter) { + FederateInstance federate, RtiConfig rtiConfig, List federateNames, MessageReporter messageReporter) { // Check if clock synchronization should be enabled for this federate in the first place if (clockSyncIsOn(federate, rtiConfig)) { messageReporter @@ -252,7 +254,7 @@ public static void initializeClockSynchronization( .info("Runtime clock synchronization is enabled for federate " + federate.id); } - addClockSyncCompileDefinitions(federate); + addClockSyncCompileDefinitions(federate, federateNames); } } @@ -264,7 +266,7 @@ public static void initializeClockSynchronization( * @see Documentation */ - public static void addClockSyncCompileDefinitions(FederateInstance federate) { + public static void addClockSyncCompileDefinitions(FederateInstance federate, List federateNames) { ClockSyncMode mode = federate.targetConfig.get(ClockSyncModeProperty.INSTANCE); ClockSyncOptions options = federate.targetConfig.get(ClockSyncOptionsProperty.INSTANCE); @@ -274,6 +276,7 @@ public static void addClockSyncCompileDefinitions(FederateInstance federate) { defs.put("_LF_CLOCK_SYNC_PERIOD_NS", String.valueOf(options.period.toNanoSeconds())); defs.put("_LF_CLOCK_SYNC_EXCHANGES_PER_INTERVAL", String.valueOf(options.trials)); defs.put("_LF_CLOCK_SYNC_ATTENUATION", String.valueOf(options.attenuation)); + defs.put("_LF_FEDERATE_NAMES_COMMA_SEPARATED", "\"" + String.join(",", federateNames) + "\""); if (mode == ClockSyncMode.ON) { defs.put("_LF_CLOCK_SYNC_ON", ""); diff --git a/core/src/main/java/org/lflang/federated/extensions/FedTargetExtension.java b/core/src/main/java/org/lflang/federated/extensions/FedTargetExtension.java index e708c117d7..2fa73bb860 100644 --- a/core/src/main/java/org/lflang/federated/extensions/FedTargetExtension.java +++ b/core/src/main/java/org/lflang/federated/extensions/FedTargetExtension.java @@ -1,6 +1,8 @@ package org.lflang.federated.extensions; import java.io.IOException; +import java.util.List; + import org.lflang.InferredType; import org.lflang.MessageReporter; import org.lflang.federated.generator.FedConnectionInstance; @@ -21,14 +23,14 @@ public interface FedTargetExtension { * Perform necessary actions to initialize the target config. * * @param context The context of the original code generation process. - * @param numOfFederates The number of federates in the program. + * @param federateNames The names of all the federates in the program. * @param federate The federate instance. * @param fileConfig An instance of {@code FedFileConfig}. * @param messageReporter Used to report errors. */ void initializeTargetConfig( LFGeneratorContext context, - int numOfFederates, + List federateNames, FederateInstance federate, FederationFileConfig fileConfig, MessageReporter messageReporter, diff --git a/core/src/main/java/org/lflang/federated/extensions/TSExtension.java b/core/src/main/java/org/lflang/federated/extensions/TSExtension.java index 86a220b5f1..92ec170404 100644 --- a/core/src/main/java/org/lflang/federated/extensions/TSExtension.java +++ b/core/src/main/java/org/lflang/federated/extensions/TSExtension.java @@ -33,7 +33,7 @@ public class TSExtension implements FedTargetExtension { @Override public void initializeTargetConfig( LFGeneratorContext context, - int numOfFederates, + List federateNames, FederateInstance federate, FederationFileConfig fileConfig, MessageReporter messageReporter, diff --git a/core/src/main/java/org/lflang/federated/generator/FedEmitter.java b/core/src/main/java/org/lflang/federated/generator/FedEmitter.java index 5d6452df77..a6cb4d232b 100644 --- a/core/src/main/java/org/lflang/federated/generator/FedEmitter.java +++ b/core/src/main/java/org/lflang/federated/generator/FedEmitter.java @@ -4,6 +4,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.lflang.MessageReporter; import org.lflang.federated.launcher.RtiConfig; @@ -32,7 +33,7 @@ public FedEmitter( /** Generate a .lf file for federate {@code federate}. */ Map generateFederate( - LFGeneratorContext context, FederateInstance federate, int numOfFederates) + LFGeneratorContext context, FederateInstance federate, List federateNames) throws IOException { String fedName = federate.name; Files.createDirectories(fileConfig.getSrcPath()); @@ -49,7 +50,7 @@ Map generateFederate( "\n", new FedTargetEmitter() .generateTarget( - context, numOfFederates, federate, fileConfig, messageReporter, rtiConfig), + context, federateNames, federate, fileConfig, messageReporter, rtiConfig), new FedImportEmitter().generateImports(federate, fileConfig), new FedPreambleEmitter() .generatePreamble(federate, fileConfig, rtiConfig, messageReporter), diff --git a/core/src/main/java/org/lflang/federated/generator/FedGenerator.java b/core/src/main/java/org/lflang/federated/generator/FedGenerator.java index 445b1dacfc..d7cc39da9b 100644 --- a/core/src/main/java/org/lflang/federated/generator/FedGenerator.java +++ b/core/src/main/java/org/lflang/federated/generator/FedGenerator.java @@ -19,6 +19,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import java.util.stream.Collectors; + import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.xtext.generator.JavaIoFileSystemAccess; @@ -166,7 +168,7 @@ public boolean doGenerate(Resource resource, LFGeneratorContext context) throws // Generate LF code for each federate. Map lf2lfCodeMapMap = new HashMap<>(); for (FederateInstance federate : federates) { - lf2lfCodeMapMap.putAll(fedEmitter.generateFederate(context, federate, federates.size())); + lf2lfCodeMapMap.putAll(fedEmitter.generateFederate(context, federate, federates.stream().map(fed -> fed.name).collect(Collectors.toList()))); } // Do not invoke target code generators if --no-compile flag is used. diff --git a/core/src/main/java/org/lflang/federated/generator/FedTargetEmitter.java b/core/src/main/java/org/lflang/federated/generator/FedTargetEmitter.java index 53f3823f72..d4460e1b29 100644 --- a/core/src/main/java/org/lflang/federated/generator/FedTargetEmitter.java +++ b/core/src/main/java/org/lflang/federated/generator/FedTargetEmitter.java @@ -1,6 +1,8 @@ package org.lflang.federated.generator; import java.io.IOException; +import java.util.List; + import org.lflang.MessageReporter; import org.lflang.ast.FormattingUtil; import org.lflang.federated.extensions.FedTargetExtensionFactory; @@ -11,7 +13,7 @@ public class FedTargetEmitter { String generateTarget( LFGeneratorContext context, - int numOfFederates, + List federateNames, FederateInstance federate, FederationFileConfig fileConfig, MessageReporter messageReporter, @@ -24,7 +26,7 @@ String generateTarget( // See https://issues.lf-lang.org/1667 FedTargetExtensionFactory.getExtension(federate.targetConfig.target) .initializeTargetConfig( - context, numOfFederates, federate, fileConfig, messageReporter, rtiConfig); + context, federateNames, federate, fileConfig, messageReporter, rtiConfig); return FormattingUtil.renderer(federate.targetConfig.target) .apply(federate.targetConfig.extractTargetDecl()); diff --git a/core/src/main/resources/lib/c/reactor-c b/core/src/main/resources/lib/c/reactor-c index 4a6b4a233d..07a994a806 160000 --- a/core/src/main/resources/lib/c/reactor-c +++ b/core/src/main/resources/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 4a6b4a233df05fd58775fbcb6f7564f944f408f1 +Subproject commit 07a994a806800e8373fa91b979bb6ebf56da9f75 From cee7fc463051d43fafb0145db4237ab7132ed5c9 Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Wed, 20 Mar 2024 14:34:30 -0700 Subject: [PATCH 15/35] Support tracing plugin with federated execution --- .../java/org/lflang/target/property/TracePluginProperty.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/lflang/target/property/TracePluginProperty.java b/core/src/main/java/org/lflang/target/property/TracePluginProperty.java index cd6eadac76..0171b3f244 100644 --- a/core/src/main/java/org/lflang/target/property/TracePluginProperty.java +++ b/core/src/main/java/org/lflang/target/property/TracePluginProperty.java @@ -45,7 +45,7 @@ public String name() { @Override public Element toAstElement(TracePluginOptions value) { - throw new UnsupportedOperationException(TargetConfig.NOT_IN_LF_SYNTAX_MESSAGE); + return ASTUtils.toElement(value.implementationArchiveFile); } public static class TracePluginOptions { From b5b3842035790793cc7a1fd15c39558f832378ed Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Mon, 25 Mar 2024 19:44:28 -0700 Subject: [PATCH 16/35] Pass all federate names to each federate. --- .../lflang/federated/extensions/CExtensionUtils.java | 10 +++++----- .../org/lflang/generator/c/CMainFunctionGenerator.java | 5 +++-- core/src/main/java/org/lflang/util/StringUtil.java | 2 +- core/src/main/resources/lib/c/reactor-c | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/lflang/federated/extensions/CExtensionUtils.java b/core/src/main/java/org/lflang/federated/extensions/CExtensionUtils.java index 70dd5688bb..51a446129d 100644 --- a/core/src/main/java/org/lflang/federated/extensions/CExtensionUtils.java +++ b/core/src/main/java/org/lflang/federated/extensions/CExtensionUtils.java @@ -203,12 +203,13 @@ public static void handleCompileDefinitions( definitions.put("NUMBER_OF_FEDERATES", String.valueOf(federateNames.size())); definitions.put("EXECUTABLE_PREAMBLE", ""); definitions.put("FEDERATE_ID", String.valueOf(federate.id)); + definitions.put("_LF_FEDERATE_NAMES_COMMA_SEPARATED", "\"" + String.join(",", federateNames) + "\""); CompileDefinitionsProperty.INSTANCE.update(federate.targetConfig, definitions); handleAdvanceMessageInterval(federate); - initializeClockSynchronization(federate, rtiConfig, federateNames, messageReporter); + initializeClockSynchronization(federate, rtiConfig, messageReporter); } private static void handleAdvanceMessageInterval(FederateInstance federate) { @@ -237,7 +238,7 @@ static boolean clockSyncIsOn(FederateInstance federate, RtiConfig rtiConfig) { * href="https://github.com/icyphy/lingua-franca/wiki/Distributed-Execution#clock-synchronization">Documentation */ public static void initializeClockSynchronization( - FederateInstance federate, RtiConfig rtiConfig, List federateNames, MessageReporter messageReporter) { + FederateInstance federate, RtiConfig rtiConfig, MessageReporter messageReporter) { // Check if clock synchronization should be enabled for this federate in the first place if (clockSyncIsOn(federate, rtiConfig)) { messageReporter @@ -254,7 +255,7 @@ public static void initializeClockSynchronization( .info("Runtime clock synchronization is enabled for federate " + federate.id); } - addClockSyncCompileDefinitions(federate, federateNames); + addClockSyncCompileDefinitions(federate); } } @@ -266,7 +267,7 @@ public static void initializeClockSynchronization( * @see Documentation */ - public static void addClockSyncCompileDefinitions(FederateInstance federate, List federateNames) { + public static void addClockSyncCompileDefinitions(FederateInstance federate) { ClockSyncMode mode = federate.targetConfig.get(ClockSyncModeProperty.INSTANCE); ClockSyncOptions options = federate.targetConfig.get(ClockSyncOptionsProperty.INSTANCE); @@ -276,7 +277,6 @@ public static void addClockSyncCompileDefinitions(FederateInstance federate, Lis defs.put("_LF_CLOCK_SYNC_PERIOD_NS", String.valueOf(options.period.toNanoSeconds())); defs.put("_LF_CLOCK_SYNC_EXCHANGES_PER_INTERVAL", String.valueOf(options.trials)); defs.put("_LF_CLOCK_SYNC_ATTENUATION", String.valueOf(options.attenuation)); - defs.put("_LF_FEDERATE_NAMES_COMMA_SEPARATED", "\"" + String.join(",", federateNames) + "\""); if (mode == ClockSyncMode.ON) { defs.put("_LF_CLOCK_SYNC_ON", ""); diff --git a/core/src/main/java/org/lflang/generator/c/CMainFunctionGenerator.java b/core/src/main/java/org/lflang/generator/c/CMainFunctionGenerator.java index ba054d6e49..f48e057648 100644 --- a/core/src/main/java/org/lflang/generator/c/CMainFunctionGenerator.java +++ b/core/src/main/java/org/lflang/generator/c/CMainFunctionGenerator.java @@ -2,6 +2,8 @@ import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; + import org.lflang.generator.CodeBuilder; import org.lflang.target.TargetConfig; import org.lflang.target.property.FastProperty; @@ -98,8 +100,7 @@ private String generateSetDefaultCliOption() { ? String.join( "\n", "const char* _lf_default_argv[] = { " - + StringUtil.addDoubleQuotes( - StringUtil.joinObjects(runCommand, StringUtil.addDoubleQuotes(", "))) + + StringUtil.joinObjects(runCommand.stream().map(StringUtil::addDoubleQuotes).toList(), ", ") + " };", "void lf_set_default_command_line_options() {", " default_argc = " + runCommand.size() + ";", diff --git a/core/src/main/java/org/lflang/util/StringUtil.java b/core/src/main/java/org/lflang/util/StringUtil.java index 516f340c06..9e30c19ea5 100644 --- a/core/src/main/java/org/lflang/util/StringUtil.java +++ b/core/src/main/java/org/lflang/util/StringUtil.java @@ -154,7 +154,7 @@ private static int getWhitespacePrefix(String code, int firstLineToConsider) { } public static String addDoubleQuotes(String str) { - return "\"" + str + "\""; + return "\"" + str.replace("\"", "\\\"") + "\""; } public static String joinObjects(List things, String delimiter) { diff --git a/core/src/main/resources/lib/c/reactor-c b/core/src/main/resources/lib/c/reactor-c index 07a994a806..fa6a138d58 160000 --- a/core/src/main/resources/lib/c/reactor-c +++ b/core/src/main/resources/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 07a994a806800e8373fa91b979bb6ebf56da9f75 +Subproject commit fa6a138d5893cd595df26fe9bab4b016a78c10f4 From 24c48e4ef9b04dcfb0dc9c3efdff49c250316d18 Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Wed, 27 Mar 2024 23:25:50 -0700 Subject: [PATCH 17/35] Update submodule to include RTI Dockerfile fix --- core/src/main/resources/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/lib/c/reactor-c b/core/src/main/resources/lib/c/reactor-c index fa6a138d58..4104af14c5 160000 --- a/core/src/main/resources/lib/c/reactor-c +++ b/core/src/main/resources/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit fa6a138d5893cd595df26fe9bab4b016a78c10f4 +Subproject commit 4104af14c5b426d3fa7520c993a397fd02e6c28f From 8f9fd2c1370ef14a5f975cb1c0e89f302b75231c Mon Sep 17 00:00:00 2001 From: "Erling R. Jellum" Date: Thu, 4 Apr 2024 04:32:00 -0700 Subject: [PATCH 18/35] Bump reactor-c to include CONNECT_TIMEOUT fix --- core/src/main/resources/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/lib/c/reactor-c b/core/src/main/resources/lib/c/reactor-c index 4104af14c5..c0c19e93df 160000 --- a/core/src/main/resources/lib/c/reactor-c +++ b/core/src/main/resources/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 4104af14c5b426d3fa7520c993a397fd02e6c28f +Subproject commit c0c19e93dfba82507384a3777eefb5630e3bc994 From 16c691d0fa788b36c36eb00f994ab8c0edfd1908 Mon Sep 17 00:00:00 2001 From: "Erling R. Jellum" Date: Thu, 11 Apr 2024 07:56:30 -0700 Subject: [PATCH 19/35] Bump reactor-c --- core/src/main/resources/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/lib/c/reactor-c b/core/src/main/resources/lib/c/reactor-c index c0c19e93df..4f35e6be8d 160000 --- a/core/src/main/resources/lib/c/reactor-c +++ b/core/src/main/resources/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit c0c19e93dfba82507384a3777eefb5630e3bc994 +Subproject commit 4f35e6be8d7aff73e6ed9b36b2557466deac8668 From e02f564f0dbc34310e9517a3bb1f484b1a1ea215 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Tue, 7 May 2024 16:41:29 +0200 Subject: [PATCH 20/35] TradePluginProperty fix --- .../java/org/lflang/target/property/TracePluginProperty.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/main/java/org/lflang/target/property/TracePluginProperty.java b/core/src/main/java/org/lflang/target/property/TracePluginProperty.java index 2d150b9fab..8bee1e5b47 100644 --- a/core/src/main/java/org/lflang/target/property/TracePluginProperty.java +++ b/core/src/main/java/org/lflang/target/property/TracePluginProperty.java @@ -23,8 +23,7 @@ public TracePluginOptions initialValue() { @Override protected TracePluginOptions fromAst(Element node, MessageReporter reporter) { - reporter.at(node).error(TargetConfig.NOT_IN_LF_SYNTAX_MESSAGE); - return null; + return new TracePluginOptions(node.getLiteral()); } @Override From 4a8aab07853a606ecd346e88d4a7ee9f6bab4a00 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 8 May 2024 10:19:28 +0200 Subject: [PATCH 21/35] Dont specify trace plugin to Cmake from command line. Generate a default value in the CmakeLists.txt --- .../lflang/generator/c/CCmakeGenerator.java | 18 +++++++++++++----- .../java/org/lflang/generator/c/CCompiler.java | 9 --------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/org/lflang/generator/c/CCmakeGenerator.java b/core/src/main/java/org/lflang/generator/c/CCmakeGenerator.java index 69ecc4b43c..c7da579884 100644 --- a/core/src/main/java/org/lflang/generator/c/CCmakeGenerator.java +++ b/core/src/main/java/org/lflang/generator/c/CCmakeGenerator.java @@ -42,6 +42,7 @@ import org.lflang.target.property.PlatformProperty; import org.lflang.target.property.ProtobufsProperty; import org.lflang.target.property.SingleThreadedProperty; +import org.lflang.target.property.TracePluginProperty; import org.lflang.target.property.WorkersProperty; import org.lflang.target.property.type.PlatformType.Platform; import org.lflang.util.FileUtil; @@ -262,16 +263,23 @@ CodeBuilder generateCMakeCode( .get(CompileDefinitionsProperty.INSTANCE) .forEach( (key, value) -> { - cMakeCode.pr("if (NOT DEFINED " + key + ")\n"); - cMakeCode.indent(); var v = "TRUE"; if (value != null && !value.isEmpty()) { v = value; } - cMakeCode.pr("set(" + key + " " + v + ")\n"); - cMakeCode.unindent(); - cMakeCode.pr("endif()\n"); + cMakeCode.pr("set(" + key + " " + v + " CACHE STRING \"\")\n"); }); + // Add trace-plugin data + var tracePlugin = targetConfig.getOrDefault(TracePluginProperty.INSTANCE); + System.out.println(tracePlugin); + if (tracePlugin != null) { + cMakeCode.pr( + "set(LF_TRACE_PLUGIN " + + targetConfig + .getOrDefault(TracePluginProperty.INSTANCE) + .getImplementationArchiveFile() + + " CACHE STRING \"\")\n"); + } // Setup main target for different platforms switch (platformOptions.platform()) { diff --git a/core/src/main/java/org/lflang/generator/c/CCompiler.java b/core/src/main/java/org/lflang/generator/c/CCompiler.java index c0401b5684..98ca19aacf 100644 --- a/core/src/main/java/org/lflang/generator/c/CCompiler.java +++ b/core/src/main/java/org/lflang/generator/c/CCompiler.java @@ -42,7 +42,6 @@ import org.lflang.target.property.CompilerProperty; import org.lflang.target.property.PlatformProperty; import org.lflang.target.property.PlatformProperty.PlatformOptions; -import org.lflang.target.property.TracePluginProperty; import org.lflang.target.property.type.BuildTypeType.BuildType; import org.lflang.target.property.type.PlatformType.Platform; import org.lflang.util.FileUtil; @@ -238,14 +237,6 @@ private static List cmakeOptions(TargetConfig targetConfig, FileConfig f "-DCMAKE_INSTALL_BINDIR=" + FileUtil.toUnixString(fileConfig.getOutPath().relativize(fileConfig.binPath)), "-DLF_FILE_SEPARATOR=\"" + maybeQuote + separator + maybeQuote + "\"")); - var tracePlugin = targetConfig.getOrDefault(TracePluginProperty.INSTANCE); - if (tracePlugin != null) { - arguments.add( - "-DLF_TRACE_PLUGIN=" - + targetConfig - .getOrDefault(TracePluginProperty.INSTANCE) - .getImplementationArchiveFile()); - } // Add #define for source file directory. // Do not do this for federated programs because for those, the definition is put // into the cmake file (and fileConfig.srcPath is the wrong directory anyway). From 7677ddc4d31ea45338ee04ebb88f0d6e20547b22 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 8 May 2024 10:19:52 +0200 Subject: [PATCH 22/35] Implement toString for tracing and trace-plugin --- .../java/org/lflang/target/property/TracePluginProperty.java | 5 +++++ .../java/org/lflang/target/property/TracingProperty.java | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/core/src/main/java/org/lflang/target/property/TracePluginProperty.java b/core/src/main/java/org/lflang/target/property/TracePluginProperty.java index 8bee1e5b47..388ed31ce6 100644 --- a/core/src/main/java/org/lflang/target/property/TracePluginProperty.java +++ b/core/src/main/java/org/lflang/target/property/TracePluginProperty.java @@ -51,5 +51,10 @@ public TracePluginOptions(String implementationArchiveFile) { public String getImplementationArchiveFile() { return implementationArchiveFile; } + + @Override + public String toString() { + return getImplementationArchiveFile(); + } } } diff --git a/core/src/main/java/org/lflang/target/property/TracingProperty.java b/core/src/main/java/org/lflang/target/property/TracingProperty.java index b47793c717..92f2e8e7d0 100644 --- a/core/src/main/java/org/lflang/target/property/TracingProperty.java +++ b/core/src/main/java/org/lflang/target/property/TracingProperty.java @@ -152,6 +152,11 @@ public boolean equals(Object o) { public boolean isEnabled() { return enabled; } + + @Override + public String toString() { + return Boolean.toString(this.enabled); + } } /** From 75428c2739f96800d49c6c7b7f765bf607c33fdc Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 10 May 2024 16:31:37 +0200 Subject: [PATCH 23/35] Have TracePluginProperty simple inherit from StringProperty --- .../target/property/TracePluginProperty.java | 50 ++----------------- 1 file changed, 4 insertions(+), 46 deletions(-) diff --git a/core/src/main/java/org/lflang/target/property/TracePluginProperty.java b/core/src/main/java/org/lflang/target/property/TracePluginProperty.java index 35bff7281d..f03175a063 100644 --- a/core/src/main/java/org/lflang/target/property/TracePluginProperty.java +++ b/core/src/main/java/org/lflang/target/property/TracePluginProperty.java @@ -4,64 +4,22 @@ import org.lflang.ast.ASTUtils; import org.lflang.lf.Element; import org.lflang.target.TargetConfig; -import org.lflang.target.property.TracePluginProperty.TracePluginOptions; +import org.lflang.target.property.type.LoggingType.LogLevel; import org.lflang.target.property.type.PrimitiveType; /** Property that provides an alternative tracing implementation. */ -public class TracePluginProperty extends TargetProperty { +/** The compiler to invoke, unless a build command has been specified. */ +public final class TracePluginProperty extends StringProperty { /** Singleton target property instance. */ public static final TracePluginProperty INSTANCE = new TracePluginProperty(); private TracePluginProperty() { - super(PrimitiveType.STRING); - } - - @Override - public TracePluginOptions initialValue() { - return null; - } - - @Override - protected TracePluginOptions fromAst(Element node, MessageReporter reporter) { - var s = ASTUtils.elementToSingleString(node); - if (s != null) { - return new TracePluginOptions(s); - } else { - reporter.at(node).error("Expected a string literal"); - return null; - } - } - - @Override - protected TracePluginOptions fromString(String s, MessageReporter reporter) { - return new TracePluginOptions(s); + super(); } @Override public String name() { return "trace-plugin"; } - - @Override - public Element toAstElement(TracePluginOptions value) { - return ASTUtils.toElement(value.implementationArchiveFile); - } - - public static class TracePluginOptions { - private final String implementationArchiveFile; - - public TracePluginOptions(String implementationArchiveFile) { - this.implementationArchiveFile = implementationArchiveFile; - } - - public String getImplementationArchiveFile() { - return implementationArchiveFile; - } - - @Override - public String toString() { - return getImplementationArchiveFile(); - } - } } From 03114f2b8488682b5625fb7ff27952884635d088 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 10 May 2024 16:54:26 +0200 Subject: [PATCH 24/35] Reject federations without federates --- .../java/org/lflang/federated/validation/FedValidator.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/main/java/org/lflang/federated/validation/FedValidator.java b/core/src/main/java/org/lflang/federated/validation/FedValidator.java index fc82733e0d..e9e3ab0fbc 100644 --- a/core/src/main/java/org/lflang/federated/validation/FedValidator.java +++ b/core/src/main/java/org/lflang/federated/validation/FedValidator.java @@ -18,6 +18,12 @@ public class FedValidator { public static void validateFederatedReactor(Reactor reactor, MessageReporter messageReporter) { if (!reactor.isFederated()) return; + if (reactor.getInstantiations().size() == 0) { + messageReporter + .at(reactor) + .error("The top-level reactor does not contain any federates"); + } + // Construct the set of excluded reactions for this federate. // If a reaction is a network reaction that belongs to this federate, we // don't need to perform this analysis. From 087b12806bb1f5f5c678f70c7558aa034b1fc427 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 10 May 2024 16:54:42 +0200 Subject: [PATCH 25/35] Traceplugin Cmake-gen fix --- .../src/main/java/org/lflang/generator/c/CCmakeGenerator.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/src/main/java/org/lflang/generator/c/CCmakeGenerator.java b/core/src/main/java/org/lflang/generator/c/CCmakeGenerator.java index c7da579884..df569d1b96 100644 --- a/core/src/main/java/org/lflang/generator/c/CCmakeGenerator.java +++ b/core/src/main/java/org/lflang/generator/c/CCmakeGenerator.java @@ -275,9 +275,7 @@ CodeBuilder generateCMakeCode( if (tracePlugin != null) { cMakeCode.pr( "set(LF_TRACE_PLUGIN " - + targetConfig - .getOrDefault(TracePluginProperty.INSTANCE) - .getImplementationArchiveFile() + + tracePlugin + " CACHE STRING \"\")\n"); } From feb50198e361aaaacf500d0df5fd0cf317c2e37f Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 10 May 2024 16:55:26 +0200 Subject: [PATCH 26/35] Add support for specifying a RUN cmd to be code-generated into the Dockerfile. --- .../generator/docker/CDockerGenerator.java | 8 +++-- .../generator/docker/DockerGenerator.java | 9 +++++ .../target/property/DockerProperty.java | 33 ++++++++----------- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/core/src/main/java/org/lflang/generator/docker/CDockerGenerator.java b/core/src/main/java/org/lflang/generator/docker/CDockerGenerator.java index 392b2e3422..3292485c57 100644 --- a/core/src/main/java/org/lflang/generator/docker/CDockerGenerator.java +++ b/core/src/main/java/org/lflang/generator/docker/CDockerGenerator.java @@ -72,14 +72,18 @@ protected String generateRunForBuildDependencies() { return """ # Install build dependencies RUN set -ex && apk add --no-cache %s musl-dev cmake make + # Optional user specified run command + %s """ - .formatted(compiler); + .formatted(compiler, userRunCommand()); } else { return """ + # Optional user specified run command + %s # Check for build dependencies RUN which make && which cmake && which %s """ - .formatted(compiler); + .formatted(userRunCommand(), compiler); } } diff --git a/core/src/main/java/org/lflang/generator/docker/DockerGenerator.java b/core/src/main/java/org/lflang/generator/docker/DockerGenerator.java index 203c5f94d7..2fc47e9f0e 100644 --- a/core/src/main/java/org/lflang/generator/docker/DockerGenerator.java +++ b/core/src/main/java/org/lflang/generator/docker/DockerGenerator.java @@ -42,6 +42,15 @@ public String baseImage() { return defaultImage(); } + public String userRunCommand() { + var runCmd = context.getTargetConfig().get(DockerProperty.INSTANCE).run(); + if (runCmd != null && !runCmd.isEmpty()) { + return "RUN " + runCmd; + } else { + return ""; + } + } + /** * Produce a DockerData object, which bundles all information needed to output a Dockerfile. * diff --git a/core/src/main/java/org/lflang/target/property/DockerProperty.java b/core/src/main/java/org/lflang/target/property/DockerProperty.java index e494419f67..5cc5d9c87b 100644 --- a/core/src/main/java/org/lflang/target/property/DockerProperty.java +++ b/core/src/main/java/org/lflang/target/property/DockerProperty.java @@ -9,6 +9,7 @@ import org.lflang.target.property.DockerProperty.DockerOptions; import org.lflang.target.property.type.DictionaryType; import org.lflang.target.property.type.DictionaryType.DictionaryElement; +import org.lflang.target.property.type.PlatformType; import org.lflang.target.property.type.PrimitiveType; import org.lflang.target.property.type.TargetPropertyType; import org.lflang.target.property.type.UnionType; @@ -35,6 +36,7 @@ public DockerOptions initialValue() { public DockerOptions fromAst(Element node, MessageReporter reporter) { var enabled = false; var from = ""; + var run = ""; var rti = DockerOptions.DOCKERHUB_RTI_IMAGE; if (node.getLiteral() != null) { @@ -45,15 +47,14 @@ public DockerOptions fromAst(Element node, MessageReporter reporter) { enabled = true; for (KeyValuePair entry : node.getKeyvalue().getPairs()) { DockerOption option = (DockerOption) DictionaryType.DOCKER_DICT.forName(entry.getName()); - if (option != null && option.equals(DockerOption.FROM)) { - from = ASTUtils.elementToSingleString(entry.getValue()); - } - if (option != null && option.equals(DockerOption.RTI_IMAGE)) { - rti = ASTUtils.elementToSingleString(entry.getValue()); + switch(option) { + case FROM -> from = ASTUtils.elementToSingleString(entry.getValue()); + case RUN -> run = ASTUtils.elementToSingleString(entry.getValue()); + case RTI_IMAGE -> rti = ASTUtils.elementToSingleString(entry.getValue()); } } } - return new DockerOptions(enabled, from, rti); + return new DockerOptions(enabled, from,run, rti); } @Override @@ -81,17 +82,10 @@ public Element toAstElement(DockerOptions value) { for (DockerOption opt : DockerOption.values()) { KeyValuePair pair = LfFactory.eINSTANCE.createKeyValuePair(); pair.setName(opt.toString()); - if (opt == DockerOption.FROM) { - if (value.from == null) { - continue; - } - pair.setValue(ASTUtils.toElement(value.from)); - } - if (opt == DockerOption.RTI_IMAGE) { - if (value.rti.equals(DockerOptions.DOCKERHUB_RTI_IMAGE)) { - continue; - } - pair.setValue(ASTUtils.toElement(value.rti)); + switch(opt) { + case FROM -> pair.setValue(ASTUtils.toElement(value.from)); + case RUN -> pair.setValue(ASTUtils.toElement(value.run)); + case RTI_IMAGE -> pair.setValue(ASTUtils.toElement(value.rti)); } kvp.getPairs().add(pair); } @@ -109,7 +103,7 @@ public String name() { } /** Settings related to Docker options. */ - public record DockerOptions(boolean enabled, String from, String rti) { + public record DockerOptions(boolean enabled, String from, String run, String rti) { /** Default location to pull the rti from. */ public static final String DOCKERHUB_RTI_IMAGE = "lflang/rti:rti"; @@ -118,7 +112,7 @@ public record DockerOptions(boolean enabled, String from, String rti) { public static final String LOCAL_RTI_IMAGE = "rti:local"; public DockerOptions(boolean enabled) { - this(enabled, "", DOCKERHUB_RTI_IMAGE); + this(enabled, "", "", DOCKERHUB_RTI_IMAGE); } } @@ -129,6 +123,7 @@ public DockerOptions(boolean enabled) { */ public enum DockerOption implements DictionaryElement { FROM("FROM", PrimitiveType.STRING), + RUN("RUN", PrimitiveType.STRING), RTI_IMAGE("rti-image", PrimitiveType.STRING); public final PrimitiveType type; From dbf281e6d7005846dab7a48d32f90ace5996aad4 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 8 May 2024 19:27:31 +0200 Subject: [PATCH 27/35] Also search for imported reactors in modes --- .../org/lflang/federated/generator/FederateInstance.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/src/main/java/org/lflang/federated/generator/FederateInstance.java b/core/src/main/java/org/lflang/federated/generator/FederateInstance.java index 4521546ae9..fdfa6c11bb 100644 --- a/core/src/main/java/org/lflang/federated/generator/FederateInstance.java +++ b/core/src/main/java/org/lflang/federated/generator/FederateInstance.java @@ -53,6 +53,7 @@ import org.lflang.lf.ImportedReactor; import org.lflang.lf.Input; import org.lflang.lf.Instantiation; +import org.lflang.lf.Mode; import org.lflang.lf.Output; import org.lflang.lf.Parameter; import org.lflang.lf.ParameterReference; @@ -320,6 +321,14 @@ private boolean references(Instantiation instantiation, ReactorDecl declaration) return true; } } + // Check if it is instantiated in a mode + for (Mode mode : reactorDef.getModes()) { + for (Instantiation child : mode.getInstantiations()) { + if (references(child, declaration)) { + return true; + } + } + } // Check if the reactor is a super class for (var parent : reactorDef.getSuperClasses()) { if (declaration instanceof Reactor r) { From 7ab342856534c6ef8cd2e8a09c2bb1b3da79af58 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 8 May 2024 19:54:52 +0200 Subject: [PATCH 28/35] Add test-case for the fix --- test/C/src/federated/ImportsInModes.lf | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 test/C/src/federated/ImportsInModes.lf diff --git a/test/C/src/federated/ImportsInModes.lf b/test/C/src/federated/ImportsInModes.lf new file mode 100644 index 0000000000..ac780c7000 --- /dev/null +++ b/test/C/src/federated/ImportsInModes.lf @@ -0,0 +1,15 @@ +target C { + timeout: 1 msec, +} + +import Count from "../lib/Count.lf" + +reactor R { + initial mode { + c = new Count() + } +} + +federated reactor { + fed = new R() +} \ No newline at end of file From ccb7f5672350b6763666ad22dc47740674537c2a Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 8 May 2024 19:55:06 +0200 Subject: [PATCH 29/35] Spotless --- core/src/main/java/org/lflang/ast/ToSExpr.java | 4 +--- .../federated/extensions/CExtensionUtils.java | 5 ++--- .../federated/extensions/FedTargetExtension.java | 1 - .../lflang/federated/generator/FedGenerator.java | 7 +++++-- .../federated/generator/FedTargetEmitter.java | 1 - .../lflang/federated/validation/FedValidator.java | 4 +--- .../org/lflang/generator/c/CCmakeGenerator.java | 5 +---- .../lflang/generator/c/CMainFunctionGenerator.java | 5 ++--- .../org/lflang/target/property/DockerProperty.java | 13 ++++++------- .../lflang/target/property/TracePluginProperty.java | 7 ------- test/C/src/federated/ImportsInModes.lf | 4 ++-- 11 files changed, 20 insertions(+), 36 deletions(-) diff --git a/core/src/main/java/org/lflang/ast/ToSExpr.java b/core/src/main/java/org/lflang/ast/ToSExpr.java index 49074626c9..a514d98234 100644 --- a/core/src/main/java/org/lflang/ast/ToSExpr.java +++ b/core/src/main/java/org/lflang/ast/ToSExpr.java @@ -328,9 +328,7 @@ public SExpr caseInitializer(Initializer object) { // | assign?='=' exprs+=Expression // ; return sList( - "initializer", - sList("expr", object.getExpr()), - sList("is-assign", object.isAssign())); + "initializer", sList("expr", object.getExpr()), sList("is-assign", object.isAssign())); } @Override diff --git a/core/src/main/java/org/lflang/federated/extensions/CExtensionUtils.java b/core/src/main/java/org/lflang/federated/extensions/CExtensionUtils.java index 51a446129d..d65ff0658d 100644 --- a/core/src/main/java/org/lflang/federated/extensions/CExtensionUtils.java +++ b/core/src/main/java/org/lflang/federated/extensions/CExtensionUtils.java @@ -7,8 +7,6 @@ import java.util.HashMap; import java.util.List; import java.util.regex.Pattern; -import java.util.stream.Collectors; - import org.lflang.InferredType; import org.lflang.MessageReporter; import org.lflang.ast.ASTUtils; @@ -203,7 +201,8 @@ public static void handleCompileDefinitions( definitions.put("NUMBER_OF_FEDERATES", String.valueOf(federateNames.size())); definitions.put("EXECUTABLE_PREAMBLE", ""); definitions.put("FEDERATE_ID", String.valueOf(federate.id)); - definitions.put("_LF_FEDERATE_NAMES_COMMA_SEPARATED", "\"" + String.join(",", federateNames) + "\""); + definitions.put( + "_LF_FEDERATE_NAMES_COMMA_SEPARATED", "\"" + String.join(",", federateNames) + "\""); CompileDefinitionsProperty.INSTANCE.update(federate.targetConfig, definitions); diff --git a/core/src/main/java/org/lflang/federated/extensions/FedTargetExtension.java b/core/src/main/java/org/lflang/federated/extensions/FedTargetExtension.java index 2fa73bb860..07a20809ef 100644 --- a/core/src/main/java/org/lflang/federated/extensions/FedTargetExtension.java +++ b/core/src/main/java/org/lflang/federated/extensions/FedTargetExtension.java @@ -2,7 +2,6 @@ import java.io.IOException; import java.util.List; - import org.lflang.InferredType; import org.lflang.MessageReporter; import org.lflang.federated.generator.FedConnectionInstance; diff --git a/core/src/main/java/org/lflang/federated/generator/FedGenerator.java b/core/src/main/java/org/lflang/federated/generator/FedGenerator.java index 388fe5e4a1..2f4a4dfb0a 100644 --- a/core/src/main/java/org/lflang/federated/generator/FedGenerator.java +++ b/core/src/main/java/org/lflang/federated/generator/FedGenerator.java @@ -20,7 +20,6 @@ import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.stream.Collectors; - import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.xtext.generator.JavaIoFileSystemAccess; @@ -168,7 +167,11 @@ public boolean doGenerate(Resource resource, LFGeneratorContext context) throws // Generate LF code for each federate. Map lf2lfCodeMapMap = new HashMap<>(); for (FederateInstance federate : federates) { - lf2lfCodeMapMap.putAll(fedEmitter.generateFederate(context, federate, federates.stream().map(fed -> fed.name).collect(Collectors.toList()))); + lf2lfCodeMapMap.putAll( + fedEmitter.generateFederate( + context, + federate, + federates.stream().map(fed -> fed.name).collect(Collectors.toList()))); } // Do not invoke target code generators if --no-compile flag is used. diff --git a/core/src/main/java/org/lflang/federated/generator/FedTargetEmitter.java b/core/src/main/java/org/lflang/federated/generator/FedTargetEmitter.java index d4460e1b29..deea984ca6 100644 --- a/core/src/main/java/org/lflang/federated/generator/FedTargetEmitter.java +++ b/core/src/main/java/org/lflang/federated/generator/FedTargetEmitter.java @@ -2,7 +2,6 @@ import java.io.IOException; import java.util.List; - import org.lflang.MessageReporter; import org.lflang.ast.FormattingUtil; import org.lflang.federated.extensions.FedTargetExtensionFactory; diff --git a/core/src/main/java/org/lflang/federated/validation/FedValidator.java b/core/src/main/java/org/lflang/federated/validation/FedValidator.java index e9e3ab0fbc..634a6399c1 100644 --- a/core/src/main/java/org/lflang/federated/validation/FedValidator.java +++ b/core/src/main/java/org/lflang/federated/validation/FedValidator.java @@ -19,9 +19,7 @@ public static void validateFederatedReactor(Reactor reactor, MessageReporter mes if (!reactor.isFederated()) return; if (reactor.getInstantiations().size() == 0) { - messageReporter - .at(reactor) - .error("The top-level reactor does not contain any federates"); + messageReporter.at(reactor).error("The top-level reactor does not contain any federates"); } // Construct the set of excluded reactions for this federate. diff --git a/core/src/main/java/org/lflang/generator/c/CCmakeGenerator.java b/core/src/main/java/org/lflang/generator/c/CCmakeGenerator.java index df569d1b96..9b984f5fc9 100644 --- a/core/src/main/java/org/lflang/generator/c/CCmakeGenerator.java +++ b/core/src/main/java/org/lflang/generator/c/CCmakeGenerator.java @@ -273,10 +273,7 @@ CodeBuilder generateCMakeCode( var tracePlugin = targetConfig.getOrDefault(TracePluginProperty.INSTANCE); System.out.println(tracePlugin); if (tracePlugin != null) { - cMakeCode.pr( - "set(LF_TRACE_PLUGIN " - + tracePlugin - + " CACHE STRING \"\")\n"); + cMakeCode.pr("set(LF_TRACE_PLUGIN " + tracePlugin + " CACHE STRING \"\")\n"); } // Setup main target for different platforms diff --git a/core/src/main/java/org/lflang/generator/c/CMainFunctionGenerator.java b/core/src/main/java/org/lflang/generator/c/CMainFunctionGenerator.java index f48e057648..603e7d7928 100644 --- a/core/src/main/java/org/lflang/generator/c/CMainFunctionGenerator.java +++ b/core/src/main/java/org/lflang/generator/c/CMainFunctionGenerator.java @@ -2,8 +2,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; - import org.lflang.generator.CodeBuilder; import org.lflang.target.TargetConfig; import org.lflang.target.property.FastProperty; @@ -100,7 +98,8 @@ private String generateSetDefaultCliOption() { ? String.join( "\n", "const char* _lf_default_argv[] = { " - + StringUtil.joinObjects(runCommand.stream().map(StringUtil::addDoubleQuotes).toList(), ", ") + + StringUtil.joinObjects( + runCommand.stream().map(StringUtil::addDoubleQuotes).toList(), ", ") + " };", "void lf_set_default_command_line_options() {", " default_argc = " + runCommand.size() + ";", diff --git a/core/src/main/java/org/lflang/target/property/DockerProperty.java b/core/src/main/java/org/lflang/target/property/DockerProperty.java index 5cc5d9c87b..c58e2bebf9 100644 --- a/core/src/main/java/org/lflang/target/property/DockerProperty.java +++ b/core/src/main/java/org/lflang/target/property/DockerProperty.java @@ -9,7 +9,6 @@ import org.lflang.target.property.DockerProperty.DockerOptions; import org.lflang.target.property.type.DictionaryType; import org.lflang.target.property.type.DictionaryType.DictionaryElement; -import org.lflang.target.property.type.PlatformType; import org.lflang.target.property.type.PrimitiveType; import org.lflang.target.property.type.TargetPropertyType; import org.lflang.target.property.type.UnionType; @@ -47,14 +46,14 @@ public DockerOptions fromAst(Element node, MessageReporter reporter) { enabled = true; for (KeyValuePair entry : node.getKeyvalue().getPairs()) { DockerOption option = (DockerOption) DictionaryType.DOCKER_DICT.forName(entry.getName()); - switch(option) { + switch (option) { case FROM -> from = ASTUtils.elementToSingleString(entry.getValue()); case RUN -> run = ASTUtils.elementToSingleString(entry.getValue()); case RTI_IMAGE -> rti = ASTUtils.elementToSingleString(entry.getValue()); } } } - return new DockerOptions(enabled, from,run, rti); + return new DockerOptions(enabled, from, run, rti); } @Override @@ -82,10 +81,10 @@ public Element toAstElement(DockerOptions value) { for (DockerOption opt : DockerOption.values()) { KeyValuePair pair = LfFactory.eINSTANCE.createKeyValuePair(); pair.setName(opt.toString()); - switch(opt) { - case FROM -> pair.setValue(ASTUtils.toElement(value.from)); - case RUN -> pair.setValue(ASTUtils.toElement(value.run)); - case RTI_IMAGE -> pair.setValue(ASTUtils.toElement(value.rti)); + switch (opt) { + case FROM -> pair.setValue(ASTUtils.toElement(value.from)); + case RUN -> pair.setValue(ASTUtils.toElement(value.run)); + case RTI_IMAGE -> pair.setValue(ASTUtils.toElement(value.rti)); } kvp.getPairs().add(pair); } diff --git a/core/src/main/java/org/lflang/target/property/TracePluginProperty.java b/core/src/main/java/org/lflang/target/property/TracePluginProperty.java index f03175a063..49bc833892 100644 --- a/core/src/main/java/org/lflang/target/property/TracePluginProperty.java +++ b/core/src/main/java/org/lflang/target/property/TracePluginProperty.java @@ -1,12 +1,5 @@ package org.lflang.target.property; -import org.lflang.MessageReporter; -import org.lflang.ast.ASTUtils; -import org.lflang.lf.Element; -import org.lflang.target.TargetConfig; -import org.lflang.target.property.type.LoggingType.LogLevel; -import org.lflang.target.property.type.PrimitiveType; - /** Property that provides an alternative tracing implementation. */ /** The compiler to invoke, unless a build command has been specified. */ public final class TracePluginProperty extends StringProperty { diff --git a/test/C/src/federated/ImportsInModes.lf b/test/C/src/federated/ImportsInModes.lf index ac780c7000..6fd4aa0ec5 100644 --- a/test/C/src/federated/ImportsInModes.lf +++ b/test/C/src/federated/ImportsInModes.lf @@ -1,5 +1,5 @@ target C { - timeout: 1 msec, + timeout: 1 msec } import Count from "../lib/Count.lf" @@ -12,4 +12,4 @@ reactor R { federated reactor { fed = new R() -} \ No newline at end of file +} From 251c6b47a739e7041812d519659e2f6ae4c8337a Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 10 May 2024 16:58:33 +0200 Subject: [PATCH 30/35] Bump reactor-c --- core/src/main/resources/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/lib/c/reactor-c b/core/src/main/resources/lib/c/reactor-c index 5e3bf4713e..20f9a3665a 160000 --- a/core/src/main/resources/lib/c/reactor-c +++ b/core/src/main/resources/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 5e3bf4713ebef4272d5c96c9f0501a6eb4e282dc +Subproject commit 20f9a3665a71c06171c3792a71d729848b51cf4c From 60c3ca12dc315cba1c9105a173c064b3843d81dc Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 10 May 2024 19:12:00 +0200 Subject: [PATCH 31/35] Add MockCancelIndicator and pass empty json string in LSP test --- .../java/org/lflang/tests/lsp/LspTests.java | 4 +++- .../org/lflang/tests/lsp/MockCancelIndicator.java | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 core/src/integrationTest/java/org/lflang/tests/lsp/MockCancelIndicator.java diff --git a/core/src/integrationTest/java/org/lflang/tests/lsp/LspTests.java b/core/src/integrationTest/java/org/lflang/tests/lsp/LspTests.java index 6b92b46754..2903d6c4ff 100644 --- a/core/src/integrationTest/java/org/lflang/tests/lsp/LspTests.java +++ b/core/src/integrationTest/java/org/lflang/tests/lsp/LspTests.java @@ -12,6 +12,7 @@ import java.util.function.Predicate; import org.eclipse.emf.common.util.URI; import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.xtext.util.CancelIndicator; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.lflang.generator.IntegratedBuilder; @@ -229,8 +230,9 @@ private static Predicate> diagnosticsIncludeText( */ private void runTest(Path test) { MockReportProgress reportProgress = new MockReportProgress(); + MockCancelIndicator cancelIndicator = new MockCancelIndicator(); try { - builder.run(URI.createFileURI(test.toString()), false, reportProgress, () -> false); + builder.run(URI.createFileURI(test.toString()), "", false, reportProgress, cancelIndicator); } catch (Exception e) { e.printStackTrace(); throw e; diff --git a/core/src/integrationTest/java/org/lflang/tests/lsp/MockCancelIndicator.java b/core/src/integrationTest/java/org/lflang/tests/lsp/MockCancelIndicator.java new file mode 100644 index 0000000000..c03e7c5f28 --- /dev/null +++ b/core/src/integrationTest/java/org/lflang/tests/lsp/MockCancelIndicator.java @@ -0,0 +1,14 @@ +package org.lflang.tests.lsp; + + +import org.eclipse.xtext.util.CancelIndicator; +/** + * Mock the CancelIndicator interface for testing. + * @author Erling Jellum + */ +public class MockCancelIndicator implements CancelIndicator { + @Override + public boolean isCanceled() { + return false; + } +} \ No newline at end of file From fe6132c0b79e684481e2cee727f9cf566eb37de5 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 10 May 2024 19:13:42 +0200 Subject: [PATCH 32/35] Bump reactor-c --- core/src/main/resources/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/lib/c/reactor-c b/core/src/main/resources/lib/c/reactor-c index 20f9a3665a..8ce3b84b63 160000 --- a/core/src/main/resources/lib/c/reactor-c +++ b/core/src/main/resources/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 20f9a3665a71c06171c3792a71d729848b51cf4c +Subproject commit 8ce3b84b63e52077e755d944f23b6d44edeb5945 From be1d3043de45d30cd9f1dafc846be10b9d8252d2 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 10 May 2024 19:17:27 +0200 Subject: [PATCH 33/35] Spotless --- .../java/org/lflang/tests/lsp/LspTests.java | 1 - .../org/lflang/tests/lsp/MockCancelIndicator.java | 13 +++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core/src/integrationTest/java/org/lflang/tests/lsp/LspTests.java b/core/src/integrationTest/java/org/lflang/tests/lsp/LspTests.java index 2903d6c4ff..842b8e4dfb 100644 --- a/core/src/integrationTest/java/org/lflang/tests/lsp/LspTests.java +++ b/core/src/integrationTest/java/org/lflang/tests/lsp/LspTests.java @@ -12,7 +12,6 @@ import java.util.function.Predicate; import org.eclipse.emf.common.util.URI; import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.xtext.util.CancelIndicator; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.lflang.generator.IntegratedBuilder; diff --git a/core/src/integrationTest/java/org/lflang/tests/lsp/MockCancelIndicator.java b/core/src/integrationTest/java/org/lflang/tests/lsp/MockCancelIndicator.java index c03e7c5f28..9fe243d481 100644 --- a/core/src/integrationTest/java/org/lflang/tests/lsp/MockCancelIndicator.java +++ b/core/src/integrationTest/java/org/lflang/tests/lsp/MockCancelIndicator.java @@ -1,14 +1,15 @@ package org.lflang.tests.lsp; - import org.eclipse.xtext.util.CancelIndicator; + /** * Mock the CancelIndicator interface for testing. + * * @author Erling Jellum */ public class MockCancelIndicator implements CancelIndicator { - @Override - public boolean isCanceled() { - return false; - } -} \ No newline at end of file + @Override + public boolean isCanceled() { + return false; + } +} From 22cca2b771df48fe051b2755ed074c636c76683c Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Fri, 10 May 2024 18:14:10 -0700 Subject: [PATCH 34/35] Address test failure --- .../lflang/tests/compiler/LinguaFrancaValidationTest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/test/java/org/lflang/tests/compiler/LinguaFrancaValidationTest.java b/core/src/test/java/org/lflang/tests/compiler/LinguaFrancaValidationTest.java index a400ad69d3..5454f44fa3 100644 --- a/core/src/test/java/org/lflang/tests/compiler/LinguaFrancaValidationTest.java +++ b/core/src/test/java/org/lflang/tests/compiler/LinguaFrancaValidationTest.java @@ -1086,7 +1086,10 @@ public void testFederationSupport() throws Exception { parseWithoutError( """ target %s - federated reactor {} + reactor Foo {} + federated reactor { + foo = new Foo() + } """ .formatted(target)); From 91c81f98df888e63735047e5f81d55595b1f2c37 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Fri, 10 May 2024 21:41:15 -0700 Subject: [PATCH 35/35] Point reactor-c to HEAD of main --- core/src/main/resources/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/lib/c/reactor-c b/core/src/main/resources/lib/c/reactor-c index 8ce3b84b63..1bd513091c 160000 --- a/core/src/main/resources/lib/c/reactor-c +++ b/core/src/main/resources/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 8ce3b84b63e52077e755d944f23b6d44edeb5945 +Subproject commit 1bd513091c57e65f7e4d3eae972b65c01f529e28