TopicsBabel.jsParserExercise Complex as Bigint

Exercise: Implement Complex Numbers in Babel

Tokenizer

See the treatment of BigInt in the tokenizer file index.js. Instead of n we use “i” and introduce the token “tt.imaginary”

if (next === charCodes.lowercaseN) { 
        // disallow floats, legacy octal syntax and non octal decimals
        // new style octal ("0o") is handled in this.readRadixNumber
        if (isFloat || octal || isNonOctalDecimalInt) {
          this.raise(start, "Invalid BigIntLiteral");
        }
        ++this.state.pos;
        isBigInt = true;
      }
 
      if (isIdentifierStart(this.input.codePointAt(this.state.pos))) {
        throw this.raise(this.state.pos, Errors.NumberIdentifier);
      }
 
      // remove "_" for numeric literal separator, and "n" for BigInts
      const str = this.input.slice(start, this.state.pos).replace(/[_n]/g, "");
 
      if (isBigInt) {
        this.finishToken(tt.bigint, str); // create a BigInt token 
        return;
      }

Then we need to add the token to the tokenTypes in the file tokenTypes.js

In the parser we have to allow binary operations with “imaginary” numbers.

File babel-tanhauhau/packages/babel-parser/src/utils.js: Function isLiteralPropertyName

  /**
   * Test if current token is a literal property name
   * https://tc39.es/ecma262/#prod-LiteralPropertyName
   * LiteralPropertyName:
   *   IdentifierName
   *   StringLiteral
   *   NumericLiteral
   *   BigIntLiteral
   */
  isLiteralPropertyName(): boolean {
    return (
      this.match(tt.name) ||
      !!this.state.type.keyword ||
      this.match(tt.string) ||
      this.match(tt.num) ||
      this.match(tt.bigint)
    );
  }
}

File: babel-tanhauhau/packages/babel-parser/src/types.js

It appears in the type declaration Literal in lines 94-100:

// Literals
 
export type Literal =
  | RegExpLiteral
  | NullLiteral
  | StringLiteral
  | BooleanLiteral
  | NumericLiteral
  | BigIntLiteral;

later appears in line 127-130:

export type BigIntLiteral = NodeBase & {
  type: "BigIntLiteral",
  value: number,
};

and in lines 1201-1213:

export type TsKeywordTypeType =
  | "TSAnyKeyword"
  | "TSUnknownKeyword"
  | "TSNumberKeyword"
  | "TSObjectKeyword"
  | "TSBooleanKeyword"
  | "TSBigIntKeyword"
  | "TSStringKeyword"
  | "TSSymbolKeyword"
  | "TSVoidKeyword"
  | "TSUndefinedKeyword"
  | "TSNullKeyword"
  | "TSNeverKeyword";

File: babel-tanhauhau/packages/babel-parser/src/parser/expression.js

parseExprAtom(refExpressionErrors?: ?ExpressionErrors): N.Expression {
    // If a division operator appears in an expression position, the
    // tokenizer got confused, and we force it to read a regexp instead.
    if (this.state.type === tt.slash) this.readRegexp();
 
    const canBeArrow = this.state.potentialArrowAt === this.state.start;
    let node;
 
    switch (this.state.type) {
      case tt._super:
      case tt._import:
      case tt._this:
      case tt.name: 
      case tt._do: 
      case tt.regexp: 
      case tt.num:
        return this.parseLiteral(this.state.value, "NumericLiteral");
      case tt.bigint:
        return this.parseLiteral(this.state.value, "BigIntLiteral");
      case tt.string:
        return this.parseLiteral(this.state.value, "StringLiteral");
      case tt._null:
      case tt._true:
      case tt._false:
      case tt.parenL:
      case tt.bracketBarL:
      case tt.bracketHashL: 
      case tt.bracketL: 
      case tt.braceBarL:
      case tt.braceHashL: 
      case tt.braceL: 
      case tt._function:
      case tt.at:
      case tt._class:
      case tt._new:
 
      case tt.backQuote:
 
      case tt.doubleColon: 
 
      // fall through
      default:
        throw this.unexpected();
    }
  }