@holoscript/robotics-plugin 2.0.1 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +9 -9
- package/src/__tests__/isaac-lab-interop.test.ts +13 -10
- package/src/ast.ts +6 -1
- package/src/index.ts +32 -22
- package/src/parser.ts +19 -7
- package/src/traits/ROS2HardwareLoopTrait.ts +29 -14
- package/src/usd-codegen.ts +43 -14
- package/LICENSE +0 -21
- package/dist/ast.d.ts +0 -69
- package/dist/ast.js +0 -9
- package/dist/index.d.ts +0 -127
- package/dist/index.js +0 -349
- package/dist/lexer.d.ts +0 -52
- package/dist/lexer.js +0 -251
- package/dist/parser.d.ts +0 -30
- package/dist/parser.js +0 -376
- package/dist/traits/ROS2HardwareLoopTrait.d.ts +0 -15
- package/dist/traits/ROS2HardwareLoopTrait.js +0 -49
- package/dist/traits/types.d.ts +0 -4
- package/dist/traits/types.js +0 -1
- package/dist/usd-codegen.d.ts +0 -45
- package/dist/usd-codegen.js +0 -504
package/dist/lexer.js
DELETED
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HoloScript Lexer (Tokenizer)
|
|
3
|
-
*
|
|
4
|
-
* Converts HoloScript source code into a stream of tokens for parsing.
|
|
5
|
-
* Supports: keywords, identifiers, traits (@trait), strings, numbers, symbols
|
|
6
|
-
*/
|
|
7
|
-
export var TokenType;
|
|
8
|
-
(function (TokenType) {
|
|
9
|
-
// Keywords
|
|
10
|
-
TokenType["KEYWORD"] = "KEYWORD";
|
|
11
|
-
// Identifiers & Traits
|
|
12
|
-
TokenType["IDENTIFIER"] = "IDENTIFIER";
|
|
13
|
-
TokenType["TRAIT"] = "TRAIT";
|
|
14
|
-
TokenType["STRING"] = "STRING";
|
|
15
|
-
TokenType["NUMBER"] = "NUMBER";
|
|
16
|
-
// Symbols
|
|
17
|
-
TokenType["LBRACE"] = "LBRACE";
|
|
18
|
-
TokenType["RBRACE"] = "RBRACE";
|
|
19
|
-
TokenType["LBRACKET"] = "LBRACKET";
|
|
20
|
-
TokenType["RBRACKET"] = "RBRACKET";
|
|
21
|
-
TokenType["COLON"] = "COLON";
|
|
22
|
-
TokenType["COMMA"] = "COMMA";
|
|
23
|
-
// Special
|
|
24
|
-
TokenType["COMMENT"] = "COMMENT";
|
|
25
|
-
TokenType["EOF"] = "EOF";
|
|
26
|
-
})(TokenType || (TokenType = {}));
|
|
27
|
-
export class Lexer {
|
|
28
|
-
constructor(source) {
|
|
29
|
-
this.position = 0;
|
|
30
|
-
this.line = 1;
|
|
31
|
-
this.column = 1;
|
|
32
|
-
this.tokens = [];
|
|
33
|
-
this.keywords = new Set([
|
|
34
|
-
'composition',
|
|
35
|
-
'object',
|
|
36
|
-
'using',
|
|
37
|
-
'template',
|
|
38
|
-
'group',
|
|
39
|
-
'scene',
|
|
40
|
-
]);
|
|
41
|
-
this.source = source;
|
|
42
|
-
}
|
|
43
|
-
tokenize() {
|
|
44
|
-
while (this.position < this.source.length) {
|
|
45
|
-
this.scanToken();
|
|
46
|
-
}
|
|
47
|
-
this.tokens.push({
|
|
48
|
-
type: TokenType.EOF,
|
|
49
|
-
value: '',
|
|
50
|
-
line: this.line,
|
|
51
|
-
column: this.column,
|
|
52
|
-
});
|
|
53
|
-
return this.tokens;
|
|
54
|
-
}
|
|
55
|
-
scanToken() {
|
|
56
|
-
const char = this.currentChar();
|
|
57
|
-
// Skip whitespace
|
|
58
|
-
if (this.isWhitespace(char)) {
|
|
59
|
-
this.skipWhitespace();
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
// Comments
|
|
63
|
-
if (char === '/' && this.peek() === '/') {
|
|
64
|
-
this.skipComment();
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
// Traits (@identifier)
|
|
68
|
-
if (char === '@') {
|
|
69
|
-
this.scanTrait();
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
// String literals ("...")
|
|
73
|
-
if (char === '"') {
|
|
74
|
-
this.scanString();
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
// Numbers (0.1, 3.14, -2.0)
|
|
78
|
-
if (this.isDigit(char) || (char === '-' && this.isDigit(this.peek()))) {
|
|
79
|
-
this.scanNumber();
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
// Keywords & Identifiers
|
|
83
|
-
if (this.isAlpha(char)) {
|
|
84
|
-
this.scanIdentifierOrKeyword();
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
// Single-character tokens
|
|
88
|
-
this.scanSymbol(char);
|
|
89
|
-
}
|
|
90
|
-
scanTrait() {
|
|
91
|
-
const startLine = this.line;
|
|
92
|
-
const startColumn = this.column;
|
|
93
|
-
this.advance(); // Skip '@'
|
|
94
|
-
let trait = '';
|
|
95
|
-
while (this.position < this.source.length &&
|
|
96
|
-
this.isAlphaNumericOrUnderscore(this.currentChar())) {
|
|
97
|
-
trait += this.currentChar();
|
|
98
|
-
this.advance();
|
|
99
|
-
}
|
|
100
|
-
this.tokens.push({
|
|
101
|
-
type: TokenType.TRAIT,
|
|
102
|
-
value: trait,
|
|
103
|
-
line: startLine,
|
|
104
|
-
column: startColumn,
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
scanString() {
|
|
108
|
-
const startLine = this.line;
|
|
109
|
-
const startColumn = this.column;
|
|
110
|
-
this.advance(); // Skip opening "
|
|
111
|
-
let str = '';
|
|
112
|
-
while (this.position < this.source.length && this.currentChar() !== '"') {
|
|
113
|
-
if (this.currentChar() === '\\') {
|
|
114
|
-
// Handle escape sequences
|
|
115
|
-
this.advance();
|
|
116
|
-
if (this.position < this.source.length) {
|
|
117
|
-
str += this.currentChar();
|
|
118
|
-
this.advance();
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
else {
|
|
122
|
-
str += this.currentChar();
|
|
123
|
-
this.advance();
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
if (this.currentChar() === '"') {
|
|
127
|
-
this.advance(); // Skip closing "
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
throw new Error(`Unterminated string at line ${startLine}, column ${startColumn}`);
|
|
131
|
-
}
|
|
132
|
-
this.tokens.push({
|
|
133
|
-
type: TokenType.STRING,
|
|
134
|
-
value: str,
|
|
135
|
-
line: startLine,
|
|
136
|
-
column: startColumn,
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
scanNumber() {
|
|
140
|
-
const startLine = this.line;
|
|
141
|
-
const startColumn = this.column;
|
|
142
|
-
let num = '';
|
|
143
|
-
// Handle negative sign
|
|
144
|
-
if (this.currentChar() === '-') {
|
|
145
|
-
num += this.currentChar();
|
|
146
|
-
this.advance();
|
|
147
|
-
}
|
|
148
|
-
// Integer part
|
|
149
|
-
while (this.position < this.source.length && this.isDigit(this.currentChar())) {
|
|
150
|
-
num += this.currentChar();
|
|
151
|
-
this.advance();
|
|
152
|
-
}
|
|
153
|
-
// Decimal part
|
|
154
|
-
if (this.currentChar() === '.' && this.isDigit(this.peek())) {
|
|
155
|
-
num += this.currentChar();
|
|
156
|
-
this.advance();
|
|
157
|
-
while (this.position < this.source.length && this.isDigit(this.currentChar())) {
|
|
158
|
-
num += this.currentChar();
|
|
159
|
-
this.advance();
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
this.tokens.push({
|
|
163
|
-
type: TokenType.NUMBER,
|
|
164
|
-
value: num,
|
|
165
|
-
line: startLine,
|
|
166
|
-
column: startColumn,
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
scanIdentifierOrKeyword() {
|
|
170
|
-
const startLine = this.line;
|
|
171
|
-
const startColumn = this.column;
|
|
172
|
-
let ident = '';
|
|
173
|
-
while (this.position < this.source.length &&
|
|
174
|
-
this.isAlphaNumericOrUnderscore(this.currentChar())) {
|
|
175
|
-
ident += this.currentChar();
|
|
176
|
-
this.advance();
|
|
177
|
-
}
|
|
178
|
-
const type = this.keywords.has(ident) ? TokenType.KEYWORD : TokenType.IDENTIFIER;
|
|
179
|
-
this.tokens.push({
|
|
180
|
-
type,
|
|
181
|
-
value: ident,
|
|
182
|
-
line: startLine,
|
|
183
|
-
column: startColumn,
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
scanSymbol(char) {
|
|
187
|
-
const symbolMap = {
|
|
188
|
-
'{': TokenType.LBRACE,
|
|
189
|
-
'}': TokenType.RBRACE,
|
|
190
|
-
'[': TokenType.LBRACKET,
|
|
191
|
-
']': TokenType.RBRACKET,
|
|
192
|
-
':': TokenType.COLON,
|
|
193
|
-
',': TokenType.COMMA,
|
|
194
|
-
};
|
|
195
|
-
const tokenType = symbolMap[char];
|
|
196
|
-
if (tokenType) {
|
|
197
|
-
this.tokens.push({
|
|
198
|
-
type: tokenType,
|
|
199
|
-
value: char,
|
|
200
|
-
line: this.line,
|
|
201
|
-
column: this.column,
|
|
202
|
-
});
|
|
203
|
-
this.advance();
|
|
204
|
-
}
|
|
205
|
-
else {
|
|
206
|
-
throw new Error(`Unexpected character '${char}' at line ${this.line}, column ${this.column}`);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
skipWhitespace() {
|
|
210
|
-
while (this.position < this.source.length && this.isWhitespace(this.currentChar())) {
|
|
211
|
-
if (this.currentChar() === '\n') {
|
|
212
|
-
this.line++;
|
|
213
|
-
this.column = 1;
|
|
214
|
-
}
|
|
215
|
-
else {
|
|
216
|
-
this.column++;
|
|
217
|
-
}
|
|
218
|
-
this.position++;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
skipComment() {
|
|
222
|
-
// Skip until end of line
|
|
223
|
-
while (this.position < this.source.length && this.currentChar() !== '\n') {
|
|
224
|
-
this.position++;
|
|
225
|
-
this.column++;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
currentChar() {
|
|
229
|
-
return this.source[this.position] || '';
|
|
230
|
-
}
|
|
231
|
-
peek(offset = 1) {
|
|
232
|
-
const peekPos = this.position + offset;
|
|
233
|
-
return this.source[peekPos] || '';
|
|
234
|
-
}
|
|
235
|
-
advance() {
|
|
236
|
-
this.position++;
|
|
237
|
-
this.column++;
|
|
238
|
-
}
|
|
239
|
-
isWhitespace(char) {
|
|
240
|
-
return /\s/.test(char);
|
|
241
|
-
}
|
|
242
|
-
isDigit(char) {
|
|
243
|
-
return /[0-9]/.test(char);
|
|
244
|
-
}
|
|
245
|
-
isAlpha(char) {
|
|
246
|
-
return /[a-z_]/i.test(char);
|
|
247
|
-
}
|
|
248
|
-
isAlphaNumericOrUnderscore(char) {
|
|
249
|
-
return /[a-z0-9_]/i.test(char);
|
|
250
|
-
}
|
|
251
|
-
}
|
package/dist/parser.d.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HoloScript Parser
|
|
3
|
-
*
|
|
4
|
-
* Converts token stream into Abstract Syntax Tree (AST).
|
|
5
|
-
* Uses recursive descent parsing.
|
|
6
|
-
*/
|
|
7
|
-
import { Token } from './lexer';
|
|
8
|
-
import { CompositionNode } from './ast';
|
|
9
|
-
export declare class Parser {
|
|
10
|
-
private tokens;
|
|
11
|
-
private position;
|
|
12
|
-
constructor(tokens: Token[]);
|
|
13
|
-
parse(): CompositionNode;
|
|
14
|
-
private currentToken;
|
|
15
|
-
private peek;
|
|
16
|
-
private advance;
|
|
17
|
-
private expect;
|
|
18
|
-
private parseComposition;
|
|
19
|
-
private parseObject;
|
|
20
|
-
private parseValue;
|
|
21
|
-
private parseArray;
|
|
22
|
-
private parseDomainRandomizationBlock;
|
|
23
|
-
private parsePhysicsRandomization;
|
|
24
|
-
private parseActuatorRandomization;
|
|
25
|
-
private parseObservationRandomization;
|
|
26
|
-
private parseInitialStateRandomization;
|
|
27
|
-
private parseNumberRangeMap;
|
|
28
|
-
private parseDisturbanceRandomization;
|
|
29
|
-
private parseActuatorGroupBlock;
|
|
30
|
-
}
|
package/dist/parser.js
DELETED
|
@@ -1,376 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HoloScript Parser
|
|
3
|
-
*
|
|
4
|
-
* Converts token stream into Abstract Syntax Tree (AST).
|
|
5
|
-
* Uses recursive descent parsing.
|
|
6
|
-
*/
|
|
7
|
-
import { TokenType } from './lexer';
|
|
8
|
-
export class Parser {
|
|
9
|
-
constructor(tokens) {
|
|
10
|
-
this.position = 0;
|
|
11
|
-
this.tokens = tokens;
|
|
12
|
-
}
|
|
13
|
-
parse() {
|
|
14
|
-
return this.parseComposition();
|
|
15
|
-
}
|
|
16
|
-
currentToken() {
|
|
17
|
-
const token = this.tokens[this.position];
|
|
18
|
-
if (!token) {
|
|
19
|
-
throw new Error('Unexpected end of input');
|
|
20
|
-
}
|
|
21
|
-
return token;
|
|
22
|
-
}
|
|
23
|
-
peek(offset = 1) {
|
|
24
|
-
const pos = this.position + offset;
|
|
25
|
-
const token = pos < this.tokens.length ? this.tokens[pos] : this.tokens[this.tokens.length - 1];
|
|
26
|
-
if (!token) {
|
|
27
|
-
throw new Error('Unexpected end of input');
|
|
28
|
-
}
|
|
29
|
-
return token;
|
|
30
|
-
}
|
|
31
|
-
advance() {
|
|
32
|
-
const token = this.currentToken();
|
|
33
|
-
this.position++;
|
|
34
|
-
return token;
|
|
35
|
-
}
|
|
36
|
-
expect(type, value) {
|
|
37
|
-
const token = this.currentToken();
|
|
38
|
-
if (token.type !== type) {
|
|
39
|
-
throw new Error(`Expected ${type}${value ? ` "${value}"` : ''} but got ${token.type} "${token.value}" at line ${token.line}, column ${token.column}`);
|
|
40
|
-
}
|
|
41
|
-
if (value !== undefined && token.value !== value) {
|
|
42
|
-
throw new Error(`Expected "${value}" but got "${token.value}" at line ${token.line}, column ${token.column}`);
|
|
43
|
-
}
|
|
44
|
-
return this.advance();
|
|
45
|
-
}
|
|
46
|
-
parseComposition() {
|
|
47
|
-
const startToken = this.expect(TokenType.KEYWORD, 'composition');
|
|
48
|
-
const name = this.expect(TokenType.STRING).value;
|
|
49
|
-
this.expect(TokenType.LBRACE);
|
|
50
|
-
const objects = [];
|
|
51
|
-
let domainRandomization;
|
|
52
|
-
while (this.currentToken().type !== TokenType.RBRACE &&
|
|
53
|
-
this.currentToken().type !== TokenType.EOF) {
|
|
54
|
-
// Check for domain_randomization block at composition level
|
|
55
|
-
if (this.currentToken().type === TokenType.IDENTIFIER &&
|
|
56
|
-
this.currentToken().value === 'domain_randomization') {
|
|
57
|
-
this.advance();
|
|
58
|
-
this.expect(TokenType.COLON);
|
|
59
|
-
domainRandomization = this.parseDomainRandomizationBlock();
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
objects.push(this.parseObject());
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
this.expect(TokenType.RBRACE);
|
|
66
|
-
return {
|
|
67
|
-
type: 'composition',
|
|
68
|
-
name,
|
|
69
|
-
objects,
|
|
70
|
-
domainRandomization,
|
|
71
|
-
line: startToken.line,
|
|
72
|
-
column: startToken.column,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
parseObject() {
|
|
76
|
-
const startToken = this.expect(TokenType.KEYWORD, 'object');
|
|
77
|
-
const name = this.expect(TokenType.STRING).value;
|
|
78
|
-
// Parse traits (@trait1 @trait2 ...)
|
|
79
|
-
const traits = [];
|
|
80
|
-
while (this.currentToken().type === TokenType.TRAIT) {
|
|
81
|
-
traits.push(this.advance().value);
|
|
82
|
-
}
|
|
83
|
-
this.expect(TokenType.LBRACE);
|
|
84
|
-
// Parse properties (key: value)
|
|
85
|
-
const properties = {};
|
|
86
|
-
let domainRandomization;
|
|
87
|
-
let actuatorGroups;
|
|
88
|
-
while (this.currentToken().type !== TokenType.RBRACE &&
|
|
89
|
-
this.currentToken().type !== TokenType.EOF) {
|
|
90
|
-
// Support both object "link" @trait { ... } and object "link" { @trait ... }.
|
|
91
|
-
if (this.currentToken().type === TokenType.TRAIT) {
|
|
92
|
-
const trait = this.advance().value;
|
|
93
|
-
if (!traits.includes(trait)) {
|
|
94
|
-
traits.push(trait);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
// Check for domain_randomization block
|
|
98
|
-
else if (this.currentToken().type === TokenType.IDENTIFIER &&
|
|
99
|
-
this.currentToken().value === 'domain_randomization') {
|
|
100
|
-
this.advance();
|
|
101
|
-
this.expect(TokenType.COLON);
|
|
102
|
-
domainRandomization = this.parseDomainRandomizationBlock();
|
|
103
|
-
}
|
|
104
|
-
// Check for actuator_group block
|
|
105
|
-
else if (this.currentToken().type === TokenType.IDENTIFIER &&
|
|
106
|
-
this.currentToken().value === 'actuator_group') {
|
|
107
|
-
this.advance();
|
|
108
|
-
this.expect(TokenType.COLON);
|
|
109
|
-
const group = this.parseActuatorGroupBlock();
|
|
110
|
-
if (!actuatorGroups)
|
|
111
|
-
actuatorGroups = [];
|
|
112
|
-
actuatorGroups.push(group);
|
|
113
|
-
}
|
|
114
|
-
// Regular property
|
|
115
|
-
else {
|
|
116
|
-
const keyName = this.expect(TokenType.IDENTIFIER).value;
|
|
117
|
-
this.expect(TokenType.COLON);
|
|
118
|
-
const value = this.parseValue();
|
|
119
|
-
properties[keyName] = value;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
this.expect(TokenType.RBRACE);
|
|
123
|
-
return {
|
|
124
|
-
type: 'object',
|
|
125
|
-
name,
|
|
126
|
-
traits,
|
|
127
|
-
properties,
|
|
128
|
-
domainRandomization,
|
|
129
|
-
actuatorGroups,
|
|
130
|
-
line: startToken.line,
|
|
131
|
-
column: startToken.column,
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
parseValue() {
|
|
135
|
-
const token = this.currentToken();
|
|
136
|
-
// String value
|
|
137
|
-
if (token.type === TokenType.STRING) {
|
|
138
|
-
return this.advance().value;
|
|
139
|
-
}
|
|
140
|
-
// Number value
|
|
141
|
-
if (token.type === TokenType.NUMBER) {
|
|
142
|
-
return parseFloat(this.advance().value);
|
|
143
|
-
}
|
|
144
|
-
// Array value [1, 2, 3]
|
|
145
|
-
if (token.type === TokenType.LBRACKET) {
|
|
146
|
-
return this.parseArray();
|
|
147
|
-
}
|
|
148
|
-
// Function call (for future: diagonal([1, 2, 3]))
|
|
149
|
-
if (token.type === TokenType.IDENTIFIER && this.peek().type === TokenType.LBRACKET) {
|
|
150
|
-
this.advance(); // Skip function name for now
|
|
151
|
-
const args = this.parseArray();
|
|
152
|
-
// For now, treat function calls as arrays (e.g., diagonal([1,2,3]) -> [1,2,3])
|
|
153
|
-
// In future, could preserve function name metadata
|
|
154
|
-
return args;
|
|
155
|
-
}
|
|
156
|
-
throw new Error(`Unexpected value type ${token.type} "${token.value}" at line ${token.line}, column ${token.column}`);
|
|
157
|
-
}
|
|
158
|
-
parseArray() {
|
|
159
|
-
this.expect(TokenType.LBRACKET);
|
|
160
|
-
const values = [];
|
|
161
|
-
while (this.currentToken().type !== TokenType.RBRACKET &&
|
|
162
|
-
this.currentToken().type !== TokenType.EOF) {
|
|
163
|
-
if (this.currentToken().type === TokenType.NUMBER) {
|
|
164
|
-
values.push(parseFloat(this.advance().value));
|
|
165
|
-
}
|
|
166
|
-
else if (this.currentToken().type === TokenType.STRING) {
|
|
167
|
-
values.push(this.advance().value);
|
|
168
|
-
}
|
|
169
|
-
else if (this.currentToken().type === TokenType.LBRACKET) {
|
|
170
|
-
// Nested array
|
|
171
|
-
values.push(this.parseArray());
|
|
172
|
-
}
|
|
173
|
-
else {
|
|
174
|
-
throw new Error(`Unexpected token in array: ${this.currentToken().type} at line ${this.currentToken().line}`);
|
|
175
|
-
}
|
|
176
|
-
// Optional comma
|
|
177
|
-
if (this.currentToken().type === TokenType.COMMA) {
|
|
178
|
-
this.advance();
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
this.expect(TokenType.RBRACKET);
|
|
182
|
-
return values;
|
|
183
|
-
}
|
|
184
|
-
parseDomainRandomizationBlock() {
|
|
185
|
-
this.expect(TokenType.LBRACE);
|
|
186
|
-
const config = {};
|
|
187
|
-
while (this.currentToken().type !== TokenType.RBRACE &&
|
|
188
|
-
this.currentToken().type !== TokenType.EOF) {
|
|
189
|
-
const key = this.expect(TokenType.IDENTIFIER).value;
|
|
190
|
-
this.expect(TokenType.COLON);
|
|
191
|
-
switch (key) {
|
|
192
|
-
case 'physics':
|
|
193
|
-
config.physics = this.parsePhysicsRandomization();
|
|
194
|
-
break;
|
|
195
|
-
case 'actuator':
|
|
196
|
-
config.actuator = this.parseActuatorRandomization();
|
|
197
|
-
break;
|
|
198
|
-
case 'observation':
|
|
199
|
-
config.observation = this.parseObservationRandomization();
|
|
200
|
-
break;
|
|
201
|
-
case 'initialState':
|
|
202
|
-
config.initialState = this.parseInitialStateRandomization();
|
|
203
|
-
break;
|
|
204
|
-
case 'disturbance':
|
|
205
|
-
config.disturbance = this.parseDisturbanceRandomization();
|
|
206
|
-
break;
|
|
207
|
-
default:
|
|
208
|
-
// Skip unknown keys
|
|
209
|
-
this.parseValue();
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
this.expect(TokenType.RBRACE);
|
|
213
|
-
return config;
|
|
214
|
-
}
|
|
215
|
-
parsePhysicsRandomization() {
|
|
216
|
-
this.expect(TokenType.LBRACE);
|
|
217
|
-
const physics = {};
|
|
218
|
-
while (this.currentToken().type !== TokenType.RBRACE &&
|
|
219
|
-
this.currentToken().type !== TokenType.EOF) {
|
|
220
|
-
const key = this.expect(TokenType.IDENTIFIER).value;
|
|
221
|
-
this.expect(TokenType.COLON);
|
|
222
|
-
const value = this.parseValue();
|
|
223
|
-
if (key === 'massScale' && Array.isArray(value)) {
|
|
224
|
-
physics.massScale = [value[0], value[1]];
|
|
225
|
-
}
|
|
226
|
-
else if (key === 'frictionRange' && Array.isArray(value)) {
|
|
227
|
-
physics.frictionRange = [value[0], value[1]];
|
|
228
|
-
}
|
|
229
|
-
else if (key === 'dampingRange' && Array.isArray(value)) {
|
|
230
|
-
physics.dampingRange = [value[0], value[1]];
|
|
231
|
-
}
|
|
232
|
-
else if (key === 'armatureRange' && Array.isArray(value)) {
|
|
233
|
-
physics.armatureRange = [value[0], value[1]];
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
this.expect(TokenType.RBRACE);
|
|
237
|
-
return physics;
|
|
238
|
-
}
|
|
239
|
-
parseActuatorRandomization() {
|
|
240
|
-
this.expect(TokenType.LBRACE);
|
|
241
|
-
const actuator = {};
|
|
242
|
-
while (this.currentToken().type !== TokenType.RBRACE &&
|
|
243
|
-
this.currentToken().type !== TokenType.EOF) {
|
|
244
|
-
const key = this.expect(TokenType.IDENTIFIER).value;
|
|
245
|
-
this.expect(TokenType.COLON);
|
|
246
|
-
const value = this.parseValue();
|
|
247
|
-
if (key === 'kpNoise' && typeof value === 'number') {
|
|
248
|
-
actuator.kpNoise = value;
|
|
249
|
-
}
|
|
250
|
-
else if (key === 'kdNoise' && typeof value === 'number') {
|
|
251
|
-
actuator.kdNoise = value;
|
|
252
|
-
}
|
|
253
|
-
else if (key === 'latencyNoise' && typeof value === 'number') {
|
|
254
|
-
actuator.latencyNoise = value;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
this.expect(TokenType.RBRACE);
|
|
258
|
-
return actuator;
|
|
259
|
-
}
|
|
260
|
-
parseObservationRandomization() {
|
|
261
|
-
this.expect(TokenType.LBRACE);
|
|
262
|
-
const observation = {};
|
|
263
|
-
while (this.currentToken().type !== TokenType.RBRACE &&
|
|
264
|
-
this.currentToken().type !== TokenType.EOF) {
|
|
265
|
-
const key = this.expect(TokenType.IDENTIFIER).value;
|
|
266
|
-
this.expect(TokenType.COLON);
|
|
267
|
-
const value = this.parseValue();
|
|
268
|
-
if (key === 'jointPosNoise' && typeof value === 'number') {
|
|
269
|
-
observation.jointPosNoise = value;
|
|
270
|
-
}
|
|
271
|
-
else if (key === 'jointVelNoise' && typeof value === 'number') {
|
|
272
|
-
observation.jointVelNoise = value;
|
|
273
|
-
}
|
|
274
|
-
else if (key === 'imuNoise' && typeof value === 'number') {
|
|
275
|
-
observation.imuNoise = value;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
this.expect(TokenType.RBRACE);
|
|
279
|
-
return observation;
|
|
280
|
-
}
|
|
281
|
-
parseInitialStateRandomization() {
|
|
282
|
-
this.expect(TokenType.LBRACE);
|
|
283
|
-
const initialState = {};
|
|
284
|
-
while (this.currentToken().type !== TokenType.RBRACE &&
|
|
285
|
-
this.currentToken().type !== TokenType.EOF) {
|
|
286
|
-
const key = this.expect(TokenType.IDENTIFIER).value;
|
|
287
|
-
this.expect(TokenType.COLON);
|
|
288
|
-
if (key === 'jointPosRange') {
|
|
289
|
-
initialState.jointPosRange = this.parseNumberRangeMap();
|
|
290
|
-
}
|
|
291
|
-
else {
|
|
292
|
-
const value = this.parseValue();
|
|
293
|
-
if (key === 'rootPoseRange' && Array.isArray(value)) {
|
|
294
|
-
initialState.rootPoseRange = value;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
this.expect(TokenType.RBRACE);
|
|
299
|
-
return initialState;
|
|
300
|
-
}
|
|
301
|
-
parseNumberRangeMap() {
|
|
302
|
-
this.expect(TokenType.LBRACE);
|
|
303
|
-
const ranges = {};
|
|
304
|
-
while (this.currentToken().type !== TokenType.RBRACE &&
|
|
305
|
-
this.currentToken().type !== TokenType.EOF) {
|
|
306
|
-
const key = this.expect(TokenType.IDENTIFIER).value;
|
|
307
|
-
this.expect(TokenType.COLON);
|
|
308
|
-
const value = this.parseValue();
|
|
309
|
-
if (Array.isArray(value)) {
|
|
310
|
-
ranges[key] = [value[0], value[1]];
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
this.expect(TokenType.RBRACE);
|
|
314
|
-
return ranges;
|
|
315
|
-
}
|
|
316
|
-
parseDisturbanceRandomization() {
|
|
317
|
-
this.expect(TokenType.LBRACE);
|
|
318
|
-
const disturbance = {};
|
|
319
|
-
while (this.currentToken().type !== TokenType.RBRACE &&
|
|
320
|
-
this.currentToken().type !== TokenType.EOF) {
|
|
321
|
-
const key = this.expect(TokenType.IDENTIFIER).value;
|
|
322
|
-
this.expect(TokenType.COLON);
|
|
323
|
-
const value = this.parseValue();
|
|
324
|
-
if (key === 'forceRange' && Array.isArray(value)) {
|
|
325
|
-
disturbance.forceRange = [value[0], value[1]];
|
|
326
|
-
}
|
|
327
|
-
else if (key === 'intervalRange' && Array.isArray(value)) {
|
|
328
|
-
disturbance.intervalRange = [value[0], value[1]];
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
this.expect(TokenType.RBRACE);
|
|
332
|
-
return disturbance;
|
|
333
|
-
}
|
|
334
|
-
parseActuatorGroupBlock() {
|
|
335
|
-
// Parse actuator_group { name: "foo" type: "DelayedPDActuator" joints: ["joint1", "joint2"] ... }
|
|
336
|
-
this.expect(TokenType.LBRACE);
|
|
337
|
-
const group = {
|
|
338
|
-
name: '',
|
|
339
|
-
type: 'IdealPDActuator',
|
|
340
|
-
jointNames: [],
|
|
341
|
-
};
|
|
342
|
-
while (this.currentToken().type !== TokenType.RBRACE &&
|
|
343
|
-
this.currentToken().type !== TokenType.EOF) {
|
|
344
|
-
const key = this.expect(TokenType.IDENTIFIER).value;
|
|
345
|
-
this.expect(TokenType.COLON);
|
|
346
|
-
const value = this.parseValue();
|
|
347
|
-
switch (key) {
|
|
348
|
-
case 'name':
|
|
349
|
-
group.name = value;
|
|
350
|
-
break;
|
|
351
|
-
case 'type':
|
|
352
|
-
group.type = value;
|
|
353
|
-
break;
|
|
354
|
-
case 'joints':
|
|
355
|
-
if (Array.isArray(value)) {
|
|
356
|
-
group.jointNames = value;
|
|
357
|
-
}
|
|
358
|
-
break;
|
|
359
|
-
case 'stiffness':
|
|
360
|
-
group.stiffness = value;
|
|
361
|
-
break;
|
|
362
|
-
case 'damping':
|
|
363
|
-
group.damping = value;
|
|
364
|
-
break;
|
|
365
|
-
case 'friction':
|
|
366
|
-
group.friction = value;
|
|
367
|
-
break;
|
|
368
|
-
case 'latency':
|
|
369
|
-
group.latency = value;
|
|
370
|
-
break;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
this.expect(TokenType.RBRACE);
|
|
374
|
-
return group;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/** @ros2_hardware_loop Trait — Bidirectional hardware-in-the-loop bridge for physical robotics. @trait ros2_hardware_loop */
|
|
2
|
-
export interface ROS2Config {
|
|
3
|
-
nodeName: string;
|
|
4
|
-
topicPrefix: string;
|
|
5
|
-
updateFrequencyHz: number;
|
|
6
|
-
bidirectional: boolean;
|
|
7
|
-
}
|
|
8
|
-
export interface ROS2State {
|
|
9
|
-
connected: boolean;
|
|
10
|
-
lastPingMs: number;
|
|
11
|
-
activeTopics: string[];
|
|
12
|
-
hardwareSyncDriftMs: number;
|
|
13
|
-
}
|
|
14
|
-
import type { TraitHandler } from './types';
|
|
15
|
-
export declare function createROS2HardwareLoopHandler(): TraitHandler<ROS2Config>;
|