forked from NUDT-compiler/nudt-compiler-cpp
parent
4f0036900a
commit
4bb7007fd3
@ -1,17 +1,42 @@
|
||||
// 极简符号表:记录局部变量定义点。
|
||||
// 符号表:记录局部变量/常量/参数定义。
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "SysYParser.h"
|
||||
#include "SysYParser.h"
|
||||
|
||||
enum class BaseTypeKind { Int, Float, Void };
|
||||
|
||||
struct TypeDesc {
|
||||
BaseTypeKind base = BaseTypeKind::Int;
|
||||
std::vector<int> dims; // 为空表示标量;数组维度允许首维为 -1 表示形参不定长
|
||||
bool is_const = false;
|
||||
};
|
||||
|
||||
enum class SymbolKind { Var, Const, Param };
|
||||
|
||||
struct SymbolEntry {
|
||||
SymbolKind kind = SymbolKind::Var;
|
||||
SysYParser::VarDefContext* var_decl = nullptr;
|
||||
SysYParser::ConstDefContext* const_decl = nullptr;
|
||||
SysYParser::FuncFParamContext* param_decl = nullptr;
|
||||
TypeDesc type; // 记录类型信息
|
||||
bool is_const = false;
|
||||
std::optional<int> const_value;
|
||||
};
|
||||
|
||||
class SymbolTable {
|
||||
public:
|
||||
void Add(const std::string& name, SysYParser::VarDefContext* decl);
|
||||
bool Contains(const std::string& name) const;
|
||||
SysYParser::VarDefContext* Lookup(const std::string& name) const;
|
||||
void EnterScope();
|
||||
void ExitScope();
|
||||
|
||||
bool ContainsInCurrentScope(const std::string& name) const;
|
||||
void Add(const std::string& name, const SymbolEntry& entry);
|
||||
const SymbolEntry* Lookup(const std::string& name) const;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, SysYParser::VarDefContext*> table_;
|
||||
};
|
||||
std::vector<std::unordered_map<std::string, SymbolEntry>> scopes_;
|
||||
};
|
||||
@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Reconfigure with IR pipeline enabled, build, then run Lab2 test script.
|
||||
RESULT_FILE="test/test_result/run_lab2_result.log"
|
||||
mkdir -p "$(dirname \"$RESULT_FILE\")"
|
||||
: > "$RESULT_FILE"
|
||||
|
||||
{
|
||||
echo "[run_lab2] start: $(date '+%Y-%m-%d %H:%M:%S')"
|
||||
echo "[run_lab2] logging to: $RESULT_FILE"
|
||||
|
||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCOMPILER_PARSE_ONLY=OFF
|
||||
cmake --build build -j "$(nproc)"
|
||||
bash scripts/test_lab2.sh
|
||||
|
||||
echo "[run_lab2] end: $(date '+%Y-%m-%d %H:%M:%S')"
|
||||
} 2>&1 | tee "$RESULT_FILE"
|
||||
@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Lab2 quick/full verification helper.
|
||||
# Usage:
|
||||
# bash scripts/test_lab2.sh
|
||||
# Optional env vars:
|
||||
# COMPILER=./build/bin/compiler
|
||||
# CASE_DIR=test/test_case/functional
|
||||
# OUT_DIR=test/test_result/lab2_ir
|
||||
# LOG_FILE=test/test_result/lab2_test.log
|
||||
|
||||
COMPILER="${COMPILER:-./build/bin/compiler}"
|
||||
CASE_DIR="${CASE_DIR:-test/test_case/functional}"
|
||||
OUT_DIR="${OUT_DIR:-test/test_result/lab2_ir}"
|
||||
LOG_FILE="${LOG_FILE:-test/test_result/lab2_test.log}"
|
||||
VERIFY_SCRIPT="./scripts/verify_ir.sh"
|
||||
|
||||
if [[ ! -x "$COMPILER" ]]; then
|
||||
echo "compiler not found or not executable: $COMPILER" >&2
|
||||
echo "build first:" >&2
|
||||
echo " cmake -S . -B build -DCMAKE_BUILD_TYPE=Release" >&2
|
||||
echo " cmake --build build -j \"\$(nproc)\"" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -x "$VERIFY_SCRIPT" ]]; then
|
||||
echo "verify script not found or not executable: $VERIFY_SCRIPT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -d "$CASE_DIR" ]]; then
|
||||
echo "case dir not found: $CASE_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$OUT_DIR"
|
||||
|
||||
# Preflight: ensure compiler supports IR emission (not parse-only build).
|
||||
probe_input="$CASE_DIR/simple_add.sy"
|
||||
probe_err="$OUT_DIR/.lab2_probe.err"
|
||||
if [[ -f "$probe_input" ]]; then
|
||||
set +e
|
||||
"$COMPILER" --emit-ir "$probe_input" > /dev/null 2> "$probe_err"
|
||||
probe_rc=$?
|
||||
set -e
|
||||
if [[ $probe_rc -ne 0 ]] && grep -Eiq "parse-only|IR/汇编输出已禁用" "$probe_err"; then
|
||||
echo "detected parse-only compiler build, cannot run Lab2 IR tests." >&2
|
||||
echo "rebuild with IR enabled:" >&2
|
||||
echo " cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCOMPILER_PARSE_ONLY=OFF" >&2
|
||||
echo " cmake --build build -j \"\$(nproc)\"" >&2
|
||||
rm -f "$probe_err"
|
||||
exit 2
|
||||
fi
|
||||
rm -f "$probe_err"
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$LOG_FILE")"
|
||||
: > "$LOG_FILE"
|
||||
|
||||
echo "[Lab2] start test" | tee -a "$LOG_FILE"
|
||||
echo "compiler : $COMPILER" | tee -a "$LOG_FILE"
|
||||
echo "cases : $CASE_DIR" | tee -a "$LOG_FILE"
|
||||
echo "out dir : $OUT_DIR" | tee -a "$LOG_FILE"
|
||||
|
||||
echo "[Step 1] single sample check: simple_add.sy" | tee -a "$LOG_FILE"
|
||||
if "$VERIFY_SCRIPT" "$CASE_DIR/simple_add.sy" "$OUT_DIR" --run >> "$LOG_FILE" 2>&1; then
|
||||
echo "single sample: PASS" | tee -a "$LOG_FILE"
|
||||
else
|
||||
echo "single sample: FAIL" | tee -a "$LOG_FILE"
|
||||
echo "stop here. see log: $LOG_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[Step 2] full functional regression" | tee -a "$LOG_FILE"
|
||||
|
||||
pass=0
|
||||
fail=0
|
||||
total=0
|
||||
failed_list=()
|
||||
|
||||
while IFS= read -r -d '' sy; do
|
||||
total=$((total + 1))
|
||||
name="$(basename "$sy")"
|
||||
echo "[$total] $name" | tee -a "$LOG_FILE"
|
||||
|
||||
if "$VERIFY_SCRIPT" "$sy" "$OUT_DIR" --run >> "$LOG_FILE" 2>&1; then
|
||||
pass=$((pass + 1))
|
||||
echo " PASS" | tee -a "$LOG_FILE"
|
||||
else
|
||||
fail=$((fail + 1))
|
||||
failed_list+=("$sy")
|
||||
echo " FAIL" | tee -a "$LOG_FILE"
|
||||
fi
|
||||
done < <(find "$CASE_DIR" -type f -name "*.sy" -print0 | sort -z)
|
||||
|
||||
echo "" | tee -a "$LOG_FILE"
|
||||
echo "[Summary]" | tee -a "$LOG_FILE"
|
||||
echo "total: $total" | tee -a "$LOG_FILE"
|
||||
echo "pass : $pass" | tee -a "$LOG_FILE"
|
||||
echo "fail : $fail" | tee -a "$LOG_FILE"
|
||||
|
||||
if [[ $fail -gt 0 ]]; then
|
||||
echo "failed cases:" | tee -a "$LOG_FILE"
|
||||
for f in "${failed_list[@]}"; do
|
||||
echo " - $f" | tee -a "$LOG_FILE"
|
||||
done
|
||||
echo "Lab2 target is not fully met yet." | tee -a "$LOG_FILE"
|
||||
echo "see details in $LOG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "All functional cases passed. Lab2 target (functional regression) is met." | tee -a "$LOG_FILE"
|
||||
echo "see details in $LOG_FILE"
|
||||
@ -1,31 +1,148 @@
|
||||
// 当前仅支持 void、i32 和 i32*。
|
||||
// 支持 void/i1/i32/float/ptr/array/function/label。
|
||||
#include "ir/IR.h"
|
||||
|
||||
namespace ir {
|
||||
|
||||
Type::Type(Kind k) : kind_(k) {}
|
||||
|
||||
Type::Type(Kind k, std::shared_ptr<Type> elem, size_t count)
|
||||
: kind_(k), elem_type_(std::move(elem)), array_size_(count) {}
|
||||
|
||||
Type::Type(Kind k, std::shared_ptr<Type> ret,
|
||||
std::vector<std::shared_ptr<Type>> params, bool is_vararg)
|
||||
: kind_(k), ret_type_(std::move(ret)), param_types_(std::move(params)),
|
||||
is_vararg_(is_vararg) {}
|
||||
|
||||
const std::shared_ptr<Type>& Type::GetVoidType() {
|
||||
static const std::shared_ptr<Type> type = std::make_shared<Type>(Kind::Void);
|
||||
return type;
|
||||
}
|
||||
|
||||
const std::shared_ptr<Type>& Type::GetInt1Type() {
|
||||
static const std::shared_ptr<Type> type = std::make_shared<Type>(Kind::Int1);
|
||||
return type;
|
||||
}
|
||||
|
||||
const std::shared_ptr<Type>& Type::GetInt32Type() {
|
||||
static const std::shared_ptr<Type> type = std::make_shared<Type>(Kind::Int32);
|
||||
return type;
|
||||
}
|
||||
|
||||
const std::shared_ptr<Type>& Type::GetPtrInt32Type() {
|
||||
static const std::shared_ptr<Type> type = std::make_shared<Type>(Kind::PtrInt32);
|
||||
const std::shared_ptr<Type>& Type::GetFloatType() {
|
||||
static const std::shared_ptr<Type> type = std::make_shared<Type>(Kind::Float);
|
||||
return type;
|
||||
}
|
||||
|
||||
const std::shared_ptr<Type>& Type::GetLabelType() {
|
||||
static const std::shared_ptr<Type> type = std::make_shared<Type>(Kind::Label);
|
||||
return type;
|
||||
}
|
||||
|
||||
std::shared_ptr<Type> Type::GetPointerType(std::shared_ptr<Type> elem) {
|
||||
if (!elem) {
|
||||
throw std::runtime_error("PointerType 缺少 element type");
|
||||
}
|
||||
return std::make_shared<Type>(Kind::Pointer, std::move(elem), 0);
|
||||
}
|
||||
|
||||
std::shared_ptr<Type> Type::GetArrayType(std::shared_ptr<Type> elem,
|
||||
size_t count) {
|
||||
if (!elem) {
|
||||
throw std::runtime_error("ArrayType 缺少 element type");
|
||||
}
|
||||
return std::make_shared<Type>(Kind::Array, std::move(elem), count);
|
||||
}
|
||||
|
||||
std::shared_ptr<Type> Type::GetFunctionType(
|
||||
std::shared_ptr<Type> ret, std::vector<std::shared_ptr<Type>> params,
|
||||
bool is_vararg) {
|
||||
if (!ret) {
|
||||
throw std::runtime_error("FunctionType 缺少 return type");
|
||||
}
|
||||
return std::make_shared<Type>(Kind::Function, std::move(ret),
|
||||
std::move(params), is_vararg);
|
||||
}
|
||||
|
||||
Type::Kind Type::GetKind() const { return kind_; }
|
||||
|
||||
bool Type::IsVoid() const { return kind_ == Kind::Void; }
|
||||
|
||||
bool Type::IsInt1() const { return kind_ == Kind::Int1; }
|
||||
|
||||
bool Type::IsInt32() const { return kind_ == Kind::Int32; }
|
||||
|
||||
bool Type::IsPtrInt32() const { return kind_ == Kind::PtrInt32; }
|
||||
bool Type::IsFloat() const { return kind_ == Kind::Float; }
|
||||
|
||||
bool Type::IsPointer() const { return kind_ == Kind::Pointer; }
|
||||
|
||||
bool Type::IsArray() const { return kind_ == Kind::Array; }
|
||||
|
||||
bool Type::IsFunction() const { return kind_ == Kind::Function; }
|
||||
|
||||
bool Type::IsLabel() const { return kind_ == Kind::Label; }
|
||||
|
||||
const std::shared_ptr<Type>& Type::GetElementType() const {
|
||||
if (!elem_type_) {
|
||||
throw std::runtime_error("Type 没有 element type");
|
||||
}
|
||||
return elem_type_;
|
||||
}
|
||||
|
||||
size_t Type::GetArraySize() const {
|
||||
if (!IsArray()) {
|
||||
throw std::runtime_error("Type 不是 array");
|
||||
}
|
||||
return array_size_;
|
||||
}
|
||||
|
||||
const std::shared_ptr<Type>& Type::GetReturnType() const {
|
||||
if (!IsFunction()) {
|
||||
throw std::runtime_error("Type 不是 function");
|
||||
}
|
||||
return ret_type_;
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<Type>>& Type::GetParamTypes() const {
|
||||
if (!IsFunction()) {
|
||||
throw std::runtime_error("Type 不是 function");
|
||||
}
|
||||
return param_types_;
|
||||
}
|
||||
|
||||
bool Type::IsVarArg() const {
|
||||
if (!IsFunction()) {
|
||||
throw std::runtime_error("Type 不是 function");
|
||||
}
|
||||
return is_vararg_;
|
||||
}
|
||||
|
||||
bool Type::Equals(const Type& other) const {
|
||||
if (kind_ != other.kind_) return false;
|
||||
switch (kind_) {
|
||||
case Kind::Pointer:
|
||||
return elem_type_ && other.elem_type_ &&
|
||||
elem_type_->Equals(*other.elem_type_);
|
||||
case Kind::Array:
|
||||
return array_size_ == other.array_size_ && elem_type_ &&
|
||||
other.elem_type_ && elem_type_->Equals(*other.elem_type_);
|
||||
case Kind::Function: {
|
||||
if (!ret_type_ || !other.ret_type_ ||
|
||||
!ret_type_->Equals(*other.ret_type_) ||
|
||||
is_vararg_ != other.is_vararg_ ||
|
||||
param_types_.size() != other.param_types_.size()) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < param_types_.size(); ++i) {
|
||||
if (!param_types_[i] || !other.param_types_[i] ||
|
||||
!param_types_[i]->Equals(*other.param_types_[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,39 +1,162 @@
|
||||
#include "irgen/IRGen.h"
|
||||
|
||||
#include <any>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "SysYParser.h"
|
||||
#include "ir/IR.h"
|
||||
#include "utils/Log.h"
|
||||
|
||||
// 语句生成当前只实现了最小子集。
|
||||
// 目前支持:
|
||||
// - return <exp>;
|
||||
//
|
||||
// 还未支持:
|
||||
// - 赋值语句
|
||||
// - if / while 等控制流
|
||||
// - 空语句、块语句嵌套分发之外的更多语句形态
|
||||
|
||||
std::any IRGenImpl::visitStmt(SysYParser::StmtContext* ctx) {
|
||||
if (!ctx) {
|
||||
throw std::runtime_error(FormatError("irgen", "缺少语句"));
|
||||
if (!ctx) return {};
|
||||
if (ctx->lVal() && ctx->ASSIGN()) {
|
||||
ir::Value* addr = GetLValAddress(ctx->lVal());
|
||||
ir::Value* val = EvalExp(ctx->exp());
|
||||
BoundDecl bound = sema_.ResolveVarUse(ctx->lVal());
|
||||
const TypeDesc* ty = nullptr;
|
||||
if (bound.kind == BoundDecl::Kind::Var && bound.var_decl) {
|
||||
ty = sema_.GetVarType(bound.var_decl);
|
||||
} else if (bound.kind == BoundDecl::Kind::Param && bound.param_decl) {
|
||||
ty = sema_.GetParamType(bound.param_decl);
|
||||
} else if (bound.kind == BoundDecl::Kind::Const) {
|
||||
throw std::runtime_error(FormatError("irgen", "不能给常量赋值"));
|
||||
}
|
||||
if (!ty && ctx->lVal()->ID()) {
|
||||
const auto name = ctx->lVal()->ID()->getText();
|
||||
for (const auto& kv : var_storage_) {
|
||||
auto* def = const_cast<SysYParser::VarDefContext*>(kv.first);
|
||||
if (def && def->ID() && def->ID()->getText() == name) {
|
||||
ty = sema_.GetVarType(kv.first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ty) {
|
||||
for (const auto& kv : const_storage_) {
|
||||
auto* def = const_cast<SysYParser::ConstDefContext*>(kv.first);
|
||||
if (def && def->ID() && def->ID()->getText() == name) {
|
||||
throw std::runtime_error(FormatError("irgen", "不能给常量赋值"));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ty) {
|
||||
for (const auto& kv : param_storage_) {
|
||||
auto* def = const_cast<SysYParser::FuncFParamContext*>(kv.first);
|
||||
if (def && def->ID() && def->ID()->getText() == name) {
|
||||
ty = sema_.GetParamType(kv.first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ty) {
|
||||
for (const auto& kv : global_var_storage_) {
|
||||
auto* def = const_cast<SysYParser::VarDefContext*>(kv.first);
|
||||
if (def && def->ID() && def->ID()->getText() == name) {
|
||||
ty = sema_.GetVarType(kv.first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ty) {
|
||||
for (const auto& kv : global_const_storage_) {
|
||||
auto* def = const_cast<SysYParser::ConstDefContext*>(kv.first);
|
||||
if (def && def->ID() && def->ID()->getText() == name) {
|
||||
throw std::runtime_error(FormatError("irgen", "不能给常量赋值"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ty) {
|
||||
throw std::runtime_error(FormatError("irgen", "无法解析赋值类型"));
|
||||
}
|
||||
if (ty->base == BaseTypeKind::Float && val->IsInt32()) {
|
||||
val = CastToFloat(val);
|
||||
} else if (ty->base == BaseTypeKind::Int && val->IsFloat()) {
|
||||
val = CastToInt(val);
|
||||
}
|
||||
builder_.CreateStore(val, addr);
|
||||
return BlockFlow::Continue;
|
||||
}
|
||||
if (ctx->block()) {
|
||||
return ctx->block()->accept(this);
|
||||
}
|
||||
if (ctx->returnStmt()) {
|
||||
return ctx->returnStmt()->accept(this);
|
||||
if (ctx->IF()) {
|
||||
auto* then_bb = func_->CreateBlock("if.then");
|
||||
auto* else_bb = func_->CreateBlock("if.else");
|
||||
auto* merge_bb = func_->CreateBlock("if.end");
|
||||
EmitCondBr(ctx->cond(), then_bb, else_bb);
|
||||
|
||||
builder_.SetInsertPoint(then_bb);
|
||||
auto then_flow = std::any_cast<BlockFlow>(ctx->stmt(0)->accept(this));
|
||||
if (then_flow != BlockFlow::Terminated) {
|
||||
builder_.CreateBr(merge_bb);
|
||||
}
|
||||
|
||||
builder_.SetInsertPoint(else_bb);
|
||||
if (ctx->stmt(1)) {
|
||||
auto else_flow = std::any_cast<BlockFlow>(ctx->stmt(1)->accept(this));
|
||||
if (else_flow != BlockFlow::Terminated) {
|
||||
builder_.CreateBr(merge_bb);
|
||||
}
|
||||
} else {
|
||||
builder_.CreateBr(merge_bb);
|
||||
}
|
||||
|
||||
builder_.SetInsertPoint(merge_bb);
|
||||
return BlockFlow::Continue;
|
||||
}
|
||||
throw std::runtime_error(FormatError("irgen", "暂不支持的语句类型"));
|
||||
}
|
||||
if (ctx->WHILE()) {
|
||||
auto* cond_bb = func_->CreateBlock("while.cond");
|
||||
auto* body_bb = func_->CreateBlock("while.body");
|
||||
auto* end_bb = func_->CreateBlock("while.end");
|
||||
builder_.CreateBr(cond_bb);
|
||||
|
||||
builder_.SetInsertPoint(cond_bb);
|
||||
EmitCondBr(ctx->cond(), body_bb, end_bb);
|
||||
|
||||
std::any IRGenImpl::visitReturnStmt(SysYParser::ReturnStmtContext* ctx) {
|
||||
if (!ctx) {
|
||||
throw std::runtime_error(FormatError("irgen", "缺少 return 语句"));
|
||||
builder_.SetInsertPoint(body_bb);
|
||||
PushLoop(end_bb, cond_bb);
|
||||
auto body_flow = std::any_cast<BlockFlow>(ctx->stmt(0)->accept(this));
|
||||
PopLoop();
|
||||
if (body_flow != BlockFlow::Terminated) {
|
||||
builder_.CreateBr(cond_bb);
|
||||
}
|
||||
|
||||
builder_.SetInsertPoint(end_bb);
|
||||
return BlockFlow::Continue;
|
||||
}
|
||||
if (ctx->BREAK()) {
|
||||
auto* target = CurrentBreak();
|
||||
if (!target) {
|
||||
throw std::runtime_error(FormatError("irgen", "break 不在循环内"));
|
||||
}
|
||||
builder_.CreateBr(target);
|
||||
return BlockFlow::Terminated;
|
||||
}
|
||||
if (ctx->CONTINUE()) {
|
||||
auto* target = CurrentContinue();
|
||||
if (!target) {
|
||||
throw std::runtime_error(FormatError("irgen", "continue 不在循环内"));
|
||||
}
|
||||
builder_.CreateBr(target);
|
||||
return BlockFlow::Terminated;
|
||||
}
|
||||
if (ctx->RETURN()) {
|
||||
if (!ctx->exp()) {
|
||||
builder_.CreateRetVoid();
|
||||
return BlockFlow::Terminated;
|
||||
}
|
||||
ir::Value* v = EvalExp(ctx->exp());
|
||||
auto ret_ty = func_->GetReturnType();
|
||||
if (ret_ty->IsFloat() && v->IsInt32()) {
|
||||
v = CastToFloat(v);
|
||||
} else if (ret_ty->IsInt32() && v->IsFloat()) {
|
||||
v = CastToInt(v);
|
||||
}
|
||||
builder_.CreateRet(v);
|
||||
return BlockFlow::Terminated;
|
||||
}
|
||||
if (!ctx->exp()) {
|
||||
throw std::runtime_error(FormatError("irgen", "return 缺少表达式"));
|
||||
if (ctx->exp()) {
|
||||
EvalExp(ctx->exp());
|
||||
}
|
||||
ir::Value* v = EvalExpr(*ctx->exp());
|
||||
builder_.CreateRet(v);
|
||||
return BlockFlow::Terminated;
|
||||
}
|
||||
return BlockFlow::Continue;
|
||||
}
|
||||
@ -1,4 +1,54 @@
|
||||
// SysY 运行库实现:
|
||||
// - 按实验/评测规范提供 I/O 等函数实现
|
||||
// - 与编译器生成的目标代码链接,支撑运行时行为
|
||||
#include <stdio.h>
|
||||
|
||||
int getint() {
|
||||
int v = 0;
|
||||
if (scanf("%d", &v) != 1) return 0;
|
||||
return v;
|
||||
}
|
||||
|
||||
int getch() { return getchar(); }
|
||||
|
||||
int getarray(int a[]) {
|
||||
int n = 0;
|
||||
if (scanf("%d", &n) != 1) return 0;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
scanf("%d", &a[i]);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void putint(int x) { printf("%d", x); }
|
||||
|
||||
void putch(int x) { putchar(x); }
|
||||
|
||||
void putarray(int n, int a[]) {
|
||||
printf("%d", n);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
printf(" %d", a[i]);
|
||||
}
|
||||
}
|
||||
|
||||
float getfloat() {
|
||||
float v = 0.0f;
|
||||
if (scanf("%f", &v) != 1) return 0.0f;
|
||||
return v;
|
||||
}
|
||||
|
||||
int getfarray(float a[]) {
|
||||
int n = 0;
|
||||
if (scanf("%d", &n) != 1) return 0;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
scanf("%f", &a[i]);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void putfloat(float x) { printf("%a", x); }
|
||||
|
||||
void putfarray(int n, float a[]) {
|
||||
printf("%d:", n);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
printf(" %a", a[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in new issue