/*
* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
* This source file is part of the Cangjie project, licensed under Apache-2.0
* with Runtime Library Exception.
*
* See https://cangjie-lang.cn/pages/LICENSE for license information.
*/
// The Cangjie API is in Beta. For details on its capabilities and limitations, please refer to the README file.
package std.ast
import std.collection.ArrayList
let EXPR_LIST: Array<String> = [
"block",
"wildcard_expr",
"call_expr",
"paren_expr",
"member_access_expr",
"optional_expr",
"primitive_type_expr",
"lit_const_expr",
"interpolation_expr",
"unary_expr",
"assign_expr",
"binary_expr",
"while_expr",
"ref_expr",
"inc_or_dec_expr",
"optional_chain_expr",
"return_expr",
"str_interpolation_expr",
"subscript_expr",
"is_expr",
"as_expr",
"range_expr",
"array_lit_expr",
"array_expr",
"tuple_lit_expr",
"match_expr",
"if_expr",
"let_pattern_destructor",
"token_part",
"quote_expr",
"try_expr",
"jump_expr",
"lambda_expr",
"trailing_closure_expr",
"for_in_expr",
"do_while_expr",
"type_conv_expr",
"throw_expr",
"perform_expr",
"resume_expr",
"spawn_expr",
"synchronized_expr",
"macro_expand_expr"
]
let DECL_LIST: Array<String> = [
"main_decl",
"func_decl",
"macro_decl",
"class_decl",
"interface_decl",
"enum_decl",
"struct_decl",
"type_alias_decl",
"primary_ctor_decl",
"var_decl",
"prop_decl",
"extend_decl",
"func_param",
"var_with_pattern_decl",
"macro_expand_decl"
]
let TYPE_LIST: Array<String> = [
"ref_type",
"qualified_type",
"option_type",
"constant_type",
"varray_type",
"primitive_type",
"paren_type",
"func_type",
"tuple_type",
"this_type"
]
let PATTERN_LIST: Array<String> = [
"var_pattern",
"const_pattern",
"tuple_pattern",
"enum_pattern",
"var_or_enum_pattern",
"type_pattern",
"except_type_pattern",
"effect_type_pattern",
"wildcard_pattern"
]
public func parseProgram(input: Tokens): Program {
let node = unsafe {
try {
parse(input, {
p: CPointer<UInt8> => return CJ_AST_ParseTopLevel(match (MACRO_OBJECT.get()) {
case Some(ptr) => ptr
case None => CPointer<Unit>()
}, p)
})
} catch (e: Exception) {
throw ParseASTException("\n" + e.message + "parsing program error.")
}
}
return match (createProgram(node.GetRootAsFile()) as Program) {
case Some(v) => v
case None => throw ParseASTException("Illegal argument was encountered while obtaining the Program node.")
}
}
func addPositionInfo(node: Node, nodeBase: NodeFormat_NodeBase) {
var begin = nodeBase.GetBegin()
var end = nodeBase.GetEnd()
var astKind: String = nodeBase.GetAstKind()
node.beginPos = Position(begin.fileId, begin.line, begin.column)
node.endPos = Position(end.fileId, end.line, end.column)
node.astKind = astKind
}
func generateNode(n: Option<NodeFormat_Node>): Node {
var node = match (n) {
case Some(v) => v
case None => throw ParseASTException()
}
var nodeBase = match (node.GetBase()) {
case Some(v) => v
case None => throw ParseASTException()
}
var astKind: String = nodeBase.GetAstKind()
if (EXPR_LIST.contains(astKind)) {
return generateExprAST(node.GetRootAsExpr())
}
if (DECL_LIST.contains(astKind)) {
return generateDeclAST(node.GetRootAsDecl())
}
if (PATTERN_LIST.contains(astKind)) {
return generatePatternAST(node.GetRootAsPattern())
}
var astNode = match (astKind) {
case "file" => createProgram(node.GetRootAsFile())
case "features_directive" => createFeatureHeader(node.GetRootAsFeaturesDirective()) ?? throw ParseASTException("Failed to parse the node: ${astKind}")
case "package_spec" => createPackageHeader(node.GetRootAsPackageSpec())
case "import_spec" => createImportList(node.GetRootAsImportSpec())
case _ => throw ParseASTException("Unsupported parse the node: ${astKind}.\n")
}
addPositionInfo(astNode, nodeBase)
return astNode
}
func createAnnotation(anno: Option<NodeFormat_Annotation>): Annotation {
var annotation = match (anno) {
case Some(v) => v
case None => throw ParseASTException()
}
let atKind = if (annotation.GetIsCompileTimeVisible()) {
TokenKind.AT_EXCL
} else {
TokenKind.AT
}
var at = Token(atKind).addPosition(getNodeBeginPos(annotation.GetBase()))
var identifier = Token(TokenKind.IDENTIFIER, annotation.GetIdentifier()).addPosition(annotation.GetIdentPos())
var lSquare = Token(TokenKind.LSQUARE).addPosition(annotation.GetLeftSquarePos())
var rSquare = Token(TokenKind.RSQUARE).addPosition(annotation.GetRightSquarePos())
var args: ArrayList<Argument> = ArrayList<Argument>()
for (arg in annotation.GetArgs()) {
args.add(createArgument(arg))
}
var attrs: Tokens = Tokens()
for (attr in annotation.GetAttrs()) {
attrs.append(Token(TokenKind.IDENTIFIER, attr.getOrThrow()))
}
var cond = match (annotation.GetCondExpr()) {
case Some(v) => Some<Expr>(generateExprAST(annotation.GetCondExpr()))
case None => None<Expr>
}
return Annotation(at, identifier, lSquare, args, rSquare, attrs, cond)
}
func createModifier(modifier: Option<NodeFormat_Modifier>): Modifier {
var mod = match (modifier) {
case Some(v) => v
case None => throw ParseASTException()
}
var kind = Token(getTokenKind(mod.GetKind())).addPosition(getNodeBeginPos(mod.GetBase()))
return Modifier(kind)
}
func createArgument(arg: Option<NodeFormat_FuncArg>): Argument {
var argument = match (arg) {
case Some(v) => v
case None => throw ParseASTException()
}
var nodeBase = match (argument.GetBase()) {
case Some(v) => v
case None => throw ParseASTException()
}
var inout_: Token = Token()
if (argument.GetWithInout()) {
inout_ = Token(TokenKind.INOUT).addPosition(nodeBase.GetBegin())
}
var identifier: Token = Token(TokenKind.IDENTIFIER, argument.GetName()).addPosition(argument.GetNamePos())
var colon: Token = Token()
if (isValidPosition(argument.GetColonPos())) {
colon = Token(TokenKind.COLON).addPosition(argument.GetColonPos())
}
var expr: Expr = generateExprAST(argument.GetExpr())
var comma: Token = Token()
if (isValidPosition(argument.GetCommaPos())) {
comma = Token(TokenKind.COMMA).addPosition(argument.GetCommaPos())
}
var argumentObj = Argument(inout_, identifier, colon, expr, comma)
addPositionInfo(argumentObj, nodeBase)
return argumentObj
}
func generateGenericParamTk(gp: Option<NodeFormat_GenericParamDecl>): (Token, Token) {
var genericParam = match (gp) {
case Some(v) => v
case None => throw ParseASTException()
}
var ident = match (genericParam.GetBase()) {
case Some(v) => Token(TokenKind.IDENTIFIER, v.GetIdentifier()).addPosition(v.GetIdentifierPos())
case None => throw ParseASTException()
}
var comma: Token = Token()
if (isValidPosition(genericParam.GetCommaPos())) {
comma = Token(TokenKind.COMMA).addPosition(genericParam.GetCommaPos())
}
return (ident, comma)
}
func createGenericParam(gic: Option<NodeFormat_Generic>): Option<GenericParam> {
var generic = match (gic) {
case Some(v) => v
case None => return None
}
if (generic.GetTypeParameters().isEmpty()) {
return None
}
var lAngle: Token = Token()
if (isValidPosition(generic.GetLeftAnglePos())) {
lAngle = Token(TokenKind.LT).addPosition(generic.GetLeftAnglePos())
}
var idents: Tokens = Tokens()
var commas: Tokens = Tokens()
for (gp in generic.GetTypeParameters()) {
var (ident, comma) = generateGenericParamTk(gp)
idents.append(ident)
if (isValidToken(comma)) {
commas.append(comma)
}
}
var rAngle: Token = Token()
if (isValidPosition(generic.GetRightAnglePos())) {
rAngle = Token(TokenKind.GT).addPosition(generic.GetRightAnglePos())
}
return Some(GenericParam(lAngle, idents, rAngle, commas))
}
func createGenericConstraint(flag: Bool, cons: Option<NodeFormat_GenericConstraint>): GenericConstraint {
var constraint = match (cons) {
case Some(v) => v
case None => throw ParseASTException()
}
var keyword = Token()
if (flag) {
keyword = Token(TokenKind.WHERE).addPosition(constraint.GetWherePos())
}
var ty: TypeNode = createRefType(constraint.GetType(), NodeFormat_NodeBase(Array<UInt8>(0, repeat: 0), 0))
var upbound: Token = Token(TokenKind.UPPERBOUND).addPosition(constraint.GetOperatorPos())
var upperBounds: ArrayList<TypeNode> = ArrayList<TypeNode>()
for (ups in constraint.GetUpperBound()) {
match (generateTypeAST(ups) as TypeNode) {
case Some(v) => upperBounds.add(v)
case None => throw ParseASTException()
}
}
let bitAnds = Tokens()
for (bitAndPos in constraint.GetBitAndPosVec()) {
bitAnds.append(Token(TokenKind.BITAND).addPosition(bitAndPos))
}
return GenericConstraint(keyword, ty, upbound, upperBounds, bitAnds)
}
func createMacroExpand(nodeInvocation: Option<NodeFormat_MacroInvocation>, isDecl!: Bool = false,
isFuncParam!: Bool = false): Node {
var invocation = match (nodeInvocation) {
case Some(v) => v
case None => throw ParseASTException()
}
var pos = invocation.GetAtPos()
var key = Token(TokenKind.AT).addPosition(pos)
var packageIdentifier: Token = Token(TokenKind.IDENTIFIER, invocation.GetFullName()).addPosition(
invocation.GetIdentifierPos())
var identifier = Token(TokenKind.IDENTIFIER, invocation.GetIdentifier()).addPosition(invocation.GetIdentifierPos())
var lSquare: Token = Token()
var rSquare: Token = Token()
var lParen: Token = Token()
var rParen: Token = Token()
if (isValidPosition(invocation.GetLeftSquarePos())) {
lSquare = Token(TokenKind.LSQUARE).addPosition(invocation.GetLeftSquarePos())
}
if (isValidPosition(invocation.GetRightSquarePos())) {
rSquare = Token(TokenKind.RSQUARE).addPosition(invocation.GetRightSquarePos())
}
if (isValidPosition(invocation.GetLeftParenPos())) {
lParen = Token(TokenKind.LPAREN).addPosition(invocation.GetLeftParenPos())
}
if (isValidPosition(invocation.GetRightParenPos())) {
rParen = Token(TokenKind.RPAREN).addPosition(invocation.GetRightParenPos())
}
var attrs = Tokens()
for (tk in invocation.GetAttrs()) {
match (tk) {
case Some(v) => attrs.append(Token(getTokenKind(v.GetKind()), v.GetValue()).addPosition(v.GetPos()))
case None => ()
}
}
var inputs = Tokens()
for (tk in invocation.GetArgs()) {
match (tk) {
case Some(v) => inputs.append(Token(getTokenKind(v.GetKind()), v.GetValue()).addPosition(v.GetPos()))
case None => ()
}
}
var hasParenthesis = invocation.GetHasParenthesis()
var isCompileTimeVisible = invocation.GetIsCompileTimeVisible()
var macroInputDecl: Option<Decl> = try {
Some(generateDeclAST(invocation.GetDecl()))
} catch (e: ParseASTException) {
None<Decl>
}
if (isDecl) {
let m = MacroExpandDecl(key, packageIdentifier, identifier, lSquare, attrs, rSquare, lParen, inputs, rParen,
macroInputDecl, hasParenthesis, isCompileTimeVisible)
if (isCompileTimeVisible) {
return processCustomAnnotationLikeMacro(macroInputDecl, m)
}
return m
} else if (isFuncParam) {
let m = MacroExpandParam(key, packageIdentifier, identifier, lSquare, attrs, rSquare, lParen, inputs, rParen,
macroInputDecl, hasParenthesis, isCompileTimeVisible)
if (isCompileTimeVisible) {
return processCustomAnnotationLikeMacro(macroInputDecl, m)
}
return m
}
return MacroExpandExpr(key, packageIdentifier, identifier, lSquare, attrs, rSquare, lParen, inputs, rParen,
macroInputDecl, hasParenthesis)
}
func processCustomAnnotationLikeMacro(macroInputDecl: Option<Decl>, macroNode: Decl) {
match (macroInputDecl) {
case Some(v) =>
v.annotations.add(createCustomAnnotation(macroNode), at: 0)
return v
case None => throw ParseASTException("can't create macro extend declaration")
}
}
func getPackageIdentToken(s: String): Token {
let isPkgIdent = s.contains(".") || s.contains("-") || s.contains(" ")
Token(if (isPkgIdent) {
TokenKind.PACKAGE_IDENTIFIER
} else {
TokenKind.IDENTIFIER
}, s)
}
func getPackagePrefixes(prefixPaths: Array<Option<String>>, prefixPoses: Array<NodeFormat_Position>,
prefixDotPoses: Array<NodeFormat_Position>): (Tokens, Tokens) {
if (prefixPaths.size != prefixPoses.size || prefixPaths.size != prefixDotPoses.size) {
throw ParseASTException()
}
var paths = Tokens()
for (i in 0..prefixPaths.size) {
match (prefixPaths[i]) {
case Some(v) => paths.append(
getPackageIdentToken(v).addPosition(prefixPoses[i].fileId, prefixPoses[i].line, prefixPoses[i].column))
case None => throw ParseASTException()
}
}
var dots = Tokens()
for (pos in prefixDotPoses) {
dots.append(Token(TokenKind.DOT, ".", pos.fileId, pos.line, pos.column))
}
(paths, dots)
}
func isSamePos(pos1: NodeFormat_Position, pos2: NodeFormat_Position) {
return pos1.fileId == pos2.fileId && pos1.line == pos2.line && pos1.column == pos2.column
}
func createFeatureHeader(ftrHeader: Option<NodeFormat_FeaturesDirective>): Option<FeaturesDirective> {
return match (ftrHeader) {
case Some(v) =>
let annos = v.GetAnnotations()
let annotations = ArrayList<Annotation>()
for (item in annos) {
annotations.add(createAnnotation(item))
}
let keyword = Token(TokenKind.FEATURES, "features").addPosition(v.GetFeaturesPos())
let featuresSet = createFeaturesSet(v.GetFeaturesSet())
FeaturesDirective(annotations, keyword, featuresSet)
case _ => None
}
}
func createFeaturesSet(ftrSet: Option<NodeFormat_FeaturesSet>): FeaturesSet {
return match (ftrSet) {
case Some(v) =>
let lCurl = Token(TokenKind.LCURL).addPosition(v.GetLCurlPos())
let content = createFeatureContent(v.GetContent())
let commaPoses = v.GetCommaPoses()
let commas = Tokens()
for (pos in commaPoses) {
commas.append(Token(TokenKind.COMMA).addPosition(pos))
}
let rCurl = Token(TokenKind.RCURL).addPosition(v.GetRCurlPos())
FeaturesSet(lCurl, content, commas, rCurl)
case _ => throw ParseASTException()
}
}
func createFeatureContent(ftrContent: Array<Option<NodeFormat_FeatureId>>): ArrayList<FeatureId> {
let ret = ArrayList<FeatureId>()
for (item in ftrContent) {
match (item) {
case Some(v) =>
let idents = v.GetIdentifiers()
let identPoses = v.GetIdentPoses()
let dotPoses = v.GetDotPoses()
let identifiers = Tokens()
let dots = Tokens()
for (idx in 0..idents.size) {
identifiers.append(Token(TokenKind.IDENTIFIER, idents[idx] ?? throw ParseASTException()).addPosition(identPoses[idx]))
if (idx < dotPoses.size) {
let pos = nodePositionToPosition(dotPoses[idx])
dots.append(Token(TokenKind.DOT).addPosition(pos))
}
}
ret.add(FeatureId(identifiers, dots))
case None => throw ParseASTException()
}
}
return ret
}
func nodePositionToPosition(node: NodeFormat_Position): Position {
return Position(node.fileId, node.line, node.column)
}
func createPackageHeader(pkgHeader: Option<NodeFormat_PackageSpec>): PackageHeader {
return match (pkgHeader) {
case Some(v) =>
let kind = match (v.GetAccessible()) {
case NodeFormat_AccessibleKind.AccessibleKind_ACCESSIBLE_PUBLIC => TokenKind.PUBLIC
case NodeFormat_AccessibleKind.AccessibleKind_ACCESSIBLE_PROTECTED => TokenKind.PROTECTED
case _ => TokenKind.INTERNAL
}
let beginPos = getNodeBeginPos(v.GetBase())
let accessible = if (isSamePos(v.GetMacroPos(), beginPos) || isSamePos(v.GetPackagePos(), beginPos)) {
Token()
} else {
Token(kind).addPosition(beginPos)
}
let macroPos = v.GetMacroPos()
let macroKey = if (isValidPosition(macroPos)) {
Token(TokenKind.MACRO).addPosition(macroPos)
} else {
Token()
}
let keyword: Token = Token(TokenKind.PACKAGE).addPosition(v.GetPackagePos())
let (prefixPaths, prefixDots) = getPackagePrefixes(v.GetPrefixPaths(), v.GetPrefixPoses(),
v.GetPrefixDotPoses())
let packageIdentifier: Token = getPackageIdentToken(v.GetPackageName()).addPosition(v.GetPackageNamePos())
// prefixPaths[0] and prefixDots[0] would be orgnization name and position of double colon if double colon exists
if (v.GetHasDoubleColon()) {
PackageHeader(accessible, macroKey, keyword, prefixPaths[0],
Token(TokenKind.DOUBLE_COLON).addPosition(prefixDots[0].pos.fileID, prefixDots[0].pos.line,
prefixDots[0].pos.column), prefixPaths[1..], prefixDots[1..], packageIdentifier)
} else {
PackageHeader(accessible, macroKey, keyword, Token(), Token(), prefixPaths, prefixDots,
packageIdentifier)
}
case None => PackageHeader(true)
}
}
func createImportContent(content: Option<NodeFormat_ImportContent>): ImportContent {
match (content) {
case Some(v) =>
let kind = match (v.GetKind()) {
case ImportKind_IMPORT_SINGLE => ImportKind.Single
case ImportKind_IMPORT_ALIAS => ImportKind.Alias
case ImportKind_IMPORT_ALL => ImportKind.All
case ImportKind_IMPORT_MULTI => ImportKind.Multi
}
let (prefixPaths, prefixDots) = getPackagePrefixes(v.GetPrefixPaths(), v.GetPrefixPoses(),
v.GetPrefixDotPoses())
let identPos = v.GetIdentifierPos()
let identifier = if (let ImportKind.All <- kind) {
Token(TokenKind.MUL, "*", identPos.fileId, identPos.line, identPos.column)
} else {
getPackageIdentToken(v.GetIdentifier()).addPosition(identPos.fileId, identPos.line, identPos.column)
}
var importAlias = Tokens()
if (let ImportKind.Alias <- kind) {
let asPos = v.GetAsPos()
importAlias.append(Token(TokenKind.AS, "as", asPos.fileId, asPos.line, asPos.column))
let asIdentPos = v.GetAsIdentifierPos()
importAlias.append(
Token(TokenKind.IDENTIFIER, v.GetAsIdentifier(), asIdentPos.fileId, asIdentPos.line,
asIdentPos.column))
}
var items = ArrayList<ImportContent>()
var commas = Tokens()
var lCurl = Token()
var rCurl = Token()
if (let ImportKind.Multi <- kind) {
let vItems = v.GetItems()
for (item in vItems) {
items.add(createImportContent(item))
}
for (pos in v.GetCommaPoses()) {
commas.append(Token(TokenKind.COMMA, ",", pos.fileId, pos.line, pos.column))
}
var lCurlPos = v.GetLeftCurlPos()
lCurl = Token(TokenKind.LCURL, "{", lCurlPos.fileId, lCurlPos.line, lCurlPos.column)
var rCurlPos = v.GetRightCurlPos()
rCurl = Token(TokenKind.RCURL, "}", rCurlPos.fileId, rCurlPos.line, rCurlPos.column)
}
// prefixPaths[0] and prefixDots[0] would be orgnization name and position of double colon if double colon exists
if (v.GetHasDoubleColon()) {
ImportContent(kind, prefixPaths[0],
Token(TokenKind.DOUBLE_COLON).addPosition(prefixDots[0].pos.fileID, prefixDots[0].pos.line,
prefixDots[0].pos.column), prefixPaths[1..], prefixDots[1..], identifier, importAlias, lCurl, items,
commas, rCurl)
} else {
ImportContent(kind, Token(), Token(), prefixPaths, prefixDots, identifier, importAlias, lCurl, items,
commas, rCurl)
}
case None => throw ParseASTException()
}
}
func createImportList(imports: Option<NodeFormat_ImportSpec>): ImportList {
var imp = match (imports) {
case Some(v) => v
case None => throw ParseASTException()
}
let kind = match (imp.GetReExport()) {
case ReExportKind_REEXPORT_PUBLIC => TokenKind.PUBLIC
case ReExportKind_REEXPORT_PROTECTED => TokenKind.PROTECTED
case ReExportKind_REEXPORT_INTERNAL => TokenKind.INTERNAL
case _ => TokenKind.PRIVATE
}
let beginPos = getNodeBeginPos(imp.GetBase())
let reExportToken = if (!isSamePos(imp.GetImportPos(), beginPos)) {
Token(kind).addPosition(beginPos)
} else {
Token()
}
var import_: Token = Token(TokenKind.IMPORT).addPosition(imp.GetImportPos())
var content = if (let Some(v) <- imp.GetContent()) {
createImportContent(v)
} else {
throw ParseASTException()
}
ImportList(reExportToken, import_, content)
}
func createProgram(file: Option<NodeFormat_File>): Program {
var f = match (file) {
case Some(v) => v
case None => throw ParseASTException()
}
var ftrDirective: Option<FeaturesDirective> = createFeatureHeader(f.GetFeature())
var pkgHeader: PackageHeader = createPackageHeader(f.GetPackage())
var imports: ArrayList<ImportList> = ArrayList<ImportList>()
for (im in f.GetImports()) {
imports.add(createImportList(im))
}
var decls: ArrayList<Decl> = ArrayList<Decl>()
for (d in f.GetDecls()) {
decls.add(generateDeclAST(d))
}
return Program(pkgHeader, imports, decls, ftrDirective)
}
func createConstructor(decl: Option<NodeFormat_Decl>): Constructor {
var d = match (decl) {
case Some(v) => v
case None => throw ParseASTException()
}
var db = match (d.GetBase()) {
case Some(v) => v
case None => throw ParseASTException()
}
var nb = match (db.GetBase()) {
case Some(v) => v
case None => throw ParseASTException()
}
var astKind: String = nb.GetAstKind()
var annos: ArrayList<Annotation> = ArrayList<Annotation>()
if (astKind == "macro_expand_decl") {
return createConstructorLikeMacroExpand(d)
}
var ident = Token(TokenKind.IDENTIFIER, db.GetIdentifier()).addPosition(db.GetIdentifierPos())
for (anno in db.GetAnnotations()) {
annos.add(createAnnotation(anno))
}
if (astKind == "var_decl") {
return Constructor(annos, ident)
}
var funcBody = match (d.GetDeclAsFuncDecl()) {
case Some(v) => v.GetFuncBody()
case None => throw ParseASTException()
}
var paramList = match (funcBody) {
case Some(v) => v.GetParamList().getOrThrow()
case None => throw ParseASTException()
}
var lParen = Token(TokenKind.LPAREN).addPosition(paramList.GetLeftParenPos())
var params_: ArrayList<TypeNode> = ArrayList<TypeNode>()
let commas = Tokens()
var params = paramList.GetParams()
for (i in 0..params.size) {
let param = params[i].getOrThrow()
var ty = match (param.GetBase()) {
case Some(v) => generateTypeAST(v.GetType())
case None => throw ParseASTException("Cannot get param from enum decl constructor")
}
params_.add(ty)
let commaPos = param.GetCommaPos()
if (isValidPosition(commaPos)) {
commas.append(Token(TokenKind.COMMA).addPosition(commaPos))
}
}
var rParen = Token(TokenKind.RPAREN).addPosition(paramList.GetRightParenPos())
return Constructor(annos, ident, lParen, params_, commas, rParen)
}
func createArgumentTokens(m: Decl): Tokens {
var argTokens = Tokens()
argTokens.append(m.identifier)
let isCompileTimeVisible = match (m as MacroExpandDecl) {
case Some(v) =>
argTokens.append(v.lSquare)
argTokens.append(v.macroAttrs)
argTokens.append(v.rSquare)
v.isCompileTimeVisible_
case None => match (m as MacroExpandParam) {
case Some(v) =>
argTokens.append(v.lSquare)
argTokens.append(v.macroAttrs)
argTokens.append(v.rSquare)
v.isCompileTimeVisible_
case None => throw ParseASTException()
}
}
let at = if (isCompileTimeVisible) {
Token(TokenKind.AT_EXCL).addPosition(m.keyword.pos)
} else {
Token(TokenKind.AT).addPosition(m.keyword.pos)
}
return at + argTokens
}
func createCustomAnnotation(m: Decl): Annotation {
let argTokens = createArgumentTokens(m)
let node = unsafe {
try {
parse(argTokens, {p: CPointer<UInt8> => return CJ_AST_ParseAnnotationArguments(p)})
} catch (e: Exception) {
throw ParseASTException("\n" + e.message + "parsing custom annotation error.")
}
}
createAnnotation(node.GetRootAsAnnotation())
}
func createConstructorLikeMacroExpand(decl: NodeFormat_Decl): Constructor {
var md = createMacroExpandDecl(decl)
if (md is MacroExpandDecl) {
// annotation(@Anno) and MacroExpandDecl(@Macro) are syntactically similar, need convert to Constructor.
return createConstructorWithAt(md)
} else {
return createConstructorWithAtExcl(md)
}
}
func createConstructorWithAt(decl: Decl): Constructor {
var annos: ArrayList<Annotation> = ArrayList<Annotation>()
var ident = decl.identifier
var lParen = Token(TokenKind.LPAREN)
let commas = Tokens()
var params = ArrayList<TypeNode>()
var rParen = Token(TokenKind.RPAREN)
var macroNode = match (decl as MacroExpandDecl) {
case Some(v) =>
annos.add(all: v.annotations)
annos.add(createCustomAnnotation(v))
v
case None => throw ParseASTException()
}
while (true) {
if (!(macroNode.macroInputDecl is MacroExpandDecl)) {
ident = macroNode.macroInputDecl.identifier
break
}
macroNode = match (macroNode.macroInputDecl as MacroExpandDecl) {
case Some(v) =>
annos.add(createCustomAnnotation(v))
v
case None => throw ParseASTException()
}
}
let enumConstructor = match (macroNode.macroInputDecl as FuncDecl) {
case Some(v) =>
ident = v.identifier
lParen = v.lParen
for (p in v.funcParams) {
params.add(p.paramType)
}
lParen = v.lParen
annos.add(all: v.annotations)
Constructor(annos, ident, lParen, params, commas, rParen)
case None => match (macroNode.macroInputDecl as VarDecl) {
case Some(v) =>
annos.add(all: v.annotations)
Constructor(annos, v.identifier)
case None => throw ParseASTException()
}
}
return enumConstructor
}
func createConstructorWithAtExcl(decl: Decl): Constructor {
match (decl as FuncDecl) {
case Some(v) =>
var params: ArrayList<TypeNode> = ArrayList<TypeNode>()
for (p in v.funcParams) {
params.add(p.paramType)
}
return Constructor(v.annotations, v.identifier, v.lParen, params, Tokens(), v.rParen)
case None => match (decl as VarDecl) {
case Some(v) => return Constructor(v.annotations, v.identifier)
case None => throw ParseASTException()
}
}
}