Skip to content

Commit

Permalink
added findLastBinop (#213)
Browse files Browse the repository at this point in the history
* added findLastBinop
* fixed enum type parameter
* fixed macro type hint
  • Loading branch information
AlexHaxe authored Dec 1, 2022
1 parent a1be77a commit f06fcdb
Show file tree
Hide file tree
Showing 10 changed files with 206 additions and 15 deletions.
2 changes: 1 addition & 1 deletion .haxerc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"version": "779b005",
"version": "3478049",
"resolveLibs": "scoped"
}
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## dev branch / next version (1.x.x)

## version 1.2.4 (2022-12-01)

- Added findLastBinop utils function ([#213](https://github.com/HaxeCheckstyle/tokentree/issues/213))
- Fixed macro type hint ([#213](https://github.com/HaxeCheckstyle/tokentree/issues/213))
- Fixed enum type parameter ([#213](https://github.com/HaxeCheckstyle/tokentree/issues/213))

## version 1.2.3 (2022-09-14)

- Fixed suffixes for negative numeric literals
Expand Down
2 changes: 1 addition & 1 deletion checkstyle.json
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@
"tokentree/walk/WalkStatement",
"tokentree/walk/WalkFinal",
"tokentree/utils/TokenTreeCheckUtils",
"tokentree/TokenStream:consumeInlineMarkup",
"tokentree/TokenStream:consumeInlineMarkup"
],
"NestedForDepth": [
"TestMain"
Expand Down
4 changes: 2 additions & 2 deletions haxelib.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
"contributors": [
"AlexHaxe"
],
"releasenote": "fixed suffixes for negative numeric literals - see CHANGELOG for details",
"version": "1.2.3",
"releasenote": "added findLastBinop utils function, fixed macro type hint and enum type parameter - see CHANGELOG for details",
"version": "1.2.4",
"url": "https://github.com/HaxeCheckstyle/tokentree",
"dependencies": {}
}
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tokentree",
"version": "1.2.3",
"version": "1.2.4",
"description": "TokenTree library used by haxe-checkstyle, haxe-formatter and haxe-languageserver",
"repository": {
"type": "git",
Expand All @@ -16,7 +16,7 @@
"email": "[email protected]"
},
"devDependencies": {
"lix": "^15.11.6"
"lix": "^15.12.0"
},
"bugs": "https://github.com/HaxeCheckstyle/tokentree/issues",
"license": "MIT"
Expand Down
70 changes: 70 additions & 0 deletions src/tokentree/utils/TokenTreeCheckUtils.hx
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,8 @@ class TokenTreeCheckUtils {
return findColonParent(parent);
case Kwd(KwdFunction):
return TypeHint;
case Kwd(KwdMacro):
return TypeHint;
default:
}
return Unknown;
Expand Down Expand Up @@ -978,6 +980,74 @@ class TokenTreeCheckUtils {
}
return false;
}

public static function findLastBinop(token:Null<TokenTree>):Null<TokenTree> {
if (token == null) {
return null;
}
var lastBinop:Null<TokenTree> = null;
function visitSiblings(parent:TokenTree) {
if (parent == null) {
return null;
}
while (parent.nextSibling != null) {
switch (parent.nextSibling.tok) {
case Binop(_):
parent = parent.nextSibling;
lastBinop = parent;
case Dot:
return parent.nextSibling;
case Comma | Semicolon:
return parent;
default:
return parent;
}
}
return parent;
}

token = visitSiblings(token);

while (token.hasChildren()) {
var child = switch (token.tok) {
case BkOpen: token.access().firstOf(BkClose).token;
case BrOpen: token.access().firstOf(BrClose).token;
case POpen:
token.access().firstOf(PClose).token;
token = visitSiblings(token);
default: token.getFirstChild();
}
if (child == null) {
return lastBinop;
}
switch (child.tok) {
case Binop(_):
lastBinop = child;
token = visitSiblings(child);
case BkOpen:
token = child.access().firstOf(BkClose).token;
if (token == null) {
return lastBinop;
}
case BrOpen:
token = child.access().firstOf(BrClose).token;
if (token == null) {
return lastBinop;
}
case POpen:
token = child.access().firstOf(PClose).token;
if (token == null) {
return lastBinop;
}
token = visitSiblings(token);
case Dot:
token = visitSiblings(child);
default:
token = child;
}
}
return lastBinop;
}
}

enum BrOpenType {
Expand Down
11 changes: 11 additions & 0 deletions src/tokentree/walk/WalkStatement.hx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package tokentree.walk;

import tokentree.utils.TokenTreeCheckUtils;

class WalkStatement {
public static function walkStatement(stream:TokenStream, parent:TokenTree) {
walkStatementWithoutSemicolon(stream, parent);
Expand Down Expand Up @@ -39,6 +41,9 @@ class WalkStatement {
if (stream.tokenForMatch().match(Arrow)) {
walkStatementWithoutSemicolon(stream, parent);
}
if (stream.tokenForMatch().match(POpen)) {
walkStatementWithoutSemicolon(stream, parent);
}
return;
}
wantMore = true;
Expand Down Expand Up @@ -366,6 +371,12 @@ class WalkStatement {
return parent;
case Binop(_):
return parent;
case DblDot:
var type:ColonType = TokenTreeCheckUtils.determineColonType(parent);
switch (type) {
case SwitchCase | At: return null;
case TypeHint | TypeCheck | Ternary | ObjectLiteral | Unknown:
}
default:
}
parent = parent.parent;
Expand Down
13 changes: 13 additions & 0 deletions test/tokentree/TokenTreeBuilderParsingTest.hx
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ class TokenTreeBuilderParsingTest implements ITest {
assertCodeParses(ABSTRACT_MYABSTRACT);
assertCodeParses(STATIC_LOCALS);
assertCodeParses(INLINE_MARKUP);
assertCodeParses(ENUM_TYPE_PARAM);
assertCodeParses(MACRO_COLON_TYPE);
}

@Test
Expand Down Expand Up @@ -1836,4 +1838,15 @@ import #if haxe4 js.lib.Promise #else js.Promise #end as JsPromise;
</soap:Body>
</soap:Envelope>;
';

var ENUM_TYPE_PARAM = 'enum Foo<Child> {
Bar<T>(options : Array<T>);
}';

var MACRO_COLON_TYPE = 'function test() {
switch type {
case Foo:
macro :Float;
}
}';
}
91 changes: 91 additions & 0 deletions test/tokentree/utils/TokenTreeCheckUtilsTest.hx
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,84 @@ class TokenTreeCheckUtilsTest implements ITest {
Assert.isFalse(TokenTreeCheckUtils.isMetadata(tokens[index++]));
}

@Test
public function testFindLastBinop() {
function assertLastBinop(code:String, binop:String, offset:Int = 0, ?pos:PosInfos):Void {
var root:TokenTree = assertExpressionCodeParses(code, pos);

if (offset != 0) {
var tokens = root.filterCallback((token:TokenTree, index:Int) -> {
if ((token.pos.min <= offset) && (token.pos.max > offset)) {
return FoundSkipSubtree;
}
GoDeeper;
});
if (tokens.length == 1) {
root = tokens[0];
}
}
var lastBinop:Null<TokenTree> = TokenTreeCheckUtils.findLastBinop(root);
Assert.notNull(lastBinop, pos);
Assert.equals(binop, '$lastBinop', pos);
}

assertLastBinop("x + 1", "+");
assertLastBinop("x - 1", "-");
assertLastBinop("x * 1", "*");
assertLastBinop("x / 1", "/");

assertLastBinop("width.x + 1 - 1", "-");
assertLastBinop("width.x * 1 - 1", "-");
assertLastBinop("width.x / 1 - 1", "-");

assertLastBinop("width.x + 1 * 1", "*");
assertLastBinop("width.x - 1 * 1", "*");
assertLastBinop("width.x / 1 * 1", "*");

assertLastBinop("width.x + (1 * 1)", "+");
assertLastBinop("width.x - (1 * 1)", "-");
assertLastBinop("width.x / (1 * 1)", "/");
assertLastBinop("width.x - (1 + 1)", "-");
assertLastBinop("width.x * (1 + 1)", "*");
assertLastBinop("width.x / (1 + 1)", "/");

assertLastBinop("width * point.x * (point.y * 1) + 2", "+");
assertLastBinop("width + point.x + (point.y - 1) * 2", "*");
assertLastBinop("width + point.x / (point.y / 1) * 2", "*");
assertLastBinop("width + point.x + (point.y + 1) - 2", "-");

assertLastBinop("var x = (width.width) * point.x / (point.y * 1)", "/");
assertLastBinop("var x = (width.width) * point.x + (point.y * 1)", "+");
assertLastBinop("var x = (width.width) * point.x - (point.y * 1)", "-");
assertLastBinop("var x = (width.width) + point.x - (point.y * 1)", "-");
assertLastBinop("var x = (width.width) + point.x * (point.y * 1)", "*");

assertLastBinop("width.width * point.x / (point.y * 1)", "/");
assertLastBinop("width.width / point.x * (point.y * 1)", "*");
assertLastBinop("width.width - point.x + (point.y * 1)", "+");
assertLastBinop("width.width * point.x + (point.y * 1)", "+");
assertLastBinop("width.width / point.x + (point.y * 1)", "+");

assertLastBinop("var val = Math.max(widthx - 1, widthy * 6)", "-", 19);
assertLastBinop("var val = Math.max(width - 1, width * 6)", "*", 30);

assertLastBinop("var val = Math.max(width * 1 / 2, width/6);", "/", 19);
assertLastBinop("var val = Math.max(width / 1 + 4, width/6);", "+", 19);
assertLastBinop("var val = Math.max(width.x + 1 - 1, width/6*6);", "-", 19);

assertLastBinop("var val = Math.max(width * point.x * (point.y * 1) - 2, width * 6)", "-", 19);
assertLastBinop("var val = Math.max(width * point.x * (point.y * 1) / 2, width * 6)", "/", 19);
assertLastBinop("var val = Math.max(width + point.x * (point.y * 1) - 2, width * 6)", "-", 19);
assertLastBinop("var val = Math.max(width + point.x * (point.y * 1) / 2, width * 6)", "/", 19);
assertLastBinop("var val = Math.max(width * point.x + (point.y * 1) - 2, width * 6)", "-", 19);
assertLastBinop("var val = Math.max(width * point.x + (point.y * 1) / 2, width * 6)", "/", 19);
assertLastBinop("var val = Math.max(width + point.x + (point.y * 1) - 2, width * 6)", "-", 19);
assertLastBinop("var val = Math.max(width + point.x + (point.y * 1) / 2, width * 6)", "/", 19);

assertLastBinop("var val = Math.max((width).width * point.x + (point.y * 1), width * 6)", "+", 19);
assertLastBinop("var val = Math.max((width).width * point.x / (point.y * 1), width * 6)", "/", 19);
}

public function assertCodeParses(code:String, ?pos:PosInfos):TokenTree {
var builder:Null<TestTokenTreeBuilder> = null;
try {
Expand All @@ -631,6 +709,19 @@ class TokenTreeCheckUtilsTest implements ITest {
}
return new TokenTree(null, "", null, 0, true);
}

public function assertExpressionCodeParses(code:String, ?pos:PosInfos):TokenTree {
var builder:Null<TestTokenTreeBuilder> = null;
try {
builder = TestTokenTreeBuilder.parseExpressionCode(code);
Assert.isTrue(builder.isStreamEmpty(), pos);
return builder.root;
}
catch (e:Any) {
Assert.fail("code should not throw execption " + e, pos);
}
return new TokenTree(null, "", null, 0, true);
}
}

@:enum
Expand Down

0 comments on commit f06fcdb

Please sign in to comment.