diff --git a/README.md b/README.md index 1e7496b57..38f581b8c 100644 --- a/README.md +++ b/README.md @@ -19,24 +19,25 @@ JPlag is a system that finds similarities among multiple sets of source code fil In the following, a list of all supported languages with their supported language version is provided. A language can be selected from the command line using subcommands (jplag [jplag options] [language options]). Alternatively you can use the legacy "-l" argument. -| Language | Version | CLI Argument Name | [state](https://github.com/jplag/JPlag/wiki/2.-Supported-Languages) | parser | -|--------------------------------------------------------|--------:|-------------------|:-------------------------------------------------------------------:|:---------:| -| [Java](https://www.java.com) | 17 | java | mature | JavaC | -| [C/C++](https://isocpp.org) | 11 | cpp | legacy | JavaCC | -| [C/C++](https://isocpp.org) | 14 | cpp2 | beta | ANTLR 4 | -| [C#](https://docs.microsoft.com/en-us/dotnet/csharp/) | 6 | csharp | beta | ANTLR 4 | -| [Go](https://go.dev) | 1.17 | golang | beta | ANTLR 4 | -| [Kotlin](https://kotlinlang.org) | 1.3 | kotlin | beta | ANTLR 4 | -| [Python](https://www.python.org) | 3.6 | python3 | legacy | ANTLR 4 | -| [R](https://www.r-project.org/) | 3.5.0 | rlang | beta | ANTLR 4 | -| [Rust](https://www.rust-lang.org/) | 1.60.0 | rust | beta | ANTLR 4 | -| [Scala](https://www.scala-lang.org) | 2.13.8 | scala | beta | Scalameta | -| [Scheme](http://www.scheme-reports.org) | ? | scheme | unknown | JavaCC | -| [Swift](https://www.swift.org) | 5.4 | swift | beta | ANTLR 4 | -| [EMF Metamodel](https://www.eclipse.org/modeling/emf/) | 2.25.0 | emf | beta | EMF | -| [EMF Model](https://www.eclipse.org/modeling/emf/) | 2.25.0 | emf-model | alpha | EMF | -| [LLVM IR](https://llvm.org) | 15 | llvmir | beta | ANTLR 4 | -| Text (naive) | - | text | legacy | CoreNLP | +| Language | Version | CLI Argument Name | [state](https://github.com/jplag/JPlag/wiki/2.-Supported-Languages) | parser | +|------------------------------------------------------------|---------------------------------------------------------------------------------------:|-------------------|:-------------------------------------------------------------------:|:---------:| +| [Java](https://www.java.com) | 17 | java | mature | JavaC | +| [C/C++](https://isocpp.org) | 11 | cpp | legacy | JavaCC | +| [C/C++](https://isocpp.org) | 14 | cpp2 | beta | ANTLR 4 | +| [C#](https://docs.microsoft.com/en-us/dotnet/csharp/) | 6 | csharp | beta | ANTLR 4 | +| [Go](https://go.dev) | 1.17 | golang | beta | ANTLR 4 | +| [Kotlin](https://kotlinlang.org) | 1.3 | kotlin | beta | ANTLR 4 | +| [Python](https://www.python.org) | 3.6 | python3 | legacy | ANTLR 4 | +| [R](https://www.r-project.org/) | 3.5.0 | rlang | beta | ANTLR 4 | +| [Rust](https://www.rust-lang.org/) | 1.60.0 | rust | beta | ANTLR 4 | +| [Scala](https://www.scala-lang.org) | 2.13.8 | scala | beta | Scalameta | +| [Scheme](http://www.scheme-reports.org) | ? | scheme | unknown | JavaCC | +| [Swift](https://www.swift.org) | 5.4 | swift | beta | ANTLR 4 | +| [EMF Metamodel](https://www.eclipse.org/modeling/emf/) | 2.25.0 | emf | beta | EMF | +| [EMF Model](https://www.eclipse.org/modeling/emf/) | 2.25.0 | emf-model | alpha | EMF | +| [LLVM IR](https://llvm.org) | 15 | llvmir | beta | ANTLR 4 | +| [TypeScript](https://www.typescriptlang.org/) / JavaScript | [~5](https://github.com/antlr/grammars-v4/tree/master/javascript/typescript/README.md) | typescript | beta | ANTLR 4 | +| Text (naive) | - | text | legacy | CoreNLP | ## Download and Installation You need Java SE 17 to run or build JPlag. @@ -161,6 +162,7 @@ Commands: scxml swift text + typescript ``` ### Java API diff --git a/cli/pom.xml b/cli/pom.xml index 2c542543e..b6812a3eb 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -102,6 +102,11 @@ emf-model ${revision} + + de.jplag + typescript + ${revision} + de.jplag llvmir diff --git a/cli/src/test/java/de/jplag/cli/LanguageTest.java b/cli/src/test/java/de/jplag/cli/LanguageTest.java index 1ed766329..ed59a1a13 100644 --- a/cli/src/test/java/de/jplag/cli/LanguageTest.java +++ b/cli/src/test/java/de/jplag/cli/LanguageTest.java @@ -26,7 +26,7 @@ void testInvalidLanguage() { @Test void testLoading() { var languages = LanguageLoader.getAllAvailableLanguages(); - assertEquals(17, languages.size(), "Loaded Languages: " + languages.keySet()); + assertEquals(18, languages.size(), "Loaded Languages: " + languages.keySet()); } @Test diff --git a/coverage-report/pom.xml b/coverage-report/pom.xml index 57b22a10b..3d918dd10 100644 --- a/coverage-report/pom.xml +++ b/coverage-report/pom.xml @@ -126,6 +126,11 @@ emf-model ${revision} + + de.jplag + typescript + ${revision} + de.jplag llvmir diff --git a/language-api/src/main/java/de/jplag/options/OptionType.java b/language-api/src/main/java/de/jplag/options/OptionType.java index 04cc24487..aaf677370 100644 --- a/language-api/src/main/java/de/jplag/options/OptionType.java +++ b/language-api/src/main/java/de/jplag/options/OptionType.java @@ -21,6 +21,14 @@ private IntegerType() { } } + static final class BooleanType extends OptionType { + public static final BooleanType INSTANCE = new BooleanType(); + + private BooleanType() { + super(Boolean.class); + } + } + public static StringType string() { return StringType.INSTANCE; } @@ -29,6 +37,10 @@ public static IntegerType integer() { return IntegerType.INSTANCE; } + public static BooleanType bool() { + return BooleanType.INSTANCE; + } + private final Class javaType; private OptionType(Class javaType) { diff --git a/languages/pom.xml b/languages/pom.xml index 2de4e639e..203275f10 100644 --- a/languages/pom.xml +++ b/languages/pom.xml @@ -26,6 +26,7 @@ scxml swift text + typescript llvmir diff --git a/languages/typescript/README.md b/languages/typescript/README.md new file mode 100644 index 000000000..351bc2da2 --- /dev/null +++ b/languages/typescript/README.md @@ -0,0 +1,24 @@ +# JPlag TypeScript language module +Due to TypeScript being a superset of JavaScript this frontend can also parse JavaScript files. +
+The JPlag TypeScript module allows the use of JPlag with submissions in TypeScript.
+It is based on the [TypeScript ANTLR4 grammar](https://github.com/antlr/grammars-v4/tree/master/javascript/typescript), licensed under the Apache 2.0. + + +### TypeScript specification compatibility +> This TypeScript grammar does not exactly correspond to the TypeScript standard. The main goal during developing was practical usage, performance, and clarity (getting rid of duplicates). + +Since the grammar has no support for decorators the version can be estimated < 5.0. The grammar can still parse files with decorators, but can not extract a tokens for them. +
The grammar can parse multiple language features from version 4.x. +
Because of this the version is still given as an estimated v5. + +If there are any major updates or fixes to the grammar1, they should surely be applied to this module as well. + +### Token Extraction +The choice of tokens is intended to be similar to the Java or Python modules. It includes a range of nesting structures (class, method, control flow expressions) as well as variable declaration, object creation and assignment. + +### Usage +To use the TypeScript module, use the `typescript` subcommand in the CLI, or use a `JPlagOption` object with `new de.jplag.typescript.TypeScriptLanguage()` as `language` in the Java API as described in the usage information in the [readme of the main project](https://github.com/jplag/JPlag#usage) and [in the wiki](https://github.com/jplag/JPlag/wiki/1.-How-to-Use-JPlag). + +#### Footnotes +
1 The grammar files are taken from grammar-v4, with the most recent modification in commit 768b12e from March 2023.
\ No newline at end of file diff --git a/languages/typescript/pom.xml b/languages/typescript/pom.xml new file mode 100644 index 000000000..f866687aa --- /dev/null +++ b/languages/typescript/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + + de.jplag + languages + ${revision} + + typescript + + + + org.antlr + antlr4-runtime + + + de.jplag + language-antlr-utils + ${revision} + compile + + + + + + + org.antlr + antlr4-maven-plugin + + + + antlr4 + + + + + + + diff --git a/languages/typescript/src/main/antlr4/de/jplag/typescript/grammar/TypeScriptLexer.g4 b/languages/typescript/src/main/antlr4/de/jplag/typescript/grammar/TypeScriptLexer.g4 new file mode 100644 index 000000000..3f2d34d80 --- /dev/null +++ b/languages/typescript/src/main/antlr4/de/jplag/typescript/grammar/TypeScriptLexer.g4 @@ -0,0 +1,309 @@ +/// This grammer was slightly modified, so the generated code fit the JPlag code style +lexer grammar TypeScriptLexer; + +channels { ERROR } + +options { + superClass=TypeScriptLexerBase; +} + + +MultiLineComment: '/*' .*? '*/' -> channel(HIDDEN); +SingleLineComment: '//' ~[\r\n\u2028\u2029]* -> channel(HIDDEN); +RegularExpressionLiteral: '/' RegularExpressionFirstChar RegularExpressionChar* {this.isRegexPossible()}? '/' IdentifierPart*; + +OpenBracket: '['; +CloseBracket: ']'; +OpenParen: '('; +CloseParen: ')'; +OpenBrace: '{' {this.processOpenBrace();}; +TemplateCloseBrace: {this.isInTemplateString()}? '}' -> popMode; +CloseBrace: '}' {this.processCloseBrace();}; +SemiColon: ';'; +Comma: ','; +Assign: '='; +QuestionMark: '?'; +Colon: ':'; +Ellipsis: '...'; +Dot: '.'; +PlusPlus: '++'; +MinusMinus: '--'; +Plus: '+'; +Minus: '-'; +BitNot: '~'; +Not: '!'; +Multiply: '*'; +Divide: '/'; +Modulus: '%'; +RightShiftArithmetic: '>>'; +LeftShiftArithmetic: '<<'; +RightShiftLogical: '>>>'; +LessThan: '<'; +MoreThan: '>'; +LessThanEquals: '<='; +GreaterThanEquals: '>='; +Equals_: '=='; +NotEquals: '!='; +IdentityEquals: '==='; +IdentityNotEquals: '!=='; +BitAnd: '&'; +BitXOr: '^'; +BitOr: '|'; +And: '&&'; +Or: '||'; +MultiplyAssign: '*='; +DivideAssign: '/='; +ModulusAssign: '%='; +PlusAssign: '+='; +MinusAssign: '-='; +LeftShiftArithmeticAssign: '<<='; +RightShiftArithmeticAssign: '>>='; +RightShiftLogicalAssign: '>>>='; +BitAndAssign: '&='; +BitXorAssign: '^='; +BitOrAssign: '|='; +ARROW: '=>'; + +/// Null Literals + +NullLiteral: 'null'; + +/// Boolean Literals + +BooleanLiteral: 'true' + | 'false'; + +/// Numeric Literals + +DecimalLiteral: DecimalIntegerLiteral '.' [0-9]* ExponentPart? + | '.' [0-9]+ ExponentPart? + | DecimalIntegerLiteral ExponentPart? + ; + +/// Numeric Literals + +HexIntegerLiteral: '0' [xX] HexDigit+; +OctalIntegerLiteral: '0' [0-7]+ {!this.isStrictMode()}?; +OctalIntegerLiteral2: '0' [oO] [0-7]+; +BinaryIntegerLiteral: '0' [bB] [01]+; + +/// Keywords + +Break: 'break'; +Do: 'do'; +Instanceof: 'instanceof'; +Typeof: 'typeof'; +Case: 'case'; +Else: 'else'; +New: 'new'; +Var: 'var'; +Catch: 'catch'; +Finally: 'finally'; +Return: 'return'; +Void: 'void'; +Continue: 'continue'; +For: 'for'; +Switch: 'switch'; +While: 'while'; +Debugger: 'debugger'; +Function_: 'function'; +This: 'this'; +With: 'with'; +Default: 'default'; +If: 'if'; +Throw: 'throw'; +Delete: 'delete'; +In: 'in'; +Try: 'try'; +As: 'as'; +From: 'from'; +ReadOnly: 'readonly'; +Async: 'async'; + +/// Future Reserved Words + +Class: 'class'; +Enum: 'enum'; +Extends: 'extends'; +Super: 'super'; +Const: 'const'; +Export: 'export'; +Import: 'import'; + +/// The following tokens are also considered to be FutureReservedWords +/// when parsing strict mode + +Implements: 'implements' ; +Let: 'let' ; +Private: 'private' ; +Public: 'public' ; +Interface: 'interface' ; +Package: 'package' ; +Protected: 'protected' ; +Static: 'static' ; +Yield: 'yield' ; + +//keywords: + +Any : 'any'; +Number: 'number'; +Boolean: 'boolean'; +String: 'string'; +Symbol: 'symbol'; + + +TypeAlias : 'type'; + +Get: 'get'; +Set: 'set'; + +Constructor: 'constructor'; +Namespace: 'namespace'; +Require: 'require'; +Module: 'module'; +Declare: 'declare'; + +Abstract: 'abstract'; + +Is: 'is'; + +// +// Ext.2 Additions to 1.8: Decorators +// +At: '@'; + +/// Identifier Names and Identifiers + +Identifier: IdentifierStart IdentifierPart*; + +/// String Literals +StringLiteral: ('"' DoubleStringCharacter* '"' + | '\'' SingleStringCharacter* '\'') {this.processStringLiteral();} + ; + +BackTick: '`' {this.increaseTemplateDepth();} -> pushMode(TEMPLATE); + +WhiteSpaces: [\t\u000B\u000C\u0020\u00A0]+ -> channel(HIDDEN); + +LineTerminator: [\r\n\u2028\u2029] -> channel(HIDDEN); + +/// Comments + + +HtmlComment: '' -> channel(HIDDEN); +CDataComment: '' -> channel(HIDDEN); +UnexpectedCharacter: . -> channel(ERROR); + +mode TEMPLATE; + +TemplateStringEscapeAtom: '\\' .; +BackTickInside: '`' {this.decreaseTemplateDepth();} -> type(BackTick), popMode; +TemplateStringStartExpression: '${' {this.startTemplateString();} -> pushMode(DEFAULT_MODE); +TemplateStringAtom: ~[`\\]; + +// Fragment rules + +fragment DoubleStringCharacter + : ~["\\\r\n] + | '\\' EscapeSequence + | LineContinuation + ; + +fragment SingleStringCharacter + : ~['\\\r\n] + | '\\' EscapeSequence + | LineContinuation + ; + +fragment EscapeSequence + : CharacterEscapeSequence + | '0' // no digit ahead! TODO + | HexEscapeSequence + | UnicodeEscapeSequence + | ExtendedUnicodeEscapeSequence + ; + +fragment CharacterEscapeSequence + : SingleEscapeCharacter + | NonEscapeCharacter + ; + +fragment HexEscapeSequence + : 'x' HexDigit HexDigit + ; + +fragment UnicodeEscapeSequence + : 'u' HexDigit HexDigit HexDigit HexDigit + ; + +fragment ExtendedUnicodeEscapeSequence + : 'u' '{' HexDigit+ '}' + ; + +fragment SingleEscapeCharacter + : ['"\\bfnrtv] + ; + +fragment NonEscapeCharacter + : ~['"\\bfnrtv0-9xu\r\n] + ; + +fragment EscapeCharacter + : SingleEscapeCharacter + | [0-9] + | [xu] + ; + +fragment LineContinuation + : '\\' [\r\n\u2028\u2029] + ; + +fragment HexDigit + : [0-9a-fA-F] + ; + +fragment DecimalIntegerLiteral + : '0' + | [1-9] [0-9]* + ; + +fragment ExponentPart + : [eE] [+-]? [0-9]+ + ; + +fragment IdentifierPart + : IdentifierStart + | [\p{Mn}] + | [\p{Nd}] + | [\p{Pc}] + | '\u200C' + | '\u200D' + ; + +fragment IdentifierStart + : [\p{L}] + | [$_] + | '\\' UnicodeEscapeSequence + ; + +fragment RegularExpressionFirstChar + : ~[*\r\n\u2028\u2029\\/[] + | RegularExpressionBackslashSequence + | '[' RegularExpressionClassChar* ']' + ; + +fragment RegularExpressionChar + : ~[\r\n\u2028\u2029\\/[] + | RegularExpressionBackslashSequence + | '[' RegularExpressionClassChar* ']' + ; + +fragment RegularExpressionClassChar + : ~[\r\n\u2028\u2029\]\\] + | RegularExpressionBackslashSequence + ; + +fragment RegularExpressionBackslashSequence + : '\\' ~[\r\n\u2028\u2029] + ; + diff --git a/languages/typescript/src/main/antlr4/de/jplag/typescript/grammar/TypeScriptParser.g4 b/languages/typescript/src/main/antlr4/de/jplag/typescript/grammar/TypeScriptParser.g4 new file mode 100644 index 000000000..6e81b63ea --- /dev/null +++ b/languages/typescript/src/main/antlr4/de/jplag/typescript/grammar/TypeScriptParser.g4 @@ -0,0 +1,854 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 by Bart Kiers (original author) and Alexandre Vitorelli (contributor -> ported to CSharp) + * Copyright (c) 2017 by Ivan Kochurkin (Positive Technologies): + added ECMAScript 6 support, cleared and transformed to the universal grammar. + * Copyright (c) 2018 by Juan Alvarez (contributor -> ported to Go) + * Copyright (c) 2019 by Andrii Artiushok (contributor -> added TypeScript support) + * + * 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. + */ +parser grammar TypeScriptParser; + +options { + tokenVocab=TypeScriptLexer; + superClass=TypeScriptParserBase; +} + +// SupportSyntax + +initializer + : '=' singleExpression + ; + +bindingPattern + : (arrayLiteral | objectLiteral) + ; + +// TypeScript SPart +// A.1 Types + +typeParameters + : '<' typeParameterList? '>' + ; + +typeParameterList + : typeParameter (',' typeParameter)* + ; + +typeParameter + : Identifier constraint? + | typeParameters + ; + +constraint + : 'extends' type_ + ; + +typeArguments + : '<' typeArgumentList? '>' + ; + +typeArgumentList + : typeArgument (',' typeArgument)* + ; + +typeArgument + : type_ + ; + +type_ + : unionOrIntersectionOrPrimaryType + | functionType + | constructorType + | typeGeneric + | StringLiteral + ; + +unionOrIntersectionOrPrimaryType + : unionOrIntersectionOrPrimaryType '|' unionOrIntersectionOrPrimaryType #Union + | unionOrIntersectionOrPrimaryType '&' unionOrIntersectionOrPrimaryType #Intersection + | primaryType #Primary + ; + +primaryType + : '(' type_ ')' #ParenthesizedPrimType + | predefinedType #PredefinedPrimType + | typeReference #ReferencePrimType + | objectType #ObjectPrimType + | primaryType {notLineTerminator()}? '[' ']' #ArrayPrimType + | '[' tupleElementTypes ']' #TuplePrimType + | typeQuery #QueryPrimType + | This #ThisPrimType + | typeReference Is primaryType #RedefinitionOfType + ; + +predefinedType + : Any + | Number + | Boolean + | String + | Symbol + | Void + ; + +typeReference + : typeName nestedTypeGeneric? + ; + +nestedTypeGeneric + : typeIncludeGeneric + | typeGeneric + ; + +// I tried recursive include, but it's not working. +// typeGeneric +// : '<' typeArgumentList typeGeneric?'>' +// ; +// +// TODO: Fix recursive +// +typeGeneric + : '<' typeArgumentList '>' + ; + +typeIncludeGeneric + :'<' typeArgumentList '<' typeArgumentList ('>' bindingPattern '>' | '>>') + ; + +typeName + : Identifier + | namespaceName + ; + +objectType + : '{' typeBody? '}' + ; + +typeBody + : typeMemberList (SemiColon | ',')? + ; + +typeMemberList + : typeMember ((SemiColon | ',') typeMember)* + ; + +typeMember + : propertySignatur + | callSignature + | constructSignature + | indexSignature + | methodSignature ('=>' type_)? + ; + +arrayType + : primaryType {notLineTerminator()}? '[' ']' + ; + +tupleType + : '[' tupleElementTypes ']' + ; + +tupleElementTypes + : type_ (',' type_)* + ; + +functionType + : typeParameters? '(' parameterList? ')' '=>' type_ + ; + +constructorType + : 'new' typeParameters? '(' parameterList? ')' '=>' type_ + ; + +typeQuery + : 'typeof' typeQueryExpression + ; + +typeQueryExpression + : Identifier + | (identifierName '.')+ identifierName + ; + +propertySignatur + : ReadOnly? propertyName '?'? typeAnnotation? ('=>' type_)? + ; + +typeAnnotation + : ':' type_ + ; + +callSignature + : typeParameters? '(' parameterList? ')' typeAnnotation? + ; + +parameterList + : restParameter + | parameter (',' parameter)* (',' restParameter)? + ; + +requiredParameterList + : requiredParameter (',' requiredParameter)* + ; + +parameter + : requiredParameter + | optionalParameter + ; + +optionalParameter + : decoratorList? ( accessibilityModifier? identifierOrPattern ('?' typeAnnotation? | typeAnnotation? initializer)) + ; + +restParameter + : '...' singleExpression typeAnnotation? + ; + +requiredParameter + : decoratorList? accessibilityModifier? identifierOrPattern typeAnnotation? + ; + +accessibilityModifier + : Public + | Private + | Protected + ; + +identifierOrPattern + : identifierName + | bindingPattern + ; + +constructSignature + : 'new' typeParameters? '(' parameterList? ')' typeAnnotation? + ; + +indexSignature + : '[' Identifier ':' (Number|String) ']' typeAnnotation + ; + +methodSignature + : propertyName '?'? callSignature + ; + +typeAliasDeclaration + : 'type' Identifier typeParameters? '=' type_ SemiColon + ; + +constructorDeclaration + : accessibilityModifier? Constructor '(' formalParameterList? ')' ( ('{' functionBody '}') | SemiColon)? + ; + +// A.5 Interface + +interfaceDeclaration + : Export? Declare? Interface Identifier typeParameters? interfaceExtendsClause? objectType SemiColon? + ; + +interfaceExtendsClause + : Extends classOrInterfaceTypeList + ; + +classOrInterfaceTypeList + : typeReference (',' typeReference)* + ; + +// A.7 Interface + +enumDeclaration + : Const? Enum Identifier '{' enumBody? '}' + ; + +enumBody + : enumMemberList ','? + ; + +enumMemberList + : enumMember (',' enumMember)* + ; + +enumMember + : propertyName ('=' singleExpression)? + ; + +// A.8 Namespaces + +namespaceDeclaration + : Namespace namespaceName '{' statementList? '}' + ; + +namespaceName + : Identifier ('.'+ Identifier)* + ; + +importAliasDeclaration + : Identifier '=' namespaceName SemiColon + ; + +// Ext.2 Additions to 1.8: Decorators + +decoratorList + : decorator+ ; + +decorator + : '@' (decoratorMemberExpression | decoratorCallExpression) + ; + +decoratorMemberExpression + : Identifier + | decoratorMemberExpression '.' identifierName + | '(' singleExpression ')' + ; + +decoratorCallExpression + : decoratorMemberExpression arguments; + +// ECMAPart +program + : sourceElements? EOF + ; + +sourceElement + : Export? statement + ; + +statement + : block + | importStatement + | exportStatement + | emptyStatement_ + | abstractDeclaration //ADDED + | classDeclaration + | interfaceDeclaration //ADDED + | namespaceDeclaration //ADDED + | ifStatement + | iterationStatement + | continueStatement + | breakStatement + | returnStatement + | yieldStatement + | withStatement + | labelledStatement + | switchStatement + | throwStatement + | tryStatement + | debuggerStatement + | functionDeclaration + | arrowFunctionDeclaration + | generatorFunctionDeclaration + | variableStatement + | typeAliasDeclaration //ADDED + | enumDeclaration //ADDED + | expressionStatement + | Export statement + ; + +block + : '{' statementList? '}' + ; + +statementList + : statement+ + ; + +abstractDeclaration + : Abstract (Identifier callSignature | variableStatement) eos + ; + +importStatement + : Import (fromBlock | importAliasDeclaration) + ; + +fromBlock + : (Multiply | multipleImportStatement) (As identifierName)? From StringLiteral eos + ; + +multipleImportStatement + : (identifierName ',')? '{' identifierName (',' identifierName)* '}' + ; + +exportStatement + : Export Default? (fromBlock | statement) + ; + +variableStatement + : bindingPattern typeAnnotation? initializer SemiColon? + | accessibilityModifier? varModifier? ReadOnly? variableDeclarationList SemiColon? + | Declare varModifier? variableDeclarationList SemiColon? + ; + +variableDeclarationList + : variableDeclaration (',' variableDeclaration)* + ; + +variableDeclaration + : ( identifierOrKeyWord | arrayLiteral | objectLiteral) typeAnnotation? singleExpression? ('=' typeParameters? singleExpression)? // ECMAScript 6: Array & Object Matching + ; + +emptyStatement_ + : SemiColon + ; + +expressionStatement + : {this.notOpenBraceAndNotFunction()}? expressionSequence SemiColon? + ; + +ifStatement + : If '(' expressionSequence ')' statement (Else statement)? + ; + + +iterationStatement + : Do statement While '(' expressionSequence ')' eos # DoStatement + | While '(' expressionSequence ')' statement # WhileStatement + | For '(' expressionSequence? SemiColon expressionSequence? SemiColon expressionSequence? ')' statement # ForStatement + | For '(' varModifier variableDeclarationList SemiColon expressionSequence? SemiColon expressionSequence? ')' + statement # ForVarStatement + | For '(' singleExpression (In | Identifier{this.p("of")}?) expressionSequence ')' statement # ForInStatement + | For '(' varModifier variableDeclaration (In | Identifier{this.p("of")}?) expressionSequence ')' statement # ForVarInStatement + ; + +varModifier + : Var + | Let + | Const + ; + +continueStatement + : Continue ({this.notLineTerminator()}? Identifier)? eos + ; + +breakStatement + : Break ({this.notLineTerminator()}? Identifier)? eos + ; + +returnStatement + : Return ({this.notLineTerminator()}? expressionSequence)? eos + ; + +yieldStatement + : Yield ({this.notLineTerminator()}? expressionSequence)? eos + ; + +withStatement + : With '(' expressionSequence ')' statement + ; + +switchStatement + : Switch '(' expressionSequence ')' caseBlock + ; + +caseBlock + : '{' caseClauses? (defaultClause caseClauses?)? '}' + ; + +caseClauses + : caseClause+ + ; + +caseClause + : Case expressionSequence ':' statementList? + ; + +defaultClause + : Default ':' statementList? + ; + +labelledStatement + : Identifier ':' statement + ; + +throwStatement + : Throw {this.notLineTerminator()}? expressionSequence eos + ; + +tryStatement + : Try block (catchProduction finallyProduction? | finallyProduction) + ; + +catchProduction + : Catch '(' Identifier ')' block + ; + +finallyProduction + : Finally block + ; + +debuggerStatement + : Debugger eos + ; + +functionDeclaration + : Function_ Identifier callSignature ( ('{' functionBody '}') | SemiColon) + ; + +//Ovveride ECMA +classDeclaration + : decoratorList? (Export Default?)? Abstract? Class Identifier typeParameters? classHeritage classTail + ; + +classHeritage + : classExtendsClause? implementsClause? + ; + +classTail + : '{' classElement* '}' + ; + +classExtendsClause + : Extends typeReference + ; + +implementsClause + : Implements classOrInterfaceTypeList + ; + +// Classes modified +classElement + : constructorDeclaration + | decoratorList? propertyMemberDeclaration + | indexMemberDeclaration + | statement + ; + +propertyMemberDeclaration + : propertyMemberBase propertyName '?'? typeAnnotation? initializer? SemiColon # PropertyDeclarationExpression + | propertyMemberBase propertyName callSignature ( ('{' functionBody '}') | SemiColon) # MethodDeclarationExpression + | propertyMemberBase (getAccessor | setAccessor) # GetterSetterDeclarationExpression + | abstractDeclaration # AbstractMemberDeclaration + ; + +propertyMemberBase + : accessibilityModifier? Async? Static? ReadOnly? + ; + +indexMemberDeclaration + : indexSignature SemiColon + ; + +generatorMethod + : '*'? Identifier '(' formalParameterList? ')' '{' functionBody '}' + ; + +generatorFunctionDeclaration + : Function_ '*' Identifier? '(' formalParameterList? ')' '{' functionBody '}' + ; + +generatorBlock + : '{' generatorDefinition (',' generatorDefinition)* ','? '}' + ; + +generatorDefinition + : '*' iteratorDefinition + ; + +iteratorBlock + : '{' iteratorDefinition (',' iteratorDefinition)* ','? '}' + ; + +iteratorDefinition + : '[' singleExpression ']' '(' formalParameterList? ')' '{' functionBody '}' + ; + +formalParameterList + : formalParameterArg (',' formalParameterArg)* (',' lastFormalParameterArg)? + | lastFormalParameterArg + | arrayLiteral // ECMAScript 6: Parameter Context Matching + | objectLiteral (':' formalParameterList)? // ECMAScript 6: Parameter Context Matching + ; + +formalParameterArg + : decorator? accessibilityModifier? identifierOrKeyWord '?'? typeAnnotation? ('=' singleExpression)? // ECMAScript 6: Initialization + ; + +lastFormalParameterArg // ECMAScript 6: Rest Parameter + : Ellipsis Identifier typeAnnotation? + ; + +functionBody + : sourceElements? + ; + +sourceElements + : sourceElement+ + ; + +arrayLiteral + : ('[' elementList? ']') + ; + +elementList + : arrayElement (','+ arrayElement)* + ; + +arrayElement // ECMAScript 6: Spread Operator + : Ellipsis? (singleExpression | Identifier) ','? + ; + +objectLiteral + : '{' (propertyAssignment (',' propertyAssignment)* ','?)? '}' + ; + +// MODIFIED +propertyAssignment + : propertyName (':' |'=') singleExpression # PropertyExpressionAssignment + | '[' singleExpression ']' ':' singleExpression # ComputedPropertyExpressionAssignment + | getAccessor # PropertyGetter + | setAccessor # PropertySetter + | generatorMethod # MethodProperty + | identifierOrKeyWord # PropertyShorthand + | restParameter # RestParameterInObject + ; + +getAccessor + : getter '(' ')' typeAnnotation? '{' functionBody '}' + ; + +setAccessor + : setter '(' ( Identifier | bindingPattern) typeAnnotation? ')' '{' functionBody '}' + ; + +propertyName + : identifierName + | StringLiteral + | numericLiteral + ; + +arguments + : '(' (argumentList ','?)? ')' + ; + +argumentList + : argument (',' argument)* + ; + +argument // ECMAScript 6: Spread Operator + : Ellipsis? (singleExpression | Identifier) + ; + +expressionSequence + : singleExpression (',' singleExpression)* + ; + +functionExpressionDeclaration + : Function_ Identifier? '(' formalParameterList? ')' typeAnnotation? '{' functionBody '}' + ; + +singleExpression + : functionExpressionDeclaration # FunctionExpression + | arrowFunctionDeclaration # ArrowFunctionExpression // ECMAScript 6 + | singleExpression '[' expressionSequence ']' # MemberIndexExpression + | singleExpression '!'? '.' identifierName nestedTypeGeneric? # MemberDotExpression + // Split to try `new Date()` first, then `new Date`. + | New singleExpression typeArguments? arguments # NewExpression + | New singleExpression typeArguments? # NewExpression + | singleExpression arguments # ArgumentsExpression + | singleExpression {this.notLineTerminator()}? '++' # PostIncrementExpression + | singleExpression {this.notLineTerminator()}? '--' # PostDecreaseExpression + | Delete singleExpression # DeleteExpression + | Void singleExpression # VoidExpression + | Typeof singleExpression # TypeofExpression + | '++' singleExpression # PreIncrementExpression + | '--' singleExpression # PreDecreaseExpression + | '+' singleExpression # UnaryPlusExpression + | '-' singleExpression # UnaryMinusExpression + | '~' singleExpression # BitNotExpression + | '!' singleExpression # NotExpression + | singleExpression ('*' | '/' | '%') singleExpression # MultiplicativeExpression + | singleExpression ('+' | '-') singleExpression # AdditiveExpression + | singleExpression ('<<' | '>>' | '>>>') singleExpression # BitShiftExpression + | singleExpression ('<' | '>' | '<=' | '>=') singleExpression # RelationalExpression + | singleExpression Instanceof singleExpression # InstanceofExpression + | singleExpression In singleExpression # InExpression + | singleExpression ('==' | '!=' | '===' | '!==') singleExpression # EqualityExpression + | singleExpression '&' singleExpression # BitAndExpression + | singleExpression '^' singleExpression # BitXOrExpression + | singleExpression '|' singleExpression # BitOrExpression + | singleExpression '&&' singleExpression # LogicalAndExpression + | singleExpression '||' singleExpression # LogicalOrExpression + | singleExpression '?' singleExpression ':' singleExpression # TernaryExpression + | singleExpression '=' singleExpression # AssignmentExpression + | singleExpression assignmentOperator singleExpression # AssignmentOperatorExpression + | singleExpression templateStringLiteral # TemplateStringExpression // ECMAScript 6 + | iteratorBlock # IteratorsExpression // ECMAScript 6 + | generatorBlock # GeneratorsExpression // ECMAScript 6 + | generatorFunctionDeclaration # GeneratorsFunctionExpression // ECMAScript 6 + | yieldStatement # YieldExpression // ECMAScript 6 + | This # ThisExpression + | identifierName singleExpression? # IdentifierExpression + | Super # SuperExpression + | literal # LiteralExpression + | arrayLiteral # ArrayLiteralExpression + | objectLiteral # ObjectLiteralExpression + | '(' expressionSequence ')' # ParenthesizedExpression + | typeArguments expressionSequence? # GenericTypes + | singleExpression As asExpression # CastAsExpression + ; + +asExpression + : predefinedType ('[' ']')? + | singleExpression + ; + +arrowFunctionDeclaration + : Async? arrowFunctionParameters typeAnnotation? '=>' arrowFunctionBody + ; + +arrowFunctionParameters + : Identifier + | '(' formalParameterList? ')' + ; + +arrowFunctionBody + : singleExpression + | '{' functionBody '}' + ; + +assignmentOperator + : '*=' + | '/=' + | '%=' + | '+=' + | '-=' + | '<<=' + | '>>=' + | '>>>=' + | '&=' + | '^=' + | '|=' + ; + +literal + : NullLiteral + | BooleanLiteral + | StringLiteral + | templateStringLiteral + | RegularExpressionLiteral + | numericLiteral + ; + +templateStringLiteral + : BackTick templateStringAtom* BackTick + ; + +templateStringAtom + : TemplateStringAtom + | TemplateStringStartExpression singleExpression TemplateCloseBrace + | TemplateStringEscapeAtom + ; + +numericLiteral + : DecimalLiteral + | HexIntegerLiteral + | OctalIntegerLiteral + | OctalIntegerLiteral2 + | BinaryIntegerLiteral + ; + +identifierName + : Identifier + | reservedWord + ; + +identifierOrKeyWord + : Identifier + | TypeAlias + | Require + ; + +reservedWord + : keyword + | NullLiteral + | BooleanLiteral + ; + +keyword + : Break + | Do + | Instanceof + | Typeof + | Case + | Else + | New + | Var + | Catch + | Finally + | Return + | Void + | Continue + | For + | Switch + | While + | Debugger + | Function_ + | This + | With + | Default + | If + | Throw + | Delete + | In + | Try + | ReadOnly + | Async + | From + | Class + | Enum + | Extends + | Super + | Const + | Export + | Import + | Implements + | Let + | Private + | Public + | Interface + | Package + | Protected + | Static + | Yield + | Get + | Set + | Require + | TypeAlias + | String + | Boolean + | Number + | Module + ; + +getter + : Get propertyName + ; + +setter + : Set propertyName + ; + +eos + : SemiColon + | EOF + | {this.lineTerminatorAhead()}? + | {this.closeBrace()}? + ; diff --git a/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptLanguage.java b/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptLanguage.java new file mode 100644 index 000000000..d6f1fbb0a --- /dev/null +++ b/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptLanguage.java @@ -0,0 +1,45 @@ +package de.jplag.typescript; + +import org.kohsuke.MetaInfServices; + +import de.jplag.antlr.AbstractAntlrLanguage; + +/** + * This represents the TypeScript language as a language supported by JPlag. + */ +@MetaInfServices(de.jplag.Language.class) +public class TypeScriptLanguage extends AbstractAntlrLanguage { + + private static final String IDENTIFIER = "typescript"; + private final TypeScriptLanguageOptions options = new TypeScriptLanguageOptions(); + + @Override + public String[] suffixes() { + return new String[] {".ts", ".js"}; + } + + @Override + public String getName() { + return "Typescript Parser"; + } + + @Override + public String getIdentifier() { + return IDENTIFIER; + } + + @Override + public int minimumTokenMatch() { + return 12; + } + + @Override + public TypeScriptLanguageOptions getOptions() { + return options; + } + + @Override + protected TypeScriptParserAdapter initializeParser() { + return new TypeScriptParserAdapter(getOptions().useStrictDefault()); + } +} diff --git a/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptLanguageOptions.java b/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptLanguageOptions.java new file mode 100644 index 000000000..8a01976c0 --- /dev/null +++ b/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptLanguageOptions.java @@ -0,0 +1,22 @@ +package de.jplag.typescript; + +import de.jplag.options.LanguageOption; +import de.jplag.options.LanguageOptions; +import de.jplag.options.OptionType; + +/** + * Language Specific options for the TypeScript language + */ +public class TypeScriptLanguageOptions extends LanguageOptions { + + /** + * Whether the Antlr Grammar should parse + */ + private final LanguageOption useStrictDefault = createDefaultOption(OptionType.bool(), "useStrictMode", + "If set JPlag parses files with the JavaScript strict syntax", false); + + public boolean useStrictDefault() { + return this.useStrictDefault.getValue(); + } + +} diff --git a/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptListener.java b/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptListener.java new file mode 100644 index 000000000..a71b3dc84 --- /dev/null +++ b/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptListener.java @@ -0,0 +1,151 @@ +package de.jplag.typescript; + +import static de.jplag.typescript.TypeScriptTokenType.ASSIGNMENT; +import static de.jplag.typescript.TypeScriptTokenType.BREAK; +import static de.jplag.typescript.TypeScriptTokenType.CATCH_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.CATCH_END; +import static de.jplag.typescript.TypeScriptTokenType.CLASS_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.CLASS_END; +import static de.jplag.typescript.TypeScriptTokenType.CONSTRUCTOR_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.CONSTRUCTOR_END; +import static de.jplag.typescript.TypeScriptTokenType.CONTINUE; +import static de.jplag.typescript.TypeScriptTokenType.DECLARATION; +import static de.jplag.typescript.TypeScriptTokenType.ENUM_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.ENUM_END; +import static de.jplag.typescript.TypeScriptTokenType.ENUM_MEMBER; +import static de.jplag.typescript.TypeScriptTokenType.EXPORT; +import static de.jplag.typescript.TypeScriptTokenType.FINALLY_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.FINALLY_END; +import static de.jplag.typescript.TypeScriptTokenType.FOR_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.FOR_END; +import static de.jplag.typescript.TypeScriptTokenType.FUNCTION_CALL; +import static de.jplag.typescript.TypeScriptTokenType.IF_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.IF_END; +import static de.jplag.typescript.TypeScriptTokenType.IMPORT; +import static de.jplag.typescript.TypeScriptTokenType.INTERFACE_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.INTERFACE_END; +import static de.jplag.typescript.TypeScriptTokenType.METHOD_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.METHOD_END; +import static de.jplag.typescript.TypeScriptTokenType.NAMESPACE_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.NAMESPACE_END; +import static de.jplag.typescript.TypeScriptTokenType.RETURN; +import static de.jplag.typescript.TypeScriptTokenType.SWITCH_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.SWITCH_CASE; +import static de.jplag.typescript.TypeScriptTokenType.SWITCH_END; +import static de.jplag.typescript.TypeScriptTokenType.THROW; +import static de.jplag.typescript.TypeScriptTokenType.TRY_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.WHILE_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.WHILE_END; +import static de.jplag.typescript.grammar.TypeScriptParser.ArgumentsContext; +import static de.jplag.typescript.grammar.TypeScriptParser.ArrowFunctionDeclarationContext; +import static de.jplag.typescript.grammar.TypeScriptParser.AssignmentExpressionContext; +import static de.jplag.typescript.grammar.TypeScriptParser.BreakStatementContext; +import static de.jplag.typescript.grammar.TypeScriptParser.CaseClauseContext; +import static de.jplag.typescript.grammar.TypeScriptParser.CatchProductionContext; +import static de.jplag.typescript.grammar.TypeScriptParser.ClassDeclarationContext; +import static de.jplag.typescript.grammar.TypeScriptParser.ConstructorDeclarationContext; +import static de.jplag.typescript.grammar.TypeScriptParser.ContinueStatementContext; +import static de.jplag.typescript.grammar.TypeScriptParser.DefaultClauseContext; +import static de.jplag.typescript.grammar.TypeScriptParser.Else; +import static de.jplag.typescript.grammar.TypeScriptParser.EnumDeclarationContext; +import static de.jplag.typescript.grammar.TypeScriptParser.EnumMemberContext; +import static de.jplag.typescript.grammar.TypeScriptParser.Export; +import static de.jplag.typescript.grammar.TypeScriptParser.FinallyProductionContext; +import static de.jplag.typescript.grammar.TypeScriptParser.ForInStatementContext; +import static de.jplag.typescript.grammar.TypeScriptParser.ForStatementContext; +import static de.jplag.typescript.grammar.TypeScriptParser.ForVarStatementContext; +import static de.jplag.typescript.grammar.TypeScriptParser.FunctionDeclarationContext; +import static de.jplag.typescript.grammar.TypeScriptParser.FunctionExpressionDeclarationContext; +import static de.jplag.typescript.grammar.TypeScriptParser.GetterSetterDeclarationExpressionContext; +import static de.jplag.typescript.grammar.TypeScriptParser.IfStatementContext; +import static de.jplag.typescript.grammar.TypeScriptParser.ImportStatementContext; +import static de.jplag.typescript.grammar.TypeScriptParser.InterfaceDeclarationContext; +import static de.jplag.typescript.grammar.TypeScriptParser.MethodDeclarationExpressionContext; +import static de.jplag.typescript.grammar.TypeScriptParser.NamespaceDeclarationContext; +import static de.jplag.typescript.grammar.TypeScriptParser.PostDecreaseExpressionContext; +import static de.jplag.typescript.grammar.TypeScriptParser.PostIncrementExpressionContext; +import static de.jplag.typescript.grammar.TypeScriptParser.PreDecreaseExpressionContext; +import static de.jplag.typescript.grammar.TypeScriptParser.PreIncrementExpressionContext; +import static de.jplag.typescript.grammar.TypeScriptParser.PropertyDeclarationExpressionContext; +import static de.jplag.typescript.grammar.TypeScriptParser.PropertySetterContext; +import static de.jplag.typescript.grammar.TypeScriptParser.PropertySignaturContext; +import static de.jplag.typescript.grammar.TypeScriptParser.ReturnStatementContext; +import static de.jplag.typescript.grammar.TypeScriptParser.SwitchStatementContext; +import static de.jplag.typescript.grammar.TypeScriptParser.ThrowStatementContext; +import static de.jplag.typescript.grammar.TypeScriptParser.TryStatementContext; +import static de.jplag.typescript.grammar.TypeScriptParser.VariableDeclarationContext; +import static de.jplag.typescript.grammar.TypeScriptParser.WhileStatementContext; + +import java.io.File; + +import de.jplag.antlr.AbstractAntlrListener; +import de.jplag.antlr.TokenCollector; + +/** + * This class is responsible for mapping parsed TypeScript to the internal Token structure + */ +public class TypeScriptListener extends AbstractAntlrListener { + + public TypeScriptListener(TokenCollector collector, File currentFile) { + super(collector, currentFile); + + this.mapRange(ImportStatementContext.class, IMPORT); + this.mapTerminal(Export, EXPORT); + this.mapEnterExit(NamespaceDeclarationContext.class, NAMESPACE_BEGIN, NAMESPACE_END); + + this.mapEnterExit(ClassDeclarationContext.class, CLASS_BEGIN, CLASS_END); + this.mapEnterExit(GetterSetterDeclarationExpressionContext.class, METHOD_BEGIN, METHOD_END); + this.mapRange(PropertyDeclarationExpressionContext.class, DECLARATION); + this.mapRange(PropertyDeclarationExpressionContext.class, ASSIGNMENT, it -> it.initializer() != null); + this.mapRange(PropertySetterContext.class, ASSIGNMENT); + this.mapRange(PropertySignaturContext.class, DECLARATION); + + this.mapEnterExit(InterfaceDeclarationContext.class, INTERFACE_BEGIN, INTERFACE_END); + this.mapEnterExit(ConstructorDeclarationContext.class, CONSTRUCTOR_BEGIN, CONSTRUCTOR_END); + + this.mapEnterExit(EnumDeclarationContext.class, ENUM_BEGIN, ENUM_END); + this.mapRange(EnumMemberContext.class, ENUM_MEMBER); + + this.mapRange(VariableDeclarationContext.class, DECLARATION); + this.mapRange(VariableDeclarationContext.class, ASSIGNMENT, it -> it.Assign() != null); + + this.mapEnterExit(IfStatementContext.class, IF_BEGIN, IF_END); + this.mapTerminal(Else, IF_BEGIN); + + this.mapEnterExit(SwitchStatementContext.class, SWITCH_BEGIN, SWITCH_END); + this.mapRange(CaseClauseContext.class, SWITCH_CASE); + this.mapRange(DefaultClauseContext.class, SWITCH_CASE); + + this.mapEnterExit(MethodDeclarationExpressionContext.class, METHOD_BEGIN, METHOD_END); + + this.mapRange(FunctionDeclarationContext.class, DECLARATION); + this.mapRange(FunctionDeclarationContext.class, ASSIGNMENT); + this.mapEnterExit(FunctionDeclarationContext.class, METHOD_BEGIN, METHOD_END); + + this.mapEnterExit(ArrowFunctionDeclarationContext.class, METHOD_BEGIN, METHOD_END); + this.mapEnterExit(FunctionExpressionDeclarationContext.class, METHOD_BEGIN, METHOD_END); + + this.mapEnterExit(WhileStatementContext.class, WHILE_BEGIN, WHILE_END); + this.mapEnterExit(ForStatementContext.class, FOR_BEGIN, FOR_END); + this.mapEnterExit(ForVarStatementContext.class, FOR_BEGIN, FOR_END); + this.mapEnterExit(ForInStatementContext.class, FOR_BEGIN, FOR_END); + + this.mapRange(TryStatementContext.class, TRY_BEGIN); + this.mapEnterExit(CatchProductionContext.class, CATCH_BEGIN, CATCH_END); + this.mapEnterExit(FinallyProductionContext.class, FINALLY_BEGIN, FINALLY_END); + + this.mapRange(BreakStatementContext.class, BREAK); + this.mapRange(ReturnStatementContext.class, RETURN); + this.mapRange(ContinueStatementContext.class, CONTINUE); + this.mapRange(ThrowStatementContext.class, THROW); + + this.mapRange(AssignmentExpressionContext.class, ASSIGNMENT); + this.mapRange(PostDecreaseExpressionContext.class, ASSIGNMENT); + this.mapRange(PreDecreaseExpressionContext.class, ASSIGNMENT); + this.mapRange(PostIncrementExpressionContext.class, ASSIGNMENT); + this.mapRange(PreIncrementExpressionContext.class, ASSIGNMENT); + + this.mapRange(ArgumentsContext.class, FUNCTION_CALL); + } + +} diff --git a/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptParserAdapter.java b/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptParserAdapter.java new file mode 100644 index 000000000..2f7586d1c --- /dev/null +++ b/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptParserAdapter.java @@ -0,0 +1,52 @@ +package de.jplag.typescript; + +import java.io.File; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.ParserRuleContext; + +import de.jplag.antlr.AbstractAntlrListener; +import de.jplag.antlr.AbstractAntlrParserAdapter; +import de.jplag.antlr.TokenCollector; +import de.jplag.typescript.grammar.TypeScriptLexer; +import de.jplag.typescript.grammar.TypeScriptParser; + +/** + * The Antlr adapter used for the TypeScript language module + */ +public class TypeScriptParserAdapter extends AbstractAntlrParserAdapter { + + private final boolean useStrictDefault; + + /** + * Creates a new Parser adapter for the Typescript Antlr Grammar + * @param useStrictDefault True if the grammars should parse the files using the JavaScript strict syntax + */ + public TypeScriptParserAdapter(boolean useStrictDefault) { + this.useStrictDefault = useStrictDefault; + } + + @Override + protected Lexer createLexer(CharStream input) { + TypeScriptLexer lexer = new TypeScriptLexer(input); + lexer.setUseStrictDefault(useStrictDefault); + return lexer; + } + + @Override + protected TypeScriptParser createParser(CommonTokenStream tokenStream) { + return new TypeScriptParser(tokenStream); + } + + @Override + protected ParserRuleContext getEntryContext(TypeScriptParser parser) { + return parser.sourceElements(); + } + + @Override + protected AbstractAntlrListener createListener(TokenCollector collector, File currentFile) { + return new TypeScriptListener(collector, currentFile); + } +} diff --git a/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptTokenType.java b/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptTokenType.java new file mode 100644 index 000000000..da8b0f8da --- /dev/null +++ b/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptTokenType.java @@ -0,0 +1,56 @@ +package de.jplag.typescript; + +import de.jplag.TokenType; + +/** + * Tokens extracted by the TypeScript language module + */ +public enum TypeScriptTokenType implements TokenType { + + IMPORT("IMPORT"), + EXPORT("EXPORT"), + NAMESPACE_BEGIN("NAMESPACE{"), + NAMESPACE_END("}NAMESPACE"), + CLASS_BEGIN("CLASS{"), + CLASS_END("}CLASS"), + INTERFACE_BEGIN("INTERFACE{"), + INTERFACE_END("}INTERFACE"), + ENUM_BEGIN("ENUM{"), + ENUM_END("}ENUM"), + METHOD_BEGIN("METHOD{"), + METHOD_END("}METHOD"), + WHILE_BEGIN("WHILE{"), + WHILE_END("}WHILE"), + FOR_BEGIN("FOR{"), + FOR_END("}FOR"), + ASSIGNMENT("ASSIGN"), + IF_BEGIN("IF{"), + IF_END("}IF"), + SWITCH_BEGIN("SWITCH{"), + SWITCH_END("}SWITCH"), + SWITCH_CASE("CASE"), + TRY_BEGIN("TRY{"), + CATCH_BEGIN("}CATCH{"), + CATCH_END("}CATCH"), + FINALLY_BEGIN("FINALLY{"), + FINALLY_END("}FINALLY"), + BREAK("BREAK"), + RETURN("RETURN"), + THROW("THROW"), + CONTINUE("CONTINUE"), + FUNCTION_CALL("CALL"), + ENUM_MEMBER("ENUM_MEMBER"), + CONSTRUCTOR_BEGIN("CONSTRUCT{"), + CONSTRUCTOR_END("}CONSTRUCT"), + DECLARATION("DECLARE"); + + private final String description; + + public String getDescription() { + return this.description; + } + + TypeScriptTokenType(String description) { + this.description = description; + } +} diff --git a/languages/typescript/src/main/java/de/jplag/typescript/grammar/TypeScriptLexerBase.java b/languages/typescript/src/main/java/de/jplag/typescript/grammar/TypeScriptLexerBase.java new file mode 100644 index 000000000..d08aa4cb6 --- /dev/null +++ b/languages/typescript/src/main/java/de/jplag/typescript/grammar/TypeScriptLexerBase.java @@ -0,0 +1,133 @@ +package de.jplag.typescript.grammar; + +import java.util.ArrayDeque; +import java.util.Deque; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.Token; + +/** + * Copied from https://github.com/antlr/grammars-v4/tree/master/javascript/typescript/Java. Slightly modified to fit + * JPlag code style + */ +abstract class TypeScriptLexerBase extends Lexer { + /** + * Stores values of nested modes. By default mode is strict or defined externally (useStrictDefault) + */ + private final Deque scopeStrictModes = new ArrayDeque<>(); + + private Token lastToken = null; + /** + * Default value of strict mode Can be defined externally by setUseStrictDefault + */ + private boolean useStrictDefault = false; + /** + * Current value of strict mode Can be defined during parsing, see StringFunctions.js and StringGlobal.js samples + */ + private boolean useStrictCurrent = false; + /** + * Keeps track of the current depth of nested template string backticks. E.g. after the X in: `${a ? `${X templateDepth + * will be 2. This variable is needed to determine if a `}` is a plain CloseBrace, or one that closes an expression + * inside a template string. + */ + private int templateDepth = 0; + + private int openBracesCount = 0; + + protected TypeScriptLexerBase(CharStream input) { + super(input); + } + + public boolean getStrictDefault() { + return useStrictDefault; + } + + public void setUseStrictDefault(boolean value) { + useStrictDefault = value; + useStrictCurrent = value; + } + + public boolean isStrictMode() { + return useStrictCurrent; + } + + public void startTemplateString() { + this.openBracesCount = 0; + } + + public boolean isInTemplateString() { + return this.templateDepth > 0 && this.openBracesCount == 0; + } + + /** + * Return the next token from the character stream and records this last token in case it resides on the default + * channel. This recorded token is used to determine when the lexer could possibly match a regex literal. Also changes + * scopeStrictModes stack if tokenize special string 'use strict'; + * @return the next token from the character stream. + */ + @Override + public Token nextToken() { + Token next = super.nextToken(); + + if (next.getChannel() == Token.DEFAULT_CHANNEL) { + // Keep track of the last token on the default channel. + this.lastToken = next; + } + + return next; + } + + protected void processOpenBrace() { + openBracesCount++; + useStrictCurrent = !scopeStrictModes.isEmpty() && scopeStrictModes.peek() || useStrictDefault; + scopeStrictModes.push(useStrictCurrent); + } + + protected void processCloseBrace() { + openBracesCount--; + useStrictCurrent = scopeStrictModes.isEmpty() ? useStrictDefault : scopeStrictModes.pop(); + } + + protected void processStringLiteral() { + if (lastToken == null || lastToken.getType() == TypeScriptLexer.OpenBrace) { + String text = getText(); + if (text.equals("\"use strict\"") || text.equals("'use strict'")) { + if (!scopeStrictModes.isEmpty()) { + scopeStrictModes.pop(); + } + useStrictCurrent = true; + scopeStrictModes.push(true); + } + } + } + + protected void increaseTemplateDepth() { + this.templateDepth++; + } + + protected void decreaseTemplateDepth() { + this.templateDepth--; + } + + /** + * Returns {@code true} if the lexer can match a regex literal. + */ + protected boolean isRegexPossible() { + + if (this.lastToken == null) { + // No token has been produced yet: at the start of the input, + // no division is possible, so a regex literal _is_ possible. + return true; + } + + return switch (this.lastToken.getType()) { + case TypeScriptLexer.Identifier, TypeScriptLexer.NullLiteral, TypeScriptLexer.BooleanLiteral, TypeScriptLexer.This, TypeScriptLexer.CloseBracket, TypeScriptLexer.CloseParen, TypeScriptLexer.OctalIntegerLiteral, TypeScriptLexer.DecimalLiteral, TypeScriptLexer.HexIntegerLiteral, TypeScriptLexer.StringLiteral, TypeScriptLexer.PlusPlus, TypeScriptLexer.MinusMinus -> + // After any of the tokens above, no regex literal can follow. + false; + default -> + // In all other cases, a regex literal _is_ possible. + true; + }; + } +} \ No newline at end of file diff --git a/languages/typescript/src/main/java/de/jplag/typescript/grammar/TypeScriptParserBase.java b/languages/typescript/src/main/java/de/jplag/typescript/grammar/TypeScriptParserBase.java new file mode 100644 index 000000000..cf4c032cc --- /dev/null +++ b/languages/typescript/src/main/java/de/jplag/typescript/grammar/TypeScriptParserBase.java @@ -0,0 +1,113 @@ +package de.jplag.typescript.grammar; + +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.TokenStream; + +/** + * All parser methods that used in grammar (p, prev, notLineTerminator, etc.) should start with lower case char similar + * to parser rules. Copied from https://github.com/antlr/grammars-v4/tree/master/javascript/typescript/Java. Slightly + * modified to fit JPlag code style + */ +public abstract class TypeScriptParserBase extends Parser { + protected TypeScriptParserBase(TokenStream input) { + super(input); + } + + /** + * Short form for prev(String str) + */ + protected boolean p(String str) { + return prev(str); + } + + /** + * Whether the previous token value equals to @param str + */ + protected boolean prev(String str) { + return _input.LT(-1).getText().equals(str); + } + + /** + * Short form for next(String str) + */ + protected boolean n(String str) { + return next(str); + } + + /** + * Whether the next token value equals to @param str + */ + protected boolean next(String str) { + return _input.LT(1).getText().equals(str); + } + + protected boolean notLineTerminator() { + return !here(TypeScriptParser.LineTerminator); + } + + protected boolean notOpenBraceAndNotFunction() { + int nextTokenType = _input.LT(1).getType(); + return nextTokenType != TypeScriptParser.OpenBrace && nextTokenType != TypeScriptParser.Function_; + } + + protected boolean closeBrace() { + return _input.LT(1).getType() == TypeScriptParser.CloseBrace; + } + + /** + * Returns {@code true} iff on the current index of the parser's token stream a token of the given {@code type} exists + * on the {@code HIDDEN} channel. + * @param type the type of the token on the {@code HIDDEN} channel to check. + * @return {@code true} iff on the current index of the parser's token stream a token of the given {@code type} exists + * on the {@code HIDDEN} channel. + */ + private boolean here(final int type) { + + // Get the token ahead of the current index. + int possibleIndexEosToken = this.getCurrentToken().getTokenIndex() - 1; + Token ahead = _input.get(possibleIndexEosToken); + + // Check if the token resides on the HIDDEN channel and if it's of the + // provided type. + return (ahead.getChannel() == Lexer.HIDDEN) && (ahead.getType() == type); + } + + /** + * Returns {@code true} iff on the current index of the parser's token stream a token exists on the {@code HIDDEN} + * channel which either is a line terminator, or is a multi line comment that contains a line terminator. + * @return {@code true} iff on the current index of the parser's token stream a token exists on the {@code HIDDEN} + * channel which either is a line terminator, or is a multi line comment that contains a line terminator. + */ + protected boolean lineTerminatorAhead() { + + // Get the token ahead of the current index. + int possibleIndexEosToken = this.getCurrentToken().getTokenIndex() - 1; + Token ahead = _input.get(possibleIndexEosToken); + + if (ahead.getChannel() != Lexer.HIDDEN) { + // We're only interested in tokens on the HIDDEN channel. + return false; + } + + if (ahead.getType() == TypeScriptParser.LineTerminator) { + // There is definitely a line terminator ahead. + return true; + } + + if (ahead.getType() == TypeScriptParser.WhiteSpaces) { + // Get the token ahead of the current whitespaces. + possibleIndexEosToken = this.getCurrentToken().getTokenIndex() - 2; + ahead = _input.get(possibleIndexEosToken); + } + + // Get the token's text and type. + String text = ahead.getText(); + int type = ahead.getType(); + + // Check if the token is, or contains a line terminator. + return (type == TypeScriptParser.MultiLineComment && (text.contains("\r") || text.contains("\n"))) + || (type == TypeScriptParser.LineTerminator); + } +} diff --git a/languages/typescript/src/test/java/de/jplag/typescript/JavaScriptLanguageTest.java b/languages/typescript/src/test/java/de/jplag/typescript/JavaScriptLanguageTest.java new file mode 100644 index 000000000..77c696a3f --- /dev/null +++ b/languages/typescript/src/test/java/de/jplag/typescript/JavaScriptLanguageTest.java @@ -0,0 +1,37 @@ +package de.jplag.typescript; + +import static de.jplag.typescript.TypeScriptTokenType.ENUM_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.ENUM_END; +import static de.jplag.typescript.TypeScriptTokenType.ENUM_MEMBER; +import static de.jplag.typescript.TypeScriptTokenType.INTERFACE_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.INTERFACE_END; +import static de.jplag.typescript.TypeScriptTokenType.NAMESPACE_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.NAMESPACE_END; + +import java.util.ArrayList; +import java.util.List; + +import de.jplag.TokenType; +import de.jplag.testutils.LanguageModuleTest; +import de.jplag.testutils.datacollector.TestDataCollector; +import de.jplag.testutils.datacollector.TestSourceIgnoredLinesCollector; + +public class JavaScriptLanguageTest extends LanguageModuleTest { + + public JavaScriptLanguageTest() { + super(new TypeScriptLanguage(), TypeScriptTokenType.class); + } + + @Override + protected void collectTestData(TestDataCollector collector) { + List javascriptTokens = new ArrayList<>(List.of(TypeScriptTokenType.values())); + javascriptTokens.removeAll(List.of(NAMESPACE_BEGIN, NAMESPACE_END, INTERFACE_BEGIN, INTERFACE_END, ENUM_BEGIN, ENUM_END, ENUM_MEMBER)); + collector.testFile("allJSTokens.js").testSourceCoverage().testContainedTokens(javascriptTokens.toArray(new TokenType[0])); + } + + @Override + protected void configureIgnoredLines(TestSourceIgnoredLinesCollector collector) { + collector.ignoreMultipleLines("/*", "*/"); + collector.ignoreLinesByPrefix("//"); + } +} diff --git a/languages/typescript/src/test/java/de/jplag/typescript/TypeScriptLanguageTest.java b/languages/typescript/src/test/java/de/jplag/typescript/TypeScriptLanguageTest.java new file mode 100644 index 000000000..338066bc7 --- /dev/null +++ b/languages/typescript/src/test/java/de/jplag/typescript/TypeScriptLanguageTest.java @@ -0,0 +1,46 @@ +package de.jplag.typescript; + +import de.jplag.testutils.LanguageModuleTest; +import de.jplag.testutils.datacollector.TestDataCollector; +import de.jplag.testutils.datacollector.TestSourceIgnoredLinesCollector; + +public class TypeScriptLanguageTest extends LanguageModuleTest { + + public TypeScriptLanguageTest() { + super(new TypeScriptLanguage(), TypeScriptTokenType.class); + } + + @Override + protected void collectTestData(TestDataCollector collector) { + collector.testFile("simpleTest.ts").testSourceCoverage().testTokenSequence(TypeScriptTokenType.DECLARATION, TypeScriptTokenType.ASSIGNMENT, + TypeScriptTokenType.DECLARATION, TypeScriptTokenType.ASSIGNMENT, TypeScriptTokenType.FOR_BEGIN, TypeScriptTokenType.ASSIGNMENT, + TypeScriptTokenType.ASSIGNMENT, TypeScriptTokenType.FUNCTION_CALL, TypeScriptTokenType.FOR_END, TypeScriptTokenType.DECLARATION, + TypeScriptTokenType.ASSIGNMENT, TypeScriptTokenType.FUNCTION_CALL, TypeScriptTokenType.ASSIGNMENT); + collector.testFile("forLoops.ts").testTokenSequence(TypeScriptTokenType.DECLARATION, TypeScriptTokenType.ASSIGNMENT, + TypeScriptTokenType.FOR_BEGIN, TypeScriptTokenType.ASSIGNMENT, TypeScriptTokenType.ASSIGNMENT, TypeScriptTokenType.FUNCTION_CALL, + TypeScriptTokenType.FOR_END, TypeScriptTokenType.FOR_BEGIN, TypeScriptTokenType.FUNCTION_CALL, TypeScriptTokenType.FOR_END, + TypeScriptTokenType.FOR_BEGIN, TypeScriptTokenType.FUNCTION_CALL, TypeScriptTokenType.FOR_END); + collector.testFile("methods.ts").testTokenSequence(TypeScriptTokenType.DECLARATION, TypeScriptTokenType.ASSIGNMENT, + TypeScriptTokenType.METHOD_BEGIN, TypeScriptTokenType.RETURN, TypeScriptTokenType.METHOD_END, TypeScriptTokenType.DECLARATION, + TypeScriptTokenType.ASSIGNMENT, TypeScriptTokenType.METHOD_BEGIN, TypeScriptTokenType.RETURN, TypeScriptTokenType.METHOD_END, + TypeScriptTokenType.DECLARATION, TypeScriptTokenType.ASSIGNMENT, TypeScriptTokenType.METHOD_BEGIN, TypeScriptTokenType.RETURN, + TypeScriptTokenType.METHOD_END); + collector.testFile("class.ts").testSourceCoverage().testTokenSequence(TypeScriptTokenType.CLASS_BEGIN, TypeScriptTokenType.DECLARATION, + TypeScriptTokenType.DECLARATION, TypeScriptTokenType.ASSIGNMENT, TypeScriptTokenType.CONSTRUCTOR_BEGIN, + TypeScriptTokenType.ASSIGNMENT, TypeScriptTokenType.CONSTRUCTOR_END, TypeScriptTokenType.METHOD_BEGIN, TypeScriptTokenType.RETURN, + TypeScriptTokenType.METHOD_END, TypeScriptTokenType.METHOD_BEGIN, TypeScriptTokenType.ASSIGNMENT, TypeScriptTokenType.METHOD_END, + TypeScriptTokenType.METHOD_BEGIN, TypeScriptTokenType.RETURN, TypeScriptTokenType.METHOD_END, TypeScriptTokenType.METHOD_BEGIN, + TypeScriptTokenType.ASSIGNMENT, TypeScriptTokenType.METHOD_END, TypeScriptTokenType.CLASS_END); + collector.testFile("if.ts").testSourceCoverage().testTokenSequence(TypeScriptTokenType.IF_BEGIN, TypeScriptTokenType.FUNCTION_CALL, + TypeScriptTokenType.IF_BEGIN, TypeScriptTokenType.IF_BEGIN, TypeScriptTokenType.FUNCTION_CALL, TypeScriptTokenType.IF_BEGIN, + TypeScriptTokenType.FUNCTION_CALL, TypeScriptTokenType.IF_END, TypeScriptTokenType.IF_END, TypeScriptTokenType.IF_BEGIN, + TypeScriptTokenType.FUNCTION_CALL, TypeScriptTokenType.IF_END); + collector.testFile("allTokens.ts").testCoverages(); + } + + @Override + protected void configureIgnoredLines(TestSourceIgnoredLinesCollector collector) { + collector.ignoreMultipleLines("/*", "*/"); + collector.ignoreLinesByPrefix("//"); + } +} diff --git a/languages/typescript/src/test/resources/de/jplag/typescript/allJSTokens.js b/languages/typescript/src/test/resources/de/jplag/typescript/allJSTokens.js new file mode 100644 index 000000000..315b9d965 --- /dev/null +++ b/languages/typescript/src/test/resources/de/jplag/typescript/allJSTokens.js @@ -0,0 +1,58 @@ +import {test} from 'test'; +class Class { + #x; + + constructor(x) { + this.x = x; + } + + test(y) { + return this.x + y; + } +} + +let a = 3; +const d = 4; + +function c() { + let b; + b = 8; + return a - b; +} + +switch (c()) { + case 1: + break; + default: + console.log(c()) +} + +try { + throw "Error" +} catch (e) { + console.log('Error', e); +} finally { + console.log() +} + +for (let i = 0; i < a; i++) { + +} + +while(a > 3) { + a--; + if (d > 5) { + continue; + } +} + +export default Class; +export { c }; + +if (d) { + +} else if (d < 10) { + +} else { + +} \ No newline at end of file diff --git a/languages/typescript/src/test/resources/de/jplag/typescript/allTokens.ts b/languages/typescript/src/test/resources/de/jplag/typescript/allTokens.ts new file mode 100644 index 000000000..86a64b99a --- /dev/null +++ b/languages/typescript/src/test/resources/de/jplag/typescript/allTokens.ts @@ -0,0 +1,70 @@ +import {test} from 'test'; + +namespace Name { + enum Values { + val1 = 3, + val2 + } +} + +interface Inter { + value: number; +} + +class Class { + private x; + + constructor(x) { + this.x = x; + } + + public test(y: number) { + return this.x + y; + } +} + +let a = 3; +const d = 4; + +function c() { + let b; + b = 8; + return a - b; +} + +switch (c()) { + case 1: + break; + default: + console.log(c()) +} + +try { + throw "Error" +} catch (e) { + console.log('Error', e); +} finally { + console.log() +} + +for (let i = 0; i < a; i++) { + +} + +while(a > 3) { + a--; + if (d > 5) { + continue; + } +} + +export default Name; +export { c }; + +if (d) { + +} else if (d < 10) { + +} else { + +} \ No newline at end of file diff --git a/languages/typescript/src/test/resources/de/jplag/typescript/class.ts b/languages/typescript/src/test/resources/de/jplag/typescript/class.ts new file mode 100644 index 000000000..fca08b0d6 --- /dev/null +++ b/languages/typescript/src/test/resources/de/jplag/typescript/class.ts @@ -0,0 +1,25 @@ +class AddTest { + + private _x: number; + protected _y = 5; + + constructor(x) { + this._x = x; + } + + public getX() { + return this._x; + } + + public setX(x: number) { + this._x = x; + } + + get y() { + return this._y; + } + + set y(y: number) { + this._y = y; + } +} \ No newline at end of file diff --git a/languages/typescript/src/test/resources/de/jplag/typescript/forLoops.ts b/languages/typescript/src/test/resources/de/jplag/typescript/forLoops.ts new file mode 100644 index 000000000..17c9b8a78 --- /dev/null +++ b/languages/typescript/src/test/resources/de/jplag/typescript/forLoops.ts @@ -0,0 +1,13 @@ +const are = ["Test", "Test2"]; + +for (let i = 0; i < are.length; i++) { + console.log(are[i]) +} + +for (const a in are) { + console.log(a) +} + +for (const a of are) { + console.log(a) +} \ No newline at end of file diff --git a/languages/typescript/src/test/resources/de/jplag/typescript/if.ts b/languages/typescript/src/test/resources/de/jplag/typescript/if.ts new file mode 100644 index 000000000..11c474542 --- /dev/null +++ b/languages/typescript/src/test/resources/de/jplag/typescript/if.ts @@ -0,0 +1,11 @@ +if (true) { + console.log() +} else if(true) { + console.log() +} else { + console.log() +} + +if (true) { + console.log() +} \ No newline at end of file diff --git a/languages/typescript/src/test/resources/de/jplag/typescript/methods.ts b/languages/typescript/src/test/resources/de/jplag/typescript/methods.ts new file mode 100644 index 000000000..69cf57521 --- /dev/null +++ b/languages/typescript/src/test/resources/de/jplag/typescript/methods.ts @@ -0,0 +1,11 @@ +function add1(a: number, b: number) { + return a + b; +} + +const add2 = function (a: number, b: number) { + return a + b; +} + +const add3 = (a: number, b: number) => { + return a + b; +} \ No newline at end of file diff --git a/languages/typescript/src/test/resources/de/jplag/typescript/simpleTest.ts b/languages/typescript/src/test/resources/de/jplag/typescript/simpleTest.ts new file mode 100644 index 000000000..9f57d346d --- /dev/null +++ b/languages/typescript/src/test/resources/de/jplag/typescript/simpleTest.ts @@ -0,0 +1,9 @@ +let counter = 0; +let arr = [] + +for (let i = 1; i <=5; i++) { + arr.push(i); +} + +let arrRev = arr.reverse(); +counter++; \ No newline at end of file