Skip to content

Commit 94a22c4

Browse files
committed
1 parent 8ed4e4b commit 94a22c4

File tree

2 files changed

+102
-0
lines changed

2 files changed

+102
-0
lines changed

fluent-syntax/src/ast.js

+72
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,50 @@
77
*/
88
class BaseNode {
99
constructor() {}
10+
11+
equals(other, ignoredFields = ["span"]) {
12+
const thisKeys = new Set(Object.keys(this));
13+
const otherKeys = new Set(Object.keys(other));
14+
if (ignoredFields) {
15+
for (const fieldName of ignoredFields) {
16+
thisKeys.delete(fieldName);
17+
otherKeys.delete(fieldName);
18+
}
19+
}
20+
if (thisKeys.size !== otherKeys.size) {
21+
return false;
22+
}
23+
for (const fieldName of thisKeys) {
24+
if (!otherKeys.has(fieldName)) {
25+
return false;
26+
}
27+
const thisVal = this[fieldName];
28+
const otherVal = other[fieldName];
29+
if (typeof thisVal !== typeof otherVal) {
30+
return false;
31+
}
32+
if (thisVal instanceof Array) {
33+
if (thisVal.length !== otherVal.length) {
34+
return false;
35+
}
36+
// Sort elements of order-agnostic fields to ensure the
37+
// comparison is order-agnostic as well. Annotations should be
38+
// here too but they don't have sorting keys.
39+
if (["attributes", "variants"].indexOf(fieldName) >= 0) {
40+
thisVal.sort(sorting_key_compare);
41+
otherVal.sort(sorting_key_compare);
42+
}
43+
for (let i = 0, ii = thisVal.length; i < ii; ++i) {
44+
if (!scalars_equal(thisVal[i], otherVal[i], ignoredFields)) {
45+
return false;
46+
}
47+
}
48+
} else if (!scalars_equal(thisVal, otherVal, ignoredFields)) {
49+
return false;
50+
}
51+
}
52+
return true;
53+
}
1054
}
1155

1256
/*
@@ -18,6 +62,23 @@ class SyntaxNode extends BaseNode {
1862
}
1963
}
2064

65+
function scalars_equal(thisVal, otherVal, ignoredFields) {
66+
if (thisVal instanceof BaseNode) {
67+
return thisVal.equals(otherVal, ignoredFields);
68+
}
69+
return thisVal === otherVal;
70+
}
71+
72+
function sorting_key_compare(left, right) {
73+
if (left.sorting_key < right.sorting_key) {
74+
return -1;
75+
}
76+
if (left.sorting_key === right.sorting_key) {
77+
return 0;
78+
}
79+
return 1;
80+
}
81+
2182
export class Resource extends SyntaxNode {
2283
constructor(body = []) {
2384
super();
@@ -166,6 +227,10 @@ export class Attribute extends SyntaxNode {
166227
this.id = id;
167228
this.value = value;
168229
}
230+
231+
get sorting_key() {
232+
return this.id.name;
233+
}
169234
}
170235

171236
export class Variant extends SyntaxNode {
@@ -176,6 +241,13 @@ export class Variant extends SyntaxNode {
176241
this.value = value;
177242
this.default = def;
178243
}
244+
245+
get sorting_key() {
246+
if (this.key instanceof NumberExpression) {
247+
return this.key.value;
248+
}
249+
return this.key.name;
250+
}
179251
}
180252

181253
export class NamedArgument extends SyntaxNode {

fluent-syntax/test/ast_test.js

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"use strict";
2+
3+
import assert from "assert";
4+
import * as AST from "../src/ast";
5+
6+
suite("BaseNode.equals", function() {
7+
test("Identifier.equals", function() {
8+
const thisNode = new AST.Identifier("name");
9+
const otherNode = new AST.Identifier("name");
10+
assert.strictEqual(thisNode.equals(otherNode), true);
11+
});
12+
test("Node.type", function() {
13+
const thisNode = new AST.Identifier("name");
14+
const otherNode = new AST.Function("name");
15+
assert.strictEqual(thisNode.equals(otherNode), false);
16+
});
17+
test("Array children", function() {
18+
const thisNode = new AST.Pattern([
19+
new AST.TextElement("one"),
20+
new AST.TextElement("two"),
21+
new AST.TextElement("three"),
22+
]);
23+
let otherNode = new AST.Pattern([
24+
new AST.TextElement("one"),
25+
new AST.TextElement("two"),
26+
new AST.TextElement("three"),
27+
]);
28+
assert.strictEqual(thisNode.equals(otherNode), true);
29+
});
30+
});

0 commit comments

Comments
 (0)