CYaRonC -- CYaRon! 语言到LLVM IR的编译器

· · 科技·工程

项目地址:https://github.com/yuygfgg/CYaRonC

下面的内容主要是对README的翻译。

CYaRonC

CYaRonC 是 CYaRon! 编程语言的编译器。它能够将 CYaRon! 源代码编译成 LLVM 中间表示(IR)。

功能特性

CYaRonC 实现了一个 CYaRon! 编程语言的扩展版本,该语言最初定义于 P3695 。

扩展语法 (与原题相比)

此实现为原版 CYaRon! 语言增加了几项新功能:

构建编译器

您需要预先安装 cmake、一个兼容 C++20 的编译器(如 clanggcc)以及 llvm

# 创建构建目录
mkdir -p build && cd build

# 配置并构建项目
cmake -DCMAKE_BUILD_TYPE=Release ..
make

编译器用法

1. 编译到 LLVM IR

使用 cyaronc 可执行文件将 .cyaron 源文件编译成 .ll 格式的 LLVM IR 文件。

# 语法
./cyaronc [-g] <input.cyaron> <output.ll>

# 示例
./cyaronc example/segment_tree.cyaron output.ll

# 带调试符号的示例
./cyaronc -g example/segment_tree.cyaron output.ll

2. 执行代码

您可以通过多种方式运行生成的 LLVM IR:

# 1. 使用 lli 直接解释执行 LLVM IR
lli output.ll

# 2. 使用 clang 将 LLVM IR 编译为原生可执行文件
clang output.ll -o my_program

# 运行可执行文件
./my_program

# 3. 编译时附带调试符号,并使用调试器
clang -g output.ll -o my_program_debug
lldb ./my_program_debug

语言语法

注释

注释以 # 开始,并持续到行尾。

# 这是一行注释。

变量

所有变量必须在文件顶部的 {vars ...} 代码块中声明。变量默认初始化为零。

{ vars
    my_int: int
    my_float: float
    my_array: array[int, 1..10]
}

类型

语句

输出: :yosoro

将一个表达式的值输出到标准输出,并后跟一个空格。

:yosoro 123          # 输出 "123 "
:yosoro my_int + 5   # 输出表达式的计算结果和一个空格

赋值: :set

为一个变量或数组元素赋值。

:set my_int, 100
:set my_array[1], 200
:set my_int, my_int + my_array[1]

输入: :input

从标准输入读取一个数字,并存入一个变量或数组元素。

:input my_int
:input my_array[5]

控制流

控制流语句使用 {...} 定义的代码块结构。

比较运算符

以下运算符用于所有控制流语句:

If 语句: {ihu ...}

ihu 语句在条件为真时执行一个代码块。该语句没有 else 分支。

# 语法: {ihu <op>, <expr1>, <expr2> ... }

{ihu eq, my_int, 10
    :yosoro 1
}

For 循环: {hor ...}

hor 语句创建一个在闭区间范围内进行迭代的循环。循环变量必须是整数。 循环结束后,循环变量的值会被设置为结束值。

# 语法: {hor <var>, <start_expr>, <end_expr> ... }
# 从 start_expr 循环到 end_expr (包含两者)。

{hor i, 1, 5
    :yosoro i  # 输出 "1 2 3 4 5 "
}

While 循环: {while ...}

while 语句在条件为真时,会持续执行一个代码块。

# 语法: {while <op>, <expr1>, <expr2> ... }

:set i, 1
{while le, i, 3
    :yosoro i  # 输出 "1 2 3 "
    :set i, i + 1
}

表达式

运算符

类型提升

在包含 intfloat 类型的表达式中,整数会自动提升为浮点数进行计算,其结果也将是 float 类型。

:yosoro (5 + 0.5)  # 输出 "5.500000 "

优先级

使用括号 () 对表达式进行分组,以强制指定运算顺序。

:yosoro (2 + 3) * 4  # 输出 "20 "

示例

下面的代码是P3372 【模板】线段树 1 的CYaRon! 语实现。

此代码也可以在github仓库的example目录下找到。

{ vars
    n:int
    m:int
    i:int
    op:int
    x:int
    y:int
    k:int
    res:int
    N:int
    logN:int
    l:int
    r:int
    l0:int
    r0:int
    p:int
    s:int
    p_l:int
    p_r:int
    cur_p:int
    cur_l:int
    cur_r:int
    a:array[int, 1..100001]
    tree:array[int, 1..530000]
    lazy:array[int, 1..530000]
    len:array[int, 1..530000]
    pow2:array[int, 0..18]
}

# --- Initialization ---
:input n
:input m

:set pow2[0], 1
{ hor i, 1, 18
    :set pow2[i], pow2[i-1] * 2
}

:set N, 1
:set logN, 0
{ while lt, N, n
    :set N, N * 2
    :set logN, logN + 1
}

:set len[1], N
:set i, 1
{ while lt, i, N
    :set len[2*i], len[i] / 2
    :set len[2*i+1], len[i] / 2
    :set i, i + 1
}

{ hor i, 1, n
    :input a[i]
}

# --- Build tree ---
{ hor i, 1, n
    :set tree[N+i-1], a[i]
}
:set i, N - 1
{ while ge, i, 1
    :set tree[i], tree[2*i] + tree[2*i+1]
    :set i, i - 1
}

# --- Main loop for m operations ---
{ hor i, 1, m
    :input op
    { ihu eq, op, 1 # Update
        :input x
        :input y
        :input k

        :set l, x + N - 1
        :set r, y + N - 1
        :set l0, l
        :set r0, r

        # Push down from roots to leaves
        :set s, logN
        { while ge, s, 1
            :set p_l, l / pow2[s]
            :set p_r, r / pow2[s]
            # push(p_l)
            { ihu neq, lazy[p_l], 0
                :set tree[2*p_l], tree[2*p_l] + lazy[p_l] * len[2*p_l]
                :set lazy[2*p_l], lazy[2*p_l] + lazy[p_l]
                :set tree[2*p_l+1], tree[2*p_l+1] + lazy[p_l] * len[2*p_l+1]
                :set lazy[2*p_l+1], lazy[2*p_l+1] + lazy[p_l]
                :set lazy[p_l], 0
            }
            # push(p_r) only if different
            { ihu neq, p_l, p_r
                { ihu neq, lazy[p_r], 0
                    :set tree[2*p_r], tree[2*p_r] + lazy[p_r] * len[2*p_r]
                    :set lazy[2*p_r], lazy[2*p_r] + lazy[p_r]
                    :set tree[2*p_r+1], tree[2*p_r+1] + lazy[p_r] * len[2*p_r+1]
                    :set lazy[2*p_r+1], lazy[2*p_r+1] + lazy[p_r]
                    :set lazy[p_r], 0
                }
            }
            :set s, s - 1
        }

        # Apply updates to interval boundaries
        :set cur_l, l
        :set cur_r, r
        { while le, cur_l, cur_r
            { ihu eq, cur_l & 1, 1 # if cur_l is a right child
                :set tree[cur_l], tree[cur_l] + k * len[cur_l]
                { ihu lt, cur_l, N # only non-leaf nodes have lazy tags
                    :set lazy[cur_l], lazy[cur_l] + k
                }
                :set cur_l, cur_l + 1
            }
            { ihu eq, cur_r & 1, 0 # if cur_r is a left child
                :set tree[cur_r], tree[cur_r] + k * len[cur_r]
                { ihu lt, cur_r, N
                    :set lazy[cur_r], lazy[cur_r] + k
                }
                :set cur_r, cur_r - 1
            }

            :set cur_l, cur_l / 2
            :set cur_r, cur_r / 2
        }

        # Pull up sums from leaves to roots
        :set cur_p, l0 / 2
        { while ge, cur_p, 1
            :set tree[cur_p], tree[2*cur_p] + tree[2*cur_p+1] + lazy[cur_p] * len[cur_p]
            :set cur_p, cur_p / 2
        }
        :set cur_p, r0 / 2
        { while ge, cur_p, 1
            :set tree[cur_p], tree[2*cur_p] + tree[2*cur_p+1] + lazy[cur_p] * len[cur_p]
            :set cur_p, cur_p / 2
        }
    }
    { ihu eq, op, 2 # Query
        :input x
        :input y

        :set l, x + N - 1
        :set r, y + N - 1
        :set res, 0

        # Push down from roots to leaves
        :set s, logN
        { while ge, s, 1
            :set p_l, l / pow2[s]
            :set p_r, r / pow2[s]
            # push(p_l)
            { ihu neq, lazy[p_l], 0
                :set tree[2*p_l], tree[2*p_l] + lazy[p_l] * len[2*p_l]
                :set lazy[2*p_l], lazy[2*p_l] + lazy[p_l]
                :set tree[2*p_l+1], tree[2*p_l+1] + lazy[p_l] * len[2*p_l+1]
                :set lazy[2*p_l+1], lazy[2*p_l+1] + lazy[p_l]
                :set lazy[p_l], 0
            }
            # push(p_r) only if different
            { ihu neq, p_l, p_r
                { ihu neq, lazy[p_r], 0
                    :set tree[2*p_r], tree[2*p_r] + lazy[p_r] * len[2*p_r]
                    :set lazy[2*p_r], lazy[2*p_r] + lazy[p_r]
                    :set tree[2*p_r+1], tree[2*p_r+1] + lazy[p_r] * len[2*p_r+1]
                    :set lazy[2*p_r+1], lazy[2*p_r+1] + lazy[p_r]
                    :set lazy[p_r], 0
                }
            }
            :set s, s - 1
        }

        # Query intervals
        :set cur_l, l
        :set cur_r, r
        { while le, cur_l, cur_r
            { ihu eq, cur_l & 1, 1 # if cur_l is a right child
                :set res, res + tree[cur_l]
                :set cur_l, cur_l + 1
            }
            { ihu eq, cur_r & 1, 0 # if cur_r is a left child
                :set res, res + tree[cur_r]
                :set cur_r, cur_r - 1
            }

            :set cur_l, cur_l / 2
            :set cur_r, cur_r / 2
        }
        :yosoro res
    }
}