#toy.g #externs.g /* * statement.d: parsing and code generation of statements. */ uint ELIF_MAX = 10; /* * parseIfStatement - parsing and code generation for the 'if' statement. */ proc parseIfStatement()void: [ELIF_MAX + 1] uint offsets; register uint offsetCount, lastBranch; register TokenKind_t tk; register ResultType_t typ; bool first; first := true; offsetCount := 0; while nextToken(); if not first then if offsetCount = ELIF_MAX + 1 then abort("too many 'elif's in 'if'"); fi; offsets[offsetCount] := branchForward(false); offsetCount := offsetCount + 1; patchBranch(lastBranch); fi; first := false; typ := parseExpression(); if typ = rt_void then errorHere("condition for 'if' must be expression"); else if typ ~= rt_bool and typ ~= rt_error then errorHere("condition for 'if' must be bool"); fi; lastBranch := branchForward(true); fi; if getSimpleToken() = tk_then then nextToken(); else errorHere("expecting 'then' in 'if'"); fi; if parseStatementList() ~= rt_void then errorHere("'if' expressions not allowed"); fi; tk := getSimpleToken(); if tk ~= tk_elif and tk ~= tk_else and tk ~= tk_fi then errorHere("expecting 'elif', 'else' or 'fi' in 'if'"); fi; tk = tk_elif do od; if tk = tk_else then nextToken(); if offsetCount = ELIF_MAX + 1 then abort("too many 'elif's in 'if'"); fi; offsets[offsetCount] := branchForward(false); offsetCount := offsetCount + 1; patchBranch(lastBranch); if parseStatementList() ~= rt_void then errorHere("'if' expressions not allowed"); fi; tk := getSimpleToken(); else patchBranch(lastBranch); fi; while offsetCount ~= 0 do offsetCount := offsetCount - 1; patchBranch(offsets[offsetCount]); od; if tk = tk_fi then nextToken(); else errorHere("expecting 'fi' to end 'if'"); fi; corp; /* * parseWhileStatement - parsing and code generation for the 'while' statement. */ proc parseWhileStatement()void: register uint loopPos, branchPos; register ResultType_t typ; nextToken(); loopPos := getPos(); typ := parseStatementList(); if typ = rt_void then errorHere("condition for 'while' must be expression"); else if typ ~= rt_bool and typ ~= rt_error then errorHere("condition for 'while' must be bool"); fi; branchPos := branchForward(true); fi; if getSimpleToken() = tk_do then nextToken(); else errorHere("expecting 'do' in 'while'"); fi; if parseStatementList() ~= rt_void then errorHere("body of 'while' must not be an expression"); fi; branchTo(loopPos); patchBranch(branchPos); if getSimpleToken() = tk_od then nextToken(); else errorHere("expecting 'od' to end 'while'"); fi; corp; /* * parseReadlnStatement - handle the 'readln' statement. */ proc parseReadlnStatement()void: *SymbolEntry_t se; register TokenKind_t tk; nextToken(); if getSimpleToken() = tk_leftParen then nextToken(); else errorHere("expecting '(' in 'readln' statement"); fi; callSpecial(rc_readLine); while getToken(&se, nil, nil) = tk_id do checkUndef(se); checkAssign(se); if se*.se_type ~= rt_int and se*.se_type ~= rt_error then errorHere("only int variables can be used in 'readln'"); fi; callSpecial(rc_readInt); popVariable(se); nextToken(); tk := getSimpleToken(); if tk = tk_comma then nextToken(); elif tk ~= tk_rightParen then errorHere("expecting ',' between variables in 'readln'"); findClose(); fi; od; if getSimpleToken() = tk_rightParen then nextToken(); else errorHere("expecting ')' to end 'readln' statement"); fi; corp; /* * parseWriteStatement - handle the 'write' statement. */ proc parseWriteStatement()void: *char buffer; ulong number; register TokenKind_t tk; ResultType_t typ; nextToken(); if getSimpleToken() = tk_leftParen then nextToken(); else errorHere("expecting '(' in 'write' statement"); fi; while tk := getToken(nil, &buffer, &number); tk ~= tk_eof and tk ~= tk_rightParen and (tk = tk_id or tk = tk_number or not isStatement()) do if tk = tk_string then emitString(buffer, number); callSpecial(rc_printString); nextToken(); else typ := parseExpression(); if typ = rt_void then errorHere("expecting string or expression in 'write'"); else if typ ~= rt_int and typ ~= rt_error then errorHere("only int values can be used with 'write'"); fi; callSpecial(rc_printInt); fi; fi; tk := getSimpleToken(); if tk = tk_comma then nextToken(); elif tk ~= tk_rightParen then errorHere("expecting ',' between expressions in 'write'"); findClose(); fi; od; if getSimpleToken() = tk_rightParen then nextToken(); else errorHere("expecting ')' to end 'write' statement"); fi; corp; /* * parseAssignmentStatement - handle ':='; variable already given. */ proc parseAssignmentStatement(register *SymbolEntry_t se)void: register ResultType_t typ; checkAssign(se); nextToken(); typ := parseExpression(); if typ = rt_void then errorHere("assignment right-hand-side must be expression"); else if typ ~= se*.se_type and typ ~= rt_error and se*.se_type ~= rt_error then errorHere("assignment incompatibility"); fi; popVariable(se); fi; corp; /* * parseStatement - handle a single statement or expression. */ proc parseStatement()ResultType_t: register TokenKind_t tk; ResultType_t typ; typ := rt_void; tk := getSimpleToken(); if tk = tk_if then parseIfStatement(); elif tk = tk_while then parseWhileStatement(); elif tk = tk_readln then parseReadlnStatement(); elif tk = tk_write then parseWriteStatement(); else typ := parseExpression(); fi; typ corp; /* * parseStatementList - handle a ';'-separated list of statements, which * can end in an expression. */ proc parseStatementList()ResultType_t: register TokenKind_t tk; register ResultType_t typ; typ := rt_void; while tk := getSimpleToken(); tk ~= tk_eof and tk ~= tk_int and tk ~= tk_proc and tk ~= tk_corp and tk ~= tk_elif and tk ~= tk_else and tk ~= tk_fi and tk ~= tk_od and tk ~= tk_do do if tk = tk_semicolon then if typ ~= rt_void and typ ~= rt_error then errorHere("can't use expression as a statement"); fi; nextToken(); typ := rt_void; else typ := parseStatement(); fi; od; typ corp;