diff --git a/.gitignore b/.gitignore index 95e6b465..e0b6b59f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,5 @@ embeddeds/haskell/dist # Python *.py[cod] - - - +.DS_Store +.idea diff --git a/embeddeds/groovy/.gitignore b/embeddeds/groovy/.gitignore index c45f5337..3ad10a67 100644 --- a/embeddeds/groovy/.gitignore +++ b/embeddeds/groovy/.gitignore @@ -4,4 +4,6 @@ GroovuinoML/.settings GroovuinoML/.classpath GroovuinoML/.project -target \ No newline at end of file +target +.gradle +build \ No newline at end of file diff --git a/embeddeds/groovy/GroovuinoML/.gitignore b/embeddeds/groovy/GroovuinoML/.gitignore deleted file mode 100644 index ae3c1726..00000000 --- a/embeddeds/groovy/GroovuinoML/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/bin/ diff --git a/embeddeds/groovy/GroovuinoML/build.bat b/embeddeds/groovy/GroovuinoML/build.bat deleted file mode 100644 index 7e7c3221..00000000 --- a/embeddeds/groovy/GroovuinoML/build.bat +++ /dev/null @@ -1 +0,0 @@ -mvn clean compile assembly:single \ No newline at end of file diff --git a/embeddeds/groovy/GroovuinoML/pom.xml b/embeddeds/groovy/GroovuinoML/pom.xml deleted file mode 100644 index bf35a970..00000000 --- a/embeddeds/groovy/GroovuinoML/pom.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - 4.0.0 - io.github.mosser.arduinoml - dsl-groovy - 1.0 - jar - GroovuinoML: ArduinoML DSL embeded in Groovy - - - 1.8 - 1.8 - UTF-8 - 2.3.10 - 2.3 - - - - - io.github.mosser.arduinoml - kernel-jvm - 1.0 - - - org.codehaus.groovy - groovy-all - ${groovy.version} - - - - src/main/groovy - - - maven-compiler-plugin - - 3.1 - - groovy-eclipse-compiler - - - - org.codehaus.groovy - groovy-eclipse-compiler - 2.9.1-01 - - - - org.codehaus.groovy - groovy-eclipse-batch - 2.3.7-01 - - - - - maven-assembly-plugin - - - - main.groovy.groovuinoml.main.GroovuinoML - - - - jar-with-dependencies - - - - - - diff --git a/embeddeds/groovy/GroovuinoML/run.bat b/embeddeds/groovy/GroovuinoML/run.bat deleted file mode 100644 index 23f8966a..00000000 --- a/embeddeds/groovy/GroovuinoML/run.bat +++ /dev/null @@ -1 +0,0 @@ -java -jar target\dsl-groovy-1.0-jar-with-dependencies.jar scripts\Switch.groovy > result.ino \ No newline at end of file diff --git a/embeddeds/groovy/GroovuinoML/src/main/groovy/groovuinoml/dsl/GroovuinoMLBasescript.groovy b/embeddeds/groovy/GroovuinoML/src/main/groovy/groovuinoml/dsl/GroovuinoMLBasescript.groovy deleted file mode 100644 index 9761b94d..00000000 --- a/embeddeds/groovy/GroovuinoML/src/main/groovy/groovuinoml/dsl/GroovuinoMLBasescript.groovy +++ /dev/null @@ -1,77 +0,0 @@ -package main.groovy.groovuinoml.dsl - -import java.util.List; - -import io.github.mosser.arduinoml.kernel.behavioral.Action -import io.github.mosser.arduinoml.kernel.behavioral.State -import io.github.mosser.arduinoml.kernel.structural.Actuator -import io.github.mosser.arduinoml.kernel.structural.Sensor -import io.github.mosser.arduinoml.kernel.structural.SIGNAL - -abstract class GroovuinoMLBasescript extends Script { - // sensor "name" pin n - def sensor(String name) { - [pin: { n -> ((GroovuinoMLBinding)this.getBinding()).getGroovuinoMLModel().createSensor(name, n) }, - onPin: { n -> ((GroovuinoMLBinding)this.getBinding()).getGroovuinoMLModel().createSensor(name, n)}] - } - - // actuator "name" pin n - def actuator(String name) { - [pin: { n -> ((GroovuinoMLBinding)this.getBinding()).getGroovuinoMLModel().createActuator(name, n) }] - } - - // state "name" means actuator becomes signal [and actuator becomes signal]*n - def state(String name) { - List actions = new ArrayList() - ((GroovuinoMLBinding) this.getBinding()).getGroovuinoMLModel().createState(name, actions) - // recursive closure to allow multiple and statements - def closure - closure = { actuator -> - [becomes: { signal -> - Action action = new Action() - action.setActuator(actuator instanceof String ? (Actuator)((GroovuinoMLBinding)this.getBinding()).getVariable(actuator) : (Actuator)actuator) - action.setValue(signal instanceof String ? (SIGNAL)((GroovuinoMLBinding)this.getBinding()).getVariable(signal) : (SIGNAL)signal) - actions.add(action) - [and: closure] - }] - } - [means: closure] - } - - // initial state - def initial(state) { - ((GroovuinoMLBinding) this.getBinding()).getGroovuinoMLModel().setInitialState(state instanceof String ? (State)((GroovuinoMLBinding)this.getBinding()).getVariable(state) : (State)state) - } - - // from state1 to state2 when sensor becomes signal - def from(state1) { - [to: { state2 -> - [when: { sensor -> - [becomes: { signal -> - ((GroovuinoMLBinding) this.getBinding()).getGroovuinoMLModel().createTransition( - state1 instanceof String ? (State)((GroovuinoMLBinding)this.getBinding()).getVariable(state1) : (State)state1, - state2 instanceof String ? (State)((GroovuinoMLBinding)this.getBinding()).getVariable(state2) : (State)state2, - sensor instanceof String ? (Sensor)((GroovuinoMLBinding)this.getBinding()).getVariable(sensor) : (Sensor)sensor, - signal instanceof String ? (SIGNAL)((GroovuinoMLBinding)this.getBinding()).getVariable(signal) : (SIGNAL)signal) - }] - }] - }] - } - - // export name - def export(String name) { - println(((GroovuinoMLBinding) this.getBinding()).getGroovuinoMLModel().generateCode(name).toString()) - } - - // disable run method while running - int count = 0 - abstract void scriptBody() - def run() { - if(count == 0) { - count++ - scriptBody() - } else { - println "Run method is disabled" - } - } -} diff --git a/embeddeds/groovy/GroovuinoML/src/main/groovy/groovuinoml/dsl/GroovuinoMLBinding.java b/embeddeds/groovy/GroovuinoML/src/main/groovy/groovuinoml/dsl/GroovuinoMLBinding.java deleted file mode 100644 index 98cf3f38..00000000 --- a/embeddeds/groovy/GroovuinoML/src/main/groovy/groovuinoml/dsl/GroovuinoMLBinding.java +++ /dev/null @@ -1,53 +0,0 @@ -package main.groovy.groovuinoml.dsl; - -import java.util.Map; - -import groovy.lang.Binding; -import groovy.lang.Script; - -public class GroovuinoMLBinding extends Binding { - // can be useful to return the script in case of syntax trick - private Script script; - - private GroovuinoMLModel model; - - public GroovuinoMLBinding() { - super(); - } - - @SuppressWarnings("rawtypes") - public GroovuinoMLBinding(Map variables) { - super(variables); - } - - public GroovuinoMLBinding(Script script) { - super(); - this.script = script; - } - - public void setScript(Script script) { - this.script = script; - } - - public void setGroovuinoMLModel(GroovuinoMLModel model) { - this.model = model; - } - - public Object getVariable(String name) { - // Easter egg (to show you this trick: seb is now a keyword!) - if ("seb".equals(name)) { - // could do something else like: ((App) this.getVariable("app")).action(); - System.out.println("Seb, c'est bien"); - return script; - } - return super.getVariable(name); - } - - public void setVariable(String name, Object value) { - super.setVariable(name, value); - } - - public GroovuinoMLModel getGroovuinoMLModel() { - return this.model; - } -} diff --git a/embeddeds/groovy/GroovuinoML/src/main/groovy/groovuinoml/dsl/GroovuinoMLDSL.groovy b/embeddeds/groovy/GroovuinoML/src/main/groovy/groovuinoml/dsl/GroovuinoMLDSL.groovy deleted file mode 100644 index 8595343a..00000000 --- a/embeddeds/groovy/GroovuinoML/src/main/groovy/groovuinoml/dsl/GroovuinoMLDSL.groovy +++ /dev/null @@ -1,65 +0,0 @@ -package main.groovy.groovuinoml.dsl - -import org.codehaus.groovy.control.CompilerConfiguration -import org.codehaus.groovy.control.customizers.SecureASTCustomizer -import io.github.mosser.arduinoml.kernel.structural.SIGNAL - -class GroovuinoMLDSL { - private GroovyShell shell - private CompilerConfiguration configuration - private GroovuinoMLBinding binding - private GroovuinoMLBasescript basescript - - GroovuinoMLDSL() { - binding = new GroovuinoMLBinding() - binding.setGroovuinoMLModel(new GroovuinoMLModel(binding)); - configuration = getDSLConfiguration() - configuration.setScriptBaseClass("main.groovy.groovuinoml.dsl.GroovuinoMLBasescript") - shell = new GroovyShell(configuration) - - binding.setVariable("high", SIGNAL.HIGH) - binding.setVariable("low", SIGNAL.LOW) - } - - private static CompilerConfiguration getDSLConfiguration() { - def secure = new SecureASTCustomizer() - secure.with { - //disallow closure creation - closuresAllowed = false - //disallow method definitions - methodDefinitionAllowed = true - //empty white list => forbid imports - importsWhitelist = [ - 'java.lang.*' - ] - staticImportsWhitelist = [] - staticStarImportsWhitelist= [] - //language tokens disallowed -// tokensBlacklist= [] - //language tokens allowed - tokensWhitelist= [] - //types allowed to be used (including primitive types) - constantTypesClassesWhiteList= [ - int, Integer, Number, Integer.TYPE, String, Object - ] - //classes who are allowed to be receivers of method calls - receiversClassesWhiteList= [ - int, Number, Integer, String, Object - ] - } - - def configuration = new CompilerConfiguration() - configuration.addCompilationCustomizers(secure) - - return configuration - } - - void eval(File scriptFile) { - Script script = shell.parse(scriptFile) - - binding.setScript(script) - script.setBinding(binding) - - script.run() - } -} diff --git a/embeddeds/groovy/GroovuinoML/src/main/groovy/groovuinoml/dsl/GroovuinoMLModel.java b/embeddeds/groovy/GroovuinoML/src/main/groovy/groovuinoml/dsl/GroovuinoMLModel.java deleted file mode 100644 index a0ca77a9..00000000 --- a/embeddeds/groovy/GroovuinoML/src/main/groovy/groovuinoml/dsl/GroovuinoMLModel.java +++ /dev/null @@ -1,79 +0,0 @@ -package main.groovy.groovuinoml.dsl; - -import java.util.*; - -import groovy.lang.Binding; -import io.github.mosser.arduinoml.kernel.App; -import io.github.mosser.arduinoml.kernel.behavioral.Action; -import io.github.mosser.arduinoml.kernel.behavioral.State; -import io.github.mosser.arduinoml.kernel.behavioral.Transition; -import io.github.mosser.arduinoml.kernel.generator.ToWiring; -import io.github.mosser.arduinoml.kernel.generator.Visitor; -import io.github.mosser.arduinoml.kernel.structural.Actuator; -import io.github.mosser.arduinoml.kernel.structural.Brick; -import io.github.mosser.arduinoml.kernel.structural.SIGNAL; -import io.github.mosser.arduinoml.kernel.structural.Sensor; - -public class GroovuinoMLModel { - private List bricks; - private List states; - private State initialState; - - private Binding binding; - - public GroovuinoMLModel(Binding binding) { - this.bricks = new ArrayList(); - this.states = new ArrayList(); - this.binding = binding; - } - - public void createSensor(String name, Integer pinNumber) { - Sensor sensor = new Sensor(); - sensor.setName(name); - sensor.setPin(pinNumber); - this.bricks.add(sensor); - this.binding.setVariable(name, sensor); -// System.out.println("> sensor " + name + " on pin " + pinNumber); - } - - public void createActuator(String name, Integer pinNumber) { - Actuator actuator = new Actuator(); - actuator.setName(name); - actuator.setPin(pinNumber); - this.bricks.add(actuator); - this.binding.setVariable(name, actuator); - } - - public void createState(String name, List actions) { - State state = new State(); - state.setName(name); - state.setActions(actions); - this.states.add(state); - this.binding.setVariable(name, state); - } - - public void createTransition(State from, State to, Sensor sensor, SIGNAL value) { - Transition transition = new Transition(); - transition.setNext(to); - transition.setSensor(sensor); - transition.setValue(value); - from.setTransition(transition); - } - - public void setInitialState(State state) { - this.initialState = state; - } - - @SuppressWarnings("rawtypes") - public Object generateCode(String appName) { - App app = new App(); - app.setName(appName); - app.setBricks(this.bricks); - app.setStates(this.states); - app.setInitial(this.initialState); - Visitor codeGenerator = new ToWiring(); - app.accept(codeGenerator); - - return codeGenerator.getResult(); - } -} diff --git a/embeddeds/groovy/GroovuinoML/src/main/groovy/groovuinoml/main/GroovuinoML.java b/embeddeds/groovy/GroovuinoML/src/main/groovy/groovuinoml/main/GroovuinoML.java deleted file mode 100644 index b71f3818..00000000 --- a/embeddeds/groovy/GroovuinoML/src/main/groovy/groovuinoml/main/GroovuinoML.java +++ /dev/null @@ -1,24 +0,0 @@ -package main.groovy.groovuinoml.main; - -import java.io.File; - -import main.groovy.groovuinoml.dsl.GroovuinoMLDSL; - -/** - * This main takes one argument: the path to the Groovy script file to execute. - * This Groovy script file must follow GroovuinoML DSL's rules. - * - * "We've Got A Groovy Thing Goin'"! - * - * @author Thomas Moreau - */ -public class GroovuinoML { - public static void main(String[] args) { - GroovuinoMLDSL dsl = new GroovuinoMLDSL(); - if(args.length > 0) { - dsl.eval(new File(args[0])); - } else { - System.out.println("/!\\ Missing arg: Please specify the path to a Groovy script file to execute"); - } - } -} diff --git a/embeddeds/groovy/README.md b/embeddeds/groovy/README.md index 264e1045..b7f2115d 100644 --- a/embeddeds/groovy/README.md +++ b/embeddeds/groovy/README.md @@ -1,20 +1,88 @@ -# GroovuinoML +# GroovuinoML + # ArduinoML implementation using Groovy This project is a quick and dirty implementation of the ArduinoML language using Groovy as a host language. -(Note: groovy means fashionable and exciting) +*(Note: groovy means fashionable and exciting)* + +## Get started (quickly) + +The bundled `run.sh` script will build and run the project in a single command. It will also download and install the +required dependencies if they are missing from your system. It's the perfect way to get started quickly! Here are some examples of usage: +```bash +# Run the project with the scripts/Switch.groovy and the first found JAR file +./run.sh + +# Run the project with a specific Groovy script file and the first found JAR file +./run.sh + +# Run the project with a specific Groovy script file and a specific JAR file +./run.sh [-p ] +``` + +> Please note that if your script is named `Example.groovy`, then a file `Example.ino` will be generated in the current +working directory. + +That's it! Now you can then upload the file to your Arduino board and have fun. + +## Bump to Java 17 🕺 + +In order to provide a more current implementation of this project with the latest Groovy and Java LTS version, the +project has been bumped to Java 17 and Groovy 4.0.6 (from Java 8 and Groovy 2.3.10, *sic*). The project is now +compatible with the JDK17 which most students run nowadays. + +### Maven + +As a consequence of bumping the Java version of the kernel and this project to release 17, the compilation methods +described at the end of this README are not up-to-date due to an incompatibility between Java 17 and the Eclipse Maven +Compiler (required for Groovy). Therefore, Maven has been removed from the project. + +### Gradle + +For reasons given above, this project has been switched over to Gradle. The Gradle build file is located in +the `build.gradle` file. + +To build (and package) the project manually, you can build the project and package it through an IDE (Eclipse, IntelliJ, +etc.), as usual, or you can use the Gradle wrapper: + +```bash +# Compile and assemble a (fat) JAR with all dependencies +./gradlew shadowJar +``` + +Alternatively, you can use the `build.sh` script, as it will also install the kernel in your local Maven repository and +package the project in ``build/libs/dsl-groovy-all.jar``. + +```bash +# Optional if you plan to use run.sh +./build.sh +``` + +Then, once the (fat) jar has been assembled, you can run it with: + +```bash +# Run the project directly from the JAR +java -jar build\libs\dsl-groovy-all.jar scripts\Switch.groovy > Switch.ino + +# Run from the run.sh script (redirects the output to a file named after the script basename) +./run.sh scripts\Switch.groovy +``` ## Intentions - * Have the easiest syntax possible, undestandable by any "domain" person (ie. knows what is a sensor, button...) - * Mix Java and Groovy code to proove it can be used in a Java based application - * Use method chaining and Groovy's flexible syntax - * The code could have been shorter in a single file but it is always better to separate the different key parts of the DSL +* Have the easiest syntax possible, understandable by any "domain" person (ie. knows what is a sensor, button...) +* Mix Java and Groovy code to prove it can be used in a Java based application +* Use method chaining and Groovy's flexible syntax +* The code could have been shorter in a single file, but it is always better to separate the different key parts of the + DSL ## Limitations - * Code completion will not be supported by default by Eclipse. But it is possible to create the associated DSL Descriptor (https://spring.io/blog/2011/05/09/better-dsl-support-in-groovy-eclipse). The DSLD file that you can find in this project works but is not complete. This is more an example than a perfect implementation. - * The syntax could be improved by using some meta-programming capabilities of Groovy and redefining some reserved keywords like +* Code completion will not be supported by default by Eclipse. But it is possible to create the associated DSL + Descriptor (https://spring.io/blog/2011/05/09/better-dsl-support-in-groovy-eclipse). The DSLD file that you can find + in this project works but is not complete. This is more an example than a perfect implementation. +* The syntax could be improved by using some meta-programming capabilities of Groovy and redefining some reserved + keywords like `is` ## Syntax example @@ -35,6 +103,7 @@ export "Switch!" ``` ## Another example + ```Groovy sensor "button" pin 9 actuator "led1" pin 12 @@ -53,6 +122,7 @@ export "Switch!" ``` ## An even better example with latest commit, to allow using "" to access values, making the syntax more homogeneous: + ```Groovy sensor "button" pin 9 actuator "led1" pin 12 @@ -72,11 +142,38 @@ export "Switch!" ## Requirements - * The project is delivered as a maven 3 artefact - * The code relies on the [JVM kernel](https://github.com/mosser/ArduinoML-kernel/tree/master/kernels/jvm) defined in this repository +### Current + +* **Java 17 (or higher)** +* The project is delivered as a Graddle 7 artefact +* The code relies on the [JVM kernel](https://github.com/mosser/ArduinoML-kernel/tree/master/kernels/jvm) defined in + this repository + * `mvn install` it before compiling or running this example +* (The code can be compiled using `./gradlew ./gradlew compileGroovy compileJava`) +* Or an executable jar can be built using `./gradlew shadowJar` +* Then, to run the Switch example: + * `java -jar build\libs\dsl-groovy-all.jar scripts\Switch.groovy` +* ...it is also possible to use the .bat files build.bat and run.bat (Windows) +* ...and the .sh files build.sh and/or run.sh (macOS/Linux) + +### Deprecated + +* Java 1.8 +* The project is delivered as a Maven 3 artefact +* The code relies on the [JVM kernel](https://github.com/mosser/ArduinoML-kernel/tree/master/kernels/jvm) defined in + this repository * `mvn install` it before compiling or running this example - * (The code can be compiled using `mvn clean install`) - * Or an executable jar can be built using `mvn clean compile assembly:single` - * Then, to run the Switch example: +* (The code can be compiled using `mvn clean install`) +* Or an executable jar can be built using `mvn clean compile assembly:single` +* Then, to run the Switch example: * `java -jar target\dsl-groovy-1.0-jar-with-dependencies.jar scripts\Switch.groovy` - * ... it is also possible to use the .bat files build.bat and run.bat (Windows) \ No newline at end of file +* ... it is also possible to use the .bat files build.bat and run.bat (Windows) + +> ⚠️ See "Bump to Java 17" section for more information about the new build process. + +## Known issues + +* You need to apply a chmod +x to the `build.sh` and `run.sh` scripts if you are on Linux or macOS; +* You may need to edit the `build.sh` and `run.sh` scripts to change the path to the `kernels` directory if you are not + using the whole cloned repository; +* You need to delete the `build` directory if you want to rebuild the kernel (`run.sh` does that for you) \ No newline at end of file diff --git a/embeddeds/groovy/build.bat b/embeddeds/groovy/build.bat new file mode 100644 index 00000000..3633c9f8 --- /dev/null +++ b/embeddeds/groovy/build.bat @@ -0,0 +1 @@ +gradlew.bat shadowJar \ No newline at end of file diff --git a/embeddeds/groovy/build.gradle b/embeddeds/groovy/build.gradle new file mode 100644 index 00000000..0827b404 --- /dev/null +++ b/embeddeds/groovy/build.gradle @@ -0,0 +1,22 @@ +plugins { + id 'com.github.johnrengelman.shadow' version '7.1.2' + id 'groovy' + id 'java' +} + +archivesBaseName = 'dsl-groovy' +configurations { provided; inlib } +repositories { mavenCentral(); mavenLocal() } + +dependencies { + implementation 'org.apache.groovy:groovy:4.0.6' + shadow 'org.apache.groovy:groovy:4.0.6' + implementation 'io.github.mosser.arduinoml:kernel-jvm:1.0' + shadow 'io.github.mosser.arduinoml:kernel-jvm:1.0' +} + +jar { + manifest { + attributes 'Main-Class': 'GroovuinoML' + } +} \ No newline at end of file diff --git a/embeddeds/groovy/build.sh b/embeddeds/groovy/build.sh new file mode 100755 index 00000000..8113e2d0 --- /dev/null +++ b/embeddeds/groovy/build.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +CWD=$(pwd) +cd ../../kernels/jvm || exit +mvn install +cd "$CWD" || exit +./gradlew shadowJar \ No newline at end of file diff --git a/embeddeds/groovy/gradle/wrapper/gradle-wrapper.jar b/embeddeds/groovy/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..41d9927a Binary files /dev/null and b/embeddeds/groovy/gradle/wrapper/gradle-wrapper.jar differ diff --git a/embeddeds/groovy/gradle/wrapper/gradle-wrapper.properties b/embeddeds/groovy/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..41dfb879 --- /dev/null +++ b/embeddeds/groovy/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/embeddeds/groovy/gradlew b/embeddeds/groovy/gradlew new file mode 100755 index 00000000..1b6c7873 --- /dev/null +++ b/embeddeds/groovy/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/embeddeds/groovy/run.bat b/embeddeds/groovy/run.bat new file mode 100644 index 00000000..50c01e76 --- /dev/null +++ b/embeddeds/groovy/run.bat @@ -0,0 +1 @@ +java -jar target\dsl-groovy-all.jar scripts\Switch.groovy > result.ino \ No newline at end of file diff --git a/embeddeds/groovy/run.sh b/embeddeds/groovy/run.sh new file mode 100755 index 00000000..949aa27c --- /dev/null +++ b/embeddeds/groovy/run.sh @@ -0,0 +1,113 @@ +#!/bin/sh + +printf "[[ MAGIC RUN.SH ]]\n" + +JAVA_VER=$(java -version 2>&1 | sed -n ';s/.* version "\(.*\)\.\(.*\)\..*".*/\1\2/p;' | head -n 1) + +if [ -z "$JAVA_VER" ]; then + echo "Java is not installed!" + exit 1 +fi + +# JAVA_VER=$((JAVA_VER - 0)) + +# if [ "$JAVA_VER" -lt 170 ]; then +# echo "Java version is too low, please install Java 17 or higher!" +# exit 1 +# fi + +EMBEDDED_PATH=$(find . -name "*.jar" -not -path "./gradle/*" | head -n 1) + +if [ -d ~/.m2/repository ]; then + KERNEL_PATH=$(find ~/.m2/repository -name "kernel-jvm*.jar" | head -n 1) +fi + +while [ "$#" -gt 0 ]; do + case "$1" in + -p | --package) + EMBEDDED_PATH="$2" + shift 2 + ;; + -h | --help) + echo "Usage: $0 [-p|--package ] ]" + exit 0 + ;; + *) + SCRIPT_PATH="$1" + break + ;; + esac +done + +HAS_KERNEL=false +HAS_EMBEDDED=false + +if [ -n "$EMBEDDED_PATH" ]; then + if [ -f "$EMBEDDED_PATH" ]; then + HAS_EMBEDDED=true + else + echo "Provided DSL package path (-p) does not exist!" + exit 1 + fi +fi + +if [ -n "$KERNEL_PATH" ]; then + if [ -f "$KERNEL_PATH" ]; then + HAS_KERNEL=true + else + echo "Provided kernel package path (-k) does not exist!" + exit 1 + fi +fi + +if [ "$HAS_KERNEL" = false ] || [ "$HAS_EMBEDDED" = false ]; then + CWD=$(pwd) + if [ "$HAS_KERNEL" = false ]; then + echo "Kernel not found, building it..." + cd ../../kernels/jvm || exit + mvn install >/dev/null + cd "$CWD" >/dev/null || exit + KERNEL_PATH=$(find ~/.m2/repository -name "kernel-jvm*.jar" | head -n 1) + + if [ "$HAS_EMBEDDED" = true ]; then + rm -rf build + echo "Removing DSL package build directory since kernel is newer..." + HAS_EMBEDDED=false + fi + fi + + if [ "$HAS_EMBEDDED" = false ]; then + echo "Embedded DSL package not found, building it..." + ./gradlew shadowJar >/dev/null + EMBEDDED_PATH=$(find . -name "*.jar" -not -path "./gradle/*" | head -n 1) + fi +fi + +if [ -z "$EMBEDDED_PATH" ]; then + echo "Build failed, please build the project through build.sh to debug errors" + exit 1 +fi + + +if [ -z "$SCRIPT_PATH" ]; then + echo "Usage: $0 [-p|--package ] ]" + printf "No script path provided. " + + if [ -f "scripts/Switch.groovy" ]; then + printf 'Run with the default scripts/Switch.groovy? [y/n]: ' + read -r answer + if [ "$answer" != "y" ]; then + exit 1 + fi + SCRIPT_PATH=scripts/Switch.groovy + else + printf "No example script found, exiting." + exit 1 + fi + +fi + +FILE_NAME=$(basename "$SCRIPT_PATH") && FILE_NAME="${FILE_NAME%.*}" +echo "// Generated using run.sh with $(basename "$SCRIPT_PATH") by $(basename "$EMBEDDED_PATH") on $(date)" >"${FILE_NAME}.ino" +java -jar "$EMBEDDED_PATH" "$SCRIPT_PATH" >>"${FILE_NAME}.ino" +echo "Exported ${FILE_NAME}.ino successfully!" \ No newline at end of file diff --git a/embeddeds/groovy/GroovuinoML/scripts/GroovuinoML.dsld b/embeddeds/groovy/scripts/GroovuinoML.dsld similarity index 100% rename from embeddeds/groovy/GroovuinoML/scripts/GroovuinoML.dsld rename to embeddeds/groovy/scripts/GroovuinoML.dsld diff --git a/embeddeds/groovy/GroovuinoML/scripts/Switch.groovy b/embeddeds/groovy/scripts/Switch.groovy similarity index 100% rename from embeddeds/groovy/GroovuinoML/scripts/Switch.groovy rename to embeddeds/groovy/scripts/Switch.groovy diff --git a/embeddeds/groovy/GroovuinoML/scripts/Switch2.groovy b/embeddeds/groovy/scripts/Switch2.groovy similarity index 100% rename from embeddeds/groovy/GroovuinoML/scripts/Switch2.groovy rename to embeddeds/groovy/scripts/Switch2.groovy diff --git a/embeddeds/groovy/settings.gradle b/embeddeds/groovy/settings.gradle new file mode 100644 index 00000000..b7d017b6 --- /dev/null +++ b/embeddeds/groovy/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'embedded' + diff --git a/embeddeds/groovy/src/main/groovy/GroovuinoML.groovy b/embeddeds/groovy/src/main/groovy/GroovuinoML.groovy new file mode 100644 index 00000000..3d7fd6a2 --- /dev/null +++ b/embeddeds/groovy/src/main/groovy/GroovuinoML.groovy @@ -0,0 +1,20 @@ +import dsl.GroovuinoMLDSL + +/** + * This main takes one argument: the path to the Groovy script file to execute. + * This Groovy script file must follow GroovuinoML DSL's rules. + * + * "We've Got A Groovy Thing Goin'"! + * + * @author Thomas Moreau + */ +class GroovuinoML { + static void main(String[] args) { + GroovuinoMLDSL dsl = new GroovuinoMLDSL() + if (args.length > 0) { + dsl.eval(new File(args[0])) + } else { + println "Usage: groovy GroovuinoML.groovy " + } + } +} diff --git a/embeddeds/groovy/src/main/groovy/dsl/GroovuinoMLBasescript.groovy b/embeddeds/groovy/src/main/groovy/dsl/GroovuinoMLBasescript.groovy new file mode 100644 index 00000000..0f1b1c8a --- /dev/null +++ b/embeddeds/groovy/src/main/groovy/dsl/GroovuinoMLBasescript.groovy @@ -0,0 +1,77 @@ +package dsl + +import io.github.mosser.arduinoml.kernel.behavioral.Action +import io.github.mosser.arduinoml.kernel.behavioral.State +import io.github.mosser.arduinoml.kernel.structural.Actuator +import io.github.mosser.arduinoml.kernel.structural.SIGNAL +import io.github.mosser.arduinoml.kernel.structural.Sensor + +abstract class GroovuinoMLBasescript extends Script { + // sensor "name" pin n + def sensor(String name) { + [pin : { n -> ((GroovuinoMLBinding) this.getBinding()).getGroovuinoMLModel().createSensor(name, n) }, + onPin: { n -> ((GroovuinoMLBinding) this.getBinding()).getGroovuinoMLModel().createSensor(name, n) }] + } + + // actuator "name" pin n + def actuator(String name) { + [pin: { n -> ((GroovuinoMLBinding) this.getBinding()).getGroovuinoMLModel().createActuator(name, n) }] + } + + // state "name" means actuator becomes signal [and actuator becomes signal]*n + def state(String name) { + List actions = new ArrayList() + ((GroovuinoMLBinding) this.getBinding()).getGroovuinoMLModel().createState(name, actions) + // recursive closure to allow multiple and statements + def closure + closure = { actuator -> + [becomes: { signal -> + Action action = new Action() + action.setActuator(actuator instanceof String ? (Actuator) ((GroovuinoMLBinding) this.getBinding()).getVariable(actuator) : (Actuator) actuator) + action.setValue(signal instanceof String ? (SIGNAL) ((GroovuinoMLBinding) this.getBinding()).getVariable(signal) : (SIGNAL) signal) + actions.add(action) + [and: closure] + }] + } + [means: closure] + } + + // initial state + def initial(state) { + ((GroovuinoMLBinding) this.getBinding()).getGroovuinoMLModel().setInitialState(state instanceof String ? (State) ((GroovuinoMLBinding) this.getBinding()).getVariable(state) : (State) state) + } + + // from state1 to state2 when sensor becomes signal + def from(state1) { + [to: { state2 -> + [when: { sensor -> + [becomes: { signal -> + ((GroovuinoMLBinding) this.getBinding()).getGroovuinoMLModel().createTransition( + state1 instanceof String ? (State) ((GroovuinoMLBinding) this.getBinding()).getVariable(state1) : (State) state1, + state2 instanceof String ? (State) ((GroovuinoMLBinding) this.getBinding()).getVariable(state2) : (State) state2, + sensor instanceof String ? (Sensor) ((GroovuinoMLBinding) this.getBinding()).getVariable(sensor) : (Sensor) sensor, + signal instanceof String ? (SIGNAL) ((GroovuinoMLBinding) this.getBinding()).getVariable(signal) : (SIGNAL) signal) + }] + }] + }] + } + + // export name + def export(String name) { + println(((GroovuinoMLBinding) this.getBinding()).getGroovuinoMLModel().generateCode(name).toString()) + } + + // disable run method while running + int count = 0 + + abstract void scriptBody() + + def run() { + if (count == 0) { + count++ + scriptBody() + } else { + println "Run method is disabled" + } + } +} diff --git a/embeddeds/groovy/src/main/groovy/dsl/GroovuinoMLBinding.java b/embeddeds/groovy/src/main/groovy/dsl/GroovuinoMLBinding.java new file mode 100644 index 00000000..6c727814 --- /dev/null +++ b/embeddeds/groovy/src/main/groovy/dsl/GroovuinoMLBinding.java @@ -0,0 +1,53 @@ +package dsl; + +import groovy.lang.Binding; +import groovy.lang.Script; + +import java.util.Map; + +public class GroovuinoMLBinding extends Binding { + // can be useful to return the script in case of syntax trick + private Script script; + + private GroovuinoMLModel model; + + public GroovuinoMLBinding() { + super(); + } + + @SuppressWarnings("rawtypes") + public GroovuinoMLBinding(Map variables) { + super(variables); + } + + public GroovuinoMLBinding(Script script) { + super(); + this.script = script; + } + + public void setScript(Script script) { + this.script = script; + } + + public void setGroovuinoMLModel(GroovuinoMLModel model) { + this.model = model; + } + + public Object getVariable(String name) { + // Easter egg (to show you this trick: seb is now a keyword!) + if ("seb".equals(name)) { + // could do something else like: ((App) this.getVariable("app")).action(); + System.out.println("Seb, c'est bien"); + return script; + } + return super.getVariable(name); + } + + public void setVariable(String name, Object value) { + super.setVariable(name, value); + } + + public GroovuinoMLModel getGroovuinoMLModel() { + return this.model; + } +} diff --git a/embeddeds/groovy/src/main/groovy/dsl/GroovuinoMLDSL.groovy b/embeddeds/groovy/src/main/groovy/dsl/GroovuinoMLDSL.groovy new file mode 100644 index 00000000..e12e9b33 --- /dev/null +++ b/embeddeds/groovy/src/main/groovy/dsl/GroovuinoMLDSL.groovy @@ -0,0 +1,66 @@ +package dsl + + +import io.github.mosser.arduinoml.kernel.structural.SIGNAL +import org.codehaus.groovy.control.CompilerConfiguration +import org.codehaus.groovy.control.customizers.SecureASTCustomizer + +class GroovuinoMLDSL { + private GroovyShell shell + private CompilerConfiguration configuration + private GroovuinoMLBinding binding + private GroovuinoMLBasescript basescript + + GroovuinoMLDSL() { + binding = new GroovuinoMLBinding() + binding.setGroovuinoMLModel(new GroovuinoMLModel(binding)) + configuration = getDSLConfiguration() + configuration.setScriptBaseClass("dsl.GroovuinoMLBasescript") + shell = new GroovyShell(configuration) + + binding.setVariable("high", SIGNAL.HIGH) + binding.setVariable("low", SIGNAL.LOW) + } + + private static CompilerConfiguration getDSLConfiguration() { + def secure = new SecureASTCustomizer() + secure.with { + //disallow closure creation + closuresAllowed = false + //disallow method definitions + methodDefinitionAllowed = true + //empty white list => forbid imports + importsWhitelist = [ + 'java.lang.*' + ] + staticImportsWhitelist = [] + staticStarImportsWhitelist = [] + //language tokens disallowed +// tokensBlacklist= [] + //language tokens allowed + tokensWhitelist = [] + //types allowed to be used (including primitive types) + constantTypesClassesWhiteList = [ + int, Integer, Number, Integer.TYPE, String, Object + ] + //classes who are allowed to be receivers of method calls + receiversClassesWhiteList = [ + int, Number, Integer, String, Object + ] + } + + def configuration = new CompilerConfiguration() + configuration.addCompilationCustomizers(secure) + + return configuration + } + + void eval(File scriptFile) { + Script script = shell.parse(scriptFile) + + binding.setScript(script) + script.setBinding(binding) + + script.run() + } +} diff --git a/embeddeds/groovy/src/main/groovy/dsl/GroovuinoMLModel.java b/embeddeds/groovy/src/main/groovy/dsl/GroovuinoMLModel.java new file mode 100644 index 00000000..d7b8a167 --- /dev/null +++ b/embeddeds/groovy/src/main/groovy/dsl/GroovuinoMLModel.java @@ -0,0 +1,80 @@ +package dsl; + +import groovy.lang.Binding; +import io.github.mosser.arduinoml.kernel.App; +import io.github.mosser.arduinoml.kernel.behavioral.Action; +import io.github.mosser.arduinoml.kernel.behavioral.State; +import io.github.mosser.arduinoml.kernel.behavioral.Transition; +import io.github.mosser.arduinoml.kernel.generator.ToWiring; +import io.github.mosser.arduinoml.kernel.generator.Visitor; +import io.github.mosser.arduinoml.kernel.structural.Actuator; +import io.github.mosser.arduinoml.kernel.structural.Brick; +import io.github.mosser.arduinoml.kernel.structural.SIGNAL; +import io.github.mosser.arduinoml.kernel.structural.Sensor; + +import java.util.ArrayList; +import java.util.List; + +public class GroovuinoMLModel { + private final List bricks; + private final List states; + private State initialState; + + private final Binding binding; + + public GroovuinoMLModel(Binding binding) { + this.bricks = new ArrayList<>(); + this.states = new ArrayList<>(); + this.binding = binding; + } + + public void createSensor(String name, Integer pinNumber) { + Sensor sensor = new Sensor(); + sensor.setName(name); + sensor.setPin(pinNumber); + this.bricks.add(sensor); + this.binding.setVariable(name, sensor); +// System.out.println("> sensor " + name + " on pin " + pinNumber); + } + + public void createActuator(String name, Integer pinNumber) { + Actuator actuator = new Actuator(); + actuator.setName(name); + actuator.setPin(pinNumber); + this.bricks.add(actuator); + this.binding.setVariable(name, actuator); + } + + public void createState(String name, List actions) { + State state = new State(); + state.setName(name); + state.setActions(actions); + this.states.add(state); + this.binding.setVariable(name, state); + } + + public void createTransition(State from, State to, Sensor sensor, SIGNAL value) { + Transition transition = new Transition(); + transition.setNext(to); + transition.setSensor(sensor); + transition.setValue(value); + from.setTransition(transition); + } + + public void setInitialState(State state) { + this.initialState = state; + } + + @SuppressWarnings("rawtypes") + public Object generateCode(String appName) { + App app = new App(); + app.setName(appName); + app.setBricks(this.bricks); + app.setStates(this.states); + app.setInitial(this.initialState); + Visitor codeGenerator = new ToWiring(); + app.accept(codeGenerator); + + return codeGenerator.getResult(); + } +} diff --git a/kernels/jvm/pom.xml b/kernels/jvm/pom.xml index 6d6d2bea..a2079ea1 100644 --- a/kernels/jvm/pom.xml +++ b/kernels/jvm/pom.xml @@ -19,7 +19,7 @@ junit junit - 4.12 + 4.13.2 test @@ -29,7 +29,7 @@ org.apache.maven.plugins maven-compiler-plugin - 2.3.2 + 3.10.1 1.8 1.8