private tokenize()

in tools/apiview/emitters/typespec-apiview/src/apiview.ts [524:879]


  private tokenize(node: BaseNode) {
    let obj;
    let last = 0;  // track the final index of an array
    let parentNamespace: string;
    switch (node.kind) {
      case SyntaxKind.AliasStatement:
        obj = node as AliasStatementNode;
        this.namespaceStack.push(obj.id.sv);
        this.keyword("alias", {HasSuffixSpace: true});
        this.typeDeclaration(obj.id.sv, this.namespaceStack.value(), true);
        this.tokenizeTemplateParameters(obj.templateParameters);
        this.punctuation("=", {HasSuffixSpace: true, HasPrefixSpace: true});
        this.tokenize(obj.value);
        this.namespaceStack.pop();
        break;
      case SyntaxKind.ArrayExpression:
        obj = node as ArrayExpressionNode;
        this.tokenize(obj.elementType);
        this.punctuation("[]");
        break;
      case SyntaxKind.ArrayLiteral:
        obj = node as ArrayLiteralNode;
        this.punctuation("#[");
        last = obj.values.length - 1;
        obj.values.forEach((val, i) => {
          this.tokenize(val);
          if (i !== last) {
            this.punctuation(",", {HasSuffixSpace: true});
          }
        });
        this.punctuation("]");
        break;
      case SyntaxKind.AugmentDecoratorStatement:
        obj = node as AugmentDecoratorStatementNode;
        const decoratorName = this.getNameForNode(obj.target);
        this.namespaceStack.push(decoratorName);
        this.punctuation("@@");
        this.tokenizeIdentifier(obj.target, "keyword");
        this.lineMarker();
        if (obj.arguments.length) {
          const last = obj.arguments.length - 1;
          this.punctuation("(");
          this.tokenize(obj.targetType);
          if (obj.arguments.length) {
            this.punctuation(",", {HasSuffixSpace: true});
          }
          for (let x = 0; x < obj.arguments.length; x++) {
            const arg = obj.arguments[x];
            this.tokenize(arg);
            if (x !== last) {
              this.punctuation(",", {HasSuffixSpace: true});
            }
          }
          this.punctuation(")");
          this.namespaceStack.pop();
        }
        break;
      case SyntaxKind.BooleanLiteral:
        obj = node as BooleanLiteralNode;
        this.literal(obj.value.toString());
        break;
      case SyntaxKind.BlockComment:
        throw new Error(`Case "BlockComment" not implemented`);
      case SyntaxKind.TypeSpecScript:
        throw new Error(`Case "TypeSpecScript" not implemented`);
      case SyntaxKind.ConstStatement:
        obj = node as ConstStatementNode;
        this.namespaceStack.push(obj.id.sv);
        this.keyword("const", {HasSuffixSpace: true});
        this.tokenizeIdentifier(obj.id, "declaration");
        this.punctuation("=", {HasSuffixSpace: true, HasPrefixSpace: true});
        this.tokenize(obj.value);
        this.namespaceStack.pop();
        break;
      case SyntaxKind.DecoratorExpression:
        obj = node as DecoratorExpressionNode;
        parentNamespace = this.namespaceStack.value();
        this.namespaceStack.push(generateId(obj)!);
        this.punctuation("@");
        this.tokenizeIdentifier(obj.target, "keyword");
        this.lineMarker({relatedLineId: parentNamespace});
        if (obj.arguments.length) {
          last = obj.arguments.length - 1;
          this.punctuation("(");
          for (let x = 0; x < obj.arguments.length; x++) {
            const arg = obj.arguments[x];
            this.tokenize(arg);
            if (x !== last) {
              this.punctuation(",", {HasSuffixSpace: true});
            }
          }
          this.punctuation(")");
        }
        this.namespaceStack.pop();
        break;
      case SyntaxKind.DirectiveExpression:
        obj = node as DirectiveExpressionNode;
        parentNamespace = this.namespaceStack.value();
        this.namespaceStack.push(generateId(node)!);
        this.lineMarker({relatedLineId: parentNamespace});
        this.keyword(`#${obj.target.sv}`, {HasSuffixSpace: true});
        for (const arg of obj.arguments) {
          switch (arg.kind) {
            case SyntaxKind.StringLiteral:
              this.stringLiteral(arg.value, {HasSuffixSpace: true});
              break;
            case SyntaxKind.Identifier:
              this.stringLiteral(arg.sv, {HasSuffixSpace: true});
              break;
          }
        }
        this.newline();
        this.namespaceStack.pop();
        break;
      case SyntaxKind.EmptyStatement:
        throw new Error(`Case "EmptyStatement" not implemented`);
      case SyntaxKind.EnumMember:
        obj = node as EnumMemberNode;
        this.tokenizeDecoratorsAndDirectives(obj.decorators, obj.directives, false);
        this.tokenizeIdentifier(obj.id, "member");
        this.lineMarker({addCrossLanguageId: true});
        if (obj.value) {
          this.punctuation(":", {HasSuffixSpace: true});
          this.tokenize(obj.value);
        }
        break;
      case SyntaxKind.EnumSpreadMember:
        obj = node as EnumSpreadMemberNode;
        this.punctuation("...");
        this.tokenize(obj.target);
        this.lineMarker();
        break;
      case SyntaxKind.EnumStatement:
        this.tokenizeEnumStatement(node as EnumStatementNode);
        break;
      case SyntaxKind.JsNamespaceDeclaration:
        throw new Error(`Case "JsNamespaceDeclaration" not implemented`);
      case SyntaxKind.JsSourceFile:
        throw new Error(`Case "JsSourceFile" not implemented`);
      case SyntaxKind.Identifier:
        obj = node as IdentifierNode;
        const id = this.namespaceStack.value();
        this.typeReference(obj.sv, {NavigateToId: id});
        break;
      case SyntaxKind.ImportStatement:
        throw new Error(`Case "ImportStatement" not implemented`);
      case SyntaxKind.IntersectionExpression:
        obj = node as IntersectionExpressionNode;
        for (let x = 0; x < obj.options.length; x++) {
          const opt = obj.options[x];
          this.tokenize(opt);
          if (x !== obj.options.length - 1) {
            this.punctuation("&", {HasPrefixSpace: true, HasSuffixSpace: true});
          }
        }
        break;
      case SyntaxKind.InterfaceStatement:
        this.tokenizeInterfaceStatement(node as InterfaceStatementNode);
        break;
      case SyntaxKind.InvalidStatement:
        throw new Error(`Case "InvalidStatement" not implemented`);
      case SyntaxKind.LineComment:
        throw new Error(`Case "LineComment" not implemented`);
      case SyntaxKind.MemberExpression:
        this.tokenizeIdentifier(node as MemberExpressionNode, "reference");
        break;
      case SyntaxKind.ModelExpression:
        this.indent();
        this.tokenizeModelExpression(node as ModelExpressionNode, {isOperationSignature: false});
        this.deindent();
        break;
      case SyntaxKind.ModelProperty:
        this.tokenizeModelProperty(node as ModelPropertyNode, false);
        break;
      case SyntaxKind.ModelSpreadProperty:
        obj = node as ModelSpreadPropertyNode;
        this.punctuation("...");
        this.tokenize(obj.target);
        this.lineMarker();
        break;
      case SyntaxKind.ModelStatement:
        obj = node as ModelStatementNode;
        this.tokenizeModelStatement(obj);
        break;
      case SyntaxKind.NamespaceStatement:
        throw new Error(`Case "NamespaceStatement" not implemented`);
      case SyntaxKind.NeverKeyword:
        this.keyword("never");
        break;
      case SyntaxKind.NumericLiteral:
        obj = node as NumericLiteralNode;
        this.literal(obj.value.toString());
        break;
      case SyntaxKind.ObjectLiteral:
        obj = node as ObjectLiteralNode;
        this.punctuation("#{");
        this.indent();
        last = obj.properties.length - 1;
        obj.properties.forEach((prop, i) => {
          this.tokenize(prop);
          if (i !== last) {
            this.punctuation(",", {HasSuffixSpace: false});
          }
          this.newline();
        });
        this.deindent();
        this.punctuation("}");
        break;
      case SyntaxKind.ObjectLiteralProperty:
        obj = node as ObjectLiteralPropertyNode;
        this.tokenizeIdentifier(obj.id, "member");
        this.punctuation(":", {HasSuffixSpace: true});
        this.tokenize(obj.value);
        break;
      case SyntaxKind.ObjectLiteralSpreadProperty:
        obj = node as ObjectLiteralSpreadPropertyNode;
        // TODO: Whenever there is an example?
        throw new Error(`Case "ObjectLiteralSpreadProperty" not implemented`);
      case SyntaxKind.OperationStatement:
        this.tokenizeOperationStatement(node as OperationStatementNode);
        break;
      case SyntaxKind.OperationSignatureDeclaration:
        obj = node as OperationSignatureDeclarationNode;
        this.punctuation("(");
        if (obj.parameters.properties.length) {
          this.indent();
          this.tokenizeModelExpression(obj.parameters, {isOperationSignature: true});
          this.deindent();  
        }
        this.punctuation("):", {HasSuffixSpace: true});
        this.tokenizeReturnType(obj, {isExpanded: true});
        break;
      case SyntaxKind.OperationSignatureReference:
        obj = node as OperationSignatureReferenceNode;
        this.keyword("is", {HasPrefixSpace: true, HasSuffixSpace: true});
        this.tokenize(obj.baseOperation);
        break;
      case SyntaxKind.Return:
        throw new Error(`Case "Return" not implemented`);
      case SyntaxKind.StringLiteral:
        obj = node as StringLiteralNode;
        this.stringLiteral(obj.value);
        break;
      case SyntaxKind.ScalarStatement:
        this.tokenizeScalarStatement(node as ScalarStatementNode);
        break;
      case SyntaxKind.TemplateParameterDeclaration:
        obj = node as TemplateParameterDeclarationNode;
        this.tokenize(obj.id);
        if (obj.constraint) {
          this.keyword("extends", {HasSuffixSpace: true, HasPrefixSpace: true});
          this.tokenize(obj.constraint);
        }
        if (obj.default) {
          this.punctuation("=", {HasSuffixSpace: true, HasPrefixSpace: true});
          this.tokenize(obj.default);
        }
        break;
      case SyntaxKind.TupleExpression:
        obj = node as TupleExpressionNode;
        this.punctuation("[", {HasSuffixSpace: true});
        for (let x = 0; x < obj.values.length; x++) {
          const val = obj.values[x];
          this.tokenize(val);
          if (x !== obj.values.length - 1) {
            this.punctuation(",", {HasSuffixSpace: true});
          }
        }
        this.punctuation("]");
        break;
      case SyntaxKind.TypeReference:
        obj = node as TypeReferenceNode;
        this.tokenizeIdentifier(obj.target, "reference");
        this.tokenizeTemplateInstantiation(obj);
        break;
      case SyntaxKind.UnionExpression:
        obj = node as UnionExpressionNode;
        for (let x = 0; x < obj.options.length; x++) {
          const opt = obj.options[x];
          this.tokenize(opt);
          if (x !== obj.options.length - 1) {
            this.punctuation("|", {HasPrefixSpace: true, HasSuffixSpace: true});
          }
        }
        break;
      case SyntaxKind.UnionStatement:
        this.tokenizeUnionStatement(node as UnionStatementNode);
        break;
      case SyntaxKind.UnionVariant:
        this.tokenizeUnionVariant(node as UnionVariantNode);
        break;
      case SyntaxKind.UnknownKeyword:
        this.keyword("unknown");
        break;
      case SyntaxKind.UsingStatement:
        throw new Error(`Case "UsingStatement" not implemented`);
      case SyntaxKind.ValueOfExpression:
        this.keyword("valueof", {HasSuffixSpace: true});
        this.tokenize((node as ValueOfExpressionNode).target);
        break;
      case SyntaxKind.VoidKeyword:
        this.keyword("void");
        break;
      case SyntaxKind.TemplateArgument:
        
      case SyntaxKind.StringTemplateExpression:
        obj = node as StringTemplateExpressionNode;
        const stringValue = this.buildTemplateString(obj);
        const multiLine = stringValue.includes("\n");
        // single line case
        if (!multiLine) {
          this.stringLiteral(stringValue);
          break;
        }
        // otherwise multiline case
        const lines = stringValue.split("\n");
        this.punctuation(`"""`);
        this.indent();
        for (const line of lines) {
          this.literal(line);
          this.newline();
        }
        this.deindent();
        this.punctuation(`"""`);
        break;
      case SyntaxKind.StringTemplateSpan:
        obj = node as StringTemplateSpanNode;
        this.punctuation("${");
        this.tokenize(obj.expression);
        this.punctuation("}");
        this.tokenize(obj.literal);
        break;
      case SyntaxKind.StringTemplateHead:
      case SyntaxKind.StringTemplateMiddle:
      case SyntaxKind.StringTemplateTail:
        obj = node as StringTemplateHeadNode;
        this.literal(obj.value);
        break;
      case SyntaxKind.CallExpression:
        obj = node as CallExpressionNode;
        this.tokenize(obj.target);
        this.punctuation("(", {HasSuffixSpace: false});
        for (let x = 0; x < obj.arguments.length; x++) {
          const arg = obj.arguments[x];
          this.tokenize(arg);
          if (x !== obj.arguments.length - 1) {
            this.punctuation(",", {HasSuffixSpace: true, snapTo: "}"});
          }
        }
        this.punctuation(")");
        break;
      default:
        // All Projection* cases should fail here...
        throw new Error(`Case "${SyntaxKind[node.kind].toString()}" not implemented`);
    }
  }