diff --git a/docs/readme.md b/docs/readme.md index 1ef10e1..5873bd8 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -33,7 +33,7 @@ You can take a look at the [examples](../examples). go run gotest.tools/gotestsum@latest ``` -This will run over 350 [tests](../tests) in various categories. +This will run over 400 [tests](../tests) in various categories. ## Platforms diff --git a/src/core/CompileAssignDivision.go b/src/core/CompileAssignDivision.go index c75026a..3d4ab0e 100644 --- a/src/core/CompileAssignDivision.go +++ b/src/core/CompileAssignDivision.go @@ -24,13 +24,13 @@ func (f *Function) CompileAssignDivision(expr *expression.Expression) error { ) if expr.Token.Kind == token.Define { - quotientVariable, err = f.Define(variables.Children[0]) + quotientVariable, err = f.NewVariable(variables.Children[0]) if err != nil { return err } - remainderVariable, err = f.Define(variables.Children[1]) + remainderVariable, err = f.NewVariable(variables.Children[1]) if err != nil { return err diff --git a/src/core/CompileDefinition.go b/src/core/CompileDefinition.go index 1ea9564..ddab01b 100644 --- a/src/core/CompileDefinition.go +++ b/src/core/CompileDefinition.go @@ -13,7 +13,7 @@ func (f *Function) CompileDefinition(node *ast.Define) error { right := node.Expression.Children[1] if left.IsLeaf() { - variable, err := f.Define(left) + variable, err := f.NewVariable(left) if err != nil { return err diff --git a/src/core/CompileFor.go b/src/core/CompileFor.go index 0d1530f..a820b9d 100644 --- a/src/core/CompileFor.go +++ b/src/core/CompileFor.go @@ -9,6 +9,7 @@ import ( "git.urbach.dev/cli/q/src/errors" "git.urbach.dev/cli/q/src/eval" "git.urbach.dev/cli/q/src/expression" + "git.urbach.dev/cli/q/src/scope" "git.urbach.dev/cli/q/src/token" "git.urbach.dev/cli/q/src/types" ) @@ -29,14 +30,15 @@ func (f *Function) CompileFor(loop *ast.For) error { to *expression.Expression ) + counter = f.NewRegister() + defer f.FreeRegister(counter) + switch loop.Head.Token.Kind { case token.Define: from = loop.Head.Children[1].Children[0] to = loop.Head.Children[1].Children[1] case token.Range: - counter = f.NewRegister() - defer f.FreeRegister(counter) from = loop.Head.Children[0] to = loop.Head.Children[1] @@ -60,20 +62,6 @@ func (f *Function) CompileFor(loop *ast.For) error { return errors.New(&errors.TypeMismatch{Encountered: value.Type().Name(), Expected: types.AnyInt.Name()}, f.File, to.Token.Position) } - scope := f.PushScope(loop.Body, f.File.Bytes) - scope.InLoop = true - - if loop.Head.Token.Kind == token.Define { - variable, err := f.Define(loop.Head.Children[0]) - - if err != nil { - return err - } - - counter = variable.Value.Register - f.AddVariable(variable) - } - switch value := value.(type) { case *eval.Number: f.AddLabel(label) @@ -95,6 +83,29 @@ func (f *Function) CompileFor(loop *ast.For) error { panic(fmt.Errorf("%s: not implemented: %v", f.UniqueName, value)) } + loopScope := f.PushScope(loop.Body, f.File.Bytes) + loopScope.InLoop = true + + if loop.Head.Token.Kind == token.Define { + leaf := loop.Head.Children[0] + name := leaf.Token.Text(f.File.Bytes) + variable := f.VariableByName(name) + + if variable != nil { + return errors.New(&errors.VariableAlreadyExists{Name: name}, f.File, leaf.Token.Position) + } + + variable = &scope.Variable{ + Name: name, + Value: eval.Register{ + Register: counter, + Alive: ast.Count(loop.Body, f.File.Bytes, token.Identifier, name), + }, + } + + f.AddVariable(variable) + } + f.Jump(asm.JGE, labelEnd) err = f.CompileAST(loop.Body) f.RegisterNumber(asm.ADD, counter, 1) diff --git a/src/core/MultiDefine.go b/src/core/MultiDefine.go index 6240c69..c1ed73b 100644 --- a/src/core/MultiDefine.go +++ b/src/core/MultiDefine.go @@ -15,7 +15,7 @@ func (f *Function) MultiDefine(left *expression.Expression, right *expression.Ex } return left.EachLeaf(func(leaf *expression.Expression) error { - variable, err := f.Define(leaf) + variable, err := f.NewVariable(leaf) if err != nil { return err diff --git a/src/core/Define.go b/src/core/NewVariable.go similarity index 85% rename from src/core/Define.go rename to src/core/NewVariable.go index c5c6cef..ecf405a 100644 --- a/src/core/Define.go +++ b/src/core/NewVariable.go @@ -8,8 +8,8 @@ import ( "git.urbach.dev/cli/q/src/token" ) -// Define defines a new variable. -func (f *Function) Define(leaf *expression.Expression) (*scope.Variable, error) { +// NewVariable creates a new variable. +func (f *Function) NewVariable(leaf *expression.Expression) (*scope.Variable, error) { name := leaf.Token.Text(f.File.Bytes) variable := f.VariableByName(name) diff --git a/tests/programs/factorial.q b/tests/programs/factorial.q new file mode 100644 index 0000000..3e52582 --- /dev/null +++ b/tests/programs/factorial.q @@ -0,0 +1,23 @@ +main() { + for i := 0..10 { + assert fac1(i) == fac2(i) + } +} + +fac1(n int) -> int { + x := 1 + + for i := 1..n+1 { + x = x * i + } + + return x +} + +fac2(n int) -> int { + if n <= 1 { + return 1 + } + + return n * fac2(n - 1) +} \ No newline at end of file diff --git a/tests/programs_test.go b/tests/programs_test.go index cfb6d2a..b4b18ed 100644 --- a/tests/programs_test.go +++ b/tests/programs_test.go @@ -62,6 +62,7 @@ var programs = []struct { {"loop-lifetime", 0}, {"loop-in-loop", 0}, {"for", 0}, + {"factorial", 0}, {"fibonacci", 0}, {"memory-free", 0}, {"out-of-memory", 0},