From afc340d52d7e2562fb970156243a09292b3cf7f1 Mon Sep 17 00:00:00 2001 From: Mattias Hansson Date: Tue, 4 Nov 2025 20:47:04 +0100 Subject: [PATCH] IF and WHILE now take single operator --- internal/commands/comparison.go | 22 +++++++++- internal/commands/if.go | 45 +++++++++++++------- internal/commands/while.go | 75 ++++++++++++--------------------- 3 files changed, 78 insertions(+), 64 deletions(-) diff --git a/internal/commands/comparison.go b/internal/commands/comparison.go index 874b428..0b82765 100644 --- a/internal/commands/comparison.go +++ b/internal/commands/comparison.go @@ -176,7 +176,7 @@ func (cg *comparisonGenerator) cmpOperand(op *operandInfo, offset int) string { // tempLabel creates and returns a temporary label func (cg *comparisonGenerator) tempLabel() string { label := cg.generalStack.Push() - cg.generalStack.Pop() + _, _ = cg.generalStack.Pop() return label } @@ -633,3 +633,23 @@ func inferKindFromValue(val uint16) compiler.VarKind { } return compiler.KindWord } + +// parseOperator converts string operator to comparisonOp +func parseOperator(op string) (comparisonOp, error) { + switch op { + case "=", "==": + return opEqual, nil + case "<>", "!=": + return opNotEqual, nil + case ">": + return opGreater, nil + case "<": + return opLess, nil + case ">=": + return opGreaterEqual, nil + case "<=": + return opLessEqual, nil + default: + return 0, fmt.Errorf("unsupported operator %q", op) + } +} diff --git a/internal/commands/if.go b/internal/commands/if.go index a4d79c2..496fa79 100644 --- a/internal/commands/if.go +++ b/internal/commands/if.go @@ -34,13 +34,12 @@ func (c *IfCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext) return err } - if len(params) != 4 { - return fmt.Errorf("IF: expected 4 parameters, got %d", len(params)) + // IF or IF [THEN] + if len(params) < 2 { + return fmt.Errorf("IF: expected at least 2 parameters, got %d", len(params)) } - c.operator = normalizeOperator(params[2]) scope := ctx.CurrentScope() - constLookup := func(name string) (int64, bool) { sym := ctx.SymbolTable.Lookup(name, scope) if sym != nil && sym.IsConst() { @@ -62,17 +61,31 @@ func (c *IfCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext) isVar: isVar, } - // Parse param2 - varName, varKind, value, isVar, err = compiler.ParseOperandParam( - params[3], ctx.SymbolTable, scope, constLookup) - if err != nil { - return fmt.Errorf("IF: param2: %w", err) - } - c.param2 = &operandInfo{ - varName: varName, - varKind: varKind, - value: value, - isVar: isVar, + // Single param: IF + if len(params) == 2 || (len(params) == 3 && strings.ToUpper(params[2]) == "THEN") { + c.operator = "!=" + c.param2 = nil + } else { + // Full comparison: IF [THEN] + if len(params) == 5 && strings.ToUpper(params[4]) != "THEN" { + return fmt.Errorf("IF: expected THEN when 5 parameters, got %s", params[4]) + } else if len(params) != 4 && len(params) != 5 { + return fmt.Errorf("IF: expected 2, 3, 4, or 5 parameters, got %d", len(params)) + } + + c.operator = params[2] + // Parse param2 + varName, varKind, value, isVar, err = compiler.ParseOperandParam( + params[3], ctx.SymbolTable, scope, constLookup) + if err != nil { + return fmt.Errorf("IF: param2: %w", err) + } + c.param2 = &operandInfo{ + varName: varName, + varKind: varKind, + value: value, + isVar: isVar, + } } // Check pragma @@ -80,7 +93,7 @@ func (c *IfCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext) longJumpPragma := ps.GetPragma("_P_USE_LONG_JUMP") c.useLongJump = longJumpPragma != "" && longJumpPragma != "0" - // Create skip label (jumps here on FALSE, or to ELSE if present) + // Create skip label c.skipLabel = ctx.IfStack.Push() return nil diff --git a/internal/commands/while.go b/internal/commands/while.go index 7e44442..94c53d5 100644 --- a/internal/commands/while.go +++ b/internal/commands/while.go @@ -35,13 +35,12 @@ func (c *WhileCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContex return err } - if len(params) != 4 { - return fmt.Errorf("WHILE: expected 4 parameters, got %d", len(params)) + // WHILE or WHILE [DO] + if len(params) < 2 { + return fmt.Errorf("WHILE: expected at least 2 parameters, got %d", len(params)) } - c.operator = normalizeOperator(params[2]) scope := ctx.CurrentScope() - constLookup := func(name string) (int64, bool) { sym := ctx.SymbolTable.Lookup(name, scope) if sym != nil && sym.IsConst() { @@ -63,17 +62,31 @@ func (c *WhileCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContex isVar: isVar, } - // Parse param2 - varName, varKind, value, isVar, err = compiler.ParseOperandParam( - params[3], ctx.SymbolTable, scope, constLookup) - if err != nil { - return fmt.Errorf("WHILE: param2: %w", err) - } - c.param2 = &operandInfo{ - varName: varName, - varKind: varKind, - value: value, - isVar: isVar, + // Single param: WHILE + if len(params) == 2 || (len(params) == 3 && strings.ToUpper(params[2]) == "DO") { + c.operator = "!=" + c.param2 = nil + } else { + // Full comparison: WHILE [DO] + if len(params) == 5 && strings.ToUpper(params[4]) != "DO" { + return fmt.Errorf("WHILE: expected DO when 5 parameters, got %s", params[4]) + } else if len(params) != 4 && len(params) != 5 { + return fmt.Errorf("WHILE: expected 2, 3, 4, or 5 parameters, got %d", len(params)) + } + + c.operator = params[2] + // Parse param2 + varName, varKind, value, isVar, err = compiler.ParseOperandParam( + params[3], ctx.SymbolTable, scope, constLookup) + if err != nil { + return fmt.Errorf("WHILE: param2: %w", err) + } + c.param2 = &operandInfo{ + varName: varName, + varKind: varKind, + value: value, + isVar: isVar, + } } // Check pragma @@ -118,35 +131,3 @@ func (c *WhileCommand) Generate(ctx *compiler.CompilerContext) ([]string, error) asm = append(asm, cmpAsm...) return asm, nil } - -// normalizeOperator converts operator variants to canonical form -func normalizeOperator(op string) string { - switch op { - case "==": - return "=" - case "!=": - return "<>" - default: - return op - } -} - -// parseOperator converts string operator to comparisonOp -func parseOperator(op string) (comparisonOp, error) { - switch op { - case "=": - return opEqual, nil - case "<>": - return opNotEqual, nil - case ">": - return opGreater, nil - case "<": - return opLess, nil - case ">=": - return opGreaterEqual, nil - case "<=": - return opLessEqual, nil - default: - return 0, fmt.Errorf("unsupported operator %q", op) - } -}