仓颉编程语言:编译、报错与调试

【华为仓颉编程语言】运行第一个仓颉程序 上节课我们成功安装了仓颉工具链,今天让我们一起编写并运行第一个仓颉程序。相信很多同学都还记得学习第一门编程语言时写的"Hello World"程序,那种看到程序成功运行的激动心情。今天,我们就要在仓颉语言中重现这个经典时刻! 阅读详情

宏的编译和使用

当前编译器约束宏的定义与宏的调用不允许在同一包里。宏包必须首先被编译,然后再编译宏调用的包。在宏调用的包中,不允许出现宏的定义。由于宏需在包中导出给另一个包使用,因此编译器约束宏定义必须使用 public 修饰。

下面介绍一个简单的例子。

源码目录结构如下:

// Directory layout.
src
`-- macros
      |-- m.cj

`-- demo.cj

宏定义放在 _macros_子目录下:

// macros/m.cj
// In this file, we define the macro Inner, Outer.
macro package define
import std.ast.*

public macro Inner(input: Tokens) {
    return input
}

public macro Outer(input: Tokens) {
    return input
}

宏调用代码如下:

// demo.cj
import define.*
@Outer
class Demo {
    @Inner var state = 1
    @Inner var cnt = 42
}

main() {
    println("test macro")
    0
}

以下为 Linux 平台的编译命令(具体编译选项会随着 cjc 更新而演进,以最新 cjc 的编译选项为准):

# 当前目录: src
# 先编译宏定义文件在当前目录产生默认的动态库文件(允许指定动态库的路径,但不能指定动态库的名字)
cjc macros/m.cj --compile-macro
# 编译使用宏的文件,宏替换完成,产生可执行文件
cjc demo.cj -o demo
# 运行可执行文件
./demo

在 Linux 平台上,将生成用于包管理的 macro_define.cjo 和实际的动态库文件。

若在 Windows 平台:

# 当前目录: src
# 先编译宏定义文件在当前目录产生默认的动态库文件(允许指定动态库的路径,但不能指定动态库的名字)
cjc macros/m.cj --compile-macro
# 编译使用宏的文件,宏替换完成,产生可执行文件
cjc demo.cj -o demo.exe

若用 CJVM 虚拟机后端:

# 当前目录: src
# 先编译宏定义文件在当前目录产生默认的动态库文件(允许指定动态库的路径,但不能指定动态库的名字)
cjc macros/m.cj --compile-macro
# 编译使用宏的文件,宏替换完成,产生cbc文件
cjc demo.cj --interp-macro -o demo.cbc

并行宏展开

可以在编译宏调用文件时添加 --parallel-macro-expansion 选项,启用并行宏展开的能力。编译器会自动分析宏调用之间的依赖关系,无依赖关系的宏调用可以并行执行,如上述例子中的两个 @Inner 就可以并行展开,如此可以缩短整体编译时间。

注意

如果宏函数依赖一些全局变量,使用并行宏展开会存在风险。

macro package define
import std.ast.*
import std.collection.*

var Counts = HashMap<String, Int64>()

public macro Inner(input: Tokens) {
    for (t in input) {
        if (t.value.size == 0) {
            continue
        }
        // 统计所有有效token value的出现次数
        if (!Counts.contains(t.value)) {
            Counts[t.value] = 0
        }
        Counts[t.value] = Counts[t.value] + 1
    }
    return input
}

public macro B(input: Tokens) {
    return input
}

参考上述代码,如果 @Inner 的宏调用出现在多处,并且启用了并行宏展开选项,则访问全局变量 Counts 就可能存在冲突,导致最后获取的结果不正确。

建议不要在宏函数中使用全局变量,如果必须使用,要么关闭并行宏展开选项,或者可以通过仓颉线程锁对全局变量进行保护。

diagReport 报错机制

仓颉 ast 包提供了自定义报错接口 diagReport。方便定义宏的用户,在解析传入 tokens 时,对错误 tokens 内容进行自定义报错。

自定义报错接口提供同原生编译器报错一样的输出格式,允许用户报 warning 和 error 两类错误提示信息。

diagReport 的函数原型如下:

public func diagReport(level: DiagReportLevel, tokens: Tokens, message: String, hint: String): Unit

其参数含义如下:

  • level: 报错信息等级
  • tokens: 报错信息中所引用源码内容对应的 tokens
  • message: 报错的主信息
  • hint: 辅助提示信息

参考如下使用示例。

宏定义文件:

// macro_definition.cj
macro package macro_definition

import std.ast.*

public macro TestDef(input: Tokens): Tokens {
    for (i in 0..input.size) {
        if (input[i].kind == IDENTIFIER) {
            diagReport(DiagReportLevel.ERROR, input[i..(i + 1)],
                       "This expression is not allowed to contain identifier",
                       "Here is the illegal identifier")
        }
    }
    return input
}

宏调用文件:

// macro_call.cj
package macro_calling

import std.ast.*
import macro_definition.*

main(): Int64 {
    let a = @TestDef(1)
    let b = @TestDef(a)
    let c = @TestDef(1 + a)
    return 0
}

编译宏调用文件过程中,会出现如下报错信息:

error: This expression is not allowed to contain identifier
 ==> call.cj:9:22:
  |
9 |     let b = @TestDef(a)
  |                      ^ Here is the illegal identifier
  |

error: This expression is not allowed to contain identifier
  ==> call.cj:10:26:
   |
10 |     let c = @TestDef(1 + a)
   |                          ^ Here is the illegal identifier
   |

2 errors generated, 2 errors printed.

使用 --debug-macro 输出宏展开结果

借助宏在编译期做代码生成时,如果发生错误,处理起来十分棘手,这是开发者经常遇到但一般很难定位的问题。这是因为,开发者写的源码,经过宏的变换后变成了不同的代码片段。编译器抛出的错误信息是基于宏最终生成的代码进行提示的,但这些代码在开发者的源码中没有体现。

为了解决这个问题,仓颉宏提供 debug 模式,在这个模式下,开发者可以从编译器为宏生成的 debug 文件中看到完整的宏展开后的代码,如下所示。

宏定义文件:

macro package define

import std.ast.*

public macro Outer(input: Tokens): Tokens {
    let messages = GetChildMessages("Inner")

    let getTotalFunc = quote(public func getCnt() {
                       )
    for (m in messages) {
        let identName = m.getString("identifierName")
        getTotalFunc.append(Token(TokenKind.IDENTIFIER, identName))
        getTotalFunc.append(quote(+))
    }
    getTotalFunc.append(quote(0))
    getTotalFunc.append(quote(}))
    let funcDecl = parseDecl(getTotalFunc)

    let decl = (parseDecl(input) as ClassDecl).getOrThrow()
    decl.body.decls.append(funcDecl)
    return decl.toTokens()

}

public macro Inner(input: Tokens): Tokens {
    AssertParentContext("Outer")
    let decl = parseDecl(input)
    SetItem("identifierName", decl.identifier.value)
    return input
}

宏调用文件 demo.cj:

import define.*

@Outer
class Demo {
    @Inner var state = 1
    @Inner var cnt = 42
}

main(): Int64 {
    let d = Demo()
    println("${d.getCnt()}")
    return 0
}

在编译使用宏的文件时,在选项中,增加 --debug-macro,即使用仓颉宏的 debug 模式。

cjc --debug-macro demo.cj

在 debug 模式下,会生成临时文件 _demo.cj.macrocall_,对应宏展开的部分如下:

// demo.cj.macrocall
/* ===== Emitted by MacroCall @Outer in demo.cj:3:1 ===== */
/* 3.1 */class Demo {
/* 3.2 */    var state = 1
/* 3.3 */    var cnt = 42
/* 3.4 */    public func getCnt() {
/* 3.5 */        state + cnt + 0
/* 3.6 */    }
/* 3.7 */}
/* 3.8 */
/* ===== End of the Emit ===== */

如果宏展开后的代码有语义错误,则编译器的错误信息会溯源到宏展开后代码的具体行列号。仓颉宏的 debug 模式有以下注意事项:

  • 宏的 debug 模式会重排源码的行列号信息,不适用于某些特殊的换行场景。比如

    // before expansion
    @M{} - 2 // macro M return 2
    
    // after expansion
    // ===== Emmitted my Macro M at line 1 ===
    2
    // ===== End of the Emit =====
    - 2

    这些因换行符导致语义改变的情形,不应使用 debug 模式。

  • 不支持宏调用在宏定义内的调试,会编译报错。

    public macro M(input: Tokens) {
        let a = @M2(1+2) // M2 is in macro M, not suitable for debug mode.
        return input + quote($a)
    }
  • 不支持带括号宏的调试。

    // main.cj
    
    main() {
        // For macro with parenthesis, newline introduced by debug will change the semantics
        // of the expression, so it is not suitable for debug mode.
        let t = @M(1+2)
        0
    }
AI原生语言设计解析:仓颉如何实现AI亲和化与高性能开发 在人工智能与编程语言深度融合的背景下,AI原生语言成为解决传统开发效率与性能矛盾的关键方向。这类语言并非简单为AI增加接口,而是在语法、编译器和运行时层面系统性地适配张量计算、自动微分及异构硬件调度。仓颉语言作为面向AI时代的实践者,通过内建张量类型、多范式渐进类型、管道式数据流以及Python互操作等设计,试图兼顾Python的开发速度与C++的运行性能。同时,编译器层面的自动优化与结构化并发能力,为AI训练、推理服务及数据处理场景提供了更高效的工程基础。对于关注AI基础设施与编程语言演进的开发者而言,理 阅读详情

相关推荐

鸿蒙开发新选择:手把手教你用CodeArts IDE创建第一个仓颉语言项目

本文详细介绍了如何使用华为CodeArts IDE创建并运行第一个仓颉语言项目,涵盖环境准备、项目创建、配置运行及进阶开发技巧。仓颉语言作为华为推出的新一代编程语言,专为鸿蒙系统优化,适合全场景智能开发。通过实战教程,帮助开发者快速上手这一新兴技术。

weixin_42662293的博客 412

namespace “std“ 没有成员 “format“ 报错解决

2. C++语言标准选ISO C++20 标准 (/std:c++20)namespace "std" 没有成员 "format" 报错。满足以上两条,报错可解决。通过以下两条进行检查排除。C++代码如果在使用。

3475

告别WSL!实测AMD ROCm7预览版在Windows下跑ComfyUI,速度提升与避坑全记录

本文详细记录了在Windows原生环境下部署AMD ROCm7+PyTorch+ComfyUI的全流程,相比WSL环境性能显著提升。涵盖硬件兼容性确认、开发环境配置、Python环境搭建、ComfyUI部署优化及性能对比测试,为AI绘图爱好者提供实用指南。

weixin_42528090的博客 672

仓颉语言的编译和构建

CJPM(Cangjie Package Manager) 是仓颉语言的官方包管理工具,用来管理、维护仓颉项目的模块系统,并且提供更简易统一的编译入口,支持自定义编译命令。通过包管理器自动依赖管理实现对引入的多版本三方依赖软件进行分析合并,无需开发者担心多版本依赖冲突问题,大大减轻开发者负担;同时提供基于仓颉语言原生的自定义构建机制,允许开发者在构建的不同阶段增加预处理和后处理流程,实现构建流程可灵活定制,能够满足开发者不同业务场景下的编译构建诉求。

程小二的博客 7535

从玩具到工业:手把手教你用Webots仿真Scara抓取(逆解实战篇)

本文详细介绍了如何在Webots仿真环境中实现Scara机械臂的逆解计算与抓取操作,涵盖从环境配置、逆运动学算法到轨迹规划与实时控制的完整流程。通过实战案例演示,帮助读者掌握机械臂逆解的核心技术,并应用于工业自动化场景,提升Scara机械臂的编程与控制能力。

weixin_30932215的博客 504

ADS与Cadence中Verilog-A文件的编译与调用实战指南

本文详细介绍了在ADS与Cadence平台中Verilog-A文件的编译与调用实战指南,涵盖开发环境配置、模型创建与编译、仿真设置技巧以及跨平台协作与调试等内容。通过具体案例和优化策略,帮助工程师高效完成混合信号仿真设计,提升电路建模与验证效率。

weixin_29196315的博客 499

利用仓颉语言实现一个正整数中数字出现的频次统计

​ 利用仓颉编程语言实现如何统计一个正整数中各个数字出现的次数的小应用。目前看仓颉可供参考的资料还是偏少,只能看官方的开发指南或sdk,详见华为官方文档,目前用各类搜索引擎或者deepseek等大模型搜索,回复基本都将其认为是一种输入法,查找内容都不理想。因此,本次就参考官网sdk进行了实现,其实实现原理很简单,如果用python、java等语言实现非常容易,整体思路就是采用除法取余方式计算每一位的数字是几并进行对应十个位置的存储记录和累积,最后输出即可。

hhue2007的专栏 780

萤石摄像头批量下载视频后FFmepg拼接

本文描述萤石摄像头,如何批量下载视频,然后把视频片段拼接为一个完整的视频,亲测有效。有问题,可以下面留言

烤鱼哥哥的博客 1202

仓颉编程语言-01-首次体验

本文主要完成仓颉编程语言-首次体验,let’s go!!!

IT从业者的成长历程 2073

Unity最强动作战斗框架?Combat System Framework 深度解析

Combat System Framework 并不是一个简单的角色控制器,而是一套完整的动作游戏战斗开发框架。它借鉴了现代 AAA 游戏常见的架构思想,以 Gameplay Tag 为核心,结合 ScriptableObject 数据驱动设计,将技能、属性、Buff、伤害、武器、动画通知等系统有机整合在一起。同时,配套的可视化动画时间轴、Root Motion 工具和实时预览窗口,大幅优化了动作开发流程。当然,这套框架也有一定的学习门槛。

2403_88403568的博客 684

npm报错 npm WARN deprecated....的问题解决方式

报错 :npm WARN deprecated … 现象:无法进行有效的vue项目的部署以及npm install 命令的执行。 原因一:版本问题 说明:由于npm的版本过低或者过高导致的报错 检查版本: npm -v 如果版本是比较旧的,或者是最新的,这里可以进行对npm的更新和回退。 更新npm版本: npm updated -d 查看可以更新的版本 npm install -g npm 默认安装最新版本 安装回退指定的版本: npm updated -d 查看所有的版本 npm -g i.

任聪聪的博客 16万+

仓颉编程语言开发指南(下)

文章目录仓颉编程语言开发指南(上)十五、宏1.宏的简介2.Tokens 相关类型和 quote 表达式Token 类型Tokens 类型quote 表达式和插值3.语法节点节点的解析使用解析表达式和声明的函数使用构造函数进行解析节点的组成部分BinaryExpr 案例FuncDecl 案例使用 quote 插值语法节点4.宏的实现非属性宏属性宏嵌套宏宏定义中嵌套宏调用宏调用中嵌套宏调用嵌套宏之间的消息传递5.编译、报错与调试宏的编译和使用并行宏展开diagReport 报错机制使用 --debug-macr

欢迎来到我的学习笔记薄 2994

【人工智能】在 cc-switch 中配置 AnyRouter 接入 Claude Code 完整指南

本文介绍了在cc-switch工具中配置AnyRouter中转服务以接入Claude Code的详细步骤。主要内容包括:1) 准备API令牌和端点地址;2) 填写基础信息和模型参数;3) 保存配置并重启终端生效;4) 常见问题排查方法。重点强调了API地址格式、模型ID准确性以及配置写入的必要性,帮助用户顺利完成中转服务配置,实现Claude 4.5版本的正常使用。

奶爸的编程之路,也就一周冷个三天 3107

仓颉编程语言:数据竞争检测的深度解析与实践

在现代多核处理器时代,并发编程已成为提升应用性能的关键手段。然而,并发带来的数据竞争(Data Race)问题却是开发者面临的最大挑战之一。仓颉编程语言作为华为自研的新一代编程语言,从设计之初就将"安全可靠"和"轻松并发"作为核心理念,在数据竞争检测方面融入了诸多创新性的技术方案。

2401_88163728的博客 662

OpenClaw实战-行政与HR必备:AI驱动的办公自动化

智能会议纪要自动生成会议时间智能协调自动提醒和跟进简历智能筛选和匹配度分析面试安排自动化入职办理流程化通知公告一键发布发票识别和报销辅助资产管理数字化让AI处理繁杂的事务性工作,让您有更多时间专注于更有价值的战略和管理任务。

gaoxiang_120的专栏 220

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

DevEco Studio​ 中仓颉经典问题

在DevEco Studio中使用仓颉语言开发鸿蒙应用时,常见问题包括:1) 基础语法:变量类型需明确,避免Any;2) 鸿蒙API:UI组件需设置布局约束(如width('100%'));3) 异步编程:通过then或async/await处理Promise;4) 性能优化:用@Link减少UI频繁更新;5) 调试:使用hilog输出日志,通过Logcat查看;6) 空值:用?.或??安全处理。建议遵循静态类型规范,正确调用生命周期方法,并优化组件更新逻辑以提升效率。

m0_59315734的博客 1241

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

HarmonyOS鸿蒙应用:仓颉语言与JavaScript核心差异深度解析

本文对比了仓颉语言与JavaScript的核心差异。仓颉作为鸿蒙生态专属语言,强调静态类型、安全设计和原生分布式支持,适合高性能、高安全的鸿蒙全场景开发;JavaScript则凭借动态灵活性和丰富生态,主导Web、服务端等跨平台场景。选型建议:鸿蒙原生开发首选仓颉,Web及跨平台场景选择JavaScript。两种语言分别代表"生态定制化"与"通用灵活性"的不同设计哲学,开发者应根据目标场景选择最合适的工具。

weixin_43578304的博客 7030

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

仓颉语言宏系统的设计与应用:从元编程到领域特定语言

本文深入探讨仓颉语言的宏系统设计,重点分析了其卫生宏机制、类型安全特性及多阶段编译模型。文章介绍了三种宏类型(函数式宏、派生宏和属性宏)及其应用场景,并通过构建类型安全查询DSL的实践案例展示宏的实际价值。同时讨论了宏展开的性能优化策略、调试方法及最佳实践,强调宏系统在提升开发效率、减少样板代码方面的优势。最后展望了宏系统在元编程领域的未来发展潜力,指出其在代码生成、性能优化等方向的应用前景。

user340的博客 1049

仓颉编程语言入门指南:从环境搭建到基础语法

Linux环境下有个贴心设计:envsetup.sh脚本自动配置环境变量,省去手动配置的麻烦。Windows用户推荐通过VSCode插件快速搭建,下载SDK时需注意路径配置要精确到cangjie子目录,这个细节直接影响环境变量生效。对于教学演示场景,推荐使用平台的"分享项目"功能,学生点击链接就能看到运行效果。我在演示数组遍历时,学生通过网页直接修改循环边界值观察输出变化,互动效果超出预期。实测仓颉语言片段非常便捷,无需配置本地环境即可体验语法特性。平台自动补全功能对新手友好,能快速感知类型系统的特点。

RubyWolf84的博客 305

仓颉编程入门(一)

摘要:仓颉(Cangjie)是华为为鸿蒙系统设计的静态类型编程语言,具有高效、安全和易用等特点。主要特性包括:静态类型检查、多范式支持(面向对象和函数式)、内存安全机制(所有权系统和空值处理)、优化的并发模型(Actor和异步/等待)、与鸿蒙深度集成(分布式能力和组件化开发)以及完善的工具链支持。该语言目前处于开发者预览阶段,专注物联网和分布式系统开发,未来有望成为鸿蒙生态的重要开发工具。

weixin_42314209的博客 766

仓颉编程语言新手全流程上手指南:从安装工具链到提交首个PR

你大概也遇到过这样的尴尬:听说仓颉语言已经开源,兴致勃勃地克隆了代码仓库,结果面对一堆源码目录、PDF 指南和英文文档,完全不知道第一行命令该敲什么。别急,这篇指南就是为"从零开始"的你准备的——我们会跟着一个真实的上手旅程,把环境搭建、写代码、找资料、参与贡献这四件事一次走通,让你真正成为仓颉社区的一员。 ## 出发之前:先弄清这个社区仓库里到底有什么 在动手之前,花两分钟认识一下你即将进入

gitblog_00982的博客 840

《仓颉数据竞争检测:从编译时守护到运行时追踪的完整防线》

仓颉语言采用多层防御体系应对数据竞争问题:1)编译时通过Send/Sync类型系统阻止不安全线程共享;2)运行时通过Mutex<T>等封装类型强制锁与数据绑定;3)集成ThreadSanitizer动态检测工具;4)推荐Actor模型消除共享状态。这种"预防优先"的哲学将并发安全问题从程序员责任转化为系统保障,通过类型系统、同步原语、动态检测和架构模式的协同作用,在开发生命周期的各个阶段层层设防,最大化消除数据竞争风险。

2301_80851406的博客 655

《仓颉数据竞争检测:从编译时守护到运行时追踪的完整防线

仓颉语言采用多层次防御体系解决数据竞争问题:1) 编译时通过所有权系统和Send/Sync特性标记确保类型安全;2) 运行时使用Mutex<T>和RwLock<T>强制同步访问共享数据;3) 集成ThreadSanitizer进行动态检测;4) 推荐Actor模型避免共享状态。这种分层策略从语言设计层面预防并发问题,相比传统语言的"程序员负责"模式,提供了更可靠的并发安全保障。

2301_80851406的博客 926

仓颉语言核心技术深度解析与实战应用

本文围绕仓颉语言核心技术展开全方位探讨,涵盖语言特性深度解析、标准库源码细致拆解、实战项目完整复盘、入门级基础教程、三方库适配、服务端与鸿蒙应用开发及性能优化等内容,旨在为读者全面呈现仓颉语言的技术要点与应用实践。

weixin_42300449的博客 1227

仓颉从入门到精通 - 使用 --debug-macro 输出宏展开结果

仓颉语言中宏的定义需要放在由 macro package 声明的包中,被 macro package 限定的包仅允许宏定义对外可见,其他声明包内可见。说明:重导出的声明也允许对外可见,关于包管理和重导出的相关概念,请参见包的导入章节。macro package define // 编译 define.cjo 携带 macro 属性public func A() {} // Error, 宏包不允许定义外部可见的非宏定义,此处会报错。

wanmeijuhao的博客 1098
上一篇: 仓颉编程语言:宏的实现
下一篇: 仓颉编程语言:宏包定义和导入
懂你的大海
博客等级 码龄6年 9049粉丝 1381原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值