仓颉编程语言:宏的实现

仓颉语言 -- 宏 阅读详情

本章节介绍仓颉宏的定义和使用,仓颉宏可以分为非属性宏和属性宏。同时本章节还会介绍宏出现嵌套时的行为。

非属性宏

非属性宏只接受被转换的代码,不接受其他参数(属性),其定义格式如下:

import std.ast.*

public macro MacroName(args: Tokens): Tokens {
    ... // Macro body
}

宏的调用格式如下:

@MacroName(...)

宏调用使用 () 括起来。括号里面可以是任意合法 tokens,也可以是空。

当宏作用于声明时,一般可以省略括号。参考下面例子

@MacroName func name() {}        // Before a FuncDecl
@MacroName struct name {}        // Before a StructDecl
@MacroName class name {}         // Before a ClassDecl
@MacroName var a = 1             // Before a VarDecl
@MacroName enum e {}             // Before a enum
@MacroName interface i {}        // Before a InterfaceDecl
@MacroName extend e <: i {}      // Before a ExtendDecl
@MacroName mut prop i: Int64 {}  // Before a PropDecl
@MacroName @AnotherMacro(input)  // Before a macro call

宏展开过程作用于仓颉语法树,宏展开后,编译器会继续进行后续的编译过程,因此,用户需要保证宏展开后的代码依然是合法的仓颉代码,否则可能引发编译问题。当宏用于声明时,如果省略括号,宏的输入必须是语法合法的声明,IDE 也会提供相应的语法检查和高亮。

下面是几个宏应用的典型示例。

  • 示例 1

    宏定义文件 macro_definition.cj

    macro package macro_definition
    
    import std.ast.*
    
    public macro TestDef(input: Tokens): Tokens {
        println("I'm in macro body")
        return input
    }

    宏调用文件 macro_call.cj

    package macro_calling
    
    import macro_definition.*
    
    main(): Int64 {
        println("I'm in function body")
        let a: Int64 = @TestDef(1 + 2)
        println("a = ${a}")
        return 0
    }

    上述代码的编译过程可以参考宏的编译和使用。

    我们在用例中添加了打印信息,其中宏定义中的 I'm in macro body 将在编译 macro_call.cj 的期间输出,即对宏定义求值。同时,宏调用点被展开,如编译如下代码:

    let a: Int64 = @TestDef(1 + 2)

    编译器将宏返回的 Tokens 更新到调用点的语法树上,得到如下代码:

    let a: Int64 = 1 + 2

    也就是说,可执行程序中的代码实际变为了:

    main(): Int64 {
        println("I'm in function body")
        let a: Int64 = 1 + 2
        println("a = ${a}")
        return 0
    }

    a 经过计算得到的值为 3,在打印 a 的值时插值为 3。至此,上述程序的运行结果为:

    I'm in function body
    a = 3

下面看一个更有意义的用宏处理函数的例子,这个宏 ModifyFunc 宏的作用是给 MyFunc 增加 Composer 参数,并在counter++前后插入一段代码。

  • 示例 2

    宏定义文件 macro_definition.cj

    // file macro_definition.cj
    macro package macro_definition
    
    import std.ast.*
    
    public macro ModifyFunc(input: Tokens): Tokens {
        println("I'm in macro body")
        let funcDecl = FuncDecl(input)
        return quote(
        func $(funcDecl.identifier)(id: Int64) {
            println("start ${id}")
            $(funcDecl.block.nodes)
            println("end")
        })
    }

    宏调用文件 macro_call.cj

    package macro_calling
    
    import macro_definition.*
    
    var counter = 0
    
    @ModifyFunc
    func MyFunc() {
        counter++
    }
    
    func exModifyFunc() {
        println("I'm in function body")
        MyFunc(123)
        println("MyFunc called: ${counter} times")
        return 0
    }

    同样的,上述两段代码分别位于不同文件中,先编译宏定义文件 macro_definition.cj,再编译宏调用 macro_call.cj 生成可执行文件。

    这个例子中,ModifyFunc 宏的输入是一个函数声明,因此可以省略括号:

    @ModifyFunc
    func MyFunc() {
        counter++
    }

    经过宏展开后,得到如下代码:

    func MyFunc(id: Int64) {
        println("start ${id}")
        counter++
        println("end")
    }

    MyFunc 会在 main 中调用,它接受的实参也是在 main 中定义的,从而形成了一段合法的仓颉程序。运行时打印如下:

    I'm in function body
    start 123
    end
    MyFunc called: 1 times

属性宏

和非属性宏相比,属性宏的定义会增加一个 Tokens 类型的输入,这个增加的入参可以让开发者输入额外的信息。比如开发者可能希望在不同的调用场景下使用不同的宏展开策略,则可以通过这个属性入参进行标记位设置。同时,这个属性入参也可以传入任意 Tokens,这些 Tokens 可以与被宏修饰的代码进行组合拼接等。下面是一个简单的例子:

// Macro definition with attribute
public macro Foo(attrTokens: Tokens, inputTokens: Tokens): Tokens {
    return attrTokens + inputTokens  // Concatenate attrTokens and inputTokens.
}

如上面的宏定义,属性宏的入参数量为 2,入参类型为 Tokens,在宏定义内,可以对 attrTokens 和 inputTokens 进行一系列的组合、拼接等变换操作,最后返回新的 Tokens。

带属性的宏与不带属性的宏的调用类似,属性宏调用时新增的入参 attrTokens 通过 [] 传入,其调用形式为:

// attribute macro with parentheses
var a: Int64 = @Foo[1+](2+3)

// attribute macro without parentheses
@Foo[public]
struct Data {
    var count: Int64 = 100
}
  • 宏 Foo 调用,当参数是 2+3 时,与 [] 内的属性 1+ 进行拼接,经过宏展开后,得到 var a: Int64 = 1+2+3 。

  • 宏 Foo 调用,当参数是 struct Data 时,与 [] 内的属性 public 进行拼接,经过宏展开后,得到

    public struct Data {
        var count: Int64 = 100
    }

关于属性宏,需要注意以下几点:

  • 带属性的宏,与不带属性的宏相比,能修饰的 AST 是相同的,可以理解为带属性的宏对可传入参数做了增强。

  • 要求属性宏调用时,[] 内中括号匹配,且可以为空。中括号内只允许对中括号的转义 \[ 或 \],该转义中括号不计入匹配规则,其他字符会被作为 Token,不能进行转义。

    @Foo[[miss one](2+3) // Illegal
    @Foo[[matched]](2+3) // Legal
    @Foo[](2+3)          // Legal, empty in []
    @Foo[\[](2+3)        // Legal, use escape for [
    @Foo[\(](2+3)        // Illegal, only [ and ] allowed in []
  • 宏的定义和调用的类型要保持一致:如果宏定义有两个入参,即为属性宏定义,调用时必须加上 [],且内容可以为空;如果宏定义有一个入参,即为非属性宏定义,调用时不能使用 []。

嵌套宏

仓颉语言不支持宏定义的嵌套;有条件地支持在宏定义和宏调用中嵌套宏调用。

宏定义中嵌套宏调用

下面是一个宏定义中包含其他宏调用的例子。

宏包 pkg1 中定义 GetIdent 宏:

macro package pkg1

import std.ast.*

public macro GetIdent(attr:Tokens, input:Tokens):Tokens {
    return quote(
        let decl = (parseDecl(input) as VarDecl).getOrThrow()
        let name = decl.identifier.value
        let size = name.size - 1
        let $(attr) = Token(TokenKind.IDENTIFIER, name[0..size])
    )
}

宏包 pkg2 中定义 Prop 宏,其中嵌套了 GetIdent 宏的调用:

macro package pkg2

import std.ast.*
import pkg1.*

public macro Prop(input:Tokens):Tokens {
    let v = parseDecl(input)
    @GetIdent[ident](input)
    return quote(
        $(input)
        public prop $(ident): $(decl.declType) {
            get() {
                this.$(v.identifier)
            }
        }
    )
}

宏调用包 pkg3 中调用 Prop 宏:

package pkg3

import pkg2.*
class A {
    @Prop
    private let a_: Int64 = 1
}

main() {
    let b = A()
    println("${b.a}")
}

注意,按照宏定义必须比宏调用点先编译的约束,上述 3 个文件的编译顺序必须是:pkg1 -> pkg2 -> pkg3。pkg2 中的 Prop 宏定义:

public macro Prop(input:Tokens):Tokens {
    let v = parseDecl(input)
    @GetIdent[ident](input)
    return quote(
        $(input)
        public prop $(ident): $(decl.declType) {
            get() {
                this.$(v.identifier)
            }
        }
    )
}

会先被展开成如下代码,再进行编译。

public macro Prop(input: Tokens): Tokens {
    let v = parseDecl(input)

    let decl = (parseDecl(input) as VarDecl).getOrThrow()
    let name = decl.identifier.value
    let size = name.size - 1
    let ident = Token(TokenKind.IDENTIFIER, name[0 .. size])

    return quote(
        $(input)
        public prop $(ident): $(decl.declType) {
            get() {
                this.$(v.identifier)
            }
        }
    )
}

宏调用中嵌套宏调用

嵌套宏的常见场景,是宏修饰的代码块中,出现了宏调用。一个具体的例子如下:

pkg1 包中定义 Foo 和 Bar 宏:

macro package pkg1

import std.ast.*

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

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

pkg2 包中定义 AddToMul 宏:

macro package pkg2

import std.ast.*

public macro AddToMul(inputTokens: Tokens): Tokens {
    var expr: BinaryExpr = match (parseExpr(inputTokens) as BinaryExpr) {
        case Some(v) => v
        case None => throw Exception()
    }
    var op0: Expr = expr.leftExpr
    var op1: Expr = expr.rightExpr
    return quote(($(op0)) * ($(op1)))
}

pkg3 包中使用上面定义的三个宏:

package pkg3

import pkg1.*
import pkg2.*
@Foo
struct Data {
    let a = 2
    let b = @AddToMul(2+3)

    @Bar
    public func getA() {
        return a
    }

    public func getB() {
        return b
    }
}

main(): Int64 {
    let data = Data()
    var a = data.getA() // a = 2
    var b = data.getB() // b = 6
    println("a: ${a}, b: ${b}")
    return 0
}

如上代码所示,宏 Foo 修饰了 struct Data,而在 struct Data 内,出现了宏调用 AddToMul 和 Bar。这种嵌套场景下,代码变换的规则是:将嵌套内层的宏 (AddToMul 和 Bar) 展开后,再去展开外层的宏 (Foo)。允许出现多层宏嵌套,代码变换的规则总是由内向外去依次展开宏。

嵌套宏可以出现在带括号和不带括号的宏调用中,二者可以组合,但用户需要保证没有歧义,且明确宏的展开顺序:

var a = @Foo(@Foo1(2 * 3)+@Foo2(1 + 3))  // Foo1, Foo2 have to be defined.

@Foo1 // Foo2 expands first, then Foo1 expands.
@Foo2[attr: struct] // Attribute macro can be used in nested macro.
struct Data{
    @Foo3 @Foo4[123] var a = @Bar1(@Bar2(2 + 3) + 3)  // Bar2, Bar1, Foo4, Foo3 expands in order.
    public func getA() {
        return @Foo(a + 2)
    }
}

嵌套宏之间的消息传递

这里指的是宏调用的嵌套。

内层宏可以调用库函数 AssertParentContext 来保证内层宏调用一定嵌套在特定的外层宏调用中。如果内层宏调用这个函数时没有嵌套在给定的外层宏调用中,该函数将抛出一个错误。库函数 InsideParentContext 同样用于检查内层宏调用是否嵌套在特定的外层宏调用中,该函数返回一个布尔值。下面是一个简单的例子。

宏定义如下:

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

public macro Inner(input: Tokens): Tokens {
    AssertParentContext("Outer")
    return input
}

宏调用如下:

@Outer var a = 0
@Inner var b = 0 // Error, The macro call 'Inner' should with the surround code contains a call 'Outer'.

如上代码所示,Inner 宏在定义时使用了 AssertParentContext 函数用于检查其在调用阶段是否位于 Outer 宏中,在代码示例的宏调用场景下,由于 Outer 和 Inner 在调用时不存在这样的嵌套关系,因此编译器将报告一个错误。

内层宏也可以通过发送键/值对的方式与外层宏通信。当内层宏执行时,通过调用标准库函数 SetItem 向外层宏发送信息;随后,当外层宏执行时,调用标准库函数 GetChildMessages 接收每一个内层宏发送的信息(一组键/值对映射)。下面是一个简单的例子。

宏定义如下:

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")
        // let value = m.getString("key")            // 接收多组消息
        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)
    // SetItem("key", "value")                      // 可以通过不同的key值传递多组消息
    return input
}

宏调用如下:

import define.*

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

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

在上面的代码中,Outer 接收两个 Inner 宏发送来的变量名,自动为类添加如下内容:

public func getCnt() {
    state + cnt + 0
}

具体流程为:内层宏 Inner 通过 SetItem 向外层宏发送信息;Outer 宏通过 GetChildMessages 函数接收到 Inner 发送的一组信息对象(Outer 中可以调用多次 Inner);最后通过该信息对象的 getString 函数接收对应的值。


 看完三件事❤️

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注作者公众皓 『 蜀道衫 』,不定期分享原创知识。
  • 关注后回复【666】扫码即可获取学习资料包。
  • 同时可以期待后续文章ing🚀。

​

仓颉编程语言开发:程序结构 在仓颉程序的顶层作用域中,可以定义一系列的变量、函数和自定义类型(如 struct、class、enum 和 interface 等)....... 阅读详情

相关推荐

【华为仓颉编程语言】运行第一个仓颉程序

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

九丘教育官方博客 1789

仓颉语言学习和编程实验

仓颉编程语言作为一款面向全场景应用开发的现代编程语言,通过现代语言特性的集成、全方位的编译优化和运行时实现、以及开箱即用的IDE工具链支持,为开发者打造友好开发体验和卓越程序性能。具备高效编程、安全可靠、轻松并发、卓越性能等设计理念,可以说仓颉整合了C++、Java、python、javascript等语言的各种优点特性,是一种大模型浪潮下的AI for PL、PL for AI的形态,支持面向应用开发的一系列工具链,包括语言服务(高亮、联想)、调试(跨语言调试、线程级可视化调试)、静态检查、性能分析。

hhue2007的专栏 1751

华为仓颉编程语言观感

华为仓颉编程语言概览

HelloMagina的博客 7541

仓颉编程语言:cjc 使用

本章将对基本使用方法进行介绍。

Androidbye的博客 873

国产自研编程语言“仓颉”来了!

在探索仓颉编程语言的旅程中,我们领略了其独特的设计理念、强大的功能和无尽的潜力。从基础的数据类型到复杂的算法结构,仓颉语言展现了一种既注重效率又追求简洁与优雅的编程范式。其枚举类型作为语言中的一个重要组成部分,不仅增强了代码的可读性和类型安全性,还为开发者提供了更多的灵活性和便利性。然而,仓颉语言仍然是一个不断发展的领域,它将继续吸引来自全球各地的开发者们共同探索和创新。我们期待着未来仓颉语言能够带来更多的惊喜和突破,为解决现实世界中的复杂问题提供更加高效和可靠的编程工具。

静Yu的博客 3135

仓颉编程语言:基本概念

在以上程序中,if 表达式作为变量定义的初值使用,由于变量 bit 没有被标注类型、需要从初值中推导,所以 if 表达式的类型取为两个分支代码块类型的最小公共父类型,根据前文对“代码块”的介绍,可知两个分支代码块类型都是 Int8,所以 if 表达式的类型被确定为 Int8,其值为所执行分支即 else 分支代码块的值,所以变量 bit 的类型为 Int8、值为 1。在仓颉程序中,由一对大括号“{}”包围起来的一组表达式,被称为“代码块”,它将作为程序的一个顺序执行流,其中的表达式将按编码顺序依次执行。

啥也不会的云计算小白 3124

仓颉编程语言中的宏系统:概念、Tokens与quote表达式详解

仓颉编程语言中的宏系统是一种强大的编译时元编程机制,主要通过Token和Tokens类型操作代码片段。宏分为非属性宏和属性宏两类,使用@符号调用。quote表达式简化了Token序列构造,支持插值和多行模板。宏可用于实现调试工具、DSL构建和编译期验证等场景。最佳实践包括合理嵌套、错误报告机制和性能优化。掌握Tokens操作和quote使用是仓颉元编程的关键,建议从简单宏入手逐步深入。

Huang兄 594

人工智能(AI)简史:推动新时代的科技力量

人工智能(AI,Artificial Intelligence)是计算机科学的一个分支,旨在研究和开发可以模拟、扩展或增强人类智能的系统。它涉及多种技术和方法,包括机器学习、深度学习、自然语言处理(NLP)、计算机视觉、专家系统等。

qq_17153885的博客 1万+

仓颉编程语言内置宏实战:用Attribute宏给变量打标签,实现动态属性赋值

本文深入探讨了仓颉编程语言中内置宏`Attribute`的实战应用,展示了如何通过该宏为变量打标签并实现动态属性赋值。文章详细介绍了`Attribute`宏的语法、配套函数及实际应用场景,对比了传统方法与宏实现的优劣,并提供了高级应用和最佳实践建议,帮助开发者高效利用这一强大特性。

weixin_30369041的博客 393

基础的pdf病毒木马

基础的pdf病毒木马 环境: 攻击机:Kali 目标操作系统:Windows 10或Windows 7 (关闭防火墙和安全软件) 目标软件:Adobe Reader 8.1.2 msf框架搜索:adobe-pdf 1.打开msfconsole 2.搜索 adobe_pdf模块 3.加载攻击模块并设置payload 4.运行生成pdf 5.将pdf拖入到win10 6.打开msf,加...

小小猪的博客 4624

别再让PyCharm吃光你的C盘!保姆级教程:安全迁移config、plugins和remote sources到D盘

本文提供了一份详细的PyCharm配置与缓存文件迁移指南,帮助开发者将config、plugins和remote sources等占用大量C盘空间的文件安全转移到D盘。通过修改data.properties文件和正确迁移顺序,确保PyCharm功能不受影响,同时释放宝贵的系统盘空间。

weixin_28836989的博客 424

跟我一起学“仓颉”编程语言-非属性宏和属性宏

本章为大家详细的介绍了仓颉编程语言中非属性宏和属性宏的内容。

Cool_foolisher的博客 367

MTK Meta ISP 工具使用技巧与参数读取路径全解析

在联发科(MediaTek, MTK)平台上进行相机图像调试时,Meta ISP 工具是连接 PC 与设备进行参数调整、寄存器读取和图像输出的重要工具。本文以实战角度出发,系统梳理 Meta ISP 的使用流程、关键模块功能、典型调试技巧与参数读取路径,结合最新版平台(如MTK Genio/Dimensity 系列)架构,帮助工程师掌握联发科图像调优的核心流程。

努力分享一些人工智能、计算机视觉、影像等相关的知识干货! 1152

一文带你入门仓颉编程语言(下)

仓颉编程语言,专为 HarmonyOS 精心打造,它宛如一座桥梁,无缝打通了鸿蒙与欧拉在应用开发生态上的阻碍,为鸿蒙生态基础设施建设添上了画龙点睛的最后一笔。从移动应用、智能穿戴,到智能家居、工业控制,鸿蒙系统广泛的应用场景,对编程语言提出了严苛至极的要求。仓颉编程语言应运而生,承载着满足多样化需求、助力开发者高效创新的重任,已然成为鸿蒙生态蓬勃发展的核心驱动力。

倔强的石头的博客 6748

【软考中级】多媒体应用设计师复习笔记第十章【free】

第10章法律法规与标准规范 行业标准 缩写的大写拼音 地方标准 DB加生代码 企业标准Q加斜线企业代号国际标准是国际标准化组织ISO和国际电工委员会IEC制定的标准 国家标准是由国务院标准化行政主管部门制定,全国范围内使用 ?强制性国家标准的代号为GB,推荐性国家标准的代号为GB/T。 ?软件开发规范GB8566-88属于强制性国家标准 在这里插入图片描述 行业标准代号由大写汉语拼音字母组成,加上斜线和T,组成推荐性行业标准,如XX/T。已经发布的行业代号有QJ(航天),SJ(电子

weixin_47120900的博客 641

交换机背板带宽、交换容量与包转发率到底是啥?

在网络工程中,交换机是核心设备之一,而关于它的性能指标,背板带宽、交换容量、以及包转发率往往成为人们衡量其性能的重要参数。这些术语的定义、关系、以及在实际场景中的应用,都对交换机的选型与部署有着重要意义。本文将为您全面解读这些概念,帮助您深入了解交换机的核心性能指标。

网络技术联盟站 1551

仓颉编程语言入门

五年磨一剑,华为仓颉编程语言正式发布。

程小二的博客 4万+

【Linux0.11代码分析】之 系列文章链接汇总(全)

【Linux0.11代码分析】00 之 系列文章链接汇总(全)

|~~~热爱生活、努力学习的小伙汁~~~| 1451

一文带你入门仓颉编程语言(上)

仓颉编程语言,专为 HarmonyOS 精心打造,它宛如一座桥梁,无缝打通了鸿蒙与欧拉在应用开发生态上的阻碍,为鸿蒙生态基础设施建设添上了画龙点睛的最后一笔。从移动应用、智能穿戴,到智能家居、工业控制,鸿蒙系统广泛的应用场景,对编程语言提出了严苛至极的要求。仓颉编程语言应运而生,承载着满足多样化需求、助力开发者高效创新的重任,已然成为鸿蒙生态蓬勃发展的核心驱动力。

倔强的石头的博客 2万+

计算机视觉教程1-1:透视空间与透视变换

在生活中,透视现象很常见,其物理本质是光的直线传播。由于人眼与相机在捕获图像时均存在透视现象,因此计算机几何基于透视空间进行研究。本节介绍透视变换原理

FRIGIDWINTER的博客 3092

假人有限元模型

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

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

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

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

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

跟我一起学“仓颉”编程语言—仓颉语言介绍及Hello World

本章为大家详细的介绍了仓颉编程语言的特性、环境安装以及Hello World的内容。

Cool_foolisher的博客 9003

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

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

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

华为仓颉编程语言正式发布,仓颉编程教程

华为在 2024 年 6 月 21 日的华为开发者大会上,华为终端 BG 软件部总裁龚体正式官宣了华为自研仓颉编程语言,并发布了 HarmonyOS NEXT 仓颉语言开发者预览版。仓颉 | 华为开发者联盟仓颉编程语言文件后缀名为.cj你好,仓颉。lambda表达式可以让函数的创建和使用更加灵活,lambda表达式的值就是一个匿名函数仓颉提供了Exception和Error两个类型,用于描述程序运行时的异常行为开发者可以继承Exception或其子类来自定义异常类,但不能继承Error或其子类。

shixuyong的博客 4408

仓颉编程入门 -- 宏的介绍

在编程中,宏可以被视为一种强大的工具,它允许开发者在编译时期对代码进行变换。与普通的函数不同,宏不仅操作数据值,还直接操作代码本身。通过宏,我们可以将复杂的、重复的代码模式抽象化,并在编译时自动生成这些模式的具体实现。假设我们有一个需求:在开发过程中频繁地需要打印变量或表达式的值及其表达式本身,以便于调试。传统的做法是通过编写大量的print语句来实现,但这既繁琐又容易出错。宏提供了一种优雅的解决方案,让我们能够定义一个dprint宏,自动完成这一任务。为了定义并实现dprint。

几许的博客 1万+

仓颉编程语言:编程世界的 “文化瑰宝”

在当今编程领域百花齐放的时代,各种编程语言争奇斗艳,服务于不同的应用场景和开发者群体。然而,有这样一种编程语言,它承载着独特的文化内涵,宛如编程世界里一颗熠熠生辉的“文化瑰宝”,那就是仓颉编程语言。

2301_80350265学无止尽5的博客 3770

华为仓颉编程语言

仓颉编程语言是华为公司自主研发的一款静态强类型、编译型语言,具备高效编程、安全可靠、轻松并发和卓越性能等特点。该语言旨在解决全场景应用开发中的挑战,为开发者提供友好的开发体验和卓越的程序性能。自2019年启动研发以来,仓颉编程语言已经历了五年的沉淀和大量研发投入,目前已正式开启开发者预览版。

了不起的小白君的博客 4175
上一篇: 仓颉编程语言:语法节点
下一篇: 仓颉编程语言:编译、报错与调试
懂你的大海
博客等级 码龄6年 9049粉丝 1381原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值