mirror of
https://github.com/oxc-project/oxc.git
synced 2026-09-14 19:36:11 +08:00
a4478e960a
Add `oxc-codegen` package - a printer written in TS. ### Features - Prints JS, JSX, TS, TSX. - Matches `oxc_codegen` exactly on all Test262, TypeScript, Acorn-JSX fixtures. - Very fast. ### Deficiencies - Pretty-printing only (no compact/minified output). - No support for printing comments. - Source map support is semi-there, but largely untested, and poor API (design doc has some ideas on how to improve it). ### This PR adds - The printer code. - Conformance testing infra. - Further tests for edge cases. - README. - Design doc - how it differs from `oxc_codegen` and why, what produces the perf. ### This PR doesn't add - Not integrated into CI or release pipeline. ### Notes This started as a slop port of `oxc_codegen`. I've iterated on that base, cleaned it up a lot, and at least skimmed through all of it. I think it's in fairly decent shape. The design doc is I think worth a read. It explains the rationale for the choices, in particular the "1 codebase, 4 builds" approach. I've put it in a new top-level directory called `packages`. It's not an app, so didn't seem to me to fit alongside `apps/oxlint` and `apps/oxfmt`. It's pure JS, so not like e.g. `napi/parser`. And it felt too heavyweight to put into `npm` dir. But maybe there's a better place for it. Co-authored-by: Cameron <cameron.clark@hey.com>
153 lines
3.4 KiB
TypeScript
153 lines
3.4 KiB
TypeScript
// Operator tables.
|
|
//
|
|
// The context flags a printer is given about the position it is printing into, the precedence of
|
|
// each binary and assignment operator, those operators pre-padded with the spaces pretty mode
|
|
// always puts around them, and the category a unary or update operator leaves in `state.last`.
|
|
|
|
import {
|
|
CAT_OP_UN_NEG,
|
|
CAT_OP_UN_NOT,
|
|
CAT_OP_UN_PLUS,
|
|
CAT_OP_UPD_DEC,
|
|
CAT_OP_UPD_INC,
|
|
CAT_OTHER,
|
|
} from "./write.ts";
|
|
import { debugAssert } from "../asserts.ts";
|
|
|
|
import type { Category } from "./write.ts";
|
|
|
|
// Context flags (`oxc_codegen::Context`).
|
|
|
|
/** No context. */
|
|
export const CTX_NONE = 0;
|
|
|
|
/** Set in a `for` statement's head, where an unparenthesized `in` would end the head. */
|
|
export const CTX_FORBID_IN = 1;
|
|
|
|
/** Set under a `new`, where an unparenthesized call would be taken as `new`'s own argument list. */
|
|
export const CTX_FORBID_CALL = 2;
|
|
|
|
/** Set inside a TS type, where a numeric literal prints its raw text rather than the shortest form. */
|
|
export const CTX_TYPESCRIPT = 4;
|
|
|
|
/**
|
|
* Binary/logical operator precedences on the Oxc scale.
|
|
*/
|
|
export const BIN_PRECEDENCE = {
|
|
__proto__: null,
|
|
"**": 17,
|
|
"*": 16,
|
|
"/": 16,
|
|
"%": 16,
|
|
"+": 15,
|
|
"-": 15,
|
|
"<<": 14,
|
|
">>": 14,
|
|
">>>": 14,
|
|
"<": 13,
|
|
">": 13,
|
|
"<=": 13,
|
|
">=": 13,
|
|
instanceof: 13,
|
|
in: 13,
|
|
"==": 12,
|
|
"!=": 12,
|
|
"===": 12,
|
|
"!==": 12,
|
|
"&": 11,
|
|
"^": 10,
|
|
"|": 9,
|
|
"&&": 8,
|
|
"||": 7,
|
|
"??": 6,
|
|
};
|
|
|
|
/**
|
|
* Space-padded forms of every binary/logical operator.
|
|
*
|
|
* In pretty-print mode an operator is always surrounded by single spaces,
|
|
* so the whole token is one constant string and needs a single `write` call.
|
|
*/
|
|
export const PADDED_BIN_OPERATORS = {
|
|
__proto__: null,
|
|
"**": " ** ",
|
|
"*": " * ",
|
|
"/": " / ",
|
|
"%": " % ",
|
|
"+": " + ",
|
|
"-": " - ",
|
|
"<<": " << ",
|
|
">>": " >> ",
|
|
">>>": " >>> ",
|
|
"<": " < ",
|
|
">": " > ",
|
|
"<=": " <= ",
|
|
">=": " >= ",
|
|
instanceof: " instanceof ",
|
|
in: " in ",
|
|
"==": " == ",
|
|
"!=": " != ",
|
|
"===": " === ",
|
|
"!==": " !== ",
|
|
"&": " & ",
|
|
"^": " ^ ",
|
|
"|": " | ",
|
|
"&&": " && ",
|
|
"||": " || ",
|
|
"??": " ?? ",
|
|
};
|
|
|
|
/**
|
|
* Space-padded forms of every assignment operator.
|
|
*
|
|
* In pretty-print mode an operator is always surrounded by single spaces,
|
|
* so the whole token is one constant string and needs a single `write` call.
|
|
*/
|
|
export const PADDED_ASSIGN_OPERATORS = {
|
|
__proto__: null,
|
|
"=": " = ",
|
|
"+=": " += ",
|
|
"-=": " -= ",
|
|
"*=": " *= ",
|
|
"/=": " /= ",
|
|
"%=": " %= ",
|
|
"**=": " **= ",
|
|
"<<=": " <<= ",
|
|
">>=": " >>= ",
|
|
">>>=": " >>>= ",
|
|
"&=": " &= ",
|
|
"^=": " ^= ",
|
|
"|=": " |= ",
|
|
"&&=": " &&= ",
|
|
"||=": " ||= ",
|
|
"??=": " ??= ",
|
|
};
|
|
|
|
/**
|
|
* The category a unary operator leaves in `last`, which is what `printSpaceBeforeOperator` reads
|
|
* to decide whether the next operator needs separating.
|
|
*
|
|
* `typeof`, `void` and `delete` never reach here - they end in an identifier character and are handled as words.
|
|
*/
|
|
export function unaryOperatorCode(operator: string): Category {
|
|
debugAssert(!["typeof", "void", "delete"].includes(operator));
|
|
|
|
switch (operator) {
|
|
case "+":
|
|
return CAT_OP_UN_PLUS;
|
|
case "-":
|
|
return CAT_OP_UN_NEG;
|
|
case "!":
|
|
return CAT_OP_UN_NOT;
|
|
default:
|
|
return CAT_OTHER;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The category `++` or `--` leaves in `last`, as `unaryOperatorCode` does for unary operators.
|
|
*/
|
|
export function updateOperatorCode(operator: string): Category {
|
|
return operator === "++" ? CAT_OP_UPD_INC : CAT_OP_UPD_DEC;
|
|
}
|