/*
* 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
import std.collection.HashSet
/**
* @brief Parses a source file located at the specified path and returns the parsing result.
*
* @param filePath The file path to the source file that needs to be parsed.
* @return A ParsingResult object containing the parsed SourceFile or an error.
*
* @throws Exception if there is an issue reading the file.
*/
public func parseFile(filePath: String): ParsingResult<SourceFile> {
let (file, diags) = Parser.parseFile(filePath)
ParsingResult<SourceFile>(file, diags)
}
/**
* @brief Parses a package located at the specified path and returns the parsing result.
*
* @param dirPath The directory path to the source files that needs to be parsed.
* @return A ParsingResult object containing the parsed Package or an error.
*
* @throws Exception if there is an issue reading the file.
*/
public func parsePackage(dirPath: String): ParsingResult<Package> {
let (pkg, diags) = Parser.parsePackage(dirPath)
ParsingResult<Package>(pkg, diags)
}
func skipRedundancy(text: String) {
return text.replace("\r\n", "").replace("\n", "").replace("\t", "").replace("\f", "").replace(" ", "").replace(";", "")
}
/**
* @brief Parse the input text and return the parsing result along with diagnostic information.
*
* @param text The text that needs to be parsed.
* @return A ParsingResult object containing the parsed node or an error.
*
* @throws Exception if there is an issue parsing the text.
*/
public func parseText(programText: String): ParsingResult<SyntaxTreeNode> {
let (res, diags) = Parser.parseText(programText)
// If the content of the parsed node is less than that of the input content,
// there are multiple input nodes.
if (let Some(result) <- res && skipRedundancy(result.toString()).size < skipRedundancy(programText).size) {
throw Exception("parseText function not support parse more than one node.")
}
ParsingResult<SyntaxTreeNode>(res, diags)
}
func checkPos(tokens: Tokens): Bool {
var cur = Offset()
var set = HashSet<UInt32>()
var curFileId: UInt32 = tokens[0].pos.fileID
for (token in tokens) {
let pos = token.pos
// For tokens from the same file, they must appear consecutively;
// otherwise, their positions are considered invalid.
if (pos.fileID != curFileId && set.contains(pos.fileID)) {
return false
}
curFileId = pos.fileID
set.add(curFileId)
// Under the premise of considering the value of each Token, the
// positions of Tokens must be incremental; that is, the start
// position of a subsequent Token must be greater than the end position of the preceding Token.
if (cur.line > pos.line || (cur.line == pos.line && cur.column > pos.column)) {
return false
}
cur = Offset(pos.line, pos.column)
cur.moveString(token.value)
}
return true
}
/**
* @brief Parse the input tokens and return the parsing result along with diagnostic information.
*
* @param tokens The tokens that need to be parsed.
* @return A ParsingResult object containing the parsed node or an error.
*
* @throws Exception if there is an issue parsing the tokens.
*/
public func parseTokens(tokens: Tokens, refreshPos!: Bool = true): ParsingResult<SyntaxTreeNode> {
if (tokens.size == 0) {
throw Exception("the input tokens is empty.")
}
if (!refreshPos && !checkPos(tokens)) { // check if the position of tokens is valid
throw Exception("the position of the input tokens is invalid, you may open the refreshPos option.")
}
let (res, diags) = Parser.parseTokens(tokens, refreshPos)
ParsingResult<SyntaxTreeNode>(res, diags)
}