Compare commits
21 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
2de1561210 | 2 weeks ago |
|
|
5cc0bf587a | 2 weeks ago |
|
|
d2f49b71c5 | 2 weeks ago |
|
|
b740348401 | 2 weeks ago |
|
|
8ba3b01271 | 2 weeks ago |
|
|
8297700496 | 2 weeks ago |
|
|
b1477851c6 | 2 weeks ago |
|
|
fe3a3410a6 | 2 weeks ago |
|
|
02e5a7d4e7 | 2 weeks ago |
|
|
c858f75d9a | 2 weeks ago |
|
|
83b6f17c78 | 2 weeks ago |
|
|
a85898b35a | 2 weeks ago |
|
|
b3230ec6d5 | 2 weeks ago |
|
|
304599b17b | 2 weeks ago |
|
|
3e066b8375 | 2 weeks ago |
|
|
3342955abb | 2 weeks ago |
|
|
7dd139671b | 3 weeks ago |
|
|
3e4165c4bb | 3 weeks ago |
|
|
d5469f93f6 | 3 weeks ago |
|
|
18366d3cc8 | 3 weeks ago |
|
|
3d0361e648 | 3 weeks ago |
@ -1,30 +1,69 @@
|
|||||||
// 基于语法树的语义检查与名称绑定。
|
// 基于语法树的语义检查与名称绑定。
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "SysYParser.h"
|
#include "SysYParser.h"
|
||||||
|
|
||||||
|
enum class SemanticType {
|
||||||
|
Void,
|
||||||
|
Int,
|
||||||
|
Float,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ScalarConstant {
|
||||||
|
SemanticType type = SemanticType::Int;
|
||||||
|
double number = 0.0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ObjectBinding {
|
||||||
|
enum class DeclKind {
|
||||||
|
Var,
|
||||||
|
Const,
|
||||||
|
Param,
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
SemanticType type = SemanticType::Int;
|
||||||
|
DeclKind decl_kind = DeclKind::Var;
|
||||||
|
bool is_array_param = false;
|
||||||
|
std::vector<int> dimensions;
|
||||||
|
const SysYParser::VarDefContext* var_def = nullptr;
|
||||||
|
const SysYParser::ConstDefContext* const_def = nullptr;
|
||||||
|
const SysYParser::FuncFParamContext* func_param = nullptr;
|
||||||
|
bool has_const_value = false;
|
||||||
|
ScalarConstant const_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FunctionBinding {
|
||||||
|
std::string name;
|
||||||
|
SemanticType return_type = SemanticType::Int;
|
||||||
|
std::vector<ObjectBinding> params;
|
||||||
|
const SysYParser::FuncDefContext* func_def = nullptr;
|
||||||
|
bool is_builtin = false;
|
||||||
|
};
|
||||||
|
|
||||||
class SemanticContext {
|
class SemanticContext {
|
||||||
public:
|
public:
|
||||||
void BindVarUse(SysYParser::VarContext* use,
|
void BindObjectUse(const SysYParser::LValContext* use, ObjectBinding binding);
|
||||||
SysYParser::VarDefContext* decl) {
|
const ObjectBinding* ResolveObjectUse(
|
||||||
var_uses_[use] = decl;
|
const SysYParser::LValContext* use) const;
|
||||||
}
|
|
||||||
|
void BindFunctionCall(const SysYParser::UnaryExpContext* call,
|
||||||
|
FunctionBinding binding);
|
||||||
|
const FunctionBinding* ResolveFunctionCall(
|
||||||
|
const SysYParser::UnaryExpContext* call) const;
|
||||||
|
|
||||||
SysYParser::VarDefContext* ResolveVarUse(
|
void RegisterFunction(FunctionBinding binding);
|
||||||
const SysYParser::VarContext* use) const {
|
const FunctionBinding* ResolveFunction(const std::string& name) const;
|
||||||
auto it = var_uses_.find(use);
|
|
||||||
return it == var_uses_.end() ? nullptr : it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_map<const SysYParser::VarContext*,
|
std::unordered_map<const SysYParser::LValContext*, ObjectBinding> object_uses_;
|
||||||
SysYParser::VarDefContext*>
|
std::unordered_map<const SysYParser::UnaryExpContext*, FunctionBinding>
|
||||||
var_uses_;
|
function_calls_;
|
||||||
|
std::unordered_map<std::string, FunctionBinding> functions_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 目前仅检查:
|
|
||||||
// - 变量先声明后使用
|
|
||||||
// - 局部变量不允许重复定义
|
|
||||||
SemanticContext RunSema(SysYParser::CompUnitContext& comp_unit);
|
SemanticContext RunSema(SysYParser::CompUnitContext& comp_unit);
|
||||||
|
|||||||
@ -1,17 +1,25 @@
|
|||||||
// 极简符号表:记录局部变量定义点。
|
// 维护对象符号的多层作用域。
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "SysYParser.h"
|
#include "sem/Sema.h"
|
||||||
|
|
||||||
class SymbolTable {
|
class SymbolTable {
|
||||||
public:
|
public:
|
||||||
void Add(const std::string& name, SysYParser::VarDefContext* decl);
|
SymbolTable();
|
||||||
bool Contains(const std::string& name) const;
|
|
||||||
SysYParser::VarDefContext* Lookup(const std::string& name) const;
|
void EnterScope();
|
||||||
|
void ExitScope();
|
||||||
|
|
||||||
|
bool Add(const ObjectBinding& symbol);
|
||||||
|
bool ContainsInCurrentScope(std::string_view name) const;
|
||||||
|
const ObjectBinding* Lookup(std::string_view name) const;
|
||||||
|
size_t Depth() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_map<std::string, SysYParser::VarDefContext*> table_;
|
std::vector<std::unordered_map<std::string, ObjectBinding>> scopes_;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,83 @@
|
|||||||
|
--- include/ir/IR.h
|
||||||
|
+++ include/ir/IR.h
|
||||||
|
@@ -93,6 +93,7 @@
|
||||||
|
class Type {
|
||||||
|
public:
|
||||||
|
- enum class Kind { Void, Int32, PtrInt32 };
|
||||||
|
+ enum class Kind { Void, Int1, Int32, PtrInt32 };
|
||||||
|
explicit Type(Kind k);
|
||||||
|
// 使用静态共享对象获取类型。
|
||||||
|
// 同一类型可直接比较返回值是否相等,例如:
|
||||||
|
// Type::GetInt32Type() == Type::GetInt32Type()
|
||||||
|
static const std::shared_ptr<Type>& GetVoidType();
|
||||||
|
+ static const std::shared_ptr<Type>& GetInt1Type();
|
||||||
|
static const std::shared_ptr<Type>& GetInt32Type();
|
||||||
|
static const std::shared_ptr<Type>& GetPtrInt32Type();
|
||||||
|
Kind GetKind() const;
|
||||||
|
bool IsVoid() const;
|
||||||
|
+ bool IsInt1() const;
|
||||||
|
bool IsInt32() const;
|
||||||
|
bool IsPtrInt32() const;
|
||||||
|
@@ -118,6 +119,7 @@
|
||||||
|
const std::string& GetName() const;
|
||||||
|
void SetName(std::string n);
|
||||||
|
bool IsVoid() const;
|
||||||
|
+ bool IsInt1() const;
|
||||||
|
bool IsInt32() const;
|
||||||
|
bool IsPtrInt32() const;
|
||||||
|
bool IsConstant() const;
|
||||||
|
@@ -153,7 +155,9 @@
|
||||||
|
|
||||||
|
// 后续还需要扩展更多指令类型。
|
||||||
|
-// enum class Opcode { Add, Sub, Mul, Alloca, Load, Store, Ret };
|
||||||
|
-enum class Opcode { Add, Sub, Mul, Div, Mod, Neg, Alloca, Load, Store, Ret };
|
||||||
|
+enum class Opcode { Add, Sub, Mul, Div, Mod, Neg, Alloca, Load, Store, Ret, Cmp, Zext, Br, CondBr };
|
||||||
|
+
|
||||||
|
+enum class CmpOp { Eq, Ne, Lt, Gt, Le, Ge };
|
||||||
|
|
||||||
|
// User 是所有“会使用其他 Value 作为输入”的 IR 对象的抽象基类。
|
||||||
|
@@ -231,6 +235,33 @@
|
||||||
|
Value* GetPtr() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
+class CmpInst : public Instruction {
|
||||||
|
+ public:
|
||||||
|
+ CmpInst(CmpOp cmp_op, Value* lhs, Value* rhs, std::string name);
|
||||||
|
+ CmpOp GetCmpOp() const;
|
||||||
|
+ Value* GetLhs() const;
|
||||||
|
+ Value* GetRhs() const;
|
||||||
|
+ private:
|
||||||
|
+ CmpOp cmp_op_;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+class ZextInst : public Instruction {
|
||||||
|
+ public:
|
||||||
|
+ ZextInst(std::shared_ptr<Type> dest_ty, Value* val, std::string name);
|
||||||
|
+ Value* GetValue() const;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+class BranchInst : public Instruction {
|
||||||
|
+ public:
|
||||||
|
+ BranchInst(BasicBlock* dest);
|
||||||
|
+ BasicBlock* GetDest() const;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+class CondBranchInst : public Instruction {
|
||||||
|
+ public:
|
||||||
|
+ CondBranchInst(Value* cond, BasicBlock* true_bb, BasicBlock* false_bb);
|
||||||
|
+ Value* GetCond() const;
|
||||||
|
+ BasicBlock* GetTrueBlock() const;
|
||||||
|
+ BasicBlock* GetFalseBlock() const;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
// BasicBlock 已纳入 Value 体系,便于后续向更完整 IR 类图靠拢。
|
||||||
|
@@ -315,6 +346,10 @@
|
||||||
|
LoadInst* CreateLoad(Value* ptr, const std::string& name);
|
||||||
|
StoreInst* CreateStore(Value* val, Value* ptr);
|
||||||
|
ReturnInst* CreateRet(Value* v);
|
||||||
|
+ CmpInst* CreateCmp(CmpOp op, Value* lhs, Value* rhs, const std::string& name);
|
||||||
|
+ ZextInst* CreateZext(Value* val, const std::string& name);
|
||||||
|
+ BranchInst* CreateBr(BasicBlock* dest);
|
||||||
|
+ CondBranchInst* CreateCondBr(Value* cond, BasicBlock* true_bb, BasicBlock* false_bb);
|
||||||
|
|
||||||
|
private:
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
case_dir="${1:-test/test_case}"
|
||||||
|
log_dir="${2:-test/test_result/lab1_parse_logs}"
|
||||||
|
|
||||||
|
if [[ ! -d "$case_dir" ]]; then
|
||||||
|
echo "测试目录不存在: $case_dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
compiler="./build/bin/compiler"
|
||||||
|
if [[ ! -x "$compiler" ]]; then
|
||||||
|
echo "未找到编译器: $compiler ,请先构建 parse-only 版本。" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$log_dir"
|
||||||
|
|
||||||
|
mapfile -t cases < <(find "$case_dir" -name '*.sy' | sort)
|
||||||
|
if [[ ${#cases[@]} -eq 0 ]]; then
|
||||||
|
echo "未找到任何 .sy 测试文件: $case_dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for f in "${cases[@]}"; do
|
||||||
|
rel="${f#$case_dir/}"
|
||||||
|
safe_name="${rel//\//__}"
|
||||||
|
log_file="$log_dir/${safe_name%.sy}.parse.log"
|
||||||
|
echo "TEST $f -> $log_file"
|
||||||
|
if ! "$compiler" --emit-parse-tree "$f" >"$log_file" 2>&1; then
|
||||||
|
echo "FAIL $f (see $log_file)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "ALL_PARSE_OK (${#cases[@]} cases) logs: $log_dir"
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
mode="${1:-positive}"
|
||||||
|
log_dir="${2:-test/test_result/lab2_sema_logs}"
|
||||||
|
|
||||||
|
checker="./build-sema/sema_check"
|
||||||
|
if [[ ! -x "$checker" ]]; then
|
||||||
|
echo "未找到语义测试驱动: $checker" >&2
|
||||||
|
echo "请先准备 build-sema/sema_check。" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$log_dir"
|
||||||
|
|
||||||
|
case_files=()
|
||||||
|
expected_prefix=""
|
||||||
|
|
||||||
|
case "$mode" in
|
||||||
|
positive)
|
||||||
|
expected_prefix="OK"
|
||||||
|
case_files=(
|
||||||
|
"test/test_case/functional/simple_add.sy"
|
||||||
|
"test/test_case/functional/09_func_defn.sy"
|
||||||
|
"test/test_case/functional/25_scope3.sy"
|
||||||
|
"test/test_case/functional/29_break.sy"
|
||||||
|
"test/test_case/functional/05_arr_defn4.sy"
|
||||||
|
"test/test_case/functional/95_float.sy"
|
||||||
|
)
|
||||||
|
;;
|
||||||
|
negative)
|
||||||
|
expected_prefix="ERR"
|
||||||
|
case_files=(
|
||||||
|
"test/test_case/sema_negative/undef.sy"
|
||||||
|
"test/test_case/sema_negative/break.sy"
|
||||||
|
"test/test_case/sema_negative/ret.sy"
|
||||||
|
"test/test_case/sema_negative/call.sy"
|
||||||
|
)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "用法: $0 [positive|negative] [log_dir]" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ ${#case_files[@]} -eq 0 ]]; then
|
||||||
|
echo "没有可执行的测试用例" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for f in "${case_files[@]}"; do
|
||||||
|
if [[ ! -f "$f" ]]; then
|
||||||
|
echo "测试文件不存在: $f" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
all_ok=true
|
||||||
|
for f in "${case_files[@]}"; do
|
||||||
|
base="$(basename "${f%.sy}")"
|
||||||
|
log_file="$log_dir/${base}.sema.log"
|
||||||
|
echo "TEST $f -> $log_file"
|
||||||
|
set +e
|
||||||
|
"$checker" "$f" >"$log_file" 2>&1
|
||||||
|
status=$?
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if ! grep -q "^${expected_prefix} $f$" "$log_file"; then
|
||||||
|
echo "FAIL $f (see $log_file)" >&2
|
||||||
|
all_ok=false
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$mode" == "positive" && $status -ne 0 ]]; then
|
||||||
|
echo "FAIL $f (expected success, see $log_file)" >&2
|
||||||
|
all_ok=false
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$mode" == "negative" && $status -eq 0 ]]; then
|
||||||
|
echo "FAIL $f (expected semantic error, see $log_file)" >&2
|
||||||
|
all_ok=false
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$all_ok" != true ]]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "ALL_SEMA_${mode^^}_OK (${#case_files[@]} cases) logs: $log_dir"
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,17 +1,39 @@
|
|||||||
// 维护局部变量声明的注册与查找。
|
// 维护对象符号的注册与按作用域查找。
|
||||||
|
|
||||||
#include "sem/SymbolTable.h"
|
#include "sem/SymbolTable.h"
|
||||||
|
|
||||||
void SymbolTable::Add(const std::string& name,
|
#include <stdexcept>
|
||||||
SysYParser::VarDefContext* decl) {
|
|
||||||
table_[name] = decl;
|
SymbolTable::SymbolTable() : scopes_(1) {}
|
||||||
|
|
||||||
|
void SymbolTable::EnterScope() { scopes_.emplace_back(); }
|
||||||
|
|
||||||
|
void SymbolTable::ExitScope() {
|
||||||
|
if (scopes_.size() <= 1) {
|
||||||
|
throw std::runtime_error("symbol table scope underflow");
|
||||||
|
}
|
||||||
|
scopes_.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SymbolTable::Contains(const std::string& name) const {
|
bool SymbolTable::Add(const ObjectBinding& symbol) {
|
||||||
return table_.find(name) != table_.end();
|
auto& scope = scopes_.back();
|
||||||
|
return scope.emplace(symbol.name, symbol).second;
|
||||||
}
|
}
|
||||||
|
|
||||||
SysYParser::VarDefContext* SymbolTable::Lookup(const std::string& name) const {
|
bool SymbolTable::ContainsInCurrentScope(std::string_view name) const {
|
||||||
auto it = table_.find(name);
|
const auto& scope = scopes_.back();
|
||||||
return it == table_.end() ? nullptr : it->second;
|
return scope.find(std::string(name)) != scope.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ObjectBinding* SymbolTable::Lookup(std::string_view name) const {
|
||||||
|
const std::string key(name);
|
||||||
|
for (auto it = scopes_.rbegin(); it != scopes_.rend(); ++it) {
|
||||||
|
auto found = it->find(key);
|
||||||
|
if (found != it->end()) {
|
||||||
|
return &found->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t SymbolTable::Depth() const { return scopes_.size(); }
|
||||||
|
|||||||
Binary file not shown.
@ -0,0 +1 @@
|
|||||||
|
3
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
// 测试:简单加法
|
||||||
|
int main() {
|
||||||
|
int a = 1;
|
||||||
|
int b = 2;
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
37
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
// 测试:减法和乘法
|
||||||
|
int main() {
|
||||||
|
int a = 10;
|
||||||
|
int b = 3;
|
||||||
|
int c = a - b;
|
||||||
|
int d = a * b;
|
||||||
|
return c + d;
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
5
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
// 测试:除法和取模
|
||||||
|
int main() {
|
||||||
|
int a = 20;
|
||||||
|
int b = 6;
|
||||||
|
int c = a / b;
|
||||||
|
int d = a % b;
|
||||||
|
return c + d;
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
5
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
// 测试:一元运算符(正负号)
|
||||||
|
int main() {
|
||||||
|
int a = 5;
|
||||||
|
int b = -a;
|
||||||
|
int c = +10;
|
||||||
|
return b + c;
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
20
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
// 测试:赋值表达式
|
||||||
|
int main() {
|
||||||
|
int a = 10;
|
||||||
|
int b = 20;
|
||||||
|
a = b;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
6
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
// 测试:逗号分隔的多变量声明
|
||||||
|
int main() {
|
||||||
|
int a = 1, b = 2, c = 3;
|
||||||
|
return a + b + c;
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
37
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
// 测试:综合测试(所有功能)
|
||||||
|
int main() {
|
||||||
|
int a = 10, b = 5;
|
||||||
|
int c = a + b;
|
||||||
|
int d = a - b;
|
||||||
|
int e = a * 2;
|
||||||
|
int f = a / b;
|
||||||
|
int g = a % b;
|
||||||
|
int h = -c;
|
||||||
|
int i = +d;
|
||||||
|
a = b + c;
|
||||||
|
b = d + e;
|
||||||
|
return a + b + f + g + h + i;
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
int main(){ break; return 0; }
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
int f(int x){ return x; }
|
||||||
|
int main(){ return f(); }
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
void f(){ return 1; }
|
||||||
|
int main(){ return 0; }
|
||||||
@ -0,0 +1 @@
|
|||||||
|
int main(){ return a; }
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
#include <exception>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "frontend/AntlrDriver.h"
|
||||||
|
#include "sem/Sema.h"
|
||||||
|
#include "utils/Log.h"
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
if (argc < 2) {
|
||||||
|
std::cerr << "usage: sema_check <input.sy> [more.sy...]\n";
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool failed = false;
|
||||||
|
for (int i = 1; i < argc; ++i) {
|
||||||
|
const std::string path = argv[i];
|
||||||
|
try {
|
||||||
|
auto antlr = ParseFileWithAntlr(path);
|
||||||
|
auto* comp_unit = dynamic_cast<SysYParser::CompUnitContext*>(antlr.tree);
|
||||||
|
if (!comp_unit) {
|
||||||
|
throw std::runtime_error(FormatError("sema_check", "语法树根节点不是 compUnit"));
|
||||||
|
}
|
||||||
|
(void)RunSema(*comp_unit);
|
||||||
|
std::cout << "OK " << path << "\n";
|
||||||
|
} catch (const std::exception& ex) {
|
||||||
|
failed = true;
|
||||||
|
std::cout << "ERR " << path << "\n";
|
||||||
|
PrintException(std::cout, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return failed ? 1 : 0;
|
||||||
|
}
|
||||||
Loading…
Reference in new issue