贡献

我们欢迎任何人的贡献,即使你是开源新手。起初为编译器做贡献可能听起来令人生畏,但可以尝试去做,这并不复杂。我们将帮助你解决任何技术问题并帮助改进你的贡献,以便将其合并。

基本设置

要做出贡献,请确保你的设置:

  • 你的用户名 + 电子邮件

  • 你的 ~/.gitconfig

  • 你的 shell 提示显示当前分支名称

拉取分叉 LFortran

步骤 1. 创建 项目存储库 的分支

步骤 2. 使用 GitHub 设置你的 SSH 密钥

步骤 3. 从 GitHub 克隆项目存储库并设置你的远程存储库

git clone https://github.com/lfortran/lfortran.git
cd lfortran
git remote add REMOTE_NAME git@github.com:YOUR_GITHUB_ID/lfortran.git

:fontawesome-solid-edit: REMOTE_NAME 是你的远程仓库的名字,可以是你喜欢的任何名字,例如你的名字。

:fontawesome-solid-edit: YOUR_GITHUB_ID 是你在 GitHub 上的用户 ID,应该是你帐户路径的一部分。

你可以使用 git remote -v 检查新远程地址是否设置正确。

发送新的合并请求

步骤 1. 创建一个新分支

git checkout -b fix1

步骤 2. 更改相关文件

步骤 3. 提交更改:

git add FILE1 (FILE2 ...)
git commit -m "YOUR_COMMIT_MESSAGE"

这里 是一些关于编写好的提交消息的好技巧。

第 4 步:检查以确保你的更改看起来不错

git log --pretty=oneline --graph --decorate --all

步骤 5. 发送合并请求

git push REMOTE_NAME fix1

该命令会将新分支 fix1 推送到您之前创建的远程存储库 REMOTE_NAME 中。此外,它还将显示一个链接,你可以单击该链接以打开新的合并请求。单击链接后,写下标题和简明描述,然后单击 创建 按钮。是的,你现在一切就绪。

添加新功能

下面的示例显示了创建插入符号二元运算符 ^ 所需的步骤,该运算符计算两个操作数的平均值。

创建新令牌

我们扩展了 tokenizer.reparser.yy 以添加新的令牌 ^。我们还告诉 LFortran 如何在 parser.cpp 中打印新标记。

:fontawesome-solid-code: src/lfortran/parser/tokenizer.re

// "^" { RET(TK_CARET) }

:fontawesome-solid-code: src/lfortran/parser/parser.yy

%token TK_CARET "^"

:fontawesome-solid-code: src/lfortran/parser/parser.cpp

std:string token2text(const int token)
{
    switch (token) {
        T(TK_CARET, "^")
    }
}

添加的代码使用 lfortran --show-tokens examples2/expr2.f90 进行测试

解析新令牌

现在我们必须解析 new 运算符。我们通过使用插入符运算符扩展 BinOp 并修改 AST.asdl 文件将其添加到 AST。然后我们将它添加到 parse.yy 中以正确解析并在 semantics.h 中生成新的 AST。最后我们扩展 pickle.cpp 以便新的操作符可以打印自己。

:fontawesome-solid-code:grammar/AST.asdl

operator = Add | Sub | Mul | Div | Pow | Caret

:fontawesome-solid-code:src/lfortran/parser/parser.yy

%left "^"

expr
    : id { $$=$1; }
    | expr "^" expr { $$ = CARET($1, $3, @$); }

:fontawesome-solid-code:src/lfortran/parser/semantics.h

#define CARET(x,y,l) make_BinOp_t(p.m_a, l, EXPR(x), operatorType::Caret, EXPR(y))

:fontawesome-solid-code:src/lfortran/pickle.cpp

std::string op2str(const operatorType type)
{
    switch (type) {
        case (operatorType::Caret) : return "^";
    } // now the caret operator can print itself

}

该部分使用 lfortran --show-ast examples/expr2.f90 进行测试

实现新令牌的语义

我们首先在 ASR.asdl 中扩展 ASR,并添加 ^ 作为 BinOp 运算符选项。

:fontawesome-solid-code:src/libasr/ASR.asdl

binop = Add | Sub | Mul | Div | Pow | Caret

:fontawesome-solid-code:src/lfortran/semantics/ast_common_visitor.h

namespace LFortran {
class CommonVisitorMethods {
public:
  inline static void visit_BinOp(Allocator &al, const AST::BinOp_t &x,
                                 ASR::expr_t *&left, ASR::expr_t *&right,
                                 ASR::asr_t *&asr) {
    ASR::binopType op;
    switch (x.m_op) {

    case (AST::Caret):
      op = ASR::Caret;
      break;
    }
    if (LFortran::ASRUtils::expr_value(left) != nullptr &&
        LFortran::ASRUtils::expr_value(right) != nullptr) {
      if (ASR::is_a<LFortran::ASR::Integer_t>(*dest_type)) {
        int64_t left_value = ASR::down_cast<ASR::IntegerConstant_t>(
                                 LFortran::ASRUtils::expr_value(left))
                                 ->m_n;
        int64_t right_value = ASR::down_cast<ASR::IntegerConstant_t>(
                                  LFortran::ASRUtils::expr_value(right))
                                  ->m_n;
        int64_t result;
        switch (op) {
        case (ASR::Caret):
          result = (left_value + right_value)/2;
          break;
        }
  }
}
}
}
}

然后我们通过扩展 src/lfortran/semantics/ast_common_visitor.h 将其从 AST 转换为 ASR。

我们还将它添加到由表达式触发的编译时评估中,例如在编译时评估的 e = (2+3)^5。诸如e = x^5之类的表达式仅在运行时进行评估。

该部分使用 lfortran --show-asr examples/expr2.f90 进行测试

在 LLVM 中实现新令牌

为了在 LLVM 中实现,我们通过处理 new 运算符来扩展 BinOp 转换。我们首先将两个数字相加,然后除以 2。 :fontawesome-solid-code:src/lfortran/codegen/asr_to_llvm.cpp

    void visit_BinOp(const ASR::BinOp_t &x) {
        if (x.m_value) {
            this->visit_expr_wrapper(x.m_value, true);
            return;
        }
        this->visit_expr_wrapper(x.m_left, true);
        llvm::Value *left_val = tmp;
        this->visit_expr_wrapper(x.m_right, true);
        llvm::Value *right_val = tmp;
        if (x.m_type->type == ASR::ttypeType::Integer ||
            x.m_type->type == ASR::ttypeType::IntegerPointer) {
            switch (x.m_op) {

                case ASR::binopType::Caret: {
                    tmp = builder->CreateAdd(left_val, right_val);
                    llvm::Value *two = llvm::ConstantInt::get(context,
                        llvm::APInt(32, 2, true));
                    tmp = builder->CreateUDiv(tmp, two);
                    break;
                };
}
}
}

该部分使用 lfortran --show-llvm examples/expr2.f90 进行测试

现在当 LLVM 工作时,我们可以通过以下方式测试最终的可执行文件:

lfortran examples/expr2.f90
./a.out

它应该打印 6。

它还可以交互工作:

$ lfortran
Interactive Fortran. Experimental prototype, not ready for end users.
  * Use Ctrl-D to exit
  * Use Enter to submit
  * Use Alt-Enter or Ctrl-N to make a new line
    - Editing (Keys: Left, Right, Home, End, Backspace, Delete)
    - History (Keys: Up, Down)
>>> 4^8                                                                  1,4   ]
6
>>> integer :: x                                                         1,13  ]
>>> x = 4                                                                1,6   ]
>>> x^8                                                                  1,4   ]
6

得到

如果你有任何问题或需要帮助,请通过我们的 mailinglistchat 提问。

请注意,该项目的所有参与者都应遵守我们的行为准则。参与本项目即表示你同意遵守其条款。请参阅 CODE_OF_CONDUCT.md

通过提交 PR,你同意根据 LFortran 的 BSD 许可 许可你的贡献,除非另有明确说明。