Make rawstr4c a separate project

This commit is contained in:
Aoran Zeng 2025-07-21 10:45:56 +08:00
parent 38bafe6ed1
commit ff82bc632b
No known key found for this signature in database
GPG Key ID: 8F8BA8488E10ED98
26 changed files with 0 additions and 1853 deletions

12
.gitignore vendored
View File

@ -82,15 +82,3 @@ chsrc-dbgsym_*.ddeb
chsrc_*.build
chsrc_*.buildinfo
chsrc_*.changes
##############################
# rawstr4c
##############################
.precomp
*.tar.gz
tool/rawstr4c/test/fixture/rawstr4c.c
tool/rawstr4c/test/fixture/rawstr4c.h

View File

@ -1,7 +0,0 @@
# 2025-07-21
#
# 这个放在内部的 .gitignore 文件也是能起作用的
#
# 但是 Raku 很容易不小心生成 .precomp 目录
#
# 所以我们还是把所有 ignore 配置放在项目根目录下吧

View File

@ -1,201 +0,0 @@
The Artistic License 2.0
Copyright (c) 2000-2006, The Perl Foundation.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
This license establishes the terms under which a given free software
Package may be copied, modified, distributed, and/or redistributed.
The intent is that the Copyright Holder maintains some artistic
control over the development of that Package while still keeping the
Package available as open source and free software.
You are always permitted to make arrangements wholly outside of this
license directly with the Copyright Holder of a given Package. If the
terms of this license do not permit the full use that you propose to
make of the Package, you should contact the Copyright Holder and seek
a different licensing arrangement.
Definitions
"Copyright Holder" means the individual(s) or organization(s)
named in the copyright notice for the entire Package.
"Contributor" means any party that has contributed code or other
material to the Package, in accordance with the Copyright Holder's
procedures.
"You" and "your" means any person who would like to copy,
distribute, or modify the Package.
"Package" means the collection of files distributed by the
Copyright Holder, and derivatives of that collection and/or of
those files. A given Package may consist of either the Standard
Version, or a Modified Version.
"Distribute" means providing a copy of the Package or making it
accessible to anyone else, or in the case of a company or
organization, to others outside of your company or organization.
"Distributor Fee" means any fee that you charge for Distributing
this Package or providing support for this Package to another
party. It does not mean licensing fees.
"Standard Version" refers to the Package if it has not been
modified, or has been modified only in ways explicitly requested
by the Copyright Holder.
"Modified Version" means the Package, if it has been changed, and
such changes were not explicitly requested by the Copyright
Holder.
"Original License" means this Artistic License as Distributed with
the Standard Version of the Package, in its current version or as
it may be modified by The Perl Foundation in the future.
"Source" form means the source code, documentation source, and
configuration files for the Package.
"Compiled" form means the compiled bytecode, object code, binary,
or any other form resulting from mechanical transformation or
translation of the Source form.
Permission for Use and Modification Without Distribution
(1) You are permitted to use the Standard Version and create and use
Modified Versions for any purpose without restriction, provided that
you do not Distribute the Modified Version.
Permissions for Redistribution of the Standard Version
(2) You may Distribute verbatim copies of the Source form of the
Standard Version of this Package in any medium without restriction,
either gratis or for a Distributor Fee, provided that you duplicate
all of the original copyright notices and associated disclaimers. At
your discretion, such verbatim copies may or may not include a
Compiled form of the Package.
(3) You may apply any bug fixes, portability changes, and other
modifications made available from the Copyright Holder. The resulting
Package will still be considered the Standard Version, and as such
will be subject to the Original License.
Distribution of Modified Versions of the Package as Source
(4) You may Distribute your Modified Version as Source (either gratis
or for a Distributor Fee, and with or without a Compiled form of the
Modified Version) provided that you clearly document how it differs
from the Standard Version, including, but not limited to, documenting
any non-standard features, executables, or modules, and provided that
you do at least ONE of the following:
(a) make the Modified Version available to the Copyright Holder
of the Standard Version, under the Original License, so that the
Copyright Holder may include your modifications in the Standard
Version.
(b) ensure that installation of your Modified Version does not
prevent the user installing or running the Standard Version. In
addition, the Modified Version must bear a name that is different
from the name of the Standard Version.
(c) allow anyone who receives a copy of the Modified Version to
make the Source form of the Modified Version available to others
under
(i) the Original License or
(ii) a license that permits the licensee to freely copy,
modify and redistribute the Modified Version using the same
licensing terms that apply to the copy that the licensee
received, and requires that the Source form of the Modified
Version, and of any works derived from it, be made freely
available in that license fees are prohibited but Distributor
Fees are allowed.
Distribution of Compiled Forms of the Standard Version
or Modified Versions without the Source
(5) You may Distribute Compiled forms of the Standard Version without
the Source, provided that you include complete instructions on how to
get the Source of the Standard Version. Such instructions must be
valid at the time of your distribution. If these instructions, at any
time while you are carrying out such distribution, become invalid, you
must provide new instructions on demand or cease further distribution.
If you provide valid instructions or cease distribution within thirty
days after you become aware that the instructions are invalid, then
you do not forfeit any of your rights under this license.
(6) You may Distribute a Modified Version in Compiled form without
the Source, provided that you comply with Section 4 with respect to
the Source of the Modified Version.
Aggregating or Linking the Package
(7) You may aggregate the Package (either the Standard Version or
Modified Version) with other packages and Distribute the resulting
aggregation provided that you do not charge a licensing fee for the
Package. Distributor Fees are permitted, and licensing fees for other
components in the aggregation are permitted. The terms of this license
apply to the use and Distribution of the Standard or Modified Versions
as included in the aggregation.
(8) You are permitted to link Modified and Standard Versions with
other works, to embed the Package in a larger work of your own, or to
build stand-alone binary or bytecode versions of applications that
include the Package, and Distribute the result without restriction,
provided the result does not expose a direct interface to the Package.
Items That are Not Considered Part of a Modified Version
(9) Works (including, but not limited to, modules and scripts) that
merely extend or make use of the Package, do not, by themselves, cause
the Package to be a Modified Version. In addition, such works are not
considered parts of the Package itself, and are not subject to the
terms of this license.
General Provisions
(10) Any use, modification, and distribution of the Standard or
Modified Versions is governed by this Artistic License. By using,
modifying or distributing the Package, you accept this license. Do not
use, modify, or distribute the Package, if you do not accept this
license.
(11) If your Modified Version has been derived from a Modified
Version made by someone other than you, you are nevertheless required
to ensure that your Modified Version complies with the requirements of
this license.
(12) This license does not grant you the right to use any trademark,
service mark, tradename, or logo of the Copyright Holder.
(13) This license includes the non-exclusive, worldwide,
free-of-charge patent license to make, have made, use, offer to sell,
sell, import and otherwise transfer the Package with respect to any
patent claims licensable by the Copyright Holder that are necessarily
infringed by the Package. If you institute patent litigation
(including a cross-claim or counterclaim) against any party alleging
that the Package constitutes direct or contributory patent
infringement, then this Artistic License to you shall terminate on the
date that such litigation is filed.
(14) Disclaimer of Warranty:
THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,28 +0,0 @@
{
"name": "rawstr4c",
"version": "0.2.1",
"description": "Raw strings for the C programming language",
"authors": ["Aoran Zeng"],
"auth": "zef:ccmywish",
"license": "Artistic-2.0",
"tags": ["c"],
"support": {
"homepage": "https://github.com/RubyMetric/chsrc/blob/dev/tool/rawstr4c",
"source": "https://github.com/RubyMetric/chsrc.git",
"bugtracker": "https://github.com/RubyMetric/chsrc/issues"
},
"raku": "6.*",
"resources": [],
"depends": [],
"build-depends": [],
"test-depends": [],
"provides": {
"Rawstr4c::Parser": "lib/Rawstr4c/Parser.rakumod",
"Rawstr4c::Config": "lib/Rawstr4c/Config.rakumod",
"Rawstr4c::Generator": "lib/Rawstr4c/Generator.rakumod",
"Rawstr4c::Version": "lib/Rawstr4c/Version.rakumod"
}
}

View File

@ -1,104 +0,0 @@
<!-- -----------------------------------------------------------
! SPDX-License-Identifier: GFDL-1.3-or-later
! -------------------------------------------------------------
! Doc Type : Markdown
! Doc Name : (rawstr4c introduction).md
! Doc Authors : Aoran Zeng <ccmywish@qq.com>
! Contributors : Nul None <nul@none.org>
! |
! Created On : <2025-07-12>
! Last Modified : <2025-07-21>
! ---------------------------------------------------------- -->
# rawstr4c
Use this tool when you need to write complex C language strings.
```bash
$ rawstr4c --help
```
<br>
## Convention
A configuration file should use this order:
1. section title
2. description of the variable
3. configuration block (configblock)
4. configuration block (configblock) comments
5. code block (codeblock) (raw string)
6. comments for the content of the code block (codeblock)
<br>
## Configuration Syntax
```markdown
- config-item1 = `:mode`
- config-item2 = `true|false|yes|no`
- config-item3 = `string value`
```
Configuration items always start with `-`, followed by the configuration item name and an `=`, and the right-hand value must be wrapped with ``` `` ```.
Note: if the value is not arbitrarily given by the user, it should be set as a mode type, using `:` as a prefix.
<br>
## Configuration Items
Note: unless otherwise specified, the first item is the default value
- output =
- `:terminal` = output to terminal
- `:macro` = output as a `.h` file, defined as macro
- `:global-variable` = output a `.h` file and corresponding `.c` file, defined as global variable
- `:global-variable-only-header` = output only as a `.h` file, defined as global variable
- ~~output-file~~ = (not yet implemented)
Custom generated header filename, default value is `rawstr4c.h`
- translate =
- `:escape` = escape only
- `:oct` = octal
- `:hex` = hexadecimal
- postfix =
- `:use-language` = use the language of the codeblock
- `your string` = use a custom string as suffix
- name =
Generated variable name, will include prefix and suffix by default. If this configuration item is not given, the section title will be used
- name-literally = `false` | `true`
Ignore other configuration items and directly use `name` as the variable name
- namespace =
Will serve as a prefix after `prefix` and before variable name `name`, affecting the next level section
- keep-prefix = `true` | `false`
Whether the variable name uses prefix
- keep-postfix = `true` | `false`
Whether the variable name uses postfix

View File

@ -1,104 +0,0 @@
#!/usr/bin/env raku
# ---------------------------------------------------------------
# Copyright © 2025-2025 Aoran Zeng
# SPDX-License-Identifier: Artistic-2.0
# ---------------------------------------------------------------
# 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-16>
#
# rawstr4c:
#
# Raw strings for the C programming language
# ---------------------------------------------------------------
use Rawstr4c::Parser;
use Rawstr4c::Generator;
use Rawstr4c::Version;
sub USAGE() {
print qq:to/END/;
rawstr4c: Raw Strings for C (Artistic-2.0) v{Rawstr4c::Version::VERSION}-{Rawstr4c::Version::RELEASE_DATE}
Usage: rawstr4c [options] <FILE.md|DIR>
Arguments:
FILE.md Process a specific markdown file
DIR Process rawstr4c.md file in the given directory
Options:
-d|--debug Show debug information during processing
Value can be [generator|parser]. Default to generator.
-v|--version Show version information
-h|--help Show this help message
END
}
sub MAIN(
Str $input-path?,
# 如果是 Str 类型,则 --debug 缺少命令行参数
# 如果是 Any 类型,则可以直接使用 --debug值为 True
Any :$debug,
Any :$version,
:$d, :$v, :$h
)
{
if ($version || $v) {
print Rawstr4c::Version::VERSION_CONTENT_FOR_-version;
exit(0);
}
if ($h) {
USAGE;
exit(0);
}
if (!$input-path) {
USAGE;
exit(0);
}
my $markdown-file;
if $input-path.IO.d {
$markdown-file = $input-path.IO.add("rawstr4c.md");
unless $markdown-file.e {
# 也可以 warn
note "Error: No 'rawstr4c.md' file found in directory '$input-path'";
exit(1);
}
}
elsif $input-path.IO.f {
$markdown-file = $input-path.IO;
} else {
note "Error: '$input-path' is neither a file nor a directory";
exit(1);
}
my $parser = Rawstr4c::Parser::Parser.new($markdown-file.Str);
$parser.parse;
my $generator = Rawstr4c::Generator::Generator.new($parser);
if ($debug.defined) {
given $debug {
when 'parser' {$parser.debug;}
default {$generator.debug;}
}
}
if ($d.defined) {
given $d {
when 'parser' {$parser.debug;}
default {$generator.debug;}
}
}
$generator.generate;
}

View File

@ -1,116 +0,0 @@
<!-- -----------------------------------------------------------
! SPDX-License-Identifier: GFDL-1.3-or-later
! -------------------------------------------------------------
! Doc Type : Markdown
! Doc Name : 01-Develop.md
! Doc Authors : Aoran Zeng <ccmywish@qq.com>
! Contributors : Nul None <nul@none.org>
! |
! Created On : <2025-07-21>
! Last Modified : <2025-07-21>
! ---------------------------------------------------------- -->
# Develop `rawstr4c`
## Dependencies and Dev environment
Please install these first:
1. [rakudo]
2. [just]
<br>
## Get code
**Please make sure to use the dev branch for development**
```bash
git clone https://gitee.com/RubyMetric/chsrc.git -b dev
```
<br>
## Run
**For convenience, when developing, we only use `just` to invoke it.**
> [!IMPORTANT]
> When developing `rawstr4c` and maintaining `chsrc`,
> we must always give a path relative to the root directory of the whole `chsrc` project!
> This is because `just` will switch back to the project root directory by itself.
```bash
# Now we've already cd into the current dir
cd src/recipe
# Still have to use path relative to root!!!
just rawstr4c ./src/recipe/ware
```
```bash
just rawstr4c
```
We can install the distribution, by this way, we don't need `just`.
```bash
zef install .
rawstr4c --help
```
And therefore no such limitations mentioned above!
```bash
cd src/recipe
# No need to to use path relative to root now!!!
rawstr4c .
```
```bash
zef uninstall rawstr4c
```
<br>
## Debug
```bash
just rawstr4c --debug
# Note: there must be an = between option value and option
just rawstr4c --debug=parser
just rawstr4c --debug=generator
```
<br>
## Test
Run test scripts
```bash
cd test
raku ./xx-file.rakutest
```
Test executable file
```bash
just rawstr4c
```
<br>
[rakudo]: https://rakudo.org/
[just]: https://github.com/casey/just

View File

@ -1,174 +0,0 @@
# ---------------------------------------------------------------
# SPDX-License-Identifier: Artistic-2.0
# ---------------------------------------------------------------
# File Name : Config.rakumod
# File Authors : Aoran Zeng <ccmywish@qq.com>
# Contributors : Nul None <nul@none.org>
# Created On : <2025-07-16>
# Last Modified : <2025-07-16>
#
# Represent a section's working configuration
# ---------------------------------------------------------------
use Rawstr4c::Parser;
unit module Rawstr4c::Config;
#| section ()
class SectionConfig {
has Rawstr4c::Parser::Section $.section;
method new($section) {
self.bless(:$section);
}
#| section
#|
#| @param $default 使!
#|
method get-inherited-config($key, Str $default?) {
my $current = $.section;
while $current {
if $current.configblock.exist($key) {
return $current.configblock.get($key);
}
$current = $current.parent;
}
# 如果都没找到,生成一个新值
# 当 $default 为空时,生成的是 RS4C-Nil
return Rawstr4c::Parser::ConfigItem's-Value.new($default);
}
#| section
#|
#| @param $default 使!
#|
method get-direct-config($key, Str $default?) {
if $.section.configblock.exist($key) {
return $.section.configblock.get($key);
}
if ! $default.defined {
# say "DEBUG: Key <$key> is undefined";
}
return Rawstr4c::Parser::ConfigItem's-Value.new($default);
}
# ============================================================
# 返回当前 section 的 各种配置
# 注意,这些函数全部都返回 ConfigValue's-Value 实例
#| RS4C-Mode
method translate-mode() {
return self.get-inherited-config('translate', ':escape');
}
#| RS4C-Mode
method output-mode() {
return self.get-inherited-config('output', ':terminal');
}
#| RS4C-String
method prefix() {
return self.get-inherited-config('prefix', '_rawstr4c');
}
#| RS4C-String
method postfix() {
# RS4C-Mode 或 RS4C-String
my $config-postfix = self.get-inherited-config('postfix', ':use-language');
my $language = self.language.string-value;
my $postfix;
if $config-postfix.is-mode() && $config-postfix.mode-value() eq 'use-language' {
$postfix = 'in_' ~ $language;
} else {
# 如果不是模式,那就是用户给了一个具体的字符串
$postfix = $config-postfix.string-value();
}
return Rawstr4c::Parser::ConfigItem's-Value.new($postfix);
}
#| RS4C-Bool
method keep-prefix() {
return self.get-inherited-config('keep-prefix', 'true');
}
#| RS4C-Bool
method keep-postfix() {
return self.get-inherited-config('keep-postfix', 'true');
}
#| RS4C-String
method language() {
# RS4C-String 或 RS4C-Nil
my $config-language = self.get-direct-config('language');
my Str $lang;
if $config-language.is-nil {
# codeblock 没有写语言,就给 Unknown
$lang = 'Unknown';
} else {
$lang = $config-language.string-value;
}
return Rawstr4c::Parser::ConfigItem's-Value.new($lang);
}
#| RS4C-String
method name() {
# RS4C-String 或 RS4C-Nil
my $config-name = self.get-direct-config('name');
my $name;
if $config-name.is-nil {
$name = $.section.title.lc
} else {
$name = $config-name.string-value;
}
return Rawstr4c::Parser::ConfigItem's-Value.new($name);
}
#| RS4C-Bool
method name-literally() {
return self.get-direct-config('name-literally', 'false');
}
# RS4C-String
method namespace() {
my $config-namespace = self.get-direct-config('namespace', '');
my $current-namespace = $config-namespace.string-value;
# 嵌套增加
my $parent = $.section.parent;
while $parent {
if $parent.configblock.exist('namespace') {
$current-namespace = $parent.configblock.get('namespace').string-value ~ $current-namespace;
} else {
# 空字符串
$current-namespace = '' ~ $current-namespace;
}
$parent = $parent.parent;
}
return Rawstr4c::Parser::ConfigItem's-Value.new($current-namespace);
}
#| RS4C-Bool
method debug() {
return self.get-inherited-config('debug', 'false');
}
}

View File

@ -1,291 +0,0 @@
# ---------------------------------------------------------------
# SPDX-License-Identifier: Artistic-2.0
# ---------------------------------------------------------------
# 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-16>
#
# Generates C code from rawstr4c configuration
# ---------------------------------------------------------------
unit module Rawstr4c::Generator;
use Rawstr4c::Parser;
use Rawstr4c::Config;
use Rawstr4c::Version;
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 "\n" { return '\\n'; }
when "\t" { return '\\t'; }
when "\r" { return '\\r'; }
when "\0" { return '\\0'; }
default { return $char; }
}
}
default { die "Unknown translate 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($section) {
my $config = Rawstr4c::Config::SectionConfig.new($section);
my $prefix = $config.prefix.string-value;
my $postfix = $config.postfix.string-value;
my $keep-prefix = $config.keep-prefix.bool-value;
my $keep-postfix = $config.keep-postfix.bool-value;
my $name = $config.name.string-value;
my $namespace = $config.namespace.string-value;
my $name-literally = $config.name-literally.bool-value;
# 替换非法字符
$name = $name.subst(/<-[a..z A..Z 0..9 _]>/, '_', :g);
# 合并连续的下划线
$name = $name.subst(/_+/, '_', :g);
# 移除结尾的下划线
$name = $name.subst(/_+$/, '');
my $varname = "";
if $name-literally {
# 如果是字面量,直接使用原名
$varname = $name;
} else {
# 否则,按照规则组装变量名
$varname ~= $prefix if $keep-prefix && $prefix;
$varname ~= "_" if $varname && $namespace;
$varname ~= $namespace if $namespace;
$varname ~= "_" if $varname && $name;
$varname ~= $name if $name;
$varname ~= "_" if $varname && $postfix && $keep-postfix;
$varname ~= $postfix if $postfix && $keep-postfix;
}
return $varname || "unnamed_var";
}
}
#| .h / .c @variables
my class CVariableGenerator {
has Hash @.variables;
has Str $.c-header-filename;
method new() {
my $c-header-filename = "rawstr4c.h";
self.bless(:$c-header-filename, :variables([]));
}
#| CC,
method add-variable($name, $value, $kind) {
@.variables.push: {
name => $name,
value => $value,
kind => $kind
};
}
#| C
method generate-c-header-file() {
my $header = qq:to/EOF/;
#pragma once
/**
* Generated by rawstr4c v{Version::VERSION}-{Version::RELEASE_DATE}
*
* Date: {DateTime.now.Str}
*/
EOF
for @.variables -> $var {
given $var<kind> {
when 'global-variable-only-header' {
$header ~= "char {$var<name>}[] = \"{$var<value>}\";\n\n";
}
when 'global-variable' {
$header ~= "extern char {$var<name>}[];\n";
}
when 'macro' {
$header ~= "#define {$var<name>.uc} \"{$var<value>}\"\n\n";
}
default {
die "Unknown variable kind: {$var<kind>}";
}
}
}
return $header;
}
#| C
method generate-c-source-file() {
my $source = qq:to/EOF/;
/**
* Generated by rawstr4c v{Version::VERSION}-{Version::RELEASE_DATE}
*
* Date: {DateTime.now.Str}
*/
#include "{$.c-header-filename}"
EOF
for @.variables -> $var {
if $var<kind> eq 'global-variable' {
$source ~= "char {$var<name>}[] = \"{$var<value>}\";\n";
}
}
return $source;
}
method save-files($dest-dir) {
my $c-header-file = $dest-dir.IO.child($.c-header-filename).Str;
$c-header-file.IO.spurt(self.generate-c-header-file());
say "Generated C header file: $c-header-file";
# 检查是否有 "头、源并存的变量",如果有就使用并存的头文件和源文件模式
my $need-gen-c-source-file = @.variables.grep({ $_<kind> eq 'global-variable' }).elems > 0;
if $need-gen-c-source-file {
my $c-source-filename = $.c-header-filename.subst(/'.h'$/, '.c');
my $c-source-file = $dest-dir.IO.child($c-source-filename).Str;
$c-source-file.IO.spurt(self.generate-c-source-file());
say "Generated C source file: $c-source-file";
}
}
}
class Generator {
has Bool $!enable-debug = False; # 是否启用调试模式
has Rawstr4c::Parser::Parser $.parser;
has CStringConverter $.string-converter;
has CVariableNameGenerator $.varname-generator;
has CVariableGenerator $.variable-generator;
method new($parser) {
self.bless(
:$parser,
:string-converter(CStringConverter.new),
:varname-generator(CVariableNameGenerator.new),
:variable-generator(CVariableGenerator.new)
);
}
method debug() {
$!enable-debug = True;
}
method generate-for-section($section) {
my $configblock = $section.configblock;
my $title = $section.title;
my $rawstr = $section.codeblock;
my $config = Rawstr4c::Config::SectionConfig.new($section);
my $debug-in-config = $config.debug.bool-value;
return unless $rawstr;
my $translate-mode = $config.translate-mode.mode-value;
my $output-mode = $config.output-mode.mode-value;
my $varname = $.varname-generator.generate($section);
if $debug-in-config || $!enable-debug {
my $language = $config.language.string-value;
my $prefix = $config.prefix.string-value;
my $postfix = $config.postfix.string-value;
say "------ Section: $title ------";
say "Output mode = $output-mode";
say "Translate mode = $translate-mode";
say "Language = $language";
say "Prefix = $prefix";
say "Postfix = $postfix";
say "Variable name = $varname";
say '';
}
my $c-string = $.string-converter.convert-string($rawstr, $translate-mode);
given $output-mode {
when 'terminal' {
say 'char ' ~ $varname ~ '[] = "' ~ $c-string ~ '";';
say "";
}
when 'global-variable' {
$.variable-generator.add-variable($varname, $c-string, 'global-variable');
}
when 'global-variable-only-header' {
$.variable-generator.add-variable($varname, $c-string, 'global-variable-only-header');
}
when 'macro' {
$.variable-generator.add-variable($varname, $c-string, 'macro');
}
default {
die "Illegal output mode: $output-mode";
}
}
}
method generate() {
my $root-section = $.parser.root-section;
# 获取所有需要处理的 sections包括 root section 和所有子 sections
my @all-sections = ($root-section, |$root-section.get-all-descendants());
# 这个 generate-for-section() 要么把变量输出到终端,要么累计到 @variables 中
for @all-sections -> $section {
self.generate-for-section($section);
}
# 如果有任何变量被添加 (没有被输出到终端),就保存文件
if $.variable-generator.variables.elems > 0 {
my $dest-dir = $.parser.input-file.IO.dirname.Str;
$.variable-generator.save-files($dest-dir);
}
}
}

View File

@ -1,356 +0,0 @@
# ---------------------------------------------------------------
# SPDX-License-Identifier: Artistic-2.0
# ---------------------------------------------------------------
# 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-16>
#
# rawstr4c.md parsing
# ---------------------------------------------------------------
unit module Rawstr4c::Parser;
#| Bool Boolean
my enum ConfigItem's-ValueType < RS4C-Nil RS4C-String RS4C-Mode RS4C-Bool>;
#|
class ConfigItem's-Value {
has ConfigItem's-ValueType $.type;
has Str $.raw-value;
has Any $.parsed-value;
#| $raw-text undefined
method new(Str $input-text) {
my $type;
my $parsed-value;
my $raw-value = $input-text;
# 明确区分空字符串和无值情况
# 这种情况不可能是用户写的(并没有nil这个字面量)
if ! $input-text.defined {
$type = RS4C-Nil;
$parsed-value = Nil;
$raw-value = "<internal-rs4c-nil>"; # 一个完全用不到的值,但是由于 $.raw-value 类型是字符串,所以必须随便给一个值
}
else {
# wrtd: 不要试图在这里利用 given when 统一处理未定义的值,因为会一直报错
given $input-text {
when /^ ':' (.+) $/ {
# 模式值 :mode
$type = RS4C-Mode;
$parsed-value = ~$0;
}
when /^ ('true'|'false'|'yes'|'no') $/ {
# 特殊字面量 - true/false/yes/no 都是 literal
$type = RS4C-Bool;
$parsed-value = ~$0 ~~ /^('true'|'yes')$/ ?? True !! False;
}
# 输入为空时被当做是字符串类型
default {
# 普通字符串
$type = RS4C-String;
$parsed-value = $input-text;
}
}
}
self.bless(:$type, :$raw-value, :$parsed-value);
}
# 获得适合调用者接受的值
method value() {
given $.type {
when RS4C-Nil | RS4C-String | RS4C-Bool | RS4C-Mode { return $.parsed-value; }
default { die "Unknown config value type: {$.type}"; }
}
}
# 这些函数防止开发者写错类型
method nil-value() {
return self.value if $.type == RS4C-Nil;
die "The config value type should be RS4C-Nil, but it is: {$.type}";
}
method string-value() {
return self.value if $.type == RS4C-String;
die "The config value type should be RS4C-String, but it is: {$.type}";
}
method bool-value() {
return self.value if $.type == RS4C-Bool;
die "The config value type should be RS4C-Bool, but it is: {$.type}";
}
method mode-value() {
return self.value if $.type == RS4C-Mode;
die "The config value type should be RS4C-Mode, but it is: {$.type}";
}
# 类型检查方法
method is-nil() { return $.type == RS4C-Nil; }
method is-mode() { return $.type == RS4C-Mode; }
method is-bool() { return $.type == RS4C-Bool; }
method is-string() { return $.type == RS4C-String; }
}
#| config items
#| get ConfigItem's-Value
my class ConfigBlock {
has %!items;
# 如果非要在程序内部中调用,而不是直接从 Markdown 文件中读取出来
# 一定要记得 $raw-value 用的是 rawstr4c 的语法!也就是说,这里一定是一个字符串
method set($k, $raw-value) {
%!items{$k} = ConfigItem's-Value.new($raw-value);
}
method get($k) {
return %!items{$k};
}
method exist($k) {
return %!items{$k}:exists;
}
# 配置项名称
# @danger: 把这个函数命名为 items会让我的机器蓝屏.....
method keys() {
return %!items.keys;
}
}
#| section
class Section {
has Str $.title;
has Int $.level;
has ConfigBlock $.configblock;
has Str $.codeblock is rw;
has Section $.parent is rw;
has Section @.children;
method new($title, $level) {
my $configblock = ConfigBlock.new();
# parent 和 codeblock 刻意不初始化
self.bless(:$title, :$level, :$configblock, :children([]));
}
method add-child($child-section) {
$child-section.parent = self;
@.children.push: $child-section;
}
method has-children() {
return @.children.elems > 0;
}
# 递归获取所有后代section深度优先遍历
method get-all-descendants() {
my @descendants;
for @.children -> $child {
@descendants.push: $child;
@descendants.append: $child.get-all-descendants();
}
return @descendants;
}
# 获取section的路径从根到当前节点
method get-hierarchical-path() {
my @path;
my $current = self;
while $current {
@path.unshift: $current.title;
$current = $current.parent;
}
return @path.join(" > ");
}
}
#|()
所有内容都是 section:
- level 0: root section 无标题
- level 1: # 一级标题
- level 2: ## 二级标题
- ...
)
class Parser {
has Str $.input-file is rw;
#| sections
has Section @!sections;
#| $markdown-file markdown
method new($markdown-file) {
self.bless(:input-file($markdown-file));
}
# 获取根sectionlevel 0
method root-section() {
return @!sections.first({ $_.level == 0 });
}
# 配置项所在行 -> 解析为配置项
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() {
my $content = $.input-file.IO.slurp;
my @lines = $content.lines;
my $current-section;
my $in-codeblock = False;
# 在代码块中的 raw string
my $rawstr = "";
# 无论有没有具体的 root 信息 (比如所处理的文件第一行就是标题)
# 都创建一个 root section (level 0)
$current-section = Section.new("", 0);
@!sections.push: $current-section;
# 开始遍历
my $line-count = 0;
for @lines -> $line {
$line-count++;
# Step1: 处理标题,这里最重要,因为是判断上一个 section 结束的标志
if !$in-codeblock && $line ~~ /^ '#' ('#'*) \s* (.+) / {
my $level = 1 + $0.chars;
my $title = ~$1;
# 保存当前section的codeblock
$current-section.codeblock = $rawstr;
# 准备创建一个新的 section
$rawstr = "";
my $new-section = Section.new($title, $level);
@!sections.push: $new-section;
# 找到合适的父节点
my $parent = self.find-parent-section($level);
if $parent {
$parent.add-child($new-section);
}
$current-section = $new-section;
next;
}
# Step2: 处理配置项 (如果该行不是配置项则下一行)
if self.parse-config-item-line($line, $current-section.configblock) {
next;
}
# Step3: 开始处理 codeblock
if $line ~~ /^ '```' (.*)? / {
if $in-codeblock {
$in-codeblock = False;
} else {
$in-codeblock = True;
my $lang = ~($0 // '');
if $lang && $current-section && !$current-section.configblock.exist('language') {
$current-section.configblock.set('language', $lang);
}
}
next;
}
# 代码块里的内容统统进来
if $in-codeblock {
$rawstr ~= $line ~ "\n";
}
}
# 遍历结束保存最后一个section的codeblock
if $rawstr && $current-section {
$current-section.codeblock = $rawstr;
}
}
method find-parent-section($new-level) {
# 从@!sections尾部向前找找到第一个level小于new-level的section作为父节点
for @!sections.reverse -> $section {
if $section.level < $new-level {
return $section;
}
}
return Nil; # 没有找到父节点,说明是 root section
}
# 调试方法扁平打印所有sections
method debug-print-sections-flatly() {
say "====== Sections ======";
for @!sections.kv -> $i, $section {
my $title = $section.title || "(Root)";
my $has-config = $section.configblock.keys ?? "[Has Config]" !! "[NO Config]";
my $has-code = $section.codeblock ?? "[Has Code]" !! "[NO Code]";
say " [$i] Level {$section.level}: $title - $has-config, $has-code";
}
}
# 调试方法层级打印sections
method debug-print-sections-hierarchyly() {
say "====== Hierarchy ======";
my $indent = 0;
# 嵌套的格式化函数
my sub format-section($section, $level) {
my $prefix = ' ' x $level;
my $title = $section.title // '(Root)';
my $base-info = "{$prefix}- {$title} (level {$section.level})";
my $config-info = "";
if $section.configblock.keys {
my @config-items;
for $section.configblock.keys -> $key {
my $value = $section.configblock.get($key);
@config-items.push: "$key = {$value.raw-value}";
}
$config-info = "\n" ~ "{$prefix} [" ~ @config-items.join(", ") ~ "]";
}
return $base-info ~ $config-info;
}
# 嵌套的递归打印函数
my sub print-section-tree($section, $level) {
say format-section($section, $level);
if $section.has-children {
for $section.children -> $child {
print-section-tree($child, $level + 1);
}
}
}
my $root = self.root-section();
print-section-tree($root, $indent);
}
# 调试方法:完整的调试信息打印
method debug() {
self.debug-print-sections-flatly();
self.debug-print-sections-hierarchyly();
}
}

View File

Internal Server Error - Gitea: Git server for yexuejc
500 Internal Server Error

Gitea Version: 1.23.8

@ -1,27 +0,0 @@
# ---------------------------------------------------------------
# SPDX-License-Identifier: Artistic-2.0
# ---------------------------------------------------------------
# File Name : Version.rakumod
# File Authors : Aoran Zeng <ccmywish@qq.com>
# Contributors : Nul None <nul@none.org>
# Created On : <2025-07-14>
# Last Modified : <2025-07-16>
# ---------------------------------------------------------------
unit module Rawstr4c::Version;
constant VERSION = "0.2.1.0";
constant RELEASE_DATE = "2025/07/16";
constant Maintain_URL = "https://github.com/RubyMetric/chsrc/blob/dev/tool/rawstr4c";
constant Maintain_URL2 = "https://gitee.com/RubyMetric/chsrc/blob/dev/tool/rawstr4c";