/*
* 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
const NUM_SPACES: UInt16 = 2
func isValidPosition(pos: NodeFormat_Position): Bool {
if (pos.line == 0 && pos.column == 0) {
return false
}
return true
}
func isValidPosition(tk: Token): Bool {
if (tk.pos.line == 0 && tk.pos.column == 0) {
return false
}
return true
}
func isValidToken(tk: Token): Bool {
return tk.kind != TokenKind.ILLEGAL
}
func getNodeBeginPos(nodeBase: Option<NodeFormat_NodeBase>): NodeFormat_Position {
return match (nodeBase) {
case Some(v) => v.GetBegin()
case None => throw ParseASTException()
}
}
func getOperaterKindOrIdentTokens(opKind: Int32, pos: NodeFormat_Position, ident: Tokens): Tokens {
let identifierTokens: Tokens = match (getTokenKind(UInt16(opKind))) {
case NOT => Tokens().append(Token(TokenKind.NOT).addPosition(pos))
case ADD => Tokens().append(Token(TokenKind.ADD).addPosition(pos))
case SUB => Tokens().append(Token(TokenKind.SUB).addPosition(pos))
case EXP => Tokens().append(Token(TokenKind.EXP).addPosition(pos))
case MUL => Tokens().append(Token(TokenKind.MUL).addPosition(pos))
case DIV => Tokens().append(Token(TokenKind.DIV).addPosition(pos))
case MOD => Tokens().append(Token(TokenKind.MOD).addPosition(pos))
case LT => Tokens().append(Token(TokenKind.LT).addPosition(pos))
case LE => Tokens().append(Token(TokenKind.LE).addPosition(pos))
case GT => Tokens().append(Token(TokenKind.GT).addPosition(pos))
case BITAND => Tokens().append(Token(TokenKind.BITAND).addPosition(pos))
case BITOR => Tokens().append(Token(TokenKind.BITOR).addPosition(pos))
case BITXOR => Tokens().append(Token(TokenKind.BITXOR).addPosition(pos))
case LSHIFT => Tokens().append(Token(TokenKind.LSHIFT).addPosition(pos))
case EQUAL => Tokens().append(Token(TokenKind.EQUAL).addPosition(pos))
case NOTEQ => Tokens().append(Token(TokenKind.NOTEQ).addPosition(pos))
case RSHIFT => Tokens().append(Token(TokenKind.RSHIFT).addPosition(pos))
case GE => Tokens().append(Token(TokenKind.GE).addPosition(pos))
case LSQUARE => Token(TokenKind.LSQUARE).addPosition(pos) + Token(TokenKind.RSQUARE).addPosition(pos.fileId,
pos.line, pos.column + 1)
case LPAREN => Token(TokenKind.LPAREN).addPosition(pos) + Token(TokenKind.RPAREN).addPosition(pos.fileId,
pos.line, pos.column + 1)
case _ => ident
}
return identifierTokens
}
func getIndent(indent: UInt16): String {
return String(Array<Rune>(Int64(indent * NUM_SPACES), repeat: r' '))
}
func getTokenIndent(name: String, tk: Token, indent: UInt16, noNameIndent!: Bool = false, index!: Option<Int64> = None): String {
var ret: String = ""
if (!isValidToken(tk) || (tk.value.isEmpty() && tk.pos.fileID != 1)) {
return ret
}
var nameIndent = indent
if (noNameIndent) {
nameIndent = 0
}
match (index) {
case Some(v) => ret += getIndent(nameIndent) + "${name}: ${v}, Token {\n"
case None => ret += getIndent(nameIndent) + "${name}: Token {\n"
}
var value = tk.value
if (value == "\n") {
value = "\\n"
}
ret += getIndent(indent + 1) + "value: \"${value}\"\n"
ret += getIndent(indent + 1) + "kind: ${tk.kind.toString()}\n"
if (!isValidPosition(tk) || tk.pos.fileID != 1) {
ret += getIndent(indent) + "}\n"
return ret
}
ret += getIndent(indent + 1) + "pos: ${tk.pos.line}: ${tk.pos.column}\n"
ret += getIndent(indent) + "}\n"
return ret
}
func dumpNodes<N>(name: String, contents: ArrayList<N>, indent: UInt16): String where N <: Node {
var ret = ""
for (i in 0..contents.size) {
ret += getIndent(indent) + "${name}: ${i}, "
ret += contents[i].dump(indent)
}
return ret
}
/**
* Return true if two tokens are considered equal somehow(exclude NL, END and position info).
*/
public func compareTokens(tokens1: Tokens, tokens2: Tokens): Bool {
/** Delete newline token first. */
func rmNewlineEnd(input: Tokens): Tokens {
var tokenArr: Tokens = Tokens()
var iterator = input.iterator()
while (true) {
match (iterator.next()) {
case None => break
case Some(obj) where obj.kind != TokenKind.NL && obj.kind != TokenKind.END => tokenArr.append(obj);
case _ => ()
}
}
return tokenArr
}
var tokens1_: Tokens = rmNewlineEnd(tokens1)
var tokens2_: Tokens = rmNewlineEnd(tokens2)
/* Compare tokens. */
var size1: Int64 = tokens1_.size
var size2: Int64 = tokens2_.size
if (size1 != size2) {
return false
}
for (i in 0..size1) {
var token1 = tokens1_[i]
var token2 = tokens2_[i]
/* Skip the check for position. */
if (token1.kind != token2.kind || token1.value != token2.value) {
return false
}
}
return true
}
func checkKindValue(k: TokenKind, v: String): Bool {
let kv: String = getTokenLiteral(k)
if (kv == "" || k == TokenKind.UNIT_LITERAL) {
return true
}
if (k == TokenKind.NL && (v == "\n" || v == "\r\n")) {
return true
}
return kv == v
}
func getImportSpecTokens(imports: ArrayList<ImportList>): Tokens {
var r: Tokens = Tokens()
for (i in 0..imports.size) {
r.append(imports[i].toTokens())
}
return r
}
func dumpCommonContent(currentIndent: UInt16, upperBound: Token, superTypes: ArrayList<TypeNode>,
constraint: ArrayList<GenericConstraint>, body: Body) {
var ret: String = ""
if (isValidToken(upperBound)) {
ret += getTokenIndent("-upperBound", upperBound, currentIndent)
}
for (i in 0..superTypes.size) {
ret += getIndent(currentIndent) + "-superTypes: ${i}, "
ret += superTypes[i].dump(currentIndent)
}
for (i in 0..constraint.size) {
ret += getIndent(currentIndent) + "-genericConstraint: ${i}, "
ret += constraint[i].dump(currentIndent)
}
ret += getIndent(currentIndent) + "-body: "
ret += body.dump(currentIndent)
return ret
}
struct RefTypeUpperBoundInfo {
RefTypeUpperBoundInfo(let upperBound: Token, let superTypes: ArrayList<TypeNode>, let superTypeBitAnds: Tokens) {}
}
func referenceType2Tokens(ret: Tokens, upperBoundInfo: RefTypeUpperBoundInfo, constraint: ArrayList<GenericConstraint>,
constraintCommas: Tokens, body: Body) {
let upperBound = upperBoundInfo.upperBound
let superTypes = upperBoundInfo.superTypes
let superTypeBitAnds = upperBoundInfo.superTypeBitAnds
if (!superTypes.isEmpty()) {
if (upperBound.kind != ILLEGAL) {
ret.append(upperBound)
} else {
ret.append(Token(TokenKind.UPPERBOUND))
}
for (i in 0..superTypes.size - 1) {
let bitAnd = superTypeBitAnds.tryGet(i) ?? Token(TokenKind.BITAND)
ret.append(superTypes[i].toTokens()).append(bitAnd)
}
ret.append(superTypes[superTypes.size - 1].toTokens())
}
if (!constraint.isEmpty()) {
var index = constraint.size - 1
for (i in 0..index) {
let comma = constraintCommas.tryGet(i) ?? Token(TokenKind.COMMA)
ret.append(constraint[i].toTokens()).append(comma)
}
ret.append(constraint[index].toTokens())
}
ret.append(body.toTokens())
}
extend Tokens {
func tryGet(index: Int64): ?Token {
if (index >= size) {
Option<Token>.None
} else {
Some(this[index])
}
}
}
extend Token {
func tryGet(): ?Token {
if (kind == TokenKind.ILLEGAL) {
Option<Token>.None
} else {
Some(this)
}
}
}
func checkTokenType(tk: Token, kind: TokenKind) {
if (tk.kind != kind) {
throw ASTException("Illegal TokenKind, TokenKind should be ${kind}")
}
}
func checkTokensType(tks: Tokens, kind: TokenKind) {
for (tk in tks) {
checkTokenType(tk, kind)
}
}
func checkTokenTypeAssign(tk: Token) {
let arr = [ASSIGN, ADD_ASSIGN, SUB_ASSIGN, MUL_ASSIGN, EXP_ASSIGN, DIV_ASSIGN, MOD_ASSIGN, AND_ASSIGN, OR_ASSIGN,
BITAND_ASSIGN, BITOR_ASSIGN, BITXOR_ASSIGN, LSHIFT_ASSIGN, RSHIFT_ASSIGN]
for (t in arr) {
if (tk.kind == t) {
return
}
}
throw ASTException("Illegal TokenKind, TokenKind should be assign")
}
func checkPrimitiveType(tk: Token) {
let arr = [INT8, INT16, INT32, INT64, INTNATIVE, UINT8, UINT16, UINT32, UINT64, UINTNATIVE, FLOAT16, FLOAT32,
FLOAT64, RUNE, BOOLEAN, NOTHING, UNIT]
for (t in arr) {
if (tk.kind == t) {
return
}
}
throw ASTException("Illegal TokenKind, TokenKind should be a primitive type.")
}
func checkValid(input: Token) {
if (isValidPosition(input)) {
return input
} else {
return Token()
}
}