littlewing 0.2.0 → 0.3.0
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/dist/index.d.ts +62 -1
- package/dist/index.js +124 -26
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -176,6 +176,67 @@ declare function exponentiate(left: ASTNode, right: ASTNode): BinaryOp;
|
|
|
176
176
|
*/
|
|
177
177
|
declare function negate(argument: ASTNode): UnaryOp;
|
|
178
178
|
/**
|
|
179
|
+
* CodeGenerator - converts AST nodes back to source code
|
|
180
|
+
*/
|
|
181
|
+
declare class CodeGenerator {
|
|
182
|
+
/**
|
|
183
|
+
* Generate source code from an AST node
|
|
184
|
+
*/
|
|
185
|
+
generate(node: ASTNode): string;
|
|
186
|
+
/**
|
|
187
|
+
* Generate code for a program (multiple statements)
|
|
188
|
+
*/
|
|
189
|
+
private generateProgram;
|
|
190
|
+
/**
|
|
191
|
+
* Generate code for a number literal
|
|
192
|
+
*/
|
|
193
|
+
private generateNumberLiteral;
|
|
194
|
+
/**
|
|
195
|
+
* Generate code for a string literal
|
|
196
|
+
*/
|
|
197
|
+
private generateStringLiteral;
|
|
198
|
+
/**
|
|
199
|
+
* Generate code for an identifier
|
|
200
|
+
*/
|
|
201
|
+
private generateIdentifier;
|
|
202
|
+
/**
|
|
203
|
+
* Generate code for a binary operation
|
|
204
|
+
*/
|
|
205
|
+
private generateBinaryOp;
|
|
206
|
+
/**
|
|
207
|
+
* Generate code for a unary operation
|
|
208
|
+
*/
|
|
209
|
+
private generateUnaryOp;
|
|
210
|
+
/**
|
|
211
|
+
* Generate code for a function call
|
|
212
|
+
*/
|
|
213
|
+
private generateFunctionCall;
|
|
214
|
+
/**
|
|
215
|
+
* Generate code for a variable assignment
|
|
216
|
+
*/
|
|
217
|
+
private generateAssignment;
|
|
218
|
+
/**
|
|
219
|
+
* Check if left operand needs parentheses based on operator precedence
|
|
220
|
+
* - For left-associative operators: parens only if strictly lower precedence
|
|
221
|
+
* - For right-associative operators (^): parens if lower or equal precedence
|
|
222
|
+
*/
|
|
223
|
+
private needsParensLeft;
|
|
224
|
+
/**
|
|
225
|
+
* Check if right operand needs parentheses based on operator precedence and associativity
|
|
226
|
+
* - For right-associative operators (^): parens if strictly lower precedence
|
|
227
|
+
* - For left-associative operators: parens if lower or equal precedence
|
|
228
|
+
*/
|
|
229
|
+
private needsParensRight;
|
|
230
|
+
/**
|
|
231
|
+
* Get precedence of an operator (higher number = higher precedence)
|
|
232
|
+
*/
|
|
233
|
+
private getPrecedence;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Generate source code from an AST node
|
|
237
|
+
*/
|
|
238
|
+
declare function generate(node: ASTNode): string;
|
|
239
|
+
/**
|
|
179
240
|
* Default execution context with common Math functions and date utilities
|
|
180
241
|
* Users can use this as-is or spread it into their own context
|
|
181
242
|
*
|
|
@@ -368,4 +429,4 @@ declare class Parser {
|
|
|
368
429
|
* Parse source code string into AST
|
|
369
430
|
*/
|
|
370
431
|
declare function parseSource(source: string): ASTNode;
|
|
371
|
-
export { parseSource, isUnaryOp, isStringLiteral, isProgram, isNumberLiteral, isIdentifier, isFunctionCall, isBinaryOp, isAssignment, execute, defaultContext, exports_ast as ast, TokenType, Token, RuntimeValue, Parser, Lexer, Executor, ExecutionContext, ASTNode };
|
|
432
|
+
export { parseSource, isUnaryOp, isStringLiteral, isProgram, isNumberLiteral, isIdentifier, isFunctionCall, isBinaryOp, isAssignment, generate, execute, defaultContext, exports_ast as ast, TokenType, Token, RuntimeValue, Parser, Lexer, Executor, ExecutionContext, CodeGenerator, ASTNode };
|
package/dist/index.js
CHANGED
|
@@ -89,31 +89,6 @@ function exponentiate(left, right) {
|
|
|
89
89
|
function negate(argument) {
|
|
90
90
|
return unaryOp(argument);
|
|
91
91
|
}
|
|
92
|
-
// src/defaults.ts
|
|
93
|
-
var defaultContext = {
|
|
94
|
-
functions: {
|
|
95
|
-
abs: Math.abs,
|
|
96
|
-
ceil: Math.ceil,
|
|
97
|
-
floor: Math.floor,
|
|
98
|
-
round: Math.round,
|
|
99
|
-
sqrt: Math.sqrt,
|
|
100
|
-
min: Math.min,
|
|
101
|
-
max: Math.max,
|
|
102
|
-
sin: Math.sin,
|
|
103
|
-
cos: Math.cos,
|
|
104
|
-
tan: Math.tan,
|
|
105
|
-
log: Math.log,
|
|
106
|
-
log10: Math.log10,
|
|
107
|
-
exp: Math.exp,
|
|
108
|
-
now: () => new Date,
|
|
109
|
-
date: (...args) => new Date(...args),
|
|
110
|
-
milliseconds: (ms) => ms,
|
|
111
|
-
seconds: (s) => s * 1000,
|
|
112
|
-
minutes: (m) => m * 60 * 1000,
|
|
113
|
-
hours: (h) => h * 60 * 60 * 1000,
|
|
114
|
-
days: (d) => d * 24 * 60 * 60 * 1000
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
92
|
// src/types.ts
|
|
118
93
|
var TokenType;
|
|
119
94
|
((TokenType2) => {
|
|
@@ -157,6 +132,127 @@ function isProgram(node) {
|
|
|
157
132
|
return node.type === "Program";
|
|
158
133
|
}
|
|
159
134
|
|
|
135
|
+
// src/codegen.ts
|
|
136
|
+
class CodeGenerator {
|
|
137
|
+
generate(node) {
|
|
138
|
+
if (isProgram(node))
|
|
139
|
+
return this.generateProgram(node);
|
|
140
|
+
if (isNumberLiteral(node))
|
|
141
|
+
return this.generateNumberLiteral(node);
|
|
142
|
+
if (isStringLiteral(node))
|
|
143
|
+
return this.generateStringLiteral(node);
|
|
144
|
+
if (isIdentifier(node))
|
|
145
|
+
return this.generateIdentifier(node);
|
|
146
|
+
if (isBinaryOp(node))
|
|
147
|
+
return this.generateBinaryOp(node);
|
|
148
|
+
if (isUnaryOp(node))
|
|
149
|
+
return this.generateUnaryOp(node);
|
|
150
|
+
if (isFunctionCall(node))
|
|
151
|
+
return this.generateFunctionCall(node);
|
|
152
|
+
if (isAssignment(node))
|
|
153
|
+
return this.generateAssignment(node);
|
|
154
|
+
throw new Error(`Unknown node type`);
|
|
155
|
+
}
|
|
156
|
+
generateProgram(node) {
|
|
157
|
+
return node.statements.map((stmt) => this.generate(stmt)).join("; ");
|
|
158
|
+
}
|
|
159
|
+
generateNumberLiteral(node) {
|
|
160
|
+
return String(node.value);
|
|
161
|
+
}
|
|
162
|
+
generateStringLiteral(node) {
|
|
163
|
+
return `'${node.value}'`;
|
|
164
|
+
}
|
|
165
|
+
generateIdentifier(node) {
|
|
166
|
+
return node.name;
|
|
167
|
+
}
|
|
168
|
+
generateBinaryOp(node) {
|
|
169
|
+
const left = this.generate(node.left);
|
|
170
|
+
const right = this.generate(node.right);
|
|
171
|
+
const leftNeedsParens = this.needsParensLeft(node.left, node.operator);
|
|
172
|
+
const leftCode = leftNeedsParens ? `(${left})` : left;
|
|
173
|
+
const rightNeedsParens = this.needsParensRight(node.right, node.operator);
|
|
174
|
+
const rightCode = rightNeedsParens ? `(${right})` : right;
|
|
175
|
+
return `${leftCode} ${node.operator} ${rightCode}`;
|
|
176
|
+
}
|
|
177
|
+
generateUnaryOp(node) {
|
|
178
|
+
const arg = this.generate(node.argument);
|
|
179
|
+
const needsParens = isBinaryOp(node.argument) || isAssignment(node.argument);
|
|
180
|
+
const argCode = needsParens ? `(${arg})` : arg;
|
|
181
|
+
return `-${argCode}`;
|
|
182
|
+
}
|
|
183
|
+
generateFunctionCall(node) {
|
|
184
|
+
const args = node.arguments.map((arg) => this.generate(arg)).join(", ");
|
|
185
|
+
return `${node.name}(${args})`;
|
|
186
|
+
}
|
|
187
|
+
generateAssignment(node) {
|
|
188
|
+
const value = this.generate(node.value);
|
|
189
|
+
return `${node.name} = ${value}`;
|
|
190
|
+
}
|
|
191
|
+
needsParensLeft(node, operator) {
|
|
192
|
+
if (!isBinaryOp(node))
|
|
193
|
+
return false;
|
|
194
|
+
const nodePrecedence = this.getPrecedence(node.operator);
|
|
195
|
+
const operatorPrecedence = this.getPrecedence(operator);
|
|
196
|
+
if (operator === "^") {
|
|
197
|
+
return nodePrecedence <= operatorPrecedence;
|
|
198
|
+
}
|
|
199
|
+
return nodePrecedence < operatorPrecedence;
|
|
200
|
+
}
|
|
201
|
+
needsParensRight(node, operator) {
|
|
202
|
+
if (!isBinaryOp(node))
|
|
203
|
+
return false;
|
|
204
|
+
const nodePrecedence = this.getPrecedence(node.operator);
|
|
205
|
+
const operatorPrecedence = this.getPrecedence(operator);
|
|
206
|
+
if (operator === "^") {
|
|
207
|
+
return nodePrecedence < operatorPrecedence;
|
|
208
|
+
}
|
|
209
|
+
return nodePrecedence <= operatorPrecedence;
|
|
210
|
+
}
|
|
211
|
+
getPrecedence(operator) {
|
|
212
|
+
switch (operator) {
|
|
213
|
+
case "^":
|
|
214
|
+
return 4;
|
|
215
|
+
case "*":
|
|
216
|
+
case "/":
|
|
217
|
+
case "%":
|
|
218
|
+
return 3;
|
|
219
|
+
case "+":
|
|
220
|
+
case "-":
|
|
221
|
+
return 2;
|
|
222
|
+
default:
|
|
223
|
+
return 0;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function generate(node) {
|
|
228
|
+
const generator = new CodeGenerator;
|
|
229
|
+
return generator.generate(node);
|
|
230
|
+
}
|
|
231
|
+
// src/defaults.ts
|
|
232
|
+
var defaultContext = {
|
|
233
|
+
functions: {
|
|
234
|
+
abs: Math.abs,
|
|
235
|
+
ceil: Math.ceil,
|
|
236
|
+
floor: Math.floor,
|
|
237
|
+
round: Math.round,
|
|
238
|
+
sqrt: Math.sqrt,
|
|
239
|
+
min: Math.min,
|
|
240
|
+
max: Math.max,
|
|
241
|
+
sin: Math.sin,
|
|
242
|
+
cos: Math.cos,
|
|
243
|
+
tan: Math.tan,
|
|
244
|
+
log: Math.log,
|
|
245
|
+
log10: Math.log10,
|
|
246
|
+
exp: Math.exp,
|
|
247
|
+
now: () => new Date,
|
|
248
|
+
date: (...args) => new Date(...args),
|
|
249
|
+
milliseconds: (ms) => ms,
|
|
250
|
+
seconds: (s) => s * 1000,
|
|
251
|
+
minutes: (m) => m * 60 * 1000,
|
|
252
|
+
hours: (h) => h * 60 * 60 * 1000,
|
|
253
|
+
days: (d) => d * 24 * 60 * 60 * 1000
|
|
254
|
+
}
|
|
255
|
+
};
|
|
160
256
|
// src/lexer.ts
|
|
161
257
|
class Lexer {
|
|
162
258
|
source;
|
|
@@ -692,11 +788,13 @@ export {
|
|
|
692
788
|
isFunctionCall,
|
|
693
789
|
isBinaryOp,
|
|
694
790
|
isAssignment,
|
|
791
|
+
generate,
|
|
695
792
|
execute,
|
|
696
793
|
defaultContext,
|
|
697
794
|
exports_ast as ast,
|
|
698
795
|
TokenType,
|
|
699
796
|
Parser,
|
|
700
797
|
Lexer,
|
|
701
|
-
Executor
|
|
798
|
+
Executor,
|
|
799
|
+
CodeGenerator
|
|
702
800
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "littlewing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A minimal, high-performance arithmetic expression language with lexer, parser, and executor. Optimized for browsers with zero dependencies and type-safe execution.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"arithmetic",
|