IF and WHILE now take single operator

This commit is contained in:
Mattias Hansson 2025-11-04 20:47:04 +01:00
parent 3d6f786949
commit afc340d52d
3 changed files with 78 additions and 64 deletions

View file

@ -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)
}
}

View file

@ -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 <param1> or IF <param1> <op> <param2> [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 <param1>
if len(params) == 2 || (len(params) == 3 && strings.ToUpper(params[2]) == "THEN") {
c.operator = "!="
c.param2 = nil
} else {
// Full comparison: IF <param1> <op> <param2> [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

View file

@ -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 <param1> or WHILE <param1> <op> <param2> [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 <param1>
if len(params) == 2 || (len(params) == 3 && strings.ToUpper(params[2]) == "DO") {
c.operator = "!="
c.param2 = nil
} else {
// Full comparison: WHILE <param1> <op> <param2> [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)
}
}