diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASS.g4 b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASS.g4 new file mode 100644 index 00000000000..6fc4b8c1465 --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASS.g4 @@ -0,0 +1,285 @@ +grammar CASS; + +// -------------------------- +// 1) Top-Level Structure +// -------------------------- + +// Parse one or more function definitions. +prog + : statement+ EOF + ; + +functionDefinition + : typeSpec primaryExpression '(' parameterList? ')' compoundStatement + ; + +// A block of statements in braces +compoundStatement + : '{' statement* '}' + ; + + +// -------------------------- +// 2) Declarations & Statements +// -------------------------- + +statement + + : declarationStatement + | forBlockStatement + | forSingleStatement + | whileBlockStatement + | whileSingleStatement + | ifBlockStatement + | ifSingleStatement + | returnStatement + | switchStatement + | caseStatement + | expressionStatement + | functionDefinition + | includeStatement + ; + +declarationStatement + : typeSpec POINTER* (primaryExpression || arrayDeclarator) ('=' (expression || nullptr || emptyInitializer))? ';'? + ; + +forBlockStatement + : 'for' '(' (declarationStatement || assignmentExpression) ';' logicalOrExpression ';' unaryExpression ')' compoundStatement + ; + +forSingleStatement + : 'for' '(' (declarationStatement?|| assignmentExpression) ';' logicalOrExpression ';' unaryExpression ')' statement + ; + +conditionClause + : logicalOrExpression + ; + +whileBlockStatement + : 'while' '(' conditionClause ')' compoundStatement + ; + +whileSingleStatement + : 'while' '(' conditionClause ')' statement + ; + +ifBlockStatement + : 'if' '(' conditionClause ')' compoundStatement elseClause? + ; + +ifSingleStatement + : 'if' '(' conditionClause ')' statement elseClause? + ; + +elseClause + : 'else' (compoundStatement | ifBlockStatement | statement) + ; + +switchStatement + : 'switch' '(' conditionClause ')' compoundStatement + ; + +caseStatement + : ('case' | defaultExpression) primaryExpression? ':' statement* breakExpression? + ; + +functionCall + : ID '(' argumentList? ')' + ; + +arrayDeclarator + : primaryExpression '[' primaryExpression? ']' + ; + +listInitializer + : '{' primaryExpression (',' primaryExpression)* '}' + ; + +emptyInitializer + : '{' '}' + ; + +nullptr + : 'nullptr' + ; + +argumentList + : expression (',' expression)* + ; + +returnStatement + : 'return' expression? ';' + ; + +expressionStatement + : expression ';' + ; + +includeStatement + : 'include' STRING + ; +// -------------------------- +// 3) Parameters & Types +// -------------------------- + +parameterList + : parameter (',' parameter)* + ; + +parameter + : typeSpec primaryExpression + ; + +typeSpec + : 'int' + | 'float' + | 'double' + | 'void' + ; + +// -------------------------- +// 4) Expressions +// -------------------------- + +// For simplicity, we let "expression" wrap typical C operator precedences. + + +expression + : assignmentExpression + | functionCall + ; + +defaultExpression + : 'default' + ; + +breakExpression + : 'break' ';' + ; + +assignmentExpression + : unaryExpression assignmentOperator assignmentExpression nullptr? emptyInitializer? + | logicalOrExpression + ; + +unaryExpression + : ('++' | '--') unaryExpression + | unaryExpression ('++' | '--') + | pointerExpression + | primaryExpression + | listInitializer + ; + +comparingExpression + : '>' + | '<' + | '<=' + | '>=' + ; + +primaryExpression + : ID + | INT + | FLOAT + | CHAR + | STRING + | BOOL + | functionCall + | '(' expression ')' + ; + +pointerExpression + : '&' primaryExpression + | '*' primaryExpression + ; + +assignmentOperator + : '=' + | '+=' + | '-=' + | '*=' + | '/=' + ; + +logicalOrExpression + : logicalAndExpression ('||' logicalAndExpression)* + ; + +logicalAndExpression + : equalityExpression ('&&' equalityExpression)* + ; + +equalityExpression + : relationalExpression (( '==' | '!=' ) relationalExpression)* + ; + +relationalExpression + : additiveExpression (( '<' | '>' | '<=' | '>=' ) additiveExpression)* + ; + +additiveExpression + : multiplicativeExpression (( '+' | '-' ) multiplicativeExpression)* + ; + +multiplicativeExpression + : unaryExpression (( '*' | '/' | '%' ) unaryExpression)* + ; + +operationExpression + : additiveExpression // Handles '+' and '-' precedence + | multiplicativeExpression // Handles '*' and '/' + ; + +// -------------------------- +// 5) Lexer Rules +// -------------------------- + +SL_COMMENT + : '//' ~[\r\n]* -> skip + ; + +ML_COMMENT + : '/*' .*? '*/' -> skip + ; + +ID + : [a-zA-Z_] [a-zA-Z0-9_]* + ; + +INT + : '-'? [0-9]+ + ; + +BOOL + : 'true' + | 'false' + ; + +FLOAT + : [0-9]+ '.' [0-9]+ ([eE] [+-]? [0-9]+)? + | '.' [0-9]+ ([eE] [+-]? [0-9]+)? + | [0-9]+ ([eE] [+-]? [0-9]+) + ; + +CHAR + : '"'[a-zA-Z] '"' + ; + +POINTER + : '*' + ; + + +STRING + : '"' (ESC_SEQ | ~["\\])* '"' // A string starts and ends with double quotes + ; + +fragment ESC_SEQ + : '\\' [btnfr"'\\] // Escape sequences for backslash, single quote, double quote, etc. + ; + +// Skip whitespace and newlines +WS + : [ \t\r\n]+ -> skip + ; diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASS.interp b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASS.interp new file mode 100644 index 00000000000..69658b30c9b --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASS.interp @@ -0,0 +1,162 @@ +token literal names: +null +'(' +')' +'{' +'}' +'=' +';' +'for' +'while' +'if' +'else' +'switch' +'case' +':' +'[' +']' +',' +'nullptr' +'return' +'include' +'int' +'float' +'double' +'void' +'default' +'break' +'++' +'--' +'>' +'<' +'<=' +'>=' +'&' +'+=' +'-=' +'*=' +'/=' +'||' +'&&' +'==' +'!=' +'+' +'-' +'/' +'%' +null +null +null +null +null +null +null +'*' +null +null + +token symbolic names: +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +SL_COMMENT +ML_COMMENT +ID +INT +BOOL +FLOAT +CHAR +POINTER +STRING +WS + +rule names: +prog +functionDefinition +compoundStatement +statement +declarationStatement +forBlockStatement +forSingleStatement +conditionClause +whileBlockStatement +whileSingleStatement +ifBlockStatement +ifSingleStatement +elseClause +switchStatement +caseStatement +functionCall +arrayDeclarator +listInitializer +emptyInitializer +nullptr +argumentList +returnStatement +expressionStatement +includeStatement +parameterList +parameter +typeSpec +expression +defaultExpression +breakExpression +assignmentExpression +unaryExpression +comparingExpression +primaryExpression +pointerExpression +assignmentOperator +logicalOrExpression +logicalAndExpression +equalityExpression +relationalExpression +additiveExpression +multiplicativeExpression +operationExpression + + +atn: +[4, 1, 54, 415, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 1, 0, 4, 0, 88, 8, 0, 11, 0, 12, 0, 89, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 98, 8, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 5, 2, 105, 8, 2, 10, 2, 12, 2, 108, 9, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 125, 8, 3, 1, 4, 1, 4, 5, 4, 129, 8, 4, 10, 4, 12, 4, 132, 9, 4, 1, 4, 1, 4, 1, 4, 3, 4, 137, 8, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 3, 4, 145, 8, 4, 3, 4, 147, 8, 4, 1, 4, 3, 4, 150, 8, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 157, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 3, 6, 169, 8, 6, 1, 6, 1, 6, 3, 6, 173, 8, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 3, 10, 202, 8, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 3, 11, 210, 8, 11, 1, 12, 1, 12, 1, 12, 1, 12, 3, 12, 216, 8, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 3, 14, 226, 8, 14, 1, 14, 3, 14, 229, 8, 14, 1, 14, 1, 14, 5, 14, 233, 8, 14, 10, 14, 12, 14, 236, 9, 14, 1, 14, 3, 14, 239, 8, 14, 1, 15, 1, 15, 1, 15, 3, 15, 244, 8, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 3, 16, 251, 8, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 5, 17, 259, 8, 17, 10, 17, 12, 17, 262, 9, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 5, 20, 274, 8, 20, 10, 20, 12, 20, 277, 9, 20, 1, 21, 1, 21, 3, 21, 281, 8, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 5, 24, 294, 8, 24, 10, 24, 12, 24, 297, 9, 24, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 27, 1, 27, 3, 27, 306, 8, 27, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 30, 3, 30, 317, 8, 30, 1, 30, 3, 30, 320, 8, 30, 1, 30, 3, 30, 323, 8, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 3, 31, 331, 8, 31, 1, 31, 1, 31, 5, 31, 335, 8, 31, 10, 31, 12, 31, 338, 9, 31, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 3, 33, 353, 8, 33, 1, 34, 1, 34, 1, 34, 1, 34, 3, 34, 359, 8, 34, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 5, 36, 366, 8, 36, 10, 36, 12, 36, 369, 9, 36, 1, 37, 1, 37, 1, 37, 5, 37, 374, 8, 37, 10, 37, 12, 37, 377, 9, 37, 1, 38, 1, 38, 1, 38, 5, 38, 382, 8, 38, 10, 38, 12, 38, 385, 9, 38, 1, 39, 1, 39, 1, 39, 5, 39, 390, 8, 39, 10, 39, 12, 39, 393, 9, 39, 1, 40, 1, 40, 1, 40, 5, 40, 398, 8, 40, 10, 40, 12, 40, 401, 9, 40, 1, 41, 1, 41, 1, 41, 5, 41, 406, 8, 41, 10, 41, 12, 41, 409, 9, 41, 1, 42, 1, 42, 3, 42, 413, 8, 42, 1, 42, 0, 1, 62, 43, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 0, 7, 1, 0, 20, 23, 1, 0, 26, 27, 1, 0, 28, 31, 2, 0, 5, 5, 33, 36, 1, 0, 39, 40, 1, 0, 41, 42, 2, 0, 43, 44, 52, 52, 437, 0, 87, 1, 0, 0, 0, 2, 93, 1, 0, 0, 0, 4, 102, 1, 0, 0, 0, 6, 124, 1, 0, 0, 0, 8, 126, 1, 0, 0, 0, 10, 151, 1, 0, 0, 0, 12, 165, 1, 0, 0, 0, 14, 181, 1, 0, 0, 0, 16, 183, 1, 0, 0, 0, 18, 189, 1, 0, 0, 0, 20, 195, 1, 0, 0, 0, 22, 203, 1, 0, 0, 0, 24, 211, 1, 0, 0, 0, 26, 217, 1, 0, 0, 0, 28, 225, 1, 0, 0, 0, 30, 240, 1, 0, 0, 0, 32, 247, 1, 0, 0, 0, 34, 254, 1, 0, 0, 0, 36, 265, 1, 0, 0, 0, 38, 268, 1, 0, 0, 0, 40, 270, 1, 0, 0, 0, 42, 278, 1, 0, 0, 0, 44, 284, 1, 0, 0, 0, 46, 287, 1, 0, 0, 0, 48, 290, 1, 0, 0, 0, 50, 298, 1, 0, 0, 0, 52, 301, 1, 0, 0, 0, 54, 305, 1, 0, 0, 0, 56, 307, 1, 0, 0, 0, 58, 309, 1, 0, 0, 0, 60, 322, 1, 0, 0, 0, 62, 330, 1, 0, 0, 0, 64, 339, 1, 0, 0, 0, 66, 352, 1, 0, 0, 0, 68, 358, 1, 0, 0, 0, 70, 360, 1, 0, 0, 0, 72, 362, 1, 0, 0, 0, 74, 370, 1, 0, 0, 0, 76, 378, 1, 0, 0, 0, 78, 386, 1, 0, 0, 0, 80, 394, 1, 0, 0, 0, 82, 402, 1, 0, 0, 0, 84, 412, 1, 0, 0, 0, 86, 88, 3, 6, 3, 0, 87, 86, 1, 0, 0, 0, 88, 89, 1, 0, 0, 0, 89, 87, 1, 0, 0, 0, 89, 90, 1, 0, 0, 0, 90, 91, 1, 0, 0, 0, 91, 92, 5, 0, 0, 1, 92, 1, 1, 0, 0, 0, 93, 94, 3, 52, 26, 0, 94, 95, 3, 66, 33, 0, 95, 97, 5, 1, 0, 0, 96, 98, 3, 48, 24, 0, 97, 96, 1, 0, 0, 0, 97, 98, 1, 0, 0, 0, 98, 99, 1, 0, 0, 0, 99, 100, 5, 2, 0, 0, 100, 101, 3, 4, 2, 0, 101, 3, 1, 0, 0, 0, 102, 106, 5, 3, 0, 0, 103, 105, 3, 6, 3, 0, 104, 103, 1, 0, 0, 0, 105, 108, 1, 0, 0, 0, 106, 104, 1, 0, 0, 0, 106, 107, 1, 0, 0, 0, 107, 109, 1, 0, 0, 0, 108, 106, 1, 0, 0, 0, 109, 110, 5, 4, 0, 0, 110, 5, 1, 0, 0, 0, 111, 125, 3, 8, 4, 0, 112, 125, 3, 10, 5, 0, 113, 125, 3, 12, 6, 0, 114, 125, 3, 16, 8, 0, 115, 125, 3, 18, 9, 0, 116, 125, 3, 20, 10, 0, 117, 125, 3, 22, 11, 0, 118, 125, 3, 42, 21, 0, 119, 125, 3, 26, 13, 0, 120, 125, 3, 28, 14, 0, 121, 125, 3, 44, 22, 0, 122, 125, 3, 2, 1, 0, 123, 125, 3, 46, 23, 0, 124, 111, 1, 0, 0, 0, 124, 112, 1, 0, 0, 0, 124, 113, 1, 0, 0, 0, 124, 114, 1, 0, 0, 0, 124, 115, 1, 0, 0, 0, 124, 116, 1, 0, 0, 0, 124, 117, 1, 0, 0, 0, 124, 118, 1, 0, 0, 0, 124, 119, 1, 0, 0, 0, 124, 120, 1, 0, 0, 0, 124, 121, 1, 0, 0, 0, 124, 122, 1, 0, 0, 0, 124, 123, 1, 0, 0, 0, 125, 7, 1, 0, 0, 0, 126, 130, 3, 52, 26, 0, 127, 129, 5, 52, 0, 0, 128, 127, 1, 0, 0, 0, 129, 132, 1, 0, 0, 0, 130, 128, 1, 0, 0, 0, 130, 131, 1, 0, 0, 0, 131, 136, 1, 0, 0, 0, 132, 130, 1, 0, 0, 0, 133, 137, 3, 66, 33, 0, 134, 137, 1, 0, 0, 0, 135, 137, 3, 32, 16, 0, 136, 133, 1, 0, 0, 0, 136, 134, 1, 0, 0, 0, 136, 135, 1, 0, 0, 0, 137, 146, 1, 0, 0, 0, 138, 144, 5, 5, 0, 0, 139, 145, 3, 54, 27, 0, 140, 145, 1, 0, 0, 0, 141, 145, 3, 38, 19, 0, 142, 145, 1, 0, 0, 0, 143, 145, 3, 36, 18, 0, 144, 139, 1, 0, 0, 0, 144, 140, 1, 0, 0, 0, 144, 141, 1, 0, 0, 0, 144, 142, 1, 0, 0, 0, 144, 143, 1, 0, 0, 0, 145, 147, 1, 0, 0, 0, 146, 138, 1, 0, 0, 0, 146, 147, 1, 0, 0, 0, 147, 149, 1, 0, 0, 0, 148, 150, 5, 6, 0, 0, 149, 148, 1, 0, 0, 0, 149, 150, 1, 0, 0, 0, 150, 9, 1, 0, 0, 0, 151, 152, 5, 7, 0, 0, 152, 156, 5, 1, 0, 0, 153, 157, 3, 8, 4, 0, 154, 157, 1, 0, 0, 0, 155, 157, 3, 60, 30, 0, 156, 153, 1, 0, 0, 0, 156, 154, 1, 0, 0, 0, 156, 155, 1, 0, 0, 0, 157, 158, 1, 0, 0, 0, 158, 159, 5, 6, 0, 0, 159, 160, 3, 72, 36, 0, 160, 161, 5, 6, 0, 0, 161, 162, 3, 62, 31, 0, 162, 163, 5, 2, 0, 0, 163, 164, 3, 4, 2, 0, 164, 11, 1, 0, 0, 0, 165, 166, 5, 7, 0, 0, 166, 172, 5, 1, 0, 0, 167, 169, 3, 8, 4, 0, 168, 167, 1, 0, 0, 0, 168, 169, 1, 0, 0, 0, 169, 173, 1, 0, 0, 0, 170, 173, 1, 0, 0, 0, 171, 173, 3, 60, 30, 0, 172, 168, 1, 0, 0, 0, 172, 170, 1, 0, 0, 0, 172, 171, 1, 0, 0, 0, 173, 174, 1, 0, 0, 0, 174, 175, 5, 6, 0, 0, 175, 176, 3, 72, 36, 0, 176, 177, 5, 6, 0, 0, 177, 178, 3, 62, 31, 0, 178, 179, 5, 2, 0, 0, 179, 180, 3, 6, 3, 0, 180, 13, 1, 0, 0, 0, 181, 182, 3, 72, 36, 0, 182, 15, 1, 0, 0, 0, 183, 184, 5, 8, 0, 0, 184, 185, 5, 1, 0, 0, 185, 186, 3, 14, 7, 0, 186, 187, 5, 2, 0, 0, 187, 188, 3, 4, 2, 0, 188, 17, 1, 0, 0, 0, 189, 190, 5, 8, 0, 0, 190, 191, 5, 1, 0, 0, 191, 192, 3, 14, 7, 0, 192, 193, 5, 2, 0, 0, 193, 194, 3, 6, 3, 0, 194, 19, 1, 0, 0, 0, 195, 196, 5, 9, 0, 0, 196, 197, 5, 1, 0, 0, 197, 198, 3, 14, 7, 0, 198, 199, 5, 2, 0, 0, 199, 201, 3, 4, 2, 0, 200, 202, 3, 24, 12, 0, 201, 200, 1, 0, 0, 0, 201, 202, 1, 0, 0, 0, 202, 21, 1, 0, 0, 0, 203, 204, 5, 9, 0, 0, 204, 205, 5, 1, 0, 0, 205, 206, 3, 14, 7, 0, 206, 207, 5, 2, 0, 0, 207, 209, 3, 6, 3, 0, 208, 210, 3, 24, 12, 0, 209, 208, 1, 0, 0, 0, 209, 210, 1, 0, 0, 0, 210, 23, 1, 0, 0, 0, 211, 215, 5, 10, 0, 0, 212, 216, 3, 4, 2, 0, 213, 216, 3, 20, 10, 0, 214, 216, 3, 6, 3, 0, 215, 212, 1, 0, 0, 0, 215, 213, 1, 0, 0, 0, 215, 214, 1, 0, 0, 0, 216, 25, 1, 0, 0, 0, 217, 218, 5, 11, 0, 0, 218, 219, 5, 1, 0, 0, 219, 220, 3, 14, 7, 0, 220, 221, 5, 2, 0, 0, 221, 222, 3, 4, 2, 0, 222, 27, 1, 0, 0, 0, 223, 226, 5, 12, 0, 0, 224, 226, 3, 56, 28, 0, 225, 223, 1, 0, 0, 0, 225, 224, 1, 0, 0, 0, 226, 228, 1, 0, 0, 0, 227, 229, 3, 66, 33, 0, 228, 227, 1, 0, 0, 0, 228, 229, 1, 0, 0, 0, 229, 230, 1, 0, 0, 0, 230, 234, 5, 13, 0, 0, 231, 233, 3, 6, 3, 0, 232, 231, 1, 0, 0, 0, 233, 236, 1, 0, 0, 0, 234, 232, 1, 0, 0, 0, 234, 235, 1, 0, 0, 0, 235, 238, 1, 0, 0, 0, 236, 234, 1, 0, 0, 0, 237, 239, 3, 58, 29, 0, 238, 237, 1, 0, 0, 0, 238, 239, 1, 0, 0, 0, 239, 29, 1, 0, 0, 0, 240, 241, 5, 47, 0, 0, 241, 243, 5, 1, 0, 0, 242, 244, 3, 40, 20, 0, 243, 242, 1, 0, 0, 0, 243, 244, 1, 0, 0, 0, 244, 245, 1, 0, 0, 0, 245, 246, 5, 2, 0, 0, 246, 31, 1, 0, 0, 0, 247, 248, 3, 66, 33, 0, 248, 250, 5, 14, 0, 0, 249, 251, 3, 66, 33, 0, 250, 249, 1, 0, 0, 0, 250, 251, 1, 0, 0, 0, 251, 252, 1, 0, 0, 0, 252, 253, 5, 15, 0, 0, 253, 33, 1, 0, 0, 0, 254, 255, 5, 3, 0, 0, 255, 260, 3, 66, 33, 0, 256, 257, 5, 16, 0, 0, 257, 259, 3, 66, 33, 0, 258, 256, 1, 0, 0, 0, 259, 262, 1, 0, 0, 0, 260, 258, 1, 0, 0, 0, 260, 261, 1, 0, 0, 0, 261, 263, 1, 0, 0, 0, 262, 260, 1, 0, 0, 0, 263, 264, 5, 4, 0, 0, 264, 35, 1, 0, 0, 0, 265, 266, 5, 3, 0, 0, 266, 267, 5, 4, 0, 0, 267, 37, 1, 0, 0, 0, 268, 269, 5, 17, 0, 0, 269, 39, 1, 0, 0, 0, 270, 275, 3, 54, 27, 0, 271, 272, 5, 16, 0, 0, 272, 274, 3, 54, 27, 0, 273, 271, 1, 0, 0, 0, 274, 277, 1, 0, 0, 0, 275, 273, 1, 0, 0, 0, 275, 276, 1, 0, 0, 0, 276, 41, 1, 0, 0, 0, 277, 275, 1, 0, 0, 0, 278, 280, 5, 18, 0, 0, 279, 281, 3, 54, 27, 0, 280, 279, 1, 0, 0, 0, 280, 281, 1, 0, 0, 0, 281, 282, 1, 0, 0, 0, 282, 283, 5, 6, 0, 0, 283, 43, 1, 0, 0, 0, 284, 285, 3, 54, 27, 0, 285, 286, 5, 6, 0, 0, 286, 45, 1, 0, 0, 0, 287, 288, 5, 19, 0, 0, 288, 289, 5, 53, 0, 0, 289, 47, 1, 0, 0, 0, 290, 295, 3, 50, 25, 0, 291, 292, 5, 16, 0, 0, 292, 294, 3, 50, 25, 0, 293, 291, 1, 0, 0, 0, 294, 297, 1, 0, 0, 0, 295, 293, 1, 0, 0, 0, 295, 296, 1, 0, 0, 0, 296, 49, 1, 0, 0, 0, 297, 295, 1, 0, 0, 0, 298, 299, 3, 52, 26, 0, 299, 300, 3, 66, 33, 0, 300, 51, 1, 0, 0, 0, 301, 302, 7, 0, 0, 0, 302, 53, 1, 0, 0, 0, 303, 306, 3, 60, 30, 0, 304, 306, 3, 30, 15, 0, 305, 303, 1, 0, 0, 0, 305, 304, 1, 0, 0, 0, 306, 55, 1, 0, 0, 0, 307, 308, 5, 24, 0, 0, 308, 57, 1, 0, 0, 0, 309, 310, 5, 25, 0, 0, 310, 311, 5, 6, 0, 0, 311, 59, 1, 0, 0, 0, 312, 313, 3, 62, 31, 0, 313, 314, 3, 70, 35, 0, 314, 316, 3, 60, 30, 0, 315, 317, 3, 38, 19, 0, 316, 315, 1, 0, 0, 0, 316, 317, 1, 0, 0, 0, 317, 319, 1, 0, 0, 0, 318, 320, 3, 36, 18, 0, 319, 318, 1, 0, 0, 0, 319, 320, 1, 0, 0, 0, 320, 323, 1, 0, 0, 0, 321, 323, 3, 72, 36, 0, 322, 312, 1, 0, 0, 0, 322, 321, 1, 0, 0, 0, 323, 61, 1, 0, 0, 0, 324, 325, 6, 31, -1, 0, 325, 326, 7, 1, 0, 0, 326, 331, 3, 62, 31, 5, 327, 331, 3, 68, 34, 0, 328, 331, 3, 66, 33, 0, 329, 331, 3, 34, 17, 0, 330, 324, 1, 0, 0, 0, 330, 327, 1, 0, 0, 0, 330, 328, 1, 0, 0, 0, 330, 329, 1, 0, 0, 0, 331, 336, 1, 0, 0, 0, 332, 333, 10, 4, 0, 0, 333, 335, 7, 1, 0, 0, 334, 332, 1, 0, 0, 0, 335, 338, 1, 0, 0, 0, 336, 334, 1, 0, 0, 0, 336, 337, 1, 0, 0, 0, 337, 63, 1, 0, 0, 0, 338, 336, 1, 0, 0, 0, 339, 340, 7, 2, 0, 0, 340, 65, 1, 0, 0, 0, 341, 353, 5, 47, 0, 0, 342, 353, 5, 48, 0, 0, 343, 353, 5, 50, 0, 0, 344, 353, 5, 51, 0, 0, 345, 353, 5, 53, 0, 0, 346, 353, 5, 49, 0, 0, 347, 353, 3, 30, 15, 0, 348, 349, 5, 1, 0, 0, 349, 350, 3, 54, 27, 0, 350, 351, 5, 2, 0, 0, 351, 353, 1, 0, 0, 0, 352, 341, 1, 0, 0, 0, 352, 342, 1, 0, 0, 0, 352, 343, 1, 0, 0, 0, 352, 344, 1, 0, 0, 0, 352, 345, 1, 0, 0, 0, 352, 346, 1, 0, 0, 0, 352, 347, 1, 0, 0, 0, 352, 348, 1, 0, 0, 0, 353, 67, 1, 0, 0, 0, 354, 355, 5, 32, 0, 0, 355, 359, 3, 66, 33, 0, 356, 357, 5, 52, 0, 0, 357, 359, 3, 66, 33, 0, 358, 354, 1, 0, 0, 0, 358, 356, 1, 0, 0, 0, 359, 69, 1, 0, 0, 0, 360, 361, 7, 3, 0, 0, 361, 71, 1, 0, 0, 0, 362, 367, 3, 74, 37, 0, 363, 364, 5, 37, 0, 0, 364, 366, 3, 74, 37, 0, 365, 363, 1, 0, 0, 0, 366, 369, 1, 0, 0, 0, 367, 365, 1, 0, 0, 0, 367, 368, 1, 0, 0, 0, 368, 73, 1, 0, 0, 0, 369, 367, 1, 0, 0, 0, 370, 375, 3, 76, 38, 0, 371, 372, 5, 38, 0, 0, 372, 374, 3, 76, 38, 0, 373, 371, 1, 0, 0, 0, 374, 377, 1, 0, 0, 0, 375, 373, 1, 0, 0, 0, 375, 376, 1, 0, 0, 0, 376, 75, 1, 0, 0, 0, 377, 375, 1, 0, 0, 0, 378, 383, 3, 78, 39, 0, 379, 380, 7, 4, 0, 0, 380, 382, 3, 78, 39, 0, 381, 379, 1, 0, 0, 0, 382, 385, 1, 0, 0, 0, 383, 381, 1, 0, 0, 0, 383, 384, 1, 0, 0, 0, 384, 77, 1, 0, 0, 0, 385, 383, 1, 0, 0, 0, 386, 391, 3, 80, 40, 0, 387, 388, 7, 2, 0, 0, 388, 390, 3, 80, 40, 0, 389, 387, 1, 0, 0, 0, 390, 393, 1, 0, 0, 0, 391, 389, 1, 0, 0, 0, 391, 392, 1, 0, 0, 0, 392, 79, 1, 0, 0, 0, 393, 391, 1, 0, 0, 0, 394, 399, 3, 82, 41, 0, 395, 396, 7, 5, 0, 0, 396, 398, 3, 82, 41, 0, 397, 395, 1, 0, 0, 0, 398, 401, 1, 0, 0, 0, 399, 397, 1, 0, 0, 0, 399, 400, 1, 0, 0, 0, 400, 81, 1, 0, 0, 0, 401, 399, 1, 0, 0, 0, 402, 407, 3, 62, 31, 0, 403, 404, 7, 6, 0, 0, 404, 406, 3, 62, 31, 0, 405, 403, 1, 0, 0, 0, 406, 409, 1, 0, 0, 0, 407, 405, 1, 0, 0, 0, 407, 408, 1, 0, 0, 0, 408, 83, 1, 0, 0, 0, 409, 407, 1, 0, 0, 0, 410, 413, 3, 80, 40, 0, 411, 413, 3, 82, 41, 0, 412, 410, 1, 0, 0, 0, 412, 411, 1, 0, 0, 0, 413, 85, 1, 0, 0, 0, 40, 89, 97, 106, 124, 130, 136, 144, 146, 149, 156, 168, 172, 201, 209, 215, 225, 228, 234, 238, 243, 250, 260, 275, 280, 295, 305, 316, 319, 322, 330, 336, 352, 358, 367, 375, 383, 391, 399, 407, 412] \ No newline at end of file diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASS.tokens b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASS.tokens new file mode 100644 index 00000000000..ffe14a736f0 --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASS.tokens @@ -0,0 +1,99 @@ +T__0=1 +T__1=2 +T__2=3 +T__3=4 +T__4=5 +T__5=6 +T__6=7 +T__7=8 +T__8=9 +T__9=10 +T__10=11 +T__11=12 +T__12=13 +T__13=14 +T__14=15 +T__15=16 +T__16=17 +T__17=18 +T__18=19 +T__19=20 +T__20=21 +T__21=22 +T__22=23 +T__23=24 +T__24=25 +T__25=26 +T__26=27 +T__27=28 +T__28=29 +T__29=30 +T__30=31 +T__31=32 +T__32=33 +T__33=34 +T__34=35 +T__35=36 +T__36=37 +T__37=38 +T__38=39 +T__39=40 +T__40=41 +T__41=42 +T__42=43 +T__43=44 +SL_COMMENT=45 +ML_COMMENT=46 +ID=47 +INT=48 +BOOL=49 +FLOAT=50 +CHAR=51 +POINTER=52 +STRING=53 +WS=54 +'('=1 +')'=2 +'{'=3 +'}'=4 +'='=5 +';'=6 +'for'=7 +'while'=8 +'if'=9 +'else'=10 +'switch'=11 +'case'=12 +':'=13 +'['=14 +']'=15 +','=16 +'nullptr'=17 +'return'=18 +'include'=19 +'int'=20 +'float'=21 +'double'=22 +'void'=23 +'default'=24 +'break'=25 +'++'=26 +'--'=27 +'>'=28 +'<'=29 +'<='=30 +'>='=31 +'&'=32 +'+='=33 +'-='=34 +'*='=35 +'/='=36 +'||'=37 +'&&'=38 +'=='=39 +'!='=40 +'+'=41 +'-'=42 +'/'=43 +'%'=44 +'*'=52 diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASSLexer.interp b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASSLexer.interp new file mode 100644 index 00000000000..315deca7ee1 --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASSLexer.interp @@ -0,0 +1,180 @@ +token literal names: +null +'(' +')' +'{' +'}' +'=' +';' +'for' +'while' +'if' +'else' +'switch' +'case' +':' +'[' +']' +',' +'nullptr' +'return' +'include' +'int' +'float' +'double' +'void' +'default' +'break' +'++' +'--' +'>' +'<' +'<=' +'>=' +'&' +'+=' +'-=' +'*=' +'/=' +'||' +'&&' +'==' +'!=' +'+' +'-' +'/' +'%' +null +null +null +null +null +null +null +'*' +null +null + +token symbolic names: +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +SL_COMMENT +ML_COMMENT +ID +INT +BOOL +FLOAT +CHAR +POINTER +STRING +WS + +rule names: +T__0 +T__1 +T__2 +T__3 +T__4 +T__5 +T__6 +T__7 +T__8 +T__9 +T__10 +T__11 +T__12 +T__13 +T__14 +T__15 +T__16 +T__17 +T__18 +T__19 +T__20 +T__21 +T__22 +T__23 +T__24 +T__25 +T__26 +T__27 +T__28 +T__29 +T__30 +T__31 +T__32 +T__33 +T__34 +T__35 +T__36 +T__37 +T__38 +T__39 +T__40 +T__41 +T__42 +T__43 +SL_COMMENT +ML_COMMENT +ID +INT +BOOL +FLOAT +CHAR +POINTER +STRING +ESC_SEQ +WS + +channel names: +DEFAULT_TOKEN_CHANNEL +HIDDEN + +mode names: +DEFAULT_MODE + +atn: +[4, 0, 54, 402, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 41, 1, 41, 1, 42, 1, 42, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 5, 44, 275, 8, 44, 10, 44, 12, 44, 278, 9, 44, 1, 44, 1, 44, 1, 45, 1, 45, 1, 45, 1, 45, 5, 45, 286, 8, 45, 10, 45, 12, 45, 289, 9, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 5, 46, 298, 8, 46, 10, 46, 12, 46, 301, 9, 46, 1, 47, 3, 47, 304, 8, 47, 1, 47, 4, 47, 307, 8, 47, 11, 47, 12, 47, 308, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 3, 48, 320, 8, 48, 1, 49, 4, 49, 323, 8, 49, 11, 49, 12, 49, 324, 1, 49, 1, 49, 4, 49, 329, 8, 49, 11, 49, 12, 49, 330, 1, 49, 1, 49, 3, 49, 335, 8, 49, 1, 49, 4, 49, 338, 8, 49, 11, 49, 12, 49, 339, 3, 49, 342, 8, 49, 1, 49, 1, 49, 4, 49, 346, 8, 49, 11, 49, 12, 49, 347, 1, 49, 1, 49, 3, 49, 352, 8, 49, 1, 49, 4, 49, 355, 8, 49, 11, 49, 12, 49, 356, 3, 49, 359, 8, 49, 1, 49, 4, 49, 362, 8, 49, 11, 49, 12, 49, 363, 1, 49, 1, 49, 3, 49, 368, 8, 49, 1, 49, 4, 49, 371, 8, 49, 11, 49, 12, 49, 372, 3, 49, 375, 8, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 5, 52, 386, 8, 52, 10, 52, 12, 52, 389, 9, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 54, 4, 54, 397, 8, 54, 11, 54, 12, 54, 398, 1, 54, 1, 54, 1, 287, 0, 55, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 33, 67, 34, 69, 35, 71, 36, 73, 37, 75, 38, 77, 39, 79, 40, 81, 41, 83, 42, 85, 43, 87, 44, 89, 45, 91, 46, 93, 47, 95, 48, 97, 49, 99, 50, 101, 51, 103, 52, 105, 53, 107, 0, 109, 54, 1, 0, 10, 2, 0, 10, 10, 13, 13, 3, 0, 65, 90, 95, 95, 97, 122, 4, 0, 48, 57, 65, 90, 95, 95, 97, 122, 1, 0, 48, 57, 2, 0, 69, 69, 101, 101, 2, 0, 43, 43, 45, 45, 2, 0, 65, 90, 97, 122, 2, 0, 34, 34, 92, 92, 8, 0, 34, 34, 39, 39, 92, 92, 98, 98, 102, 102, 110, 110, 114, 114, 116, 116, 3, 0, 9, 10, 13, 13, 32, 32, 423, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0, 0, 0, 65, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0, 0, 73, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 0, 77, 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0, 81, 1, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, 0, 0, 89, 1, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 93, 1, 0, 0, 0, 0, 95, 1, 0, 0, 0, 0, 97, 1, 0, 0, 0, 0, 99, 1, 0, 0, 0, 0, 101, 1, 0, 0, 0, 0, 103, 1, 0, 0, 0, 0, 105, 1, 0, 0, 0, 0, 109, 1, 0, 0, 0, 1, 111, 1, 0, 0, 0, 3, 113, 1, 0, 0, 0, 5, 115, 1, 0, 0, 0, 7, 117, 1, 0, 0, 0, 9, 119, 1, 0, 0, 0, 11, 121, 1, 0, 0, 0, 13, 123, 1, 0, 0, 0, 15, 127, 1, 0, 0, 0, 17, 133, 1, 0, 0, 0, 19, 136, 1, 0, 0, 0, 21, 141, 1, 0, 0, 0, 23, 148, 1, 0, 0, 0, 25, 153, 1, 0, 0, 0, 27, 155, 1, 0, 0, 0, 29, 157, 1, 0, 0, 0, 31, 159, 1, 0, 0, 0, 33, 161, 1, 0, 0, 0, 35, 169, 1, 0, 0, 0, 37, 176, 1, 0, 0, 0, 39, 184, 1, 0, 0, 0, 41, 188, 1, 0, 0, 0, 43, 194, 1, 0, 0, 0, 45, 201, 1, 0, 0, 0, 47, 206, 1, 0, 0, 0, 49, 214, 1, 0, 0, 0, 51, 220, 1, 0, 0, 0, 53, 223, 1, 0, 0, 0, 55, 226, 1, 0, 0, 0, 57, 228, 1, 0, 0, 0, 59, 230, 1, 0, 0, 0, 61, 233, 1, 0, 0, 0, 63, 236, 1, 0, 0, 0, 65, 238, 1, 0, 0, 0, 67, 241, 1, 0, 0, 0, 69, 244, 1, 0, 0, 0, 71, 247, 1, 0, 0, 0, 73, 250, 1, 0, 0, 0, 75, 253, 1, 0, 0, 0, 77, 256, 1, 0, 0, 0, 79, 259, 1, 0, 0, 0, 81, 262, 1, 0, 0, 0, 83, 264, 1, 0, 0, 0, 85, 266, 1, 0, 0, 0, 87, 268, 1, 0, 0, 0, 89, 270, 1, 0, 0, 0, 91, 281, 1, 0, 0, 0, 93, 295, 1, 0, 0, 0, 95, 303, 1, 0, 0, 0, 97, 319, 1, 0, 0, 0, 99, 374, 1, 0, 0, 0, 101, 376, 1, 0, 0, 0, 103, 380, 1, 0, 0, 0, 105, 382, 1, 0, 0, 0, 107, 392, 1, 0, 0, 0, 109, 396, 1, 0, 0, 0, 111, 112, 5, 40, 0, 0, 112, 2, 1, 0, 0, 0, 113, 114, 5, 41, 0, 0, 114, 4, 1, 0, 0, 0, 115, 116, 5, 123, 0, 0, 116, 6, 1, 0, 0, 0, 117, 118, 5, 125, 0, 0, 118, 8, 1, 0, 0, 0, 119, 120, 5, 61, 0, 0, 120, 10, 1, 0, 0, 0, 121, 122, 5, 59, 0, 0, 122, 12, 1, 0, 0, 0, 123, 124, 5, 102, 0, 0, 124, 125, 5, 111, 0, 0, 125, 126, 5, 114, 0, 0, 126, 14, 1, 0, 0, 0, 127, 128, 5, 119, 0, 0, 128, 129, 5, 104, 0, 0, 129, 130, 5, 105, 0, 0, 130, 131, 5, 108, 0, 0, 131, 132, 5, 101, 0, 0, 132, 16, 1, 0, 0, 0, 133, 134, 5, 105, 0, 0, 134, 135, 5, 102, 0, 0, 135, 18, 1, 0, 0, 0, 136, 137, 5, 101, 0, 0, 137, 138, 5, 108, 0, 0, 138, 139, 5, 115, 0, 0, 139, 140, 5, 101, 0, 0, 140, 20, 1, 0, 0, 0, 141, 142, 5, 115, 0, 0, 142, 143, 5, 119, 0, 0, 143, 144, 5, 105, 0, 0, 144, 145, 5, 116, 0, 0, 145, 146, 5, 99, 0, 0, 146, 147, 5, 104, 0, 0, 147, 22, 1, 0, 0, 0, 148, 149, 5, 99, 0, 0, 149, 150, 5, 97, 0, 0, 150, 151, 5, 115, 0, 0, 151, 152, 5, 101, 0, 0, 152, 24, 1, 0, 0, 0, 153, 154, 5, 58, 0, 0, 154, 26, 1, 0, 0, 0, 155, 156, 5, 91, 0, 0, 156, 28, 1, 0, 0, 0, 157, 158, 5, 93, 0, 0, 158, 30, 1, 0, 0, 0, 159, 160, 5, 44, 0, 0, 160, 32, 1, 0, 0, 0, 161, 162, 5, 110, 0, 0, 162, 163, 5, 117, 0, 0, 163, 164, 5, 108, 0, 0, 164, 165, 5, 108, 0, 0, 165, 166, 5, 112, 0, 0, 166, 167, 5, 116, 0, 0, 167, 168, 5, 114, 0, 0, 168, 34, 1, 0, 0, 0, 169, 170, 5, 114, 0, 0, 170, 171, 5, 101, 0, 0, 171, 172, 5, 116, 0, 0, 172, 173, 5, 117, 0, 0, 173, 174, 5, 114, 0, 0, 174, 175, 5, 110, 0, 0, 175, 36, 1, 0, 0, 0, 176, 177, 5, 105, 0, 0, 177, 178, 5, 110, 0, 0, 178, 179, 5, 99, 0, 0, 179, 180, 5, 108, 0, 0, 180, 181, 5, 117, 0, 0, 181, 182, 5, 100, 0, 0, 182, 183, 5, 101, 0, 0, 183, 38, 1, 0, 0, 0, 184, 185, 5, 105, 0, 0, 185, 186, 5, 110, 0, 0, 186, 187, 5, 116, 0, 0, 187, 40, 1, 0, 0, 0, 188, 189, 5, 102, 0, 0, 189, 190, 5, 108, 0, 0, 190, 191, 5, 111, 0, 0, 191, 192, 5, 97, 0, 0, 192, 193, 5, 116, 0, 0, 193, 42, 1, 0, 0, 0, 194, 195, 5, 100, 0, 0, 195, 196, 5, 111, 0, 0, 196, 197, 5, 117, 0, 0, 197, 198, 5, 98, 0, 0, 198, 199, 5, 108, 0, 0, 199, 200, 5, 101, 0, 0, 200, 44, 1, 0, 0, 0, 201, 202, 5, 118, 0, 0, 202, 203, 5, 111, 0, 0, 203, 204, 5, 105, 0, 0, 204, 205, 5, 100, 0, 0, 205, 46, 1, 0, 0, 0, 206, 207, 5, 100, 0, 0, 207, 208, 5, 101, 0, 0, 208, 209, 5, 102, 0, 0, 209, 210, 5, 97, 0, 0, 210, 211, 5, 117, 0, 0, 211, 212, 5, 108, 0, 0, 212, 213, 5, 116, 0, 0, 213, 48, 1, 0, 0, 0, 214, 215, 5, 98, 0, 0, 215, 216, 5, 114, 0, 0, 216, 217, 5, 101, 0, 0, 217, 218, 5, 97, 0, 0, 218, 219, 5, 107, 0, 0, 219, 50, 1, 0, 0, 0, 220, 221, 5, 43, 0, 0, 221, 222, 5, 43, 0, 0, 222, 52, 1, 0, 0, 0, 223, 224, 5, 45, 0, 0, 224, 225, 5, 45, 0, 0, 225, 54, 1, 0, 0, 0, 226, 227, 5, 62, 0, 0, 227, 56, 1, 0, 0, 0, 228, 229, 5, 60, 0, 0, 229, 58, 1, 0, 0, 0, 230, 231, 5, 60, 0, 0, 231, 232, 5, 61, 0, 0, 232, 60, 1, 0, 0, 0, 233, 234, 5, 62, 0, 0, 234, 235, 5, 61, 0, 0, 235, 62, 1, 0, 0, 0, 236, 237, 5, 38, 0, 0, 237, 64, 1, 0, 0, 0, 238, 239, 5, 43, 0, 0, 239, 240, 5, 61, 0, 0, 240, 66, 1, 0, 0, 0, 241, 242, 5, 45, 0, 0, 242, 243, 5, 61, 0, 0, 243, 68, 1, 0, 0, 0, 244, 245, 5, 42, 0, 0, 245, 246, 5, 61, 0, 0, 246, 70, 1, 0, 0, 0, 247, 248, 5, 47, 0, 0, 248, 249, 5, 61, 0, 0, 249, 72, 1, 0, 0, 0, 250, 251, 5, 124, 0, 0, 251, 252, 5, 124, 0, 0, 252, 74, 1, 0, 0, 0, 253, 254, 5, 38, 0, 0, 254, 255, 5, 38, 0, 0, 255, 76, 1, 0, 0, 0, 256, 257, 5, 61, 0, 0, 257, 258, 5, 61, 0, 0, 258, 78, 1, 0, 0, 0, 259, 260, 5, 33, 0, 0, 260, 261, 5, 61, 0, 0, 261, 80, 1, 0, 0, 0, 262, 263, 5, 43, 0, 0, 263, 82, 1, 0, 0, 0, 264, 265, 5, 45, 0, 0, 265, 84, 1, 0, 0, 0, 266, 267, 5, 47, 0, 0, 267, 86, 1, 0, 0, 0, 268, 269, 5, 37, 0, 0, 269, 88, 1, 0, 0, 0, 270, 271, 5, 47, 0, 0, 271, 272, 5, 47, 0, 0, 272, 276, 1, 0, 0, 0, 273, 275, 8, 0, 0, 0, 274, 273, 1, 0, 0, 0, 275, 278, 1, 0, 0, 0, 276, 274, 1, 0, 0, 0, 276, 277, 1, 0, 0, 0, 277, 279, 1, 0, 0, 0, 278, 276, 1, 0, 0, 0, 279, 280, 6, 44, 0, 0, 280, 90, 1, 0, 0, 0, 281, 282, 5, 47, 0, 0, 282, 283, 5, 42, 0, 0, 283, 287, 1, 0, 0, 0, 284, 286, 9, 0, 0, 0, 285, 284, 1, 0, 0, 0, 286, 289, 1, 0, 0, 0, 287, 288, 1, 0, 0, 0, 287, 285, 1, 0, 0, 0, 288, 290, 1, 0, 0, 0, 289, 287, 1, 0, 0, 0, 290, 291, 5, 42, 0, 0, 291, 292, 5, 47, 0, 0, 292, 293, 1, 0, 0, 0, 293, 294, 6, 45, 0, 0, 294, 92, 1, 0, 0, 0, 295, 299, 7, 1, 0, 0, 296, 298, 7, 2, 0, 0, 297, 296, 1, 0, 0, 0, 298, 301, 1, 0, 0, 0, 299, 297, 1, 0, 0, 0, 299, 300, 1, 0, 0, 0, 300, 94, 1, 0, 0, 0, 301, 299, 1, 0, 0, 0, 302, 304, 5, 45, 0, 0, 303, 302, 1, 0, 0, 0, 303, 304, 1, 0, 0, 0, 304, 306, 1, 0, 0, 0, 305, 307, 7, 3, 0, 0, 306, 305, 1, 0, 0, 0, 307, 308, 1, 0, 0, 0, 308, 306, 1, 0, 0, 0, 308, 309, 1, 0, 0, 0, 309, 96, 1, 0, 0, 0, 310, 311, 5, 116, 0, 0, 311, 312, 5, 114, 0, 0, 312, 313, 5, 117, 0, 0, 313, 320, 5, 101, 0, 0, 314, 315, 5, 102, 0, 0, 315, 316, 5, 97, 0, 0, 316, 317, 5, 108, 0, 0, 317, 318, 5, 115, 0, 0, 318, 320, 5, 101, 0, 0, 319, 310, 1, 0, 0, 0, 319, 314, 1, 0, 0, 0, 320, 98, 1, 0, 0, 0, 321, 323, 7, 3, 0, 0, 322, 321, 1, 0, 0, 0, 323, 324, 1, 0, 0, 0, 324, 322, 1, 0, 0, 0, 324, 325, 1, 0, 0, 0, 325, 326, 1, 0, 0, 0, 326, 328, 5, 46, 0, 0, 327, 329, 7, 3, 0, 0, 328, 327, 1, 0, 0, 0, 329, 330, 1, 0, 0, 0, 330, 328, 1, 0, 0, 0, 330, 331, 1, 0, 0, 0, 331, 341, 1, 0, 0, 0, 332, 334, 7, 4, 0, 0, 333, 335, 7, 5, 0, 0, 334, 333, 1, 0, 0, 0, 334, 335, 1, 0, 0, 0, 335, 337, 1, 0, 0, 0, 336, 338, 7, 3, 0, 0, 337, 336, 1, 0, 0, 0, 338, 339, 1, 0, 0, 0, 339, 337, 1, 0, 0, 0, 339, 340, 1, 0, 0, 0, 340, 342, 1, 0, 0, 0, 341, 332, 1, 0, 0, 0, 341, 342, 1, 0, 0, 0, 342, 375, 1, 0, 0, 0, 343, 345, 5, 46, 0, 0, 344, 346, 7, 3, 0, 0, 345, 344, 1, 0, 0, 0, 346, 347, 1, 0, 0, 0, 347, 345, 1, 0, 0, 0, 347, 348, 1, 0, 0, 0, 348, 358, 1, 0, 0, 0, 349, 351, 7, 4, 0, 0, 350, 352, 7, 5, 0, 0, 351, 350, 1, 0, 0, 0, 351, 352, 1, 0, 0, 0, 352, 354, 1, 0, 0, 0, 353, 355, 7, 3, 0, 0, 354, 353, 1, 0, 0, 0, 355, 356, 1, 0, 0, 0, 356, 354, 1, 0, 0, 0, 356, 357, 1, 0, 0, 0, 357, 359, 1, 0, 0, 0, 358, 349, 1, 0, 0, 0, 358, 359, 1, 0, 0, 0, 359, 375, 1, 0, 0, 0, 360, 362, 7, 3, 0, 0, 361, 360, 1, 0, 0, 0, 362, 363, 1, 0, 0, 0, 363, 361, 1, 0, 0, 0, 363, 364, 1, 0, 0, 0, 364, 365, 1, 0, 0, 0, 365, 367, 7, 4, 0, 0, 366, 368, 7, 5, 0, 0, 367, 366, 1, 0, 0, 0, 367, 368, 1, 0, 0, 0, 368, 370, 1, 0, 0, 0, 369, 371, 7, 3, 0, 0, 370, 369, 1, 0, 0, 0, 371, 372, 1, 0, 0, 0, 372, 370, 1, 0, 0, 0, 372, 373, 1, 0, 0, 0, 373, 375, 1, 0, 0, 0, 374, 322, 1, 0, 0, 0, 374, 343, 1, 0, 0, 0, 374, 361, 1, 0, 0, 0, 375, 100, 1, 0, 0, 0, 376, 377, 5, 34, 0, 0, 377, 378, 7, 6, 0, 0, 378, 379, 5, 34, 0, 0, 379, 102, 1, 0, 0, 0, 380, 381, 5, 42, 0, 0, 381, 104, 1, 0, 0, 0, 382, 387, 5, 34, 0, 0, 383, 386, 3, 107, 53, 0, 384, 386, 8, 7, 0, 0, 385, 383, 1, 0, 0, 0, 385, 384, 1, 0, 0, 0, 386, 389, 1, 0, 0, 0, 387, 385, 1, 0, 0, 0, 387, 388, 1, 0, 0, 0, 388, 390, 1, 0, 0, 0, 389, 387, 1, 0, 0, 0, 390, 391, 5, 34, 0, 0, 391, 106, 1, 0, 0, 0, 392, 393, 5, 92, 0, 0, 393, 394, 7, 8, 0, 0, 394, 108, 1, 0, 0, 0, 395, 397, 7, 9, 0, 0, 396, 395, 1, 0, 0, 0, 397, 398, 1, 0, 0, 0, 398, 396, 1, 0, 0, 0, 398, 399, 1, 0, 0, 0, 399, 400, 1, 0, 0, 0, 400, 401, 6, 54, 0, 0, 401, 110, 1, 0, 0, 0, 23, 0, 276, 287, 299, 303, 308, 319, 324, 330, 334, 339, 341, 347, 351, 356, 358, 363, 367, 372, 374, 385, 387, 398, 1, 6, 0, 0] \ No newline at end of file diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASSLexer.py b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASSLexer.py new file mode 100644 index 00000000000..bd93f866972 --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASSLexer.py @@ -0,0 +1,259 @@ +# Generated from CASS.g4 by ANTLR 4.13.2 +from antlr4 import * +from io import StringIO +import sys +if sys.version_info[1] > 5: + from typing import TextIO +else: + from typing.io import TextIO + + +def serializedATN(): + return [ + 4,0,54,402,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5, + 2,6,7,6,2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,2, + 13,7,13,2,14,7,14,2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19,7, + 19,2,20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25,2, + 26,7,26,2,27,7,27,2,28,7,28,2,29,7,29,2,30,7,30,2,31,7,31,2,32,7, + 32,2,33,7,33,2,34,7,34,2,35,7,35,2,36,7,36,2,37,7,37,2,38,7,38,2, + 39,7,39,2,40,7,40,2,41,7,41,2,42,7,42,2,43,7,43,2,44,7,44,2,45,7, + 45,2,46,7,46,2,47,7,47,2,48,7,48,2,49,7,49,2,50,7,50,2,51,7,51,2, + 52,7,52,2,53,7,53,2,54,7,54,1,0,1,0,1,1,1,1,1,2,1,2,1,3,1,3,1,4, + 1,4,1,5,1,5,1,6,1,6,1,6,1,6,1,7,1,7,1,7,1,7,1,7,1,7,1,8,1,8,1,8, + 1,9,1,9,1,9,1,9,1,9,1,10,1,10,1,10,1,10,1,10,1,10,1,10,1,11,1,11, + 1,11,1,11,1,11,1,12,1,12,1,13,1,13,1,14,1,14,1,15,1,15,1,16,1,16, + 1,16,1,16,1,16,1,16,1,16,1,16,1,17,1,17,1,17,1,17,1,17,1,17,1,17, + 1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,19,1,19,1,19,1,19,1,20, + 1,20,1,20,1,20,1,20,1,20,1,21,1,21,1,21,1,21,1,21,1,21,1,21,1,22, + 1,22,1,22,1,22,1,22,1,23,1,23,1,23,1,23,1,23,1,23,1,23,1,23,1,24, + 1,24,1,24,1,24,1,24,1,24,1,25,1,25,1,25,1,26,1,26,1,26,1,27,1,27, + 1,28,1,28,1,29,1,29,1,29,1,30,1,30,1,30,1,31,1,31,1,32,1,32,1,32, + 1,33,1,33,1,33,1,34,1,34,1,34,1,35,1,35,1,35,1,36,1,36,1,36,1,37, + 1,37,1,37,1,38,1,38,1,38,1,39,1,39,1,39,1,40,1,40,1,41,1,41,1,42, + 1,42,1,43,1,43,1,44,1,44,1,44,1,44,5,44,275,8,44,10,44,12,44,278, + 9,44,1,44,1,44,1,45,1,45,1,45,1,45,5,45,286,8,45,10,45,12,45,289, + 9,45,1,45,1,45,1,45,1,45,1,45,1,46,1,46,5,46,298,8,46,10,46,12,46, + 301,9,46,1,47,3,47,304,8,47,1,47,4,47,307,8,47,11,47,12,47,308,1, + 48,1,48,1,48,1,48,1,48,1,48,1,48,1,48,1,48,3,48,320,8,48,1,49,4, + 49,323,8,49,11,49,12,49,324,1,49,1,49,4,49,329,8,49,11,49,12,49, + 330,1,49,1,49,3,49,335,8,49,1,49,4,49,338,8,49,11,49,12,49,339,3, + 49,342,8,49,1,49,1,49,4,49,346,8,49,11,49,12,49,347,1,49,1,49,3, + 49,352,8,49,1,49,4,49,355,8,49,11,49,12,49,356,3,49,359,8,49,1,49, + 4,49,362,8,49,11,49,12,49,363,1,49,1,49,3,49,368,8,49,1,49,4,49, + 371,8,49,11,49,12,49,372,3,49,375,8,49,1,50,1,50,1,50,1,50,1,51, + 1,51,1,52,1,52,1,52,5,52,386,8,52,10,52,12,52,389,9,52,1,52,1,52, + 1,53,1,53,1,53,1,54,4,54,397,8,54,11,54,12,54,398,1,54,1,54,1,287, + 0,55,1,1,3,2,5,3,7,4,9,5,11,6,13,7,15,8,17,9,19,10,21,11,23,12,25, + 13,27,14,29,15,31,16,33,17,35,18,37,19,39,20,41,21,43,22,45,23,47, + 24,49,25,51,26,53,27,55,28,57,29,59,30,61,31,63,32,65,33,67,34,69, + 35,71,36,73,37,75,38,77,39,79,40,81,41,83,42,85,43,87,44,89,45,91, + 46,93,47,95,48,97,49,99,50,101,51,103,52,105,53,107,0,109,54,1,0, + 10,2,0,10,10,13,13,3,0,65,90,95,95,97,122,4,0,48,57,65,90,95,95, + 97,122,1,0,48,57,2,0,69,69,101,101,2,0,43,43,45,45,2,0,65,90,97, + 122,2,0,34,34,92,92,8,0,34,34,39,39,92,92,98,98,102,102,110,110, + 114,114,116,116,3,0,9,10,13,13,32,32,423,0,1,1,0,0,0,0,3,1,0,0,0, + 0,5,1,0,0,0,0,7,1,0,0,0,0,9,1,0,0,0,0,11,1,0,0,0,0,13,1,0,0,0,0, + 15,1,0,0,0,0,17,1,0,0,0,0,19,1,0,0,0,0,21,1,0,0,0,0,23,1,0,0,0,0, + 25,1,0,0,0,0,27,1,0,0,0,0,29,1,0,0,0,0,31,1,0,0,0,0,33,1,0,0,0,0, + 35,1,0,0,0,0,37,1,0,0,0,0,39,1,0,0,0,0,41,1,0,0,0,0,43,1,0,0,0,0, + 45,1,0,0,0,0,47,1,0,0,0,0,49,1,0,0,0,0,51,1,0,0,0,0,53,1,0,0,0,0, + 55,1,0,0,0,0,57,1,0,0,0,0,59,1,0,0,0,0,61,1,0,0,0,0,63,1,0,0,0,0, + 65,1,0,0,0,0,67,1,0,0,0,0,69,1,0,0,0,0,71,1,0,0,0,0,73,1,0,0,0,0, + 75,1,0,0,0,0,77,1,0,0,0,0,79,1,0,0,0,0,81,1,0,0,0,0,83,1,0,0,0,0, + 85,1,0,0,0,0,87,1,0,0,0,0,89,1,0,0,0,0,91,1,0,0,0,0,93,1,0,0,0,0, + 95,1,0,0,0,0,97,1,0,0,0,0,99,1,0,0,0,0,101,1,0,0,0,0,103,1,0,0,0, + 0,105,1,0,0,0,0,109,1,0,0,0,1,111,1,0,0,0,3,113,1,0,0,0,5,115,1, + 0,0,0,7,117,1,0,0,0,9,119,1,0,0,0,11,121,1,0,0,0,13,123,1,0,0,0, + 15,127,1,0,0,0,17,133,1,0,0,0,19,136,1,0,0,0,21,141,1,0,0,0,23,148, + 1,0,0,0,25,153,1,0,0,0,27,155,1,0,0,0,29,157,1,0,0,0,31,159,1,0, + 0,0,33,161,1,0,0,0,35,169,1,0,0,0,37,176,1,0,0,0,39,184,1,0,0,0, + 41,188,1,0,0,0,43,194,1,0,0,0,45,201,1,0,0,0,47,206,1,0,0,0,49,214, + 1,0,0,0,51,220,1,0,0,0,53,223,1,0,0,0,55,226,1,0,0,0,57,228,1,0, + 0,0,59,230,1,0,0,0,61,233,1,0,0,0,63,236,1,0,0,0,65,238,1,0,0,0, + 67,241,1,0,0,0,69,244,1,0,0,0,71,247,1,0,0,0,73,250,1,0,0,0,75,253, + 1,0,0,0,77,256,1,0,0,0,79,259,1,0,0,0,81,262,1,0,0,0,83,264,1,0, + 0,0,85,266,1,0,0,0,87,268,1,0,0,0,89,270,1,0,0,0,91,281,1,0,0,0, + 93,295,1,0,0,0,95,303,1,0,0,0,97,319,1,0,0,0,99,374,1,0,0,0,101, + 376,1,0,0,0,103,380,1,0,0,0,105,382,1,0,0,0,107,392,1,0,0,0,109, + 396,1,0,0,0,111,112,5,40,0,0,112,2,1,0,0,0,113,114,5,41,0,0,114, + 4,1,0,0,0,115,116,5,123,0,0,116,6,1,0,0,0,117,118,5,125,0,0,118, + 8,1,0,0,0,119,120,5,61,0,0,120,10,1,0,0,0,121,122,5,59,0,0,122,12, + 1,0,0,0,123,124,5,102,0,0,124,125,5,111,0,0,125,126,5,114,0,0,126, + 14,1,0,0,0,127,128,5,119,0,0,128,129,5,104,0,0,129,130,5,105,0,0, + 130,131,5,108,0,0,131,132,5,101,0,0,132,16,1,0,0,0,133,134,5,105, + 0,0,134,135,5,102,0,0,135,18,1,0,0,0,136,137,5,101,0,0,137,138,5, + 108,0,0,138,139,5,115,0,0,139,140,5,101,0,0,140,20,1,0,0,0,141,142, + 5,115,0,0,142,143,5,119,0,0,143,144,5,105,0,0,144,145,5,116,0,0, + 145,146,5,99,0,0,146,147,5,104,0,0,147,22,1,0,0,0,148,149,5,99,0, + 0,149,150,5,97,0,0,150,151,5,115,0,0,151,152,5,101,0,0,152,24,1, + 0,0,0,153,154,5,58,0,0,154,26,1,0,0,0,155,156,5,91,0,0,156,28,1, + 0,0,0,157,158,5,93,0,0,158,30,1,0,0,0,159,160,5,44,0,0,160,32,1, + 0,0,0,161,162,5,110,0,0,162,163,5,117,0,0,163,164,5,108,0,0,164, + 165,5,108,0,0,165,166,5,112,0,0,166,167,5,116,0,0,167,168,5,114, + 0,0,168,34,1,0,0,0,169,170,5,114,0,0,170,171,5,101,0,0,171,172,5, + 116,0,0,172,173,5,117,0,0,173,174,5,114,0,0,174,175,5,110,0,0,175, + 36,1,0,0,0,176,177,5,105,0,0,177,178,5,110,0,0,178,179,5,99,0,0, + 179,180,5,108,0,0,180,181,5,117,0,0,181,182,5,100,0,0,182,183,5, + 101,0,0,183,38,1,0,0,0,184,185,5,105,0,0,185,186,5,110,0,0,186,187, + 5,116,0,0,187,40,1,0,0,0,188,189,5,102,0,0,189,190,5,108,0,0,190, + 191,5,111,0,0,191,192,5,97,0,0,192,193,5,116,0,0,193,42,1,0,0,0, + 194,195,5,100,0,0,195,196,5,111,0,0,196,197,5,117,0,0,197,198,5, + 98,0,0,198,199,5,108,0,0,199,200,5,101,0,0,200,44,1,0,0,0,201,202, + 5,118,0,0,202,203,5,111,0,0,203,204,5,105,0,0,204,205,5,100,0,0, + 205,46,1,0,0,0,206,207,5,100,0,0,207,208,5,101,0,0,208,209,5,102, + 0,0,209,210,5,97,0,0,210,211,5,117,0,0,211,212,5,108,0,0,212,213, + 5,116,0,0,213,48,1,0,0,0,214,215,5,98,0,0,215,216,5,114,0,0,216, + 217,5,101,0,0,217,218,5,97,0,0,218,219,5,107,0,0,219,50,1,0,0,0, + 220,221,5,43,0,0,221,222,5,43,0,0,222,52,1,0,0,0,223,224,5,45,0, + 0,224,225,5,45,0,0,225,54,1,0,0,0,226,227,5,62,0,0,227,56,1,0,0, + 0,228,229,5,60,0,0,229,58,1,0,0,0,230,231,5,60,0,0,231,232,5,61, + 0,0,232,60,1,0,0,0,233,234,5,62,0,0,234,235,5,61,0,0,235,62,1,0, + 0,0,236,237,5,38,0,0,237,64,1,0,0,0,238,239,5,43,0,0,239,240,5,61, + 0,0,240,66,1,0,0,0,241,242,5,45,0,0,242,243,5,61,0,0,243,68,1,0, + 0,0,244,245,5,42,0,0,245,246,5,61,0,0,246,70,1,0,0,0,247,248,5,47, + 0,0,248,249,5,61,0,0,249,72,1,0,0,0,250,251,5,124,0,0,251,252,5, + 124,0,0,252,74,1,0,0,0,253,254,5,38,0,0,254,255,5,38,0,0,255,76, + 1,0,0,0,256,257,5,61,0,0,257,258,5,61,0,0,258,78,1,0,0,0,259,260, + 5,33,0,0,260,261,5,61,0,0,261,80,1,0,0,0,262,263,5,43,0,0,263,82, + 1,0,0,0,264,265,5,45,0,0,265,84,1,0,0,0,266,267,5,47,0,0,267,86, + 1,0,0,0,268,269,5,37,0,0,269,88,1,0,0,0,270,271,5,47,0,0,271,272, + 5,47,0,0,272,276,1,0,0,0,273,275,8,0,0,0,274,273,1,0,0,0,275,278, + 1,0,0,0,276,274,1,0,0,0,276,277,1,0,0,0,277,279,1,0,0,0,278,276, + 1,0,0,0,279,280,6,44,0,0,280,90,1,0,0,0,281,282,5,47,0,0,282,283, + 5,42,0,0,283,287,1,0,0,0,284,286,9,0,0,0,285,284,1,0,0,0,286,289, + 1,0,0,0,287,288,1,0,0,0,287,285,1,0,0,0,288,290,1,0,0,0,289,287, + 1,0,0,0,290,291,5,42,0,0,291,292,5,47,0,0,292,293,1,0,0,0,293,294, + 6,45,0,0,294,92,1,0,0,0,295,299,7,1,0,0,296,298,7,2,0,0,297,296, + 1,0,0,0,298,301,1,0,0,0,299,297,1,0,0,0,299,300,1,0,0,0,300,94,1, + 0,0,0,301,299,1,0,0,0,302,304,5,45,0,0,303,302,1,0,0,0,303,304,1, + 0,0,0,304,306,1,0,0,0,305,307,7,3,0,0,306,305,1,0,0,0,307,308,1, + 0,0,0,308,306,1,0,0,0,308,309,1,0,0,0,309,96,1,0,0,0,310,311,5,116, + 0,0,311,312,5,114,0,0,312,313,5,117,0,0,313,320,5,101,0,0,314,315, + 5,102,0,0,315,316,5,97,0,0,316,317,5,108,0,0,317,318,5,115,0,0,318, + 320,5,101,0,0,319,310,1,0,0,0,319,314,1,0,0,0,320,98,1,0,0,0,321, + 323,7,3,0,0,322,321,1,0,0,0,323,324,1,0,0,0,324,322,1,0,0,0,324, + 325,1,0,0,0,325,326,1,0,0,0,326,328,5,46,0,0,327,329,7,3,0,0,328, + 327,1,0,0,0,329,330,1,0,0,0,330,328,1,0,0,0,330,331,1,0,0,0,331, + 341,1,0,0,0,332,334,7,4,0,0,333,335,7,5,0,0,334,333,1,0,0,0,334, + 335,1,0,0,0,335,337,1,0,0,0,336,338,7,3,0,0,337,336,1,0,0,0,338, + 339,1,0,0,0,339,337,1,0,0,0,339,340,1,0,0,0,340,342,1,0,0,0,341, + 332,1,0,0,0,341,342,1,0,0,0,342,375,1,0,0,0,343,345,5,46,0,0,344, + 346,7,3,0,0,345,344,1,0,0,0,346,347,1,0,0,0,347,345,1,0,0,0,347, + 348,1,0,0,0,348,358,1,0,0,0,349,351,7,4,0,0,350,352,7,5,0,0,351, + 350,1,0,0,0,351,352,1,0,0,0,352,354,1,0,0,0,353,355,7,3,0,0,354, + 353,1,0,0,0,355,356,1,0,0,0,356,354,1,0,0,0,356,357,1,0,0,0,357, + 359,1,0,0,0,358,349,1,0,0,0,358,359,1,0,0,0,359,375,1,0,0,0,360, + 362,7,3,0,0,361,360,1,0,0,0,362,363,1,0,0,0,363,361,1,0,0,0,363, + 364,1,0,0,0,364,365,1,0,0,0,365,367,7,4,0,0,366,368,7,5,0,0,367, + 366,1,0,0,0,367,368,1,0,0,0,368,370,1,0,0,0,369,371,7,3,0,0,370, + 369,1,0,0,0,371,372,1,0,0,0,372,370,1,0,0,0,372,373,1,0,0,0,373, + 375,1,0,0,0,374,322,1,0,0,0,374,343,1,0,0,0,374,361,1,0,0,0,375, + 100,1,0,0,0,376,377,5,34,0,0,377,378,7,6,0,0,378,379,5,34,0,0,379, + 102,1,0,0,0,380,381,5,42,0,0,381,104,1,0,0,0,382,387,5,34,0,0,383, + 386,3,107,53,0,384,386,8,7,0,0,385,383,1,0,0,0,385,384,1,0,0,0,386, + 389,1,0,0,0,387,385,1,0,0,0,387,388,1,0,0,0,388,390,1,0,0,0,389, + 387,1,0,0,0,390,391,5,34,0,0,391,106,1,0,0,0,392,393,5,92,0,0,393, + 394,7,8,0,0,394,108,1,0,0,0,395,397,7,9,0,0,396,395,1,0,0,0,397, + 398,1,0,0,0,398,396,1,0,0,0,398,399,1,0,0,0,399,400,1,0,0,0,400, + 401,6,54,0,0,401,110,1,0,0,0,23,0,276,287,299,303,308,319,324,330, + 334,339,341,347,351,356,358,363,367,372,374,385,387,398,1,6,0,0 + ] + +class CASSLexer(Lexer): + + atn = ATNDeserializer().deserialize(serializedATN()) + + decisionsToDFA = [ DFA(ds, i) for i, ds in enumerate(atn.decisionToState) ] + + T__0 = 1 + T__1 = 2 + T__2 = 3 + T__3 = 4 + T__4 = 5 + T__5 = 6 + T__6 = 7 + T__7 = 8 + T__8 = 9 + T__9 = 10 + T__10 = 11 + T__11 = 12 + T__12 = 13 + T__13 = 14 + T__14 = 15 + T__15 = 16 + T__16 = 17 + T__17 = 18 + T__18 = 19 + T__19 = 20 + T__20 = 21 + T__21 = 22 + T__22 = 23 + T__23 = 24 + T__24 = 25 + T__25 = 26 + T__26 = 27 + T__27 = 28 + T__28 = 29 + T__29 = 30 + T__30 = 31 + T__31 = 32 + T__32 = 33 + T__33 = 34 + T__34 = 35 + T__35 = 36 + T__36 = 37 + T__37 = 38 + T__38 = 39 + T__39 = 40 + T__40 = 41 + T__41 = 42 + T__42 = 43 + T__43 = 44 + SL_COMMENT = 45 + ML_COMMENT = 46 + ID = 47 + INT = 48 + BOOL = 49 + FLOAT = 50 + CHAR = 51 + POINTER = 52 + STRING = 53 + WS = 54 + + channelNames = [ u"DEFAULT_TOKEN_CHANNEL", u"HIDDEN" ] + + modeNames = [ "DEFAULT_MODE" ] + + literalNames = [ "", + "'('", "')'", "'{'", "'}'", "'='", "';'", "'for'", "'while'", + "'if'", "'else'", "'switch'", "'case'", "':'", "'['", "']'", + "','", "'nullptr'", "'return'", "'include'", "'int'", "'float'", + "'double'", "'void'", "'default'", "'break'", "'++'", "'--'", + "'>'", "'<'", "'<='", "'>='", "'&'", "'+='", "'-='", "'*='", + "'/='", "'||'", "'&&'", "'=='", "'!='", "'+'", "'-'", "'/'", + "'%'", "'*'" ] + + symbolicNames = [ "", + "SL_COMMENT", "ML_COMMENT", "ID", "INT", "BOOL", "FLOAT", "CHAR", + "POINTER", "STRING", "WS" ] + + ruleNames = [ "T__0", "T__1", "T__2", "T__3", "T__4", "T__5", "T__6", + "T__7", "T__8", "T__9", "T__10", "T__11", "T__12", "T__13", + "T__14", "T__15", "T__16", "T__17", "T__18", "T__19", + "T__20", "T__21", "T__22", "T__23", "T__24", "T__25", + "T__26", "T__27", "T__28", "T__29", "T__30", "T__31", + "T__32", "T__33", "T__34", "T__35", "T__36", "T__37", + "T__38", "T__39", "T__40", "T__41", "T__42", "T__43", + "SL_COMMENT", "ML_COMMENT", "ID", "INT", "BOOL", "FLOAT", + "CHAR", "POINTER", "STRING", "ESC_SEQ", "WS" ] + + grammarFileName = "CASS.g4" + + def __init__(self, input=None, output:TextIO = sys.stdout): + super().__init__(input, output) + self.checkVersion("4.13.2") + self._interp = LexerATNSimulator(self, self.atn, self.decisionsToDFA, PredictionContextCache()) + self._actions = None + self._predicates = None + + diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASSLexer.tokens b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASSLexer.tokens new file mode 100644 index 00000000000..ffe14a736f0 --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASSLexer.tokens @@ -0,0 +1,99 @@ +T__0=1 +T__1=2 +T__2=3 +T__3=4 +T__4=5 +T__5=6 +T__6=7 +T__7=8 +T__8=9 +T__9=10 +T__10=11 +T__11=12 +T__12=13 +T__13=14 +T__14=15 +T__15=16 +T__16=17 +T__17=18 +T__18=19 +T__19=20 +T__20=21 +T__21=22 +T__22=23 +T__23=24 +T__24=25 +T__25=26 +T__26=27 +T__27=28 +T__28=29 +T__29=30 +T__30=31 +T__31=32 +T__32=33 +T__33=34 +T__34=35 +T__35=36 +T__36=37 +T__37=38 +T__38=39 +T__39=40 +T__40=41 +T__41=42 +T__42=43 +T__43=44 +SL_COMMENT=45 +ML_COMMENT=46 +ID=47 +INT=48 +BOOL=49 +FLOAT=50 +CHAR=51 +POINTER=52 +STRING=53 +WS=54 +'('=1 +')'=2 +'{'=3 +'}'=4 +'='=5 +';'=6 +'for'=7 +'while'=8 +'if'=9 +'else'=10 +'switch'=11 +'case'=12 +':'=13 +'['=14 +']'=15 +','=16 +'nullptr'=17 +'return'=18 +'include'=19 +'int'=20 +'float'=21 +'double'=22 +'void'=23 +'default'=24 +'break'=25 +'++'=26 +'--'=27 +'>'=28 +'<'=29 +'<='=30 +'>='=31 +'&'=32 +'+='=33 +'-='=34 +'*='=35 +'/='=36 +'||'=37 +'&&'=38 +'=='=39 +'!='=40 +'+'=41 +'-'=42 +'/'=43 +'%'=44 +'*'=52 diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASSListener.py b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASSListener.py new file mode 100644 index 00000000000..2ecfa54026d --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASSListener.py @@ -0,0 +1,399 @@ +# Generated from CASS.g4 by ANTLR 4.13.2 +from antlr4 import * +if "." in __name__: + from .CASSParser import CASSParser +else: + from CASSParser import CASSParser + +# This class defines a complete listener for a parse tree produced by CASSParser. +class CASSListener(ParseTreeListener): + + # Enter a parse tree produced by CASSParser#prog. + def enterProg(self, ctx:CASSParser.ProgContext): + pass + + # Exit a parse tree produced by CASSParser#prog. + def exitProg(self, ctx:CASSParser.ProgContext): + pass + + + # Enter a parse tree produced by CASSParser#functionDefinition. + def enterFunctionDefinition(self, ctx:CASSParser.FunctionDefinitionContext): + pass + + # Exit a parse tree produced by CASSParser#functionDefinition. + def exitFunctionDefinition(self, ctx:CASSParser.FunctionDefinitionContext): + pass + + + # Enter a parse tree produced by CASSParser#compoundStatement. + def enterCompoundStatement(self, ctx:CASSParser.CompoundStatementContext): + pass + + # Exit a parse tree produced by CASSParser#compoundStatement. + def exitCompoundStatement(self, ctx:CASSParser.CompoundStatementContext): + pass + + + # Enter a parse tree produced by CASSParser#statement. + def enterStatement(self, ctx:CASSParser.StatementContext): + pass + + # Exit a parse tree produced by CASSParser#statement. + def exitStatement(self, ctx:CASSParser.StatementContext): + pass + + + # Enter a parse tree produced by CASSParser#declarationStatement. + def enterDeclarationStatement(self, ctx:CASSParser.DeclarationStatementContext): + pass + + # Exit a parse tree produced by CASSParser#declarationStatement. + def exitDeclarationStatement(self, ctx:CASSParser.DeclarationStatementContext): + pass + + + # Enter a parse tree produced by CASSParser#forBlockStatement. + def enterForBlockStatement(self, ctx:CASSParser.ForBlockStatementContext): + pass + + # Exit a parse tree produced by CASSParser#forBlockStatement. + def exitForBlockStatement(self, ctx:CASSParser.ForBlockStatementContext): + pass + + + # Enter a parse tree produced by CASSParser#forSingleStatement. + def enterForSingleStatement(self, ctx:CASSParser.ForSingleStatementContext): + pass + + # Exit a parse tree produced by CASSParser#forSingleStatement. + def exitForSingleStatement(self, ctx:CASSParser.ForSingleStatementContext): + pass + + + # Enter a parse tree produced by CASSParser#conditionClause. + def enterConditionClause(self, ctx:CASSParser.ConditionClauseContext): + pass + + # Exit a parse tree produced by CASSParser#conditionClause. + def exitConditionClause(self, ctx:CASSParser.ConditionClauseContext): + pass + + + # Enter a parse tree produced by CASSParser#whileBlockStatement. + def enterWhileBlockStatement(self, ctx:CASSParser.WhileBlockStatementContext): + pass + + # Exit a parse tree produced by CASSParser#whileBlockStatement. + def exitWhileBlockStatement(self, ctx:CASSParser.WhileBlockStatementContext): + pass + + + # Enter a parse tree produced by CASSParser#whileSingleStatement. + def enterWhileSingleStatement(self, ctx:CASSParser.WhileSingleStatementContext): + pass + + # Exit a parse tree produced by CASSParser#whileSingleStatement. + def exitWhileSingleStatement(self, ctx:CASSParser.WhileSingleStatementContext): + pass + + + # Enter a parse tree produced by CASSParser#ifBlockStatement. + def enterIfBlockStatement(self, ctx:CASSParser.IfBlockStatementContext): + pass + + # Exit a parse tree produced by CASSParser#ifBlockStatement. + def exitIfBlockStatement(self, ctx:CASSParser.IfBlockStatementContext): + pass + + + # Enter a parse tree produced by CASSParser#ifSingleStatement. + def enterIfSingleStatement(self, ctx:CASSParser.IfSingleStatementContext): + pass + + # Exit a parse tree produced by CASSParser#ifSingleStatement. + def exitIfSingleStatement(self, ctx:CASSParser.IfSingleStatementContext): + pass + + + # Enter a parse tree produced by CASSParser#elseClause. + def enterElseClause(self, ctx:CASSParser.ElseClauseContext): + pass + + # Exit a parse tree produced by CASSParser#elseClause. + def exitElseClause(self, ctx:CASSParser.ElseClauseContext): + pass + + + # Enter a parse tree produced by CASSParser#switchStatement. + def enterSwitchStatement(self, ctx:CASSParser.SwitchStatementContext): + pass + + # Exit a parse tree produced by CASSParser#switchStatement. + def exitSwitchStatement(self, ctx:CASSParser.SwitchStatementContext): + pass + + + # Enter a parse tree produced by CASSParser#caseStatement. + def enterCaseStatement(self, ctx:CASSParser.CaseStatementContext): + pass + + # Exit a parse tree produced by CASSParser#caseStatement. + def exitCaseStatement(self, ctx:CASSParser.CaseStatementContext): + pass + + + # Enter a parse tree produced by CASSParser#functionCall. + def enterFunctionCall(self, ctx:CASSParser.FunctionCallContext): + pass + + # Exit a parse tree produced by CASSParser#functionCall. + def exitFunctionCall(self, ctx:CASSParser.FunctionCallContext): + pass + + + # Enter a parse tree produced by CASSParser#arrayDeclarator. + def enterArrayDeclarator(self, ctx:CASSParser.ArrayDeclaratorContext): + pass + + # Exit a parse tree produced by CASSParser#arrayDeclarator. + def exitArrayDeclarator(self, ctx:CASSParser.ArrayDeclaratorContext): + pass + + + # Enter a parse tree produced by CASSParser#listInitializer. + def enterListInitializer(self, ctx:CASSParser.ListInitializerContext): + pass + + # Exit a parse tree produced by CASSParser#listInitializer. + def exitListInitializer(self, ctx:CASSParser.ListInitializerContext): + pass + + + # Enter a parse tree produced by CASSParser#emptyInitializer. + def enterEmptyInitializer(self, ctx:CASSParser.EmptyInitializerContext): + pass + + # Exit a parse tree produced by CASSParser#emptyInitializer. + def exitEmptyInitializer(self, ctx:CASSParser.EmptyInitializerContext): + pass + + + # Enter a parse tree produced by CASSParser#nullptr. + def enterNullptr(self, ctx:CASSParser.NullptrContext): + pass + + # Exit a parse tree produced by CASSParser#nullptr. + def exitNullptr(self, ctx:CASSParser.NullptrContext): + pass + + + # Enter a parse tree produced by CASSParser#argumentList. + def enterArgumentList(self, ctx:CASSParser.ArgumentListContext): + pass + + # Exit a parse tree produced by CASSParser#argumentList. + def exitArgumentList(self, ctx:CASSParser.ArgumentListContext): + pass + + + # Enter a parse tree produced by CASSParser#returnStatement. + def enterReturnStatement(self, ctx:CASSParser.ReturnStatementContext): + pass + + # Exit a parse tree produced by CASSParser#returnStatement. + def exitReturnStatement(self, ctx:CASSParser.ReturnStatementContext): + pass + + + # Enter a parse tree produced by CASSParser#expressionStatement. + def enterExpressionStatement(self, ctx:CASSParser.ExpressionStatementContext): + pass + + # Exit a parse tree produced by CASSParser#expressionStatement. + def exitExpressionStatement(self, ctx:CASSParser.ExpressionStatementContext): + pass + + + # Enter a parse tree produced by CASSParser#includeStatement. + def enterIncludeStatement(self, ctx:CASSParser.IncludeStatementContext): + pass + + # Exit a parse tree produced by CASSParser#includeStatement. + def exitIncludeStatement(self, ctx:CASSParser.IncludeStatementContext): + pass + + + # Enter a parse tree produced by CASSParser#parameterList. + def enterParameterList(self, ctx:CASSParser.ParameterListContext): + pass + + # Exit a parse tree produced by CASSParser#parameterList. + def exitParameterList(self, ctx:CASSParser.ParameterListContext): + pass + + + # Enter a parse tree produced by CASSParser#parameter. + def enterParameter(self, ctx:CASSParser.ParameterContext): + pass + + # Exit a parse tree produced by CASSParser#parameter. + def exitParameter(self, ctx:CASSParser.ParameterContext): + pass + + + # Enter a parse tree produced by CASSParser#typeSpec. + def enterTypeSpec(self, ctx:CASSParser.TypeSpecContext): + pass + + # Exit a parse tree produced by CASSParser#typeSpec. + def exitTypeSpec(self, ctx:CASSParser.TypeSpecContext): + pass + + + # Enter a parse tree produced by CASSParser#expression. + def enterExpression(self, ctx:CASSParser.ExpressionContext): + pass + + # Exit a parse tree produced by CASSParser#expression. + def exitExpression(self, ctx:CASSParser.ExpressionContext): + pass + + + # Enter a parse tree produced by CASSParser#defaultExpression. + def enterDefaultExpression(self, ctx:CASSParser.DefaultExpressionContext): + pass + + # Exit a parse tree produced by CASSParser#defaultExpression. + def exitDefaultExpression(self, ctx:CASSParser.DefaultExpressionContext): + pass + + + # Enter a parse tree produced by CASSParser#breakExpression. + def enterBreakExpression(self, ctx:CASSParser.BreakExpressionContext): + pass + + # Exit a parse tree produced by CASSParser#breakExpression. + def exitBreakExpression(self, ctx:CASSParser.BreakExpressionContext): + pass + + + # Enter a parse tree produced by CASSParser#assignmentExpression. + def enterAssignmentExpression(self, ctx:CASSParser.AssignmentExpressionContext): + pass + + # Exit a parse tree produced by CASSParser#assignmentExpression. + def exitAssignmentExpression(self, ctx:CASSParser.AssignmentExpressionContext): + pass + + + # Enter a parse tree produced by CASSParser#unaryExpression. + def enterUnaryExpression(self, ctx:CASSParser.UnaryExpressionContext): + pass + + # Exit a parse tree produced by CASSParser#unaryExpression. + def exitUnaryExpression(self, ctx:CASSParser.UnaryExpressionContext): + pass + + + # Enter a parse tree produced by CASSParser#comparingExpression. + def enterComparingExpression(self, ctx:CASSParser.ComparingExpressionContext): + pass + + # Exit a parse tree produced by CASSParser#comparingExpression. + def exitComparingExpression(self, ctx:CASSParser.ComparingExpressionContext): + pass + + + # Enter a parse tree produced by CASSParser#primaryExpression. + def enterPrimaryExpression(self, ctx:CASSParser.PrimaryExpressionContext): + pass + + # Exit a parse tree produced by CASSParser#primaryExpression. + def exitPrimaryExpression(self, ctx:CASSParser.PrimaryExpressionContext): + pass + + + # Enter a parse tree produced by CASSParser#pointerExpression. + def enterPointerExpression(self, ctx:CASSParser.PointerExpressionContext): + pass + + # Exit a parse tree produced by CASSParser#pointerExpression. + def exitPointerExpression(self, ctx:CASSParser.PointerExpressionContext): + pass + + + # Enter a parse tree produced by CASSParser#assignmentOperator. + def enterAssignmentOperator(self, ctx:CASSParser.AssignmentOperatorContext): + pass + + # Exit a parse tree produced by CASSParser#assignmentOperator. + def exitAssignmentOperator(self, ctx:CASSParser.AssignmentOperatorContext): + pass + + + # Enter a parse tree produced by CASSParser#logicalOrExpression. + def enterLogicalOrExpression(self, ctx:CASSParser.LogicalOrExpressionContext): + pass + + # Exit a parse tree produced by CASSParser#logicalOrExpression. + def exitLogicalOrExpression(self, ctx:CASSParser.LogicalOrExpressionContext): + pass + + + # Enter a parse tree produced by CASSParser#logicalAndExpression. + def enterLogicalAndExpression(self, ctx:CASSParser.LogicalAndExpressionContext): + pass + + # Exit a parse tree produced by CASSParser#logicalAndExpression. + def exitLogicalAndExpression(self, ctx:CASSParser.LogicalAndExpressionContext): + pass + + + # Enter a parse tree produced by CASSParser#equalityExpression. + def enterEqualityExpression(self, ctx:CASSParser.EqualityExpressionContext): + pass + + # Exit a parse tree produced by CASSParser#equalityExpression. + def exitEqualityExpression(self, ctx:CASSParser.EqualityExpressionContext): + pass + + + # Enter a parse tree produced by CASSParser#relationalExpression. + def enterRelationalExpression(self, ctx:CASSParser.RelationalExpressionContext): + pass + + # Exit a parse tree produced by CASSParser#relationalExpression. + def exitRelationalExpression(self, ctx:CASSParser.RelationalExpressionContext): + pass + + + # Enter a parse tree produced by CASSParser#additiveExpression. + def enterAdditiveExpression(self, ctx:CASSParser.AdditiveExpressionContext): + pass + + # Exit a parse tree produced by CASSParser#additiveExpression. + def exitAdditiveExpression(self, ctx:CASSParser.AdditiveExpressionContext): + pass + + + # Enter a parse tree produced by CASSParser#multiplicativeExpression. + def enterMultiplicativeExpression(self, ctx:CASSParser.MultiplicativeExpressionContext): + pass + + # Exit a parse tree produced by CASSParser#multiplicativeExpression. + def exitMultiplicativeExpression(self, ctx:CASSParser.MultiplicativeExpressionContext): + pass + + + # Enter a parse tree produced by CASSParser#operationExpression. + def enterOperationExpression(self, ctx:CASSParser.OperationExpressionContext): + pass + + # Exit a parse tree produced by CASSParser#operationExpression. + def exitOperationExpression(self, ctx:CASSParser.OperationExpressionContext): + pass + + + +del CASSParser \ No newline at end of file diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASSNode.py b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASSNode.py new file mode 100644 index 00000000000..68c3d76313d --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASSNode.py @@ -0,0 +1,179 @@ +class CassNode: + def __init__(self, label): + + self.label = str(label) + self.children = [] + self.prevUse = -1 # keep track of the prev node usage + self.nextUse = -1 + + self.source_range = None + + def add_child(self, child): + self.children.append(child) + + # Creating the Cass string + def to_cass_string(self) -> str: + + cass_strings = [] # Store each function separately + node_counter = {"current_id": 1} # Start numbering at 1 for each function + + def traverse(node): + if node.label == "removed": + return "".join(traverse(child) for child in node.children) + + child_strings = [traverse(c) for c in node.children] + + if node.label.startswith("v"): + return (f"{node.label}\t{node.prevUse}\t{node.nextUse}\t" + "".join(child_strings)) + + if node.label.startswith("V") or node.label.startswith(("N", "C", "S", "F")): + return f"{node.label}\t" + "".join(child_strings) + + child_count = len(node.children) + return f"{node.label}\t{child_count}\t" + "".join(child_strings) + + # Process each top-level function separately + for function_tree in self.children: + node_counter["current_id"] = 1 # Reset numbering for each function + cass_string = traverse(function_tree) + cass_strings.append(cass_string) + + return cass_strings # Return list of separate CASS strings + + def get_source_range_string(self): + """ + Return the source-range string in the format "0,0,5,1" + only if this node is a top-level S#FS function and + source_range is set. Otherwise return a default "0,0,0,0". + """ + if self.label.startswith("S#FS") and self.source_range is not None: + (start_l, start_c, end_l, end_c) = self.source_range + return f"{start_l},{start_c},{end_l},{end_c}" + else: + # Not S#FS or we don't have the source_range info + return "0,0,0,0" + + + + # Getting all nodes in a Cass tree + def get_node_count(self) -> int: + + node_counter = {"current_id": 1} # Start numbering at 1 + total_nodes = 0 + + def traverse(node): + nonlocal total_nodes + current_id = node_counter["current_id"] + + if node.label != "removed": + node_counter["current_id"] += 1 + total_nodes += 1 + + for child in node.children: + traverse(child) + + + traverse(self) + + return total_nodes + + # Generating a GraphViz DOT file with nodes numbered in the CASS-style creation order + def to_dot(self): + + lines = ["digraph CASS {", " node [shape=ellipse];"] + node_counter = {"current_id": 1} # Start numbering at 1 + edges = [] + node_map = {} # Keep track of nodes by ID + + def traverse(node, parent_id=None): + # Do not assign an ID or add the "removed" node + if node.label == "removed": + for child in node.children: + traverse(child, parent_id) # Attach children directly to parent + return + + current_id = node_counter["current_id"] + node_counter["current_id"] += 1 + + # Escape double quotes in label if necessary + safe_label = node.label.replace('"', '\\"') + node_map[current_id] = node.label # Store node label + + lines.append(f' n{current_id} [label="[{current_id}]: {safe_label}"];') + + # Create an edge from parent to the current node + if parent_id is not None: + edges.append(f' n{parent_id} -> n{current_id};') + + # Visit children in the order they were added + for child in node.children: + traverse(child, current_id) + + return current_id + + # **Step 1: Traverse children of "removed" root, skipping "removed" itself** + for child in self.children: + traverse(child) + + # **Step 2: Add remaining edges to the DOT file** + lines.extend(edges) + + # **Step 3: Close the DOT graph** + lines.append("}") + + return lines + + +""" + 1) Numbering the nodes according to preorder Depth First Search Algorithm. + 2) For each node referencing a local variable (label 'vX'), record it in usage_map. + 3) After collecting, fill in .prevUse and .nextUse. +""" + +def assign_usage_links(root: CassNode): + + usage_map = {} + + current_id = [0] + + def dfs(node: CassNode): + + if (node.label != "removed"): + this_index = current_id[0] + current_id[0] += 1 + + # If it's a local variable usage, store in usage_map + if node.label.startswith("v"): + var_name = node.label[1:] # "vsum" => "sum" + if var_name not in usage_map: + usage_map[var_name] = [] + usage_map[var_name].append((this_index, node)) + + # Recurse on children + for child in node.children: + dfs(child) + else: + + for child in node.children: + dfs(child) + + # Collect usage in a DFS + dfs(root) + + # For each variable, link up usage + for var_name, usage_list in usage_map.items(): + # usage_list is e.g. [(4, nodeObj), (8, nodeObj), (21, nodeObj)] + for i, (this_idx, node_obj) in enumerate(usage_list): + # prev + if i > 0: + prev_idx = usage_list[i-1][0] + node_obj.prevUse = prev_idx + else: + node_obj.prevUse = -1 + + # next + if i < len(usage_list)-1: + next_idx = usage_list[i+1][0] + node_obj.nextUse = next_idx + else: + node_obj.nextUse = -1 \ No newline at end of file diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASSParser.py b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASSParser.py new file mode 100644 index 00000000000..c6a4f93e8ef --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASSParser.py @@ -0,0 +1,3436 @@ +# Generated from CASS.g4 by ANTLR 4.13.2 +# encoding: utf-8 +from antlr4 import * +from io import StringIO +import sys +if sys.version_info[1] > 5: + from typing import TextIO +else: + from typing.io import TextIO + +def serializedATN(): + return [ + 4,1,54,415,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7, + 6,2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13, + 2,14,7,14,2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19,7,19,2,20, + 7,20,2,21,7,21,2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25,2,26,7,26, + 2,27,7,27,2,28,7,28,2,29,7,29,2,30,7,30,2,31,7,31,2,32,7,32,2,33, + 7,33,2,34,7,34,2,35,7,35,2,36,7,36,2,37,7,37,2,38,7,38,2,39,7,39, + 2,40,7,40,2,41,7,41,2,42,7,42,1,0,4,0,88,8,0,11,0,12,0,89,1,0,1, + 0,1,1,1,1,1,1,1,1,3,1,98,8,1,1,1,1,1,1,1,1,2,1,2,5,2,105,8,2,10, + 2,12,2,108,9,2,1,2,1,2,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1, + 3,1,3,1,3,3,3,125,8,3,1,4,1,4,5,4,129,8,4,10,4,12,4,132,9,4,1,4, + 1,4,1,4,3,4,137,8,4,1,4,1,4,1,4,1,4,1,4,1,4,3,4,145,8,4,3,4,147, + 8,4,1,4,3,4,150,8,4,1,5,1,5,1,5,1,5,1,5,3,5,157,8,5,1,5,1,5,1,5, + 1,5,1,5,1,5,1,5,1,6,1,6,1,6,3,6,169,8,6,1,6,1,6,3,6,173,8,6,1,6, + 1,6,1,6,1,6,1,6,1,6,1,6,1,7,1,7,1,8,1,8,1,8,1,8,1,8,1,8,1,9,1,9, + 1,9,1,9,1,9,1,9,1,10,1,10,1,10,1,10,1,10,1,10,3,10,202,8,10,1,11, + 1,11,1,11,1,11,1,11,1,11,3,11,210,8,11,1,12,1,12,1,12,1,12,3,12, + 216,8,12,1,13,1,13,1,13,1,13,1,13,1,13,1,14,1,14,3,14,226,8,14,1, + 14,3,14,229,8,14,1,14,1,14,5,14,233,8,14,10,14,12,14,236,9,14,1, + 14,3,14,239,8,14,1,15,1,15,1,15,3,15,244,8,15,1,15,1,15,1,16,1,16, + 1,16,3,16,251,8,16,1,16,1,16,1,17,1,17,1,17,1,17,5,17,259,8,17,10, + 17,12,17,262,9,17,1,17,1,17,1,18,1,18,1,18,1,19,1,19,1,20,1,20,1, + 20,5,20,274,8,20,10,20,12,20,277,9,20,1,21,1,21,3,21,281,8,21,1, + 21,1,21,1,22,1,22,1,22,1,23,1,23,1,23,1,24,1,24,1,24,5,24,294,8, + 24,10,24,12,24,297,9,24,1,25,1,25,1,25,1,26,1,26,1,27,1,27,3,27, + 306,8,27,1,28,1,28,1,29,1,29,1,29,1,30,1,30,1,30,1,30,3,30,317,8, + 30,1,30,3,30,320,8,30,1,30,3,30,323,8,30,1,31,1,31,1,31,1,31,1,31, + 1,31,3,31,331,8,31,1,31,1,31,5,31,335,8,31,10,31,12,31,338,9,31, + 1,32,1,32,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33, + 3,33,353,8,33,1,34,1,34,1,34,1,34,3,34,359,8,34,1,35,1,35,1,36,1, + 36,1,36,5,36,366,8,36,10,36,12,36,369,9,36,1,37,1,37,1,37,5,37,374, + 8,37,10,37,12,37,377,9,37,1,38,1,38,1,38,5,38,382,8,38,10,38,12, + 38,385,9,38,1,39,1,39,1,39,5,39,390,8,39,10,39,12,39,393,9,39,1, + 40,1,40,1,40,5,40,398,8,40,10,40,12,40,401,9,40,1,41,1,41,1,41,5, + 41,406,8,41,10,41,12,41,409,9,41,1,42,1,42,3,42,413,8,42,1,42,0, + 1,62,43,0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40, + 42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84, + 0,7,1,0,20,23,1,0,26,27,1,0,28,31,2,0,5,5,33,36,1,0,39,40,1,0,41, + 42,2,0,43,44,52,52,437,0,87,1,0,0,0,2,93,1,0,0,0,4,102,1,0,0,0,6, + 124,1,0,0,0,8,126,1,0,0,0,10,151,1,0,0,0,12,165,1,0,0,0,14,181,1, + 0,0,0,16,183,1,0,0,0,18,189,1,0,0,0,20,195,1,0,0,0,22,203,1,0,0, + 0,24,211,1,0,0,0,26,217,1,0,0,0,28,225,1,0,0,0,30,240,1,0,0,0,32, + 247,1,0,0,0,34,254,1,0,0,0,36,265,1,0,0,0,38,268,1,0,0,0,40,270, + 1,0,0,0,42,278,1,0,0,0,44,284,1,0,0,0,46,287,1,0,0,0,48,290,1,0, + 0,0,50,298,1,0,0,0,52,301,1,0,0,0,54,305,1,0,0,0,56,307,1,0,0,0, + 58,309,1,0,0,0,60,322,1,0,0,0,62,330,1,0,0,0,64,339,1,0,0,0,66,352, + 1,0,0,0,68,358,1,0,0,0,70,360,1,0,0,0,72,362,1,0,0,0,74,370,1,0, + 0,0,76,378,1,0,0,0,78,386,1,0,0,0,80,394,1,0,0,0,82,402,1,0,0,0, + 84,412,1,0,0,0,86,88,3,6,3,0,87,86,1,0,0,0,88,89,1,0,0,0,89,87,1, + 0,0,0,89,90,1,0,0,0,90,91,1,0,0,0,91,92,5,0,0,1,92,1,1,0,0,0,93, + 94,3,52,26,0,94,95,3,66,33,0,95,97,5,1,0,0,96,98,3,48,24,0,97,96, + 1,0,0,0,97,98,1,0,0,0,98,99,1,0,0,0,99,100,5,2,0,0,100,101,3,4,2, + 0,101,3,1,0,0,0,102,106,5,3,0,0,103,105,3,6,3,0,104,103,1,0,0,0, + 105,108,1,0,0,0,106,104,1,0,0,0,106,107,1,0,0,0,107,109,1,0,0,0, + 108,106,1,0,0,0,109,110,5,4,0,0,110,5,1,0,0,0,111,125,3,8,4,0,112, + 125,3,10,5,0,113,125,3,12,6,0,114,125,3,16,8,0,115,125,3,18,9,0, + 116,125,3,20,10,0,117,125,3,22,11,0,118,125,3,42,21,0,119,125,3, + 26,13,0,120,125,3,28,14,0,121,125,3,44,22,0,122,125,3,2,1,0,123, + 125,3,46,23,0,124,111,1,0,0,0,124,112,1,0,0,0,124,113,1,0,0,0,124, + 114,1,0,0,0,124,115,1,0,0,0,124,116,1,0,0,0,124,117,1,0,0,0,124, + 118,1,0,0,0,124,119,1,0,0,0,124,120,1,0,0,0,124,121,1,0,0,0,124, + 122,1,0,0,0,124,123,1,0,0,0,125,7,1,0,0,0,126,130,3,52,26,0,127, + 129,5,52,0,0,128,127,1,0,0,0,129,132,1,0,0,0,130,128,1,0,0,0,130, + 131,1,0,0,0,131,136,1,0,0,0,132,130,1,0,0,0,133,137,3,66,33,0,134, + 137,1,0,0,0,135,137,3,32,16,0,136,133,1,0,0,0,136,134,1,0,0,0,136, + 135,1,0,0,0,137,146,1,0,0,0,138,144,5,5,0,0,139,145,3,54,27,0,140, + 145,1,0,0,0,141,145,3,38,19,0,142,145,1,0,0,0,143,145,3,36,18,0, + 144,139,1,0,0,0,144,140,1,0,0,0,144,141,1,0,0,0,144,142,1,0,0,0, + 144,143,1,0,0,0,145,147,1,0,0,0,146,138,1,0,0,0,146,147,1,0,0,0, + 147,149,1,0,0,0,148,150,5,6,0,0,149,148,1,0,0,0,149,150,1,0,0,0, + 150,9,1,0,0,0,151,152,5,7,0,0,152,156,5,1,0,0,153,157,3,8,4,0,154, + 157,1,0,0,0,155,157,3,60,30,0,156,153,1,0,0,0,156,154,1,0,0,0,156, + 155,1,0,0,0,157,158,1,0,0,0,158,159,5,6,0,0,159,160,3,72,36,0,160, + 161,5,6,0,0,161,162,3,62,31,0,162,163,5,2,0,0,163,164,3,4,2,0,164, + 11,1,0,0,0,165,166,5,7,0,0,166,172,5,1,0,0,167,169,3,8,4,0,168,167, + 1,0,0,0,168,169,1,0,0,0,169,173,1,0,0,0,170,173,1,0,0,0,171,173, + 3,60,30,0,172,168,1,0,0,0,172,170,1,0,0,0,172,171,1,0,0,0,173,174, + 1,0,0,0,174,175,5,6,0,0,175,176,3,72,36,0,176,177,5,6,0,0,177,178, + 3,62,31,0,178,179,5,2,0,0,179,180,3,6,3,0,180,13,1,0,0,0,181,182, + 3,72,36,0,182,15,1,0,0,0,183,184,5,8,0,0,184,185,5,1,0,0,185,186, + 3,14,7,0,186,187,5,2,0,0,187,188,3,4,2,0,188,17,1,0,0,0,189,190, + 5,8,0,0,190,191,5,1,0,0,191,192,3,14,7,0,192,193,5,2,0,0,193,194, + 3,6,3,0,194,19,1,0,0,0,195,196,5,9,0,0,196,197,5,1,0,0,197,198,3, + 14,7,0,198,199,5,2,0,0,199,201,3,4,2,0,200,202,3,24,12,0,201,200, + 1,0,0,0,201,202,1,0,0,0,202,21,1,0,0,0,203,204,5,9,0,0,204,205,5, + 1,0,0,205,206,3,14,7,0,206,207,5,2,0,0,207,209,3,6,3,0,208,210,3, + 24,12,0,209,208,1,0,0,0,209,210,1,0,0,0,210,23,1,0,0,0,211,215,5, + 10,0,0,212,216,3,4,2,0,213,216,3,20,10,0,214,216,3,6,3,0,215,212, + 1,0,0,0,215,213,1,0,0,0,215,214,1,0,0,0,216,25,1,0,0,0,217,218,5, + 11,0,0,218,219,5,1,0,0,219,220,3,14,7,0,220,221,5,2,0,0,221,222, + 3,4,2,0,222,27,1,0,0,0,223,226,5,12,0,0,224,226,3,56,28,0,225,223, + 1,0,0,0,225,224,1,0,0,0,226,228,1,0,0,0,227,229,3,66,33,0,228,227, + 1,0,0,0,228,229,1,0,0,0,229,230,1,0,0,0,230,234,5,13,0,0,231,233, + 3,6,3,0,232,231,1,0,0,0,233,236,1,0,0,0,234,232,1,0,0,0,234,235, + 1,0,0,0,235,238,1,0,0,0,236,234,1,0,0,0,237,239,3,58,29,0,238,237, + 1,0,0,0,238,239,1,0,0,0,239,29,1,0,0,0,240,241,5,47,0,0,241,243, + 5,1,0,0,242,244,3,40,20,0,243,242,1,0,0,0,243,244,1,0,0,0,244,245, + 1,0,0,0,245,246,5,2,0,0,246,31,1,0,0,0,247,248,3,66,33,0,248,250, + 5,14,0,0,249,251,3,66,33,0,250,249,1,0,0,0,250,251,1,0,0,0,251,252, + 1,0,0,0,252,253,5,15,0,0,253,33,1,0,0,0,254,255,5,3,0,0,255,260, + 3,66,33,0,256,257,5,16,0,0,257,259,3,66,33,0,258,256,1,0,0,0,259, + 262,1,0,0,0,260,258,1,0,0,0,260,261,1,0,0,0,261,263,1,0,0,0,262, + 260,1,0,0,0,263,264,5,4,0,0,264,35,1,0,0,0,265,266,5,3,0,0,266,267, + 5,4,0,0,267,37,1,0,0,0,268,269,5,17,0,0,269,39,1,0,0,0,270,275,3, + 54,27,0,271,272,5,16,0,0,272,274,3,54,27,0,273,271,1,0,0,0,274,277, + 1,0,0,0,275,273,1,0,0,0,275,276,1,0,0,0,276,41,1,0,0,0,277,275,1, + 0,0,0,278,280,5,18,0,0,279,281,3,54,27,0,280,279,1,0,0,0,280,281, + 1,0,0,0,281,282,1,0,0,0,282,283,5,6,0,0,283,43,1,0,0,0,284,285,3, + 54,27,0,285,286,5,6,0,0,286,45,1,0,0,0,287,288,5,19,0,0,288,289, + 5,53,0,0,289,47,1,0,0,0,290,295,3,50,25,0,291,292,5,16,0,0,292,294, + 3,50,25,0,293,291,1,0,0,0,294,297,1,0,0,0,295,293,1,0,0,0,295,296, + 1,0,0,0,296,49,1,0,0,0,297,295,1,0,0,0,298,299,3,52,26,0,299,300, + 3,66,33,0,300,51,1,0,0,0,301,302,7,0,0,0,302,53,1,0,0,0,303,306, + 3,60,30,0,304,306,3,30,15,0,305,303,1,0,0,0,305,304,1,0,0,0,306, + 55,1,0,0,0,307,308,5,24,0,0,308,57,1,0,0,0,309,310,5,25,0,0,310, + 311,5,6,0,0,311,59,1,0,0,0,312,313,3,62,31,0,313,314,3,70,35,0,314, + 316,3,60,30,0,315,317,3,38,19,0,316,315,1,0,0,0,316,317,1,0,0,0, + 317,319,1,0,0,0,318,320,3,36,18,0,319,318,1,0,0,0,319,320,1,0,0, + 0,320,323,1,0,0,0,321,323,3,72,36,0,322,312,1,0,0,0,322,321,1,0, + 0,0,323,61,1,0,0,0,324,325,6,31,-1,0,325,326,7,1,0,0,326,331,3,62, + 31,5,327,331,3,68,34,0,328,331,3,66,33,0,329,331,3,34,17,0,330,324, + 1,0,0,0,330,327,1,0,0,0,330,328,1,0,0,0,330,329,1,0,0,0,331,336, + 1,0,0,0,332,333,10,4,0,0,333,335,7,1,0,0,334,332,1,0,0,0,335,338, + 1,0,0,0,336,334,1,0,0,0,336,337,1,0,0,0,337,63,1,0,0,0,338,336,1, + 0,0,0,339,340,7,2,0,0,340,65,1,0,0,0,341,353,5,47,0,0,342,353,5, + 48,0,0,343,353,5,50,0,0,344,353,5,51,0,0,345,353,5,53,0,0,346,353, + 5,49,0,0,347,353,3,30,15,0,348,349,5,1,0,0,349,350,3,54,27,0,350, + 351,5,2,0,0,351,353,1,0,0,0,352,341,1,0,0,0,352,342,1,0,0,0,352, + 343,1,0,0,0,352,344,1,0,0,0,352,345,1,0,0,0,352,346,1,0,0,0,352, + 347,1,0,0,0,352,348,1,0,0,0,353,67,1,0,0,0,354,355,5,32,0,0,355, + 359,3,66,33,0,356,357,5,52,0,0,357,359,3,66,33,0,358,354,1,0,0,0, + 358,356,1,0,0,0,359,69,1,0,0,0,360,361,7,3,0,0,361,71,1,0,0,0,362, + 367,3,74,37,0,363,364,5,37,0,0,364,366,3,74,37,0,365,363,1,0,0,0, + 366,369,1,0,0,0,367,365,1,0,0,0,367,368,1,0,0,0,368,73,1,0,0,0,369, + 367,1,0,0,0,370,375,3,76,38,0,371,372,5,38,0,0,372,374,3,76,38,0, + 373,371,1,0,0,0,374,377,1,0,0,0,375,373,1,0,0,0,375,376,1,0,0,0, + 376,75,1,0,0,0,377,375,1,0,0,0,378,383,3,78,39,0,379,380,7,4,0,0, + 380,382,3,78,39,0,381,379,1,0,0,0,382,385,1,0,0,0,383,381,1,0,0, + 0,383,384,1,0,0,0,384,77,1,0,0,0,385,383,1,0,0,0,386,391,3,80,40, + 0,387,388,7,2,0,0,388,390,3,80,40,0,389,387,1,0,0,0,390,393,1,0, + 0,0,391,389,1,0,0,0,391,392,1,0,0,0,392,79,1,0,0,0,393,391,1,0,0, + 0,394,399,3,82,41,0,395,396,7,5,0,0,396,398,3,82,41,0,397,395,1, + 0,0,0,398,401,1,0,0,0,399,397,1,0,0,0,399,400,1,0,0,0,400,81,1,0, + 0,0,401,399,1,0,0,0,402,407,3,62,31,0,403,404,7,6,0,0,404,406,3, + 62,31,0,405,403,1,0,0,0,406,409,1,0,0,0,407,405,1,0,0,0,407,408, + 1,0,0,0,408,83,1,0,0,0,409,407,1,0,0,0,410,413,3,80,40,0,411,413, + 3,82,41,0,412,410,1,0,0,0,412,411,1,0,0,0,413,85,1,0,0,0,40,89,97, + 106,124,130,136,144,146,149,156,168,172,201,209,215,225,228,234, + 238,243,250,260,275,280,295,305,316,319,322,330,336,352,358,367, + 375,383,391,399,407,412 + ] + +class CASSParser ( Parser ): + + grammarFileName = "CASS.g4" + + atn = ATNDeserializer().deserialize(serializedATN()) + + decisionsToDFA = [ DFA(ds, i) for i, ds in enumerate(atn.decisionToState) ] + + sharedContextCache = PredictionContextCache() + + literalNames = [ "", "'('", "')'", "'{'", "'}'", "'='", "';'", + "'for'", "'while'", "'if'", "'else'", "'switch'", "'case'", + "':'", "'['", "']'", "','", "'nullptr'", "'return'", + "'include'", "'int'", "'float'", "'double'", "'void'", + "'default'", "'break'", "'++'", "'--'", "'>'", "'<'", + "'<='", "'>='", "'&'", "'+='", "'-='", "'*='", "'/='", + "'||'", "'&&'", "'=='", "'!='", "'+'", "'-'", "'/'", + "'%'", "", "", "", "", + "", "", "", "'*'" ] + + symbolicNames = [ "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "SL_COMMENT", "ML_COMMENT", "ID", "INT", + "BOOL", "FLOAT", "CHAR", "POINTER", "STRING", "WS" ] + + RULE_prog = 0 + RULE_functionDefinition = 1 + RULE_compoundStatement = 2 + RULE_statement = 3 + RULE_declarationStatement = 4 + RULE_forBlockStatement = 5 + RULE_forSingleStatement = 6 + RULE_conditionClause = 7 + RULE_whileBlockStatement = 8 + RULE_whileSingleStatement = 9 + RULE_ifBlockStatement = 10 + RULE_ifSingleStatement = 11 + RULE_elseClause = 12 + RULE_switchStatement = 13 + RULE_caseStatement = 14 + RULE_functionCall = 15 + RULE_arrayDeclarator = 16 + RULE_listInitializer = 17 + RULE_emptyInitializer = 18 + RULE_nullptr = 19 + RULE_argumentList = 20 + RULE_returnStatement = 21 + RULE_expressionStatement = 22 + RULE_includeStatement = 23 + RULE_parameterList = 24 + RULE_parameter = 25 + RULE_typeSpec = 26 + RULE_expression = 27 + RULE_defaultExpression = 28 + RULE_breakExpression = 29 + RULE_assignmentExpression = 30 + RULE_unaryExpression = 31 + RULE_comparingExpression = 32 + RULE_primaryExpression = 33 + RULE_pointerExpression = 34 + RULE_assignmentOperator = 35 + RULE_logicalOrExpression = 36 + RULE_logicalAndExpression = 37 + RULE_equalityExpression = 38 + RULE_relationalExpression = 39 + RULE_additiveExpression = 40 + RULE_multiplicativeExpression = 41 + RULE_operationExpression = 42 + + ruleNames = [ "prog", "functionDefinition", "compoundStatement", "statement", + "declarationStatement", "forBlockStatement", "forSingleStatement", + "conditionClause", "whileBlockStatement", "whileSingleStatement", + "ifBlockStatement", "ifSingleStatement", "elseClause", + "switchStatement", "caseStatement", "functionCall", "arrayDeclarator", + "listInitializer", "emptyInitializer", "nullptr", "argumentList", + "returnStatement", "expressionStatement", "includeStatement", + "parameterList", "parameter", "typeSpec", "expression", + "defaultExpression", "breakExpression", "assignmentExpression", + "unaryExpression", "comparingExpression", "primaryExpression", + "pointerExpression", "assignmentOperator", "logicalOrExpression", + "logicalAndExpression", "equalityExpression", "relationalExpression", + "additiveExpression", "multiplicativeExpression", "operationExpression" ] + + EOF = Token.EOF + T__0=1 + T__1=2 + T__2=3 + T__3=4 + T__4=5 + T__5=6 + T__6=7 + T__7=8 + T__8=9 + T__9=10 + T__10=11 + T__11=12 + T__12=13 + T__13=14 + T__14=15 + T__15=16 + T__16=17 + T__17=18 + T__18=19 + T__19=20 + T__20=21 + T__21=22 + T__22=23 + T__23=24 + T__24=25 + T__25=26 + T__26=27 + T__27=28 + T__28=29 + T__29=30 + T__30=31 + T__31=32 + T__32=33 + T__33=34 + T__34=35 + T__35=36 + T__36=37 + T__37=38 + T__38=39 + T__39=40 + T__40=41 + T__41=42 + T__42=43 + T__43=44 + SL_COMMENT=45 + ML_COMMENT=46 + ID=47 + INT=48 + BOOL=49 + FLOAT=50 + CHAR=51 + POINTER=52 + STRING=53 + WS=54 + + def __init__(self, input:TokenStream, output:TextIO = sys.stdout): + super().__init__(input, output) + self.checkVersion("4.13.2") + self._interp = ParserATNSimulator(self, self.atn, self.decisionsToDFA, self.sharedContextCache) + self._predicates = None + + + + + class ProgContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def EOF(self): + return self.getToken(CASSParser.EOF, 0) + + def statement(self, i:int=None): + if i is None: + return self.getTypedRuleContexts(CASSParser.StatementContext) + else: + return self.getTypedRuleContext(CASSParser.StatementContext,i) + + + def getRuleIndex(self): + return CASSParser.RULE_prog + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterProg" ): + listener.enterProg(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitProg" ): + listener.exitProg(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitProg" ): + return visitor.visitProg(self) + else: + return visitor.visitChildren(self) + + + + + def prog(self): + + localctx = CASSParser.ProgContext(self, self._ctx, self.state) + self.enterRule(localctx, 0, self.RULE_prog) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 87 + self._errHandler.sync(self) + _la = self._input.LA(1) + while True: + self.state = 86 + self.statement() + self.state = 89 + self._errHandler.sync(self) + _la = self._input.LA(1) + if not ((((_la) & ~0x3f) == 0 and ((1 << _la) & 17873665550719882) != 0)): + break + + self.state = 91 + self.match(CASSParser.EOF) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class FunctionDefinitionContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def typeSpec(self): + return self.getTypedRuleContext(CASSParser.TypeSpecContext,0) + + + def primaryExpression(self): + return self.getTypedRuleContext(CASSParser.PrimaryExpressionContext,0) + + + def compoundStatement(self): + return self.getTypedRuleContext(CASSParser.CompoundStatementContext,0) + + + def parameterList(self): + return self.getTypedRuleContext(CASSParser.ParameterListContext,0) + + + def getRuleIndex(self): + return CASSParser.RULE_functionDefinition + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterFunctionDefinition" ): + listener.enterFunctionDefinition(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitFunctionDefinition" ): + listener.exitFunctionDefinition(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitFunctionDefinition" ): + return visitor.visitFunctionDefinition(self) + else: + return visitor.visitChildren(self) + + + + + def functionDefinition(self): + + localctx = CASSParser.FunctionDefinitionContext(self, self._ctx, self.state) + self.enterRule(localctx, 2, self.RULE_functionDefinition) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 93 + self.typeSpec() + self.state = 94 + self.primaryExpression() + self.state = 95 + self.match(CASSParser.T__0) + self.state = 97 + self._errHandler.sync(self) + _la = self._input.LA(1) + if (((_la) & ~0x3f) == 0 and ((1 << _la) & 15728640) != 0): + self.state = 96 + self.parameterList() + + + self.state = 99 + self.match(CASSParser.T__1) + self.state = 100 + self.compoundStatement() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class CompoundStatementContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def statement(self, i:int=None): + if i is None: + return self.getTypedRuleContexts(CASSParser.StatementContext) + else: + return self.getTypedRuleContext(CASSParser.StatementContext,i) + + + def getRuleIndex(self): + return CASSParser.RULE_compoundStatement + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterCompoundStatement" ): + listener.enterCompoundStatement(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitCompoundStatement" ): + listener.exitCompoundStatement(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitCompoundStatement" ): + return visitor.visitCompoundStatement(self) + else: + return visitor.visitChildren(self) + + + + + def compoundStatement(self): + + localctx = CASSParser.CompoundStatementContext(self, self._ctx, self.state) + self.enterRule(localctx, 4, self.RULE_compoundStatement) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 102 + self.match(CASSParser.T__2) + self.state = 106 + self._errHandler.sync(self) + _la = self._input.LA(1) + while (((_la) & ~0x3f) == 0 and ((1 << _la) & 17873665550719882) != 0): + self.state = 103 + self.statement() + self.state = 108 + self._errHandler.sync(self) + _la = self._input.LA(1) + + self.state = 109 + self.match(CASSParser.T__3) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class StatementContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def declarationStatement(self): + return self.getTypedRuleContext(CASSParser.DeclarationStatementContext,0) + + + def forBlockStatement(self): + return self.getTypedRuleContext(CASSParser.ForBlockStatementContext,0) + + + def forSingleStatement(self): + return self.getTypedRuleContext(CASSParser.ForSingleStatementContext,0) + + + def whileBlockStatement(self): + return self.getTypedRuleContext(CASSParser.WhileBlockStatementContext,0) + + + def whileSingleStatement(self): + return self.getTypedRuleContext(CASSParser.WhileSingleStatementContext,0) + + + def ifBlockStatement(self): + return self.getTypedRuleContext(CASSParser.IfBlockStatementContext,0) + + + def ifSingleStatement(self): + return self.getTypedRuleContext(CASSParser.IfSingleStatementContext,0) + + + def returnStatement(self): + return self.getTypedRuleContext(CASSParser.ReturnStatementContext,0) + + + def switchStatement(self): + return self.getTypedRuleContext(CASSParser.SwitchStatementContext,0) + + + def caseStatement(self): + return self.getTypedRuleContext(CASSParser.CaseStatementContext,0) + + + def expressionStatement(self): + return self.getTypedRuleContext(CASSParser.ExpressionStatementContext,0) + + + def functionDefinition(self): + return self.getTypedRuleContext(CASSParser.FunctionDefinitionContext,0) + + + def includeStatement(self): + return self.getTypedRuleContext(CASSParser.IncludeStatementContext,0) + + + def getRuleIndex(self): + return CASSParser.RULE_statement + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterStatement" ): + listener.enterStatement(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitStatement" ): + listener.exitStatement(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitStatement" ): + return visitor.visitStatement(self) + else: + return visitor.visitChildren(self) + + + + + def statement(self): + + localctx = CASSParser.StatementContext(self, self._ctx, self.state) + self.enterRule(localctx, 6, self.RULE_statement) + try: + self.state = 124 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input,3,self._ctx) + if la_ == 1: + self.enterOuterAlt(localctx, 1) + self.state = 111 + self.declarationStatement() + pass + + elif la_ == 2: + self.enterOuterAlt(localctx, 2) + self.state = 112 + self.forBlockStatement() + pass + + elif la_ == 3: + self.enterOuterAlt(localctx, 3) + self.state = 113 + self.forSingleStatement() + pass + + elif la_ == 4: + self.enterOuterAlt(localctx, 4) + self.state = 114 + self.whileBlockStatement() + pass + + elif la_ == 5: + self.enterOuterAlt(localctx, 5) + self.state = 115 + self.whileSingleStatement() + pass + + elif la_ == 6: + self.enterOuterAlt(localctx, 6) + self.state = 116 + self.ifBlockStatement() + pass + + elif la_ == 7: + self.enterOuterAlt(localctx, 7) + self.state = 117 + self.ifSingleStatement() + pass + + elif la_ == 8: + self.enterOuterAlt(localctx, 8) + self.state = 118 + self.returnStatement() + pass + + elif la_ == 9: + self.enterOuterAlt(localctx, 9) + self.state = 119 + self.switchStatement() + pass + + elif la_ == 10: + self.enterOuterAlt(localctx, 10) + self.state = 120 + self.caseStatement() + pass + + elif la_ == 11: + self.enterOuterAlt(localctx, 11) + self.state = 121 + self.expressionStatement() + pass + + elif la_ == 12: + self.enterOuterAlt(localctx, 12) + self.state = 122 + self.functionDefinition() + pass + + elif la_ == 13: + self.enterOuterAlt(localctx, 13) + self.state = 123 + self.includeStatement() + pass + + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class DeclarationStatementContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def typeSpec(self): + return self.getTypedRuleContext(CASSParser.TypeSpecContext,0) + + + def primaryExpression(self): + return self.getTypedRuleContext(CASSParser.PrimaryExpressionContext,0) + + + def arrayDeclarator(self): + return self.getTypedRuleContext(CASSParser.ArrayDeclaratorContext,0) + + + def POINTER(self, i:int=None): + if i is None: + return self.getTokens(CASSParser.POINTER) + else: + return self.getToken(CASSParser.POINTER, i) + + def expression(self): + return self.getTypedRuleContext(CASSParser.ExpressionContext,0) + + + def nullptr(self): + return self.getTypedRuleContext(CASSParser.NullptrContext,0) + + + def emptyInitializer(self): + return self.getTypedRuleContext(CASSParser.EmptyInitializerContext,0) + + + def getRuleIndex(self): + return CASSParser.RULE_declarationStatement + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterDeclarationStatement" ): + listener.enterDeclarationStatement(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitDeclarationStatement" ): + listener.exitDeclarationStatement(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitDeclarationStatement" ): + return visitor.visitDeclarationStatement(self) + else: + return visitor.visitChildren(self) + + + + + def declarationStatement(self): + + localctx = CASSParser.DeclarationStatementContext(self, self._ctx, self.state) + self.enterRule(localctx, 8, self.RULE_declarationStatement) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 126 + self.typeSpec() + self.state = 130 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input,4,self._ctx) + while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: + if _alt==1: + self.state = 127 + self.match(CASSParser.POINTER) + self.state = 132 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input,4,self._ctx) + + self.state = 136 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input,5,self._ctx) + if la_ == 1: + self.state = 133 + self.primaryExpression() + pass + + elif la_ == 2: + pass + + elif la_ == 3: + self.state = 135 + self.arrayDeclarator() + pass + + + self.state = 146 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la==5: + self.state = 138 + self.match(CASSParser.T__4) + self.state = 144 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input,6,self._ctx) + if la_ == 1: + self.state = 139 + self.expression() + pass + + elif la_ == 2: + pass + + elif la_ == 3: + self.state = 141 + self.nullptr() + pass + + elif la_ == 4: + pass + + elif la_ == 5: + self.state = 143 + self.emptyInitializer() + pass + + + + + self.state = 149 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input,8,self._ctx) + if la_ == 1: + self.state = 148 + self.match(CASSParser.T__5) + + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class ForBlockStatementContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def logicalOrExpression(self): + return self.getTypedRuleContext(CASSParser.LogicalOrExpressionContext,0) + + + def unaryExpression(self): + return self.getTypedRuleContext(CASSParser.UnaryExpressionContext,0) + + + def compoundStatement(self): + return self.getTypedRuleContext(CASSParser.CompoundStatementContext,0) + + + def declarationStatement(self): + return self.getTypedRuleContext(CASSParser.DeclarationStatementContext,0) + + + def assignmentExpression(self): + return self.getTypedRuleContext(CASSParser.AssignmentExpressionContext,0) + + + def getRuleIndex(self): + return CASSParser.RULE_forBlockStatement + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterForBlockStatement" ): + listener.enterForBlockStatement(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitForBlockStatement" ): + listener.exitForBlockStatement(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitForBlockStatement" ): + return visitor.visitForBlockStatement(self) + else: + return visitor.visitChildren(self) + + + + + def forBlockStatement(self): + + localctx = CASSParser.ForBlockStatementContext(self, self._ctx, self.state) + self.enterRule(localctx, 10, self.RULE_forBlockStatement) + try: + self.enterOuterAlt(localctx, 1) + self.state = 151 + self.match(CASSParser.T__6) + self.state = 152 + self.match(CASSParser.T__0) + self.state = 156 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [20, 21, 22, 23]: + self.state = 153 + self.declarationStatement() + pass + elif token in [6]: + pass + elif token in [1, 3, 26, 27, 32, 47, 48, 49, 50, 51, 52, 53]: + self.state = 155 + self.assignmentExpression() + pass + else: + raise NoViableAltException(self) + + self.state = 158 + self.match(CASSParser.T__5) + self.state = 159 + self.logicalOrExpression() + self.state = 160 + self.match(CASSParser.T__5) + self.state = 161 + self.unaryExpression(0) + self.state = 162 + self.match(CASSParser.T__1) + self.state = 163 + self.compoundStatement() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class ForSingleStatementContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def logicalOrExpression(self): + return self.getTypedRuleContext(CASSParser.LogicalOrExpressionContext,0) + + + def unaryExpression(self): + return self.getTypedRuleContext(CASSParser.UnaryExpressionContext,0) + + + def statement(self): + return self.getTypedRuleContext(CASSParser.StatementContext,0) + + + def assignmentExpression(self): + return self.getTypedRuleContext(CASSParser.AssignmentExpressionContext,0) + + + def declarationStatement(self): + return self.getTypedRuleContext(CASSParser.DeclarationStatementContext,0) + + + def getRuleIndex(self): + return CASSParser.RULE_forSingleStatement + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterForSingleStatement" ): + listener.enterForSingleStatement(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitForSingleStatement" ): + listener.exitForSingleStatement(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitForSingleStatement" ): + return visitor.visitForSingleStatement(self) + else: + return visitor.visitChildren(self) + + + + + def forSingleStatement(self): + + localctx = CASSParser.ForSingleStatementContext(self, self._ctx, self.state) + self.enterRule(localctx, 12, self.RULE_forSingleStatement) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 165 + self.match(CASSParser.T__6) + self.state = 166 + self.match(CASSParser.T__0) + self.state = 172 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input,11,self._ctx) + if la_ == 1: + self.state = 168 + self._errHandler.sync(self) + _la = self._input.LA(1) + if (((_la) & ~0x3f) == 0 and ((1 << _la) & 15728640) != 0): + self.state = 167 + self.declarationStatement() + + + pass + + elif la_ == 2: + pass + + elif la_ == 3: + self.state = 171 + self.assignmentExpression() + pass + + + self.state = 174 + self.match(CASSParser.T__5) + self.state = 175 + self.logicalOrExpression() + self.state = 176 + self.match(CASSParser.T__5) + self.state = 177 + self.unaryExpression(0) + self.state = 178 + self.match(CASSParser.T__1) + self.state = 179 + self.statement() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class ConditionClauseContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def logicalOrExpression(self): + return self.getTypedRuleContext(CASSParser.LogicalOrExpressionContext,0) + + + def getRuleIndex(self): + return CASSParser.RULE_conditionClause + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterConditionClause" ): + listener.enterConditionClause(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitConditionClause" ): + listener.exitConditionClause(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitConditionClause" ): + return visitor.visitConditionClause(self) + else: + return visitor.visitChildren(self) + + + + + def conditionClause(self): + + localctx = CASSParser.ConditionClauseContext(self, self._ctx, self.state) + self.enterRule(localctx, 14, self.RULE_conditionClause) + try: + self.enterOuterAlt(localctx, 1) + self.state = 181 + self.logicalOrExpression() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class WhileBlockStatementContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def conditionClause(self): + return self.getTypedRuleContext(CASSParser.ConditionClauseContext,0) + + + def compoundStatement(self): + return self.getTypedRuleContext(CASSParser.CompoundStatementContext,0) + + + def getRuleIndex(self): + return CASSParser.RULE_whileBlockStatement + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterWhileBlockStatement" ): + listener.enterWhileBlockStatement(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitWhileBlockStatement" ): + listener.exitWhileBlockStatement(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitWhileBlockStatement" ): + return visitor.visitWhileBlockStatement(self) + else: + return visitor.visitChildren(self) + + + + + def whileBlockStatement(self): + + localctx = CASSParser.WhileBlockStatementContext(self, self._ctx, self.state) + self.enterRule(localctx, 16, self.RULE_whileBlockStatement) + try: + self.enterOuterAlt(localctx, 1) + self.state = 183 + self.match(CASSParser.T__7) + self.state = 184 + self.match(CASSParser.T__0) + self.state = 185 + self.conditionClause() + self.state = 186 + self.match(CASSParser.T__1) + self.state = 187 + self.compoundStatement() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class WhileSingleStatementContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def conditionClause(self): + return self.getTypedRuleContext(CASSParser.ConditionClauseContext,0) + + + def statement(self): + return self.getTypedRuleContext(CASSParser.StatementContext,0) + + + def getRuleIndex(self): + return CASSParser.RULE_whileSingleStatement + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterWhileSingleStatement" ): + listener.enterWhileSingleStatement(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitWhileSingleStatement" ): + listener.exitWhileSingleStatement(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitWhileSingleStatement" ): + return visitor.visitWhileSingleStatement(self) + else: + return visitor.visitChildren(self) + + + + + def whileSingleStatement(self): + + localctx = CASSParser.WhileSingleStatementContext(self, self._ctx, self.state) + self.enterRule(localctx, 18, self.RULE_whileSingleStatement) + try: + self.enterOuterAlt(localctx, 1) + self.state = 189 + self.match(CASSParser.T__7) + self.state = 190 + self.match(CASSParser.T__0) + self.state = 191 + self.conditionClause() + self.state = 192 + self.match(CASSParser.T__1) + self.state = 193 + self.statement() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class IfBlockStatementContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def conditionClause(self): + return self.getTypedRuleContext(CASSParser.ConditionClauseContext,0) + + + def compoundStatement(self): + return self.getTypedRuleContext(CASSParser.CompoundStatementContext,0) + + + def elseClause(self): + return self.getTypedRuleContext(CASSParser.ElseClauseContext,0) + + + def getRuleIndex(self): + return CASSParser.RULE_ifBlockStatement + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterIfBlockStatement" ): + listener.enterIfBlockStatement(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitIfBlockStatement" ): + listener.exitIfBlockStatement(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitIfBlockStatement" ): + return visitor.visitIfBlockStatement(self) + else: + return visitor.visitChildren(self) + + + + + def ifBlockStatement(self): + + localctx = CASSParser.IfBlockStatementContext(self, self._ctx, self.state) + self.enterRule(localctx, 20, self.RULE_ifBlockStatement) + try: + self.enterOuterAlt(localctx, 1) + self.state = 195 + self.match(CASSParser.T__8) + self.state = 196 + self.match(CASSParser.T__0) + self.state = 197 + self.conditionClause() + self.state = 198 + self.match(CASSParser.T__1) + self.state = 199 + self.compoundStatement() + self.state = 201 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input,12,self._ctx) + if la_ == 1: + self.state = 200 + self.elseClause() + + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class IfSingleStatementContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def conditionClause(self): + return self.getTypedRuleContext(CASSParser.ConditionClauseContext,0) + + + def statement(self): + return self.getTypedRuleContext(CASSParser.StatementContext,0) + + + def elseClause(self): + return self.getTypedRuleContext(CASSParser.ElseClauseContext,0) + + + def getRuleIndex(self): + return CASSParser.RULE_ifSingleStatement + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterIfSingleStatement" ): + listener.enterIfSingleStatement(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitIfSingleStatement" ): + listener.exitIfSingleStatement(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitIfSingleStatement" ): + return visitor.visitIfSingleStatement(self) + else: + return visitor.visitChildren(self) + + + + + def ifSingleStatement(self): + + localctx = CASSParser.IfSingleStatementContext(self, self._ctx, self.state) + self.enterRule(localctx, 22, self.RULE_ifSingleStatement) + try: + self.enterOuterAlt(localctx, 1) + self.state = 203 + self.match(CASSParser.T__8) + self.state = 204 + self.match(CASSParser.T__0) + self.state = 205 + self.conditionClause() + self.state = 206 + self.match(CASSParser.T__1) + self.state = 207 + self.statement() + self.state = 209 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input,13,self._ctx) + if la_ == 1: + self.state = 208 + self.elseClause() + + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class ElseClauseContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def compoundStatement(self): + return self.getTypedRuleContext(CASSParser.CompoundStatementContext,0) + + + def ifBlockStatement(self): + return self.getTypedRuleContext(CASSParser.IfBlockStatementContext,0) + + + def statement(self): + return self.getTypedRuleContext(CASSParser.StatementContext,0) + + + def getRuleIndex(self): + return CASSParser.RULE_elseClause + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterElseClause" ): + listener.enterElseClause(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitElseClause" ): + listener.exitElseClause(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitElseClause" ): + return visitor.visitElseClause(self) + else: + return visitor.visitChildren(self) + + + + + def elseClause(self): + + localctx = CASSParser.ElseClauseContext(self, self._ctx, self.state) + self.enterRule(localctx, 24, self.RULE_elseClause) + try: + self.enterOuterAlt(localctx, 1) + self.state = 211 + self.match(CASSParser.T__9) + self.state = 215 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input,14,self._ctx) + if la_ == 1: + self.state = 212 + self.compoundStatement() + pass + + elif la_ == 2: + self.state = 213 + self.ifBlockStatement() + pass + + elif la_ == 3: + self.state = 214 + self.statement() + pass + + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class SwitchStatementContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def conditionClause(self): + return self.getTypedRuleContext(CASSParser.ConditionClauseContext,0) + + + def compoundStatement(self): + return self.getTypedRuleContext(CASSParser.CompoundStatementContext,0) + + + def getRuleIndex(self): + return CASSParser.RULE_switchStatement + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterSwitchStatement" ): + listener.enterSwitchStatement(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitSwitchStatement" ): + listener.exitSwitchStatement(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitSwitchStatement" ): + return visitor.visitSwitchStatement(self) + else: + return visitor.visitChildren(self) + + + + + def switchStatement(self): + + localctx = CASSParser.SwitchStatementContext(self, self._ctx, self.state) + self.enterRule(localctx, 26, self.RULE_switchStatement) + try: + self.enterOuterAlt(localctx, 1) + self.state = 217 + self.match(CASSParser.T__10) + self.state = 218 + self.match(CASSParser.T__0) + self.state = 219 + self.conditionClause() + self.state = 220 + self.match(CASSParser.T__1) + self.state = 221 + self.compoundStatement() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class CaseStatementContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def defaultExpression(self): + return self.getTypedRuleContext(CASSParser.DefaultExpressionContext,0) + + + def primaryExpression(self): + return self.getTypedRuleContext(CASSParser.PrimaryExpressionContext,0) + + + def statement(self, i:int=None): + if i is None: + return self.getTypedRuleContexts(CASSParser.StatementContext) + else: + return self.getTypedRuleContext(CASSParser.StatementContext,i) + + + def breakExpression(self): + return self.getTypedRuleContext(CASSParser.BreakExpressionContext,0) + + + def getRuleIndex(self): + return CASSParser.RULE_caseStatement + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterCaseStatement" ): + listener.enterCaseStatement(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitCaseStatement" ): + listener.exitCaseStatement(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitCaseStatement" ): + return visitor.visitCaseStatement(self) + else: + return visitor.visitChildren(self) + + + + + def caseStatement(self): + + localctx = CASSParser.CaseStatementContext(self, self._ctx, self.state) + self.enterRule(localctx, 28, self.RULE_caseStatement) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 225 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [12]: + self.state = 223 + self.match(CASSParser.T__11) + pass + elif token in [24]: + self.state = 224 + self.defaultExpression() + pass + else: + raise NoViableAltException(self) + + self.state = 228 + self._errHandler.sync(self) + _la = self._input.LA(1) + if (((_la) & ~0x3f) == 0 and ((1 << _la) & 13370061393756162) != 0): + self.state = 227 + self.primaryExpression() + + + self.state = 230 + self.match(CASSParser.T__12) + self.state = 234 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input,17,self._ctx) + while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: + if _alt==1: + self.state = 231 + self.statement() + self.state = 236 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input,17,self._ctx) + + self.state = 238 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input,18,self._ctx) + if la_ == 1: + self.state = 237 + self.breakExpression() + + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class FunctionCallContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def ID(self): + return self.getToken(CASSParser.ID, 0) + + def argumentList(self): + return self.getTypedRuleContext(CASSParser.ArgumentListContext,0) + + + def getRuleIndex(self): + return CASSParser.RULE_functionCall + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterFunctionCall" ): + listener.enterFunctionCall(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitFunctionCall" ): + listener.exitFunctionCall(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitFunctionCall" ): + return visitor.visitFunctionCall(self) + else: + return visitor.visitChildren(self) + + + + + def functionCall(self): + + localctx = CASSParser.FunctionCallContext(self, self._ctx, self.state) + self.enterRule(localctx, 30, self.RULE_functionCall) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 240 + self.match(CASSParser.ID) + self.state = 241 + self.match(CASSParser.T__0) + self.state = 243 + self._errHandler.sync(self) + _la = self._input.LA(1) + if (((_la) & ~0x3f) == 0 and ((1 << _la) & 17873665517420554) != 0): + self.state = 242 + self.argumentList() + + + self.state = 245 + self.match(CASSParser.T__1) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class ArrayDeclaratorContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def primaryExpression(self, i:int=None): + if i is None: + return self.getTypedRuleContexts(CASSParser.PrimaryExpressionContext) + else: + return self.getTypedRuleContext(CASSParser.PrimaryExpressionContext,i) + + + def getRuleIndex(self): + return CASSParser.RULE_arrayDeclarator + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterArrayDeclarator" ): + listener.enterArrayDeclarator(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitArrayDeclarator" ): + listener.exitArrayDeclarator(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitArrayDeclarator" ): + return visitor.visitArrayDeclarator(self) + else: + return visitor.visitChildren(self) + + + + + def arrayDeclarator(self): + + localctx = CASSParser.ArrayDeclaratorContext(self, self._ctx, self.state) + self.enterRule(localctx, 32, self.RULE_arrayDeclarator) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 247 + self.primaryExpression() + self.state = 248 + self.match(CASSParser.T__13) + self.state = 250 + self._errHandler.sync(self) + _la = self._input.LA(1) + if (((_la) & ~0x3f) == 0 and ((1 << _la) & 13370061393756162) != 0): + self.state = 249 + self.primaryExpression() + + + self.state = 252 + self.match(CASSParser.T__14) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class ListInitializerContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def primaryExpression(self, i:int=None): + if i is None: + return self.getTypedRuleContexts(CASSParser.PrimaryExpressionContext) + else: + return self.getTypedRuleContext(CASSParser.PrimaryExpressionContext,i) + + + def getRuleIndex(self): + return CASSParser.RULE_listInitializer + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterListInitializer" ): + listener.enterListInitializer(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitListInitializer" ): + listener.exitListInitializer(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitListInitializer" ): + return visitor.visitListInitializer(self) + else: + return visitor.visitChildren(self) + + + + + def listInitializer(self): + + localctx = CASSParser.ListInitializerContext(self, self._ctx, self.state) + self.enterRule(localctx, 34, self.RULE_listInitializer) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 254 + self.match(CASSParser.T__2) + self.state = 255 + self.primaryExpression() + self.state = 260 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la==16: + self.state = 256 + self.match(CASSParser.T__15) + self.state = 257 + self.primaryExpression() + self.state = 262 + self._errHandler.sync(self) + _la = self._input.LA(1) + + self.state = 263 + self.match(CASSParser.T__3) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class EmptyInitializerContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + + def getRuleIndex(self): + return CASSParser.RULE_emptyInitializer + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterEmptyInitializer" ): + listener.enterEmptyInitializer(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitEmptyInitializer" ): + listener.exitEmptyInitializer(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitEmptyInitializer" ): + return visitor.visitEmptyInitializer(self) + else: + return visitor.visitChildren(self) + + + + + def emptyInitializer(self): + + localctx = CASSParser.EmptyInitializerContext(self, self._ctx, self.state) + self.enterRule(localctx, 36, self.RULE_emptyInitializer) + try: + self.enterOuterAlt(localctx, 1) + self.state = 265 + self.match(CASSParser.T__2) + self.state = 266 + self.match(CASSParser.T__3) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class NullptrContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + + def getRuleIndex(self): + return CASSParser.RULE_nullptr + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterNullptr" ): + listener.enterNullptr(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitNullptr" ): + listener.exitNullptr(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitNullptr" ): + return visitor.visitNullptr(self) + else: + return visitor.visitChildren(self) + + + + + def nullptr(self): + + localctx = CASSParser.NullptrContext(self, self._ctx, self.state) + self.enterRule(localctx, 38, self.RULE_nullptr) + try: + self.enterOuterAlt(localctx, 1) + self.state = 268 + self.match(CASSParser.T__16) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class ArgumentListContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def expression(self, i:int=None): + if i is None: + return self.getTypedRuleContexts(CASSParser.ExpressionContext) + else: + return self.getTypedRuleContext(CASSParser.ExpressionContext,i) + + + def getRuleIndex(self): + return CASSParser.RULE_argumentList + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterArgumentList" ): + listener.enterArgumentList(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitArgumentList" ): + listener.exitArgumentList(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitArgumentList" ): + return visitor.visitArgumentList(self) + else: + return visitor.visitChildren(self) + + + + + def argumentList(self): + + localctx = CASSParser.ArgumentListContext(self, self._ctx, self.state) + self.enterRule(localctx, 40, self.RULE_argumentList) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 270 + self.expression() + self.state = 275 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la==16: + self.state = 271 + self.match(CASSParser.T__15) + self.state = 272 + self.expression() + self.state = 277 + self._errHandler.sync(self) + _la = self._input.LA(1) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class ReturnStatementContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def expression(self): + return self.getTypedRuleContext(CASSParser.ExpressionContext,0) + + + def getRuleIndex(self): + return CASSParser.RULE_returnStatement + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterReturnStatement" ): + listener.enterReturnStatement(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitReturnStatement" ): + listener.exitReturnStatement(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitReturnStatement" ): + return visitor.visitReturnStatement(self) + else: + return visitor.visitChildren(self) + + + + + def returnStatement(self): + + localctx = CASSParser.ReturnStatementContext(self, self._ctx, self.state) + self.enterRule(localctx, 42, self.RULE_returnStatement) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 278 + self.match(CASSParser.T__17) + self.state = 280 + self._errHandler.sync(self) + _la = self._input.LA(1) + if (((_la) & ~0x3f) == 0 and ((1 << _la) & 17873665517420554) != 0): + self.state = 279 + self.expression() + + + self.state = 282 + self.match(CASSParser.T__5) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class ExpressionStatementContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def expression(self): + return self.getTypedRuleContext(CASSParser.ExpressionContext,0) + + + def getRuleIndex(self): + return CASSParser.RULE_expressionStatement + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterExpressionStatement" ): + listener.enterExpressionStatement(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitExpressionStatement" ): + listener.exitExpressionStatement(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitExpressionStatement" ): + return visitor.visitExpressionStatement(self) + else: + return visitor.visitChildren(self) + + + + + def expressionStatement(self): + + localctx = CASSParser.ExpressionStatementContext(self, self._ctx, self.state) + self.enterRule(localctx, 44, self.RULE_expressionStatement) + try: + self.enterOuterAlt(localctx, 1) + self.state = 284 + self.expression() + self.state = 285 + self.match(CASSParser.T__5) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class IncludeStatementContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def STRING(self): + return self.getToken(CASSParser.STRING, 0) + + def getRuleIndex(self): + return CASSParser.RULE_includeStatement + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterIncludeStatement" ): + listener.enterIncludeStatement(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitIncludeStatement" ): + listener.exitIncludeStatement(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitIncludeStatement" ): + return visitor.visitIncludeStatement(self) + else: + return visitor.visitChildren(self) + + + + + def includeStatement(self): + + localctx = CASSParser.IncludeStatementContext(self, self._ctx, self.state) + self.enterRule(localctx, 46, self.RULE_includeStatement) + try: + self.enterOuterAlt(localctx, 1) + self.state = 287 + self.match(CASSParser.T__18) + self.state = 288 + self.match(CASSParser.STRING) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class ParameterListContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def parameter(self, i:int=None): + if i is None: + return self.getTypedRuleContexts(CASSParser.ParameterContext) + else: + return self.getTypedRuleContext(CASSParser.ParameterContext,i) + + + def getRuleIndex(self): + return CASSParser.RULE_parameterList + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterParameterList" ): + listener.enterParameterList(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitParameterList" ): + listener.exitParameterList(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitParameterList" ): + return visitor.visitParameterList(self) + else: + return visitor.visitChildren(self) + + + + + def parameterList(self): + + localctx = CASSParser.ParameterListContext(self, self._ctx, self.state) + self.enterRule(localctx, 48, self.RULE_parameterList) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 290 + self.parameter() + self.state = 295 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la==16: + self.state = 291 + self.match(CASSParser.T__15) + self.state = 292 + self.parameter() + self.state = 297 + self._errHandler.sync(self) + _la = self._input.LA(1) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class ParameterContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def typeSpec(self): + return self.getTypedRuleContext(CASSParser.TypeSpecContext,0) + + + def primaryExpression(self): + return self.getTypedRuleContext(CASSParser.PrimaryExpressionContext,0) + + + def getRuleIndex(self): + return CASSParser.RULE_parameter + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterParameter" ): + listener.enterParameter(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitParameter" ): + listener.exitParameter(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitParameter" ): + return visitor.visitParameter(self) + else: + return visitor.visitChildren(self) + + + + + def parameter(self): + + localctx = CASSParser.ParameterContext(self, self._ctx, self.state) + self.enterRule(localctx, 50, self.RULE_parameter) + try: + self.enterOuterAlt(localctx, 1) + self.state = 298 + self.typeSpec() + self.state = 299 + self.primaryExpression() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class TypeSpecContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + + def getRuleIndex(self): + return CASSParser.RULE_typeSpec + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterTypeSpec" ): + listener.enterTypeSpec(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitTypeSpec" ): + listener.exitTypeSpec(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitTypeSpec" ): + return visitor.visitTypeSpec(self) + else: + return visitor.visitChildren(self) + + + + + def typeSpec(self): + + localctx = CASSParser.TypeSpecContext(self, self._ctx, self.state) + self.enterRule(localctx, 52, self.RULE_typeSpec) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 301 + _la = self._input.LA(1) + if not((((_la) & ~0x3f) == 0 and ((1 << _la) & 15728640) != 0)): + self._errHandler.recoverInline(self) + else: + self._errHandler.reportMatch(self) + self.consume() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class ExpressionContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def assignmentExpression(self): + return self.getTypedRuleContext(CASSParser.AssignmentExpressionContext,0) + + + def functionCall(self): + return self.getTypedRuleContext(CASSParser.FunctionCallContext,0) + + + def getRuleIndex(self): + return CASSParser.RULE_expression + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterExpression" ): + listener.enterExpression(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitExpression" ): + listener.exitExpression(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitExpression" ): + return visitor.visitExpression(self) + else: + return visitor.visitChildren(self) + + + + + def expression(self): + + localctx = CASSParser.ExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 54, self.RULE_expression) + try: + self.state = 305 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input,25,self._ctx) + if la_ == 1: + self.enterOuterAlt(localctx, 1) + self.state = 303 + self.assignmentExpression() + pass + + elif la_ == 2: + self.enterOuterAlt(localctx, 2) + self.state = 304 + self.functionCall() + pass + + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class DefaultExpressionContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + + def getRuleIndex(self): + return CASSParser.RULE_defaultExpression + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterDefaultExpression" ): + listener.enterDefaultExpression(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitDefaultExpression" ): + listener.exitDefaultExpression(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitDefaultExpression" ): + return visitor.visitDefaultExpression(self) + else: + return visitor.visitChildren(self) + + + + + def defaultExpression(self): + + localctx = CASSParser.DefaultExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 56, self.RULE_defaultExpression) + try: + self.enterOuterAlt(localctx, 1) + self.state = 307 + self.match(CASSParser.T__23) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class BreakExpressionContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + + def getRuleIndex(self): + return CASSParser.RULE_breakExpression + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterBreakExpression" ): + listener.enterBreakExpression(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitBreakExpression" ): + listener.exitBreakExpression(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitBreakExpression" ): + return visitor.visitBreakExpression(self) + else: + return visitor.visitChildren(self) + + + + + def breakExpression(self): + + localctx = CASSParser.BreakExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 58, self.RULE_breakExpression) + try: + self.enterOuterAlt(localctx, 1) + self.state = 309 + self.match(CASSParser.T__24) + self.state = 310 + self.match(CASSParser.T__5) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class AssignmentExpressionContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def unaryExpression(self): + return self.getTypedRuleContext(CASSParser.UnaryExpressionContext,0) + + + def assignmentOperator(self): + return self.getTypedRuleContext(CASSParser.AssignmentOperatorContext,0) + + + def assignmentExpression(self): + return self.getTypedRuleContext(CASSParser.AssignmentExpressionContext,0) + + + def nullptr(self): + return self.getTypedRuleContext(CASSParser.NullptrContext,0) + + + def emptyInitializer(self): + return self.getTypedRuleContext(CASSParser.EmptyInitializerContext,0) + + + def logicalOrExpression(self): + return self.getTypedRuleContext(CASSParser.LogicalOrExpressionContext,0) + + + def getRuleIndex(self): + return CASSParser.RULE_assignmentExpression + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterAssignmentExpression" ): + listener.enterAssignmentExpression(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitAssignmentExpression" ): + listener.exitAssignmentExpression(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitAssignmentExpression" ): + return visitor.visitAssignmentExpression(self) + else: + return visitor.visitChildren(self) + + + + + def assignmentExpression(self): + + localctx = CASSParser.AssignmentExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 60, self.RULE_assignmentExpression) + try: + self.state = 322 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input,28,self._ctx) + if la_ == 1: + self.enterOuterAlt(localctx, 1) + self.state = 312 + self.unaryExpression(0) + self.state = 313 + self.assignmentOperator() + self.state = 314 + self.assignmentExpression() + self.state = 316 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input,26,self._ctx) + if la_ == 1: + self.state = 315 + self.nullptr() + + + self.state = 319 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input,27,self._ctx) + if la_ == 1: + self.state = 318 + self.emptyInitializer() + + + pass + + elif la_ == 2: + self.enterOuterAlt(localctx, 2) + self.state = 321 + self.logicalOrExpression() + pass + + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class UnaryExpressionContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def unaryExpression(self): + return self.getTypedRuleContext(CASSParser.UnaryExpressionContext,0) + + + def pointerExpression(self): + return self.getTypedRuleContext(CASSParser.PointerExpressionContext,0) + + + def primaryExpression(self): + return self.getTypedRuleContext(CASSParser.PrimaryExpressionContext,0) + + + def listInitializer(self): + return self.getTypedRuleContext(CASSParser.ListInitializerContext,0) + + + def getRuleIndex(self): + return CASSParser.RULE_unaryExpression + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterUnaryExpression" ): + listener.enterUnaryExpression(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitUnaryExpression" ): + listener.exitUnaryExpression(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitUnaryExpression" ): + return visitor.visitUnaryExpression(self) + else: + return visitor.visitChildren(self) + + + + def unaryExpression(self, _p:int=0): + _parentctx = self._ctx + _parentState = self.state + localctx = CASSParser.UnaryExpressionContext(self, self._ctx, _parentState) + _prevctx = localctx + _startState = 62 + self.enterRecursionRule(localctx, 62, self.RULE_unaryExpression, _p) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 330 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [26, 27]: + self.state = 325 + _la = self._input.LA(1) + if not(_la==26 or _la==27): + self._errHandler.recoverInline(self) + else: + self._errHandler.reportMatch(self) + self.consume() + self.state = 326 + self.unaryExpression(5) + pass + elif token in [32, 52]: + self.state = 327 + self.pointerExpression() + pass + elif token in [1, 47, 48, 49, 50, 51, 53]: + self.state = 328 + self.primaryExpression() + pass + elif token in [3]: + self.state = 329 + self.listInitializer() + pass + else: + raise NoViableAltException(self) + + self._ctx.stop = self._input.LT(-1) + self.state = 336 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input,30,self._ctx) + while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: + if _alt==1: + if self._parseListeners is not None: + self.triggerExitRuleEvent() + _prevctx = localctx + localctx = CASSParser.UnaryExpressionContext(self, _parentctx, _parentState) + self.pushNewRecursionContext(localctx, _startState, self.RULE_unaryExpression) + self.state = 332 + if not self.precpred(self._ctx, 4): + from antlr4.error.Errors import FailedPredicateException + raise FailedPredicateException(self, "self.precpred(self._ctx, 4)") + self.state = 333 + _la = self._input.LA(1) + if not(_la==26 or _la==27): + self._errHandler.recoverInline(self) + else: + self._errHandler.reportMatch(self) + self.consume() + self.state = 338 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input,30,self._ctx) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.unrollRecursionContexts(_parentctx) + return localctx + + + class ComparingExpressionContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + + def getRuleIndex(self): + return CASSParser.RULE_comparingExpression + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterComparingExpression" ): + listener.enterComparingExpression(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitComparingExpression" ): + listener.exitComparingExpression(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitComparingExpression" ): + return visitor.visitComparingExpression(self) + else: + return visitor.visitChildren(self) + + + + + def comparingExpression(self): + + localctx = CASSParser.ComparingExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 64, self.RULE_comparingExpression) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 339 + _la = self._input.LA(1) + if not((((_la) & ~0x3f) == 0 and ((1 << _la) & 4026531840) != 0)): + self._errHandler.recoverInline(self) + else: + self._errHandler.reportMatch(self) + self.consume() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class PrimaryExpressionContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def ID(self): + return self.getToken(CASSParser.ID, 0) + + def INT(self): + return self.getToken(CASSParser.INT, 0) + + def FLOAT(self): + return self.getToken(CASSParser.FLOAT, 0) + + def CHAR(self): + return self.getToken(CASSParser.CHAR, 0) + + def STRING(self): + return self.getToken(CASSParser.STRING, 0) + + def BOOL(self): + return self.getToken(CASSParser.BOOL, 0) + + def functionCall(self): + return self.getTypedRuleContext(CASSParser.FunctionCallContext,0) + + + def expression(self): + return self.getTypedRuleContext(CASSParser.ExpressionContext,0) + + + def getRuleIndex(self): + return CASSParser.RULE_primaryExpression + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterPrimaryExpression" ): + listener.enterPrimaryExpression(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitPrimaryExpression" ): + listener.exitPrimaryExpression(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitPrimaryExpression" ): + return visitor.visitPrimaryExpression(self) + else: + return visitor.visitChildren(self) + + + + + def primaryExpression(self): + + localctx = CASSParser.PrimaryExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 66, self.RULE_primaryExpression) + try: + self.state = 352 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input,31,self._ctx) + if la_ == 1: + self.enterOuterAlt(localctx, 1) + self.state = 341 + self.match(CASSParser.ID) + pass + + elif la_ == 2: + self.enterOuterAlt(localctx, 2) + self.state = 342 + self.match(CASSParser.INT) + pass + + elif la_ == 3: + self.enterOuterAlt(localctx, 3) + self.state = 343 + self.match(CASSParser.FLOAT) + pass + + elif la_ == 4: + self.enterOuterAlt(localctx, 4) + self.state = 344 + self.match(CASSParser.CHAR) + pass + + elif la_ == 5: + self.enterOuterAlt(localctx, 5) + self.state = 345 + self.match(CASSParser.STRING) + pass + + elif la_ == 6: + self.enterOuterAlt(localctx, 6) + self.state = 346 + self.match(CASSParser.BOOL) + pass + + elif la_ == 7: + self.enterOuterAlt(localctx, 7) + self.state = 347 + self.functionCall() + pass + + elif la_ == 8: + self.enterOuterAlt(localctx, 8) + self.state = 348 + self.match(CASSParser.T__0) + self.state = 349 + self.expression() + self.state = 350 + self.match(CASSParser.T__1) + pass + + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class PointerExpressionContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def primaryExpression(self): + return self.getTypedRuleContext(CASSParser.PrimaryExpressionContext,0) + + + def POINTER(self): + return self.getToken(CASSParser.POINTER, 0) + + def getRuleIndex(self): + return CASSParser.RULE_pointerExpression + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterPointerExpression" ): + listener.enterPointerExpression(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitPointerExpression" ): + listener.exitPointerExpression(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitPointerExpression" ): + return visitor.visitPointerExpression(self) + else: + return visitor.visitChildren(self) + + + + + def pointerExpression(self): + + localctx = CASSParser.PointerExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 68, self.RULE_pointerExpression) + try: + self.state = 358 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [32]: + self.enterOuterAlt(localctx, 1) + self.state = 354 + self.match(CASSParser.T__31) + self.state = 355 + self.primaryExpression() + pass + elif token in [52]: + self.enterOuterAlt(localctx, 2) + self.state = 356 + self.match(CASSParser.POINTER) + self.state = 357 + self.primaryExpression() + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class AssignmentOperatorContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + + def getRuleIndex(self): + return CASSParser.RULE_assignmentOperator + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterAssignmentOperator" ): + listener.enterAssignmentOperator(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitAssignmentOperator" ): + listener.exitAssignmentOperator(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitAssignmentOperator" ): + return visitor.visitAssignmentOperator(self) + else: + return visitor.visitChildren(self) + + + + + def assignmentOperator(self): + + localctx = CASSParser.AssignmentOperatorContext(self, self._ctx, self.state) + self.enterRule(localctx, 70, self.RULE_assignmentOperator) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 360 + _la = self._input.LA(1) + if not((((_la) & ~0x3f) == 0 and ((1 << _la) & 128849018912) != 0)): + self._errHandler.recoverInline(self) + else: + self._errHandler.reportMatch(self) + self.consume() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class LogicalOrExpressionContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def logicalAndExpression(self, i:int=None): + if i is None: + return self.getTypedRuleContexts(CASSParser.LogicalAndExpressionContext) + else: + return self.getTypedRuleContext(CASSParser.LogicalAndExpressionContext,i) + + + def getRuleIndex(self): + return CASSParser.RULE_logicalOrExpression + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterLogicalOrExpression" ): + listener.enterLogicalOrExpression(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitLogicalOrExpression" ): + listener.exitLogicalOrExpression(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitLogicalOrExpression" ): + return visitor.visitLogicalOrExpression(self) + else: + return visitor.visitChildren(self) + + + + + def logicalOrExpression(self): + + localctx = CASSParser.LogicalOrExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 72, self.RULE_logicalOrExpression) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 362 + self.logicalAndExpression() + self.state = 367 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la==37: + self.state = 363 + self.match(CASSParser.T__36) + self.state = 364 + self.logicalAndExpression() + self.state = 369 + self._errHandler.sync(self) + _la = self._input.LA(1) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class LogicalAndExpressionContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def equalityExpression(self, i:int=None): + if i is None: + return self.getTypedRuleContexts(CASSParser.EqualityExpressionContext) + else: + return self.getTypedRuleContext(CASSParser.EqualityExpressionContext,i) + + + def getRuleIndex(self): + return CASSParser.RULE_logicalAndExpression + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterLogicalAndExpression" ): + listener.enterLogicalAndExpression(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitLogicalAndExpression" ): + listener.exitLogicalAndExpression(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitLogicalAndExpression" ): + return visitor.visitLogicalAndExpression(self) + else: + return visitor.visitChildren(self) + + + + + def logicalAndExpression(self): + + localctx = CASSParser.LogicalAndExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 74, self.RULE_logicalAndExpression) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 370 + self.equalityExpression() + self.state = 375 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la==38: + self.state = 371 + self.match(CASSParser.T__37) + self.state = 372 + self.equalityExpression() + self.state = 377 + self._errHandler.sync(self) + _la = self._input.LA(1) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class EqualityExpressionContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def relationalExpression(self, i:int=None): + if i is None: + return self.getTypedRuleContexts(CASSParser.RelationalExpressionContext) + else: + return self.getTypedRuleContext(CASSParser.RelationalExpressionContext,i) + + + def getRuleIndex(self): + return CASSParser.RULE_equalityExpression + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterEqualityExpression" ): + listener.enterEqualityExpression(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitEqualityExpression" ): + listener.exitEqualityExpression(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitEqualityExpression" ): + return visitor.visitEqualityExpression(self) + else: + return visitor.visitChildren(self) + + + + + def equalityExpression(self): + + localctx = CASSParser.EqualityExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 76, self.RULE_equalityExpression) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 378 + self.relationalExpression() + self.state = 383 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la==39 or _la==40: + self.state = 379 + _la = self._input.LA(1) + if not(_la==39 or _la==40): + self._errHandler.recoverInline(self) + else: + self._errHandler.reportMatch(self) + self.consume() + self.state = 380 + self.relationalExpression() + self.state = 385 + self._errHandler.sync(self) + _la = self._input.LA(1) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class RelationalExpressionContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def additiveExpression(self, i:int=None): + if i is None: + return self.getTypedRuleContexts(CASSParser.AdditiveExpressionContext) + else: + return self.getTypedRuleContext(CASSParser.AdditiveExpressionContext,i) + + + def getRuleIndex(self): + return CASSParser.RULE_relationalExpression + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterRelationalExpression" ): + listener.enterRelationalExpression(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitRelationalExpression" ): + listener.exitRelationalExpression(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitRelationalExpression" ): + return visitor.visitRelationalExpression(self) + else: + return visitor.visitChildren(self) + + + + + def relationalExpression(self): + + localctx = CASSParser.RelationalExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 78, self.RULE_relationalExpression) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 386 + self.additiveExpression() + self.state = 391 + self._errHandler.sync(self) + _la = self._input.LA(1) + while (((_la) & ~0x3f) == 0 and ((1 << _la) & 4026531840) != 0): + self.state = 387 + _la = self._input.LA(1) + if not((((_la) & ~0x3f) == 0 and ((1 << _la) & 4026531840) != 0)): + self._errHandler.recoverInline(self) + else: + self._errHandler.reportMatch(self) + self.consume() + self.state = 388 + self.additiveExpression() + self.state = 393 + self._errHandler.sync(self) + _la = self._input.LA(1) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class AdditiveExpressionContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def multiplicativeExpression(self, i:int=None): + if i is None: + return self.getTypedRuleContexts(CASSParser.MultiplicativeExpressionContext) + else: + return self.getTypedRuleContext(CASSParser.MultiplicativeExpressionContext,i) + + + def getRuleIndex(self): + return CASSParser.RULE_additiveExpression + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterAdditiveExpression" ): + listener.enterAdditiveExpression(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitAdditiveExpression" ): + listener.exitAdditiveExpression(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitAdditiveExpression" ): + return visitor.visitAdditiveExpression(self) + else: + return visitor.visitChildren(self) + + + + + def additiveExpression(self): + + localctx = CASSParser.AdditiveExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 80, self.RULE_additiveExpression) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 394 + self.multiplicativeExpression() + self.state = 399 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la==41 or _la==42: + self.state = 395 + _la = self._input.LA(1) + if not(_la==41 or _la==42): + self._errHandler.recoverInline(self) + else: + self._errHandler.reportMatch(self) + self.consume() + self.state = 396 + self.multiplicativeExpression() + self.state = 401 + self._errHandler.sync(self) + _la = self._input.LA(1) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class MultiplicativeExpressionContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def unaryExpression(self, i:int=None): + if i is None: + return self.getTypedRuleContexts(CASSParser.UnaryExpressionContext) + else: + return self.getTypedRuleContext(CASSParser.UnaryExpressionContext,i) + + + def POINTER(self, i:int=None): + if i is None: + return self.getTokens(CASSParser.POINTER) + else: + return self.getToken(CASSParser.POINTER, i) + + def getRuleIndex(self): + return CASSParser.RULE_multiplicativeExpression + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterMultiplicativeExpression" ): + listener.enterMultiplicativeExpression(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitMultiplicativeExpression" ): + listener.exitMultiplicativeExpression(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitMultiplicativeExpression" ): + return visitor.visitMultiplicativeExpression(self) + else: + return visitor.visitChildren(self) + + + + + def multiplicativeExpression(self): + + localctx = CASSParser.MultiplicativeExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 82, self.RULE_multiplicativeExpression) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 402 + self.unaryExpression(0) + self.state = 407 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input,38,self._ctx) + while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: + if _alt==1: + self.state = 403 + _la = self._input.LA(1) + if not((((_la) & ~0x3f) == 0 and ((1 << _la) & 4529987906437120) != 0)): + self._errHandler.recoverInline(self) + else: + self._errHandler.reportMatch(self) + self.consume() + self.state = 404 + self.unaryExpression(0) + self.state = 409 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input,38,self._ctx) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class OperationExpressionContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def additiveExpression(self): + return self.getTypedRuleContext(CASSParser.AdditiveExpressionContext,0) + + + def multiplicativeExpression(self): + return self.getTypedRuleContext(CASSParser.MultiplicativeExpressionContext,0) + + + def getRuleIndex(self): + return CASSParser.RULE_operationExpression + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterOperationExpression" ): + listener.enterOperationExpression(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitOperationExpression" ): + listener.exitOperationExpression(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitOperationExpression" ): + return visitor.visitOperationExpression(self) + else: + return visitor.visitChildren(self) + + + + + def operationExpression(self): + + localctx = CASSParser.OperationExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 84, self.RULE_operationExpression) + try: + self.state = 412 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input,39,self._ctx) + if la_ == 1: + self.enterOuterAlt(localctx, 1) + self.state = 410 + self.additiveExpression() + pass + + elif la_ == 2: + self.enterOuterAlt(localctx, 2) + self.state = 411 + self.multiplicativeExpression() + pass + + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + + def sempred(self, localctx:RuleContext, ruleIndex:int, predIndex:int): + if self._predicates == None: + self._predicates = dict() + self._predicates[31] = self.unaryExpression_sempred + pred = self._predicates.get(ruleIndex, None) + if pred is None: + raise Exception("No predicate with index:" + str(ruleIndex)) + else: + return pred(localctx, predIndex) + + def unaryExpression_sempred(self, localctx:UnaryExpressionContext, predIndex:int): + if predIndex == 0: + return self.precpred(self._ctx, 4) + + + + + diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASSVisitor.py b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASSVisitor.py new file mode 100644 index 00000000000..5c819a85b87 --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASSVisitor.py @@ -0,0 +1,228 @@ +# Generated from CASS.g4 by ANTLR 4.13.2 +from antlr4 import * +if "." in __name__: + from .CASSParser import CASSParser +else: + from CASSParser import CASSParser + +# This class defines a complete generic visitor for a parse tree produced by CASSParser. + +class CASSVisitor(ParseTreeVisitor): + + # Visit a parse tree produced by CASSParser#prog. + def visitProg(self, ctx:CASSParser.ProgContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#functionDefinition. + def visitFunctionDefinition(self, ctx:CASSParser.FunctionDefinitionContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#compoundStatement. + def visitCompoundStatement(self, ctx:CASSParser.CompoundStatementContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#statement. + def visitStatement(self, ctx:CASSParser.StatementContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#declarationStatement. + def visitDeclarationStatement(self, ctx:CASSParser.DeclarationStatementContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#forBlockStatement. + def visitForBlockStatement(self, ctx:CASSParser.ForBlockStatementContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#forSingleStatement. + def visitForSingleStatement(self, ctx:CASSParser.ForSingleStatementContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#conditionClause. + def visitConditionClause(self, ctx:CASSParser.ConditionClauseContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#whileBlockStatement. + def visitWhileBlockStatement(self, ctx:CASSParser.WhileBlockStatementContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#whileSingleStatement. + def visitWhileSingleStatement(self, ctx:CASSParser.WhileSingleStatementContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#ifBlockStatement. + def visitIfBlockStatement(self, ctx:CASSParser.IfBlockStatementContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#ifSingleStatement. + def visitIfSingleStatement(self, ctx:CASSParser.IfSingleStatementContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#elseClause. + def visitElseClause(self, ctx:CASSParser.ElseClauseContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#switchStatement. + def visitSwitchStatement(self, ctx:CASSParser.SwitchStatementContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#caseStatement. + def visitCaseStatement(self, ctx:CASSParser.CaseStatementContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#functionCall. + def visitFunctionCall(self, ctx:CASSParser.FunctionCallContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#arrayDeclarator. + def visitArrayDeclarator(self, ctx:CASSParser.ArrayDeclaratorContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#listInitializer. + def visitListInitializer(self, ctx:CASSParser.ListInitializerContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#emptyInitializer. + def visitEmptyInitializer(self, ctx:CASSParser.EmptyInitializerContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#nullptr. + def visitNullptr(self, ctx:CASSParser.NullptrContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#argumentList. + def visitArgumentList(self, ctx:CASSParser.ArgumentListContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#returnStatement. + def visitReturnStatement(self, ctx:CASSParser.ReturnStatementContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#expressionStatement. + def visitExpressionStatement(self, ctx:CASSParser.ExpressionStatementContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#includeStatement. + def visitIncludeStatement(self, ctx:CASSParser.IncludeStatementContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#parameterList. + def visitParameterList(self, ctx:CASSParser.ParameterListContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#parameter. + def visitParameter(self, ctx:CASSParser.ParameterContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#typeSpec. + def visitTypeSpec(self, ctx:CASSParser.TypeSpecContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#expression. + def visitExpression(self, ctx:CASSParser.ExpressionContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#defaultExpression. + def visitDefaultExpression(self, ctx:CASSParser.DefaultExpressionContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#breakExpression. + def visitBreakExpression(self, ctx:CASSParser.BreakExpressionContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#assignmentExpression. + def visitAssignmentExpression(self, ctx:CASSParser.AssignmentExpressionContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#unaryExpression. + def visitUnaryExpression(self, ctx:CASSParser.UnaryExpressionContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#comparingExpression. + def visitComparingExpression(self, ctx:CASSParser.ComparingExpressionContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#primaryExpression. + def visitPrimaryExpression(self, ctx:CASSParser.PrimaryExpressionContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#pointerExpression. + def visitPointerExpression(self, ctx:CASSParser.PointerExpressionContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#assignmentOperator. + def visitAssignmentOperator(self, ctx:CASSParser.AssignmentOperatorContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#logicalOrExpression. + def visitLogicalOrExpression(self, ctx:CASSParser.LogicalOrExpressionContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#logicalAndExpression. + def visitLogicalAndExpression(self, ctx:CASSParser.LogicalAndExpressionContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#equalityExpression. + def visitEqualityExpression(self, ctx:CASSParser.EqualityExpressionContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#relationalExpression. + def visitRelationalExpression(self, ctx:CASSParser.RelationalExpressionContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#additiveExpression. + def visitAdditiveExpression(self, ctx:CASSParser.AdditiveExpressionContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#multiplicativeExpression. + def visitMultiplicativeExpression(self, ctx:CASSParser.MultiplicativeExpressionContext): + return self.visitChildren(ctx) + + + # Visit a parse tree produced by CASSParser#operationExpression. + def visitOperationExpression(self, ctx:CASSParser.OperationExpressionContext): + return self.visitChildren(ctx) + + + +del CASSParser \ No newline at end of file diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASS_Driver.py b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASS_Driver.py new file mode 100644 index 00000000000..67699a24def --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/CASS_Driver.py @@ -0,0 +1,64 @@ +import sys +import subprocess +from antlr4 import FileStream, CommonTokenStream +from CASSLexer import CASSLexer +from CASSParser import CASSParser +from MyCASSVisitor import MyCassVisitor +from CASSNode import assign_usage_links + +def drive_tree(input_file): + #if len(sys.argv) < 2: + #print("Usage: python driver.py ") + #sys.exit(1) + + #input_file = sys.argv[1] + + # 1) Lex & parse + input_stream = FileStream(input_file) + lexer = CASSLexer(input_stream) + token_stream = CommonTokenStream(lexer) + parser = CASSParser(token_stream) + + parse_tree = parser.prog() # or whatever your top rule is + + # 2) Transform to CASS + visitor = MyCassVisitor() + cass_root = visitor.visit(parse_tree) + assign_usage_links(cass_root) + + count = [] + for child in cass_root.children: + count.append(child.get_node_count()) + cass_strings = cass_root.to_cass_string() # Now returns a list + final_list = [] + + # **4) Print the results correctly** + + for i in range(len(cass_strings)): + child = cass_root.children[i] + + # 1) Node count + node_count = count[i] + + # 2) Source range + src_range_str = child.get_source_range_string() + + # 3) The original CAS body + cas_body = cass_strings[i] + + # 4) Combine them: "0,0,5,1 23 S#FS#1_2 ... rest ..." + # Note the order: range -> node_count -> CAS body + final_cas = f"{src_range_str}\t{node_count}\t{cas_body}" + final_list.append(final_cas) + + + return final_list + + + # 4) Create DOT & PNG + + + +if __name__ == "__main__": + input_file = "input_code_ez.c" + print(drive_tree(input_file)) diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/DriverCASS.py b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/DriverCASS.py new file mode 100644 index 00000000000..59527f64c91 --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/DriverCASS.py @@ -0,0 +1,73 @@ +import sys +import subprocess +from antlr4 import FileStream, CommonTokenStream +from CASSLexer import CASSLexer +from CASSParser import CASSParser +from MyCASSVisitor import MyCassVisitor +from CASSNode import assign_usage_links + +def drive_tree(): + if len(sys.argv) < 2: + print("Usage: python driver.py ") + sys.exit(1) + + input_file = sys.argv[1] + + # 1) Lex & parse + input_stream = FileStream(input_file) + lexer = CASSLexer(input_stream) + token_stream = CommonTokenStream(lexer) + parser = CASSParser(token_stream) + + parse_tree = parser.prog() # or whatever your top rule is + + # 2) Transform to CASS + visitor = MyCassVisitor() + cass_root = visitor.visit(parse_tree) + #assign_usage_links(cass_root) + + count = [] + for child in cass_root.children: + assign_usage_links(child) + count.append(child.get_node_count()) + + + for child in cass_root.children: + count.append(child.get_node_count()) + cass_strings = cass_root.to_cass_string() # Now returns a list + final_list = [] + + # **4) Print the results correctly** + + for i in range(len(cass_strings)): + child = cass_root.children[i] + + # 1) Node count + node_count = count[i] + + # 2) Source range + src_range_str = child.get_source_range_string() + + # 3) The original CAS body + cas_body = cass_strings[i] + + # 4) Combine them: "0,0,5,1 23 S#FS#1_2 ... rest ..." + # Note the order: range -> node_count -> CAS body + final_cas = f"{src_range_str}\t{node_count}\t{cas_body}" + final_list.append(final_cas) + + print(final_list) + + + # 4) Create DOT & PNG + dot_lines = cass_root.to_dot() # Call to_dot() without extra arguments + with open("cass.dot", "w") as f: + f.write("\n".join(dot_lines)) + + subprocess.run(["dot", "-Tpng", "cass.dot", "-o", "cass.png"], check=True) + print("PNG saved as cass.png") + + +if __name__ == "__main__": + + drive_tree() diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/Execution.ipynb b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/Execution.ipynb new file mode 100644 index 00000000000..1bedd566155 --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/Execution.ipynb @@ -0,0 +1,446 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import MICSAS.interface as misim\n", + "from CASS_Driver import drive_tree" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Compute GNN feature vectors for each function" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['0,0,7,1\\t32\\tS#FS#1_0\\tI#compound_statement#{$$$$$$$$$$$}\\t11\\tI#expression_statement#$;\\t1\\tI#assignment_expression#$=$\\t2\\tVx\\tVthis\\tI#expression_statement#$;\\t1\\tVcass\\tI#expression_statement#$;\\t1\\tVlogi\\tI#expression_statement#$;\\t1\\tVwas\\tI#expression_statement#$;\\t1\\tVvery\\tI#expression_statement#$;\\t1\\tVhard\\tI#expression_statement#$;\\t1\\tVto\\tI#expression_statement#$;\\t1\\tVimplement\\tI#declaration#int$;\\t1\\tI#init_declarator#$=$\\t2\\tvtotal\\t-1\\t29\\tN25\\tI#expression_statement#$;\\t1\\tI#assignment_expression#$=$\\t2\\tVstring\\tI#binary_expression#$+$\\t2\\tVx\\tvtotal\\t22\\t-1\\tI#return_statement#return$;\\t1\\tVstring\\t']\n", + "SourceRange(start_line=0, start_column=0, end_line=7, end_column=1) [-4.1294155 -5.211907 3.0889378 5.3393927 -5.6841416 1.2497337\n", + " 2.4063818 -9.08663 2.2093687 1.892722 -1.1380143 1.1616006\n", + " -1.5419241 -0.20270327 -0.07288193 -3.4492426 -4.0413427 4.779633\n", + " -5.629128 -2.9557533 1.4416361 -2.5284693 1.2146845 4.0458913\n", + " -6.9593687 -2.7513788 3.2649064 3.2515032 4.074086 1.6168451\n", + " -1.8603535 0.88812 -3.513892 2.5080686 0.5179828 -1.248848\n", + " 1.1560155 -1.6043649 11.595149 6.441434 3.0664783 1.519422\n", + " 4.947104 4.967602 1.9220064 5.642183 -1.0840368 -2.3155382\n", + " -5.74603 0.59510493 6.7324905 -2.9115293 0.1455935 -3.4160588\n", + " -1.2186348 3.7370958 1.1361692 -5.390905 -2.1138291 2.8961957\n", + " -4.516396 3.8821197 4.369601 -4.262406 -5.8198633 -0.6802757\n", + " 3.0132234 -5.479684 3.283188 -0.25578684 2.431601 0.3447537\n", + " 0.38510352 3.028669 -7.568852 0.02665816 -1.3509308 2.4025087\n", + " -1.6505597 -5.4847302 -0.71817356 -2.939468 -0.6101805 -3.626529\n", + " 5.250762 -3.6420784 -3.227295 5.315017 -3.6513247 -0.9761998\n", + " -3.9514906 -4.2916794 -3.4346824 1.8359088 4.747256 2.0808563\n", + " 2.9665325 2.0699759 3.323561 1.0676523 0.31472018 1.4222972\n", + " -0.6805499 3.637668 0.08404966 0.8734891 0.6308584 -1.0322663\n", + " -6.1879883 -2.1322947 -2.051759 5.458273 -0.3508397 -0.430752\n", + " 1.4346448 4.4908223 -1.4062636 -5.8702455 -0.60570395 3.0688777\n", + " 7.723066 2.4687867 -2.4759264 -5.2423205 2.1638908 -3.9223175\n", + " -6.197274 -1.6294694 ]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "line 2:8 token recognition error at: '''\n", + "line 2:14 missing ';' at 'cass'\n", + "line 2:19 missing ';' at 'logi'\n", + "line 2:24 missing ';' at 'was'\n", + "line 2:28 missing ';' at 'very'\n", + "line 2:33 missing ';' at 'hard'\n", + "line 2:38 missing ';' at 'to'\n", + "line 2:50 token recognition error at: '''\n", + "line 2:41 missing ';' at 'implement'\n", + "line 3:4 missing ';' at 'int'\n", + "line 5:4 missing ';' at 'return'\n", + "line 8:0 missing ';' at '}'\n" + ] + } + ], + "source": [ + "cass_manager = misim.CASSManager()\n", + "gnn_preprocessor = misim.GNNPreprocessor('MICSAS/data/datasets/poj/dataset-gnn/vocab.pkl')\n", + "gnn_runner = misim.GNNRunner('MICSAS/data/datasets/poj/dataset-gnn/vocab.pkl', 'MICSAS/data/models/poj/gnn/model.pt')\n", + "\n", + "# Compute GNN feature vectors for each function/loop in a source file.\n", + "cass_strs = drive_tree(input_file='test_codes/input_code_8.c')\n", + "#cass_strs = ['1,0,10,1\\t34\\tS#FS#1_1\\tI#compound_statement#{$}\\t1\\tI#if_statement#if$$$\\t3\\tI#condition_clause#($)\\t1\\tI#binary_expression#$<$\\t2\\tvn\\t-1\\t15\\tN0\\tI#compound_statement#{$}\\t1\\tI#return_statement#return$;\\t1\\tN-1\\tI#else_clause#else$\\t1\\tI#if_statement#if$$$\\t3\\tI#condition_clause#($)\\t1\\tI#binary_expression#$||$\\t2\\tI#binary_expression#$==$\\t2\\tvn\\t5\\t18\\tN0\\tI#binary_expression#$==$\\t2\\tvn\\t15\\t27\\tN1\\tI#compound_statement#{$}\\t1\\tI#return_statement#return$;\\t1\\tN1\\tI#else_clause#else$\\t1\\tI#compound_statement#{$}\\t1\\tI#return_statement#return$;\\t1\\tI#binary_expression#$*$\\t2\\tvn\\t18\\t32\\tI#call_expression#$$\\t2\\tFfactorial\\tI#argument_list#($)\\t1\\tI#binary_expression#$-$\\t2\\tvn\\t27\\t-1\\tN1\\t']\n", + "#cass_strs = cass_manager.extract_cass_strs_from_src_file('input_code_ez.c', extract_loops=True)\n", + "casses, src_ranges = cass_manager.load_casses_from_strs(cass_strs)\n", + "inputs = gnn_preprocessor.preprocess_casses_seperated(casses)\n", + "vectors = gnn_runner.compute_code_vector_batched(inputs)\n", + "print (cass_strs)\n", + "for i in range(len(src_ranges)):\n", + " print(src_ranges[i], vectors[i])\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Compute code similarity between two source files.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.8808798\n" + ] + } + ], + "source": [ + "# Compute code similarity between two source files.\n", + "cass_strs_1 = drive_tree(input_file='test_codes/input_code_1.c')\n", + "cass_strs_2 = drive_tree(input_file='test_codes/input_code_2.c')\n", + "casses_1, _ = cass_manager.load_casses_from_strs(cass_strs_1)\n", + "casses_2, _ = cass_manager.load_casses_from_strs(cass_strs_2)\n", + "input_1 = gnn_preprocessor.preprocess_casses_combined(casses_1)\n", + "input_2 = gnn_preprocessor.preprocess_casses_combined(casses_2)\n", + "vectors = gnn_runner.compute_code_vector_batched([input_1, input_2])\n", + "from numpy.linalg import norm\n", + "similarity = (vectors[0] @ vectors[1].T) / (norm(vectors[0]) * norm(vectors[1]))\n", + "print(similarity)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABdEAAAHqCAYAAADrpwd3AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAh/NJREFUeJzt3QeYE+X2x/GzLF16L1IEEaQjVb2KBUFRFCtiAbmIHVFs4FWwI4qKAgKiYsULWMGCBRFRVJSiCNIUpCi9I83d/J/f639yk93MbnY3u9ny/TxPYDNJJu/MJJl3zpw5b0IgEAgYAAAAAAAAAABIpVDqSQAAAAAAAAAAQAiiAwAAAAAAAADggyA6AAAAAAAAAAA+CKIDAAAAAAAAAOCDIDoAAAAAAAAAAD4IogMAAAAAAAAA4IMgOgAAAAAAAAAAPgiiAwAAAAAAAADggyA6AAAAAAAAAAA+CKIjX6hbt65dddVVlpudcsop7gYAAHIv+hQAACCv9ie++OILS0hIcP/HyksvveTmuWbNmmzti2j+eh+9H5AbEURHtvr111/t2muvtXr16lnx4sWtTJkyduKJJ9rTTz9t+/fvt9wqOTnZXnnlFWvfvr1VqFDBSpcubcccc4z16tXLvv32W8sPHnnkEXv33Xejfv7YsWPt4osvttq1a7sdW24PMAAA8hf6FPmjT7Fu3Tq7//77rV27dla+fHmrVKmSOwj/7LPPsr2dAADQn8hbPvzwQ7vvvvvi3QzAKfzPf0DsffDBBy7oWqxYMffD3rRpUzt06JB99dVXdscdd9iSJUvsueees9zo5ptvtjFjxth5551nl19+uRUuXNiWL19uH330kdvZdujQIcPz/OSTTyy3HfBedNFF1r1796ieP3z4cNuzZ4876P3zzz+zvX0AAHjoU+SfPsV7773n+hR6bu/eve3vv/92QYEzzjjDXnzxRevTp0+OtBkAUPAUhP7EySef7E4GFC1aNGbvfeWVV9qll17q1lt2qlOnjmt7kSJFwoLoWm4C6cgNCKIjW6xevdr9yOpH8PPPP7fq1asHH7vxxhtt1apVbgeWG23atMmeffZZ69evX6od6MiRI23Lli2Zmm8sd2LxMHv27GAWeqlSpeLdHABAAUGfIn/1KU499VRbu3aty0D3XHfdddayZUsbMmQIQXQAQLYoKP2JQoUKuQz7WEpMTHS37KIT6sq0V/8m1m0HYolyLsgWjz32mO3du9deeOGFsJ2T5+ijj7YBAwaE/Wg++OCDVr9+fXd2U/XD7r77bjt48GDY6wKBgD300EN25JFHWsmSJd2BmM4WR7Jz50675ZZbrFatWm6eek9lPunHOb2dq95Hl3SlpABylSpVUtUG+/rrr23gwIFWuXJlO+KII+z8889PdWCcsmaYV6ts8uTJblmrVavmXnvuuee6S51TvlZnyefPn28nnHCClShRwo466igbN25cqjZqnQ0dOtQtr5Zby3/nnXeGrUu97759++zll192f0dTnkWdDT0PAICcRJ8if/UpmjRpEhZAF827a9eutn79enfVGwAAsVZQ+hORaqJ7+/6ffvrJOnbs6Nqp937zzTeDCXMqE6M+QcOGDVOVWItUEz0lZfTrZHjr1q2tbNmyrh9y0kkn2axZsyLWPR8xYoQ7AeCt36VLl6aqia7+hLLQveX0bloX2h7Kyk/pwIED7v1VsgeINTLRkS2mT5/uLinSwVk0rr76anfwpUuBb7vtNvvuu+9s2LBh9ssvv9g777wTfJ5+lLWD0oGWbgsWLLDOnTu7H+xQf/31l9s5bNiwwf14KoN67ty5NnjwYFeKRD/WaQWLZerUqe5SL+1g0tO/f39X11MHmvrh1/xvuukmdzCbnocfftjtCO666y7bvHmze22nTp1s0aJFbifm2bFjh1vmSy65xHr27GlTpkyx66+/3p2t/fe//+2eo52vDph1Odo111xjxx57rC1evNieeuopW7FiRbBe6auvvurWuUqz6HminRcAALkNfYqC0afYuHGjWz/RrCMAADKqoPUnUtK+/5xzznHZ+JqHxjzT36+//roL7OuqsMsuu8wef/xxt8w6Ca+669HavXu3Pf/8865foYx5nRTXCYsuXbrYvHnz3BVnoSZOnOgC3uo7KIiuOu8pTyZoPf3xxx/26aefuv6GR32dK664wp0Y2b59u3tt6HZWW/Q4EHMBIMZ27doV0EfrvPPOi+r5ixYtcs+/+uqrw6bffvvtbvrnn3/u7m/evDlQtGjRwNlnnx1ITk4OPu/uu+92z+vdu3dw2oMPPhg44ogjAitWrAib56BBgwKJiYmBtWvXptmmXr16uXmWL18+cP755wdGjBgR+OWXX1I9b+LEie55nTp1CmvTrbfe6t5n586dwWkd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VHHLdPbZZwd+//33sPnpdU2aNAn88MMPrh3Fixd3yzx69OhU7deyDR8+3D2/WLFigfLlywdat27t1tuuXbuCz1u2bFng5JNPdu+pdqRcfynpcT0v0k3rAQCA7ESfIn/0KYYOHerbn0i5nAAAxFp+70/49Ru0L09JbVRfISW9/sYbb0zVR1m9erVvX0RteOSRR9w81Wdo1apV4P3333frQNM8mofm9fjjj6d6X++x0PjC33//Hejfv3+gcuXKgYSEBPd4SjfccIObPmnSJN91CMRCgv7JvhA9AD9ffPGFO4OtM6YXXXRRms/VYCFbt27N8EAoAAAg/6NPAQAACioNLvrCCy+4sjclS5aMd3OQj1ETHQAAAAAAAECecuDAAVca7sILLySAjmxHTXQAAAAAAAAAeYJqwms8FtWT37ZtGwOVI0cQRAcAAAAAAACQJyxdutQuv/xyN5DoM888Yy1btox3k1AAUBMdAAAAAABk2JdffmmPP/64zZ8/3/7880975513rHv37umO4zBw4EBbsmSJ1apVy+655x676qqrcqzNAABkBjXRAQAAAABAhu3bt89atGhhY8aMier5q1evtrPPPtsNhrxo0SK75ZZb7Oqrr7aPP/4429sKAEBWkIkOAAAAAACyJCEhId1M9Lvuuss++OAD+/nnn4PTLr30Utu5c6fNmDEjh1oKAEDGURMdAAAAAABku2+++cY6deoUNq1Lly4uI93PwYMH3c2TnJxs27dvt4oVK7rAPQAAWaH88j179liNGjWsUKE0irYEMuGdd94JZPKlcRFNe8uWLRuYOHFizN5z6NCh7j11a9KkSSA3UJv69esX8bEdO3a4ts6aNSsQD3rvqVOn5sh7PfXUU4E6deoE4i3Wn7nMWL16deDEE08MFCtWzG0D3Y/15yIrvxfaTo8//niaz/noo48CLVq0CCQlJQWy4vfffw+cdNJJgZIlSwauuuqqLM0LQP7sT+R2AwYMCHTs2LFA9yfwD+3PvX6obt9//32B6FvFSjw+x3qv0G22ZcuWiM8bO3Zs4Jxzzsny+y1cuDBw3HHHuT7gfffdl+X5AaLPrvbtaWnQoEHgkUceCZv2wQcfuNf+9ddf6R5bc+PGjRs3btl1W7duXZr7sExlop911llu0JDMeumll+ymm26yvXv3Wm7UuHFj69q1q40YMSI4LSkpySpU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pb5fWp8LnVw644wzXH8hK9RfOP30061+/fpx//0DAAAFW54Loq9Zs8YFOWvVquXut2vXzt0Pvfx6+/btdumll7oyAbr17NnTduzYEfGSyTlz5rjMD2WeHXXUUS6glxFvvPGGnX322REfO/roo4OXZEa6/FABpLvvvtuVNNBBaZs2bWzVqlXBx73XysUXXxy8r3WQkUvUvdf5XX790EMPuY6uOu1PPPFE2GOzZ892neNffvklOO22226zFi1auKy5nKRM5CuvvNKtK2W0KCMllNZL9+7dXcC1evXqduONN9pff/0VfFzZYsp01rZu3ry5Pfzww8H16ylcuHBwfUW6hFfTtY4U5FCwQ516zTclTVu/fn2mllPbw2tDpLICSUlJdvPNN7vPtgI+r7/+etjjr776qgu0bN68OThNWYcKEAUCgUyve2UOa3k1WnEseN/Bk046yd1Xm1OWc0lvXSg7SZexKxNPQR0dfCqjPVSnTp2C84h0ybE+x9dff717rQ7OmjRp4jI1U9J61u+ODhgjzUfbRds85W9NtNL7vVAGqvdbpf/Xrl0bfEzbVQeqCm571q1bZ+XLl3e/USnptyCtS9S1fJHK6ni6dOnifmezcpm7Tpzo+wbEC/2J2PcncsLnn3/uls8L5p977rl26NAh95jWjdrXp08fd2Ik0v7j+++/d7+X6vfoN1/bTJm/Kel1L774otv+6gfp+W+99VZwe3tlqNSGjK4TXfXQsWPHVL/xmo/3uVF2ufoaKuGj/oZ+d0P7YhmhfVOkvko00tuH5ob+hPz4449unvpc1KlTx4YMGRJ2oje970h6fav0fPTRR26+oaXOtG9W+bOJEye6+7/++qudd955rnydfifUl07Zl41WVvqZHu3n09qm+qz7fa6j/Vzo+xmpH5IR9BcAAEBukeeC6DrY1aWO8+bNCx5k6P7bb78dfI6Cpz///HOwVMDixYtdxzgS1VrWZYZLlixxQVXVQ82Ir776ynXEI/n2229d29Qh9/P8889bv3793EGdDixUP9Cj1+omCgx6970D/mgoeKrXPPDAAxEff//9912pjFGjRrkAwCeffGJ79uwJPq6DPF0SfcUVV7j2qUM9btw4mzRpUtSX9erg0+8WGghMj7alguMLFy60yy67zHr06BE8KNQBtA4wdSJAnw0dlGid6lJSjz4DCix+/fXXLuj6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0sXatWsX7+YAAIA8ipPyAAAAAIB8GURfsmSJlS9f3t0AAAAyg5PyAAAAAIB8G0SvV69evJsAAADyOE7KAwAAAADybRAdAAAgqzgpDwAAAADI1wOLImd88cUXlpCQELxt3bo13k0CAAAAAOQSY8aMsbp161rx4sWtffv2Nm/evDSfP3LkSGvYsKErq1arVi279dZbXXk1AAByswIdRNeOfsSIEZbbKXj95ptvZjkQnpkA+AknnGB//vmnvfXWW5l+fwAAAABA/jN58mQbOHCgDR061BYsWGAtWrRwY41s3rw54vMnTZpkgwYNcs//5Zdf7IUXXnDzuPvuu3O87QAAZESBDqIjfUWLFrVq1apZhQoV4t0UAABypbxyUh4AgFh78sknrV+/ftanTx9r3LixjRs3zkqWLGkvvvhixOfPnTvXTjzxRLvsssvc/rNz587Ws2fPdLPXAQCItzwbRFdmtXbMl156qZUqVcoqVaoUzJZOSkqy+++/310apsdOPvlk+/HHH4Ov1c5ar//999/tjjvuCJYqUca2X/b3fffdZ02bNg1rgzftnXfesQYNGrjL19QhCM3+fuONN9xjZcqUsWuvvdaSk5MztIy6ycUXXxy8v2bNmuBzPv/8c2vVqpUVK1bM6tSpY6NHjw4+5rXh1FNPdfcrV67s7mv5Pb/++qudd955VrVqVdd+ZQ5Mnz7dMmPLli22fv36TL0WAAAAAJB3HDp0yObPn2+dOnUKTitUqJC7/8033/he6azXeEHz3377zT788EPr2rWr7/scPHjQdu/eHXbzjvu9m3ecrf8jTQ+dlnJ6YuL/bmYBNz102v+mBzIw3VJNL1Ton+kJCclRTdf9tKbrddFMZ5lYJpaJZcrvy5SU4jc+EAj4TtfN7/n5emDRBx980G644QZ74IEHwoK3mq7g9UsvveQCywq265KylStXWunSpe377793K6lt27b273//266//nr3usxkW2/atMmeeOIJ9x4KRH/yySdhj6sN7777rrtU7ZJLLrFu3brZOeecE9W8VUZFqlevbhMmTAi+TsFw2bZtmwuAaxn++9//2ldffWXXXXedHXvssXb66acHS7HobP+FF15oS5YsccuYmJgYfA+VeNF6uPfee91jms/555/v1tVRRx2VoXWhQP/s2bOj/vABABBvOrmsS8m1/37//ffdCeXx48e7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q1uxYsXsmGOOsQ8//DALSwQAQPajJjoAAAAAAAVErVq17Mcff7TJkye7/5WF3rdvX7v88svDBhqNhuYxcOBAGzdunAugK8O9S5cutnz5cqtSpUqq5x86dMjOOOMM99ibb75pNWvWtN9//93KlSsXwyUEACD2EgKBQCAb5gsAAAAAAHKRw4cPW6NGjez999+3Y489NsvzU+C8bdu2Nnr06GC9dQXp+/fvb4MGDUr1fAXbH3/8cVu2bJkVKVIkU++5e/duK1u2rO3atcvKlCmT5WXo1i3LswAAxMn06VmfR7T7Fcq5AAAAAABQAChwrRIusaCs8vnz51unTp2C0woVKuTuf/PNNxFfM23aNDv++ONdOZeqVata06ZN7ZFHHok40CkAALkJ5VwAAAAAACggFMBW7fPnn3/eChfOfEhg69atLvitYHgo3VemeSS//fabff755650jOqgr1q1ym644QaXIa+BTSM5ePCgu4VmDIre2wu+a3BUBfCVCR96sb03PWWQPnR6YuL/piclKc8wwRITw5//z3SzxMTkKKdrpoGw6WpWcnKiJSQkW6FCgXSnJycnWCBQyHd6oUJJlpBg6U5nmVgmlollys/LZJb6N16/7/qdjzT9n9eFtz3aIi0E0QEAAAAAKCC+//57mzlzpn3yySfWrFkzO+KII8Ief/vtt7PtvRW4UD305557zhITE61169ZuQFOVePELog8bNszuv//+VNOXLFlipUqVcn9XqFDBateubevXr7ft27cHn1OtWjV3W7Nmje3Zsyc4XSVnKlasaCtXrrQOHf6Xmb9kST3bubOMtW27NCygtHBhQzt4sKh16LA4rA3fftvMihU7ZK1aLQ8LJGl6uXJ7rEmT34LT9+8vbgsWNLIqVXbY0UevC07fubO0LVlS32rV2my1am0MTt+0qYKtWlXb6tdfb1Wr/m+Z1q2rZmvXVrNjj13j3sOzalUt27SporVsudJKlGCZWCaWiWUqGMtk9s9veehVVvXq1XNlWZYuXRoWSG/YsKEVLVrUFi8OX6Y6depYNKiJDgAAAABAAdGnT580H584cWLU5VxKlizpBgjt3r17cHrv3r1t586d9t5776V6TceOHV1Jmc8++yw47aOPPrKuXbu6bHMFN6LJRFcQXMFyr3ZtVjLRL7ww/2Rk5scsU5aJZWKZWKbkNJZp2rSsZ6Jr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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "# Paths to the two code files\n", + "file_1_path = \"test_codes/input_code_1.c\" # Update with your actual file path\n", + "file_2_path = \"test_codes/input_code_2.c\" # Update with your actual file path\n", + "\n", + "# Read the content of both files\n", + "with open(file_1_path, \"r\") as f1:\n", + " code_snippet_1 = f1.read()\n", + "\n", + "with open(file_2_path, \"r\") as f2:\n", + " code_snippet_2 = f2.read()\n", + "\n", + "# Create figure with 1 row, 3 columns (Two code snippets + Similarity bar)\n", + "fig, axes = plt.subplots(1, 3, figsize=(15, 5))\n", + "\n", + "# Display Code Snippet 1\n", + "axes[0].text(0, 1, code_snippet_1, fontsize=10, family='monospace', verticalalignment='top')\n", + "axes[0].axis(\"off\") # Hide axes\n", + "axes[0].set_title(\"Code Snippet 1\")\n", + "\n", + "# Display Code Snippet 2\n", + "axes[1].text(0, 1, code_snippet_2, fontsize=10, family='monospace', verticalalignment='top')\n", + "axes[1].axis(\"off\") # Hide axes\n", + "axes[1].set_title(\"Code Snippet 2\")\n", + "\n", + "# Plot Similarity Score\n", + "axes[2].bar([\"Code Similarity\"], [similarity], color='blue', alpha=0.7)\n", + "axes[2].set_ylim(0, 1)\n", + "axes[2].set_ylabel(\"Similarity Score\")\n", + "axes[2].set_title(\"Code Similarity\")\n", + "axes[2].grid(axis=\"y\", linestyle=\"--\", alpha=0.6)\n", + "\n", + "# Adjust layout to fit text properly\n", + "plt.tight_layout()\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.8952268\n" + ] + } + ], + "source": [ + "# Compute code similarity between two source files.\n", + "cass_strs_3 = drive_tree(input_file='test_codes/input_code_3.c')\n", + "cass_strs_4 = drive_tree(input_file='test_codes/input_code_4.c')\n", + "casses_3, _ = cass_manager.load_casses_from_strs(cass_strs_3)\n", + "casses_4, _ = cass_manager.load_casses_from_strs(cass_strs_4)\n", + "input_3 = gnn_preprocessor.preprocess_casses_combined(casses_3)\n", + "input_4 = gnn_preprocessor.preprocess_casses_combined(casses_4)\n", + "vectors = gnn_runner.compute_code_vector_batched([input_3, input_4])\n", + "from numpy.linalg import norm\n", + "similarity = (vectors[0] @ vectors[1].T) / (norm(vectors[0]) * norm(vectors[1]))\n", + "print(similarity)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABdEAAAHqCAYAAADrpwd3AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAe7FJREFUeJzt3QeYE1XXwPGzu/TeqwtI70U6vAgoChY6iog0EQtFFAHBAqK8WCgvSBelqKAUUQQVC0XpHRUUBKRL7yCwsJvvOddvYpLNLEk2u9ny/z1PIJlMZu5MdvfOnDlzbpjD4XAIAAAAAAAAAACIJTz2JAAAAAAAAAAAoAiiAwAAAAAAAABggyA6AAAAAAAAAAA2CKIDAAAAAAAAAGCDIDoAAAAAAAAAADYIogMAAAAAAAAAYIMgOgAAAAAAAAAANgiiAwAAAAAAAABggyA6AAAAAAAAAAA2CKIDXhQrVky6du0qSVmjRo3MAwAABA/HAAAApD6h6v9XrlwpYWFh5v9gmTlzplnmgQMHEvTYQZev69H1AakBQXQkK/v27ZOnnnpKihcvLhkyZJBs2bJJ/fr1Zdy4cXL16lVJqmJiYuTDDz+U2rVrS65cuSRr1qxSunRp6dy5s6xfv15SghEjRsgXX3zh8/yTJ0+Whx56SIoUKWI63qQesAAAhBbHACnjGODw4cMybNgwqVWrluTMmVPy5MljTup/+OGHBG8nACD5of9PXr7++mt57bXXQt0MIEGkSZjFAsH31VdfmaBr+vTpTcdTsWJFiYqKktWrV8uAAQNk586d8t5770lS9Oyzz8rEiROlZcuW0rFjR0mTJo3s3r1bvvnmG3MwUKdOHb+X+d1330lSO4Fu166dtGrVyqf53377bbl06ZI5iT527FiCtw8AkHxxDJByjgEWLVpkjgF03i5dusjNmzdNkOGee+6R6dOnS7du3RKlzQCApC819P933nmnuRiQLl26oK27U6dO8sgjj5j9lpCKFi1q2p42bVq3ILpuN4F0pEQE0ZEs7N+/33QC+kd6+fLlUrBgQed7vXr1kr1795oONik6ceKETJo0SXr06BGrgx87dqycOnUqoOUGs5MNhR9//NGZhZ4lS5ZQNwcAkERxDJCyjgEaN24shw4dMhnolqefflqqVq0qQ4YMIYgOAEhV/X94eLjJsA+miIgI80goegFcM+31eCTYbQeSMsq5IFl455135PLly/LBBx+4dZ6WkiVLSt++fd3+qL/xxhtSokQJc/VV65u99NJLcv36dbfPORwOGT58uNx2222SKVMmc2KnV7O9OX/+vDz33HMSGRlplqnr1Ewq7Txu1fnrevSWM08aQM6XL1+s2mVr1qyRfv36Sd68eSVz5szSunXrWCfanjXNrFpqc+fONdtaoEAB89kWLVqYW6c9P6tX8bds2SL16tWTjBkzyu233y5TpkyJ1UbdZ0OHDjXbq9ut2z9w4EC3fanrvXLlisyaNcs896U8ix4M6XwAAMSFY4CUdQxQoUIFtwC60mXff//9cuTIEXOXGgAAqaX/91YT3eqrf/nlF2nYsKFpp657wYIFzoQ0LROjfXiZMmVilUTzVhPdk2b068Xr6tWrS/bs2c1xQ4MGDWTFihVe656PGjXKXACw9u9vv/0Wqya69v+ahW5tp/XQfaHfh2ble7p27ZpZv5bsAZI6MtGRLCxevNjc8qQne7544oknzMmc3lr8wgsvyIYNG+TNN9+U33//XT7//HPnfNppaAeqJ2762Lp1q9x7772mQ3H1999/m87r6NGj5o+7ZlCvXbtWBg8ebEqRaGcSV7BYzZ8/39yKph3grfTp08fUCdUTV+2YdPm9e/c2J8e38t///td0VC+++KKcPHnSfLZJkyayfft208lazp07Z7b54Ycflg4dOsi8efPkmWeeMVeTH3/8cTOPHhzoCbjeLvfkk09KuXLl5Ndff5X//e9/8scffzjrn3700Udmn2tpFp1PaecKAEB8cQyQOo4Bjh8/bvaPL/sIAJDypbb+35P21Q8++KDJxtdl6Jhi+nz27NkmsK93cT366KMycuRIs8160Vzrrvvq4sWL8v7775vjAM2Y14vYesGiadOmsnHjRnOHmKsZM2aYgLf29RpE1zrvnhcTdD/99ddf8v3335vjA4semzz22GPmwsjZs2fNZ12/Z22Lvg8keQ4gibtw4YJDf1Rbtmzp0/zbt2838z/xxBNu0/v372+mL1++3Lw+efKkI126dI4HHnjAERMT45zvpZdeMvN16dLFOe2NN95wZM6c2fHHH3+4LXPQoEGOiIgIx6FDh+JsU+fOnc0yc+bM6WjdurVj1KhRjt9//z3WfDNmzDDzNWnSxK1Nzz//vFnP+fPnndMaNmxoHpYVK1aYzxYuXNhx8eJF5/R58+aZ6ePGjXP7rE4bPXq0c9r169cdVatWdeTLl88RFRVlpn300UeO8PBwx6pVq9zaOWXKFPP5NWvWOKfp/nHdZ/6Iz2cBACkXxwAp/xhA7dmzx5EhQwZHp06dAl4GACDlSE39v9WH6/+effWcOXOc03bt2mWmad+8fv165/Rvv/3WTNfjCM9jiv3799seO9y8edP0/67OnTvnyJ8/v+Pxxx93TtNl6LKyZctm9p8r6z3Xdffq1ctM87R7924zffLkyW7TW7Ro4ShWrJjb9wEkVZRzQZKnVyWVr1dVdSALpbdCu9Kr0cqqm6a3POnVZs34ci0rold1PekVZL21STPDTp8+7Xxodld0dLT89NNPcbZJr9pOmDDB3C6tV8H79+9vMrruvvtuc2Xbk17ddW2TrlvXc/DgwVtuvw644rqv9Kq03v5m7ReLDmziesuUZp/pa81c01u8re3WdpYtW9Ztu++66y7zvuetXgAABBPHACn/GEAz/TTDTjPl33rrraAsEwCQvKXG/t+TjhummecWLduSI0cOswwt5WKxnv/555/iD62Zbo2xohnlmiGuJXFq1KhhsvM9tW3b1pSaC1Tp0qVNWzWT3qLr1IFWdeBVSr0iOaCcC5K8bNmymf99rZGpJ5k6OIfWDHOl9UG107FOQq3/S5Uq5TafdgzaUbras2ePqUdm12noSWdctD06+Ik+zpw5Y+qdau1R7TC0Y1y1apXb/HqrmCurPXpL1614bo92RrovPOuhFSpUyNQ98+zYlM6ro4Xrduvtb4FuNwAA8cExQMo+BtAghO4Drauq+0PbBQBAauz/PWnNds/AstYO1/rsntN8PU7wpOVvRo8eLbt27ZIbN244p2vg35O3af7Si/1aok6/By15oxcqdL2dOnWK97KBxEAQHcmiA9WTqh07dvj1uWBeydQrs/fcc48ZTMsb68TTF7lz5zY1RvWhA4booCBWJ2KxG0lbB+RITLrdlSpVkjFjxnh937MDBwAgmDgGSNnHAFqDdcmSJSYrzcpwBwAgNfb/nuyOB4J1nPDxxx+bgUBbtWolAwYMMIOd6rK1jvy+fftize86tkqg9OLB888/b/p9HfRV26CZ75plDyQHBNGRLOiAGu+9956sW7dO6tatG+e82hFph6dXjvVWJ8uJEyfM6NpWR2X9r/PpgCWWU6dOxbqKqwNk6cjgeutWMGmHoR2oDkwSVwfqD90ez8507969UrlyZbfpOuDHlStX3DLRdKAwpSNnW9v9888/m1vObnVAwu1XAICEwDFAyjwG0BN2vdVdB2bTQc0AAHBF/5+wFixYYPbBwoUL3fpxHdg8PuI6JtABRR944AETRNcSLpqdH9cArUBSQ010JAt69VdP9HTEbe0IPemV0nHjxpnnOsK28vxjbGVS6R9tpZ1h2rRpZfz48W5Xbb39EX/44YdN5/3tt9/Gek87Za0dZuf48ePmNmVPWott2bJlXm87i48PP/zQ7bY37Ry1g77vvvvc5tM2T5061a09+lpvV6tevbpzu7Ve27Rp02Kt5+rVq+YE3KLfj+4LAACCiWOAlHcMMHLkSBk1apTJQuvbt6/f2wkASPno/xOWldHuuh82bNhgtjk+rAv0dscFWrpF941eTNc2uNZ9B5I6MtGRLOhV4Dlz5kj79u3NlWWtpVWxYkXTCa1du9bU0tJbkVSVKlWkS5cu5qq1/uFu2LChbNy40dT70luVGjdubObTE0Ud3ENvV9Kr3Nrxbtu2zdQoy5Mnj9v69Q/8l19+aebT9egJpp48/vrrr+YEVeuHen7GcuTIEalVq5a5TVmzubQum9ZP++STT0yGlw5iYvfZQOjV3f/85z/SrVs3c7ChBwTaQest06709ri3337btF1vRZs7d65s377d7Dc9sLA6uHnz5snTTz9tBhCrX7++qV+qNdN0uh5Q6JV0pftEB2rRAxVdttZMcx3wxNPixYvN9iutg6b15oYPH25e621unllzAIDUiWOAlHUMoIOraWBE69Hq96m3crvSW+fz588ftH0CAEie6P8Tlm6XZqG3bt3aXGTYv3+/qdlevnx5k4EfKOti/LPPPitNmzaNFSjXdWl5G/3+9CK/lpEBkg0HkIz88ccfjh49ejiKFSvmSJcunSNr1qyO+vXrO8aPH++4du2ac74bN244hg0b5rj99tsdadOmdURGRjoGDx7sNo+Kjo428xUsWNCRMWNGR6NGjRw7duxwFC1a1NGlSxe3eS9dumSWUbJkSbPuPHnyOOrVq+cYNWqUIyoqyrbNFy9edIwbN87RtGlTx2233Wbao+2uW7euY9q0aY6YmBjnvDNmzNDLwI5Nmza5LWPFihVmuv5vadiwoXl4zvPJJ5+YdubLl89s0wMPPOA4ePCg2/L0cxUqVHBs3rzZtCNDhgxmmydMmBCr/bptb7/9tpk/ffr0jpw5czqqV69u9tuFCxec8+3atctx5513mnVqOzz3nyd9X+fz9tD9AACAK44BUsYxwNChQ237f8/tBAAgpff/dv289r2etI3at3vSz/fq1SvWMcX+/fttjx20DSNGjDDL1D6+WrVqjiVLlph9oNMsugxd1siRI2Ot13rP9fz95s2bjj59+jjy5s3rCAsLM+976tmzp5k+Z84c230IJEVh+k+oA/kA4m/lypXmCrte0W3Xrl2c8+pgJqdPn/Z7oBYAAJD0cAwAAACSCx1c9IMPPjBlbzJlyhTq5gA+oyY6AAAAAAAAgAR17do1U8qtbdu2BNCR7FATHQAAAAAAAECC0JrwOn6K1pM/c+YMA4sjWSKIDgAAAAAAACBB/Pbbb9KxY0czkOi7774rVatWDXWTAL9REx0AAAAAgCTop59+kpEjR8qWLVvk2LFj8vnnn0urVq1uOU5Cv379ZOfOnRIZGSmvvPKKdO3aNdHaDABASkRNdAAAAAAAkqArV65IlSpVZOLEiT7Nv3//fnnggQfMYMPbt2+X5557Tp544gn59ttvE7ytAACkZGSiAwAAAACQxIWFhd0yE/3FF1+Ur776Snbs2OGc9sgjj8j58+dl6dKlidRSAABSHmqiAwAAAACQAqxbt06aNGniNq1p06YmI93O9evXzcMSExMjZ8+eldy5c5vAPQAAKZnml1+6dEkKFSok4eHhwQ2if/HFF9K6dWuzEgROD0jmz58v7dq1k6Rgz549cuedd8off/whWbNmjfW+ZjzkyJFDZs6cGZL2pTZ6oKu3YGpNw5RAt6VatWrmFtNixYqZaadPn5by5cvL1q1b5bbbbgt42Tdv3pRu3bqZrBv9Gf3zzz+D2HIAFvr/4KD/R1zo/31H/w/Edvz4ccmfP7/bNH198eJFuXr1qmTMmDHWZ958800ZNmxYIrYSAICk5/Dhw3EemwYURL/vvvvMoCaB0pOw3r17y+XLlyUp0vbpAXnfvn1l7NixZpqeyGhduVOnTkmePHkkJRo8eLD06tXL6wm0tV/ik4nQqFEjqVixokyYMEESig6YoydmS5YsSbB1vPbaa7JgwQK3WyQTwhtvvCE3btxI0vsivvR3qXPnzjJ06FD54IMPAl6O/n5+/PHHsnDhQqlXr15Q2wjgX/T/9P+BoP/3D/2/7+j/geD1AzoQqeXChQtSpEgROXDggGTLls1M035As/M0S931Yro1PTo62m2ZdtN1mr7nbbrS5fsyPSIiwrTD23TPNtpNZ5vYJraJbWKb2CalF5o12cPufCheQfT06dNLgQIFJCXTHfrhhx/KiBEjJFOmTJLSHTp0SBYtWiT/+9//bOfRDB8knlv98qYUXbp0kZo1a8rIkSMlV65cAS3jr7/+Mn+XNEMWQMKh/0956P+THvp/39H/A7FpP33ixAm3afpag+HestCV/h7pw1POnDmdQXQAAFIqDc6rWyUO2Rd68WL16tVmgdbDWzaITv/kk0+kVKlSpsN96qmnnFcUrEwmzfLSUcat5WiGkr+3hTZs2NCcZOhBt9Z809vWLLo8zXTzbJdm5Sh9rtkv2j7N9mnQoIHJiHEdaEUPMHQUdN0WuyyfBx980Plar9Lrcjdv3mxe6xWMDh06mAOPZ599Vtq2bWtOQj2zbX7++WepVKmSZMiQQZo1axbrgEdv99bsLW1PhQoVZN68ebHaouudPn26GTAmS5YsZls+++wz857WtnvmmWfMwZS1DF2mJ12uricyMjLWezqau/Vd6XZ72xc6Anz//v0le/bsUrRoUbfsJ/0+9LM//vijGVXeWpa/t4VrBluJEiXMvtJ1vPzyy25t0GXOmjXL3NJrrUOzxiwfffSR1KhRw/zcaDv1NvqjR4+6rcP6WVm1apXJaNJ13X777eYWZF2Wvqe3Ou7cuTPOfRIfw4cPj/N3Q9uh39WoUaMkb9685rudNm2aX/vCFytWrJDatWubfaC/z+PHj3d7X5c5evRo+c9//iOZM2eWu+++22RqWvR3/NFHHzVBqOLFi8sPP/zgdT3681+wYEGTRRYo/RuTJg1DPAAJhf7/X/T/7vuC/j946P/9R/8PxFa3bl1ZtmyZ27Tvv//eTAcAAPHg8ENUVJTj2LFjjhkzZmh+fKz3V6xYYabfe++9jh07djjmz5/vCAsLcyxevNi8//fff5vPjx071pEpUybzXB9nzpzxpxmOihUrOtq2bevYvXu3Y+fOnY7x48c7Dh065Hy/YcOGjl69esVq16lTp8xrfT5mzBjHu+++a57PnTvXMXDgQEfTpk3N+7p9mTNndsybN89RvXp1r8vo0qWL44EHHnCuY//+/eb9TZs2mddFixZ1PP/8846FCxea6aNGjTLtLFOmjPMzOj1XrlyOr776yrF9+3azrnbt2jnfX758uWnHhx9+6Ni3b59jzpw5jgwZMjjWr1/vtj90OcWKFXO88847Zp8sW7bMPJSuN2/evI4ff/zR8eeffzqWLFni+Oyzz2Lt0xYtWjieeOIJr/v7woUL5ntq1qyZ2W5POi1jxoyO//73v44//vjD0alTJ0f+/PkdN27cMO/r96ufr1u3rqNbt27O711/Hny1efNms50zZ850HDhwwLF69Wrz/VnOnz9vlvnwww87mjRp4lzHpUuXnPPovvjkk0/MPtq2bZujQYMGjrvuusttPdb3fMcdd5jvf+/evY7Zs2ebdeqydJkvvPCC+R6tdei6g+ny5ctmuT169DA/y56GDh1q9nfv3r3N/n7xxRcd6dOnd/5s+rIvbkX3kf6s6e+q7gP9Gc2dO7f5XbHofipevLjZZ/pzX7hwYbNvLPpcfw/WrFnjWLVqlfkZ1c/o74onbetjjz3mCNTkyZMdOXLkCPjzAOJG/0//T/9P/2+h/wcSl/4O698ufVj9mD4/ePCgeX/QoEHm769F/+ZrXztgwADH77//7pg4caIjIiLCsXTpUp/XqX//dV36PwAAKd0FH/s9v4Lols8//zzOk2g9YLZUrlzZMWTIELf5rJPUQGXJksUcDNjx5SRaDyj0oc+vXr3q+OabbxzlypVza58GDQoUKODYsGFDQCfRukxdtuv69OTHotP79+/vfP3tt986wsPDHWfPnjWvGzVqZA6KXD3yyCOOp556ym2aLqdz585e94WeaNWsWfOW+7RKlSqxvidPLVu2tD2JLlmypPP11q1bTZv0AC6u78UfCxYsMAd/t/qB9vxe4vLll1+aIM+1a9ec06zvefr06baf05PYChUqOBJa3759bU+i9efTarf+vGibNegS6L7w1LVrV/Oz5kp/Fq1Ak9J1vvHGG87Xzz77rOPOO+90vs6ZM6dj0qRJztf63O4kWgNO//nPfwJqqwZjHnzwQUedOnUC+jwA39H/0/+7ov9PGPT/vqH/R2ph/X3yfFh/l/V/z78Z+pmqVas60qVLZy56af/mD4LoAIDU5IKP/V6C3P9YsmRJ53O9nfns2bNBXb7envzcc8/J4sWLpU6dOvLQQw9J+fLl/VqG3qLq+lwfOlq5q7Rp05pbmSdPnmzqNvrLWq7r+jzXobflWvRWa70tVW8N1/32yy+/yLp169xuo42KivJ6i6/eku5Nx44dTW1XXY9+Tm+5bdWqVazb8bVdrvvEX3qbtUXbrvR711uhg0HbresoW7as3HPPPeYWYr193Z+6odu2bZMhQ4aYW+jPnTsnN2/eNIMO6G3HnjUA7fZnUqGjBVttdt3fwaI/e7/++qspD2DRQc70Nnpfftd1/+rD9fdSb9u24+33zxeTJk2SPn36SO7cuW1vFweQeOj/3Zfruj76/8DQ/7uj//8H/T9SE/0b7jmwmitvJbL0M/q3DwAABI9fNdF95VmbMK5OPxDvvPOOORHSOpxaw7Jq1aqmXqvF8wTRc5RXO97a+eSTT8qCBQvMCYGrQNfhC9dlax1LrQFrPX777TeZMWOGz4N+aZDh4MGDMnToUHPSqCfVPXv2jDWf1lH13EZ/eKtHGczvXbdPT+o0IKB1W3W/1K9f32yTL/RE+d577zX1ObX+qx5U6gmY3XeX1AdRS+j9rbSesevPntaB1XqKcbUj0DboybfWd/WX/jyvWbPG/Ey8+uqrAa0bQPDQ/8cP/X9s9P/u6P//Qf8PAACAxBaSkXjSpUvn88mPnXLlypmHDiCmGVw6gJJmJ1knQJcvX3bOe/jw4YDXowfnmgXleeKq69DBpuK7Dj0xcX2uI8LqoGRW5s6ff/7plu0TCG2rZuvpQwd4+t///mey61xVq1bNnKAn5e9dP6+DyOmjRYsWZtCrffv2SZkyZW65jl27dpmB5d5++23n/g10IKtg/Pwmhvi0U3/2du/eHfDPnmal6aB/+jOlgwCqHTt22M6v71nz+UMHiNNAkQ5W6O/AaQASH/3/v+j//fs8/b/v6P8BAACAEGeia7bI8ePH5fz58+a1PteH6wmrL/TA/Pr167Jo0SJzC6feouwrnbdXr17y448/mgyrL774wpzMut4qWr16dVm+fLlp78WLF2XKlCkSH5q5pSfprnQdmzZtMrde67aMHTs2oGXryfk333xjMutefvlladu2rTMLSm891tvzRo0aJX/88YdZ3xtvvCEff/yxz8sfN26cfPrpp+bzug69Bd7bbbXNmjUzt457nnRFR0c7v2fdTv2+rNf6nr/f+08//SSHDh2Sa9eu+fX5r7/+2myLboOeOM+aNcucQBUpUiTWOrZs2WJOmnUd1vbofHr787Rp08zPi2YXTpw40a/2u65Dl7FhwwazDr3N2VPXrl1NRqH+fPjL2r9///23+Xm3XvvzexLXvvDF4MGDze/YoEGDzImw7nf9GdcAjK+0FIIGLfTnSrPFRo4caZslqO3Un8FA6W3nuo0AEgb9/7/o/+n/6f/jRv8PAAAASe1B9DZt2kjBggVN1ofS5/rQkzx/1KpVS55//nnp0aOHub1Wb7P1ucHh4ea2406dOknp0qXNcvTk89FHH3XOo9lpWgNUs64aN24srVu3lvjQGpy6LFcPP/ywqS2qt5LrCbVmRwVa33XAgAEmq0pvZ3Wtf3rXXXfJnDlzZPbs2ebEV29f37hxo1vmlS8nF3oio5lmujxdxwcffBBrvvvuu898F56362qGnfU9L1261NwKbb32N/tOt1NvG9cMwowZM8pHH33kV2bT559/bur7ValSxdyOrQEBXY4r/ZnSny996HvDhw8303W79cRbAwpap3PChAny+uuvSyD050mz+nSf6Tp0nd5ODLXOZyC3hVv7V0/49QTUer127Vq/lmO3L3yhP2P6fa9atcr8fOvvke5vf2oPawkBveVeMzkfe+wxr2UElAbTNDswPnVoNYMzmCUVALij//8X/T/9P/1/3Oj/AQAAkBKF6eiioW4EkoapU6fKZ599Jt99912om5LsFShQwNTrHD16dKibkuRpAKlv375ugTB/afBHg3E6IFpcA5gBAGKj/w8e+n/f0f8DSZfezaV3/Vy4cEGyZcsW6uYAAJAk+r0EGVgUyZPefquZXpcuXQp1U5I1rW2rt2LrLdGIm9ap1RIGHTp0iNdytJ6q1katXLmyyXQEAPiO/j846P99R/8PAACA5IZMdAAp6qRcAxietXIBAEDKRf8PBBeZ6ACA1OSij/1emkRtFQAkIK25CwAAUhf6fwAAACQ0yrkAAAAAAAAAAGCDIDoAAAAAAAAAACkxiF61alV57bXXQt0MAACQiOj/AQAAAACJKVkH0ZctWyb9+/cP+PPFihWTUaNGSWo3f/58KVWqlGTIkEHq1asnO3fuDHWTAACwRf8fHPT/AAAAAJAKgui5c+eWLFmyhLoZydovv/wiHTp0kO7du8uWLVskMjJSHnjgAbl+/XqomwYAgFf0//FH/w8AAAAAKTyI3qRJEwkLCzMPb7dzN2rUSHr06CFdunSRzJkzS9myZWXDhg1uGWj62YMHD8qAAQOcy1q5cqXPbXA4HPLqq6+ak07N4NJMrgkTJjjf12XpMk+fPu3Wrt69e5vn2u6GDRtK6dKlpVy5cjJu3DjJnj27OYGNjo6WxPL+++9LtWrVZNCgQVKhQgV577335OjRo/LNN9+4zadtOnLkiJw7dy7R2gYAgCv6/+Ch/wcAAACAFB5E19uPjx07Zk767HzyySfmpHXbtm3mRLdXr17O9zZt2mQ+f9ttt8mQIUPMc33orcy+WrhwobkVfPLkybJr1y6ZOnWq31lxerK6aNEiiYmJMdu0Zs0aWbt2rWmfL3T7dZ3eHqtWrfJpGZp9Vr9+fedrPZGvXLmybN682W2+w4cPm/34/PPP+7WNAAAEC/3/P+j/AQAAACBxpZFkKGfOnOb/NGnsm1+nTh3p1q2bed6zZ09p166d3Lx503wmb968ZnpERIRkzZpVChQo4HcbDhw4YNrRrFkzs0zNbvNXjRo1TBaaZoLp5ytWrChlypSRQ4cOmfbfytdffy03btzw+l7hwoV9asOpU6ckT548Jijw1FNPydatW81rnQ4AQFJC//8P+n8AAAAASFzJMojui5IlSzqf68muZntduHDB1FENhtatW5tMNL0d++677za3Zj/88MOSLl06n5eht4Fb/7s+v3r1qk+fL1q0qARLtmzZzPLSp0/v9X09yddb2AEASMro//1D/w8AAAAAKbSciy+8ZakF8ySwePHism/fPlPLVE9A+/TpIy1btnS+r/VQPemJvC98bWcwbufWrDyt26p1ZvUW7nz58pnXVrYeAADJCf0//T8AAAAABFuKzUT3hWaN6S3egcqUKZM0b97cPGrXri3t27eXa9eumWyyHDlymHkuX75sbo+26opqvdFgCcbt3NWrVze1WC2arffLL7+YQdMAAEiJ6P/p/wEAAAAgRWeiR0VFyfHjx81DT4D1JNV6Hcgt3999952cPHnSnPz6mimmPvroI/nggw/kt99+MwOLzZ07V0qVKuW8LVufa4aaDhhmDXSmtU6DSW+/1m3w9siYMaNPy+jevbsZfO2tt96SnTt3ypNPPmlOwO+7775Y82p2nQ7WBgBAYqP//xf9PwAAAAAkrmQXRF+7dq0ULFjQPPSkb/To0c7X/ho+fLjJvCpSpIg56fzpp598/mz27Nll2rRpZgAwzUK7dOmSGZzLNUtt0qRJMnbsWClUqJCsW7dO6tevL0lNlSpVzAn++++/L3fccYfJlluyZEms2qgarFCB7GcAAOKL/j+46P8BAAAAwHdhDkaLgg/0xFprvm7fvl0qVaoU6uYAAIBEQP8PAKnPxYsXzUVjveCsd1cBAJCS+drvJbtMdITG999/L48++ign0AAApCL0/wAAAABAJjoAAAAAAPh/ZKIDAFKTi2SiAwAAAAAAAAAQPwTRAQAAAAAAAABIiUH0jz/+WIoUKSLh4eHSqFGjkLVj5syZcu+999q+nyNHDjNPKI0dO1aKFSsW0jakJl988YWEhYVJavLcc88l2O9hQi5bDRo0SPr06RPv5fzwww9Svnx5SZcuXch/54GUrGrVqvLaa6+FtA2p8e88AAAAAKRWybYm+s2bN01w+sUXX5Tu3btLhgwZJFeuXInejuvXr0vx4sVl7ty58p///MfrPCdPnpSsWbNKxowZA1qHnqTPnz9f2rVrF3A7//77b7ly5YrkzZtXkioNOvbu3VsuX76cYOtYuXKlNG7cWE6dOiV58uRJ0J+Lc+fOSYECBSS10ED39u3bzT4OtkuXLsmNGzcS7Hf89OnT5vdY26//B0r/Bujv+ZQpU6RQoUIB/84DiNuZM2ckffr0kiVLloA+rxeVtb/p379/iv47nxh93oEDB+T222+XTZs2SY0aNRJkHQCAxEVNdABAanIxpddEP378uAkKN2vWzASrQhFAVwsWLJDMmTPbBtBVvnz5Qh5My5QpU5IOoKc0GtxJyoGV5EYvQiXk77gGl+655x6ZPHlyvJbz119/yd133y0lSpQI+e88kJLlzp074AB6sPB3HgAAAABSj2QXRNeMJ83MjoyMNK9r1aplXruWejh79qw88sgj5uqBPjp06GCyxTyzs/Rzq1atknr16plMds2k2r9/v1/t+eSTT+SBBx7w+l7JkiXNOvThrbSDZsK99NJL8uCDD5ogt2Zw7d271/m+9Vn10EMPOV/rPvCn5I31uVCWc1m+fLnZPiuY36JFC4mKijLv6b7R9nXr1s1cGLHa6/qdaoabBjk12KnBSf3O1q5dG2s9+rnp06eb718DLDr/Z5995vy+NSNPaRvs9olegTpy5IjJMvTX6tWrne0P1m3++/btM3dcjBgxQoIlOjpahg0bZn6PdD/deeed8vPPPzvf1+9Ep8+ePdvMowErz9IJ69evl8qVK5vfnfvvv1/Onz/vdV2aAan7MxDDhw/3+vPgSb/fW/186/txZcnrz6T+PsdHTEyMpEmTJl7LAGCvSZMmzr8J3sq56N+JHj16SJcuXcwF7rJly8qGDRvc/g7oZw8ePCgDBgxwLsufO2gS4u98IPTOmYYNGzovMuq+0QQD5Uufp31Ly5YtJX/+/ObveJUqVWTx4sWx1qOfefPNN03mvmZn6GP8+PHO4zE9dlI1a9a03Sd6N5z2A/o/AAAAACRHyS6IrgG9Y8eOycaNG83rpUuXmtcLFy50ztOrVy/ZsWOHqU+sj19//VWeffZZ2xIUzz//vOzcuVP++9//mvrq/tCTabvblzXIqG2L61aA999/35zwa5BYy1VobWaLflYfatq0ac7X1gUEX2gJGP3M66+/LoG47777TDDV20MDrL4GbNu2bSt169Z1fi/6XAOOqn379qaNWrddg+zWdrp+pydOnDAXG6zv84477jCBWy3z4emNN96Q6tWry9atW2XevHmSM2dOE3TXZWpAXen3ra91v3saM2aM2cfr1q3ze3/Vrl3bLHfGjBkS33JF2la9cKBBIN1vGhzSiz5230eFChV8Xr7uozlz5phguQZi9E6Kpk2buu3Pa9euyVdffWX2uQarNOiu+17pBRD9TvV70M9rO/WCjTd6Acifn1lX+rup+1N/RxKaXpA7evSoCSwFSvdZ2rRpg9ouAP/S0mb6NyGuv3d6MUz/Xm7bts387dFjAov+zdfP33bbbTJkyBBnf6N9RGL9nX/66adt/477c7G0U6dOJji+ZcsWcyzSqlUrcxyhfOnztIyVBr717/xvv/1m+uLWrVt7TSaYNGmS6Z/1uObbb781+9XueMw6bnGlfbHOr/8DAAAAQHKU7FImIyIizO3TGqxSmiHreju1ZsPqSZoO+KVBMfXWW2+Zk8sJEyaYDCpXmlmlQT6lJRj8oevSejlaTsYbq/5oXJlqmv2qmWCqa9euJpBscd0urf8eyG3jml2mn9NMtUBokP/q1ate39PsNV/3kz40IG/Vm9aMN4tmlutDvxvdV962UwPorjQrbuLEiSbQ7Tmoq2ZVa4ahKl26tHO6LtcqCaIldhKiPqwGUHU9+n0F4tChQ/Lee+/JBx98YLIoNTtfA92FCxc27+t3oUFru3X7Qn933nnnHfn8889N6RGlgRtdjwZTNIvfuvih82mwqUyZMvLqq6/K5s2bpVKlSiZYogEY/XnVbdVA/6effirBpvtAHxq8SWjWPtbsSn//FqiffvrJjH+g+wJAwtCLoiquOz7q1Klj/naqnj17movJemFSP2OVNdNjCe0XA+lX4/t3Xi9q29Vi96dslf6teuaZZ5z9nA5qbNHBjW/V5+nFAH1Y9M640aNHy/fffy9PPvmk27zad2t/4Cmu4zEAAAAASEmSXRD9VjSDSjOcNdBn0YCtBgT1vapVq7rN36BBg4DXZQWXNVAdKC354hoc0FI0SYkVWIwPPbHWCxX60IC3Bjg0UFu0aFGfl6HByVdeeUVWrFhhstKtLHZvg5DG5ztVmnXtrUxAYtDMSM3wf/vtt00WtucFGL3Y4PozE4g9e/aYn902bdq4LV+n/fnnn271fjWAbtGAkfXzqdnaevHINYikv3Ou5YgsCTHQqNKsfL0wo/T3W8vvWDWS9WdLMy81W1XLNigtI6Dza/BMffPNN24/K9bvsd1Fo7ho4Eh/Lp966imT0Q8gdDz7Ve0v9IK39kVJgQa09RFfGkDXu+m0BIv2q9rHugbSb0XLp2lft2TJEpM9rhca9O9kQvSrmiSgDwAAAABIrpJdOZdgCzSTTOkJuQYhPeut+8Mzm87hcEhSEoxyLkrvDtBM3fr165vnFStW9BpwtaP1bfWWcc0+12xo6/ZxK5gerO801F5++WWTOanlVrRUyrvvvitnzpxxvh+sci5KAyea1W49du/ebcoMWLxleialn08to2S1Xe+Y0KC+9frrr7828+j/1jR9X+ezXnuWYbIuEAQyAK/+bI8bN86UXdKfTwChk9T/dgWrnItmhutYFjoui16s1CQBLeviK82G1/5Y79bTMi36d1GPa1JavwoAAAAAwZDiMtF1gCuta661m60BtPQkU6dZg18Fi94urVlfWktU63MnFL11XDPEQiEY5VwsWqdcH3379jWBymXLlrllDOr+tNtODQzoQGZW6RYriO4vXYcK1f68lVKlSplgrAY15s6dK1OmTDGlabTsz8CBA52B4/iUc9F1aNa1Zh5ag875S7+3v/76y2R3WiWStG57fO7K8JdrVr4OWKeBM88sfde7HfR9vbPCLpNf26/70PUuFl9pOQV9aJkGDUbZjZMAIGmIq79JaMEq56LKlStnHlqaTi+kakkuHePClz5P+1Ute2OVlNNBSV0v2qaUfhUAAAAAgiHFZaJrtpTWPx08eLAJtOpDB+vU8iGe9dCDoVmzZiY72JMGnvWEVB+aAafBRn1+6tQpv9ehQT+9XVsz3rX2qD8Zdbo+Xa8OGKklL6w2+Vqywgo6env4Wmdd63zrd6D1y7W0xocffigXL16MFazUZWpJjkWLFpn26eCVFg1QalD5jz/+kDVr1ngtdeLPRZYFCxaYW9l1fZ60NrguW//3l2Yz6/7VGvDK2t/ebo+/VYBYb33XgKz+DOtFB80ctwLH3h6+lsfRQLcG5l944QUzSJ+WcNEsRi0NoIFkX3/utRyBXhDRDHYN/NtlYOt26P7U+r3+svaflhjQnwfrtevPRrBoNrmWLIhP/XXNJLXqAwMILte/ARqw1b+r1mt/6d/M7777zpQK099Zb9nXCfV3Xv922v0d9zWIrvtCB0z98ccfTb+q48BoyTrPfjWuPk/7VT220KQDHYi7c+fOAV0I1Qvq+rdPx9nQvt3b30DtY7QfCFWpNAAAAACIrxQXRFda8kMzxHXQRH1odpZmMSeEHj16mBNx62TaogHfggULmoeeVGrdUn1es2ZNv9ehgzfqCa6eeGsQ1arx7Atdn65Xa21rtq7VJm1fYtGgpAZatQa3DlA5cuRImTp1qtSrV89tPh0IVoPjuk/1M64Dhk6fPt0ELrS+/RNPPGG2RwMD/tJt13rjOjCpXgTQ9niyAiE6r790G/Vz1qB21v4eNWqUBEq3edKkSTJs2DAJlqFDh5qyMZrdrgNharkcDXz4WqdXM7Y1KKM/l9o+HWj0scce8zqv7k8NzARSDsDaf1omRS/CWK/Xrl3rNl+jRo1uGaTX93U+Ozowqv7sxYfWW/cnGAfAd/p7b/0N0DEPdBBM67W/hg8fbi5uFylSxPSrehEtlH/n/aX9n15Y79SpkwmGa9+p5cAeffRRn/u8MWPGmL/LOriobpP+DbcbKP1Wf/cmT55sSsNoDXrdn8HsVwEAAAAgKQhzJKVCoclUhw4dTPbXSy+9FOqmIAj0TgbNng+0ZAz+pX9eNEtRg/R68SSp0trpWl5BMzKtgUcDoTX/dTDWOXPmxGs5AJCSWPXX9W6yxCz7BQAIjCaB6V3cesE5W7ZsoW4OAABJot9LcTXRQ0GzvLQECZI/zSJevny5KXOC+NP67Vp2QMsrJWVaLmbGjBnxDnz36dNHHn/8cUmfPr3MmjVLOnbsGLQ2AkBy9f3335s7oAigA0Dq1bx5qFsAAEhJFi9O/HWSiQ4AQaQXDXTQVq1t7Ou4AQAAAEBKzkQniA4ASKpBdDLRASAE0qVL5/MgrwAAAAAAAEj6UuTAogAAAAAAAAAABANBdPisVatW0rVr11A3AwAArxo1aiS9e/e+5Xznz5+XsLAwWblypdt0fa3Trcfp06cloWm/OmLECNtxJbQdBw4ckNRi7NixUqxYMbdpS5culapVq5pxS+JDBw2/8847JXPmzNKtW7d4thQAAABAakI5F/hs5syZ5mQeAIDkTOvdWWMXuKpXr56ZvnbtWmnbtm2Ct+OXX36RxYsXy8GDB72+X7FiRdOevHnzSmrWrFkzeeWVV2T27NnSqVOngJfz3nvvyZ49e2T16tVSvHjxoLYRAAAAQMpGJjp8liNHDhN4AAAgOdMLwgUKFDBjGLjS1zrdM7ieUMaPHy9t2rSRLFmyeH0/TZo0pj0RERGS2nXp0kXefffdeC3jr7/+kipVqki1atU4ngEAAADgF4LoAXI4HPLqq69KZGSkZMiQQUqVKiUTJkyIdUu4663grreZv/baa9KwYUMpXbq0lCtXTsaNG2dO6B544AGJjo72uz1667PnbenB8sQTTzhvbY+rnMupU6fkyJEjCdIGAEDq9cEHH0j58uW9vqelUBo3bux8ff36dRNw1ZIdZcuWlQ0bNsQKTFt9WqD95qRJk6RkyZKSKVMmqVGjhqxYscLvZWhfP3/+fHnwwQdjvad9qWtZmWCUc9GM9uHDh0vfvn0lmOLaF9ax0CeffGKOk3Sk+6eeesqtLMsff/xh7gDQYyn9X0uueNOiRQvZvHmz7N27N+C26nr1+wcAAAAAfxFED9DChQtl1KhRMnnyZNm1a5dMnTrVNpPMztGjR2XRokXmpE5PpNesWWNuId+0aVNQ2qiBBW2Tt8fTTz/t83LGjBljTr71duq4PPTQQ+aiAgAAwVS3bl3ZvXu3XL58OdZ72mfq+xYN2OpF623btpk+qVevXrH6Xl1WfEqbDRs2TEaPHi07duwwAXu9AG4X/I2rlMuFCxdM4NlToUKFTL/7ww8/SHwv+H/33Xcm271o0aLy/fffy7333mvaand84M+xjK/7Quf74osvZPr06TJt2jT5+uuvne917NjRBNe3bt0qzzzzjEyZMsXrurT9+fLlkx9//DHg/XHt2jVJmzZtwJ8HAAAAkHqRjhMgzQrLmTOnCSxrVpPnIFi+0BNnzULX24r181r7tEyZMubks06dOvFuowbKH374Ya/v6Qmrr3RefaRPnz7ebQIAwF/aV2o/tGXLFnMX1+OPPy7PPvusGWxSg+j62qL9pzVoZM+ePaVdu3Zy8+ZNZwZy/vz549WfadD49ddfl5YtW5rXffr0kTlz5pjHoEGDfF6O1kHXLO2CBQvGei88PNyUcTl+/HhAbdS74DR7X2uAX7161QS33377bZMNrnR/6KCl8eXrvtA79ypUqGAelSpVMt+ZZuDrhQTNLv/tt9/Md6x3GyxZsiTW3QOWwoULB5yVr6VcNFnhscceC3BrAQAAAKRmBNED1Lp1a5OJruVY7r77bnNSrwFrz/qqcdFbl63/XZ/rCa8v9GTUGozs77//lvvuu89ZN/Wbb76RBg0aJFpdV5VQ5WQAAKmbBptr165tAq6ajfzpp5/KbbfdZgLNmlnueuFZS4tY9GK33u2lGd+5c+eOdzsuXbpkgrjPPfecvPDCC24Zzhoc9of29ZoVrQHzYNPychrgHjBggLkrzbOEib523U8JvS88v5OzZ8+a5/v27TPHLZpAYNHP2gXR/TlGcvXII4/I3LlzpXr16vLyyy/7/XkAAAAAIIgeoOLFi5uTv2XLlpngsWZfzZ492wSvrRN+T641QG91+7Uv9HboGzdumOd667pmmWmQwcrW0hNnfXijmVh2t0wDAJDUaMkWzWDWLPLnn39eli9fLjVr1jQB2rx58zrn81bz2td+1VdaksTzjjF/7vBSefLkkaioKHMRXOuJB1OPHj1MtrmWT/n8889NZr5mo+uxgdI73uxqzCtvZXPisy88v5NAvw8Nvrt+17763//+J507d5ZHH33UtFd/fgAAAADAHwTR40FPeps3b24eGrxu3769ycDSTKkcOXI4T0T1RFkdPnxYKleuHLT1a31Q1xNUPTl2zfYKVjkXAACSQhD9o48+MgOHvvXWW6a+twbSXeuhB4tVF9wz6zlr1qym79UgdHzLgmgpGqWlTLzVRY8PPR7QQUR1EHMde0Uvmg8dOlSaNGki/fr1MwOxxrecSzD2hR6z6ACrWqNey7kora3ujX4Xmrxwxx13+L0eLZmjD91+LelCEB0AAACAvwiiB0hP5DWDTE/e9VZsvU1Ya41aZVn0uQaqdcBQvZ1aBzrzd9Cx+NJSLvEt56Int6dOnTLPNXChJ7FWjVbNBrPKx6iuXbvKrFmzZP/+/QHViAcAwI5erNb+RftZLf+hpdQmTpxo7sLylV7Y1sfFixedmc3ap2kpNtf+UpevgXTNWtbBLjNmzOi8OD5kyBBTwkRrq+tdYCdOnDCDZuqAnRqk9ZX2oVpeZNWqVbGC6Fp+RvvbM2fOmNfaD+t2azuyZ8/u8zr0Anvbtm3NQwPQWiNdj0vuueeeeJdzCca+0NIttWrVMkFtHcRcA/tffvmlKdnjad26dWYf1K9fP+D26neq9eIBAAAAwF/BL8SZSuhJrHULs57Ya23QhQsXumWpT5o0ScaOHSuFChUyJ3/xOfG7Fa1LqiewwabZ81YG19KlS2XevHnO1/qeKw1MuGbhAwAQzH5Xs5WtzPOmTZuavtefTHQdy0T7L6sGtwaX9XWbNm1iZVnrwJwzZ840fbheJLboIKYauB85cqRpz0MPPWTGJ3G9O8xXTz75pKnv7qlv377OzGmlgWZ9rdMDVaJECdNuDaQHSzD2xccff2y+Rx1kffz48eYuOm90P3Xs2DFepW/0wr+vpfUAAAAAwFWYI9iFQpEq6Y+RZqJpzVU9mQYAAHHTO7zKli1rxlSpV69eqJuTZGkmvu4nHVj29ttvD3g5Oqio3hmoGe+UtQMAe3rHll681jujgvX3snnzoCwGAABj8WJJ9H6PTHQEhZ6QanmbwYMHh7opAAAkCzpIqpZBs8q2wDvNbte7++ITQFc6uKiOXaN3zNllvAMAAACAN2SiAwAAIFXQw95jx46ZevHeaq8DAMhEBwAkfYtDkInOwKIAAABIFcLCwkydewAAAADwB+VcAAAAAAAAAABISUH08PBwyZs3r/Ts2dPclgsAAICE0apVK+natWuomwEAAAAAIZMsy7kcPnxYvv/+e+nWrZs5qatVq1aomwQAAJAizZw505RBAQAAAIDUKllmohcuXFg6duxonh89ejTUzQEAAEixcuTIYQbaAQAAAIDUKlkG0VXatGnN/9HR0aFuCgAASCW0jNyrr74qkZGRkiFDBilVqpRMmDDB+f7KlStN1vbp06ed0xo1aiS9e/c2z1977TVp2LChlC5dWsqVKyfjxo0zAeoHHnggoGOaYsWKmXUmhCeeeMJsiz7iKudy6tQpOXLkSIK0AQAAAACSgmQbRFdp0qSR69evh7oZAAAglVi4cKGMGjVKJk+eLLt27ZKpU6dKlixZ/FqG3kW3aNEiiYmJkfnz58uaNWtk7dq1smnTpqC0ccSIEaZN3h5PP/20z8sZM2aMHDt2TJo1axbnfA899JC5qAAAAAAAKVWyrIluKVu2rCxZskTatWsn6dOnD3VzAABACnfgwAHJmTOnCSzrxXzNBPdXjRo1TBZ6tWrVzOcrVqwoZcqUkUOHDkmdOnXi3UYNlD/88MNe38uWLZvPy9F59cExFgAAAIDULllnon/wwQfy3XffSaZMmWT27Nmhbg4AAEjhWrdubUq6aDmWHj16yMcffyxRUVF+LUPLwFj/uz6/evWqT5+vUKGCM7NcA+/33Xef8/WqVaskV65cUrJkSa+PfPnySbBpORndJwAAAACQUiXrIPrAgQOlfPnysmHDBmnRokWomwMAAFK44sWLy759+0wtc83S7tOnj7Rs2dL5vtYP96RlW3zhayD666+/lu3bt5tHoUKF5P3333e+1iz3YJVzAQAAAACkgHIu69atM7VI9YQRAAAgMegdcM2bNzeP2rVrS/v27eXatWsmmzxHjhxmnsuXL0uePHnM88OHD0vlypWDtv6iRYs6n2tJmcKFC5ss82CXcwEAAAAApIAgut4+7e9gXgAAAIH66KOPzPFH3bp1JTw8XObOnSulSpVylmXR5xqo1gFDBwwYIJ988okpuZKYtJyLPuIjOjpaTp06ZZ7rIO5aaub48ePmdd68eSUiIsI5b9euXWXWrFmyf//+gGrEAwAAAEBSl2zLuejJnXI9iQMAAEhI2bNnl2nTppkBQDUL/dKlS7Jw4UK3LPVJkybJ2LFjTakVvWuufv36CTrQaaNGjYK+XM2eL1iwoHksXbpU5s2b53yt77nSrHvXLHwAAAAASGnCHMl0JKiffvpJGjZsaE5O9UQWAAAAiUsPI/Pnzy9dunSRkSNHhro5AIAguHjxorlofOHChaCVAWvePCiLAQDAWLxYEr3fS5blXDJmzGhqjzZt2lRq1aoV6uYAAACkSjqYqZa3GTx4cKibAgAAAAAJJlkG0Xfu3Ck5c+Y0DwAAAIRGtWrV5Pz586FuBgAAAAAkqGQZRC9evHiomwAAAAAAAAAASAWS7cCiAAAAAAAAAAAkNILoAAAAfvriiy8kLCws1M0AAKQCEydOlGLFikmGDBmkdu3asnHjxjjnHzt2rJQpU8aMJRYZGSnPP/+8GVMMAAAELlUH0fVAZNSoUaFuBgAASGbuu+8+OXbsWKibAQBI4ebOnSv9+vWToUOHytatW6VKlSrStGlTOXnypNf558yZI4MGDTLz//777/LBBx+YZbz00kuJ3nYAAFKSVB1EBwAACET69OmlQIECoW4GACCFGzNmjPTo0UO6desm5cuXlylTpkimTJlk+vTpXudfu3at1K9fXx599FGTNHbvvfdKhw4dbpm9DgAAUmgQXW+h1gOHRx55RLJkySJ58uSRzz77zLwXHR0tw4YNM7eu6Xt33nmn/Pzzz87P6sGEfv7gwYMyYMAA81wfK1eudFv+ggULnK9fe+01qVixolsbrGmff/65lCpVytxepwcsSpely/jkk0/Me9myZZOnnnpKYmJi/NrOCRMmSIkSJcyyixYtKi+//LLzvQMHDph1bN682Tmta9eu8uCDD5rnM2fONJkKesvfbbfdZg648ubNa15fvHjRbT36+siRI3L9+nW/2gcAQGqyevVq53HDrcq56PvaV9vRPluPJeycO3fO9M16XAMASH2ioqJky5Yt0qRJE+e08PBw83rdunVeP1OvXj3zGSto/ueff8rXX38t999/v+169BxQzwddH0r7H+thncfq/96mu07zNj0i4p+HiMNMt167T3f4MV1iTQ8P/2d6WFiMT9P1dVzT9XO+TGeb2Ca2iW1im6ITfZuC1T9ZD1+kkWTsjTfekJ49e8rrr79uTjJdp2vwWoPIGnjWYLve8rZnzx7JmjWrbNq0yeygmjVryuOPPy7PPPOM+VyuXLn8bsOJEydk9OjRZh358+eX7777zu19bYPWTdVb6R5++GFp3ry5M8h9K3rw06dPH7OMRo0amW3UW/j8oRcKfvrpJ3n22WdN6Rp93rJlS3MgpRcgXDMc9MLDihUrzLoAAEBseiFay7gsXbrUZAUmJK1hO2vWLNm/f79JAAAApC6nT5825616nulKX+/atcvrZzQDXT/3n//8RxwOh9y8eVOefvrpOMu5vPnmm+Zc0NPOnTtNUpp1rlykSBFzTnr27FnnPHpXlj70ovGlS5ec0zWhLXfu3OYcXOux16ljLbO4nD+fTWrW/M0lgCKybVsZuX49ndSp86tbG9avryTp00dJtWq7ndOioyPM9Bw5LkmFCn86p1+9mkG2bi0r+fKdk5IlDzunnz+fVXbuLCGRkSclMvK4c/qJE7lk794iUqLEEcmf/99tOny4gBw6VEDKlTtg1mHZuzdSTpzILVWr7pGMGf+tMc82sU1sE9vENiX+Nv36/6uIb/+kLl++LL4Ic2jPmgxpdlfnzp3NyaUr3QG6AzU7XAPnlkKFCplAsWvgWE9Ie/fuLf379/e6/Pnz50u7du3Ma80U08z0HTt2OOfRaRrA1x2v2eKuNBO9cePGsmrVKnMAozQrvFWrVl4PULzRzPr27dubHwLNZPekPwi33367uShQo0YNZ1abHjQtWbLEBN81+3z9+vWmLp7O/+mnn5p9cMcdd8jAgQPdtoUgOgAAvtEL5K1btzYBCjt6LBFXAFz7bH3PLhtd3yeIDgCp119//SWFCxc2JVrq1q3rnK7ncT/++KNs2LAh1mf0PFTP94YPH24u/O7du1f69u1rSsK8+uqrtpnornckaya6Bhlcz0O1T9MseM3gc+37rOmeWXye09u2/Wd6dLTeDB/mFnT5d7pmNcb4OD3i/7MX/52uzYqJiTBZiuHhjltOj4kJE4cj3Ha6Zj263nRmN51tYpvYJraJbUr8bfr/YiTx7p+sfk9jyRcuXPAaf00RmegNGjSINU0D2levXpU2bdq43Wat0/RWtmDT4LxnAN1VyZIlnc9z5szpdlXkVu6++26z7LJly8o999xjgvF6QKTZ9L7SMjDW/67PdX+40hP4uG4pBwAAt2Zl7FkqVKjgPB757bffZMSIEfLxxx+b1xqw0PesQc41S9A1U1AvhusDAJA6acnSiIgIc/ezK31tNy6HBso7deokTzzxhHldqVIluXLlijz55JOmNKgGDryN86EPT7pufbjy9nlr3rime94p/0/gJDb/pod5na4BH2935vs7XYM13thNZ5vYJraJbYprOtskQd0mz24n0P4prnlSVBA9R44ctu9pJrZePXcVSLkWi10t87jaoNKkcd/F/iT+67J//fVXU4JFMwo0yD1+/HhT0kWX660Wq68115PpDQgAACRp27dvdz7XMVG0fJpmEVoX3vUONusOuBdffNG8pyXX4nucAgBIedKlSyfVq1eXZcuWmTuarfM9fa13VHvz999/xwokWMEBzgEBAAhcsg6i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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "# Paths to the two code files\n", + "file_1_path = \"test_codes/input_code_3.c\" # Update with your actual file path\n", + "file_2_path = \"test_codes/input_code_4.c\" # Update with your actual file path\n", + "\n", + "# Read the content of both files\n", + "with open(file_1_path, \"r\") as f1:\n", + " code_snippet_1 = f1.read()\n", + "\n", + "with open(file_2_path, \"r\") as f2:\n", + " code_snippet_2 = f2.read()\n", + "\n", + "# Create figure with 1 row, 3 columns (Two code snippets + Similarity bar)\n", + "fig, axes = plt.subplots(1, 3, figsize=(15, 5))\n", + "\n", + "# Display Code Snippet 1\n", + "axes[0].text(0, 1, code_snippet_1, fontsize=10, family='monospace', verticalalignment='top')\n", + "axes[0].axis(\"off\") # Hide axes\n", + "axes[0].set_title(\"Code Snippet 1\")\n", + "\n", + "# Display Code Snippet 2\n", + "axes[1].text(0, 1, code_snippet_2, fontsize=10, family='monospace', verticalalignment='top')\n", + "axes[1].axis(\"off\") # Hide axes\n", + "axes[1].set_title(\"Code Snippet 2\")\n", + "\n", + "# Plot Similarity Score\n", + "axes[2].bar([\"Code Similarity\"], [similarity], color='blue', alpha=0.7)\n", + "axes[2].set_ylim(0, 1)\n", + "axes[2].set_ylabel(\"Similarity Score\")\n", + "axes[2].set_title(\"Code Similarity\")\n", + "axes[2].grid(axis=\"y\", linestyle=\"--\", alpha=0.6)\n", + "\n", + "# Adjust layout to fit text properly\n", + "plt.tight_layout()\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.7181637\n" + ] + } + ], + "source": [ + "cass_strs_5 = drive_tree(input_file='test_codes/input_code_5.c')\n", + "cass_strs_6 = drive_tree(input_file='test_codes/input_code_6.c')\n", + "casses_5, _ = cass_manager.load_casses_from_strs(cass_strs_5)\n", + "casses_6, _ = cass_manager.load_casses_from_strs(cass_strs_6)\n", + "input_5 = gnn_preprocessor.preprocess_casses_combined(casses_5)\n", + "input_6 = gnn_preprocessor.preprocess_casses_combined(casses_6)\n", + "vectors = gnn_runner.compute_code_vector_batched([input_5, input_6])\n", + "from numpy.linalg import norm\n", + "similarity = (vectors[0] @ vectors[1].T) / (norm(vectors[0]) * norm(vectors[1]))\n", + "print(similarity)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABdEAAAHqCAYAAADrpwd3AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAwqZJREFUeJzs3Qm8jOX///GPfd/3NVvILkKpKEpJe1lSIWlDUiQtokULKqkoZamoEFkqLUKSlCQipJQlhGTLEs7/8b6+/3t+c8bMMTNnzjLnvJ6Px3Dmnpl7rvue7b4/1+f6XFkSEhISDAAAAAAAAAAAnCTryYsAAAAAAAAAAIAQRAcAAAAAAAAAIASC6AAAAAAAAAAAhEAQHQAAAAAAAACAEAiiAwAAAAAAAAAQAkF0AAAAAAAAAABCIIgOAAAAAAAAAEAIBNEBAAAAAAAAAAiBIDoAAAAAAAAAACEQREeKqVSpknXt2tXSs5YtW7oLAACAcPwCAADiTVodvyxYsMCyZMni/o+VCRMmuHX+/vvvKXrso/XrefR8QDgIomdCv/76q91+++1WpUoVy507txUsWNCaN29uI0eOtEOHDll6deLECXvzzTetadOmVrRoUStQoIBVr17dbr75Zvvmm28sIxg6dKh98MEHYd9/9OjRdv3111vFihXdl396P+kHACBaHL9kjOOXzZs325AhQ6xJkyZWpEgRK168uDsp/vzzz1O8nQAApDaOX+LLRx99ZIMHD07rZiCdyp7WDUDq+vDDD13QNVeuXO7Lr06dOnb06FH76quvrH///rZ69Wp77bXXLD26++677eWXX7Yrr7zSOnfubNmzZ7d169bZxx9/7H6QmjVrFvE6P/30U0tvJ6HXXXedXXXVVWHd/5lnnrH9+/e7E9Ft27alePsAAEgLHL9knOOXmTNnuuMX3bdLly527Ngxd5J+0UUX2bhx46xbt26p0mYAAFJaZjh+Of/8811nQM6cOWP23DfddJN17NjR7beUdNppp7m258iRI1EQXdtNIB3BEETPRDZu3Oi+iPRF8cUXX1iZMmV8t/Xs2dM2bNjgvuTTox07dtgrr7xiPXr0OOlH5oUXXrCdO3dGtd5YftGnhYULF/qy0PPnz5/WzQEAIOY4fslYxy8XXHCBbdq0yWWge+644w5r0KCBDRo0iCA6ACBDyCzHL1mzZnUZ9rGULVs2d0kp6sBXpr2Op2LddmRslHPJRJ599lk7cOCAvfHGG4m+wD3VqlWzPn36JPpiefzxx61q1aquB1A1th588EE7cuRIosclJCTYE088YeXLl7e8efO6kyP1qAbzzz//2D333GMVKlRw69RzKhtJX2Cn+gHS82jYUyAFkEuWLHlS/azFixfbvffeayVKlLB8+fLZ1VdffdLJamBdLa+e13vvvee2tXTp0u6xV1xxhRt+HPhY9SR///33ds4551iePHmscuXKNmbMmJPaqH326KOPuu3Vdmv777///kT7Us978OBBmzhxovs7nPIs+kHW/QAAyKg4fslYxy+1a9dOFEAXrbtt27a2ZcsWN8IOAIB4l1mOX4LVRPeONVauXGktWrRw7dRzT5s2zZcMqDIxOgapUaPGSSXdgtVED6SMfnW+N2rUyAoVKuSOe8477zybP39+0Lrnw4cPdx0A3v5ds2bNSTXRdfyiLHRvO72L9oVeD2XlBzp8+LB7fpXsQcZHJnomMnv2bDfsRidM4bj11lvdCZGG59533322dOlSe+qpp+znn3+2GTNm+O6nLy59ievkR5fly5fbxRdf7L7U/P3777/uC3Tr1q3uC0YZ1F9//bUNHDjQlSLRF1pSwWKZOnWqGw6lL+FT6d27t6u1qZM/fTlq/b169XInmKfy5JNPui/LAQMG2F9//eUe27p1a1uxYoX7ovfs2bPHbXP79u2tU6dONmXKFLvzzjtdj+Ytt9zi7qMfKJ3EasjWbbfdZmeccYatWrXKnn/+eVu/fr2vhuhbb73l9rlKs+h+oi94AAAyM45fMsfxy/bt293+CWcfAQCQ3mW245dAOtZo166dy8bXOjSfm/6eNGmSC+xrFNoNN9xgw4YNc9usTn/VXQ/Xvn377PXXX3fHMcqYVye8OizatGlj3377rRvh5m/8+PEu4K1jFQXRVec9sDNB++nPP/+0zz77zB3feHRsdeONN7qOkb///ts91v91Vlt0OzKBBGQKe/fuTdDLfeWVV4Z1/xUrVrj733rrrYmW9+vXzy3/4osv3PW//vorIWfOnAmXXXZZwokTJ3z3e/DBB939unTp4lv2+OOPJ+TLly9h/fr1idb5wAMPJGTLli1h06ZNSbbp5ptvdussUqRIwtVXX50wfPjwhJ9//vmk+40fP97dr3Xr1ona1LdvX/c8//zz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0wSuGtWgCWNTgrZBr0tyZMuWLV2VIAKAeJVejl+SEs7xi9qrdnu/XTo20XVtX6S0Der4DjV6TB0FlSpVinq+FOF3DAAAAEBGkSUhNQuWA6egDL+aNWu6zgtl4cej1atX24UXXmi//PKLFSxYMOr1qKasaudrUrZYduAAACCdOnVyiQIPPvjgSbc1bdrUdVTfcMMNUa//s88+cx0NK1euTDTJOQAACG3fvn1u5LhGXSfnfBIAENvv3TSpiQ6EoqHsysTzyrbEI9W2f/PNN5N9wHP77be7gHy+fPnciAUAAGJJI7M0KiuQarMry11B9uTQXCmqja7Rd2eccUay1gUAAAAAaYlM9ChrfCZF9c07dOiQau1BxqW685pcVoH0okWLpnVzAACImILymgclsBwdAAA4GZnoAJA+v3ezp2qrMogVK1YkeXvp0qVTrS3I2FRPtkKFCmndDAAAoubNWwMAAAAA8YogehSqVauW1k0AAAAAAAAAAKQCaqIDAAAAAAAAAJARg+gNGjSwwYMHh7z9999/t3PPPddy585tWbJkcdejccEFF9jkyZMtXj3wwAPWu3fvZK/n888/t1q1alnOnDltwoQJMWkbAACZzamOXwAAAAAA6UtcB9HnzZtn/fr1C3n7U089ZYcPH7Y1a9bYtm3boqotPWfOHDexY8eOHS09WrVqlZ1zzjmuo+D000+3999//6T7aB9NnDjRfvvtt2Q9l074y5UrZz///DMTpwIAkELHL6dSqVIlGz58eEzbBAAAAADIoEH0YsWKWf78+UPe/uuvv1rTpk2tSpUqbrJPTdIYqZEjR9rNN99sWbOmv12lDoLLLrvMdQ58//331r17dxfs/+mnn06a0Ouiiy6y0aNHJ+v51JnQqlUrq1q1quXJkyeZrQcAIHM61fELAAAAACB9SX+R4TC0bt3alWfRJdhw6JYtW7rblOn1yiuv+O4baTmXnTt3unW0a9fupNu6du3qAtjKJCtUqJCddtppLms9ORT0HzBggA0dOjSs+3/00Ucuw/7VV1+12rVru7ItGiL++uuvn3TfK664wt55551kte/EiROWPTtz0QIAkFLHLz169LAuXbpYvnz5rGbNmrZ06dJEGeh67B9//GH9+/f3rWvBggVht+FUxy9al9a5a9euRO3q1auX+1vtbtGihVWvXt3OOOMMl2yg9Widx48f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7I477rAePXrYli1bbPr06VE9z8CBA61nz55WoEABi0erVq1yr51OpnVSnV5ev3ik4E23bt2sVq1armOmV69eQe9HcAIAIlesWDHLnz9/yNt//fVXa9q0qVWpUsVKly5t2bJli/g5Ro4caTfffLP7Ds+IVq5caZ06dbLu3bvb999/7347LrvsMjty5Eii+1166aW2b98++/jjj6N+rp07d7pjrSuvvNI9TzSvBwAAAID4FpdnVuXKlbPOnTu7v7du3Rrz9efMmdOdtBYtWtTSA2XnJnUSvX37djt48KBdcsklVrZs2ajavWnTJps5c6Z16dLF4jXoq5NnndzqZFon1cq+++mnn9K6aXHp+PHj7nOgTMn69euHvB/BCQAIX+vWrX2j3IKVc9EoJ92mTPVXXnnFd99IR+joe1XraNeu3Um3de3a1f1e6vu9UKFCdtppp7ms9cDReP6j8NQurzNV7W7RooVVr17dzjjjDBes13q0Tv12ROurr75ynalz584N6/6vv/66NWzY0HXC1q5d21577TV3TBj4e6TflLZt29o777wTddu8jmIdjwEAAADInOIyiC45cuRw/0d7wjZ//nyX5eVlLY8aNSqix6uMzCOPPOKCft46XnrppUT3+fHHH61Vq1aWN29ed5I6aNAgFywMl7LK/cvKBJ5Ee2VHvMzdJk2auOvRlHOZMmWKy3gPlgWsdarchuqz5suXz22TTtDTk48++shl67/66qvuZFon1ao5q5Ps9MQLTuhkXu+ZggUL2u23356sTO6UoNdZ+/KWW25xwZFQCE4AQGTlR/Rbpd+pYDSSTLefffbZbjSQ/o6mnIsC0rly5XK/66GOgdThvmzZMhcQv/XWWyM6PlGwWh3v+v7WNi1evNi+/vrriEcH7t271x1/qZ3qlE2qzYHUYd68eXPfdf1W1atXz21TIB0fLVy40JLTUe9/7AkAAAAg84nbILoXdAscthuO9evXu8DfDTfc4OpnKotqyJAhLpAcLp3oDh8+3JWUWbt2rQs4+g/N3r17t1144YUuUK9g+ltvveUm93ruuefCfg5llevk+fPPPw96u06qdfu3337rrit7S9ejKeeyaNEia9y4ccjblRH3xBNPuJPQdevW2TPPPBP2urVfQl2UAa9SNKFuHzp0aNgn0wpKFC5c2Lfs/PPPD3oynR6onvoHH3xg48aN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rVo1t42NGze2+fPnn5T4Ua5cOdd5oizLwJIrCjTVrVvXPV7tV6aqJmny30Yvu/X6668P+pqqUyPY+0E+/vhjt+6DBw/6lmkS+PLly9v48eN9y9Rudbxof59++uk2atSoiPbVV199lWhkQiAvi1/7UuvX5FO33367e639t1VZqPp8ad+3bds2Udaq9xkJlYkdzmckHJqo3XtssJIg2veXXXaZK2Wo7VDgb/Xq1WGvP5x9oedQVr9eO3WO3HXXXXbkyJGI3hen8tZbb7n3rN6bCphfd911tnXrVosljYi588473TZoW/SaqhNPHnnkEXc9mNtuu80FX+XQoUN29913W6lSpVw7te/9t/P48ePudu1Hfc9MmjQp4nbqu+e1115zpWCSSuDSuvW9qe2JtnTSFVdc4b4bQ3XOabmy4uOl8y6Yo0ePuox7fTf5d0rquv/vQODIBj3G+2397bff7KOPPgr5HhF9JvR96X/x3hPexftc6f9gy/2XJbVc35uhlusS7nIJtTywjaGWs01sE9vENh1PR9sUjricWDS90AGHDnY1nPn++++3zZs3W6NGjWzx4sXu9h9//NEN/dIPrE6qlCU1aNAgd0CrAxjvgEq8g8cWLVr4DnK99f/000+J7jd16lR3cOi/TMPEPv30U5szZ447aXj11Vft2muvdSd2OqjVAYDWpYMlHZDr5CJcquEmanNSdu7c6Q4AdDIDAADSJwW8hg0bZiNGjLBnn33WHS/oJD9HjhyJ7qdjG5XUUC1YDUn3gss6Lrj11lvdMc369euDPofW98cff7iMPB2j9O7d266++moXXFVphaefftr69Oljf//9twuAiX/mscrE6NjlmmuusZo1a9rQoUPdch1P+VMpAQXPUlqofeFRNvyFF17ogv46gPcCkeo8UKamjr1UH1eB9quuusrWrVt30v7W8aQCjI899ljQcgzNmjVz/0cSXPSndT/zzDPWvXv3k27TMZ7apuCbAuEK3CrAt3btWqtYsaLbZj1Oj1cHjN432kZti2fPnj2ug0XBYR0T6v56r+j1F680ijoUtF+8MiX+r6mOcXUs6R+w8miZAtIKRinYKnp/KTit95bo/ahAld5fKqmg/XzzzTe7oGWoUh6BdIysts6dOzfRsXqwfaa66do3WrdGMPiXXnnppZdc9r+Oi7Uv9BlQSYdwhPMZCYcCyXv3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7do2YcIEO+eccywedezY0c466yy777770ropAJAsWbNmtWLFitn1119vL7/8smXJkiWtmwQAADKgyy9P6xYAQOqaPTvln4NM9DSm4Gb+/Pkts1AAODmmTZtms2bNsi+++MK2bdtmHTp0sFirVKmSDR8+3FLa+vXro2r/9OnT3bafffbZp7zvq6++ahUrVky3AfR77rnH1aPNnj27tWvXLuh9Hn74YXvyySfdLMkAEM82b95sw4YNs9GjR9t3332X1s0BAAAAAISJIDpSnCZF/fzzzxMt03Utj9Svv/5qp512mjVu3NhKly5tefLksXhVsmTJqNpftGhRt+2acDYpCQkJ9uKLL1q3bt0svTp27Jjddttt1rp165D3qVOnjlWuXNnefvvtVG0bAMRauXLlrHPnzu7vrVu3pnVzAAAAAABhIoieDIMHD3YBvhkzZtjpp59uuXPntubNm/tu//HHH61Vq1aWN29eF/gdNGiQCxp6Gegaxq0A58GDB93fuvhnanvr96f7KBs7cNm4ceNc2QtltWsC0vfff9/dpvX16NHDlUDJly+f1axZ05YuXRrRdmr9I0aMsHPPPdetQ9u0c+fOsB9foUIFGzt2rN11112uBIv+13UtD5f2hdrxyCOP2Jo1a3z7S/tRjh8/bt27d3fB1ly5crls8qeeeuqk9Sxfvtwuuugitx0aUt+pUyfXJtFjtM4//vjD+vfv73uOBQsW+B6/YsUKtx/0WpcpU8a9pgpWR/K+qFat2knt9/fcc89Z3bp13ftGr+Utt9xi+/bts0h9//33tmHDBrvssstOui0W74tYUCmaO++803UKJOWKK66wd955J+Tte/bssS1btrj3AQCkZzly5HD/830FAAAAAPGDIHoy7dixwwWYFcReuXKlC8rK7t277cILL7SmTZu6YPpbb71lkydPdgFSURkPleRQTWwFS/W3LirVEY3HH3/cGjVq5ILEU6ZMsSJFivhuU/BRQdMffvjBBa5VRztSr7zyij3xxBOuFve6devsmWeeCfux5cuXt/fee8/VAFf79L+ua3m4+vXr5/aP6mLXqFHDt7+8cigKRqgkiILSat/IkSNde7XfPQr8qwOgVKlStnjxYlcSpnr16r4yIRpar3WqXQqOe8/hlUJRffErr7zSZRJqO7RP9PrptQ/3fSHffPONW2/BggVDBoTV9lWr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Zs6cmdZNAIAMRZNKL1iwwH799VdXLqxAgQJuXgplGFKWFgCQKkF01dlUDUtloWuiJwAAAAAAgPTgjz/+cBNcb9q0yc3ldtFFF7kg+jPPPOOuh5qsGgCApERczqVo0aJWoUIFAugAAAAAACBd6dOnjzVu3NhNYu0/ebYmm543b16atg0AEL+iqokOAACA6DRo0MAGDx6c1s0AACBDWrRokT388MOWM2fORMsrVapkW7duTbN2AQDiG0F0AACAVKQsuH79+kX9eAUBhg8fbvGua9eu1q5dO4tnH3zwgSsTULhwYcuSJYvt2rUrrZsEAJneiRMn7Pjx4yct37JliyvrAgBANAiiAwAApKJixYoxqVkGsW/fPjv//PNtwIABad0UAMD/d/HFF9sLL7zgu65OzgMHDtijjz5qbdu2TdO2AQDiF0F0AACAVNC6dWt3Iq9LsHIuLVu2tB49eliXLl0sX758VrNmTVu6dGmiDHQ9VhOm9e/f37euBQsWRNSO3377za655horVKiQuygbXNl5nvfee89q1KjhhsHr/6lTpyaZCT9hwoREnQLatjp16rj7lChRwkqXLm1jx45NlIGudk+cONE+/PDDoPtEGYRDhgxx8/Bo3QpU//jjj4naoe3W4zRs/5xzzrHcuXNb5cqVbePGjZZabr75ZnvkkUfs7LPPTvJ+qsurfRwsMxIAEFv6/Vm8eLHVqlXLDh8+bDfccIOvlIsmF43Uyy+/7B6v35mmTZvat99+m+T9//nnH+vZs6eVKVPGcuXKZdWrV7ePPvooGVsEAEgPCKIDAACkAgWjt23bZrVr1w55n3feeccF03/44QcXQNZJuOe7775zjy9fvrwNGjTI/a2LAsjhOnLkiCs/sn//fvvss89syZIlLriv9cjPP//sgg3dunWzn376yf3fqVMnW7duXcSBegX7v/76axc07927t6/UyciRI93ztW/f3vfcuviXuHn88cdt8uTJLkC/YsUKO/fcc61Nmzau3YHuuece69u3r61evdqefPJJy5r11Ie3kyZNcsH5YJdLL73UYk3t0+u5efPmmK8bAJCYvm/V8frQQw+579+GDRva008/7X5bS5YsGdG61LF87733uiz25cuXW/369d3v0V9//RX0/kePHnW/s7///rtNmzbN/X6qI7lcuXIx2joAQFrJnmbPDAAAkIkUKVLE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//77bjJQ/7kpNLFnnjx5rH379uk+iA4ASJ+Cz0oBAAAAAAAQZ/7999+gEzlrcmrdBgBANAiiAwAAAACADOHss8+2Rx991A4fPuxbdujQIRsyZIi7DQCAaFDOBQAAAAAAZAgjR460Nm3aWPny5a1+/fpu2Y8//mi5c+d2k34CABANgugAAAAAACBDqFOnjv3yyy82adIkW7t2rVvWqVMn69y5s6uLDgBANAiiAwAAAACADCNv3rzWo0ePtG4GACADoSY6AAAAAADIEJ566ikbN27cScu17JlnnkmTNgEA4h9BdAAAAAAAkCG8+uqrVrNmzZOW165d28aMGZMmbQIAxD+C6AAAAAAAIEPYvn27lSlT5qTlJUqUsG3btqVJmwAA8Y8gOgAAAAAAyBAqVKhgixcvPmm5lpUtWzZN2gQAiH9MLAoAAAAAADIETSh6zz332H///WcXXnihWzZv3jy7//777b777kvr5gEA4hRBdAAAAAAAkCH079/fdu/ebXfddZcdPXrULcudO7cNGDDABg4cmNbNAwDEqSwJCQkJad0IAAAAAACAWDlw4ID9/PPPlidPHjv99NMtV65cFg/27dtnhQoVsr1791rBggWjWsfll8e8WQCQrs2enfLfu9REBwAAAAAAGUr+/PntrLPOsgIFCtivv/5qJ06cSOsmAQDiGEF0AAAAAAAQ18aNG2fPPfdcomW33XabValSxerWrWt16tSxzZs3p1n7AADxjSA6AAAAAACIa6+99poVKVLEd33u3Lk2fvx4e/PNN+27776zwoUL25AhQ9K0jQCA+MXEogAAAAAAIK798ssv1rhxY9/1mTNn2pVXXmmdO3d214cOHWrdunVLwxYCAOIZmegAAAAAACCuHTp0KNGEcF9//bWdf/75vusq67J9+/Y0ah0AIN4RRAcAAAAAAHHttNNOs++//979vWvXLlu9erU1b97cd7sC6IUKFUrDFgIA4hlBdAAAgHSgZcuW1qtXr1Pe759//rEsWbLYggULEi3XdS33LgogpLSuXbu64fHI+PR+KlmypG3ZsiVZ6zl27JjddNNNVrRoUZcVCgCx0qVLF+vZs6c9/vjjdv3111vNmjWtUaNGiTLTNbkoAADRIIgOAAAQR5RFt23bNjvnnHMSLdd1LX///fdTpR0rV6602bNn2913323p1eDBgwmYxEjx4sXt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ChTwXdfF+35+++2307qZAIA4RSY6AABAJrNjxw4bMWKEjRs3zkqVKmWffvqpL9h64YUX2u233+4Czyq7csstt7ja5ffff79vwrYVK1bY3LlzrUiRIrZ8+XIXLI4VBaT1vF9//bVde+21tnr1ahf8zZYtW9jrUED7ySeftI8//tguv/xy1wmgYHeOHDnc7ZMmTXLbGIwC3XpcONasWeMC9Xfffbe9/vrrdvDgQZs4caLv9rFjx9qLL77oltWoUcOGDBliV111la1bt87XlnC88847NmrUKHvooYesZ8+e7uIF3r/77juXYXnWWWe51+rOO+90y7XPPNddd50LwH/xxRduMj29lgqqf/TRR2G9LzxTpkxxnRHjx493wX4ASE/03eR1Lvbr14/SLQCAmCKIDgAAkMkoWK7AbtWqVd316tWru/9feukl9/fQoUPddWUkK7v5+eef9wXRlT1dt25da9y4sbvurSNWcubM6TIGvSBwyZIlXdZ7JA4fPmxPPfWUffPNNy6TW21t3bq1qxmuYPMVV1xhTZs2DRmAD9czzzzjgv4KPHu0fs9rr73mssivvvpqd3306NEuK/2TTz5JlG1+Ks2aNXPBa1GJGgXF1TGggLgy9UWdDMq81L7zt3DhQtchocxz3S56PZXRvn379kT3D/W+AIB4os5eAABijSA6AABAJlO2bNmgwW+VGlGGszLPPcpy1sWjbGeVc2nSpInL2m7btq0r/5KeHDp0yAXflS2v4PMdd9zhgtkKqivIrWCyF1BOjp9++skuvfTSkLdv2LDBevfu7buujoHy5cu75ZGoVq2a729l/yvzf+/eva40y6noNf3vv/9c8D7Qb7/9liiIHup94VH2ORnoANKjM88804040ndkw4YNg04s6tEIKgAAIkUQHQAAIJMpXLhwyNuUIT1s2LCQtyurWvW3P/vsMxekbtOmjSudMmDAAHd7YOAilqVewqUgikqe+FNw2AsQx6qcS3IFC/IE21/KOA+kmr/hUqD8yy+/PGl5uXLlwn5fAEB6duWVV/omElXZLAAAYo0gOgAAAByVaXnvvffcBJxZs4aef171sm+88UZ3KViwoH3wwQe+ILoCsQcOHPDdd/PmzUHXoWz3I0eOuGBwsGCyyrqI/6Sm0ZgwYcJJy2JVzkUTcX711Vchb1fQftWqVYkmZdXEnF5muRe0Dtxf0dTx1f4Ktq/0mv71118uuFShQoWI1wsA8VTCRSOnNDF1vXr16BgEAMRU6LMjAAAAZCq9evVyAdfu3bvbjz/+6CbO1ISZmtDSM3jwYJs5c6b9+uuvtnTpUps/f74L1HoaNWpkmzZtsm+//dZlVasOeTAabq/bVTdctbn379+f6PbKlSu7QP60adPchJ0KuMeKSrkokB3sEpidnRR1HCxevNjVjf/555/thx9+cPvQoxIymlx0xowZ7nZN+qmyKsrel0KFCrlAu7ZRFJBX/fJoqO2aCFSvn2rCexntqgnfvHlzu/766102ul43dXqoJE+kFixY4Do89B4AgPRI80NcfPHFtmfPnrRuCgAggyGIDgAAAEcTeKqm7J9//ukCr2effbaNHz/eatWq5buPMpoHDhzosrCV0a1JL/3Lv5x77rnWp08fFyiuUaOGC5YHo+Dxc889Z4MGDXKB5UceeSTR7VqmiTs1QaiC3lpXeqP9opI2Cn6rHu8ll1ziAtie2267zQXVVZO9QYMGLgtdAewcOXL47vPqq6/aRx995Gq4v/LKK64kQTSeeOIJVye9YsWKLpvev3zL9OnT7YwzzrBrr73WvW7qFNFog0h5GfPB6qsDQHqh7znN+QAAQCxlSYikoCIAAACATKlfv342ZcoUW79+veXOnTutmwMAQalzU529jz/+uBsdFVgiS2XI0rN9+/a5kUrqGI22rZdfHvNmAUC6Nnt2yn/vUhMdAAAAwClpMlnVHSaADiA9a9u2rftfo6X859zw5uBQ3XQAACJFEB0AAADAKalOPgCkd5qrAwCAWCOIDgAAAAAAMoQWLVqkdRMAABkQQXQAAAAAAJCh/Pvvv7Zp0yY7evRoouX16tVLszYBAOIXQXQAAAAAAJAh7Ny507p162Yff/xx0NupiQ4AiEbWqB4FAAAAJEPXrl2tXbt2ad0MAEAGc88999g///xjS5cutTx58tjcuXNt4sSJdvrpp9usWbPSunkAgDhFJjoAAAAAAMgQvvjiC5s5c6Y1btzYsmbNaqeddppddNFFVrBgQXvqqafssssuS+smAgDiEJnoAAAAmchvv/1m11xzjRUqVMhdlA2+ZcsW3+3vvfee1ahRw3LmzOn+nzp1aqLHV6pUyYYPH+67PmHCBMufP7/v+uDBg61OnTruPiVKlLDSpUvb2LFjE2WgZ8mSxWUFfvjhh+5vXfQ4/6H2Q4YMsQoVKrh1n3/++fbjjz8maseCBQvc4xYtWmTnnHOO5c6d2ypXrmwbN25MdL+WLVu6Noai23SfpGrqav/ofwBA+nfw4EErWbKk+7tIkSKuvIvUrVvXli9fnsatAwDEK4LoAAAAmcSRI0dcNt7+/fvts88+syVLlljr1q1t27Zt7vaff/7ZbrjhBldL9qeffnL/d+rUydatWxdxoP6PP/6wr7/+2gXNe/fubbt27XK3jRw50j1f+/btfc+tS79+/XyPf/zxx23y5MkuwL1ixQo799xzrU2bNq7dwYbt9+3b11avXm1PPvmkyzqMpSlTprhgvv4HAKR/6gD2frfq169vr776qm3dutXGjBljZcqUSevmAQDiFOVcAAAAMgkFphXM/v77761w4cJuWa1atXy3v/7669agQQN74IEH3HX9P23aNJdJ7p99fioKZOv+uXLlsgEDBtgzzzxjq1atsgsuuMCXAa86tbpdmer+Dh8+bM8++6zNmDHDWrVq5ZYNHTrUBdSVud6xY8dE9+/Vq5ddf/317u+qVasmY+8AADKCPn36+DqHH330Ubvkkkts0qRJboRVUiOTAABICkF0AACATELZ5RrO7gXQA23YsMHd7k9ZfFoeifLly7sAuTeUXv7++++wHvvLL7/YoUOHXMkZlWvxaJky3AOdd955Jy279NJLXZkX73HffPONC7aLMhLl9ttvd/8fO3bM/vvvP19JGq3v448/9q1LmfS6AADiw4033uj7u1GjRm5k1Nq1a61ixYpWvHjxNG0bACB+EUQHAABA2PwD23LixImT7pM9+8mHmAkJCRE9z5w5c1wZFX9FixY96X7BOgSUUa/guXTu3NmuvfZaF5SXUqVKuf+bNm3q/p8+fbq9//77LktRlCEPAMg48ubNa2eeeWZaNwMAEOcIogMAAGQSmvBTAea9e/e6kiqBVA5l4cKFiZZpQk//iTcVtD5w4IDv+ubNm6Nqi4bVKws80Omnn+4mCdVQfJV/iUa5cuV8fysorgnmqlWrlug+BQoUcP/rNt0n8HYAQPy49957w77vc889l6JtAQBkTEwsCgAAkElo0lANZdeknt99952beE0TfS5btszd3r17d/vhhx/s6aeftvXr17v/df3WW29NNDReWeLK9Fag+6233oqqLQpaqza7htirDroXUFcA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c1vz5s3dbVqX1vHOO++42zQ7/e23324nTpwIextfffVVu/rqq+2WW26x+vXr28svv2xLliyxFStWJLrfkSNHXIb6vn37otiTAAAAAJB6CKIDAACkoscff9waNWpky5cvtylTpliRIkV8yydPnmwTJkxwAedzzz3X2rRpY/v373e3f/fdd7Zt2zYrX768DRo0yP2tyznnnBNxG3bs2GEjRoxwAf2VK1dap06dEt2uNnzwwQfu9rFjx9pHH30U9rq///57X1BeateubUWLFrVly5Ylup8C6+oweO655yJuPwAAAACkpuyp+mwAAACZnDLMlUku1atXd/+r5Mmzzz7rssNbtWrllg0dOtQFs1UeRZnrJUqUcMuzZctmBQoUsNKlS0fdht27d9vEiROtatWqidrheeSRR1zwW5e6deu6AH67du3CWvfOnTtdhv0LL7zgLmvWrHHXtRwAAAAA4hFBdAAAgFR03nnnnbTsl19+sUOHDtk111zjyql4tOy3336LeRvKli3rC6AHU61aNd/fypT/+++/I34OZZ9XrFjRBf2DadmypSUkJES8XgAAAABIbQTRAQAAUlHhwoVD3jZnzhxX4iQwGB2tULXMk2qDZM+e+BAxkmC3MuZ37drlJhe9+eab3TJd9zLpAQAAACDeUBMdAAAgjXkTfKrGubLA/S+BQfScOXPasWPHQgbHDxw44Lu+efNmS22q97548WLf9dWrV7tM9saNG6d6WwAAAAAgFgiiAwAApDEF0FUn/b777rOpU6e6Ei4LFiywO++803766adE91Vg/dNPP7W//vrL1VL3zzZXAHv69Ol2/PhxW7t2rc2aNSvVt+X22293td3Hjx///9q79yAvq/oP4J/dRS4TsoAmt1kiL4l52wIWsDGjKGocSq1po0bQqJnUGGp1CBpjRU3UJKkgKZmuxkROjUZTNMiElYgIWCmGl3QDNW4ioJSsv939zXmYXXeXfWAXcJeF12vmmfie73m+zzn88R17fz98TnZo6TXXXBOjRo2K0tLSJvOqqqqy1jVXXHFFu68RAACgLYToAABHgcrKyrj66qtj2rRpMXTo0Jg0aVIWkp9yyilN5t18882xa9eurN94jx494s9//nPDe3PmzInnn38+u6eioiI7kLS9XXzxxXHnnXdmh5OWlZVlPxCkHwaaq6+YHzBgQLuvEQAAoC0K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9dNPP51Viq9fv77Js2bNmhWzZ8/OPjvNSX3W0/xvf/vbLa4tBdepIjsdsvmPf/wj68l+7733xu23354ddNpeUguXFH5/73vfy34ESK1l0iGjh2LIkCHZXp566qnYvn17dmBp42r0FNSnNjuXXnrpEdwBxwohOgAAAAC0s3RIZgrGx4wZE9dee22cc845WduUdAjnXXfd1dD6JB162adPn3j/+9+fherpvsWLFzd8Tnl5edZqZdq0aTFs2LD497//HVdddVWTZ6WQeOHChVlwnkL5FMKn3uZ5leipZ3pq/XLnnXdmz01rS89IB43Omzcv2ktq35LWmQL8VAWfgu477rjjkD4rrf3MM8/MfnxIn/vQQw81vJcOP02Hqab/TS1soLmCutaeZAAAAAAAcIypqqqK0047LR599NHs8FRoTogOAAAAABx3UkuX1Lrmuuuuy1rWNK5Oh8a0cwEAAAAAjjspNB8wYEBWgb5gwYKOXg5HMZXoAAAAAACQQyU6AAAAAADkEKIDAAAAAEAOIToAAAAAAOQQogMAAAAAQA4hOgAAAAAA5BCiAwAAAABADiE6AAAAAADkEKIDAAAAAEAOIToAAAAAAETL/h//nJjwiwiFgQAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "# Paths to the two code files\n", + "file_1_path = \"test_codes/input_code_5.c\" # Update with your actual file path\n", + "file_2_path = \"test_codes/input_code_6.c\" # Update with your actual file path\n", + "\n", + "# Read the content of both files\n", + "with open(file_1_path, \"r\") as f1:\n", + " code_snippet_1 = f1.read()\n", + "\n", + "with open(file_2_path, \"r\") as f2:\n", + " code_snippet_2 = f2.read()\n", + "\n", + "# Create figure with 1 row, 3 columns (Two code snippets + Similarity bar)\n", + "fig, axes = plt.subplots(1, 3, figsize=(15, 5))\n", + "\n", + "# Display Code Snippet 1\n", + "axes[0].text(0, 1, code_snippet_1, fontsize=10, family='monospace', verticalalignment='top')\n", + "axes[0].axis(\"off\") # Hide axes\n", + "axes[0].set_title(\"Code Snippet 1\")\n", + "\n", + "# Display Code Snippet 2\n", + "axes[1].text(0, 1, code_snippet_2, fontsize=10, family='monospace', verticalalignment='top')\n", + "axes[1].axis(\"off\") # Hide axes\n", + "axes[1].set_title(\"Code Snippet 2\")\n", + "\n", + "# Plot Similarity Score\n", + "axes[2].bar([\"Code Similarity\"], [similarity], color='blue', alpha=0.7)\n", + "axes[2].set_ylim(0, 1)\n", + "axes[2].set_ylabel(\"Similarity Score\")\n", + "axes[2].set_title(\"Code Similarity\")\n", + "axes[2].grid(axis=\"y\", linestyle=\"--\", alpha=0.6)\n", + "\n", + "# Adjust layout to fit text properly\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.6534402\n" + ] + } + ], + "source": [ + "cass_strs_7 = drive_tree(input_file='test_codes/input_code_7.c')\n", + "cass_strs_8 = drive_tree(input_file='test_codes/input_code_8.c')\n", + "casses_7, _ = cass_manager.load_casses_from_strs(cass_strs_7)\n", + "casses_8, _ = cass_manager.load_casses_from_strs(cass_strs_8)\n", + "input_7 = gnn_preprocessor.preprocess_casses_combined(casses_7)\n", + "input_8 = gnn_preprocessor.preprocess_casses_combined(casses_8)\n", + "vectors = gnn_runner.compute_code_vector_batched([input_7, input_8])\n", + "from numpy.linalg import norm\n", + "similarity = (vectors[0] @ vectors[1].T) / (norm(vectors[0]) * norm(vectors[1]))\n", + "print(similarity)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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eDZv3pziaqYmqdIM2RrCFE/64tQPiSboSO2HJBran+Q/KsuXL7czzzwzaLkmvti7d29QNpgm6xLNIO3t9++//+6GXh3rh5lhSACAnIb+R87sf+ikWcPNNTmaJhYDACCroO+RviZNmuSOwYcffhjUh9Ck6rFIrT+iCUVbt27tgugq4aLs/NQmaAVCoSY6IqaroDrZ0szT+kFITlcMhw0b5u5rpmlJ/qXkZTPpy0v0o5AvXz576aWXgq5ehvoyu+6669yP2BdffJHiOf04qYZWOBs2bHBDhZNTTbLp06eHHH4VizfffDNo+Jd+JPRDdckllwStpzaPGjUqqD16rGFb9erV8++36paNHj06xfvs37/fnQR79PfRsQAAIKeg/5Hz+h+DBw+2IUOGuEywHj16RL2fAACkJ/oe6cvLaA88Dj///LPb51h4yQHh+iQq3aJjowv5akNg3XcgEmSiI2K6Gjp+/Hhr166du8KqmlK1atVyX8YzZ850NaU0JEfq1KljnTp1cldv9QXWvHlz++WXX1zdKw3ZadGihVtPJ2ua5ELDdnS1Vz9Av/32m6vVVbp06aD31xfdJ5984tbT++gkTydwCxcudCeJquGZ/DWetWvXWsOGDd1QYWVUqT6Z6oi9++67LstKk3mEe21a6Crnueeea507d3Y/uvph1A+Vhi0H0jCxQYMGubZrSNaECRNs/vz57rjpB9b7op84caLdcccdbhKvc845x9UQVe0wLdcPq64oi46JJizRD7a2rdphgRN/JDdlyhS3/6J6YKq7NmDAAPdYw72SZ64BAJDR6H/krP6HJjhTcEI1YfX31HDqQBq+Xq5cubgdEwAAokXfI31pv5SFfuWVV7qLDCtXrnQ1208//XSXgZ9WXiLAPffcY61atUoRKNd7qbyN/n5KMFAZGSAqPiBKf/75p++2227zVa1a1Zc/f35f0aJFfeecc47vpZde8h04cMC/3uHDh339+/f3nXTSSb58+fL5Kleu7OvTp0/QOpKUlOTWq1Chgq9QoUK+888/37do0SLfiSee6OvUqVPQurt373bbqF69unvv0qVL+5o2beobMmSI79ChQ2HbvGvXLt+wYcN8rVq18p1wwgmuPWp3kyZNfKNHj/YdPXrUv+7YsWN1OdQ3Z86coG3MmDHDLde/nubNm7tb8nXeffdd186yZcu6fWrdurXv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eKJNnTrV4m348OFWrVo1K1iwoHuPRx55xP+chlir3b/++mtQuy677DJ/plqdOnWsUaNGdsIJJ9jIkSOtTJky7vGuXbsso7zzzjtWuHBhe/755+300093/2p/tDy5tWvXElwHAGQpb731lgsybdq0yb/smmuusYsvvth8Pp9ltfIj3333nY0YMcLfv0lepmHbtm2ur6Df5vr169vy5cuDfoe914Ur56JSL82bN7eiRYu6QLP6ZRs2bIiqrXv37rUePXpYxYoVXaCtYcOGrt2ehx56yE499VTXX6hQoYL16tXLDh8+7H9ex/2xxx5zmfJa55RTTnF9pkDffPON2z/tp/o/V1xxhR06dMjiSe9x1llnuZF26qclb4P6NJdeeqlro0biPfXUU+64Jr9gkZa+qGj/J06cmGL5kSNH3H7rmG7dutXat29vlSpVcu2sUaOGvf766yk+E1qndOnSru9cr169qNqijGP1uwN9+eWXblv6W0tSUpL179/ftVlJMOedd579/vvvIcut/PDDDy4rX8ftpJNOspUrV9qAAQPsjDPOCFpf+6YLD9Eet6+++sp9ZooVK2Zdu3a1o0eP+p8bOnSo1a5d2x0/HY9bbrklRb9Z/09pH3788UfX11Y7db6xb98+9/yUKVNc/13b6NixY9Sfu0jOVfT/4bnnnuv/f6Rv375Rfx9Vr1497PeEzrH0/7n+PzzttNNs2LBh7pxH5z/6W3rnHa1atXJ/e+2rtvfpp59G1YZjfS7Utptuusn9rZ588klr1qyZ+7tMmzYt6HOgdfR9pCSrG264Iahkz7HO2yL97gQAALlbtgyiv//++25oYPKOdKB3333XdYiU+axOWbdu3fzPzZkzx71egWV1OHU/LUNoZ8yY4TprCmKrk3nrrbe6k5Z4mTt3rnXv3t21cdmyZTZ+/HiXzRENdb5Hjx7tOrUaDqoyKtu3b7fPPvssotdfcsklrkMb6hYqCB5uPzQUOk+e//u46V91+gOD/x79ra699tqo9hEAgPSkINj//vc/9zsvb775pvs99er9Hsvq1avD/pbGc0RdYPmRzp07+/s3ycs0vPbaay7ZQP0hBaZ79+7tf05Bbb3m66+/TvV4KCit33cFEVXvOTDAHYnbb7/dBRoVzF24cKELbqnP4tm/f78LSP/xxx/29ttvuz7QM888E7Sv6te88sortnTp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AADICNk6iN6sWbMUy/766y/bv3+/XXXVVa6cikfLVqxYEfc2VKxY0R9AD6V69er++8qU37ZtW9TvoezzKlWquKB/KOeff775fL6otwsAQHai33aN+Kpfv77VqFHDli5dmtlNAgAASOHyyzO7BQCQe0yZkjHvk62D6CVKlAj73NSpU12Jk+TB6LQKV8s8tTZI3rzBhziaYLcy5rds2eImF73pppvcMj32MukBAMhNNN/I22+/ba+99pq1aNEis5sDAAAAAMglsnUQPRRvgk/VOD/WCXb+/PntyJEjYYPje/bs8T9es2aNZTQNVf/pp59cEF0WL17sMtmVgQcAQG4ze/ZsNzKrS5cumd0UAAAAAEAukq0nFg1FAXTVSe/Vq5e9//77roSLMtfuvPNOW7RoUYpSK19++aVt2rTJ1VIPzDZXAPvDDz+0pKQkN1z8k08+yfB96dq1q6vtPnbsWDdpabdu3axx48ZWt27doPVUH1ala26++eYMbyMAABlFk4oWKVIks5sBAAAAAMhlclwQXfr162d33XWXPfjgg24Ssk6dOrkgedmyZYPWGzBggO3cudNltRUqVMi+//57/3PPPfecrVy50r2mZ8+ebkLSjNa6dWt7/vnn3eSkDRs2dBcIdGEgOS9jvkKFChneRgAAMooubIebHwQAAAAAgPSS4GNGymxv+PDh1rt3b5d1n/xCAQAAOcGhQ4esTZs2bm6RadOmZXZzAABADrRr1y4rXry4S7YrVqxYmrfDxKIAkH0mFo30uz9HZqLnNl999ZXdd999BNABADnS008/7UaMzZw503r06JHZzQEAAAAA5DJkogMAgCxNk2rv3bvXlS3LmzfHzYkOAACyCDLRASD7mZJBmeiciQIAgCytVKlS7gYAAAAAQGagnAsAAAAAAAAAAPEKovfv398KFixodevWtdmzZ0f7cgAAAAAAAAAAcm4QvVevXjZ//nwXSH/iiSfSp1UAAAAAAAAAAGTHIHqRIkWsZs2adsEFF9i6devSp1UAAAAAAAAAAGQBaa6Jni9fPktKSopvawAAAAAAAAAAyClB9IMHD8a3NQAAAAAAAAAA5IQgukq6rFq1yubOnRvfFgEAAAAAAAAAkN2D6FdddZW1b9/e6tev7wLqAAAAAAAAAADkNHnT+sJvv/3W3n77bXvttdesRYsW8W0VAAAAAAAAAADZ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TwEAuRE10QEAAAAAQEy6du3q6pEPHz7clVYRZaPfc889duGFF9orr7xiWZ1qokdSF/dYLr88rs0CAKRiyhTLkO9+MtEBAAAAAEBMPvjgAzcR6Pnnn+9fpkk9CxUqZNddd122CKIDABAONdEBAAAAAEBM9u3b5y+1Eqhs2bLuOQAAsjOC6AAAAAAAICZNmjSxfv362YEDB/zL9u/fb/3793fPAQCQnVHOBQAAAAAAxGTYsGHWqlUrO+GEE6xOnTpu2e+//24FCxZ0E34CAJCdEUQHAAAAAAAxqVWrlv3111/2zjvv2NKlS92y9u3bW4cOHVxddAAAsjOC6AAAAAAAIGaFCxe22267LbObAQBA3FETHQAAAAAAxGTgwIE2ZsyYFMu1bNCgQZnSJgAA4oUgOgAAAAAAiMmoUaOsZs2aKZafccYZNnLkyExpEwAA8UIQHQAAAAAAxGTDhg1WoUKFFMvLlClj69evz5Q2AQAQLwTRAQAAAABATCpXrmw//fRTiuVaVrFixUxpEwAA8cLEogAAAAAAICaaUPTee++1w4cP2//+9z+3bPr06fbggw9ar169Mrt5AADEhCA6AAAAAACIyQMPPGBbt261u+66yw4dOuSWFSxY0B566CHr06dPZjcPAICYJPh8Pl9mNwIAAAAAAGR/e/bssT/++MMKFSpkp5xyihUoUMCyi127dlnx4sVt586dVqxYsTRv5/LL49osAEAqpkyxDPnupyY6AAAAAACIiyJFiliDBg2saNGi9vfff9vRo0czu0kAAMSMIDoAAAAAAEiTMWPG2NChQ4OW3X777XbyySdb7dq1rVatWrZmzZpMax8AAPFAEB0AAAAAAKTJq6++aiVLlvQ/njZtmo0dO9befPNNmzNnjpUoUcL69++fqW0EACBWTCwKAAAAAADS5K+//rL69ev7H0+ePNnatGljHTp0cI+ffvpp69y5cya2EACA2JGJDgAAAAAA0mT//v1BE7HNnDnTzjvvPP9jlXXZsGFDJrUOAID4IIgOAAAAAADS5MQTT7S5c+e6+1u2bLHFixfbOeec439eAfTixYtnYgsBAIgd5VwAAAAAAECadOrUybp16+aC5998843VrFnT6tWrF5SZrslFAQDIzgiiAwAAAACANHnwwQdt37599uGHH1r58uXt/fffD3r+p59+svbt22da+wAAiIcEn8/ni8uWAAAAAAAAsqldu3a50jM7d+4MqvMercsvj2uzAACpmDLFMuS7n5roAAAAAAAAAACEQRAdAAAAAAAAAIAwCKIDAAAAAAAAABAGQXQAAAAAAAAAAMIgiA4AAAAAAGIyY8aMzG4CAADphiA6AAAAAACIycUXX2zVqlWzAQMG2Jo1azK7OQAAxBVBdAAAAAAAEJN169bZ3XffbZMmTbKTTz7ZWrVqZRMnTrRDhw5ldtMAAIgZQXQAAAAAABCT0qVL23333Wfz58+3n3/+2U499VS76667rGLFinbPPffY77//ntlNBAAgzQiiAwAAAACAuDn77LOtT58+LjN9z549NmbMGKtXr541a9bMFi9enNnNAwAgagTRAQAAAABAzA4fPuzKuVx66aV24okn2hdffGHDhw+3jRs32vLly92ya6+9NrObCQBA1PJG/xIAAAAAAID/dO/e3d59913z+XzWsWNHe/bZZ61WrVr+54877jgbMmSIK+8CAEB2QxAdAAAAAADEZMmSJfbSSy/ZVVddZQUKFAhbN33GjBkZ3jYAAGJFORcAAAAAABCTfv36uVItyQPoR44cse+//97dz5s3rzVv3jyTWggAQNoRRAcAAAAAADFp0aKFbdu2LcXynTt3uucAAMjOCKIDAAAAAICYqBZ6QkJCiuVbt2519dABAMjOqIkOAAAAAADSRDXQRQH0m2++OaicS1JSki1YsMCaNm2aiS0EACB2BNEBAAAAAECaFC9e3J+JXrRoUStUqJD/ufz581vjxo3ttttuy8QWAgAQO4LoAAAAAAAgTcaOHev+rVq1qt1///2UbgEA5EgE0QEAAAAAQEz69euX2U0AACDdEEQHAAAAAABRO/vss2369OlWsmRJO+uss0JOLOqZN29ehrYNAIB4IogOAAAAAACi1qZNG/9Eom3bts3s5gAAkG4IogMAAAAAgDSXcElKSrIWLVrYmWeeaSVKlMjsZgEAEHd54r9JAAAAAACQWyQmJtpFF11k27dvz+ymAACQLgiiAwAAAACAmNSqVctWrFiR2c0AACBdEEQHAAAAAAAxGTBggN1///02depUW79+ve3atSvoBgBAdkZNdAAAAAAAEJNLL73U/XvFFVdYQkKCf7nP53OPVTcdAIDsiiA6AAAAAACIyYwZMzK7CQAApBuC6AAAAAAAICbNmzfP7CYA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ZEfKQFcAvWLFiqmuRxAdAAAAAABkGaVLl3bD8Tdu3Bi0XI/Lly8f8jVaHs36UqBAAXcLVKJEiZjaDmRnCqATREduVDyVDHTP/xWbAQAAAAAAyALy589v9erVs+nTpwdlietxkyZNQr5GywPXl6+++irs+gAARINMdAAAAAAAkKWozEqnTp2sfv361rBhQ3vhhRds79691rlzZ/f8TTfdZJUqVXJ1zaVHjx7WvHlze+6556x169b23nvv2a+//mqvvvpqJu8JACAnIIgOAAAAAACylHbt2tnmzZutb9++bnLQunXr2rRp0/yTh65evdry5PlvcH3Tpk1t/Pjx9uijj9rDDz9sp5xyin388cdWq1atTNwLIHtQWaN+/fqlKG8E4D8JPlVPBwAAAAAAAAAAKVATHQAAAAAAAACAMAiiAwAAAAAAAAAQBkF0AAAAAAAAAADCIIgOAAAAAAAAZBGPP/64m0w3vSUkJLgJeGNx8803W9u2bf2Pzz//fLv33nuzzTEAIkUQHQAAAAAAAIjShg0brHv37nbyySdbgQIFrHLlynb55Zfb9OnTM7tptnnzZrvzzjutSpUqrm3ly5e3Vq1a2U8//eRfZ/369XbJJZfE9D7Dhg2zcePGWbzdf//9QccxebAeyGh5M/wdAQAAAAAAgGxs1apVds4551iJEiVs8ODBVrt2bTt8+LB98cUX1q1bN1u6dGmmtu/qq6+2Q4cO2RtvvOGC/Bs3bnRB6a1bt/rXUWA9VsWLF7d48vl8lpSUZEWKFHE3IKsgEx0AAAAAAACIwl133eXKofzyyy8uYH3qqafaGWecYT179rTZs2f711u9erW1adPGBYSLFStm1113nQtoB3rmmWesXLlyVrRoUevSpYsdOHAgxfu99tprdtppp1nBggWtZs2a9vLLL4dt244dO+yHH36wQYMGWYsWLezEE0+0hg0bWp8+feyKK64IWc5FFwX0eOLEidasWTMrVKiQNWjQwP7880+bM2eO1a9f3+2DMteV5R5phvhbb73lXqt9U9D+hhtusE2bNvmf//bbb937fv7551avXj2XNf/jjz8GlXPRfV0MmDx5sltXN73uf//7n919991B76e25c+fP0uMBkDOQhAdAAAAAAAAiNC2bdts2rRpLuP8uOOOS/G8stPl6NGjLoCu9b/77jv76quvbMWKFdauXTv/ugpaK0j89NNP26+//moVKlRIESB/5513rG/fvvbUU0/ZH3/84dZ97LHHXGA5FC+LWwHygwcPRrVv/fr1s0cffdTmzZtnefPmdUHvBx980JVtUWB++fLlri2RUnb+k08+ab///rtrj4L1Crwn17t3b3cxQft35plnpijtoosPF198sStBo1vTpk3t1ltvtfHjxwft49tvv22VKlVyAXYgnijnAgAAAAAAAERIgWSVHVFGeGqUDb1w4UJbuXKlq5cub775pstYV3a3Mr1feOEFl32umwwYMMC+/vrroGx0Bbafe+45u+qqq9zjk046yZYsWWKjRo2yTp06pXhfBb9Vp/y2226zkSNH2tlnn23Nmze366+/PkWAOjkFrFU7XXr06GHt27d3+6HSNaJ2RlMD/ZZbbvHfV1mZF1980e33nj17gsq1PPHEE3bhhReG3IbWU2a8guWBJWh0PJSJrgx1BdlFbVOQXtnqQDyRiQ4AAAAAAABESAH0SCirWsFzL4Aup59+ustU13PeOo0aNQp6XZMmTfz39+7da3///bcLXnsZ5rop2K7l4ajEzL///muffPKJy+BW+RMF048VAA8MsqvEjKjee+CywHIsxzJ37lw32aomOFVJFwXzvTI3gVTyJVoqbdOxY0cbM2aMe6zs+UWLFoXMdAdiRSY6AAAAAAAAEKFTTjnFZTpnxOShytiW0aNHpwi2JyYmHjPIrOxu3VT+ReVPlNWeWpA5X758/vteNnfyZSpTEwldAFBWu24qSVOmTBkXPNdjTXoaKFRZnEhon1Q7fe3atTZ27FhXxkU14IF4IxMdAAAAAAAAiFCpUqVcIHjEiBEuUBxqYk/RRKBr1qxxN4/KsOh5ZaR76/z8889Brw+cmFSZ3xUrVnS11KtXrx50U1mXaOg9Q7U3vegiw9atW12tc01WqvI30WSxB9JkoUlJSSmWK0teWey6yKD66IHlY4B4IogOAAAAAAAAREEBdAV1GzZsaB988IH99ddfrjSLan575VhatmzpgrwdOnRwpUZ++eUXu+mmm1xJE698ieqOqxyJsqj//PNPlym+ePHioPfq37+/DRw40G1b66jOutYfOnRoyLYpcK2MbE2yuWDBAleT/f3337dnn33WTXSaUVTCRcHvl156yV0EUGkZTTKaFlWrVnX7smzZMtuyZYubsDQwG12BepXZufLKK+O4B8B/CKIDAAAAAAAAUdAkmQqMt2jRwnr16mW1atVyZVM0Cecrr7ziL32iSS9Llixp5513nguq63UTJkzwb6ddu3au1MqDDz5o9erVs3/++cfuvPPOoPdSkPi1115zgXMF5RWEV23zcJnoqpmu0i/PP/+8e1+1Te+hiUaHDx9uGUXlW9ROBfCVBa9A95AhQ9K0LbW9Ro0a7uKDtvvTTz/5n9Pkp5pMVf+qhA2QHhJ8kc6GAAAAAAAAAABZyKpVq6xatWo2Z84cN3kqkB4IogMAAAAAAADIVlTSRaVr7r//fleyJjA7HYg3yrkAAAAAAAAAyFYUNK9QoYLLQB85cmRmNwc5HJnoAAAAAAAAAACEQSY6AAAAAAAAAABhEEQHAAAAAAAAACAMgugAAAAAAAAAAIRBEB0AAAAAAAAAgDAIogMAAAAAAAAAEAZBdAAAAAAAAAAAwiCIDgAAAAAAAABAGATRAQAAAAAAAAAIgyA6AAAAAAAAAAAW2v8DizJbZKf1eWEAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "# Paths to the two code files\n", + "file_1_path = \"test_codes/input_code_7.c\" # Update with your actual file path\n", + "file_2_path = \"test_codes/input_code_8.c\" # Update with your actual file path\n", + "\n", + "# Read the content of both files\n", + "with open(file_1_path, \"r\") as f1:\n", + " code_snippet_1 = f1.read()\n", + "\n", + "with open(file_2_path, \"r\") as f2:\n", + " code_snippet_2 = f2.read()\n", + "\n", + "# Create figure with 1 row, 3 columns (Two code snippets + Similarity bar)\n", + "fig, axes = plt.subplots(1, 3, figsize=(15, 5))\n", + "\n", + "# Display Code Snippet 1\n", + "axes[0].text(0, 1, code_snippet_1, fontsize=10, family='monospace', verticalalignment='top')\n", + "axes[0].axis(\"off\") # Hide axes\n", + "axes[0].set_title(\"Code Snippet 1\")\n", + "\n", + "# Display Code Snippet 2\n", + "axes[1].text(0, 1, code_snippet_2, fontsize=10, family='monospace', verticalalignment='top')\n", + "axes[1].axis(\"off\") # Hide axes\n", + "axes[1].set_title(\"Code Snippet 2\")\n", + "\n", + "# Plot Similarity Score\n", + "axes[2].bar([\"Code Similarity\"], [similarity], color='blue', alpha=0.7)\n", + "axes[2].set_ylim(0, 1)\n", + "axes[2].set_ylabel(\"Similarity Score\")\n", + "axes[2].set_title(\"Code Similarity\")\n", + "axes[2].grid(axis=\"y\", linestyle=\"--\", alpha=0.6)\n", + "\n", + "# Adjust layout to fit text properly\n", + "plt.tight_layout()\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "MyPythonEnv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.10" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/cass/cass.py b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/cass/cass.py new file mode 100755 index 00000000000..336b65596f4 --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/cass/cass.py @@ -0,0 +1,357 @@ +'''MIT License + +Copyright (c) 2021 Intel Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +''' +from enum import Enum + +from .config import cass_config as config + + +class NodeType(Enum): + Internal = 0, + NumLit = 1, + CharLit = 2, + StringLit = 3, + GlobalVar = 4, + GlobalFun = 5, + LocalVar = 6, + LocalFun = 7, + FunSig = 8, + Error = 9 + + +class CassNode: + def __init__(self, node_type, label='', children=[]): + self.node_type = node_type + self.children = children + self.prev_use = None + self.next_use = None + self.parent = None + self.child_id = 0 + + self.removed = False + + if len(label) == 0: + self.label = label + self.n = label + + elif node_type == NodeType.FunSig: + if config.fsig_mode == 0: + self.n = None + else: + self.n = label + + elif node_type == NodeType.Internal: + assert label[0] == '#' + p = label[1:].find('#') + assert p > 0 + p += 2 + self.annot = label[:p] + self.label = label[p:] + + if self.annot == '#compound_statement#': + if config.compound_mode == 0: + pass + elif config.compound_mode == 1: + self.removed = True + elif config.compound_mode == 2: + self.label = '{#}' + else: + raise Exception() + + if config.annot_mode == 0: + self.n = self.label + elif config.annot_mode == 1: + self.n = self.annot + self.label + elif config.annot_mode == 2: + if self.annot == '#parenthesized_expression#' or self.annot == '#argument_list#': + self.n = self.annot + self.label + else: + self.n = self.label + else: + raise Exception() + + else: + if node_type == NodeType.LocalVar or node_type == NodeType.LocalFun: + self.n = '$VAR' + + elif node_type == NodeType.GlobalVar: + if config.gvar_mode == 0: + self.n = label + elif config.gvar_mode == 1: + self.n = label + self.removed = True + elif config.gvar_mode == 2: + self.n = '$GVAR' + elif config.gvar_mode == 3: + self.n = '$VAR' + else: + raise Exception() + + elif node_type == NodeType.GlobalFun: + if config.gfun_mode == 0: + self.n = label + elif config.gfun_mode == 1: + self.n = label + self.removed = True + elif config.gfun_mode == 2: + self.n = '$GFUN' + elif config.gfun_mode == 3: + if config.gvar_mode == 3: + self.n = '$VAR' + else: + self.n = '$GVAR' + else: + raise Exception() + + else: + self.n = label + + self.features = [] + + +class CassTree: + def __init__(self, nodes, leaf_nodes): + self.nodes = nodes + self.leaf_nodes = leaf_nodes + if nodes[0].node_type == NodeType.FunSig: + self.fun_sig_node = nodes[0] + self.root = nodes[1] + else: + self.fun_sig_node = None + self.root = nodes[0] + self.leaf_ranges = self._compute_leaf_ranges() + + def _compute_leaf_ranges(self): + node2leaf_id = {} + leaf_ranges = {} + for i, node in enumerate(self.leaf_nodes): + node2leaf_id[node] = i + + def compute_leaf_ranges_rec(node): + if len(node.children) == 0: + x = node2leaf_id[node] + leaf_ranges[node] = (x, x + 1) + else: + for c in node.children: + compute_leaf_ranges_rec(c) + leaf_ranges[node] = ( + leaf_ranges[node.children[0]][0], leaf_ranges[node.children[-1]][1]) + + compute_leaf_ranges_rec(self.root) + return leaf_ranges + + def _get_context(self, node): + assert not node.removed + + p = node.parent + if p is None: + return None + if p.label != '$.$': + if p.removed: + return None + return (node.child_id, p.n) + else: + for i in range(*(self.leaf_ranges[p])): + l = self.leaf_nodes[i] + if l.node_type == NodeType.GlobalVar or l.node_type == NodeType.GlobalFun: + if l.removed: + return None + return l.n + return None + + def featurize(self): + for i, node in enumerate(self.leaf_nodes): + if node.removed: + continue + + node.features.append(node.n) + + p = node + for _ in range(3): + cid = p.child_id + p = p.parent + if p is None: + break + if p.removed: + continue + node.features.append((node.n, cid, p.n)) + + if i > 0: + sib = self.leaf_nodes[i - 1] + if not sib.removed: + node.features.append((sib.n, node.n)) + if i < len(self.leaf_nodes) - 1: + sib = self.leaf_nodes[i + 1] + if not sib.removed: + node.features.append((node.n, sib.n)) + + if node.prev_use is not None: + if not node.prev_use.removed: + prev_ctx = self._get_context(node.prev_use) + ctx = self._get_context(node) + if prev_ctx is not None and ctx is not None: + node.features.append((prev_ctx, ctx)) + if node.next_use is not None: + if not node.next_use.removed: + ctx = self._get_context(node) + next_ctx = self._get_context(node.next_use) + if ctx is not None and next_ctx is not None: + node.features.append((ctx, next_ctx)) + + features = [] + for n in self.leaf_nodes: + features += n.features + + if config.fsig_mode == 1 and self.fun_sig_node is not None: + features.append(self.fun_sig_node.n) + + return features + + +def load_file(file_name): + casses = [] + with open(file_name) as f: + for line in f: + cass, _ = deserialize(line) + if cass is not None: + casses.append(cass) + return casses + + +def deserialize(s): + tokens = s.strip().split('\t') + src_range = tuple(map(int, tokens[0].split(','))) + assert len(src_range) == 4 + cass = deserialize_from_tokens(tokens[1:]) + return cass, src_range + + +def deserialize_from_tokens(tokens): + num_tokens = len(tokens) + if num_tokens == 0: + return None + + num_nodes = int(tokens[0]) + + nodes = [] + leaf_nodes = [] + + i = 1 + + has_fun_sig = False + if tokens[i][0] == 'S': + has_fun_sig = True + fun_sig = tokens[i] + i += 1 + fun_sig = fun_sig[1:] + nodes.append(CassNode(NodeType.FunSig, fun_sig)) + + while i < num_tokens: + node_type_label = tokens[i] + i += 1 + node_type_str = node_type_label[0] + label = node_type_label[1:] + if node_type_str == 'I': + num_child = int(tokens[i]) + i += 1 + nodes.append( + CassNode(NodeType.Internal, label, [None] * num_child)) + elif node_type_str == 'N': + node = CassNode(NodeType.NumLit, label) + nodes.append(node) + leaf_nodes.append(node) + elif node_type_str == 'C': + node = CassNode(NodeType.CharLit, label) + nodes.append(node) + leaf_nodes.append(node) + elif node_type_str == 'S': + node = CassNode(NodeType.StringLit, label) + nodes.append(node) + leaf_nodes.append(node) + elif node_type_str == 'V': + node = CassNode(NodeType.GlobalVar, label) + nodes.append(node) + leaf_nodes.append(node) + elif node_type_str == 'F': + node = CassNode(NodeType.GlobalFun, label) + nodes.append(node) + leaf_nodes.append(node) + elif node_type_str == 'v': + prev_use = int(tokens[i]) + next_use = int(tokens[i + 1]) + i += 2 + node = CassNode(NodeType.LocalVar, label) + node.prev_use = prev_use + node.next_use = next_use + nodes.append(node) + leaf_nodes.append(node) + elif node_type_str == 'f': + prev_use = int(tokens[i]) + next_use = int(tokens[i + 1]) + i += 2 + node = CassNode(NodeType.LocalFun, label) + node.prev_use = prev_use + node.next_use = next_use + nodes.append(node) + leaf_nodes.append(node) + elif node_type_str == 'E': + node = CassNode(NodeType.Error) + nodes.append(node) + leaf_nodes.append(node) + else: + raise Exception() + + assert num_nodes == len(nodes) + + for n in nodes: + if n.node_type == NodeType.LocalVar or n.node_type == NodeType.LocalFun: + if n.prev_use >= 0: + n.prev_use = nodes[n.prev_use] + else: + n.prev_use = None + if n.next_use >= 0: + n.next_use = nodes[n.next_use] + else: + n.next_use = None + + if has_fun_sig: + tree_start = 1 + else: + tree_start = 0 + + root, rem_nodes = build_tree_rec(nodes[tree_start:]) + + assert root == nodes[tree_start] + assert len(rem_nodes) == 0 + + return CassTree(nodes, leaf_nodes) + + +def build_tree_rec(nodes): + node = nodes[0] + nodes = nodes[1:] + for i in range(len(node.children)): + child, nodes = build_tree_rec(nodes) + child.parent = node + child.child_id = i + node.children[i] = child + return node, nodes \ No newline at end of file diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/cass/config.py b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/cass/config.py new file mode 100755 index 00000000000..e33b92f3a57 --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/cass/config.py @@ -0,0 +1,31 @@ +''' +MIT License + +Copyright (c) 2021 Intel Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +''' +from types import SimpleNamespace +cass_config = SimpleNamespace( +annot_mode=2, +compound_mode=2, +gvar_mode=3, +gfun_mode=1, +fsig_mode=1 +) diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/data/datasets/poj/dataset-gnn/vocab.pkl b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/data/datasets/poj/dataset-gnn/vocab.pkl new file mode 100644 index 00000000000..840cf07b2cd Binary files /dev/null and b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/data/datasets/poj/dataset-gnn/vocab.pkl differ diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/data/datasets/split_gcj.pkl b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/data/datasets/split_gcj.pkl new file mode 100644 index 00000000000..8846e8cee0b Binary files /dev/null and b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/data/datasets/split_gcj.pkl differ diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/data/datasets/split_poj.pkl b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/data/datasets/split_poj.pkl new file mode 100644 index 00000000000..349d8ae33e9 Binary files /dev/null and b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/data/datasets/split_poj.pkl differ diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/data/models/poj/gnn/model.pt b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/data/models/poj/gnn/model.pt new file mode 100644 index 00000000000..557cdfec95e Binary files /dev/null and b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/data/models/poj/gnn/model.pt differ diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/interface/README.md b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/interface/README.md new file mode 100755 index 00000000000..f8d8041c1bd --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/interface/README.md @@ -0,0 +1,37 @@ +# MISIM Interface + +## Setup + +Use this [Makefile](../Makefile). + +Vocabularies are available [here](https://www.dropbox.com/s/zilq32a4s9pygde/datasets.tar.xz). +Pre-trained models are available [here](https://www.dropbox.com/s/jlfp2oypzkc29q7/models.tar.xz). +Extract them into `../data/`. + +## Usage example: +```python +import misim.interface as misim + +cass_manager = misim.CASSManager() +gnn_preprocessor = misim.GNNPreprocessor('misim/data/datasets/poj/dataset-gnn/vocab.pkl') +gnn_runner = misim.GNNRunner('misim/data/datasets/poj/dataset-gnn/vocab.pkl', 'misim/data/models/poj/gnn/0/model.pt') + +# Compute GNN feature vectors for each function/loop in a source file. +cass_strs = cass_manager.extract_cass_strs_from_src_file('test.c', extract_loops=True) +casses, src_ranges = cass_manager.load_casses_from_strs(cass_strs) +inputs = gnn_preprocessor.preprocess_casses_seperated(casses) +vectors = gnn_runner.compute_code_vector_batched(inputs) +for i in range(len(src_ranges)): + print(src_ranges[i], vectors[i]) + +# Compute code similarity between two source files. +cass_strs_1 = cass_manager.extract_cass_strs_from_src_file('test1.c', extract_loops=False) +cass_strs_2 = cass_manager.extract_cass_strs_from_src_file('test2.c', extract_loops=False) +casses_1, _ = cass_manager.load_casses_from_strs(cass_strs_1) +casses_2, _ = cass_manager.load_casses_from_strs(cass_strs_2) +input_1 = gnn_preprocessor.preprocess_casses_combined(casses_1) +input_2 = gnn_preprocessor.preprocess_casses_combined(casses_2) +vectors = gnn_runner.compute_code_vector_batched([input1, input2]) +from numpy.linalg import norm +similarity = (vector[0] @ vector[1].T) / (norm(vector[0]) * norm(vector[1])) +``` diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/interface/__init__.py b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/interface/__init__.py new file mode 100755 index 00000000000..370caea5d6a --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/interface/__init__.py @@ -0,0 +1,26 @@ +''' +MIT License + +Copyright (c) 2021 Intel Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +''' +from .cass_manager import CASS, CASSManager, SourceRange +from .model_preprocessor import ModelPreprocessor, GNNPreprocessor +from .model_runner import ModelRunner, GNNRunner diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/interface/cass_manager.py b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/interface/cass_manager.py new file mode 100755 index 00000000000..ea9913dcb09 --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/interface/cass_manager.py @@ -0,0 +1,81 @@ +''' +MIT License + +Copyright (c) 2021 Intel Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +''' +from pathlib import Path +import subprocess +from collections import namedtuple +from typing import Tuple, Union, List + +from ..cass import cass + + +CASS = cass.CassTree +SourceRange = namedtuple('SourceRange', ['start_line', 'start_column', 'end_line', 'end_column']) + + +class CASSManager: + def __init__(self, use_c_parser: bool = False) -> None: + cass_extractor: str = str( + Path(__file__).parent.parent / + 'cass-extractor' / 'build' / 'bin' / 'cass-extractor' + ) + self.extract_command = [cass_extractor] + if use_c_parser: + self.extract_command.append('-c') + + def extract_cass_strs_from_src_file(self, file_name: str, extract_loops: bool) -> List[str]: + """ + Extract CASSes from the given source file. Return a list of serialized CASS strings. + If extract_loops is True, each function and loop will be extracted as a CASS, otherwise only functions will be extracted. + """ + cmd = self.extract_command + ['-l'] if extract_loops \ + else self.extract_command + output = subprocess.check_output( + cmd + ['-f', file_name], encoding='utf-8') + return output.splitlines() + + def extract_cass_strs_from_src_text(self, src_text: Union[str, bytes], extract_loops: bool) -> List[str]: + """ + Extract CASSes from the given source text. Return a list of serialized CASS strings. + If extract_loops is True, each function and loop will be extracted as a CASS, otherwise only functions will be extracted. + """ + cmd = self.extract_command + ['-l'] if extract_loops \ + else self.extract_command + output = subprocess.check_output(cmd, input=src_text, encoding='utf-8') + return output.splitlines() + + @staticmethod + def load_cass_from_str(cass_str: str) -> Tuple[CASS, SourceRange]: + c, src_range = cass.deserialize(cass_str) + assert c + return c, SourceRange(*src_range) + + @staticmethod + def load_casses_from_strs(cass_strs: List[str]) -> Tuple[List[CASS], List[SourceRange]]: + casses = [] + src_ranges = [] + for cass_str in cass_strs: + c, rng = CASSManager.load_cass_from_str(cass_str) + casses.append(c) + src_ranges.append(rng) + return casses, src_ranges diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/interface/model_preprocessor.py b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/interface/model_preprocessor.py new file mode 100755 index 00000000000..d29cb2052ff --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/interface/model_preprocessor.py @@ -0,0 +1,101 @@ +''' +MIT License + +Copyright (c) 2021 Intel Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +''' +import pickle +import numpy as np +from typing import List, Tuple, Dict, Union, cast, Generic, TypeVar +from abc import ABC, abstractmethod + +from ..cass import cass +from .cass_manager import CASS + + +T = TypeVar('T') + + +class ModelPreprocessor(ABC, Generic[T]): + @abstractmethod + def preprocess_cass(self, cass: CASS) -> T: + pass + + @abstractmethod + def preprocess_casses_combined(self, casses: List[CASS]) -> T: + """ + Convert multiple CASSes into one instace of model input. + """ + pass + + @abstractmethod + def preprocess_casses_seperated(self, casses: List[CASS]) -> List[T]: + """ + Convert each CASS into one instace of model input. + """ + pass + + +class GNNPreprocessor(ModelPreprocessor[Tuple[np.ndarray, np.ndarray]]): + def __init__(self, vocab: Union[str, Dict[str, int]]) -> None: + if isinstance(vocab, str): + with open(vocab, 'rb') as f: + self.vocab: Dict[str, int] = pickle.load(f) + else: + self.vocab = vocab + + def preprocess_cass(self, cass: CASS) -> Tuple[np.ndarray, np.ndarray]: + """ + Build a graph from one CASS. + """ + nodes = [] + edges = [] + build_graph(cass.root, nodes, edges) + if cass.fun_sig_node is not None and cass.fun_sig_node.n is not None: + nodes.append(cass.fun_sig_node.n) + return np.array([self.vocab.get(t, 0) for t in nodes]), np.array(edges).T + + def preprocess_casses_combined(self, casses: List[CASS]) -> Tuple[np.ndarray, np.ndarray]: + """ + Build a graph from a list of CASSes. + """ + nodes = [] + edges = [] + for cass in casses: + build_graph(cass.root, nodes, edges) + if cass.fun_sig_node is not None and cass.fun_sig_node.n is not None: + nodes.append(cass.fun_sig_node.n) + return np.array([self.vocab.get(t, 0) for t in nodes]), np.array(edges).T + + def preprocess_casses_seperated(self, casses: List[CASS]) -> List[Tuple[np.ndarray, np.ndarray]]: + """ + Build a list of graphs from a list of CASSes. + """ + return [self.preprocess_cass(c) for c in casses] + + +def build_graph(node: cass.CassNode, nodes: List[str], edges: List[Tuple[int, int]]): + node_id = len(nodes) + nodes.append(cast(str, node.n)) + last_id = node_id + for c in node.children: + edges.append((node_id, last_id + 1)) + last_id = build_graph(c, nodes, edges) + return last_id diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/interface/model_runner.py b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/interface/model_runner.py new file mode 100755 index 00000000000..6356425ee09 --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/interface/model_runner.py @@ -0,0 +1,90 @@ +''' +MIT License + +Copyright (c) 2021 Intel Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +''' +import pickle +import numpy as np +from abc import ABC, abstractmethod +from typing import Iterable, Tuple, Dict, Generic, TypeVar, Union +import torch + +from ..models.gnn_model import GNNModel + + +T = TypeVar('T') + + +class ModelRunner(ABC, Generic[T]): + @abstractmethod + def compute_code_vector(self, preprocessed_cass: T) -> np.ndarray: + """ + Compute a code vector for preprocessed CASS. + Return a 1D array with shape (vector_size,). + """ + pass + + @abstractmethod + def compute_code_vector_batched(self, preprocessed_cass_batch: Iterable[T]) -> np.ndarray: + """ + Compute code vectors for a batch of preprocessed CASS. + Return a 2D array with shape (batch_size, vector_size). + """ + pass + + +class GNNRunner(ModelRunner[Tuple[np.ndarray, np.ndarray]]): + def __init__(self, vocab: Union[str, Dict[str, int]], model_path: str, output_size: int = 128, + node_emb_size: int = 128, num_layers: int = 3, device: str = 'cpu') -> None: + if isinstance(vocab, str): + with open(vocab, 'rb') as f: + self.vocab: Dict[str, int] = pickle.load(f) + else: + self.vocab = vocab + self.device = torch.device(device) + self.model = GNNModel(node_emb_size, len(self.vocab), + output_size, num_layers) + self.model.load_state_dict(torch.load( + model_path, map_location=self.device)) + + def compute_code_vector(self, preprocessed_cass: Tuple[np.ndarray, np.ndarray]) -> np.ndarray: + return np.squeeze(self.compute_code_vector_batched([preprocessed_cass]), axis=0) + + def compute_code_vector_batched(self, preprocessed_cass_batch: Iterable[Tuple[np.ndarray, np.ndarray]]) -> np.ndarray: + num_prev_nodes = 0 + nodes_batch = [] + edges_batch = [] + indices_batch = [] + for i, (nodes, edges) in enumerate(preprocessed_cass_batch): + num_nodes = nodes.shape[0] + nodes_batch.append(torch.from_numpy(nodes)) + edges_batch.append(torch.from_numpy(edges) + num_prev_nodes) + num_prev_nodes += num_nodes + indices_batch.append(torch.full((num_nodes,), i, dtype=torch.long)) + + batched_nodes = torch.cat(nodes_batch).to(self.device) + batched_edges = torch.cat(edges_batch, dim=1).to(self.device) + batched_indices = torch.cat(indices_batch).to(self.device) + + with torch.no_grad(): + v: torch.Tensor = self.model(batched_nodes, batched_edges, batched_indices) + + return v.detach().cpu().numpy() diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/models/bof_model.py b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/models/bof_model.py new file mode 100755 index 00000000000..132c623ce40 --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/models/bof_model.py @@ -0,0 +1,42 @@ +''' +MIT License + +Copyright (c) 2021 Intel Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +''' +import torch +import torch.nn as nn +from torch_scatter import scatter_mean + + +class BagOfFeaturesModel(nn.Module): + def __init__(self, feature_emb_size, feature_vocab_size, output_size): + super().__init__() + self.feature_emb = nn.Sequential( + nn.Embedding(feature_vocab_size, feature_emb_size), + nn.Dropout(0.5) + ) + self.out = nn.Sequential( + nn.Linear(feature_emb_size, output_size) + ) + + def forward(self, features, indices): + feature_emb = self.feature_emb(features) + return self.out(scatter_mean(feature_emb, indices, dim=0)) diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/models/c2s_model.py b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/models/c2s_model.py new file mode 100755 index 00000000000..b8eb7bde3ff --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/models/c2s_model.py @@ -0,0 +1,75 @@ +''' +MIT License + +Copyright (c) 2021 Intel Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +''' +import torch +import torch.nn as nn +from torch.nn.utils.rnn import PackedSequence +from torch_scatter import scatter_add +from torch_scatter.composite import scatter_softmax + + +class C2SModel(nn.Module): + def __init__(self, emb_size, subtoken_vocab_size, node_vocab_size, rnn_size, decoder_size, output_size): + super().__init__() + self.subtoken_emb = nn.Embedding(subtoken_vocab_size, emb_size) + self.node_emb = nn.Embedding(node_vocab_size, emb_size) + self.path_rnn = nn.LSTM(emb_size, rnn_size // 2, bidirectional=True, batch_first=True) + + self.emb_dropout = nn.Dropout(0.25) + + self.fc = nn.Sequential( + nn.Linear(emb_size * 2 + rnn_size, + decoder_size, bias=False), + nn.Tanh() + ) + + self.a = nn.Parameter(torch.empty(decoder_size, dtype=torch.float)) + nn.init.uniform_(self.a) + + self.out = nn.Linear(decoder_size, output_size) + + def forward(self, ll_subtokens, ll_indices, rl_subtokens, rl_indices, paths, indices): + ll_emb = scatter_add(self.subtoken_emb(ll_subtokens), ll_indices, dim=0) + rl_emb = scatter_add(self.subtoken_emb(rl_subtokens), rl_indices, dim=0) + + _, (h, _) = self.path_rnn( + PackedSequence( + self.node_emb(paths.data), + paths.batch_sizes, + paths.sorted_indices, + paths.unsorted_indices + ) + ) # (2, batch_context_num, rnn_size // 2) + path_nodes_aggregation = torch.cat((h[0], h[1]), dim=1) # (batch_context_num, rnn_size) + + context_emb = torch.cat((ll_emb, path_nodes_aggregation, rl_emb), dim=1) + context_emb = self.emb_dropout(context_emb) + + context_emb = self.fc(context_emb) # (batch_context_num, decoder_size) + + attn_score = torch.matmul(context_emb, self.a) + attn_weight = scatter_softmax(attn_score, indices, dim=0) + weighted_context = context_emb * attn_weight.unsqueeze(1) + v = scatter_add(weighted_context, indices, dim=0) + + return self.out(v) diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/models/c2v_model.py b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/models/c2v_model.py new file mode 100755 index 00000000000..ad036b3ab62 --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/models/c2v_model.py @@ -0,0 +1,61 @@ +''' +MIT License + +Copyright (c) 2021 Intel Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +''' +import torch +import torch.nn as nn +from torch_scatter import scatter_add +from torch_scatter.composite import scatter_softmax + + +class C2VModel(nn.Module): + def __init__(self, leaf_emb_size, leaf_vocab_size, path_emb_size, path_vocab_size, code_vec_size, output_size): + super().__init__() + self.leaf_emb = nn.Embedding(leaf_vocab_size, leaf_emb_size) + self.path_emb = nn.Embedding(path_vocab_size, path_emb_size) + self.emb_dropout = nn.Dropout(0.25) + + self.fc = nn.Sequential( + nn.Linear(leaf_emb_size * 2 + path_emb_size, + code_vec_size, bias=False), + nn.Tanh() + ) + + self.a = nn.Parameter(torch.empty(code_vec_size, dtype=torch.float)) + nn.init.uniform_(self.a) + + self.out = nn.Linear(code_vec_size, output_size) + + def forward(self, contexts, indices): + context_emb = torch.cat( + (self.leaf_emb(contexts[:, 0]), self.path_emb(contexts[:, 1]), self.leaf_emb(contexts[:, 2])), + dim=1 + ) + context_emb = self.emb_dropout(context_emb) + context_emb = self.fc(context_emb) + + attn_score = torch.matmul(context_emb, self.a) + attn_weight = scatter_softmax(attn_score, indices, dim=0) + weighted_context = context_emb * attn_weight.unsqueeze(1) + v = scatter_add(weighted_context, indices, dim=0) + + return self.out(v) diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/models/circle_loss.py b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/models/circle_loss.py new file mode 100755 index 00000000000..398a6352f60 --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/models/circle_loss.py @@ -0,0 +1,42 @@ +''' +MIT License + +Copyright (c) 2021 Intel Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +''' +import torch +import torch.nn as nn +import torch.nn.functional as F + + +class CircleLoss(nn.Module): + def __init__(self, gamma, m): + super().__init__() + self.gamma = gamma + self.m = m + + def forward(self, s_p, s_n): + alpha_p = torch.clamp_min(1 + self.m - s_p, 0) + alpha_n = torch.clamp_min(self.m + s_n, 0) + delta_p = 1 - self.m + delta_n = self.m + logit_p = (-self.gamma) * alpha_p * (s_p - delta_p) + logit_n = self.gamma * alpha_n * (s_n - delta_n) + return F.softplus(torch.logsumexp(logit_p, dim=0) + torch.logsumexp(logit_n, dim=0)) diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/models/gnn_model.py b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/models/gnn_model.py new file mode 100755 index 00000000000..d5cbf54c25f --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/models/gnn_model.py @@ -0,0 +1,71 @@ +''' +MIT License + +Copyright (c) 2021 Intel Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +''' +import torch +import torch.nn as nn +from torch_scatter import scatter_max, scatter_mean + + +class GNNModel(nn.Module): + def __init__(self, node_emb_size, node_vocab_size, output_size, num_layers): + super().__init__() + self.num_layers = num_layers + self.node_emb = nn.Sequential( + nn.Embedding(node_vocab_size, node_emb_size), + nn.Dropout(0.5) + ) + self.gnn_layers = nn.ModuleList( + [RGCNLayer(node_emb_size) for _ in range(num_layers)]) + self.out = nn.Sequential( + nn.Linear(node_emb_size * 2, output_size) + ) + + def forward(self, nodes, edges, indices): + h = self.node_emb(nodes) + for i in range(self.num_layers): + h = self.gnn_layers[i](h, edges) + + v = torch.cat( + ( + scatter_mean(h, indices, dim=0), + scatter_max(h, indices, dim=0)[0] + ), + dim=1 + ) + + return self.out(v) + + +class RGCNLayer(nn.Module): + def __init__(self, node_emb_size): + super().__init__() + self.W0 = nn.Linear(node_emb_size, node_emb_size, bias=False) + self.W1 = nn.Linear(node_emb_size, node_emb_size, bias=False) + self.W2 = nn.Linear(node_emb_size, node_emb_size, bias=False) + + def forward(self, nodes, edges): + nbr_msg = torch.cat( + (self.W1(nodes[edges[0]]), self.W2(nodes[edges[1]])), dim=0) + msg = scatter_mean(nbr_msg, torch.cat( + (edges[1], edges[0])), dim=0, dim_size=nodes.size(0)) + return torch.relu(self.W0(nodes) + msg) diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/models/ncc_model.py b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/models/ncc_model.py new file mode 100755 index 00000000000..5af72a0399f --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/models/ncc_model.py @@ -0,0 +1,57 @@ +''' +MIT License + +Copyright (c) 2021 Intel Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +''' +import torch +import torch.nn as nn +from torch.nn.utils.rnn import PackedSequence + + +class NCCModel(nn.Module): + def __init__(self, inst2vec_emb, rnn_size, dense_size, output_size, use_i2v_emb): + super().__init__() + if use_i2v_emb: + self.emb = nn.Embedding.from_pretrained(inst2vec_emb, freeze=True) + else: + self.emb = nn.Embedding(inst2vec_emb.size(0), inst2vec_emb.size(1)) + self.rnn = nn.LSTM(self.emb.embedding_dim, rnn_size, num_layers=2) + self.batch_norm = nn.BatchNorm1d(rnn_size) + self.out = nn.Sequential( + nn.Linear(rnn_size, dense_size), + nn.ReLU(), + nn.Linear(dense_size, output_size) + ) + + def forward(self, seqs): + seqs = PackedSequence( + self.emb(seqs.data), + seqs.batch_sizes, + seqs.sorted_indices, + seqs.unsorted_indices + ) + + _, (hn, _) = self.rnn(seqs) + x = hn[-1] + + x = self.batch_norm(x) + + return self.out(x) diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/models/sbt_model.py b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/models/sbt_model.py new file mode 100755 index 00000000000..b380514358b --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MICSAS/models/sbt_model.py @@ -0,0 +1,65 @@ +''' +MIT License + +Copyright (c) 2021 Intel Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +''' +import torch +import torch.nn as nn +from torch.nn.utils.rnn import PackedSequence +from torch_scatter import scatter_max, scatter_mean + + +class SBTModel(nn.Module): + def __init__(self, token_emb_size, token_vocab_size, hidden_size, output_size): + super().__init__() + self.hidden_size = hidden_size + self.token_emb = nn.Sequential( + nn.Embedding(token_vocab_size, token_emb_size), + nn.Dropout(0.5) + ) + self.rnn = nn.GRU(token_emb_size, hidden_size, bidirectional=True) + self.proj = nn.Sequential( + nn.Linear(hidden_size * 4, output_size) + ) + + def forward(self, seqs, indices): + seqs = PackedSequence( + self.token_emb(seqs.data), + seqs.batch_sizes, + seqs.sorted_indices, + seqs.unsorted_indices + ) + + _, hn = self.rnn(seqs) + + h = torch.cat((hn[0], hn[1]), dim=1) + + v = torch.cat( + ( + scatter_mean(h, indices, dim=0), + scatter_max(h, indices, dim=0)[0] + ), + dim=1 + ) + + v = self.proj(v) + + return v diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MyCASSVisitor.py b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MyCASSVisitor.py new file mode 100644 index 00000000000..cf2bceaf45b --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/MyCASSVisitor.py @@ -0,0 +1,845 @@ +from CASSVisitor import CASSVisitor +from CASSParser import CASSParser +from CASSNode import CassNode + +""" +Original implementation by IntelLabs: https://github.com/IntelLabs/MICSAS/tree/master + +Cass strings in the original implementation come with a configuration setup. This is the setup (most likely) used in this project and decides on whether a node gets a label. + + annot_mode : Annotations + compound_mode: Compound Statements + gvar_mode : Global Variable + gfun_mode : Global Function + fsig_mode : Function Signatures + +Unfortunately we don't know for sure how labels influence the Cass string as changing the configuration inside the original implementation cloned from GitHub has no effect on the final output string. + +A Node according to the original implementation consists of 3 parts: + 1) Node type : + -> I : Internal + -> N / C / S : Number / Char / String literal + -> F : Global function + -> f : Local function + -> V : Global variable + -> v : Local variable + -> S : Function signature + -> E : Error * not implemented + + 2) Annotation : #compound_statement#, #init_declarator# etc. + 3) Labels : #VAR, #GVAR, #GFUN, {#} ? + +In this project we focused on replicating the Cass string building logic of the original implementation to be able to then vectorize it and calculate a similarity score. Our nodes include node types, annotations, observed suffixes representing a binary operation or the number of immediate children nodes using dollar signs "$=$", {$$$} as well as variable/function names. While building the string, nodes are seperated using '\t' followed with either number of immediate children nodes like '2' or the number (id) of the node that a local variable/function has been previously used it, and the node number where it will next be used it (current node numer). + +Here's an example of a Cass string for a simple program obtained from the original implementation showing the structure we were trying to implement : + +int summation(int start_val, int end_val) { + int sum = 0; + for (int i = start_val; i <= end_val; ++i) { + sum += i; + } + return sum; +} + +['0,0,6,1\t23\tS#FS#1_2\tI#compound_statement#{$$$}\t3\tI#declaration#int$;\t1\tI#init_declarator#$=$\t2\tvsum\t-1\t19\tN0\tI#for_statement#for($$;$)$\t4\tI#declaration#int$;\t1\tI#init_declarator#$=$\t2\tvi\t-1\t12\tvstart_val\t-1\t-1\tI#binary_expression#$<=$\t2\tvi\t9\t15\tvend_val\t-1\t-1\tI#update_expression#++$\t1\tvi\t12\t20\tI#compound_statement#{$}\t1\tI#expression_statement#$;\t1\tI#assignment_expression#$+=$\t2\tvsum\t4\t22\tvi\t15\t-1\tI#return_statement#return$;\t1\tvsum\t19\t-1\t'] + +-> 0,0,6,1 being the source range (start_row, start_col, end_row, end_col) +-> first number is the total number of nodes, here 23 +""" + + + +class MyCassVisitor(CASSVisitor): + + def __init__(self): + super().__init__() + self.scopes = [] # keeping track of scopes to differenciate between local/global + self.function_nesting_level = 0 + + def visitProg(self, ctx: CASSParser.ProgContext): + + root = CassNode("removed") # Root node to remove later + + for statement in ctx.statement(): + node = self.visit(statement) + + if isinstance(node, CassNode): + # If the node is a top-level function, treat it separately + if node.label.startswith("S#FS#"): + root.add_child(node) + else: + # Other statements or nested functions are added normally + if root.children: + root.children[-1].add_child(node) + else: + root.add_child(node) + + return root + + def visitFunctionDefinition(self, ctx: CASSParser.FunctionDefinitionContext): + """ + If we're at the top (most global) level, we produce an "S#FS#..." node. + If it's nested (within another function), we produce an "I#function_definition#...". + But in both cases, we push a scope so that parameters and local variables become local. + """ + + self.function_nesting_level += 1 + # 1) Push a new scope so that parameters/locals are recognized as local + self.scopes.append(set()) + in_global_scope = (self.function_nesting_level == 1) + + # 2) Build the function node label + func_type_text = ctx.typeSpec().getText() + params_num = 0 + if ctx.parameterList(): + params_num = len(ctx.parameterList().parameter()) + + if in_global_scope: + # This is the very first function => produce "S#FS#..." style + func_type = 0 if func_type_text == 'void' else 1 + # Possibly clamp param count + if params_num > 2: + params_num = 2 + node = CassNode(f"S#FS#{func_type}_{params_num}") + + start_line = ctx.start.line -1 + start_col = ctx.start.column + end_line = ctx.stop.line -1 + end_col = ctx.stop.column +1 + + node.source_range = (start_line, start_col, end_line, end_col) + else: + # Nested function => produce "I#function_definition#..." style + node = CassNode(f"I#function_definition#{func_type_text}$$") + + + if not in_global_scope: + # Create "I#function_declarator#$$" or similar + if params_num == 0: + decl_label = "I#function_declarator#$()" + else: + decl_label = "I#function_declarator#$$" + decl_node = CassNode(decl_label) + + # The function name (the grammar has "primaryExpression" after typeSpec) + func_name = ctx.primaryExpression().getText() + decl_node.add_child(CassNode(f"v{func_name}")) + + # If there are parameters, build the param list (which also adds them to scope) + if ctx.parameterList(): + param_list_node = self.visitParameterList(ctx.parameterList()) + decl_node.add_child(param_list_node) + + node.add_child(decl_node) + else: + + if ctx.parameterList(): + self.visitParameterList(ctx.parameterList()) # Adds param names to scope + + # 4) Visit the compound statement so local declarations become part of the scope + block_node = self.visit(ctx.compoundStatement()) + node.add_child(block_node) + + # 5) Pop the scope after finishing + self.scopes.pop() + + self.function_nesting_level -= 1 + + return node + + def visitParameterList(self, ctx: CASSParser.ParameterListContext): + + num_params = len(ctx.parameter()) + placeholders = ",".join(["$"] * num_params) + node = CassNode(f'I#parameter_list#({placeholders})') + #node.add_child(CassNode(f"{num_params}")) + for p in ctx.parameter(): + node.add_child(self.visit(p)) + return node + + def visitParameter(self, ctx: CASSParser.ParameterContext): + + param_type = ctx.typeSpec().getText() + param_name = ctx.primaryExpression().getText() + + if self.scopes: + self.scopes[-1].add(param_name) + + node = CassNode(f"I#parameter_declaration#{param_type}$") + #node.add_child(CassNode("1")) + node.add_child(self.visit(ctx.primaryExpression())) + + return node + + def visitCompoundStatement(self, ctx: CASSParser.CompoundStatementContext): + # 1) Push a new empty set for local declarations in this block + self.scopes.append(set()) + + # Count the number of direct statements (children) in the compound statement + num_children = len(ctx.statement()) + dollar_signs = "$" * num_children # Create the correct number of $ placeholders + block_node = CassNode(f"I#compound_statement#{{{dollar_signs}}}") + #block_node.add_child(CassNode(F"{num_children}")) + block_node.is_in_comp_stmt = True + + # Add each statement as a direct child + for st in ctx.statement(): + stmt_node = self.visit(st) + #stmt_node.is_in_comp_stmt = True + block_node.add_child(stmt_node) + + # 4) Pop the scope after leaving this block + self.scopes.pop() + return block_node + + def visitIncludeStatement(self, ctx: CASSParser.IncludeStatementContext): + return CassNode("removed") + + def visitDeclarationStatement(self, ctx: CASSParser.DeclarationStatementContext): + + type_label = ctx.typeSpec().getText() + + decl_node = CassNode(f"I#declaration#{type_label}$;") + + #Array handling + if ctx.arrayDeclarator(): + + if ctx.emptyInitializer() or ctx.nullptr() or ctx.expression() : + + placeholder = '$' + + array_decl = self.visit(ctx.arrayDeclarator()) + + if(ctx.emptyInitializer()): + placeholder = '{}' + + if(ctx.nullptr()): + placeholder = "nullptr" + + if ctx.expression(): + helperNode = self.visit(ctx.expression()) + array_decl.add_child(helperNode) + + + init_decl = CassNode(f"I#init_declarator#$={placeholder}") + init_decl.add_child(array_decl) + decl_node.add_child(init_decl) + + else: + + decl_node.add_child(self.visit(ctx.arrayDeclarator())) + + return decl_node + + + if ctx.primaryExpression(): + + # Mark this variable as local in the top scope + var_name = ctx.primaryExpression().getText() + + if len(self.scopes) > 0: + self.scopes[-1].add(var_name) + + pointer_node = CassNode("I#pointer_declarator#*$") + + if ctx.POINTER() and not(ctx.nullptr()) and not(ctx.emptyInitializer()) and not(ctx.expression()) : + + pointer_node.add_child(self.visit(ctx.primaryExpression())) + decl_node.add_child(pointer_node) + return decl_node + + if ctx.expression() or ctx.nullptr() or ctx.emptyInitializer(): + + placeholder = '$' + + if(ctx.emptyInitializer()): + placeholder = '{}' + + if(ctx.nullptr()): + placeholder = "nullptr" + + if ctx.expression(): + helperNode = self.visit(ctx.expression()) + + assign_node = CassNode(f"I#init_declarator#$={placeholder}") + + if(ctx.POINTER()): + + pointer_node.add_child(self.visit(ctx.primaryExpression())) + + if ctx.expression(): + pointer_node.add_child(helperNode) + + assign_node.add_child(pointer_node) + + else : + + assign_node.add_child(self.visit(ctx.primaryExpression())) + + if ctx.expression(): + assign_node.add_child(helperNode) + + + decl_node.add_child(assign_node) + + else: + + decl_node.add_child(self.visit(ctx.primaryExpression())) + + return decl_node + + + def visitListInitializer(self, ctx: CASSParser.ListInitializerContext): + + placeholders = ",".join(["$"] * len(ctx.primaryExpression())) + list_init = CassNode(f"I#initializer_list#{{{placeholders}}}") + for c in ctx.primaryExpression(): + list_init.add_child(self.visit(c)) + + return list_init + + def visitForBlockStatement(self, ctx: CASSParser.ForBlockStatementContext): + + for_node = CassNode(f"I#for_statement#for($$;$)$") + #for_node.add_child(CassNode("4")) + + # Initialization (forInit) + if ctx.declarationStatement(): + init_node = self.visit(ctx.declarationStatement()) + else: + init_node = self.visit(ctx.assignmentExpression()) + + for_node.add_child(init_node) + + cond_node = self.visit(ctx.logicalOrExpression()) + for_node.add_child(cond_node) + + # Update (forUpdate) + if ctx.unaryExpression(): + update_node = self.visit(ctx.unaryExpression()) + else: + for_node.add_child(CassNode("EMPTY_UPDATE")) + + for_node.add_child(update_node) + + + # Body (multiple statements in the block) + for_node.add_child(self.visit(ctx.compoundStatement())) + + return for_node + + + def visitForSingleStatement(self, ctx: CASSParser.ForSingleStatementContext): + for_node = CassNode("I#for_statement#for($$;$)$") + #for_node.add_child(CassNode("4")) + + # Initialization (forInit) + if ctx.forInit(): + init_node = self.visit(ctx.forInit()) + for_node.add_child(init_node) + else: + for_node.add_child(CassNode("EMPTY_INIT")) + + # Condition + if ctx.expression(): + cond_node = self.visit(ctx.expression()) + for_node.add_child(cond_node) + else: + for_node.add_child(CassNode("EMPTY_COND")) + + # Update (forUpdate) + if ctx.forUpdate(): + update_node = self.visit(ctx.forUpdate()) + for_node.add_child(update_node) + else: + for_node.add_child(CassNode("EMPTY_UPDATE")) + + # Body (single statement) + body_node = self.visit(ctx.statement()) + for_node.add_child(body_node) + + + return for_node + + def visitConditionClause(self, ctx: CASSParser.ConditionClauseContext): + node = CassNode("I#condition_clause#($)") + + if ctx.logicalOrExpression(): + node.add_child(self.visit(ctx.logicalOrExpression())) + + return node + + + def visitWhileBlockStatement(self, ctx: CASSParser.WhileBlockStatementContext): + + while_node = CassNode("I#while_statement#while$$") + + # Condition + cond_node = self.visit(ctx.conditionClause()) + while_node.add_child(cond_node) + while_node.add_child(self.visit(ctx.compoundStatement())) + + return while_node + + def visitWhileSingleStatement(self, ctx: CASSParser.WhileSingleStatementContext): + + while_node = CassNode("I#while_statement#while$$") + + # Condition + cond_node = self.visit(ctx.conditionClause()) + while_node.add_child(cond_node) + + # Single body statement + body_node = self.visit(ctx.statement()) + while_node.add_child(body_node) + + return while_node + + def visitIfBlockStatement(self, ctx: CASSParser.IfBlockStatementContext): + + num_children = 0 + if ctx.conditionClause(): + num_children += 1 + + if ctx.compoundStatement(): + num_children += 1 + + if ctx.elseClause(): + num_children += 1 + + dollar_signs = "$" * num_children # Create the correct number of $ placeholders + + # Create a node for the "if" statement + if_node = CassNode(f"I#if_statement#if{dollar_signs}") + + cond_node = self.visit(ctx.conditionClause()) + if_node.add_child(cond_node) + + # Separate "if" and "else" blocks + # Visit the 'if' body (compoundStatement) and add as a child + if_body_node = self.visit(ctx.compoundStatement()) + if_node.add_child(if_body_node) + + # Handle 'else' clause if present + if ctx.elseClause(): + else_clause_node = self.visit(ctx.elseClause()) + if_node.add_child(else_clause_node) + + return if_node + + def visitElseClause(self, ctx: CASSParser.ElseClauseContext): + else_node = CassNode("I#else_clause#else$") + + if ctx.ifBlockStatement(): + nested_if_node = self.visit(ctx.ifBlockStatement()) + else_node.add_child(nested_if_node) + elif ctx.compoundStatement(): + else_body_node = self.visit(ctx.compoundStatement()) + else_node.add_child(else_body_node) + else: + # It's a simple 'else' -> visit the statement + else_body_node = self.visit(ctx.statement()) + else_node.add_child(else_body_node) + + return else_node + + + + def visitIfSingleStatement(self, ctx: CASSParser.IfSingleStatementContext): + num_children = 0 + if ctx.conditionClause(): + num_children += 1 + + if ctx.statement(): + num_children += 1 + + if ctx.elseClause(): + num_children += 1 + + dollar_signs = "$" * num_children # Create the correct number of $ placeholders + + # Create a node for the "if" statement + if_node = CassNode(f"I#if_statement#if{dollar_signs}") + + # Condition + cond_node = self.visit(ctx.conditionClause()) + if_node.add_child(cond_node) + + # Single "if" body statement + if_body_node = self.visit(ctx.statement()) + if_node.add_child(if_body_node) + + # Optional "else" + if ctx.elseClause(): + else_node = self.visit(ctx.elseClause()) + if_node.add_child(else_node) + + return if_node + + def visitSwitchStatement(self, ctx: CASSParser.SwitchStatementContext): + + switch_node = CassNode("I#switch_statement#switch$$") + switch_node.add_child(self.visit(ctx.conditionClause())) + switch_node.add_child(self.visit(ctx.compoundStatement())) + + return switch_node + + def visitCaseStatement(self, ctx: CASSParser.CaseStatementContext): + + case_name = 'case$' + has_break = '' + + if ctx.breakExpression(): + has_break = 'break' + + if ctx.defaultExpression(): + case_name = 'default' + + num_statement = len(ctx.statement()) + placeholder = "$" * num_statement + + case_node = CassNode(f"I#case_statement#{case_name}:{placeholder}{has_break}") + + if(ctx.primaryExpression()): + case_node.add_child(self.visit(ctx.primaryExpression())) + + for c in ctx.statement(): + case_node.add_child(self.visit(c)) + + return case_node + + def visitLogicalOrExpression(self, ctx: CASSParser.LogicalOrExpressionContext): + if len(ctx.logicalAndExpression()) == 1: + # If there is only one logicalAndExpression, visit it directly + return self.visit(ctx.logicalAndExpression(0)) + + # Otherwise, create a node to represent the OR operation + node = CassNode("I#binary_expression#$||$") + + + for expr in ctx.logicalAndExpression(): + node.add_child(self.visit(expr)) + + return node + + def visitLogicalAndExpression(self, ctx: CASSParser.LogicalAndExpressionContext): + if len(ctx.equalityExpression()) == 1: + return self.visit(ctx.equalityExpression(0)) + + node = CassNode("I#binary_expression#$&&$") + + + for expr in ctx.equalityExpression(): + node.add_child(self.visit(expr)) + + return node + + def visitEqualityExpression(self, ctx: CASSParser.EqualityExpressionContext): + if len(ctx.relationalExpression()) == 1: + return self.visit(ctx.relationalExpression(0)) + + node = CassNode(f"I#binary_expression#${ctx.getChild(1).getText()}$") + + + lhs = self.visit(ctx.relationalExpression(0)) # Left operand + rhs = self.visit(ctx.relationalExpression(1)) # Right operand + + node.add_child(lhs) + node.add_child(rhs) + + return node + + def visitRelationalExpression(self, ctx: CASSParser.RelationalExpressionContext): + # If there's only one child additiveExpression, just pass it up the chain + if len(ctx.children) == 1: + return self.visit(ctx.additiveExpression(0)) + + # If there's an operator like "<=" or ">" ... + left = self.visit(ctx.additiveExpression(0)) + op = ctx.getChild(1).getText() # e.g. "<=" + right = self.visit(ctx.additiveExpression(1)) + + # Create a node labeled "$<=$" (or "$>$" etc.) + node = CassNode(f"I#binary_expression#${op}$") + node.add_child(left) + node.add_child(right) + return node + + def visitAdditiveExpression(self, ctx: CASSParser.AdditiveExpressionContext): + # If there's only one child, pass it up the chain (e.g., "a") + if len(ctx.children) == 1: + return self.visit(ctx.multiplicativeExpression(0)) + + # If there are multiple operands, create a node for each operator and operand + operands = ctx.multiplicativeExpression() + result = self.visit(operands[0]) # Start with the first operand + + for i in range(1, len(operands)): + operator = ctx.getChild(2 * i - 1).getText() # Get "+" or "-" + next_operand = self.visit(operands[i]) + operator_node = CassNode(f"I#binary_expression#${operator}$") + #operator_node.add_child(CassNode("2")) + operator_node.add_child(result) + operator_node.add_child(next_operand) + result = operator_node # Update the result to the new operator node + + return result + + def visitMultiplicativeExpression(self, ctx: CASSParser.MultiplicativeExpressionContext): + # If there's only one child, pass it up the chain (e.g., "a") + if len(ctx.children) == 1: + return self.visit(ctx.unaryExpression(0)) + + # If there are multiple operands, create a node for each operator and operand + operands = ctx.unaryExpression() + result = self.visit(operands[0]) # Start with the first operand + + for i in range(1, len(operands)): + operator = ctx.getChild(2 * i - 1).getText() # Get "*" or "/" + next_operand = self.visit(operands[i]) + operator_node = CassNode(f"I#binary_expression#${operator}$") + #operator_node.add_child(CassNode("2")) + operator_node.add_child(result) + operator_node.add_child(next_operand) + result = operator_node # Update the result to the new operator node + + return result + + + + def visitFunctionCall(self, ctx: CASSParser.FunctionCallContext): + + # 1) The function name is the ID + func_name = ctx.ID().getText() # e.g. "init" + + # call_expression always has 2 children: name and parameter list + call_node = CassNode("I#call_expression#$$") + + # 3) First child = "F", e.g. Finit + func_node = CassNode(f"F{func_name}") + call_node.add_child(func_node) + + # 4) Second child = the argument list (which might be empty) + if ctx.argumentList(): + arg_list_node = self.visit(ctx.argumentList()) + call_node.add_child(arg_list_node) + else: + # No arguments => #argument_list#() with zero placeholders + empty_args = CassNode("I#argument_list#()") + call_node.add_child(empty_args) + + return call_node + + + def visitArgumentList(self, ctx: CASSParser.ArgumentListContext): + """ + Grammar snippet: + argumentList + : expression (',' expression)* # ArgumentList + ; + """ + # Count how many arguments we have + num_args = len(ctx.expression()) + + # Create a label like #argument_list#($,$,$...) with as many $ as arguments + placeholders = ",".join(["$"] * num_args) # Join $ with commas if more than one + arg_list_node = CassNode(f"I#argument_list#({placeholders})") + + + # For each expression argument, visit it and add as a child + for expr_ctx in ctx.expression(): + arg_node = self.visit(expr_ctx) + arg_list_node.add_child(arg_node) + + return arg_list_node + + + def visitReturnStatement(self, ctx: CASSParser.ReturnStatementContext): + # e.g. "return sum;" + node = CassNode("I#return_statement#return$;") + if ctx.expression(): + expr_node = self.visit(ctx.expression()) + node.add_child(expr_node) + return node + + def visitExpressionStatement(self, ctx: CASSParser.ExpressionStatementContext): + + statement_node = CassNode("I#expression_statement#$;") + + # 2) Visit the expression, which might yield something like "$+=$" + expr_node = self.visit(ctx.expression()) + + # 3) Add it as a child + statement_node.add_child(expr_node) + + return statement_node + + + # --------------------- + # Expression Collapsing + # --------------------- + def visitExpression(self, ctx: CASSParser.ExpressionContext): + + if ctx.assignmentExpression(): + return self.visit(ctx.assignmentExpression()) + + return None + + + def visitAssignmentExpression(self, ctx: CASSParser.AssignmentExpressionContext): + # Distinguish between: + # unaryExpression assignmentOperator assignmentExpression + # vs + # logicalOrExpression + + if ctx.assignmentOperator(): + # e.g. b = b + 1 + op_text = ctx.assignmentOperator().getText() # '=' or '+=' or ... + + placeholder = '$' + + if ctx.nullptr(): + placeholder = 'nullptr' + + if ctx.emptyInitializer(): + placeholder = '{}' + + # Use a node labeled #assignment_expression#$ + # For a simple '=' you might produce '#assignment_expression#$=$' + # For '+=' maybe '#assignment_expression#$+=$', etc. + node = CassNode(f"I#assignment_expression#$" + op_text + placeholder) + + lhs = self.visit(ctx.unaryExpression()) # e.g. b + node.add_child(lhs) + + if ctx.assignmentExpression(): + rhs = self.visit(ctx.assignmentExpression()) # e.g. b + 1 + node.add_child(rhs) + + + return node + else: + # No assignment operator => just pass logicalOrExpression up + return self.visit(ctx.logicalOrExpression()) + + + def visitUnaryExpression(self, ctx: CASSParser.UnaryExpressionContext): + + if ctx.listInitializer(): + return self.visit(ctx.listInitializer()) + + if ctx.pointerExpression(): + return self.visit(ctx.pointerExpression()) + + # If it's prefix like ++i + if ctx.unaryExpression(): + op = ''.join('$' if x not in ('+', '-') else x for x in ctx.getText()) + node = CassNode(f"I#update_expression#{op}") + node.add_child(self.visit(ctx.unaryExpression())) + return node + else: + + return self.visit(ctx.primaryExpression()) + + def isLocal(self, var_name: str) -> bool: + # Search from the top of the stack downward + for scope_set in reversed(self.scopes): + if var_name in scope_set: + return True + return False + + def visitPointerExpression(self, ctx: CASSParser.PointerExpressionContext): + + var_text = ctx.primaryExpression().getText() + sign = ctx.getText()[0] + + if self.isLocal(var_text): + ptr_node = CassNode(f"I#pointer_expression#{sign}$") + ptr_node.add_child(self.visit(ctx.primaryExpression())) + else: + ptr_node = CassNode(f"I#pointer_expression#{sign}$") + ptr_node.add_child(self.visit(ctx.primaryExpression())) + + return ptr_node + + def visitArrayDeclarator(self, ctx: CASSParser.ArrayDeclaratorContext): + + + if len(ctx.primaryExpression()) > 1: + + var_name = ctx.primaryExpression(0).getText() + + if len(self.scopes) > 0: + self.scopes[-1].add(var_name) + + arr_dclr = CassNode("I#array_declarator#$[$]") + arr_dclr.add_child(self.visit(ctx.primaryExpression(0))) + arr_dclr.add_child(self.visit(ctx.primaryExpression(1))) + + elif len(ctx.primaryExpression()) == 1: + + var_name = ctx.primaryExpression(0).getText() + + if len(self.scopes) > 0: + self.scopes[-1].add(var_name) + + arr_dclr = CassNode("I#array_declarator#$[]") + arr_dclr.add_child(self.visit(ctx.primaryExpression(0))) + + return arr_dclr + + + def visitPrimaryExpression(self, ctx: CASSParser.PrimaryExpressionContext): + # Case 1: It's an identifier + if ctx.ID(): + + var_text = ctx.ID().getText() + + # Check if var_text is declared in the current or any parent scope + if self.isLocal(var_text): + return CassNode(f"v{var_text}") + else: + return CassNode(f"V{var_text}") + + + # Case 2: It's an integer literal + elif ctx.INT(): + lit_text = ctx.INT().getText() + return CassNode(f"N{lit_text}") + + # Case 3: It's a float literal + elif ctx.FLOAT(): + lit_text = ctx.FLOAT().getText() + return CassNode(f"N{lit_text}") + + elif ctx.CHAR(): + lit_text = ctx.CHAR().getText() + return CassNode(f"C{lit_text}") + + elif ctx.STRING(): + str_text = ctx.STRING().getText() + return CassNode(f"S{str_text}") + + # Case 4: It's parentheses => ( expression ) + elif ctx.expression(): + # 1) Visit the sub-expression + subexpr_node = self.visit(ctx.expression()) + + # 2) Check if subexpr_node is an additive expression + # For example, if your additive visitor produces "$+$" or "$-$" as the label: + if subexpr_node and subexpr_node.label in {"I#binary_expression#$+$", "I#binary_expression#$-$", "I#binary_expression#$*$", "I#binary_expression#$/$", "I#binary_expression#$%$"}: + # Create a paren node + paren_node = CassNode("I#parenthesized_expression#($)") + paren_node.add_child(subexpr_node) + return paren_node + else: + # If not additive, just return the inner expression without special wrapping + return subexpr_node + + elif ctx.functionCall(): + return self.visit(ctx.functionCall()) + + # Fallback if something unexpected + else: + return CassNode("???") + diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/README.md b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/README.md new file mode 100644 index 00000000000..33f7ca17aa4 --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/README.md @@ -0,0 +1,70 @@ +# CASS Parsing and AST Representation + +This repository implements a CASS (Context Aware Semantics Structure) parser using ANTLR4. It consists of the following key components: + +- **`CASS.g4`**: Defines the grammar for parsing C-like syntax using ANTLR4. +- **`MyCASSVisitor.py`**: Implements a visitor pattern to generate and traverse the parse tree. +- **`DriverCASS.py`**: Acts as the main entry point for parsing and processing CASS input. +- **`CASSNode.py`**: Defines the Cass node structure and serialization utilities. + +--- + +## CASS Grammar (`CASS.g4`) +This file defines the ANTLR4 grammar for parsing a subset of C-like syntax, including: +- Function definitions +- Statements (if, while, for, return, switch, case etc.) +- Expressions (arithmetic, logical, assignment) +- Parenthesized expressions +- Function calls +- Variable declarations +- Arrays, lists and pointers + +The grammar ensures a well-structured parse tree that is then visited by `MyCASSVisitor.py`. + +--- + +## CASS Visitor (`MyCASSVisitor.py`) +This module implements the visitor pattern for processing all parsed components using the grammar file. Key functionalities include: + +- Constructing `CASSNode`'s +- **Labeling for nodes**, including variable declarations, expressions, and operators. +- **Child management**, allowing hierarchical tree representation. +- Distinguishing between **local and global variables**. +- Recognizing **parenthesized expressions** and **operator precedence**. +- Properly formatting function calls and argument lists. + ... + +The visitor ensures a structured transformation of the parsed syntax into an intermediate AST representation. + +--- + +## Driver (`DriverCASS.py`) +The driver script serves as the main entry point for executing the CASS parsing pipeline. It: +- Loads and **compiles the grammar** using ANTLR4. +- Instantiates the `MyCASSVisitor` to process the parse tree. +- Generates and prints the corresponding AST structure. +- Serializes the AST into the expected CASS string format. + +This script acts as the core engine for testing and processing input files. + +--- + +## Node Representation (`CASSNode.py`) +This file defines the `CassNode` class, which represents nodes in our Cass tree. It includes: +- **Serialization to CASS format**, ensuring proper formatting for output. +- **GraphViz DOT export**, enabling visualization of the tree structure. + +The `CassNode` class provides the foundational structure for storing and manipulating AST representations. + +--- + +## How to Run +Ensure you have ANTLR4 installed and available in your environment. To run the parser: +``` +java -jar "antlr-4.13.2-complete.jar" -Dlanguage=Python3 -visitor CASS.g4 +python DriverCASS.py +``` + +## Our Jupyter Notebook +We also created a Jupyter Notebook Execution.ipynb with integrated vectorization and similarity score calculation using a pretrained graph neural network provided by the authors of the MISIM paper. In order to run it, it might be necessary to install some packages. + diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/antlr-4.13.2-complete.jar b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/antlr-4.13.2-complete.jar new file mode 100644 index 00000000000..75bfcc39763 Binary files /dev/null and b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/antlr-4.13.2-complete.jar differ diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/cass.dot b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/cass.dot new file mode 100644 index 00000000000..fa8369c904b --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/cass.dot @@ -0,0 +1,50 @@ +digraph CASS { + node [shape=ellipse]; + n1 [label="[1]: S#FS#1_0"]; + n2 [label="[2]: I#compound_statement#{$$$}"]; + n3 [label="[3]: I#declaration#int$;"]; + n4 [label="[4]: I#init_declarator#$=$"]; + n5 [label="[5]: vn"]; + n6 [label="[6]: N10"]; + n7 [label="[7]: I#while_statement#while$$"]; + n8 [label="[8]: I#condition_clause#($)"]; + n9 [label="[9]: I#binary_expression#$<=$"]; + n10 [label="[10]: Vtotal"]; + n11 [label="[11]: vn"]; + n12 [label="[12]: I#compound_statement#{$$}"]; + n13 [label="[13]: I#expression_statement#$;"]; + n14 [label="[14]: I#assignment_expression#$=$"]; + n15 [label="[15]: Vtotal"]; + n16 [label="[16]: N10"]; + n17 [label="[17]: I#expression_statement#$;"]; + n18 [label="[18]: I#assignment_expression#$=$"]; + n19 [label="[19]: Vi"]; + n20 [label="[20]: I#binary_expression#$+$"]; + n21 [label="[21]: Vi"]; + n22 [label="[22]: N1"]; + n23 [label="[23]: I#return_statement#return$;"]; + n24 [label="[24]: N0"]; + n1 -> n2; + n2 -> n3; + n3 -> n4; + n4 -> n5; + n4 -> n6; + n2 -> n7; + n7 -> n8; + n8 -> n9; + n9 -> n10; + n9 -> n11; + n7 -> n12; + n12 -> n13; + n13 -> n14; + n14 -> n15; + n14 -> n16; + n12 -> n17; + n17 -> n18; + n18 -> n19; + n18 -> n20; + n20 -> n21; + n20 -> n22; + n2 -> n23; + n23 -> n24; +} \ No newline at end of file diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/cass.png b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/cass.png new file mode 100644 index 00000000000..f0eb14959f4 Binary files /dev/null and b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/cass.png differ diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/input_code_ez.c b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/input_code_ez.c new file mode 100644 index 00000000000..0cac291d566 --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/input_code_ez.c @@ -0,0 +1,10 @@ +int main() { + int n, total = 0, i = 1; + scanf("%d", &n); + while (i <= n) { + total += i; + i++; + } + printf("%d\n", total); + return 0; +} \ No newline at end of file diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/input_code_og.c b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/input_code_og.c new file mode 100644 index 00000000000..d46d32781f1 --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/input_code_og.c @@ -0,0 +1,7 @@ +int summation(int start_val, int end_val) { + int sum = 0; + for (int i = start_val; i <= end_val; ++i) { + sum += i; + } + return sum; +} diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/requirements.txt b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/requirements.txt new file mode 100644 index 00000000000..7a967d5e2f7 --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/requirements.txt @@ -0,0 +1 @@ +antlr4-python3-runtime==4.13.0 \ No newline at end of file diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/test_codes/input_code_1.c b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/test_codes/input_code_1.c new file mode 100644 index 00000000000..2181f3cc550 --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/test_codes/input_code_1.c @@ -0,0 +1,7 @@ +int addValues(int begin, int finish) { + int total = 0; + for (int idx = begin; idx <= finish; idx++) { + total += idx; + } + return total; +} \ No newline at end of file diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/test_codes/input_code_2.c b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/test_codes/input_code_2.c new file mode 100644 index 00000000000..1e867713b68 --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/test_codes/input_code_2.c @@ -0,0 +1,7 @@ +int summation(int start_val, int end_val) { + int sum = 0; + for (int i = start_val; i <= end_val; ++i) { + sum += i; + } + return sum; +} \ No newline at end of file diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/test_codes/input_code_3.c b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/test_codes/input_code_3.c new file mode 100644 index 00000000000..10c40731542 --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/test_codes/input_code_3.c @@ -0,0 +1,7 @@ +int sumNumbers(int start, int end) { + int sum = 0; + for (int i = start; i <= end; i++) { + sum += i; + } + return sum; +} diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/test_codes/input_code_4.c b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/test_codes/input_code_4.c new file mode 100644 index 00000000000..84cfac2091c --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/test_codes/input_code_4.c @@ -0,0 +1,9 @@ +int sumNumbers(int start, int end) { + int sum = 0; + int i = start; + while (i <= end) { + sum += i; + i++; + } + return sum; +} diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/test_codes/input_code_5.c b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/test_codes/input_code_5.c new file mode 100644 index 00000000000..6e28b2f6408 --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/test_codes/input_code_5.c @@ -0,0 +1,12 @@ +int calc_factorial(int n) { + if (n < 0) { + // Factorial not defined for negative numbers + return -1; + } else if (n == 0 || n == 1) { + return 1; + } else { + return n * factorial(n - 1); + } +} + + diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/test_codes/input_code_6.c b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/test_codes/input_code_6.c new file mode 100644 index 00000000000..7043e7c0eaa --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/test_codes/input_code_6.c @@ -0,0 +1,20 @@ +int main() { + int num; + + + if (num < 0) { + printf("Error: Negative input is not allowed.\n"); + return 1; + } + + float result = 1; + int counter = 1; + + while (counter <= num) { + result *= counter; + counter++; + } + + printf("Factorial of %d is %lld\n", num, result); + return 0; +} \ No newline at end of file diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/test_codes/input_code_7.c b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/test_codes/input_code_7.c new file mode 100644 index 00000000000..42eeee4ff53 --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/test_codes/input_code_7.c @@ -0,0 +1,10 @@ +int main() { + int n = 0; + int sum = 0; + scanf("%d", &n); + for (int i = 1; i <= n; i++) { + sum += i; + } + printf("%d\n", sum); + return 0; +} \ No newline at end of file diff --git a/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/test_codes/input_code_8.c b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/test_codes/input_code_8.c new file mode 100644 index 00000000000..84739bb7c4b --- /dev/null +++ b/src/main/python/[SYSTEMDS-3191] Entity Resolution for Plagiarism Detection/test_codes/input_code_8.c @@ -0,0 +1,8 @@ +int main(int a) { + x = "this cass logi was very hard to implement"; + int total = 25; + string = x + total; + return string; + + +} \ No newline at end of file