diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..cbd493f --- /dev/null +++ b/run.sh @@ -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 diff --git a/src/antlr4/SysY.g4 b/src/antlr4/SysY.g4 index 263aeef..17cd890 100644 --- a/src/antlr4/SysY.g4 +++ b/src/antlr4/SysY.g4 @@ -1,68 +1,172 @@ -// SysY 子集语法:支持形如 -// int main() { int a = 1; int b = 2; return a + b; } -// 的最小返回表达式编译。 +// SysY 子集语法 +grammar SysY; -// 后续需要自行添加 +//----词法规则(优先级从高到低)----// +//keywords +Void : 'void'; +Int : 'int'; +Float : 'float'; +Const : 'const'; +If : 'if'; +Else : 'else'; +While : 'while'; +Break : 'break'; +Continue: 'continue'; +Return : 'return'; -grammar SysY; +//Two-character operator (long preferred) +LeOp : '<='; +GeOp : '>='; +EqOp : '=='; +NeOp : '!='; +AndOp : '&&'; +OrOp : '||'; -/*===-------------------------------------------===*/ -/* Lexer rules */ -/*===-------------------------------------------===*/ +//single character operators +AddOp : '+'; +SubOp : '-'; +MulOp : '*'; +DivOp : '/'; +QuoOp : '%'; +NotOp : '!'; +LOp : '<'; +GOp : '>'; +Assign : '='; -INT: 'int'; -RETURN: 'return'; +//Separator +Semi : ';'; +Comma : ','; +L_PAREN : '('; +R_PAREN : ')'; +L_BRACK : '['; +R_BRACK : ']'; +L_BRACE : '{'; +R_BRACE : '}'; -ASSIGN: '='; -ADD: '+'; +//const numeric classes +//Number... change +// float first +// 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]; -LPAREN: '('; -RPAREN: ')'; -LBRACE: '{'; -RBRACE: '}'; -SEMICOLON: ';'; +// 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]+; -ID: [a-zA-Z_][a-zA-Z_0-9]*; -ILITERAL: [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 -WS: [ \t\r\n] -> skip; -LINECOMMENT: '//' ~[\r\n]* -> skip; -BLOCKCOMMENT: '/*' .*? '*/' -> skip; +// TODO: 后续完善IR后,移除Number兼容规则 +Number + : HEX_FLOAT + | DEC_FLOAT + | HEX_INT + | OCTAL_INT + | DECIMAL_INT + | ZERO + ; + +// 标识符(放最后) +Ident + : [a-zA-Z_][a-zA-Z_0-9]* + ; -/*===-------------------------------------------===*/ -/* Syntax rules */ -/*===-------------------------------------------===*/ +// 注释和空白 +WS + : [ \t\r\n]+ -> skip + ; + +COMMENT + : '//' ~[\r\n]* -> skip + ; + +BLOCK_COMMENT + : '/*' .*? '*/' -> skip + ; +//----语法规则----// compUnit - : funcDef EOF + : (funcDef|decl|program) EOF + ; + +program + : (decl|funcDef)+ ; decl - : btype varDef SEMICOLON + : varDecl + | constDecl + ; + +constDecl + : Const bType constDef (Comma constDef)* Semi + ; + +bType + : Int + | Float ; -btype - : INT +constDef + : Ident (L_BRACK constExp R_BRACK)* Assign constInitVal + ; + +constInitVal + : constExp + | L_BRACE (constInitVal (Comma constInitVal)*)? R_BRACE + ; + +varDecl + : bType varDef (Comma varDef)* Semi + | Int Ident (Assign exp)? 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 +175,92 @@ 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 + | returnStmt ; returnStmt - : RETURN exp SEMICOLON + : Return (exp)? Semi ; exp - : LPAREN exp RPAREN # parenExp - | var # varExp - | number # numberExp - | exp ADD exp # additiveExp + : addExp + ; + +cond + : lOrExp + ; + +lVal + : Ident (L_BRACK exp R_BRACK)* + ; + +primary + : L_PAREN exp R_PAREN + | lVal + | Number // 让旧代码能用 + | HEX_FLOAT + | DEC_FLOAT + | HEX_INT + | OCTAL_INT + | DECIMAL_INT + | ZERO + | Ident + ; + +unaryExp + : primary + | Ident L_PAREN (funcRParams)? R_PAREN + | unaryOp unaryExp ; -var - : ID +unaryOp + : AddOp + | SubOp + | NotOp ; -lValue - : ID +funcRParams + : exp (Comma exp)* ; -number - : ILITERAL +mulExp + : unaryExp + | mulExp (MulOp|DivOp|QuoOp) unaryExp ; + +addExp + : mulExp + | addExp (AddOp|SubOp) mulExp + | primary (AddOp primary)* + ; + +relExp + : addExp + | relExp (LOp|GOp|LeOp|GeOp) addExp + ; + +eqExp + : relExp + | eqExp (EqOp|NeOp) relExp + ; + +lAndExp + : eqExp + | lAndExp AndOp eqExp + ; + +lOrExp + : lAndExp + | lOrExp OrOp lAndExp + ; + +constExp + : addExp + ; \ No newline at end of file