Skip to content

Commit 0452a80

Browse files
committed
1 parent 8ed4e4b commit 0452a80

File tree

2 files changed

+103
-0
lines changed

2 files changed

+103
-0
lines changed

fluent-syntax/src/ast.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,51 @@
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+
}
49+
else if (!scalars_equal(thisVal, otherVal, ignoredFields)) {
50+
return false;
51+
}
52+
}
53+
return true;
54+
}
1055
}
1156

1257
/*
@@ -18,6 +63,23 @@ class SyntaxNode extends BaseNode {
1863
}
1964
}
2065

66+
function scalars_equal(thisVal, otherVal, ignoredFields) {
67+
if (thisVal instanceof BaseNode) {
68+
return thisVal.equals(otherVal, ignoredFields);
69+
}
70+
return thisVal === otherVal;
71+
}
72+
73+
function sorting_key_compare(left, right) {
74+
if (left.sorting_key < right.sorting_key) {
75+
return -1;
76+
}
77+
if (left.sorting_key === right.sorting_key) {
78+
return 0;
79+
}
80+
return 1;
81+
}
82+
2183
export class Resource extends SyntaxNode {
2284
constructor(body = []) {
2385
super();
@@ -166,6 +228,10 @@ export class Attribute extends SyntaxNode {
166228
this.id = id;
167229
this.value = value;
168230
}
231+
232+
get sorting_key() {
233+
return this.id.name;
234+
}
169235
}
170236

171237
export class Variant extends SyntaxNode {
@@ -176,6 +242,13 @@ export class Variant extends SyntaxNode {
176242
this.value = value;
177243
this.default = def;
178244
}
245+
246+
get sorting_key() {
247+
if (this.key instanceof NumberExpression) {
248+
return this.key.value;
249+
}
250+
return this.key.name;
251+
}
179252
}
180253

181254
export class NamedArgument extends SyntaxNode {

fluent-syntax/test/ast_test.js

Lines changed: 30 additions & 0 deletions
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)