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 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.2.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",