diff --git a/compiler/parser/parser.go b/compiler/parser/parser.go index 144bb2c..6ca936f 100644 --- a/compiler/parser/parser.go +++ b/compiler/parser/parser.go @@ -1069,6 +1069,21 @@ func (p *Parser) parseMethodDecl() *ast.MethodDecl { // --- Statement parsing --- +// isIdentSuffix reports whether the given token could follow an +// identifier, signalling that a keyword on the prior slot is being +// used as a variable name (array index, method call, assignment, +// etc.). Used by parseStmtBlock to avoid prematurely ending a +// statement block when a keyword identifier appears. +func isIdentSuffix(k token.Kind) bool { + switch k { + case token.LBRACKET, token.ASSIGN, token.PLUSEQ, token.MINUSEQ, + token.STAREQ, token.SLASHEQ, token.PERCENTEQ, token.POWEREQ, + token.INC, token.DEC, token.COLON, token.DOT: + return true + } + return false +} + // parseStmtBlock parses statements until one of the stop tokens. func (p *Parser) parseStmtBlock(stopTokens ...token.Kind) []ast.Stmt { var stmts []ast.Stmt @@ -1084,6 +1099,14 @@ func (p *Parser) parseStmtBlock(stopTokens ...token.Kind) []ast.Stmt { p.peekAt(1) == token.ASSIGN { break // continue parsing as statement } + // Don't stop at a keyword-used-as-identifier. Harbour + // allows keywords like CASE/DO as variable names, so + // `case[idx]`, `case := 1`, `case:method()` are + // expression statements, not new block arms. Peek the + // next token for identifier-ish suffixes. + if isIdentSuffix(p.peekAt(1)) { + break // treat current token as identifier, parse as stmt + } return stmts } }