diff --git a/.gitignore b/.gitignore index b25ba96973..f73759d797 100644 --- a/.gitignore +++ b/.gitignore @@ -104,7 +104,7 @@ crashlytics-build.properties fabric.properties ### Maven ### -target/ +/org.lflang*/target/ pom.xml.tag pom.xml.releaseBackup pom.xml.versionsBackup diff --git a/org.lflang/src/org/lflang/LinguaFranca.xtext b/org.lflang/src/org/lflang/LinguaFranca.xtext index 64a634f9f5..edc887d987 100644 --- a/org.lflang/src/org/lflang/LinguaFranca.xtext +++ b/org.lflang/src/org/lflang/LinguaFranca.xtext @@ -90,6 +90,7 @@ Reactor: '{' ( (preambles+=Preamble) | (stateVars+=StateVar) + | (methods+=Method) | (inputs+=Input) | (outputs+=Output) | (timers+=Timer) @@ -143,6 +144,18 @@ TargetDecl: ) ';'? ; +Method: + const?='const'? 'method' name=ID + '(' (arguments+=MethodArgument (',' arguments+=MethodArgument)*)? ')' + (':' return=Type)? + code=Code + ';'? +; + +MethodArgument: + name=ID (':' type=Type)? +; + Input: mutable?='mutable'? 'input' (widthSpec=WidthSpec)? name=ID (':' type=Type)? ';'?; @@ -457,7 +470,7 @@ Token: 'target' | 'import' | 'main' | 'realtime' | 'reactor' | 'state' | 'time' | 'mutable' | 'input' | 'output' | 'timer' | 'action' | 'reaction' | 'startup' | 'shutdown' | 'after' | 'deadline' | 'mutation' | 'preamble' | - 'new' | 'federated' | 'at' | 'as' | 'from' | + 'new' | 'federated' | 'at' | 'as' | 'from' | 'const' | 'method' | // Other terminals NEGINT | TRUE | FALSE | // Action origins diff --git a/org.lflang/src/org/lflang/generator/cpp/CppMethodGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppMethodGenerator.kt new file mode 100644 index 0000000000..0ad793413c --- /dev/null +++ b/org.lflang/src/org/lflang/generator/cpp/CppMethodGenerator.kt @@ -0,0 +1,65 @@ +/************* + * Copyright (c) 2021, TU Dresden. + + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************/ + +package org.lflang.generator.cpp + +import org.lflang.InferredType +import org.lflang.generator.PrependOperator +import org.lflang.lf.Method +import org.lflang.lf.MethodArgument +import org.lflang.lf.Reactor +import org.lflang.toText + +/** A C++ code generator for state variables */ +class CppMethodGenerator(private val reactor: Reactor) { + + private val Method.targetType: String get() = if (`return` != null) InferredType.fromAST(`return`).targetType else "void" + private val MethodArgument.targetType: String get() = InferredType.fromAST(type).targetType + + private val Method.cppArgs get() = this.arguments.map { "${it.targetType} ${it.name}" } + private val Method.constQualifier get() = if (isConst) " const" else "" + + private fun generateDefinition(method: Method): String = with(PrependOperator) { + with(method) { + """ + |${reactor.templateLine} + |$targetType ${reactor.templateName}::Inner::$name(${cppArgs.joinToString(", ")})$constQualifier { + ${" | "..code.toText()} + |} + """.trimMargin() + } + } + + private fun generateDeclaration(method: Method): String = with(method) { + "$targetType $name(${cppArgs.joinToString(", ")})$constQualifier;" + } + + /** Get all method definitions */ + fun generateDefinitions() = + reactor.methods.joinToString("\n", "// methods\n", "\n") { generateDefinition(it) } + + /** Get all method declarations */ + fun generateDeclarations() = + reactor.methods.joinToString("\n", "// methods\n", "\n") { generateDeclaration(it) } +} \ No newline at end of file diff --git a/org.lflang/src/org/lflang/generator/cpp/CppReactorGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppReactorGenerator.kt index 8a760315d3..fd7c293dba 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppReactorGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppReactorGenerator.kt @@ -50,6 +50,7 @@ class CppReactorGenerator(private val reactor: Reactor, fileConfig: CppFileConfi private val parameters = CppParameterGenerator(reactor) private val state = CppStateGenerator(reactor) + private val methods = CppMethodGenerator(reactor) private val instances = CppInstanceGenerator(reactor, fileConfig, errorReporter) private val timers = CppTimerGenerator(reactor) private val actions = CppActionGenerator(reactor, errorReporter) @@ -96,6 +97,7 @@ class CppReactorGenerator(private val reactor: Reactor, fileConfig: CppFileConfi | class Inner: public lfutil::LFScope { ${" | "..parameters.generateDeclarations()} ${" | "..state.generateDeclarations()} + ${" | "..methods.generateDeclarations()} ${" | "..constructor.generateInnerDeclaration()} ${" | "..reactions.generateBodyDeclarations()} ${" | "..reactions.generateDeadlineHandlerDeclarations()} @@ -135,6 +137,8 @@ class CppReactorGenerator(private val reactor: Reactor, fileConfig: CppFileConfi | ${" |"..assemble.generateDefinition()} | + ${" |"..methods.generateDefinitions()} + | ${" |"..reactions.generateBodyDefinitions()} ${" |"..reactions.generateDeadlineHandlerDefinitions()} """.trimMargin() diff --git a/test/Cpp/src/target/Methods.lf b/test/Cpp/src/target/Methods.lf new file mode 100644 index 0000000000..f9c063def3 --- /dev/null +++ b/test/Cpp/src/target/Methods.lf @@ -0,0 +1,29 @@ +target Cpp; + +main reactor { + + state foo:int(2); + + const method getFoo(): int {= + return foo; + =} + + method add(x:int) {= + foo += x; + =} + + reaction(startup){= + std::cout << "Foo is initialized to " << getFoo() << '\n'; + if (getFoo() != 2) { + std::cerr << "Error: expected 2!\n"; + exit(1); + } + + add(40); + std::cout << "2 + 40 = " << getFoo() << '\n'; + if (getFoo() != 42) { + std::cerr << "Error: expected 42!\n"; + exit(2); + } + =} +}