Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion batchtest.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#!/bin/bash

inputext=".jtl.h"

# Function to run test on a given input file
run_test() {
local input_file="$1"
Expand All @@ -15,7 +17,7 @@ run_test() {
test_on_directory() {
local dir="$1"
for input_file in "$dir"/*; do
if [ -f "$input_file" ] && [[ "$input_file" != *.out ]]; then
if [ -f "$input_file" ] && [[ "$input_file" == *"$inputext" ]]; then
run_test "$input_file"
fi
done
Expand Down
14 changes: 12 additions & 2 deletions doc/parser.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,13 +204,15 @@ CFG的语法规则到此结束,需要更多信息可以查看`bison`的输出
- enum元素(枚举常量)的定义:新的枚举常量。(规则22,23)
- typedef的定义中的`NT_NAMED_RIGHT_TYPE_EXPR`:新的类型标识符。
- 全局变量的声明中的`NT_NAMED_RIGHT_TYPE_EXPR`:变量名。
- 在所有的`NT_LEFT_TYPE`中,引入新定义的struct/union/enum类型标识符。(规则24,27,30)
- 在所有的`NT_LEFT_TYPE`中,引入带有定义的struct/union/enum类型标识符。(规则24,27,30)
- 在所有的`NT_LEFT_TYPE`中,使用的struct/union/enum类型标识符。(规则26,29,32)
注:一般来说,会写出这样的代码 `struct Foo; struct Foo foo;`或者`struct Foo{}; struct Foo foo;`,我们一般认为前一句声明/引入了`Foo`,后一句使用了`Foo`。实际上,后一句也可以声明/引入`Foo`。`struct Foo foo; struct Foo{};`也是合法的C语言代码。只不过同一个标识符的重复声明不会引发错误而已。


参考C语言的行为,同一个标识符可以被用作不同的类型。
一个标识符可以是struct/union/enum类型标识符(此三者内部互斥),同时也可以是typedef/全局变量/枚举常量的标识符(此三者内部互斥)。我们支持这种行为:向全局作用域添加新的标识符时,将检查是否已经存在同名的标识符,如果存在,将进一步检查前置定义的类型是否与当前希望定义的类型兼容。

2. (全局)使用标识符:仅检查全局作用域中是否存在该标识符,且标识符对应的类型是否正确。
- 在所有的`NT_LEFT_TYPE`中,使用已定义的struct/union/enum类型标识符。(规则26,29,32)
- 在所有的`NT_LEFT_TYPE`中,使用已定义的typedef类型标识符。(规则35)

与struct/union的定义作用域相关的CFG产生式:
Expand All @@ -221,6 +223,14 @@ CFG的语法规则到此结束,需要更多信息可以查看`bison`的输出

以上的检查函数都被定义在`lib.h/lib.c`中,并且在parser尝试将新的语法成分加入AST时被调用。

### 标识符管理:无法解决的问题

标识符的重复声明/定义检查。
- 在C语言中,typedef的重复声明不会引发错误,只要它们所指的是同一个类型。这个问题没有在本项目中解决,因为设计AST的比较操作。
- 在C语言中,struct/union的重复声明不会引发错误;重复定义会引发错误。这个问题没有在本项目中解决,因为没有追踪struct/union是否已经被定义。默认重复声明/定义同一个struct/union不会引发警告。
- 在C语言中,全局变量的重复声明不会引发错误,但是涉及链接阶段可见性(如`extern int a; int a;`是合法的)。这个问题没有在本项目中解决,因为本项目不涉及`extern`,`static`等关键字。本项目默认重复声明同一个变量名是错误的,引发警告。


### 错误处理

如果遇到了语法错误,parser会输出错误信息并停止解析,通过`yyerror`函数实现。因为使用了`%define parse.error detailed`,`yyerror`会输出更详细的错误信息。
Expand Down
2 changes: 1 addition & 1 deletion resource/test/test-identifier-predeclared/testcase1.jtl.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ enum Foo b; // Warning: Identifier Foo has been registered as struct, not enum
union Foo c; // Warning: Identifier Foo has been registered as struct, not union


typedef Foo foo_t;
typedef Foo foo_t; // warning: should use `struct Foo` instead of `Foo`
foo_t d; // OK: foo_t has been registered as typedef
bar_t e; // Warning: Identifier bar_t has never been registered

3 changes: 3 additions & 0 deletions resource/test/test-identifier-predeclared/testcase2.jtl.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
struct foo {};
struct foo foo; // OK

struct Bar bar;
struct Bar {int a;};

enum foo {a}; // Warning: Identifier foo has been registered as struct, not enum
int foo; // Warning : Identifier foo is already registered as variable at line 2
6 changes: 3 additions & 3 deletions src/lang.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ struct left_type* TStructType(char* name) {
res->t = T_STRUCT_TYPE;
res->d.STRUCT_TYPE.name = name;

check_identifier_struct(name);
register_identifier_struct(name);

return res;
}
Expand All @@ -111,7 +111,7 @@ struct left_type* TUnionType(char* name) {
res->t = T_UNION_TYPE;
res->d.UNION_TYPE.name = name;

check_identifier_union(name);
register_identifier_union(name);

return res;
}
Expand All @@ -133,7 +133,7 @@ struct left_type* TEnumType(char* name) {
res->t = T_ENUM_TYPE;
res->d.ENUM_TYPE.name = name;

check_identifier_enum(name);
register_identifier_enum(name);

return res;
}
Expand Down
16 changes: 12 additions & 4 deletions src/lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,9 @@ struct, union, enum names should not overlap with each other.
int conflict_identifier_register_lut[6][6] = {
{1, 1, 0, 0, 0, 1}, // variable
{1, 1, 0, 0, 0, 1}, // enumerator
{0, 0, 1, 1, 1, 0}, // struct
{0, 0, 1, 1, 1, 0}, // union
{0, 0, 1, 1, 1, 0}, // enum
{0, 0, 0, 1, 1, 0}, // struct
{0, 0, 1, 0, 1, 0}, // union
{0, 0, 1, 1, 0, 0}, // enum
{1, 1, 0, 0, 0, 1}, // typedef
};

Expand Down Expand Up @@ -271,7 +271,15 @@ void check_identifier_in_table(char* name, enum IdentifierType using_type, struc
printf("Warning: (Line %d) Identifier %s is not registered as %s\n", yylineno, name, description);
for (int i = 0; i < IDENT_TYPE_COUNT; i++) {
if (info->flags & (1 << i)) {
pdebug(" - Identifier %s is registered as %s at line %d\n", name, identifier_type_str[i], info->lineno[i]);
printf(" - Identifier %s is registered as %s at line %d\n", name, identifier_type_str[i], info->lineno[i]);
if (using_type == IDENT_TYPE_TYPEDEF &&
(i == IDENT_TYPE_STRUCT || i == IDENT_TYPE_UNION ||
i == IDENT_TYPE_ENUM)) {
// e.g. Got `Foo foo` but `Foo` is registered as struct.
// Suggest to use `struct Foo foo` instead.
printf(" - Maybe you want to use %s %s instead of %s\n",
identifier_type_str[i], name, name);
}
}
}
}
Expand Down
Loading