]> gerrit.simantics Code Review - simantics/sysdyn.git/commitdiff
Unit validation for Sysdyn functions: matching argument units to the input (template...
authormiettinen <miettinen@ac1ea38d-2e2b-0410-8846-a27921b304fc>
Fri, 31 Jan 2014 12:25:35 +0000 (12:25 +0000)
committermiettinen <miettinen@ac1ea38d-2e2b-0410-8846-a27921b304fc>
Fri, 31 Jan 2014 12:25:35 +0000 (12:25 +0000)
git-svn-id: https://www.simantics.org/svn/simantics/sysdyn/trunk@28743 ac1ea38d-2e2b-0410-8846-a27921b304fc

org.simantics.sysdyn.ontology/graph.tg
org.simantics.sysdyn.ontology/graph/ModelicaFunctions.pgraph
org.simantics.sysdyn/src/org/simantics/sysdyn/unitParser/UnitCheckingNodeFactory.java
org.simantics.sysdyn/src/org/simantics/sysdyn/unitParser/UnitParser.jjt
org.simantics.sysdyn/src/org/simantics/sysdyn/unitParser/nodes/FunctionCall.java
org.simantics.sysdyn/src/org/simantics/sysdyn/unitParser/nodes/NamedArguments.java [new file with mode: 0644]
org.simantics.sysdyn/src/org/simantics/sysdyn/unitParser/nodes/UnitResult.java

index 4d3bfdfbea7b39b108038552e995d7bd957dc9cd..8f3e514e9f973f9c460afcf9f99032183af58a8b 100644 (file)
Binary files a/org.simantics.sysdyn.ontology/graph.tg and b/org.simantics.sysdyn.ontology/graph.tg differ
index 86ea5cd5ef776b396239240ca8258e91dc147bdb..c692447a5842278d59506f843ee21f45982561a0 100644 (file)
@@ -102,11 +102,11 @@ Returns y = atan2(u1,u2) such that tan(y) = u1/u2 and y is in the range -pi < y
             MF.atan2.u1 : SYSDYN.SysdynModelicaFunction.Input\r
                 SYSDYN.Variable.type "Real"\r
                 SYSDYN.SysdynModelicaFunction.optional false\r
-                SYSDYN.SysdynModelicaFunction.unit "'p"\r
+                SYSDYN.SysdynModelicaFunction.unit "ANY"\r
             MF.atan2.u2 : SYSDYN.SysdynModelicaFunction.Input\r
                 SYSDYN.Variable.type "Real"\r
                 SYSDYN.SysdynModelicaFunction.optional false\r
-                SYSDYN.SysdynModelicaFunction.unit "'p"\r
+                SYSDYN.SysdynModelicaFunction.unit "ANY"\r
     SYSDYN.SysdynModelicaFunction.outputs _ : L0.List\r
         @L0.list\r
             MF.atan2.y : SYSDYN.SysdynModelicaFunction.Output\r
@@ -467,7 +467,7 @@ Returns the integer modulus of x/y: mod(x,y) = x - floor(x/y) * y."""
             MF.mod.y : SYSDYN.SysdynModelicaFunction.Input\r
                 SYSDYN.Variable.type "Real"\r
                 SYSDYN.SysdynModelicaFunction.optional false\r
-                SYSDYN.SysdynModelicaFunction.unit "'ANY"\r
+                SYSDYN.SysdynModelicaFunction.unit "ANY"\r
     SYSDYN.SysdynModelicaFunction.outputs _ : L0.List\r
         @L0.list\r
             MF.mod.result : SYSDYN.SysdynModelicaFunction.Output\r
index 2526d473669081371463927bd9aa806c4663e49c..798659e5943139256a3078871cca40c3ed241312 100644 (file)
@@ -29,6 +29,7 @@ import org.simantics.sysdyn.unitParser.nodes.FunctionArguments;
 import org.simantics.sysdyn.unitParser.nodes.FunctionCall;\r
 import org.simantics.sysdyn.unitParser.nodes.IfThenElse;\r
 import org.simantics.sysdyn.unitParser.nodes.Multiplication;\r
+import org.simantics.sysdyn.unitParser.nodes.NamedArguments;\r
 import org.simantics.sysdyn.unitParser.nodes.Power;\r
 import org.simantics.sysdyn.unitParser.nodes.RelOp;\r
 import org.simantics.sysdyn.unitParser.nodes.Relation;\r
@@ -65,6 +66,7 @@ public class UnitCheckingNodeFactory {
         constructors.put("for_index", ForIndex.class);\r
         constructors.put("function_call", FunctionCall.class);\r
         constructors.put("function_arguments", FunctionArguments.class);\r
+        constructors.put("named_arguments", NamedArguments.class);\r
         constructors.put("expression", Expression.class);\r
 \r
         \r
index 8227241b9bdcf549f95b5d1df42c18a3b73ff374..81d5fcb861c59cdd4480456ec35a81be8ce2c0b5 100644 (file)
@@ -226,7 +226,7 @@ void function_arguments() : {
 } {\r
        //expression [ "," function_arguments | for for_indices ]\r
        //| named_arguments\r
-       LOOKAHEAD(2) named_argument()  ( "," function_arguments() )?\r
+       LOOKAHEAD(2) named_arguments()\r
        | expression() ( "," function_arguments() | "for" for_indices() )?\r
        \r
 }\r
@@ -243,12 +243,10 @@ void for_index() : {
        <IDENT> ( "in" expression() )?\r
 }\r
 \r
-/* Removed by Teemu. Refactored in function_arguments)\r
 void named_arguments() : {\r
 } {\r
        named_argument() ( "," named_arguments() )?\r
 }\r
-*/\r
 \r
 void named_argument() : {\r
 } {    \r
index a82b1c52174f477b6907c75b954bbea1cc5a1cfa..4f73e165981143faf21b33c8f8840ccd4cd8183c 100644 (file)
@@ -25,6 +25,7 @@ import org.simantics.sysdyn.utils.Function;
 import org.simantics.sysdyn.utils.Function.Input;\r
 import org.simantics.sysdyn.utils.Function.Output;\r
 import org.simantics.sysdyn.utils.Function.Parameter;\r
+import org.simantics.utils.datastructures.Pair;\r
 \r
 /**\r
  * See UnitCheckingNodeFactory for mapping\r
@@ -54,11 +55,14 @@ public class FunctionCall extends UnitCheckingNode {
                        // Get the expected inputs\r
                        ArrayList<Input> inputs = f.getInputList();\r
                        // Get arguments from parser.\r
-                       ArrayList<UnitResult> argumentUnits = getArgumentsOf(functionArgumentsNode, units, functions, allowEquivalents);\r
+                       ArrayList<Pair<UnitResult, String>> argumentUnits = getArgumentsOf(functionArgumentsNode, units, functions, allowEquivalents);\r
                        \r
+                       //HashMap<String, String> correspondencies = getReplacementUnitCorrespondencies(inputs, argumentUnits);\r
+\r
                        // If the function input parameter units are OK, set the output unit accordingly.  \r
-                       if (matchInputs(inputs, argumentUnits)) {\r
-                               result = setUnitResult(units, result, inputs, argumentUnits, f, functions, allowEquivalents);\r
+                       if (matchInputs(inputs, argumentUnits, f, functions, allowEquivalents, units)) {\r
+                               Output o = f.getOutputList().get(0); // Support only single return value functions.\r
+                               result = getUnitResult(units, result, inputs, argumentUnits, f, functions, allowEquivalents, o);\r
                                return result;\r
                        }\r
                        \r
@@ -79,24 +83,24 @@ public class FunctionCall extends UnitCheckingNode {
         throw u;\r
     }\r
 \r
-       private UnitResult setUnitResult(\r
+       private static UnitResult getUnitResult(\r
                        HashMap<String, String> units, \r
                        UnitResult result, \r
                        ArrayList<Input> inputs, \r
-                       ArrayList<UnitResult> argumentUnits, \r
+                       ArrayList<Pair<UnitResult,String>> argumentUnits, \r
                        Function f,\r
                        ArrayList<Function> functions,\r
-                       boolean allowEquivalents) throws UnitCheckingException {\r
-               Output o = f.getOutputList().get(0); // Support only single return value functions.\r
+                       boolean allowEquivalents,\r
+                       Parameter p) throws UnitCheckingException {\r
                \r
-               if (Parameter.ANY.equals(o.unit)) {\r
+               if (Parameter.ANY.equals(p.unit)) {\r
                        result.setUnitType(UnitType.ANY);\r
-               } else if ("1".equals(o.unit)) { // TODO: see if this should be changed to something else\r
+               } else if ("1".equals(p.unit)) { // TODO: see if this should be changed to something else\r
                        result.setUnitType(UnitType.SCALAR);\r
                } else {\r
                        // Replace TIME with actual time unit.\r
                        String timeUnit = units.get("time");\r
-                       String timeReplaced = o.unit.replace(Parameter.TIME, timeUnit);\r
+                       String timeReplaced = p.unit.replace(Parameter.TIME, timeUnit);\r
 \r
                        // Replace 'p, 'q, etc. in output with units from actual inputs.\r
                        HashMap<String, String> correspondencies = getReplacementUnitCorrespondencies(inputs, argumentUnits);\r
@@ -121,7 +125,7 @@ public class FunctionCall extends UnitCheckingNode {
         return result;\r
        }\r
 \r
-       private String replaceCorrespondencies(Function f, String original,\r
+       private static String replaceCorrespondencies(Function f, String original,\r
                        HashMap<String, String> correspondencies) throws UnitCheckingException {\r
                int index;\r
                String ret = new String(original);\r
@@ -138,23 +142,97 @@ public class FunctionCall extends UnitCheckingNode {
                return ret;\r
        }\r
 \r
-       private HashMap<String, String> getReplacementUnitCorrespondencies(\r
-                       ArrayList<Input> inputs, ArrayList<UnitResult> argumentUnits) {\r
+       private static HashMap<String, String> getReplacementUnitCorrespondencies(\r
+                       ArrayList<Input> inputs, ArrayList<Pair<UnitResult, String>> argumentUnits) {\r
                // TODO: General case. Currently each 'p, 'q etc. must exist as an atomic unit.\r
                HashMap<String, String> correspondencies = new HashMap<String, String>();\r
-               for (int i = 0; i < inputs.size(); ++i) {\r
-                       if (inputs.get(i).unit.matches("'[a-zA-Z]")) {\r
-                               if (argumentUnits.size() > i) { // Skip optional\r
-                                       correspondencies.put(inputs.get(i).unit, argumentUnits.get(i).getCleanFullUnit());\r
+               for (int i = 0; i < argumentUnits.size(); ++i) {\r
+                       if (argumentUnits.get(i).second != null) { // named arguments\r
+                               for (Input input : inputs) {\r
+                                       if (input.name.equals(argumentUnits.get(i).second)) {\r
+                                               addPossibleCorrespondence(correspondencies, input, argumentUnits.get(i).first);\r
+                                       }\r
                                }\r
+                       } else if (inputs.size() > i) {\r
+                               addPossibleCorrespondence(correspondencies, inputs.get(i), argumentUnits.get(i).first);\r
                        }\r
                }\r
+               // If there is no mapping between the actual and defined inputs (e.g. 'p refers to both m and s),\r
+               // then return the latest correspondence. The users of this functions currently assume such behavior.\r
                return correspondencies;\r
        }\r
 \r
+       private static void addPossibleCorrespondence(HashMap<String, String> correspondencies,\r
+                       Input input, UnitResult argumentUnit) {\r
+               if (input.unit.matches("'[a-zA-Z]")) {\r
+                       String fullUnit = argumentUnit.getCleanFullUnit();\r
+                       if ("".equals(fullUnit))\r
+                               fullUnit = "1";\r
+                       correspondencies.put(input.unit, fullUnit);\r
+               }\r
+       }\r
+\r
        private boolean matchInputs(ArrayList<Input> inputs,\r
-                       ArrayList<UnitResult> argumentUnits) {\r
-               // TODO Auto-generated method stub\r
+                       ArrayList<Pair<UnitResult, String>> argumentUnits, Function f, ArrayList<Function> functions, boolean allowEquivalents, HashMap<String, String> units) throws UnitCheckingException {\r
+               ArrayList<Boolean> inputMatches = new ArrayList<Boolean>();\r
+               for (int i = 0; i < inputs.size(); ++i)\r
+                       inputMatches.add(Boolean.FALSE);\r
+               \r
+               for (int i = 0; i < argumentUnits.size(); ++i) { // Go through all _arguments_\r
+                       if (inputs.size() == 0)\r
+                               return false; // No inputs expected but got at least one\r
+                       \r
+                       if (argumentUnits.get(i).second != null) { // Named argument\r
+                               boolean found = false;\r
+                               for (int j = 0; j < inputs.size(); ++j) {\r
+                                       Input namedInput = inputs.get(j);\r
+                                       if (namedInput.name.equals(argumentUnits.get(i).second)) {\r
+                                               // Match input unit to argument unit\r
+                                               UnitResult inputUnit = new UnitResult(allowEquivalents);\r
+                                               inputUnit = getUnitResult(units, inputUnit, inputs, argumentUnits, f, functions, allowEquivalents, namedInput);\r
+                                               if (!inputUnit.equals(argumentUnits.get(i).first))\r
+                                                       return false;\r
+                                               inputMatches.set(j, Boolean.TRUE);\r
+                                               found = true;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               if (!found) {\r
+                                       throw new UnitCheckingException("Undefined input argument " + argumentUnits.get(i).second \r
+                                                       + " used in function " + f.getName() + ".");\r
+                               }\r
+                       } else { // Position argument\r
+                               if (i >= inputs.size()) { // Test for variable length argument\r
+                                       // Assume there can be only one variable length input and its in the end.\r
+                                       // And that there cannot be any optional arguments before.\r
+                                       if (inputs.get(inputs.size() - 1).variableLength) {\r
+                                               // Match input unit to argument unit\r
+                                               UnitResult inputUnit = new UnitResult(allowEquivalents);\r
+                                               inputUnit = getUnitResult(units, inputUnit, inputs, argumentUnits, f, functions, allowEquivalents, inputs.get(inputs.size() - 1));\r
+                                               if (!inputUnit.equals(argumentUnits.get(i).first))\r
+                                                       return false;\r
+                                               // The corresponding _input_ has already been gone through, no need to set true.\r
+                                       } else {\r
+                                               return false;\r
+                                       }\r
+                               } else {\r
+                                       // Match input unit to argument unit\r
+                                       UnitResult inputUnit = new UnitResult(allowEquivalents);\r
+                                       inputUnit = getUnitResult(units, inputUnit, inputs, argumentUnits, f, functions, allowEquivalents, inputs.get(i));\r
+                                       if (!inputUnit.equals(argumentUnits.get(i).first))\r
+                                               return false;\r
+                                       inputMatches.set(i, Boolean.TRUE);\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               // See if some of the required inputs has not been defined. \r
+               for (int i = 0; i < inputs.size(); ++i) {\r
+                       if (!inputMatches.get(i) && !inputs.get(i).optional) {\r
+                               return false;\r
+                       }\r
+               }\r
+               \r
                return true;\r
        }\r
 \r
@@ -165,7 +243,7 @@ public class FunctionCall extends UnitCheckingNode {
         * @param argumentUnits\r
         * @return\r
         */\r
-       private UnitCheckingException getException(UnitCheckingNode functionLabelNode, ArrayList<Input> inputs, ArrayList<UnitResult> argumentUnits) {\r
+       private UnitCheckingException getException(UnitCheckingNode functionLabelNode, ArrayList<Input> inputs, ArrayList<Pair<UnitResult,String>> argumentUnits) {\r
        String exception = new String("Function arguments do not have correct units. Expected " + functionLabelNode.printNode() + "(");\r
                \r
        for (int i = 0; i < inputs.size(); ++i) {\r
@@ -182,12 +260,12 @@ public class FunctionCall extends UnitCheckingNode {
                }\r
                exception += " ), got " + functionLabelNode.printNode() + "(";\r
                for (int i = 0; i < argumentUnits.size(); ++i) {\r
-                       UnitResult argumentUnit = argumentUnits.get(i);\r
+                       UnitResult argumentUnit = argumentUnits.get(i).first;\r
                        if (i == 0)\r
                                exception += " ";\r
                        exception += "[" + argumentUnit.getCleanFullUnit() + "]";\r
                        \r
-                       if (i != inputs.size() - 1)\r
+                       if (i != argumentUnits.size() - 1)\r
                                exception += ", ";\r
                        \r
                }\r
@@ -196,21 +274,46 @@ public class FunctionCall extends UnitCheckingNode {
                return new UnitCheckingException(exception);\r
        }\r
 \r
-    private ArrayList<UnitResult> getArgumentsOf(\r
+    private ArrayList<Pair<UnitResult, String>> getArgumentsOf(\r
                        UnitCheckingNode functionArgumentsNode, \r
                        HashMap<String, String> units, \r
                        ArrayList<Function> functions, \r
                        boolean allowEquivalents) throws UnitCheckingException {\r
        // Arguments are defined recursively in OpenModelica\r
-               ArrayList<UnitResult> argumentUnits = new ArrayList<UnitResult>();\r
+               ArrayList<Pair<UnitResult, String>> argumentUnits = new ArrayList<Pair<UnitResult, String>>();\r
                if (functionArgumentsNode instanceof FunctionArguments) {\r
                UnitCheckingNode firstArg = ((UnitCheckingNode) functionArgumentsNode.jjtGetChild(0));\r
-               argumentUnits.add(firstArg.getUnits(units, functions, allowEquivalents));\r
-               if (functionArgumentsNode.jjtGetNumChildren() > 1) {\r
-                       UnitCheckingNode next = ((UnitCheckingNode) functionArgumentsNode.jjtGetChild(1));\r
-                       argumentUnits.addAll(getArgumentsOf(next, units, functions, allowEquivalents));\r
+               if (firstArg instanceof NamedArguments) {\r
+                       // Found all position arguments, continue with named arguments\r
+                       argumentUnits.addAll(getNamedArgumentsOf(firstArg, units, functions, allowEquivalents));\r
+               } else {\r
+                       argumentUnits.add(new Pair<UnitResult, String>(firstArg.getUnits(units, functions, allowEquivalents), null));\r
+                       if (functionArgumentsNode.jjtGetNumChildren() > 1) {\r
+                               // Continue to the next argument (within the following function_arguments)\r
+                               UnitCheckingNode next = ((UnitCheckingNode) functionArgumentsNode.jjtGetChild(1));\r
+                               argumentUnits.addAll(getArgumentsOf(next, units, functions, allowEquivalents));\r
+                       }\r
+               }\r
+               }\r
+               return argumentUnits;\r
+       }\r
+\r
+       private ArrayList<Pair<UnitResult, String>> getNamedArgumentsOf(\r
+                       UnitCheckingNode namedArgumentsNode, \r
+                       HashMap<String, String> units, \r
+                       ArrayList<Function> functions, \r
+                       boolean allowEquivalents) throws UnitCheckingException {\r
+               // Named arguments are defined recursively in OpenModelica\r
+               ArrayList<Pair<UnitResult, String>> argumentUnits = new ArrayList<Pair<UnitResult, String>>();\r
+               if (namedArgumentsNode instanceof NamedArguments) {\r
+               UnitCheckingNode firstArg = ((UnitCheckingNode) namedArgumentsNode.jjtGetChild(0));\r
+               String argumentName = firstArg.jjtGetFirstToken().image;\r
+               argumentUnits.add(new Pair<UnitResult, String>(firstArg.getUnits(units, functions, allowEquivalents), argumentName));\r
+               if (namedArgumentsNode.jjtGetNumChildren() > 1) {\r
+                       // Continue to the next argument (within the following function_arguments)\r
+                       UnitCheckingNode next = ((UnitCheckingNode) namedArgumentsNode.jjtGetChild(1));\r
+                       argumentUnits.addAll(getNamedArgumentsOf(next, units, functions, allowEquivalents));\r
                }\r
-               \r
                }\r
                return argumentUnits;\r
        }\r
diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/unitParser/nodes/NamedArguments.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/unitParser/nodes/NamedArguments.java
new file mode 100644 (file)
index 0000000..7afcf98
--- /dev/null
@@ -0,0 +1,27 @@
+/*******************************************************************************\r
+ * Copyright (c) 2014 Association for Decentralized Information Management in\r
+ * Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.sysdyn.unitParser.nodes;\r
+\r
+import org.simantics.sysdyn.unitParser.UnitCheckingNode;\r
+\r
+/**\r
+ * See UnitCheckingNodeFactory for mapping\r
+ * @author Tuomas Miettinen\r
+ *\r
+ */\r
+public class NamedArguments extends UnitCheckingNode {\r
+\r
+    public NamedArguments(int id) {\r
+        super(id);\r
+    }\r
+\r
+}\r
index 74cf4dd95fcd18c4e9792d6adc28c3f6092e8728..7daf08acc175da654fed710f48c6417761463a79 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************\r
- * Copyright (c) 2013 Association for Decentralized Information Management in\r
+ * Copyright (c) 2013-2014 Association for Decentralized Information Management in\r
  * Industry THTH ry.\r
  * All rights reserved. This program and the accompanying materials\r
  * are made available under the terms of the Eclipse Public License v1.0\r
@@ -8,7 +8,8 @@
  *\r
  * Contributors:\r
  *     Semantum Oy - initial API and implementation\r
- *******************************************************************************/\r
+ *     VTT Technical Research Centre of Finland\r
+ ********************************************************************************/\r
 package org.simantics.sysdyn.unitParser.nodes;\r
 \r
 import java.util.ArrayList;\r