mirror of
https://github.com/RubyMetric/chsrc
synced 2025-10-09 21:33:23 +08:00
Move to tool
This commit is contained in:
1
tool/rawstr4c/.gitignore
vendored
Normal file
1
tool/rawstr4c/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
lib/.precomp
|
77
tool/rawstr4c/README.md
Normal file
77
tool/rawstr4c/README.md
Normal file
@@ -0,0 +1,77 @@
|
||||
<!-- -----------------------------------------------------------
|
||||
! SPDX-License-Identifier: GFDL-1.3-or-later
|
||||
! -------------------------------------------------------------
|
||||
! Doc Type : Markdown
|
||||
! Doc Name : (rawstr4c configuration).md
|
||||
! Doc Authors : Aoran Zeng <ccmywish@qq.com>
|
||||
! Contributors : Nul None <nul@none.org>
|
||||
! |
|
||||
! Created On : <2025-07-12>
|
||||
! Last Modified : <2025-07-13>
|
||||
! ---------------------------------------------------------- -->
|
||||
|
||||
# rawstr4c
|
||||
|
||||
当你需要写一个复杂的C语言字符串的时候,可使用该工具
|
||||
|
||||
<br>
|
||||
|
||||
## 配置语法
|
||||
|
||||
```markdown
|
||||
|
||||
- config-item = `value`
|
||||
|
||||
- config-item2 = `:mode`
|
||||
|
||||
```
|
||||
|
||||
配置项总是以 `-` 开头,配置项名称后跟一个 `=`,右值必须使用 ``` `` ``` 来包裹
|
||||
|
||||
注意,如果值并非是由用户随意给出,而是只能是默认的几种值之一,则使用 `:` 作为前缀
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
|
||||
## 配置项
|
||||
|
||||
注意,如非特殊说明,第一项为默认值
|
||||
|
||||
- output =
|
||||
|
||||
- `:terminal` = 在终端输出
|
||||
- `:macro` = 输出为一个 `.h` 文件,定义为宏
|
||||
- `:global-variable` = 输出一个 `.h` 文件和对应的 `.c` 文件,定义为全局变量
|
||||
- `:global-variable-only-header` = 仅输出为一个 `.h` 文件,定义为全局变量
|
||||
|
||||
- output-file =
|
||||
|
||||
自定义生成的头文件名,默认值为 `rawstr4c.h`
|
||||
|
||||
- translate =
|
||||
|
||||
- `:escape` = 仅转义
|
||||
- `:oct` = 八进制
|
||||
- `:hex` = 十六进制
|
||||
|
||||
- postfix =
|
||||
|
||||
- `:use-language` = 使用 code block 的语言
|
||||
- `scratch string` = 使用某一自定义字符串为后缀
|
||||
|
||||
- name =
|
||||
|
||||
生成的变量名,默认将会包含前缀和后缀
|
||||
|
||||
- name-literally = `false` | `true`
|
||||
|
||||
无视其他配置项,直接使用 `name` 作为变量名
|
||||
|
||||
- keep-prefix = `true` | `false`
|
||||
|
||||
变量名是否使用前缀
|
||||
|
||||
- keep-postfix = `true` | `false`
|
||||
|
||||
变量名是否使用后缀
|
22
tool/rawstr4c/bin/run.bat
Normal file
22
tool/rawstr4c/bin/run.bat
Normal file
@@ -0,0 +1,22 @@
|
||||
:: ---------------------------------------------------------------
|
||||
:: File Name : run.bat
|
||||
:: File Authors : Aoran Zeng <ccmywish@qq.com>
|
||||
:: Contributors : Nul None <nul@none.org>
|
||||
:: Created On : <2025-07-13>
|
||||
:: Last Modified : <2025-07-13>
|
||||
::
|
||||
:: Run rawstr4c.raku script
|
||||
::
|
||||
:: Usage:
|
||||
::
|
||||
:: 用户必须在代码根目录中运行此文件
|
||||
::
|
||||
:: %PREFIX%\bin\run.bat [--debug] <Markdown.md>
|
||||
::
|
||||
:: %PREFIX%\bin\run.bat [--debug] <Dir>
|
||||
:: ---------------------------------------------------------------
|
||||
|
||||
@echo off
|
||||
set PREFIX=tool\rawstr4c
|
||||
REM -I 选项必须在文件前面
|
||||
raku -I %PREFIX%\lib %PREFIX%\rawstr4c.raku %*
|
22
tool/rawstr4c/bin/run.ps1
Normal file
22
tool/rawstr4c/bin/run.ps1
Normal file
@@ -0,0 +1,22 @@
|
||||
# ---------------------------------------------------------------
|
||||
# File Name : run.ps1
|
||||
# File Authors : Aoran Zeng <ccmywish@qq.com>
|
||||
# Contributors : Nul None <nul@none.org>
|
||||
# Created On : <2025-07-12>
|
||||
# Last Modified : <2025-07-13>
|
||||
#
|
||||
# Run rawstr4c.raku script
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# 用户必须在代码根目录中运行此文件
|
||||
#
|
||||
# $PREFIX\bin\run.ps1 [--debug] <Markdown.md>
|
||||
#
|
||||
# $PREFIX\bin\run.ps1 [--debug] <Dir>
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
$PREFIX = "tool/rawstr4c"
|
||||
|
||||
# -I 选项必须在文件前面
|
||||
raku -I $PREFIX\lib $PREFIX\rawstr4c.raku $args
|
23
tool/rawstr4c/bin/run.sh
Normal file
23
tool/rawstr4c/bin/run.sh
Normal file
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
# ---------------------------------------------------------------
|
||||
# File Name : run.sh
|
||||
# File Authors : Aoran Zeng <ccmywish@qq.com>
|
||||
# Contributors : Nul None <nul@none.org>
|
||||
# Created On : <2025-07-13>
|
||||
# Last Modified : <2025-07-13>
|
||||
#
|
||||
# Run rawstr4c.raku script
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# 用户必须在代码根目录中运行此文件!
|
||||
#
|
||||
# $PREFIX/bin/run.sh [--debug] <Markdown.md>
|
||||
#
|
||||
# $PREFIX/bin/run.sh [--debug] <Dir>
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
PREFIX="tool/rawstr4c"
|
||||
|
||||
# -I 选项必须在文件前面
|
||||
raku -I $PREFIX/lib $PREFIX/rawstr4c.raku "$@"
|
267
tool/rawstr4c/lib/Generator.rakumod
Normal file
267
tool/rawstr4c/lib/Generator.rakumod
Normal file
@@ -0,0 +1,267 @@
|
||||
#!/usr/bin/env raku
|
||||
# ---------------------------------------------------------------
|
||||
# File Name : Generator.rakumod
|
||||
# File Authors : Aoran Zeng <ccmywish@qq.com>
|
||||
# Contributors : Nul None <nul@none.org>
|
||||
# Created On : <2025-07-12>
|
||||
# Last Modified : <2025-07-13>
|
||||
#
|
||||
# Generates C code from raw string
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
unit module Generator;
|
||||
|
||||
use Parser;
|
||||
|
||||
my class CStringConverter {
|
||||
|
||||
method convert-char($char, $mode) {
|
||||
given $mode {
|
||||
when 'oct' {
|
||||
my $bytes = $char.encode('UTF-8');
|
||||
return $bytes.map({ "\\" ~ sprintf("%03o", $_) }).join('');
|
||||
}
|
||||
when 'hex' {
|
||||
my $bytes = $char.encode('UTF-8');
|
||||
return $bytes.map({ "\\x" ~ sprintf("%02x", $_) }).join('');
|
||||
}
|
||||
when 'escape' {
|
||||
given $char {
|
||||
when '"' { return '\\"'; }
|
||||
when "'" { return "\\'"; }
|
||||
when '\\' { return '\\\\'; }
|
||||
when "\n" { return '\\n'; }
|
||||
when "\t" { return '\\t'; }
|
||||
when "\r" { return '\\r'; }
|
||||
when "\0" { return '\\0'; }
|
||||
default { return $char; }
|
||||
}
|
||||
}
|
||||
default { die "Unknown translation mode: $mode"; }
|
||||
}
|
||||
}
|
||||
|
||||
method convert-string($content, $mode) {
|
||||
my $result = "";
|
||||
for $content.comb -> $char {
|
||||
$result ~= self.convert-char($char, $mode);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
my class CVariableNameGenerator {
|
||||
|
||||
method generate($global-config, $section-config, $title) {
|
||||
|
||||
# 检查 name-literally
|
||||
my $name-literally = $section-config.get('name-literally');
|
||||
if $name-literally.is-bool() && $name-literally.as-bool() {
|
||||
return $section-config.get('name', $title.lc).as-string();
|
||||
}
|
||||
|
||||
my $prefix = $global-config.get('prefix', '_rawstr4c').as-string();
|
||||
my $language = $section-config.get('language').as-string();
|
||||
my $postfix = self.resolve-postfix($global-config, $language);
|
||||
|
||||
my $keep-prefix = $section-config.get('keep-prefix', 'true').as-bool();
|
||||
my $keep-postfix = $section-config.get('keep-postfix', 'true').as-bool();
|
||||
|
||||
my $name = $section-config.get('name', $title.lc).as-string();
|
||||
$name = $name.subst(/\s+/, '_', :g);
|
||||
|
||||
# 组装变量名
|
||||
my $var-name = "";
|
||||
$var-name ~= $prefix if $keep-prefix && $prefix;
|
||||
$var-name ~= "_" if $var-name && $name;
|
||||
$var-name ~= $name if $name;
|
||||
$var-name ~= "_" if $var-name && $postfix && $keep-postfix;
|
||||
$var-name ~= $postfix if $postfix && $keep-postfix;
|
||||
|
||||
return $var-name || "unnamed_var";
|
||||
}
|
||||
|
||||
method resolve-postfix($global-config, $language) {
|
||||
my $postfix = $global-config.get('postfix');
|
||||
|
||||
if $postfix.is-mode() && $postfix.as-mode() eq 'use-language' {
|
||||
return $language ?? 'in_' ~ $language !! '';
|
||||
}
|
||||
# 如果不是模式,那就是用户给了一个具体的字符串
|
||||
return $postfix.as-string();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#| 生成 .h 文件或/和 .c 文件,或存储到 @variables 中
|
||||
my class CVariableGenerator {
|
||||
has @.variables;
|
||||
has $.c-header-filename;
|
||||
|
||||
method new() {
|
||||
|
||||
my $c-header-filename = "rawstr4c.h";
|
||||
|
||||
self.bless(:$c-header-filename, :variables([]));
|
||||
}
|
||||
|
||||
method add-variable($name, $content, $type) {
|
||||
@.variables.push: {
|
||||
name => $name,
|
||||
content => $content,
|
||||
type => $type
|
||||
};
|
||||
}
|
||||
|
||||
method generate-c-header-file($output-mode = 'global-variable') {
|
||||
my $header = qq:to/EOF/;
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Generated by rawstr4c
|
||||
*
|
||||
* Date: {DateTime.now.Str}
|
||||
*/
|
||||
|
||||
EOF
|
||||
|
||||
for @.variables -> $var {
|
||||
given $var<type> {
|
||||
when 'global-variable' {
|
||||
if $output-mode eq 'global-variable-only-header' {
|
||||
$header ~= "char {$var<name>}[] = \"{$var<content>}\";\n";
|
||||
} else {
|
||||
$header ~= "extern char {$var<name>}[];\n";
|
||||
}
|
||||
}
|
||||
when 'macro' {
|
||||
$header ~= "#define {$var<name>.uc} \"{$var<content>}\"\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
method generate-c-source-file() {
|
||||
my $source = qq:to/EOF/;
|
||||
#include "{$.c-header-filename}"
|
||||
|
||||
/**
|
||||
* Generated by rawstr4c
|
||||
*
|
||||
* Date: {DateTime.now.Str}
|
||||
*/
|
||||
|
||||
EOF
|
||||
|
||||
for @.variables -> $var {
|
||||
if $var<type> eq 'global-variable' {
|
||||
$source ~= "char {$var<name>}[] = \"{$var<content>}\";\n";
|
||||
}
|
||||
}
|
||||
|
||||
return $source;
|
||||
}
|
||||
|
||||
method save-files($output-mode) {
|
||||
$.c-header-filename.IO.spurt(self.generate-c-header-file($output-mode));
|
||||
say "Generated C header file: $.c-header-filename";
|
||||
|
||||
if $output-mode eq 'global-variable' {
|
||||
my $has-globals = @.variables.grep({ $_<type> eq 'global-variable' }).elems > 0;
|
||||
if $has-globals {
|
||||
my $c-source-filename = $.c-header-filename.subst(/'.h'$/, '.c');
|
||||
$c-source-filename.IO.spurt(self.generate-c-source-file());
|
||||
say "Generated C source file: $c-source-filename";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Generator {
|
||||
has $.cstring-converter;
|
||||
has $.varname-generator;
|
||||
has $.variable-generator;
|
||||
|
||||
method new() {
|
||||
self.bless(
|
||||
:cstring-converter(CStringConverter.new),
|
||||
:varname-generator(CVariableNameGenerator.new),
|
||||
:variable-generator(CVariableGenerator.new)
|
||||
);
|
||||
}
|
||||
|
||||
method get-config-value($global-config, $section-config, $key, $default = '') {
|
||||
return $section-config.exist($key) ??
|
||||
$section-config.get($key) !!
|
||||
$global-config.get($key, $default);
|
||||
}
|
||||
|
||||
|
||||
method generate-for-section($global-config, $section) {
|
||||
my $section-config = $section<config>;
|
||||
my $title = $section<title>;
|
||||
my $code = $section<raw-string>;
|
||||
my $debug-parser = $global-config.get('debug', False).as-bool();
|
||||
|
||||
return unless $code;
|
||||
|
||||
say "=== Section: $title ===";
|
||||
|
||||
my $translate-mode = self.get-config-value(
|
||||
$global-config, $section-config, 'translate', ':escape'
|
||||
).as-mode();
|
||||
|
||||
my $var-name = $.varname-generator.generate($global-config, $section-config, $title);
|
||||
|
||||
my $output-mode = $global-config.get('output', ':terminal').as-mode();
|
||||
|
||||
if $debug-parser {
|
||||
say "Variable name: $var-name";
|
||||
say "Translation mode: $translate-mode";
|
||||
say "Output mode: $output-mode";
|
||||
|
||||
my $language = $section-config.get('language', 'None').as-string();
|
||||
say "Language: $language";
|
||||
say '';
|
||||
}
|
||||
|
||||
my $c-string = $.cstring-converter.convert-string($code, $translate-mode);
|
||||
|
||||
given $output-mode {
|
||||
when 'terminal' {
|
||||
say 'char ' ~ $var-name ~ '[] = "' ~ $c-string ~ '";';
|
||||
}
|
||||
when 'global-variable' | 'global-variable-only-header' {
|
||||
$.variable-generator.add-variable($var-name, $c-string, 'global-variable');
|
||||
}
|
||||
when 'macro' {
|
||||
$.variable-generator.add-variable($var-name, $c-string, 'macro');
|
||||
}
|
||||
default {
|
||||
die "Illegal output mode: $output-mode";
|
||||
}
|
||||
}
|
||||
say "\n";
|
||||
}
|
||||
|
||||
|
||||
method generate($parser) {
|
||||
my $global-config = $parser.global-config;
|
||||
|
||||
# 这个 generate-for-section() 要么把变量输出到终端,要么累计到 @variabels 中
|
||||
for $parser.sections -> $section {
|
||||
self.generate-for-section($global-config, $section);
|
||||
}
|
||||
|
||||
my $output-mode = $global-config.get('output', ':terminal').as-mode();
|
||||
|
||||
# 最后把累计到 @variables 的内容输出到文件
|
||||
if $output-mode ne 'terminal' {
|
||||
$.variable-generator.save-files($output-mode);
|
||||
}
|
||||
}
|
||||
}
|
246
tool/rawstr4c/lib/Parser.rakumod
Normal file
246
tool/rawstr4c/lib/Parser.rakumod
Normal file
@@ -0,0 +1,246 @@
|
||||
#!/usr/bin/env raku
|
||||
# ---------------------------------------------------------------
|
||||
# File Name : Parser.rakumod
|
||||
# File Authors : Aoran Zeng <ccmywish@qq.com>
|
||||
# Contributors : Nul None <nul@none.org>
|
||||
# Created On : <2025-07-12>
|
||||
# Last Modified : <2025-07-13>
|
||||
#
|
||||
# rawstr4c.md parsing
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
unit module Parser;
|
||||
|
||||
#| 不能用 Bool,只能用 Boolean
|
||||
my enum ConfigValueType <String Mode Boolean>;
|
||||
|
||||
#| 配置项的值
|
||||
my class ConfigValue {
|
||||
has ConfigValueType $.type;
|
||||
has $.raw-value;
|
||||
has $.parsed-value;
|
||||
|
||||
method new($raw-text) {
|
||||
my $type;
|
||||
my $parsed;
|
||||
|
||||
given $raw-text {
|
||||
when /^ ':' (.+) $/ {
|
||||
# 模式值 :mode
|
||||
$type = Mode;
|
||||
$parsed = ~$0;
|
||||
}
|
||||
when /^ ('true'|'false'|'yes'|'no') $/ {
|
||||
# 特殊字面量 - true/false/yes/no 都是 literal
|
||||
$type = Boolean;
|
||||
$parsed = ~$0 ~~ /^('true'|'yes')$/ ?? True !! False;
|
||||
}
|
||||
default {
|
||||
# 普通字符串
|
||||
$type = String;
|
||||
$parsed = $raw-text;
|
||||
}
|
||||
}
|
||||
|
||||
self.bless(:$type, :raw-value($raw-text), :parsed-value($parsed));
|
||||
}
|
||||
|
||||
method as-string() {
|
||||
return $.parsed-value.Str;
|
||||
}
|
||||
|
||||
method as-bool() {
|
||||
given $.type {
|
||||
when Boolean { return $.parsed-value; }
|
||||
when String {
|
||||
# 尝试将字符串解析为布尔值
|
||||
return $.parsed-value ~~ /^('true'|'yes')$/;
|
||||
}
|
||||
default { return False; }
|
||||
}
|
||||
}
|
||||
|
||||
# 获取模式值(去掉冒号前缀)
|
||||
method as-mode() {
|
||||
return $.type == Mode ?? $.parsed-value !! $.raw-value;
|
||||
}
|
||||
|
||||
# 类型检查方法
|
||||
method is-mode() { return $.type == Mode; }
|
||||
method is-bool() { return $.type == Boolean; }
|
||||
method is-string() { return $.type == String; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
#| 包含所有 config items 的容器
|
||||
my class Config {
|
||||
|
||||
has %.items;
|
||||
|
||||
# 如果非要在程序内部中调用,而不是直接从 Markdown 文件中读取出来
|
||||
# 一定要记得 $raw-value 用的是 rawstr4c 的语法!也就是说,这里一定是一个字符串
|
||||
method set($k, $raw-value) {
|
||||
%.items{$k} = ConfigValue.new($raw-value);
|
||||
}
|
||||
|
||||
method get($k, $default = Nil) {
|
||||
return %.items{$k} // ($default ?? ConfigValue.new($default) !! ConfigValue.new(''));
|
||||
}
|
||||
|
||||
method exist($k) {
|
||||
return %.items{$k}:exists;
|
||||
}
|
||||
|
||||
# 配置项名称
|
||||
# @danger: 把这个函数命名为 items,会让我的机器蓝屏.....
|
||||
method keys() {
|
||||
return %.items.keys;
|
||||
}
|
||||
}
|
||||
|
||||
#|( 仅存在两个域:
|
||||
|
||||
1. Global dom
|
||||
2. Section dom
|
||||
|
||||
我们要求,在 Global dom 里,只存在配置,不存在 code block. 而 code block 只能在 Section dom 中存在。
|
||||
|
||||
因此,Parser 解析完毕后将包含:
|
||||
- $global-config
|
||||
- @sections (多个 $section)
|
||||
|
||||
一个 $section 是 Hash,其包含:
|
||||
- title
|
||||
- level
|
||||
- raw-string
|
||||
- config
|
||||
)
|
||||
class Parser {
|
||||
has $.global-config;
|
||||
has @.sections;
|
||||
|
||||
method new() {
|
||||
self.bless(
|
||||
global-config => Config.new(),
|
||||
sections => []
|
||||
);
|
||||
}
|
||||
|
||||
# 配置项所在行 -> 解析为配置项
|
||||
method parse-config-item-line($line, $section-config) {
|
||||
# 语法: - key = `value`
|
||||
if $line ~~ /^ '-' \s* (<[a..z\-]>+) \s* '=' \s* '`' (.+?) '`' / {
|
||||
my $key = ~$0;
|
||||
my $value = ~$1;
|
||||
$section-config.set($key, $value);
|
||||
return True;
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
method parse($content) {
|
||||
my @lines = $content.lines;
|
||||
|
||||
my $current-section;
|
||||
my $current-section-config = Config.new();
|
||||
my $in-global = True;
|
||||
my $in-code-block = False;
|
||||
|
||||
# 在代码块中的 raw string
|
||||
my $rawstr = "";
|
||||
|
||||
|
||||
# 开始遍历
|
||||
for @lines -> $line {
|
||||
|
||||
# Step1: 记录层次 level
|
||||
#
|
||||
# @note 我们要避免,在代码块中也有 # 字符,比如在代码块里写的是 shell 脚本
|
||||
if !$in-code-block && $line ~~ /^ '#' ('#'*) \s* (.+) / {
|
||||
my $level = 1 + $0.chars;
|
||||
my $title = ~$1;
|
||||
|
||||
# 只有匹配到下一个标题时,才说明前一个 section 已经结束,此时才有机会存下来
|
||||
# Global dom 里是没有 raw string 的,所以被这里的条件排除掉了
|
||||
if $rawstr && $current-section && $current-section<title> {
|
||||
$current-section<raw-string> = $rawstr;
|
||||
$current-section<config> = $current-section-config;
|
||||
@.sections.push: $current-section;
|
||||
}
|
||||
|
||||
$rawstr = "";
|
||||
|
||||
if $level == 1 {
|
||||
$in-global = True;
|
||||
$current-section = {};
|
||||
$current-section-config = Config.new();
|
||||
} else {
|
||||
$in-global = False;
|
||||
$current-section = {
|
||||
title => $title,
|
||||
level => $level,
|
||||
};
|
||||
$current-section-config = Config.new();
|
||||
}
|
||||
next;
|
||||
}
|
||||
|
||||
# Step2: 处理配置项
|
||||
if $in-global {
|
||||
if self.parse-config-item-line($line, $.global-config) {
|
||||
next;
|
||||
}
|
||||
} elsif $current-section {
|
||||
if self.parse-config-item-line($line, $current-section-config) {
|
||||
next;
|
||||
}
|
||||
}
|
||||
|
||||
# Step3: 开始处理raw string
|
||||
if $line ~~ /^ '```' (.*)? / {
|
||||
if $in-code-block {
|
||||
$in-code-block = False;
|
||||
} else {
|
||||
$in-code-block = True;
|
||||
my $lang = ~($0 // '');
|
||||
if $lang && $current-section && !$current-section-config.exist('language') {
|
||||
$current-section-config.set('language', $lang);
|
||||
}
|
||||
}
|
||||
next;
|
||||
}
|
||||
|
||||
# 代码块里的内容统统进来
|
||||
if $in-code-block {
|
||||
$rawstr ~= $line ~ "\n";
|
||||
}
|
||||
}
|
||||
|
||||
# 遍历结束, 这意味着文件已经阅读完毕,最后一个section还没存,现在存它
|
||||
if $rawstr && $current-section && $current-section<title> {
|
||||
$current-section<raw-string> = $rawstr;
|
||||
$current-section<config> = $current-section-config;
|
||||
@.sections.push: $current-section;
|
||||
}
|
||||
}
|
||||
|
||||
# 输出 config, 包含 global config 以及 section config
|
||||
method debug() {
|
||||
say "Global config:";
|
||||
for $.global-config.keys.sort -> $item {
|
||||
my $value = $.global-config.get($item);
|
||||
say " $item = {$value.as-string} (type: {$value.type})";
|
||||
}
|
||||
say "";
|
||||
|
||||
# 设置debug标志,后续在 Generator 中根据此信息输出 section config
|
||||
$.global-config.set('debug', "true");
|
||||
|
||||
say "Found " ~ @.sections.elems ~ " sections:";
|
||||
for @.sections -> $section {
|
||||
say "Section: " ~ $section<title>;
|
||||
}
|
||||
say "";
|
||||
}
|
||||
}
|
49
tool/rawstr4c/rawstr4c.raku
Normal file
49
tool/rawstr4c/rawstr4c.raku
Normal file
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env raku
|
||||
# ---------------------------------------------------------------
|
||||
# File Name : rawstr4c.raku
|
||||
# File Authors : Aoran Zeng <ccmywish@qq.com>
|
||||
# Contributors : Nul None <nul@none.org>
|
||||
# Created On : <2025-07-12>
|
||||
# Last Modified : <2025-07-13>
|
||||
#
|
||||
# rawstr4c:
|
||||
#
|
||||
# Raw strings for C programming language
|
||||
#
|
||||
# Usage:
|
||||
# rawstr4c <FILE.md> # 指定具体某文件名
|
||||
# rawstr4c <DIR> # 使用某一目录寻找 rawstr4c.md 文件
|
||||
#
|
||||
# 要注意的是,该程序一次性只能处理唯一一个文件
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
use Parser;
|
||||
use Generator;
|
||||
|
||||
sub MAIN(
|
||||
Str $input-path,
|
||||
Bool :$debug = False #= --debug
|
||||
) {
|
||||
my $markdown-file;
|
||||
|
||||
if $input-path.IO.d {
|
||||
$markdown-file = $input-path.IO.add("rawstr4c.md");
|
||||
unless $markdown-file.e {
|
||||
die "Error: No 'rawstr4c.md' file found in directory '$input-path'\n";
|
||||
}
|
||||
}
|
||||
elsif $input-path.IO.f {
|
||||
$markdown-file = $input-path.IO;
|
||||
} else {
|
||||
die "Error: '$input-path' is neither a file nor a directory\n";
|
||||
}
|
||||
|
||||
my $content = $markdown-file.IO.slurp;
|
||||
|
||||
my $parser = Parser::Parser.new();
|
||||
|
||||
$parser.parse($content);
|
||||
$parser.debug if $debug;
|
||||
|
||||
Generator::Generator.new.generate($parser);
|
||||
}
|
8
tool/rawstr4c/test-escape.md
Normal file
8
tool/rawstr4c/test-escape.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Test for escape mode
|
||||
|
||||
- translate = `:escape`
|
||||
|
||||
```bash
|
||||
echo "Hello World!"
|
||||
printf "Line with \"quotes\" and 'apostrophes'\n"
|
||||
```
|
7
tool/rawstr4c/test-hex.md
Normal file
7
tool/rawstr4c/test-hex.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Test for hex mode
|
||||
|
||||
- translate = `:hex`
|
||||
|
||||
```bash
|
||||
echo "Hello World!"
|
||||
```
|
Reference in New Issue
Block a user