From ac826fdc06c7018a592e18f412d4b0b1b42b3277 Mon Sep 17 00:00:00 2001 From: Lazin3ss Date: Mon, 16 Jan 2023 02:28:56 -0300 Subject: [PATCH] Add 'while' loop statement to ZSS Syntax example: let i = 0; map(domino) := 0; while < 2 { let i = + 1; while map(domino) < (5 * ) { map(domino) := map(domino) + 1; } } While accepts any conditional expression, meaning, it can create infinite loops. In that case, Ikemen GO will freeze. --- src/bytecode.go | 49 ++++++++++++++++++++++++++++++++++++------------- src/compiler.go | 27 ++++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 14 deletions(-) diff --git a/src/bytecode.go b/src/bytecode.go index fb04d756c..563b87a66 100644 --- a/src/bytecode.go +++ b/src/bytecode.go @@ -2075,6 +2075,7 @@ func (cf callFunction) Run(c *Char, _ []int32) (changeState bool) { } type StateBlock struct { + // Basic block fields persistent int32 persistentIndex int32 ignorehitpause int32 @@ -2082,6 +2083,8 @@ type StateBlock struct { trigger BytecodeExp elseBlock *StateBlock ctrls []StateController + // Loop fields + loopBlock bool } func newStateBlock() *StateBlock { @@ -2107,22 +2110,42 @@ func (b StateBlock) Run(c *Char, ps []int32) (changeState bool) { } } sys.workingChar = c - if len(b.trigger) > 0 && !b.trigger.evalB(c) { - if b.elseBlock != nil { - return b.elseBlock.Run(c, ps) + if b.loopBlock { + for { + if len(b.trigger) > 0 && !b.trigger.evalB(c) { + break + } + for _, sc := range b.ctrls { + switch sc.(type) { + case StateBlock: + default: + if !b.ctrlsIgnorehitpause && c.hitPause() { + continue + } + } + if sc.Run(c, ps) { + return true + } + } } - return false - } - for _, sc := range b.ctrls { - switch sc.(type) { - case StateBlock: - default: - if !b.ctrlsIgnorehitpause && c.hitPause() { - continue + } else { + if len(b.trigger) > 0 && !b.trigger.evalB(c) { + if b.elseBlock != nil { + return b.elseBlock.Run(c, ps) } + return false } - if sc.Run(c, ps) { - return true + for _, sc := range b.ctrls { + switch sc.(type) { + case StateBlock: + default: + if !b.ctrlsIgnorehitpause && c.hitPause() { + continue + } + } + if sc.Run(c, ps) { + return true + } } } if b.persistentIndex >= 0 { diff --git a/src/compiler.go b/src/compiler.go index f1d420cce..ce472ea16 100644 --- a/src/compiler.go +++ b/src/compiler.go @@ -4522,6 +4522,10 @@ func (c *Compiler) subBlock(line *string, root bool, if err := c.switchBlock(line, bl, sbc, numVars); err != nil { return nil, err } + case "while": + if err := c.loopBlock(line, bl, sbc, numVars); err != nil { + return nil, err + } default: return nil, c.yokisinaiToken() } @@ -4663,6 +4667,27 @@ func (c *Compiler) switchBlock(line *string, bl *StateBlock, } return nil } +func (c *Compiler) loopBlock(line *string, bl *StateBlock, + sbc *StateBytecode, numVars *int32) error { + bl.loopBlock = true + switch c.token { + // case "for": // TODO: make for loop + case "while": + expr, _, err := c.readSentence(line) + if err != nil { + return err + } + otk := c.token + if bl.trigger, err = c.fullExpression(&expr, VT_Bool); err != nil { + return err + } + c.token = otk + if err := c.needToken("{"); err != nil { + return err + } + } + return nil +} func (c *Compiler) callFunc(line *string, root bool, ctrls *[]StateController, ret []uint8) error { var cf callFunction @@ -4757,7 +4782,7 @@ func (c *Compiler) stateBlock(line *string, bl *StateBlock, root bool, return c.yokisinaiToken() } return nil - case "if", "ignorehitpause", "persistent", "switch": + case "if", "ignorehitpause", "persistent", "switch", "while": if sbl, err := c.subBlock(line, root, sbc, numVars, bl != nil && bl.ctrlsIgnorehitpause); err != nil { return err