feat(lexer, preprocessor): replace cstring conversion with copy and refactor macro expansion
- Replace `scc_cstring_from_cstr(scc_cstring_as_cstr(...))` with `scc_cstring_copy()` in lexer to fix memory leaks - Extract macro expansion logic into separate `expand_macro.c` file - Remove `expand_stack` parameter from `scc_pp_expand_macro()` function - Add new parsing functions for macro replacement lists and arguments - Add string utility functions for whitespace trimming and string joining - Update memory stream documentation for clarity
This commit is contained in:
@@ -20,6 +20,7 @@ typedef struct scc_cstring {
|
||||
*
|
||||
* @return cstring_t 初始化后的对象
|
||||
*/
|
||||
// FIXME need using `init` beacuse it not malloc
|
||||
static inline scc_cstring_t scc_cstring_new(void) {
|
||||
return (scc_cstring_t){.data = null, .size = 0, .cap = 0};
|
||||
}
|
||||
@@ -48,6 +49,10 @@ static inline scc_cstring_t scc_cstring_from_cstr(const char *s) {
|
||||
return (scc_cstring_t){.size = len + 1, .cap = len + 1, .data = data};
|
||||
}
|
||||
|
||||
static inline scc_cstring_t scc_cstring_copy(const scc_cstring_t *s) {
|
||||
return scc_cstring_from_cstr(s->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 释放动态字符串占用的内存资源
|
||||
*
|
||||
@@ -182,4 +187,15 @@ static inline char *scc_cstring_as_cstr(const scc_cstring_t *str) {
|
||||
return str->data;
|
||||
}
|
||||
|
||||
static inline char *scc_cstring_move_cstr(scc_cstring_t *str) {
|
||||
if (str == null || str->data == null) {
|
||||
return "";
|
||||
}
|
||||
char *ret = str->data;
|
||||
str->data = null;
|
||||
str->cap = 0;
|
||||
str->size = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* __SCC_CORE_STR_H__ */
|
||||
|
||||
@@ -100,7 +100,7 @@ typedef struct scc_mem_probe_stream {
|
||||
usize data_length;
|
||||
usize curr_pos; // 当前读取位置
|
||||
usize probe_pos; // 探针位置(用于peek)
|
||||
cbool owned; // 是否拥有数据(需要释放)
|
||||
cbool owned; // 是否拥有数据(如果拥有将会自动释放)
|
||||
} scc_mem_probe_stream_t;
|
||||
|
||||
/**
|
||||
@@ -109,22 +109,22 @@ typedef struct scc_mem_probe_stream {
|
||||
* @param stream 流结构指针
|
||||
* @param data 数据指针
|
||||
* @param length 数据长度
|
||||
* @param need_copy 是否需要复制数据
|
||||
* @param owned 是否拥有数据(如果拥有将会自动释放)
|
||||
* @return core_probe_stream_t* 成功返回流指针,失败返回NULL
|
||||
*/
|
||||
scc_probe_stream_t *scc_mem_probe_stream_init(scc_mem_probe_stream_t *stream,
|
||||
const char *data, usize length,
|
||||
cbool need_copy);
|
||||
cbool owned);
|
||||
/**
|
||||
* @brief 构造内存探针流(其中drop会自动释放内存)
|
||||
*
|
||||
* @param data
|
||||
* @param length
|
||||
* @param need_copy
|
||||
* @param owned 是否拥有数据(如果拥有将会自动释放)
|
||||
* @return scc_probe_stream_t*
|
||||
*/
|
||||
scc_probe_stream_t *scc_mem_probe_stream_new(const char *data, usize length,
|
||||
cbool need_copy);
|
||||
cbool owned);
|
||||
#endif
|
||||
|
||||
#endif /* __SMCC_CORE_PROBE_STREAM_H__ */
|
||||
|
||||
@@ -125,7 +125,7 @@ static void mem_probe_stream_drop(scc_probe_stream_t *_stream) {
|
||||
|
||||
scc_probe_stream_t *scc_mem_probe_stream_init(scc_mem_probe_stream_t *stream,
|
||||
const char *data, usize length,
|
||||
cbool need_copy) {
|
||||
cbool owned) {
|
||||
if (stream == null || data == null) {
|
||||
LOG_ERROR("param error");
|
||||
return null;
|
||||
@@ -133,22 +133,11 @@ scc_probe_stream_t *scc_mem_probe_stream_init(scc_mem_probe_stream_t *stream,
|
||||
|
||||
if (length == 0) {
|
||||
LOG_WARN("input memory is empty");
|
||||
need_copy = false;
|
||||
owned = false;
|
||||
}
|
||||
|
||||
stream->owned = need_copy;
|
||||
if (need_copy) {
|
||||
char *buf = (char *)scc_malloc(length);
|
||||
if (buf == null) {
|
||||
LOG_ERROR("malloc error");
|
||||
return null;
|
||||
}
|
||||
|
||||
scc_memcpy(buf, data, length);
|
||||
stream->data = buf;
|
||||
} else {
|
||||
stream->data = data;
|
||||
}
|
||||
stream->owned = owned;
|
||||
stream->data = data;
|
||||
stream->data_length = length;
|
||||
stream->curr_pos = 0;
|
||||
stream->probe_pos = 0;
|
||||
@@ -176,7 +165,7 @@ static void scc_owned_mem_stream_drop(scc_probe_stream_t *_stream) {
|
||||
}
|
||||
|
||||
scc_probe_stream_t *scc_mem_probe_stream_new(const char *data, usize length,
|
||||
cbool need_copy) {
|
||||
cbool owned) {
|
||||
scc_mem_probe_stream_t *stream =
|
||||
(scc_mem_probe_stream_t *)scc_malloc(sizeof(scc_mem_probe_stream_t));
|
||||
if (stream == null) {
|
||||
@@ -184,7 +173,7 @@ scc_probe_stream_t *scc_mem_probe_stream_new(const char *data, usize length,
|
||||
}
|
||||
|
||||
scc_probe_stream_t *ret =
|
||||
scc_mem_probe_stream_init(stream, data, length, need_copy);
|
||||
scc_mem_probe_stream_init(stream, data, length, owned);
|
||||
stream->stream.drop = scc_owned_mem_stream_drop;
|
||||
Assert(ret != null);
|
||||
return ret;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/**
|
||||
* @file hashmap.h
|
||||
* @file hashtable.h
|
||||
* @brief 开放寻址法哈希表实现
|
||||
*
|
||||
* 提供基于向量容器的哈希表实现,支持动态扩容和墓碑机制
|
||||
*/
|
||||
|
||||
#ifndef __SCC_HASHMAP_H__
|
||||
#define __SCC_HASHMAP_H__
|
||||
#ifndef __SCC_HASHTABLE_H__
|
||||
#define __SCC_HASHTABLE_H__
|
||||
|
||||
#include <libcore.h>
|
||||
|
||||
@@ -121,4 +121,4 @@ typedef int (*scc_hashtable_iter_fn)(const void *key, void *value,
|
||||
void scc_hashtable_foreach(scc_hashtable_t *ht, scc_hashtable_iter_fn iter_func,
|
||||
void *context);
|
||||
|
||||
#endif /* __SCC_HASHMAP_H__ */
|
||||
#endif /* __SCC_HASHTABLE_H__ */
|
||||
@@ -1,9 +1,9 @@
|
||||
#ifndef __SMCC_UTILS_H__
|
||||
#define __SMCC_UTILS_H__
|
||||
|
||||
#include "hashmap.h"
|
||||
#include "hashtable.h"
|
||||
#include "kllist.h"
|
||||
#include "strpool.h"
|
||||
#include <libcore.h>
|
||||
|
||||
#endif // __SMCC_UTILS_H__
|
||||
#endif /* __SMCC_UTILS_H__ */
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#ifndef __SCC_STRPOOL_H__
|
||||
#define __SCC_STRPOOL_H__
|
||||
|
||||
#include "hashmap.h"
|
||||
#include "hashtable.h"
|
||||
#include <libcore.h>
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <hashmap.h>
|
||||
#include <hashtable.h>
|
||||
|
||||
#ifndef SCC_INIT_HASHMAP_SIZE
|
||||
#define SCC_INIT_HASHMAP_SIZE (32)
|
||||
@@ -1,112 +0,0 @@
|
||||
# vector_gdb.py
|
||||
import gdb # type: ignore
|
||||
from gdb.printing import PrettyPrinter # type: ignore
|
||||
|
||||
class VectorPrinter:
|
||||
"""兼容新旧注册方式的最终方案"""
|
||||
|
||||
def __init__(self, val: gdb.Value):
|
||||
self.val:gdb.Value = val
|
||||
|
||||
def check_type(self) -> bool:
|
||||
"""类型检查(兼容匿名结构体)"""
|
||||
try:
|
||||
if self.val.type.code != gdb.TYPE_CODE_STRUCT:
|
||||
return False
|
||||
fields = self.val.type.fields()
|
||||
if not fields:
|
||||
return False
|
||||
exp = ['size', 'cap', 'data']
|
||||
for t in fields:
|
||||
if t.name in exp:
|
||||
exp.remove(t.name)
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
except gdb.error:
|
||||
return False
|
||||
|
||||
def to_string(self):
|
||||
if not self.check_type():
|
||||
return "Not a vector"
|
||||
|
||||
return "vector({} size={}, cap={})".format(
|
||||
self.val.address,
|
||||
self.val['size'],
|
||||
self.val['cap'],
|
||||
)
|
||||
|
||||
def display_hint(self):
|
||||
return 'array'
|
||||
|
||||
def children(self):
|
||||
"""生成数组元素(关键改进点)"""
|
||||
if not self.check_type():
|
||||
return []
|
||||
|
||||
size = int(self.val['size'])
|
||||
cap = int(self.val['cap'])
|
||||
data_ptr = self.val['data']
|
||||
|
||||
if cap == 0 or data_ptr == 0:
|
||||
return []
|
||||
|
||||
# 使用 GDB 内置数组转换
|
||||
array = data_ptr.dereference()
|
||||
array = array.cast(data_ptr.type.target().array(cap - 1))
|
||||
|
||||
for i in range(size):
|
||||
# state = "<used>" if i < size else "<unused>"
|
||||
try:
|
||||
value = array[i]
|
||||
yield (f"[{i}] {value.type} {value.address}", value)
|
||||
except gdb.MemoryError:
|
||||
yield (f"[{i}]", "<invalid>")
|
||||
|
||||
# 注册方式一:传统append方法(您之前有效的方式)self
|
||||
def append_printer():
|
||||
gdb.pretty_printers.append(
|
||||
lambda val: VectorPrinter(val) if VectorPrinter(val).check_type() else None
|
||||
)
|
||||
|
||||
# 注册方式二:新版注册方法(备用方案)
|
||||
def register_new_printer():
|
||||
class VectorPrinterLocator(PrettyPrinter):
|
||||
def __init__(self):
|
||||
super().__init__("vector_printer")
|
||||
|
||||
def __call__(self, val):
|
||||
ret = VectorPrinter(val).check_type()
|
||||
print(f"ret {ret}, type {val.type}, {[(i.name, i.type) for i in val.type.fields()]}")
|
||||
return None
|
||||
|
||||
gdb.printing.register_pretty_printer(
|
||||
gdb.current_objfile(),
|
||||
VectorPrinterLocator()
|
||||
)
|
||||
|
||||
# 双重注册保证兼容性
|
||||
append_printer() # 保留您原来有效的方式
|
||||
# register_new_printer() # 添加新版注册
|
||||
|
||||
class VectorInfoCommand(gdb.Command):
|
||||
"""保持原有命令不变"""
|
||||
def __init__(self):
|
||||
super().__init__("vector_info", gdb.COMMAND_USER)
|
||||
|
||||
def invoke(self, argument, from_tty):
|
||||
val = gdb.parse_and_eval(argument)
|
||||
printer = VectorPrinter(val)
|
||||
|
||||
if not printer.check_type():
|
||||
print("Invalid vector")
|
||||
return
|
||||
|
||||
print("=== Vector Details ===")
|
||||
print("Size:", val['size'])
|
||||
print("Capacity:", val['cap'])
|
||||
print("Elements:")
|
||||
for name, value in printer.children():
|
||||
print(f" {name}: {value}")
|
||||
|
||||
VectorInfoCommand()
|
||||
222
runtime/runtime_gdb.py
Normal file
222
runtime/runtime_gdb.py
Normal file
@@ -0,0 +1,222 @@
|
||||
"https://sourceware.org/gdb/current/onlinedocs/gdb.html/Python-API.html#Python-API"
|
||||
|
||||
import gdb # type: ignore
|
||||
|
||||
|
||||
class VectorPrinter(gdb.ValuePrinter):
|
||||
"""兼容新旧注册方式的最终方案"""
|
||||
|
||||
def __init__(self, val: gdb.Value):
|
||||
self.val: gdb.Value = val
|
||||
|
||||
@staticmethod
|
||||
def check_type(val: gdb.Value) -> bool:
|
||||
"""类型检查(兼容匿名结构体)"""
|
||||
try:
|
||||
if val.type.code not in (gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_TYPEDEF):
|
||||
return False
|
||||
if val.type.name == "scc_cstring_t":
|
||||
return False
|
||||
fields = val.type.fields()
|
||||
if not fields:
|
||||
return False
|
||||
exp = ["size", "cap", "data"]
|
||||
for t in fields:
|
||||
if t.name in exp:
|
||||
exp.remove(t.name)
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
except gdb.error:
|
||||
return False
|
||||
except ValueError:
|
||||
return False
|
||||
except TypeError:
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"[DEBUG] Unknown exception type: {type(e).__name__}")
|
||||
print(f"[DEBUG] Exception details: {e}")
|
||||
print(
|
||||
f"[DEBUG] val type: {val.type if hasattr(val, 'type') else 'no type attr'}"
|
||||
)
|
||||
return False
|
||||
|
||||
def to_string(self):
|
||||
"""
|
||||
GDB will call this method to display the string representation
|
||||
of the value passed to the object's constructor.
|
||||
|
||||
This is a basic method, and is optional.
|
||||
|
||||
When printing from the CLI, if the to_string method exists,
|
||||
then GDB will prepend its result to the values returned by children.
|
||||
Exactly how this formatting is done is dependent on the display hint,
|
||||
and may change as more hints are added. Also, depending on the print settings
|
||||
(see Print Settings), the CLI may print just the result of to_string
|
||||
in a stack trace, omitting the result of children.
|
||||
|
||||
If this method returns a string, it is printed verbatim.
|
||||
|
||||
Otherwise, if this method returns an instance of gdb.Value,
|
||||
then GDB prints this value. This may result in a call to another pretty-printer.
|
||||
|
||||
If instead the method returns a Python value which is convertible to a gdb.Value,
|
||||
then GDB performs the conversion and prints the resulting value. Again,
|
||||
this may result in a call to another pretty-printer. Python scalars
|
||||
(integers, floats, and booleans) and strings are convertible to gdb.Value;
|
||||
other types are not.
|
||||
|
||||
Finally, if this method returns None then no further operations are performed
|
||||
in this method and nothing is printed.
|
||||
|
||||
If the result is not one of these types, an exception is raised.
|
||||
|
||||
https://sourceware.org/gdb/current/onlinedocs/gdb.html/Pretty-Printing-API.html#Pretty-Printing-API
|
||||
"""
|
||||
return (
|
||||
f"vector({self.val.address} size={self.val['size']}, cap={self.val['cap']})"
|
||||
)
|
||||
|
||||
def display_hint(self):
|
||||
"""
|
||||
The CLI may call this method and use its result to change the formatting of a value.
|
||||
The result will also be supplied to an MI consumer as a 'displayhint'
|
||||
attribute of the variable being printed.
|
||||
|
||||
This is a basic method, and is optional. If it does exist,
|
||||
this method must return a string or the special value None.
|
||||
|
||||
Some display hints are predefined by GDB:
|
||||
|
||||
'array'
|
||||
Indicate that the object being printed is “array-like”.
|
||||
The CLI uses this to respect parameters such as set print elements and set print array.
|
||||
|
||||
'map'
|
||||
Indicate that the object being printed is “map-like”,
|
||||
and that the children of this value can be assumed to alternate between keys and values.
|
||||
|
||||
'string'
|
||||
Indicate that the object being printed is “string-like”.
|
||||
If the printer's to_string method returns a Python string of some kind,
|
||||
then GDB will call its internal language-specific string-printing function
|
||||
to format the string. For the CLI this means adding quotation marks, possibly
|
||||
escaping some characters, respecting set print elements, and the like.
|
||||
|
||||
The special value None causes GDB to apply the default display rules.
|
||||
|
||||
https://sourceware.org/gdb/current/onlinedocs/gdb.html/Pretty-Printing-API.html#Pretty-Printing-API
|
||||
"""
|
||||
return "array"
|
||||
|
||||
def num_children(self):
|
||||
"""
|
||||
This is not a basic method, so GDB will only ever call it for objects
|
||||
derived from gdb.ValuePrinter.
|
||||
|
||||
If available, this method should return the number of children.
|
||||
|
||||
None may be returned if the number can't readily be computed.
|
||||
|
||||
https://sourceware.org/gdb/current/onlinedocs/gdb.html/Pretty-Printing-API.html#Pretty-Printing-API
|
||||
"""
|
||||
return int(self.val["size"])
|
||||
|
||||
def children(self):
|
||||
"""
|
||||
This is not a basic method, so GDB will only ever call it for objects
|
||||
derived from gdb.ValuePrinter.
|
||||
|
||||
If available, this method should return the child item (that is,
|
||||
a tuple holding the name and value of this child) indicated by n.
|
||||
Indices start at zero.
|
||||
|
||||
GDB provides a function which can be used to look up the default
|
||||
pretty-printer for a gdb.Value:
|
||||
|
||||
https://sourceware.org/gdb/current/onlinedocs/gdb.html/Pretty-Printing-API.html#Pretty-Printing-API
|
||||
"""
|
||||
size = int(self.val["size"])
|
||||
cap = int(self.val["cap"])
|
||||
data_ptr = self.val["data"]
|
||||
|
||||
if cap == 0 or data_ptr == 0:
|
||||
return []
|
||||
|
||||
# 使用 GDB 内置数组转换
|
||||
array = data_ptr.dereference()
|
||||
array = array.cast(data_ptr.type.target().array(cap - 1))
|
||||
|
||||
for i in range(size):
|
||||
# state = "<used>" if i < size else "<unused>"
|
||||
try:
|
||||
value = array[i]
|
||||
yield (f"[{i}] {value.type} {value.address}", value)
|
||||
except gdb.MemoryError:
|
||||
yield (f"[{i}]", "<invalid>")
|
||||
|
||||
|
||||
class HashTablePrinter(gdb.ValuePrinter):
|
||||
def __init__(self, val: gdb.Value):
|
||||
self.val: gdb.Value = val
|
||||
|
||||
@staticmethod
|
||||
def check_type(val: gdb.Value) -> bool:
|
||||
if val.type.name in ["scc_hashtable_t", "scc_hashtable"]:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def append_printer():
|
||||
"注册方式一:传统append方法(您之前有效的方式)self"
|
||||
gdb.pretty_printers.append(
|
||||
lambda val: VectorPrinter(val) if VectorPrinter.check_type(val) else None
|
||||
)
|
||||
|
||||
|
||||
def register_new_printer():
|
||||
"注册方式二:新版注册方法(备用方案)"
|
||||
|
||||
def str_lookup_function(val):
|
||||
if VectorPrinter.check_type(val) is False:
|
||||
return None
|
||||
ret = VectorPrinter(val)
|
||||
# print(
|
||||
# f"ret {ret}, type {val.type.name}, {[(i.name, i.type) for i in val.type.fields()]}"
|
||||
# )
|
||||
return ret
|
||||
|
||||
gdb.printing.register_pretty_printer(gdb.current_objfile(), str_lookup_function)
|
||||
# if gdb.current_progspace() is not None:
|
||||
# pts = gdb.current_progspace().pretty_printers
|
||||
# print(pts, len(pts))
|
||||
# pts.append(str_lookup_function)
|
||||
|
||||
|
||||
class VectorInfoCommand(gdb.Command):
|
||||
"""保持原有命令不变"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("vector_info", gdb.COMMAND_USER)
|
||||
|
||||
def invoke(self, argument, from_tty):
|
||||
val = gdb.parse_and_eval(argument)
|
||||
|
||||
if not VectorPrinter.check_type(val):
|
||||
print("Invalid vector")
|
||||
return
|
||||
|
||||
printer = VectorPrinter(val)
|
||||
print("=== Vector Details ===")
|
||||
print("Size:", val["size"])
|
||||
print("Capacity:", val["cap"])
|
||||
print("Elements:")
|
||||
for name, value in printer.children():
|
||||
print(f" {name}: {value}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 双重注册保证兼容性
|
||||
# append_printer() # 保留您原来有效的方式
|
||||
register_new_printer() # 添加新版注册
|
||||
VectorInfoCommand()
|
||||
Reference in New Issue
Block a user