Compare commits

...

5 Commits

@ -0,0 +1,96 @@
#!/bin/bash
# 测试脚本
# 用法: ./run_tests.sh [--verbose] [<单个测试文件>]
COMPILER="./build/bin/compiler"
TEST_DIR="test/test_case"
RESULT_FILE="result.txt"
VERBOSE=0
# 解析参数
if [[ "$1" == "--verbose" ]]; then
VERBOSE=1
shift
fi
# 检查编译器是否存在
if [ ! -f "$COMPILER" ]; then
echo "错误: 编译器未找到于 $COMPILER"
echo "请先完成项目构建 (cmake 和 make)"
exit 1
fi
# 如果指定了单个文件,检查文件是否存在
if [ -n "$1" ] && [ ! -f "$1" ]; then
echo "错误: 文件 $1 不存在"
exit 1
fi
# 清空(或创建)结果文件
> "$RESULT_FILE"
# 计数器
total=0
passed=0
failed=0
echo "开始测试 SysY 解析..."
echo "输出将保存到 $RESULT_FILE"
echo "------------------------"
# 确定测试文件列表
if [ -n "$1" ]; then
# 使用提供的单个文件
TEST_FILES=("$1")
else
# 收集所有 .sy 文件
mapfile -t TEST_FILES < <(find "$TEST_DIR" -type f -name "*.sy" | sort)
fi
for file in "${TEST_FILES[@]}"; do
((total++))
if [ $VERBOSE -eq 1 ]; then
echo "测试文件: $file"
else
echo -n "测试 $file ... "
fi
echo "========== $file ==========" >> "$RESULT_FILE"
if [ $VERBOSE -eq 1 ]; then
"$COMPILER" --emit-parse-tree "$file" 2>&1 | tee -a "$RESULT_FILE"
result=${PIPESTATUS[0]}
else
"$COMPILER" --emit-parse-tree "$file" >> "$RESULT_FILE" 2>&1
result=$?
fi
echo "" >> "$RESULT_FILE"
if [ $result -eq 0 ]; then
if [ $VERBOSE -eq 0 ]; then
echo "通过"
fi
((passed++))
else
if [ $VERBOSE -eq 0 ]; then
echo "失败"
else
echo ">>> 解析失败: $file"
fi
((failed++))
fi
done
echo "------------------------"
echo "总计: $total"
echo "通过: $passed"
echo "失败: $failed"
echo "详细输出已保存至 $RESULT_FILE"
if [ $failed -gt 0 ]; then
exit 1
else
exit 0
fi

@ -1,68 +1,154 @@
// SysY 子集语法:支持形如
// int main() { int a = 1; int b = 2; return a + b; }
// 的最小返回表达式编译。
// 后续需要自行添加
// SysY 子集语法
grammar SysY;
/*===-------------------------------------------===*/
/* Lexer rules */
/*===-------------------------------------------===*/
//----词法规则(优先级从高到低)----//
//keywords
Void : 'void';
Int : 'int';
Float : 'float';
Const : 'const';
If : 'if';
Else : 'else';
While : 'while';
Break : 'break';
Continue: 'continue';
Return : 'return';
//Two-character operator (long preferred)
LeOp : '<=';
GeOp : '>=';
EqOp : '==';
NeOp : '!=';
AndOp : '&&';
OrOp : '||';
//single character operators
AddOp : '+';
SubOp : '-';
MulOp : '*';
DivOp : '/';
QuoOp : '%';
NotOp : '!';
LOp : '<';
GOp : '>';
Assign : '=';
//Separator
Semi : ';';
Comma : ',';
L_PAREN : '(';
R_PAREN : ')';
L_BRACK : '[';
R_BRACK : ']';
L_BRACE : '{';
R_BRACE : '}';
// 16进制float first
HEX_FLOAT
: '0' [xX](
// 形式1: 0x1.921fb6p+1 (有小数点和指数)
HEX_DIGIT* '.' HEX_DIGIT+ [pP] [+-]? DECIMAL_DIGIT+
| // 形式2: 0x1p+1 (没有小数点,只有指数)
HEX_DIGIT+ [pP] [+-]? DECIMAL_DIGIT+
| // 形式3: 0x.AP-3 (小数点开头)
'.' HEX_DIGIT+ [pP] [+-]? DECIMAL_DIGIT+
)
;
fragment HEX_DIGIT: [0-9a-fA-F];
fragment DECIMAL_DIGIT: [0-9];
// 10进制float
DEC_FLOAT
: [0-9]+ '.' [0-9]* EXP? //1.2/1./1.2e10/...
| '.' [0-9]+ EXP? //.1/.1e2
| [0-9]+ EXP //1e2/1e-2
;
fragment EXP: [eE] [+-]? [0-9]+;
HEX_INT: '0' [xX] [0-9a-fA-F]+; //16进制
OCTAL_INT: '0' [0-7]*; //8进制
DECIMAL_INT: [1-9][0-9]*; //10进制
ZERO: '0'; //单独0
// 标识符
Ident
: [a-zA-Z_][a-zA-Z_0-9]*
;
INT: 'int';
RETURN: 'return';
// 注释和空白
WS
: [ \t\r\n]+ -> skip
;
ASSIGN: '=';
ADD: '+';
COMMENT
: '//' ~[\r\n]* -> skip
;
LPAREN: '(';
RPAREN: ')';
LBRACE: '{';
RBRACE: '}';
SEMICOLON: ';';
BLOCK_COMMENT
: '/*' .*? '*/' -> skip
;
ID: [a-zA-Z_][a-zA-Z_0-9]*;
ILITERAL: [0-9]+;
//----语法规则----//
compUnit
: (funcDef|decl)+ EOF
;
WS: [ \t\r\n] -> skip;
LINECOMMENT: '//' ~[\r\n]* -> skip;
BLOCKCOMMENT: '/*' .*? '*/' -> skip;
decl
: varDecl
| constDecl
;
/*===-------------------------------------------===*/
/* Syntax rules */
/*===-------------------------------------------===*/
constDecl
: Const bType constDef (Comma constDef)* Semi
;
compUnit
: funcDef EOF
bType
: Int
| Float
;
decl
: btype varDef SEMICOLON
constDef
: Ident (L_BRACK constExp R_BRACK)* Assign constInitVal
;
constInitVal
: constExp
| L_BRACE (constInitVal (Comma constInitVal)*)? R_BRACE
;
btype
: INT
varDecl
: bType varDef (Comma varDef)* Semi
;
varDef
: lValue (ASSIGN initValue)?
: Ident (L_BRACK constExp R_BRACK)* (Assign initVal)?
;
initValue
initVal
: exp
| L_BRACE (initVal (Comma initVal)*)? R_BRACE
;
funcDef
: funcType ID LPAREN RPAREN blockStmt
: funcType Ident L_PAREN (funcFParams)? R_PAREN block
;
funcType
: INT
: Void
| Int
| Float
;
funcFParams
: funcFParam (Comma funcFParam)*
;
funcFParam
: bType Ident (L_BRACK R_BRACK (L_BRACK exp R_BRACK)*)?
;
blockStmt
: LBRACE blockItem* RBRACE
block
: L_BRACE (blockItem)* R_BRACE
;
blockItem
@ -71,28 +157,85 @@ blockItem
;
stmt
: returnStmt
: lVal Assign exp Semi
| (exp)? Semi
| block
| If L_PAREN cond R_PAREN stmt (Else stmt)?
| While L_PAREN cond R_PAREN stmt
| Break Semi
| Continue Semi
| Return (exp)? Semi
;
returnStmt
: RETURN exp SEMICOLON
exp
: addExp
;
exp
: LPAREN exp RPAREN # parenExp
| var # varExp
| number # numberExp
| exp ADD exp # additiveExp
cond
: lOrExp
;
lVal
: Ident (L_BRACK exp R_BRACK)*
;
primaryExp
: L_PAREN exp R_PAREN
| lVal
| HEX_FLOAT
| DEC_FLOAT
| HEX_INT
| OCTAL_INT
| DECIMAL_INT
| ZERO
;
unaryExp
: primaryExp
| Ident L_PAREN (funcRParams)? R_PAREN
| unaryOp unaryExp
;
unaryOp
: AddOp
| SubOp
| NotOp
;
funcRParams
: exp (Comma exp)*
;
mulExp
: unaryExp
| mulExp (MulOp|DivOp|QuoOp) unaryExp
;
addExp
: mulExp
| addExp (AddOp|SubOp) mulExp
;
relExp
: addExp
| relExp (LOp|GOp|LeOp|GeOp) addExp
;
eqExp
: relExp
| eqExp (EqOp|NeOp) relExp
;
var
: ID
lAndExp
: eqExp
| lAndExp AndOp eqExp
;
lValue
: ID
lOrExp
: lAndExp
| lOrExp OrOp lAndExp
;
number
: ILITERAL
constExp
: addExp
;

Loading…
Cancel
Save