ton fift设计原理与实现(一)

系列文章目录


前言

本系列着重讲解fift代码的实现,fift代码基于c++设计,可以帮助更多小伙伴学习和回顾c++代码。

代码片段

/*
    This file is part of TON Blockchain source code.

    TON Blockchain is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    TON Blockchain is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with TON Blockchain.  If not, see <http://www.gnu.org/licenses/>.

    In addition, as a special exception, the copyright holders give permission
    to link the code of portions of this program with the OpenSSL library.
    You must obey the GNU General Public License in all respects for all
    of the code used other than OpenSSL. If you modify file(s) with this
    exception, you may extend this exception to your version of the file(s),
    but you are not obligated to do so. If you do not wish to do so, delete this
    exception statement from your version. If you delete this exception statement
    from all source files in the program, then also delete it here.

    Copyright 2017-2020 Telegram Systems LLP
*/
#include "vm/stack.hpp"
#include <cassert>
#include <algorithm>
#include <string>
#include <vector>
#include <iostream>
#include <sstream>
#include <fstream>
#include <memory>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <map>
#include <functional>
#include <getopt.h>

#include "Fift.h"
#include "Dictionary.h"
#include "SourceLookup.h"
#include "words.h"

#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/Parser.h"
#include "td/utils/port/path.h"

#include "git.h"

void usage(const char* progname) {
  std::cerr << "A simple Fift interpreter. Type `bye` to quit, or `words` to get a list of all commands\n";
  std::cerr
      << "usage: " << progname
      << " [-i] [-n] [-I <source-include-path>] {-L <library-fif-file>} <source-file1-fif> <source-file2-fif> ...\n";
  std::cerr << "\t-n\tDo not preload standard preamble file `Fift.fif`\n"
               "\t-i\tForce interactive mode even if explicit source file names are indicated\n"
               "\t-I<source-search-path>\tSets colon-separated (unix) or at-separated (windows) library source include path. If not indicated, "
               "$FIFTPATH is used instead.\n"
               "\t-L<library-fif-file>\tPre-loads a library source file\n"
               "\t-s\tScript mode: use first argument as a fift source file and import remaining arguments as $n)\n"
               "\t-v<verbosity-level>\tSet verbosity level\n"
               "\t-V<version>\tShow fift build information\n";
  std::exit(2);
}

void parse_include_path_set(std::string include_path_set, std::vector<std::string>& res) {
  td::Parser parser(include_path_set);
  while (!parser.empty()) {
    #if TD_WINDOWS
    auto path_separator = '@';
    #else
    auto path_separator = ':';
    #endif
    auto path = parser.read_till_nofail(path_separator);
    if (!path.empty()) {
      res.push_back(path.str());
    }
    parser.skip_nofail(path_separator);
  }
}

int main(int argc, char* const argv[]) {
  bool interactive = false;
  bool fift_preload = true, no_env = false;
  bool script_mode = false;
  std::vector<std::string> library_source_files, source_list;
  std::vector<std::string> source_include_path;

  fift::Fift::Config config;

  int i;
  int new_verbosity_level = VERBOSITY_NAME(INFO);
  while (!script_mode && (i = getopt(argc, argv, "hinI:L:sv:V")) != -1) {
    switch (i) {
      case 'i':
        interactive = true;
        break;
      case 'n':
        fift_preload = false;
        break;
      case 'I':
        parse_include_path_set(optarg, source_include_path);
        no_env = true;
        break;
      case 'L':
        library_source_files.emplace_back(optarg);
        break;
      case 's':
        script_mode = true;
        break;
      case 'v':
        new_verbosity_level = VERBOSITY_NAME(FATAL) + td::to_integer<int>(td::Slice(optarg));
        break;
      case 'V':
        std::cout << "Fift build information: [ Commit: " << GitMetadata::CommitSHA1() << ", Date: " << GitMetadata::CommitDate() << "]\n";
        std::exit(0);
        break;

      case 'h':
      default:
        usage(argv[0]);
    }
  }
  SET_VERBOSITY_LEVEL(new_verbosity_level);

  while (optind < argc) {
    source_list.emplace_back(argv[optind++]);
    if (script_mode) {
      break;
    }
  }

  if (!no_env) {
    const char* path = std::getenv("FIFTPATH");
    parse_include_path_set(path ? path : "/usr/lib/fift", source_include_path);
  }
  std::string current_dir;
  auto r_current_dir = td::realpath(".");
  if (r_current_dir.is_ok()) {
    current_dir = r_current_dir.move_as_ok();
    source_include_path.push_back(current_dir);
  }
  config.source_lookup = fift::SourceLookup(std::make_unique<fift::OsFileLoader>());
  for (auto& path : source_include_path) {
    config.source_lookup.add_include_path(path);
  }

  fift::init_words_common(config.dictionary);
  fift::init_words_vm(config.dictionary, true);  // enable vm debug
  fift::init_words_ton(config.dictionary);

  if (script_mode) {
    fift::import_cmdline_args(config.dictionary, source_list.empty() ? "" : source_list[0], argc - optind,
                              argv + optind);
  }

  fift::Fift fift(std::move(config));

  if (fift_preload) {
    auto status = fift.interpret_file("Fift.fif", "");
    if (status.is_error()) {
      LOG(ERROR) << "Error interpreting standard preamble file `Fift.fif`: " << status.error().message()
                 << "\nCheck that correct include path is set by -I or by FIFTPATH environment variable, or disable "
                    "standard preamble by -n.\n";
      std::exit(2);
    }
  }

  for (auto source : library_source_files) {
    auto status = fift.interpret_file(source, "");
    if (status.is_error()) {
      LOG(ERROR) << "Error interpreting preloaded file `" << source << "`: " << status.error().message();
      std::exit(2);
    }
  }

  if (source_list.empty()) {
    interactive = true;
  }
  for (const auto& source : source_list) {
    bool is_stdin = (source.empty() || source == "-");
    auto status =
        !is_stdin ? fift.interpret_file(source, current_dir) : fift.interpret_istream(std::cin, current_dir, false);
    if (status.is_error()) {
      if (!is_stdin) {
        LOG(ERROR) << "Error interpreting file `" << source << "`: " << status.error().message();
      } else {
        LOG(ERROR) << "Error interpreting stdin: " << status.error().message();
      }
      std::exit(2);
    }
    auto res = status.move_as_ok();
    if (res) {
      std::exit(~res);
    }
  }
  if (interactive) {
    auto status = fift.interpret_istream(std::cin, current_dir);
    if (status.is_error()) {
      LOG(ERROR) << status.error().message();
      std::exit(2);
    } else {
      int res = status.move_as_ok();
      if (res) {
        std::exit(~res);
      }
    }
  }
}

学习内容

1. 函数usage

这个函数打印出程序的使用说明,包括支持的命令行参数和它们的描述。它使用了std::cerr来输出错误信息或使用说明。

2. 函数parse_include_path_set

这个函数用于解析包含多个路径的字符串,并将这些路径分割出来存储到一个std::vector<std::string>类型的容器中。它使用了平台相关的路径分隔符,Windows下是'@',其他平台是':'

3. main函数

main函数是程序的入口点,它执行以下步骤:

a. 变量声明

声明了一些布尔变量和字符串向量来存储命令行参数的解析结果。

b. 命令行参数解析

使用getopt函数来解析命令行参数。getopt是一个用于解析命令行参数的函数,它支持短选项(如-i)和长选项(如--version)。这个函数会根据提供的选项字符串(如"hinI:L:sv:V")来识别和处理参数。

  • -i:强制进入交互模式。
  • -n:不预加载标准前缀文件Fift.fif
  • -I:设置源搜索路径。
  • -L:预加载库源文件。
  • -s:脚本模式,使用第一个参数作为Fift源文件,并将剩余参数导入为$n
  • -v:设置详细级别。
  • -V:显示Fift构建信息。
c. 环境变量处理

如果用户没有通过-I选项指定源搜索路径,程序会尝试从环境变量FIFTPATH中获取。

d. 配置解释器

设置解释器的配置,包括源文件查找器和包含路径。

e. 初始化Fift字典

初始化Fift字典,包括公共单词、VM单词和TON单词。

f. 执行脚本

根据命令行参数,执行预加载的库文件和用户提供的源文件。

g. 交互模式

如果没有提供源文件,程序将进入交互模式,允许用户在命令行中输入Fift命令。

学习重点

  1. 命令行参数解析:学习如何使用getopt或类似的库来解析命令行参数。
  2. 字符串处理:了解如何使用字符串和向量来处理和存储数据。
  3. 条件编译:学习如何使用预处理器指令(如#if TD_WINDOWS)来处理不同平台的特定代码。
  4. 文件和目录操作:了解如何使用realpath函数来获取当前目录的绝对路径。
  5. 异常处理:学习如何使用std::exit来处理程序的异常退出。
  6. 面向对象编程:理解如何使用类和对象来封装数据和行为,例如fift::Fift类的使用。
  7. 函数式编程元素:了解如何使用函数对象(如std::vectoremplace_back方法)来简化代码。

通过学习这些概念,可以更好地理解C++程序的设计和实现,以及如何编写健壮、可维护的代码。

猜你喜欢

转载自blog.csdn.net/zhuqiyua/article/details/142929064