Skip to content

Commit

Permalink
Issue #1688: improvements of commit 55fb037
Browse files Browse the repository at this point in the history
  • Loading branch information
rbudde committed Oct 1, 2024
1 parent 55fb037 commit 67847a1
Show file tree
Hide file tree
Showing 39 changed files with 1,220 additions and 2,053 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ microbitv2SensorExpr : ACCELEROMETER_SENSOR '(' NAME ')'
| COMPASS_SENSOR '.' 'getAngle' '(' ')'
| GESTURE_SENSOR '.' 'currentGesture' '(' NAME ')'
| KEYS_SENSOR '.' 'isPressed' '(' NAME ')'
| LIGHT_SENSOR '.' 'getLevel' '('')'
| LIGHT_SENSOR '.' 'getLevel' '(' ')'
| PIN_GET_VALUE_SENSOR '(' NAME ',' op = ('analog'|'digital'|'pulseHigh'|'pulseLow') ')'
| PIN_TOUCH_SENSOR '.' 'isPressed' '(' INT ')'
| SOUND_SENSOR '.' 'microphone' '.' 'soundLevel' '(' ')'
Expand Down Expand Up @@ -159,8 +159,8 @@ robotEv3Expr : 'ev3' '.' ev3SensorExpr # Rob
ev3SensorExpr : GETSPEEDMOTOR '(' NAME ')'
| GETVOLUME '(' ')'
| TOUCH_SENSOR '.' 'isPressed' '(' INT ')'
| ULTRASONIC_SENSOR '.' 'getDistance''(' INT ')'
| ULTRASONIC_SENSOR '.''getPresence''(' INT ')'
| ULTRASONIC_SENSOR '.' 'getDistance' '(' INT ')'
| ULTRASONIC_SENSOR '.' 'getPresence' '(' INT ')'
| COLOR_SENSOR '(' NAME ',' INT ')'
| INFRARED_SENSOR '.' 'getDistance' '(' INT ')'
| INFRARED_SENSOR '.' 'getPresence' '(' INT ')'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,20 @@
* ANY
* I
* +---------+--------+-------+
* I I I I
* STRING COLOR NUMERIC BOOL
* I I I I
* +---------+ I I
* I I I
* NULL +-------+
* I I
* REF PRIM
* I I
* +--------+-------+
* I I I
* I I PRIM
* I I I
* I I +-------+-------+
* I I I I I
* STRING COLOR NUMERIC BOOL STRING
* I I I I I
* +---------+ I I I
* I I I I
* NULL I I I
* I I I I
* REF I I I
* I I I I
* +--------+-------+-------+
* I
* NOTHING
* </pre>
Expand All @@ -47,19 +51,20 @@ public enum BlocklyType {
ANY(""),
COMPARABLE("",ANY),
ADDABLE("",ANY),
PRIM("", ANY),

BOOLEAN("Boolean",COMPARABLE),
NUMBER("Number",COMPARABLE, ADDABLE),
BOOLEAN("Boolean",COMPARABLE, PRIM),
NUMBER("Number",COMPARABLE, ADDABLE, PRIM),
NUMBER_INT("Number", COMPARABLE, ADDABLE),
STRING("String",COMPARABLE, ADDABLE),
STRING("String",COMPARABLE, ADDABLE, PRIM),
COLOR("Colour",ANY), //
IMAGE("Image", ANY),
CONNECTION("Connection", ANY),
PREDEFINED_IMAGE("PredefinedImage", ANY),

NULL("",STRING, COLOR, CONNECTION),
REF("",NULL),
PRIM("", NUMBER, BOOLEAN),

NOTHING("Void", REF, PRIM),
VOID(""),
CAPTURED_TYPE(""),
Expand Down Expand Up @@ -109,6 +114,8 @@ public BlocklyType[] getSuperTypes() {
public boolean hasAsSuperType(BlocklyType potentialSuperType) {
if ( this.equalAsTypes(potentialSuperType) ) {
return true;
} else if ( this.equals(BlocklyType.NOTHING) || potentialSuperType.equals(BlocklyType.NOTHING) ) {
return false;
} else if ( this.superTypes.length == 0 ) {
return false;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,19 @@ public static Sig ofParamList(BlocklyType returnType, List<BlocklyType> paramLis
}

/**
* for a given AST-object {@link phraseWhoseSignaturIsChecked}, that has a signature object of {@link Sig} (a return type and its parameter types),
* check that the parameter types of the signature are consistent with the actual parameter types as defined by {@link paramTypes}
* If they are not consistent, add an error annoation to the AST-object.
* for a given AST-object phraseToCheck, that has a signature object of class Sig (a return type and its parameter types),
* check that the parameter types of the signature are consistent with the actual parameter types as defined by actualParamTypes
* If they are not consistent, add an error annotation to the AST-object.
*
* @param phraseWhoseSignaturIsChecked the (parent) AST-object whose parameters are to be checked. It is needed only to add the error annotation.
* @param phraseToCheck the (parent) AST-object whose parameters are to be checked. It is needed only to add the error annotation.
* @param actualParamTypes the actual parameter types derived from the actual AST-object (by typechecking, of course)
* @return the result type from the signature; never null
*/
private BlocklyType typeCheck(Phrase phraseWhoseSignaturIsChecked, List<BlocklyType> actualParamTypes) {
private BlocklyType typeCheck(Phrase phraseToCheck, List<BlocklyType> actualParamTypes) {
BlocklyType returnTypeIfError = this.returnType; // before: BlocklyType.NOTHING
if ( varargParamType == null && actualParamTypes.size() != this.paramTypes.length ) {
phraseWhoseSignaturIsChecked.addTextlyError("number of parameters don't match", true);
return BlocklyType.NOTHING;
phraseToCheck.addTextlyError("number of parameters don't match", true);
return returnTypeIfError;
}
int i = 0;
BlocklyType capturedType = null;
Expand All @@ -71,9 +72,9 @@ private BlocklyType typeCheck(Phrase phraseWhoseSignaturIsChecked, List<BlocklyT
if ( actualParamType.hasAsSuperType(varargParamType) ) {
// everything is fine, do NOT increment i !!!
} else {
String message = "for parameter " + i + " a vararg expected (super-)type is: " + varargParamType + ", but found was type : " + actualParamType;
phraseWhoseSignaturIsChecked.addTextlyError(message, true);
return BlocklyType.NOTHING;
String message = "for parameter " + (i+1) + " an expected type is: " + b2m(varargParamType) + ", but found was type : " + b2m(actualParamType);
phraseToCheck.addTextlyError(message, true);
return returnTypeIfError;
}
} else {
if ( actualParamType.hasAsSuperType(this.paramTypes[i]) ) {
Expand All @@ -82,36 +83,37 @@ private BlocklyType typeCheck(Phrase phraseWhoseSignaturIsChecked, List<BlocklyT
if ( this.paramTypes[i].equalAsTypes(BlocklyType.CAPTURED_TYPE) ) {
if ( capturedType == null ) {
capturedType = actualParamType;
returnTypeIfError = capturedType;
}
if ( returnType == BlocklyType.VOID && !capturedType.isArray() ) {
String message = "For this function a List was expected, but found was type: " + String.valueOf(actualParamType).toLowerCase();
phraseWhoseSignaturIsChecked.addTextlyError(message, true);
return BlocklyType.NOTHING;
String message = "a list type was expected, but found was type: " + b2m(actualParamType);
phraseToCheck.addTextlyError(message, true);
return returnTypeIfError;
} else if ( !actualParamType.equalAsTypes(capturedType) ) {
String message = "for parameter " + i + " the expected captured type is: " + capturedType + ", but found was type : " + actualParamType;
phraseWhoseSignaturIsChecked.addTextlyError(message, true);
return BlocklyType.NOTHING;
String message = "for parameter " + i + " the expected captured type is: " + b2m(capturedType) + ", but found was type : " + b2m(actualParamType);
phraseToCheck.addTextlyError(message, true);
return returnTypeIfError;
}
} else if ( this.paramTypes[i].equalAsTypes(BlocklyType.CAPTURED_TYPE_ARRAY_ITEM) ) {
if ( capturedType == null ) {
capturedType = actualParamType.getMatchingArrayTypeForElementType();
returnTypeIfError = capturedType;
} else if ( !actualParamType.equalAsTypes(capturedType.getMatchingElementTypeForArrayType()) ) {
String message = "For the " + formatName(phraseWhoseSignaturIsChecked.getKind().getName()) + " the expected type for one of the parameters is: " + String.valueOf(capturedType.getMatchingElementTypeForArrayType()).toLowerCase() + ", but found was type: " + String.valueOf(actualParamType).toLowerCase();
phraseWhoseSignaturIsChecked.addTextlyError(message, true);
return BlocklyType.NOTHING;
String message = "For the " + n2m(phraseToCheck) + " the expected type for one of the parameters is: " + b2m(capturedType.getMatchingElementTypeForArrayType()) + ", but found was type: " + b2m(actualParamType);
phraseToCheck.addTextlyError(message, true);
return returnTypeIfError;
}
} else {

if ( phraseWhoseSignaturIsChecked instanceof Binary ) {
String[] op = ((Binary) phraseWhoseSignaturIsChecked).op.values;
String message = "For the function " + op[0] + " The expected type for one of the parameters is: " + String.valueOf(this.paramTypes[i]).toLowerCase() + ", but found was type: " + formatName(String.valueOf(actualParamType));
phraseWhoseSignaturIsChecked.addTextlyError(message, true);
return BlocklyType.NOTHING;
if ( phraseToCheck instanceof Binary ) {
String[] op = ((Binary) phraseToCheck).op.values;
String leftOrRight = i==0 ? "left" : "right";
String message = "For the binary op \"" + op[0] + "\" the expected type for the " + leftOrRight + " parameter is: " + b2m(this.paramTypes[i]) + ", but found was type: " + b2m(actualParamType);
phraseToCheck.addTextlyError(message, true);
return returnTypeIfError;
} else {
String message = "For the " + formatName(phraseWhoseSignaturIsChecked.getKind().getName()) + " the expected type for one of the parameters is: " + String.valueOf(this.paramTypes[i]).toLowerCase() + ", but found was type: " + String.valueOf(actualParamType).toLowerCase();
//String message = "for parameter " + (i + 1) + " the expected type is: " + this.paramTypes[i] + ", but found was type: " + actualParamType;
phraseWhoseSignaturIsChecked.addTextlyError(message, true);
return BlocklyType.NOTHING;
String message = "For the parameter " + (i+1) + " of " + n2m(phraseToCheck) + " the expected type: " + b2m(this.paramTypes[i]) + ", but found was type: " + b2m(actualParamType);
phraseToCheck.addTextlyError(message, true);
return returnTypeIfError;
}
}
}
Expand All @@ -125,17 +127,23 @@ private BlocklyType typeCheck(Phrase phraseWhoseSignaturIsChecked, List<BlocklyT
if ( capturedType.isArray() ) {
return capturedType.getMatchingElementTypeForArrayType();
} else {
phraseWhoseSignaturIsChecked.addTextlyError("For this function a List was expected, but found was type: " + capturedType.toString().toLowerCase(), true);
return BlocklyType.NOTHING;
phraseToCheck.addTextlyError("For this function a list was expected, but found was type: " + b2m(capturedType), true);
return returnTypeIfError;
}

} else {
return this.returnType;
}
}

private static String formatName(String input) {
String formatted = input.toLowerCase().replace('_', ' ');
/**
* format a name of a phrase to be usable as part of a message
* @param phrase of which the name is formatted
* @return the formatted name
*/
private static String n2m(Phrase phrase) {
String name = phrase.getKind().getName();
String formatted = name.toLowerCase().replace('_', ' ');

if ( formatted.length() > 0 ) {
formatted = formatted.substring(0, 1).toUpperCase() + formatted.substring(1);
Expand All @@ -144,6 +152,14 @@ private static String formatName(String input) {
return formatted;
}

/**
* format a BlocklyType to be usable as part of a message
* @param blocklyType the BlocklyType to be formatted
* @return the formatted BlocklyType
*/private static String b2m(BlocklyType blocklyType) {
return String.valueOf(blocklyType).toLowerCase();
}

public BlocklyType typeCheckPhraseList(Phrase phraseToCheck, IVisitor<BlocklyType> visitor, List<? extends Phrase> phrases) {
List<BlocklyType> parameterTypes = new ArrayList<>();
for ( Phrase phrase : phrases ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ public String getOpSymbol() {
* get function from {@link FunctionNames} from string parameter. It is possible for one function to have multiple string mappings. Throws exception if the
* operator does not exists.
*
* @param s of the function
* @param s name of the function
* @return function from the enum {@link FunctionNames}
*/

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ public BlocklyType visitColorConst(ColorConst colorConst) {

@Override
public BlocklyType visitDebugAction(DebugAction debugAction) {
// really ANY, not PRIM only
return Sig.of(BlocklyType.VOID, BlocklyType.ANY).typeCheckPhrases(debugAction, this, debugAction.value);
}

Expand Down Expand Up @@ -471,24 +472,24 @@ public BlocklyType visitRepeatStmt(RepeatStmt repeatStmt) {
case "FOR_EACH":
Binary exprBinary = (Binary) repeatStmt.expr;
BlocklyType listType = typeCheckPhrase(repeatStmt, exprBinary.right, BlocklyType.CAPTURED_TYPE);
BlocklyType varType = typeCheckPhrase(repeatStmt, exprBinary.left, BlocklyType.CAPTURED_TYPE);
BlocklyType varType = exprBinary.left instanceof VarDeclaration ? exprBinary.left.getBlocklyType() : BlocklyType.NOTHING;
if ( listType.isArray() ) {
if ( listType.getMatchingElementTypeForArrayType().equalAsTypes(((VarDeclaration) exprBinary.left).getBlocklyType()) ) {
if ( listType.getMatchingElementTypeForArrayType().equalAsTypes(varType) ) {
break;
} else {
repeatStmt.addTextlyError("A list of " + exprBinary.left.getBlocklyType().getBlocklyName() + " was expected but it was found a " + listType.getMatchingElementTypeForArrayType().toString().toLowerCase(), true);
repeatStmt.addTextlyError("a list of " + exprBinary.left.getBlocklyType().getBlocklyName() + " was expected but it was found a list of " + listType.getMatchingElementTypeForArrayType().toString().toLowerCase(), true);
break;
}
} else {
repeatStmt.addTextlyError("This control statement is only for a list of numbers, images, strings or booleans", true);
repeatStmt.addTextlyError("this control statement is only for a list of numbers, images, strings or booleans", true);
break;
}
case "WAIT":
typeCheckPhrase(repeatStmt, repeatStmt.expr, BlocklyType.BOOLEAN);
break;

default:
repeatStmt.addTextlyError("Invalid repeat mode. Expected 'TIMES', 'FOR', 'UNTIL', 'WHILE', 'FOREVER', 'FOR_EACH', or 'WAIT'.", true);
repeatStmt.addTextlyError("invalid repeat mode. Expected 'TIMES', 'FOR', 'UNTIL', 'WHILE', 'FOREVER', 'FOR_EACH', or 'WAIT'.", true);
}
typeCheckPhrase(repeatStmt, repeatStmt.list, BlocklyType.VOID);
return BlocklyType.VOID;
Expand Down Expand Up @@ -544,7 +545,7 @@ public BlocklyType visitStringConst(StringConst stringConst) {

@Override
public BlocklyType visitTextAppendStmt(TextAppendStmt textAppendStmt) {
return Sig.of(BlocklyType.VOID, BlocklyType.STRING, BlocklyType.STRING).typeCheckPhrases(textAppendStmt, this, textAppendStmt.var, textAppendStmt.text);
return Sig.of(BlocklyType.VOID, BlocklyType.STRING, BlocklyType.PRIM).typeCheckPhrases(textAppendStmt, this, textAppendStmt.var, textAppendStmt.text);
}

@Override
Expand All @@ -554,7 +555,7 @@ public BlocklyType visitTextCharCastNumberFunct(TextCharCastNumberFunct textChar

@Override
public BlocklyType visitTextJoinFunct(TextJoinFunct textJoinFunct) {
return Sig.of(BlocklyType.STRING, BlocklyType.VARARGS, BlocklyType.ANY).typeCheckPhraseList(textJoinFunct, this, textJoinFunct.param.el);
return Sig.of(BlocklyType.STRING, BlocklyType.VARARGS, BlocklyType.PRIM).typeCheckPhraseList(textJoinFunct, this, textJoinFunct.param.el);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,8 +314,8 @@ public void testAllRobotSpecificProgramsAsUnitTests() throws Exception {
@Ignore
@Test
public void testOneRobotSpecificProgramAsUnitTests() throws Exception {
String robotName = "ev3lejosv1";
String programName = "textlyJava_expressions";
String robotName = "microbitv2";
String programName = "for_loop";
LOG.info("========= testing program " + programName + " for robot " + robotName);
final String resourceDirectory = setupRobotFactoryAndGetResourceDirForRobotSpecificTests(robotName);
runRegenerateAndCodeGenerationForOneRobotSpecificProgram(resourceDirectory, programName + ".xml", robotName, Collections.emptyList());
Expand Down
Loading

0 comments on commit 67847a1

Please sign in to comment.