/*
* 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.
*/
package stdx.syntax
public import std.ast.{Tokens, Token, TokenKind}
import std.collection.ArrayList
/*
* The api used comes from lsp.so, which is a dynamic package.
*/
foreign func CJ_ParseAnnotationArguments(tokensBytes: CPointer<UInt8>): CPointer<ParseRes>
foreign func CJ_CheckAddSpaces(tokBytes: CPointer<UInt8>, spaceFlag: CPointer<Bool>): Unit
foreign func CJ_CheckIdentifier(str: CString, useContextualKeyWords: Bool): Bool
// ======================== PART ONE: Parse/Lex API from CJC static library ========================
/**
* NOTE: four bytes in the front of buffer represents the size of the buffer.
*/
let BUFFER_HEADER_SIZE: Int64 = 4
/**
* Returns Int32 value from pointer.
*/
func getInt32FromUnsafePointer(ptr: CPointer<UInt8>): Int32 {
if (ptr.isNull()) {
throw IllegalArgumentException("the input pointer is null.\n")
}
var buf = Array<UInt8>(BUFFER_HEADER_SIZE, {x: Int64 => unsafe { ptr.read(x) }})
return getInt32(buf[0..4])
}
@C
struct ParseRes {
let node: CPointer<UInt8> = CPointer<UInt8>()
let errMsg: CPointer<UInt8> = CPointer<UInt8>()
}
unsafe func parse(input: Tokens, nativeParse: (CPointer<UInt8>) -> CPointer<ParseRes>): NodeFormat_Node {
let tokensBytes: Array<UInt8> = input.toBytes()
let pTokensBytes: CPointer<UInt8> = unsafePointerCastFromUint8Array(tokensBytes)
if (pTokensBytes.isNull()) {
throw Exception("empty pointer found.")
}
let parseResultRaw: CPointer<ParseRes> = nativeParse(pTokensBytes)
if (parseResultRaw.isNull()) {
LibC.free(pTokensBytes)
throw Exception("Unable to get parseResult.")
}
/*
* If not use isNull(), there will be segfault for some cases for cjnative backend.
* isNull() API only exists for cjnative.
*/
try {
let pNode = parseResultRaw.read().node
let node = genNodeFormatNode(pNode, pTokensBytes)
return node
} finally {
LibC.free(parseResultRaw)
}
}
func genNodeFormatNode(pNode: CPointer<UInt8>, pTokensBytes: CPointer<UInt8>) {
if (pNode.isNull()) {
unsafe { LibC.free(pTokensBytes) }
throw IllegalArgumentException("Negative or zero length.\n")
}
let bufferSize: Int64 = Int64(getInt32FromUnsafePointer(pNode))
if (bufferSize <= 0) {
unsafe {
LibC.free(pNode)
LibC.free(pTokensBytes)
}
throw IllegalArgumentException("Negative or zero length.\n")
}
let flatBuf = Array<UInt8>(bufferSize, {x: Int64 => unsafe { pNode.read(x) }})
unsafe {
LibC.free(pNode)
LibC.free(pTokensBytes)
}
return NodeFormat_Node(flatBuf[BUFFER_HEADER_SIZE..], 0)
}
// ======================== PART TWO: CPointer<UInt8>/Array casting APIs ========================
// The call of UnsafePointerCastFromUint8Array will be generated by compiler when we desugar a MacroDecl.
/**
* @throws IllegalMemoryException while system error.
*/
func unsafePointerCastFromUint8Array(arr: Array<UInt8>): CPointer<UInt8> {
let arrSize: Int64 = arr.size
if (arrSize <= 0) {
return CPointer<UInt8>()
}
var ptr: CPointer<UInt8> = unsafe { LibC.malloc<UInt8>(count: arrSize) }
if (ptr.isNull()) {
throw IllegalMemoryException("unsafePointerCastFromUint8Array malloc failed!")
}
for (i in 0..arrSize) {
unsafe { ptr.write(i, arr[i]) }
}
return ptr
}