implement-range-loops #68
@ -109,3 +109,47 @@ testString (test,
|
|||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFor (test *testing.T) {
|
||||||
|
testString (test,
|
||||||
|
`
|
||||||
|
[f str:String] = for c:Byte in str { }
|
||||||
|
[g str:String] = for i:Index c:Byte in str { }
|
||||||
|
`)}
|
||||||
|
|
||||||
|
func TestForBreak (test *testing.T) {
|
||||||
|
testString (test,
|
||||||
|
`
|
||||||
|
[f str:String]:Byte = for c:Byte in str [break c]
|
||||||
|
`)}
|
||||||
|
|
||||||
|
func TestForBreakBadType (test *testing.T) {
|
||||||
|
testStringErr (test,
|
||||||
|
"expected Byte", 2, 56,
|
||||||
|
`
|
||||||
|
[f str:String]:Byte = for i:Index c:Byte in str [break i]
|
||||||
|
`)}
|
||||||
|
|
||||||
|
func TestForErrBadIndex (test *testing.T) {
|
||||||
|
testStringErr (test,
|
||||||
|
"expected Index", 2, 22,
|
||||||
|
`
|
||||||
|
[f str:String] = for i:Int c:Byte in str { }
|
||||||
|
`)}
|
||||||
|
|
||||||
|
func TestForErrBadOver (test *testing.T) {
|
||||||
|
testStringErr (test,
|
||||||
|
"cannot use U8 as Int", 2, 31,
|
||||||
|
`
|
||||||
|
[f str:String] = for c:Int in str { }
|
||||||
|
`)}
|
||||||
|
|
||||||
|
func TestForErrDeclarationScoped (test *testing.T) {
|
||||||
|
testStringErr (test,
|
||||||
|
"no variable named c", 4, 2,
|
||||||
|
`
|
||||||
|
[f str:String] = {
|
||||||
|
for c:Byte in str { }
|
||||||
|
c = 'a'
|
||||||
|
}
|
||||||
|
`)}
|
||||||
|
@ -48,6 +48,8 @@ func (this *Tree) analyzeExpression (
|
|||||||
return this.analyzeMatch(into, mode, expression)
|
return this.analyzeMatch(into, mode, expression)
|
||||||
case *entity.Loop:
|
case *entity.Loop:
|
||||||
return this.analyzeLoop(into, mode, expression)
|
return this.analyzeLoop(into, mode, expression)
|
||||||
|
case *entity.For:
|
||||||
|
return this.analyzeFor(into, mode, expression)
|
||||||
case *entity.Break:
|
case *entity.Break:
|
||||||
return this.analyzeBreak(into, mode, expression)
|
return this.analyzeBreak(into, mode, expression)
|
||||||
case *entity.Return:
|
case *entity.Return:
|
||||||
|
@ -808,6 +808,52 @@ func (this *Tree) analyzeLoop (
|
|||||||
return loop, nil
|
return loop, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *Tree) analyzeFor (
|
||||||
|
into entity.Type,
|
||||||
|
mode strictness,
|
||||||
|
loop *entity.For,
|
||||||
|
) (
|
||||||
|
entity.Expression,
|
||||||
|
error,
|
||||||
|
) {
|
||||||
|
loop.Ty = into
|
||||||
|
|
||||||
|
this.pushScope(loop)
|
||||||
|
defer this.popScope()
|
||||||
|
|
||||||
|
// index
|
||||||
|
if loop.Index != nil {
|
||||||
|
index, err := this.analyzeDeclaration (
|
||||||
|
builtinType("Index"),
|
||||||
|
strict, loop.Index)
|
||||||
|
if err != nil { return nil, err }
|
||||||
|
loop.Index = index.(*entity.Declaration)
|
||||||
|
}
|
||||||
|
|
||||||
|
// element
|
||||||
|
element, err := this.analyzeDeclaration(nil, strict, loop.Element)
|
||||||
|
if err != nil { return nil, err }
|
||||||
|
loop.Element = element.(*entity.Declaration)
|
||||||
|
|
||||||
|
// over
|
||||||
|
over, err := this.analyzeExpression (
|
||||||
|
&entity.TypeSlice {
|
||||||
|
Pos: loop.Position(),
|
||||||
|
Element: element.Type(),
|
||||||
|
}, weak, loop.Over)
|
||||||
|
if err != nil { return nil, err }
|
||||||
|
loop.Over = over
|
||||||
|
|
||||||
|
this.pushLoop(loop)
|
||||||
|
defer this.popLoop()
|
||||||
|
|
||||||
|
body, err := this.analyzeExpression(nil, strict, loop.Body)
|
||||||
|
if err != nil { return nil, err }
|
||||||
|
loop.Body = body
|
||||||
|
|
||||||
|
return loop, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (this *Tree) analyzeBreak (
|
func (this *Tree) analyzeBreak (
|
||||||
into entity.Type,
|
into entity.Type,
|
||||||
mode strictness,
|
mode strictness,
|
||||||
|
@ -16,7 +16,7 @@ func (this *scopeContextManager) popScopeContext () {
|
|||||||
*this = (*this)[:len(*this) - 1]
|
*this = (*this)[:len(*this) - 1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *scopeContextManager) pushLoop (loop *entity.Loop) {
|
func (this *scopeContextManager) pushLoop (loop entity.Breakable) {
|
||||||
this.assertPopulated()
|
this.assertPopulated()
|
||||||
(*this)[len(*this) - 1].pushLoop(loop)
|
(*this)[len(*this) - 1].pushLoop(loop)
|
||||||
}
|
}
|
||||||
@ -26,7 +26,7 @@ func (this *scopeContextManager) popLoop () {
|
|||||||
(*this)[len(*this) - 1].popLoop()
|
(*this)[len(*this) - 1].popLoop()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *scopeContextManager) topLoop () (*entity.Loop, bool) {
|
func (this *scopeContextManager) topLoop () (entity.Breakable, bool) {
|
||||||
this.assertPopulated()
|
this.assertPopulated()
|
||||||
return (*this)[len(*this) - 1].topLoop()
|
return (*this)[len(*this) - 1].topLoop()
|
||||||
}
|
}
|
||||||
@ -71,11 +71,11 @@ func (this *scopeContextManager) assertPopulated () {
|
|||||||
// entities.
|
// entities.
|
||||||
type scopeContext struct {
|
type scopeContext struct {
|
||||||
scopes []entity.Scoped
|
scopes []entity.Scoped
|
||||||
loops []*entity.Loop
|
loops []entity.Breakable
|
||||||
declaration entity.TopLevel
|
declaration entity.TopLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *scopeContext) pushLoop (loop *entity.Loop) {
|
func (this *scopeContext) pushLoop (loop entity.Breakable) {
|
||||||
this.loops = append(this.loops, loop)
|
this.loops = append(this.loops, loop)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ func (this *scopeContext) popLoop () {
|
|||||||
this.loops = this.loops[:len(this.loops) - 1]
|
this.loops = this.loops[:len(this.loops) - 1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *scopeContext) topLoop () (*entity.Loop, bool) {
|
func (this *scopeContext) topLoop () (entity.Breakable, bool) {
|
||||||
if len(this.loops) < 1 { return nil, false }
|
if len(this.loops) < 1 { return nil, false }
|
||||||
return this.loops[len(this.loops) - 1], true
|
return this.loops[len(this.loops) - 1], true
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package compiler
|
package compiler
|
||||||
|
|
||||||
|
import "time"
|
||||||
import "io/fs"
|
import "io/fs"
|
||||||
import "embed"
|
import "embed"
|
||||||
import "strings"
|
import "strings"
|
||||||
import "testing"
|
import "testing"
|
||||||
|
import "context"
|
||||||
import "os/exec"
|
import "os/exec"
|
||||||
import "path/filepath"
|
import "path/filepath"
|
||||||
import "git.tebibyte.media/fspl/fspl/entity"
|
import "git.tebibyte.media/fspl/fspl/entity"
|
||||||
@ -73,9 +75,9 @@ func testUnit (
|
|||||||
// instantiate and configure the compiler
|
// instantiate and configure the compiler
|
||||||
compOutputBuilder := new(strings.Builder)
|
compOutputBuilder := new(strings.Builder)
|
||||||
comp := defaultCompiler()
|
comp := defaultCompiler()
|
||||||
defer func () {
|
outputCompilerLog := func () {
|
||||||
test.Log("COMPILER LOG (main unit):\n" + compOutputBuilder.String())
|
test.Log("COMPILER LOG (main unit):\n" + compOutputBuilder.String())
|
||||||
} ()
|
}
|
||||||
comp.Writer = compOutputBuilder
|
comp.Writer = compOutputBuilder
|
||||||
comp.Output = filepath.Join(temp, "output.o")
|
comp.Output = filepath.Join(temp, "output.o")
|
||||||
|
|
||||||
@ -83,8 +85,10 @@ func testUnit (
|
|||||||
// compile to object file
|
// compile to object file
|
||||||
err := comp.CompileUnit(address)
|
err := comp.CompileUnit(address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
outputCompilerLog()
|
||||||
test.Fatal("compiler returned error:", errors.Format(err))
|
test.Fatal("compiler returned error:", errors.Format(err))
|
||||||
}
|
}
|
||||||
|
outputCompilerLog()
|
||||||
|
|
||||||
// link the object file into an executable
|
// link the object file into an executable
|
||||||
executablePath := filepath.Join(temp, "output")
|
executablePath := filepath.Join(temp, "output")
|
||||||
@ -101,14 +105,21 @@ func testUnit (
|
|||||||
test.Fatal("error linking executable:", err)
|
test.Fatal("error linking executable:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// run the executable file and check its output
|
// run the executable file (with timeout) and check its output
|
||||||
executableCommand := exec.Command(executablePath, args...)
|
timeoutDuration := 10 * time.Millisecond
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), timeoutDuration)
|
||||||
|
defer cancel()
|
||||||
|
executableCommand := exec.CommandContext(ctx, executablePath, args...)
|
||||||
stdoutBuilder := new(strings.Builder)
|
stdoutBuilder := new(strings.Builder)
|
||||||
executableCommand.Stdin = strings.NewReader(stdin)
|
executableCommand.Stdin = strings.NewReader(stdin)
|
||||||
executableCommand.Stdout = stdoutBuilder
|
executableCommand.Stdout = stdoutBuilder
|
||||||
test.Log("running executable command: ", executableCommand)
|
test.Log("running executable command: ", executableCommand)
|
||||||
err = executableCommand.Run()
|
err = executableCommand.Run()
|
||||||
|
|
||||||
|
if ctx.Err() == context.DeadlineExceeded {
|
||||||
|
test.Errorf("timeout of %v exceeded!", timeoutDuration)
|
||||||
|
}
|
||||||
|
|
||||||
// check error, compare exit code
|
// check error, compare exit code
|
||||||
if exitErr, ok := err.(*exec.ExitError); ok {
|
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||||
code := exitErr.ExitCode()
|
code := exitErr.ExitCode()
|
||||||
|
@ -91,3 +91,54 @@ testUnit (test,
|
|||||||
"", "false\n",
|
"", "false\n",
|
||||||
0,
|
0,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
func TestLoopBreakExitCode (test *testing.T) {
|
||||||
|
testUnit (test,
|
||||||
|
"/test-data/data/loop-break-exit-code", nil,
|
||||||
|
"", "",
|
||||||
|
5,
|
||||||
|
)}
|
||||||
|
|
||||||
|
func TestLoopBreakBranchExitCode (test *testing.T) {
|
||||||
|
testUnit (test,
|
||||||
|
"/test-data/data/loop-break-branch-exit-code", nil,
|
||||||
|
"", "",
|
||||||
|
2,
|
||||||
|
)}
|
||||||
|
|
||||||
|
func TestForSimple (test *testing.T) {
|
||||||
|
testUnit (test,
|
||||||
|
"/test-data/data/for-simple", nil,
|
||||||
|
"", "",
|
||||||
|
0,
|
||||||
|
)}
|
||||||
|
|
||||||
|
func TestForStringArray (test *testing.T) {
|
||||||
|
dependencies := []string {
|
||||||
|
compileDependency(test, "io"),
|
||||||
|
}
|
||||||
|
testUnit (test,
|
||||||
|
"/test-data/data/for-string-array", dependencies,
|
||||||
|
"", "a\nb\nc\na\nb\nc\n",
|
||||||
|
0,
|
||||||
|
)}
|
||||||
|
|
||||||
|
func TestForStringArrayOnce (test *testing.T) {
|
||||||
|
dependencies := []string {
|
||||||
|
compileDependency(test, "io"),
|
||||||
|
}
|
||||||
|
testUnit (test,
|
||||||
|
"/test-data/data/for-string-array-once", dependencies,
|
||||||
|
"", "abc\n",
|
||||||
|
0,
|
||||||
|
)}
|
||||||
|
|
||||||
|
func TestForBreakBranch (test *testing.T) {
|
||||||
|
dependencies := []string {
|
||||||
|
compileDependency(test, "io"),
|
||||||
|
}
|
||||||
|
testUnit (test,
|
||||||
|
"/test-data/data/for-break-branch", dependencies,
|
||||||
|
"", "iter\niter\n",
|
||||||
|
0,
|
||||||
|
)}
|
||||||
|
2
compiler/test-data/data/for-break-branch/fspl.mod
Normal file
2
compiler/test-data/data/for-break-branch/fspl.mod
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
'61b23750-b3c5-4cbd-85f7-71322a23f980'
|
||||||
|
+ 'io'
|
8
compiler/test-data/data/for-break-branch/main.fspl
Normal file
8
compiler/test-data/data/for-break-branch/main.fspl
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[main]:I32 'main' = {
|
||||||
|
arr:3:Int = (* 5 6 7)
|
||||||
|
for i:Index e:Int in arr {
|
||||||
|
if [>= i 2] then [break]
|
||||||
|
io::[println 'iter']
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
2
compiler/test-data/data/for-simple/fspl.mod
Normal file
2
compiler/test-data/data/for-simple/fspl.mod
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
'11371094-76a7-424f-b9ae-14636962073b'
|
||||||
|
+ 'io'
|
5
compiler/test-data/data/for-simple/main.fspl
Normal file
5
compiler/test-data/data/for-simple/main.fspl
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[main]:I32 'main' = {
|
||||||
|
str:String = 'a'
|
||||||
|
for c:Byte in str { }
|
||||||
|
0
|
||||||
|
}
|
3
compiler/test-data/data/for-string-array-once/fspl.mod
Normal file
3
compiler/test-data/data/for-string-array-once/fspl.mod
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
'c9024148-f4ec-4d69-84c1-c838f9c68073'
|
||||||
|
+ 'io'
|
||||||
|
+ 'cstdio'
|
5
compiler/test-data/data/for-string-array-once/main.fspl
Normal file
5
compiler/test-data/data/for-string-array-once/main.fspl
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[main]:I32 'main' = {
|
||||||
|
arr:5:String = (* 'abc' 'def' 'ghi')
|
||||||
|
for e:String in arr { io::[println e] [break] }
|
||||||
|
0
|
||||||
|
}
|
2
compiler/test-data/data/for-string-array/fspl.mod
Normal file
2
compiler/test-data/data/for-string-array/fspl.mod
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
'cfb45339-4c4d-4519-9578-3abf0c698867'
|
||||||
|
+ 'io'
|
10
compiler/test-data/data/for-string-array/main.fspl
Normal file
10
compiler/test-data/data/for-string-array/main.fspl
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[main]:I32 'main' = {
|
||||||
|
; array
|
||||||
|
arr:3:String = (* 'a' 'b' 'c')
|
||||||
|
for e:String in arr io::[println e]
|
||||||
|
|
||||||
|
; slice
|
||||||
|
slice:*:String = arr
|
||||||
|
for e:String in slice io::[println e]
|
||||||
|
0
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
'e8962449-6214-4622-afcf-f6b58046fab2'
|
@ -0,0 +1,10 @@
|
|||||||
|
[main]:I32 'main' = {
|
||||||
|
y:I32 = 6
|
||||||
|
loop {
|
||||||
|
if [< y 3]
|
||||||
|
then [break y]
|
||||||
|
else {
|
||||||
|
y = [-- y]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
compiler/test-data/data/loop-break-exit-code/fspl.mod
Normal file
1
compiler/test-data/data/loop-break-exit-code/fspl.mod
Normal file
@ -0,0 +1 @@
|
|||||||
|
'779e632d-88a6-46c4-bfa2-7db4fc919f07'
|
3
compiler/test-data/data/loop-break-exit-code/main.fspl
Normal file
3
compiler/test-data/data/loop-break-exit-code/main.fspl
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[main]:I32 'main' = loop {
|
||||||
|
[break 5]
|
||||||
|
}
|
@ -276,6 +276,12 @@ statement. The result of the loop may be assigned to any type that satisfies the
|
|||||||
assignment rules of all of its break statements. Loops may be nested, and break
|
assignment rules of all of its break statements. Loops may be nested, and break
|
||||||
statements only apply to the closest containing loop. The value of the loop's
|
statements only apply to the closest containing loop. The value of the loop's
|
||||||
expression is never used.
|
expression is never used.
|
||||||
|
### For
|
||||||
|
For is a special kind of loop that evaluates an expression for each element of
|
||||||
|
an array or slice. It accepts an index declaration and an element declaration,
|
||||||
|
which are scoped to the loop's body and are set to the index of the current
|
||||||
|
element and the element itself respectively at the beginning of each iteration.
|
||||||
|
The assignment rules of a for statement are identical to that of a normal loop.
|
||||||
### Break
|
### Break
|
||||||
Break allows breaking out of loops. It may be assigned to anything, but the
|
Break allows breaking out of loops. It may be assigned to anything, but the
|
||||||
assignment will have no effect as execution of the code after and surrounding it
|
assignment will have no effect as execution of the code after and surrounding it
|
||||||
@ -362,6 +368,7 @@ this without hand-writing a parser.
|
|||||||
["else" <expression>]
|
["else" <expression>]
|
||||||
<match> -> "match" <expression> <case>*
|
<match> -> "match" <expression> <case>*
|
||||||
<loop> -> "loop" <expression>
|
<loop> -> "loop" <expression>
|
||||||
|
<for> -> "for" <expression> [<expression>] in <expression> <expression>
|
||||||
<break> -> "[" "break" [<expression>] "]"
|
<break> -> "[" "break" [<expression>] "]"
|
||||||
<return> -> "[" "return" [<expression>] "]"
|
<return> -> "[" "return" [<expression>] "]"
|
||||||
<assignment> -> <expression> "=" <expression>
|
<assignment> -> <expression> "=" <expression>
|
||||||
|
@ -439,7 +439,14 @@ func (this *Match) String () string {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Breakable is any expression that can be halted using a break.
|
||||||
|
type Breakable interface {
|
||||||
|
Expression
|
||||||
|
breakable()
|
||||||
|
}
|
||||||
|
|
||||||
var _ Expression = &Loop { }
|
var _ Expression = &Loop { }
|
||||||
|
var _ Breakable = &Loop { }
|
||||||
// Loop is a control flow expression that repeats an expression until a break
|
// Loop is a control flow expression that repeats an expression until a break
|
||||||
// statement is called from within it. The break statement must be given a value
|
// statement is called from within it. The break statement must be given a value
|
||||||
// if the value of the loop is used. Otherwise, it need not even have a break
|
// if the value of the loop is used. Otherwise, it need not even have a break
|
||||||
@ -456,6 +463,7 @@ type Loop struct {
|
|||||||
Ty Type
|
Ty Type
|
||||||
}
|
}
|
||||||
func (*Loop) expression(){}
|
func (*Loop) expression(){}
|
||||||
|
func (*Loop) breakable(){}
|
||||||
func (this *Loop) Position () errors.Position { return this.Pos }
|
func (this *Loop) Position () errors.Position { return this.Pos }
|
||||||
func (this *Loop) Type () Type { return this.Ty }
|
func (this *Loop) Type () Type { return this.Ty }
|
||||||
func (this *Loop) HasExplicitType () bool {
|
func (this *Loop) HasExplicitType () bool {
|
||||||
@ -467,6 +475,44 @@ func (this *Loop) String () string {
|
|||||||
return fmt.Sprint("loop ", this.Body)
|
return fmt.Sprint("loop ", this.Body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ Expression = &For { }
|
||||||
|
var _ Breakable = &For { }
|
||||||
|
// For is a special kind of loop that evaluates an expression for each element
|
||||||
|
// of an array or slice. It accepts an index declaration and an element
|
||||||
|
// declaration, which are scoped to the loop's body and are set to the index of
|
||||||
|
// the current element and the element itself respectively at the beginning of
|
||||||
|
// each iteration. The assignment rules of a for statement are identical to that
|
||||||
|
// of a normal loop.
|
||||||
|
type For struct {
|
||||||
|
// Syntax
|
||||||
|
Pos errors.Position
|
||||||
|
Index *Declaration
|
||||||
|
Element *Declaration
|
||||||
|
Over Expression
|
||||||
|
Body Expression
|
||||||
|
|
||||||
|
// Semantics
|
||||||
|
Ty Type
|
||||||
|
Scope
|
||||||
|
}
|
||||||
|
func (*For) expression(){}
|
||||||
|
func (*For) breakable(){}
|
||||||
|
func (this *For) Position () errors.Position { return this.Pos }
|
||||||
|
func (this *For) Type () Type { return this.Ty }
|
||||||
|
func (this *For) HasExplicitType () bool {
|
||||||
|
// this is as accurate as possible without doing a full analysis of the
|
||||||
|
// loop
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func (this *For) String () string {
|
||||||
|
out := "for"
|
||||||
|
if this.Index != nil {
|
||||||
|
out += " " + this.Index.String()
|
||||||
|
}
|
||||||
|
out += fmt.Sprintf(" %v in %v %v", this.Element, this.Over, this.Body)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
var _ Expression = &Break { }
|
var _ Expression = &Break { }
|
||||||
// Break allows breaking out of loops. It has no value and may not be assigned
|
// Break allows breaking out of loops. It has no value and may not be assigned
|
||||||
// to anything. It is never a valid location expression.
|
// to anything. It is never a valid location expression.
|
||||||
@ -476,7 +522,7 @@ type Break struct {
|
|||||||
Value Expression
|
Value Expression
|
||||||
|
|
||||||
// Semantics
|
// Semantics
|
||||||
Loop *Loop
|
Loop Breakable
|
||||||
}
|
}
|
||||||
func (*Break) expression(){}
|
func (*Break) expression(){}
|
||||||
func (this *Break) Position () errors.Position { return this.Pos }
|
func (this *Break) Position () errors.Position { return this.Pos }
|
||||||
|
@ -165,7 +165,12 @@ func (this *generator) generateAssignmentToDestination ( // TODO: add -Val suffi
|
|||||||
source, err := this.generateExpressionVal(source)
|
source, err := this.generateExpressionVal(source)
|
||||||
if err != nil { return nil, err }
|
if err != nil { return nil, err }
|
||||||
this.blockManager.NewStore(source, destDataFieldLoc)
|
this.blockManager.NewStore(source, destDataFieldLoc)
|
||||||
|
|
||||||
|
if destinationSpecified {
|
||||||
|
return nil, nil
|
||||||
|
} else {
|
||||||
return this.blockManager.NewLoad(irDestType, irDestLoc), nil
|
return this.blockManager.NewLoad(irDestType, irDestLoc), nil
|
||||||
|
}
|
||||||
|
|
||||||
// conversion from any type to pointer
|
// conversion from any type to pointer
|
||||||
case *entity.TypePointer:
|
case *entity.TypePointer:
|
||||||
@ -192,6 +197,8 @@ func (this *generator) generateAssignmentToDestination ( // TODO: add -Val suffi
|
|||||||
switch sourceTypeBase := analyzer.ReduceToBase(source.Type()).(type) {
|
switch sourceTypeBase := analyzer.ReduceToBase(source.Type()).(type) {
|
||||||
// conversion from array to slice
|
// conversion from array to slice
|
||||||
case *entity.TypeArray:
|
case *entity.TypeArray:
|
||||||
|
irIndexType, err := this.generateTypeIndex()
|
||||||
|
if err != nil { return nil, err }
|
||||||
array, err := this.generateExpressionLoc(source)
|
array, err := this.generateExpressionLoc(source)
|
||||||
if err != nil { return nil, err }
|
if err != nil { return nil, err }
|
||||||
irDestType, err := this.generateType(destType)
|
irDestType, err := this.generateType(destType)
|
||||||
@ -204,10 +211,12 @@ func (this *generator) generateAssignmentToDestination ( // TODO: add -Val suffi
|
|||||||
destLengthField := this.getSliceLengthFieldLoc(irDestLoc, irDestType)
|
destLengthField := this.getSliceLengthFieldLoc(irDestLoc, irDestType)
|
||||||
this.blockManager.NewStore(array, destDataField)
|
this.blockManager.NewStore(array, destDataField)
|
||||||
this.blockManager.NewStore (
|
this.blockManager.NewStore (
|
||||||
llvm.NewConstInt(llvm.I32, int64(sourceTypeBase.Length)),
|
llvm.NewConstInt(irIndexType, int64(sourceTypeBase.Length)),
|
||||||
destLengthField)
|
destLengthField)
|
||||||
|
|
||||||
if !destinationSpecified {
|
if destinationSpecified {
|
||||||
|
return nil, nil
|
||||||
|
} else {
|
||||||
return this.blockManager.NewLoad(irDestType, irDestLoc), nil
|
return this.blockManager.NewLoad(irDestType, irDestLoc), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,40 @@ import "git.tebibyte.media/fspl/fspl/llvm"
|
|||||||
import "git.tebibyte.media/fspl/fspl/entity"
|
import "git.tebibyte.media/fspl/fspl/entity"
|
||||||
|
|
||||||
type loopEntry struct {
|
type loopEntry struct {
|
||||||
value llvm.Value
|
breakBlocks []*llvm.Incoming // incoming value/block pairs of breaks
|
||||||
stub *llvm.Block
|
mode resultMode // result mode for break statements
|
||||||
mode resultMode
|
loc bool // for when mode is resultModeAny
|
||||||
loc bool
|
}
|
||||||
|
|
||||||
|
// tieUp branches all breakBlocks to exit.
|
||||||
|
func (this *loopEntry) tieUp (exit *llvm.Block) {
|
||||||
|
for _, incoming := range this.breakBlocks {
|
||||||
|
predecessor := incoming.Predecessor.(*llvm.Block)
|
||||||
|
predecessor.SetTerminator(&llvm.TerminatorBr {
|
||||||
|
Target: exit,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tieUpPhi branches all breakBlocks to exit, creates a new phi in exit, and
|
||||||
|
// returns the phi.
|
||||||
|
func (this *loopEntry) tieUpPhi (ty llvm.Type, exit *llvm.Block, extraIncomings... *llvm.Incoming) llvm.Value {
|
||||||
|
for _, incoming := range this.breakBlocks {
|
||||||
|
predecessor := incoming.Predecessor.(*llvm.Block)
|
||||||
|
predecessor.SetTerminator(&llvm.TerminatorBr {
|
||||||
|
Target: exit,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
totalIncomings := make([]*llvm.Incoming, len(this.breakBlocks))
|
||||||
|
copy(totalIncomings, this.breakBlocks)
|
||||||
|
totalIncomings = append(totalIncomings, extraIncomings...)
|
||||||
|
|
||||||
|
if len(totalIncomings) == 0 {
|
||||||
|
return llvm.NewConstZeroInitializer(ty)
|
||||||
|
} else {
|
||||||
|
return exit.NewPhi(totalIncomings...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type blockManager struct {
|
type blockManager struct {
|
||||||
|
@ -76,7 +76,7 @@ testString (test,
|
|||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoop (test *testing.T) {
|
func TestLoopSimple (test *testing.T) {
|
||||||
testString (test,
|
testString (test,
|
||||||
`define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
`define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||||
0:
|
0:
|
||||||
@ -105,7 +105,8 @@ testString (test,
|
|||||||
2:
|
2:
|
||||||
br label %3
|
br label %3
|
||||||
3:
|
3:
|
||||||
store i64 5, ptr %1
|
%4 = phi i64 [ 5, %2 ]
|
||||||
|
store i64 %4, ptr %1
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
@ -128,19 +129,20 @@ testString (test,
|
|||||||
2:
|
2:
|
||||||
%3 = load i64, ptr %1
|
%3 = load i64, ptr %1
|
||||||
%4 = icmp slt i64 %3, 3
|
%4 = icmp slt i64 %3, 3
|
||||||
br i1 %4, label %5, label %8
|
br i1 %4, label %7, label %10
|
||||||
5:
|
5:
|
||||||
%6 = load i64, ptr %1
|
%6 = phi i64 [ %8, %7 ]
|
||||||
br label %11
|
|
||||||
7:
|
|
||||||
br label %2
|
|
||||||
8:
|
|
||||||
%9 = load i64, ptr %1
|
|
||||||
%10 = sub i64 %9, 1
|
|
||||||
store i64 %10, ptr %1
|
|
||||||
br label %7
|
|
||||||
11:
|
|
||||||
ret i64 %6
|
ret i64 %6
|
||||||
|
7:
|
||||||
|
%8 = load i64, ptr %1
|
||||||
|
br label %5
|
||||||
|
9:
|
||||||
|
br label %2
|
||||||
|
10:
|
||||||
|
%11 = load i64, ptr %1
|
||||||
|
%12 = sub i64 %11, 1
|
||||||
|
store i64 %12, ptr %1
|
||||||
|
br label %9
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
`
|
`
|
||||||
@ -375,18 +377,19 @@ testString (test,
|
|||||||
3:
|
3:
|
||||||
%4 = load i64, ptr %1
|
%4 = load i64, ptr %1
|
||||||
%5 = icmp eq i64 %4, 5
|
%5 = icmp eq i64 %4, 5
|
||||||
br i1 %5, label %6, label %9
|
br i1 %5, label %8, label %11
|
||||||
6:
|
6:
|
||||||
br label %11
|
%7 = phi i64 [ 0, %8 ]
|
||||||
7:
|
ret i64 %7
|
||||||
%8 = phi i64 [ %10, %9 ]
|
8:
|
||||||
store i64 %8, ptr %2
|
br label %6
|
||||||
br label %3
|
|
||||||
9:
|
9:
|
||||||
%10 = load i64, ptr %1
|
%10 = phi i64 [ %12, %11 ]
|
||||||
br label %7
|
store i64 %10, ptr %2
|
||||||
|
br label %3
|
||||||
11:
|
11:
|
||||||
ret i64 0
|
%12 = load i64, ptr %1
|
||||||
|
br label %9
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
`
|
`
|
||||||
@ -397,3 +400,114 @@ testString (test,
|
|||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFor (test *testing.T) {
|
||||||
|
testString (test,
|
||||||
|
`%"AAAAAAAAAAAAAAAAAAAAAA==::Index" = type i64
|
||||||
|
%"AAAAAAAAAAAAAAAAAAAAAA==::String" = type { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }
|
||||||
|
%"AAAAAAAAAAAAAAAAAAAAAA==::Byte" = type i8
|
||||||
|
define void @"0zNZN147MN2wzMAQ6NS2dQ==::f"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %str) {
|
||||||
|
0:
|
||||||
|
%1 = alloca %"AAAAAAAAAAAAAAAAAAAAAA==::String"
|
||||||
|
store %"AAAAAAAAAAAAAAAAAAAAAA==::String" %str, ptr %1
|
||||||
|
%2 = alloca %"AAAAAAAAAAAAAAAAAAAAAA==::Index"
|
||||||
|
%3 = alloca %"AAAAAAAAAAAAAAAAAAAAAA==::Byte"
|
||||||
|
br label %4
|
||||||
|
4:
|
||||||
|
%5 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %1, i32 0, i32 1
|
||||||
|
%6 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Index", ptr %5
|
||||||
|
%7 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Index", ptr %2
|
||||||
|
%8 = icmp ult %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %7, %6
|
||||||
|
br i1 %8, label %9, label %18
|
||||||
|
9:
|
||||||
|
%10 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %1, i32 0, i32 0
|
||||||
|
%11 = load ptr, ptr %10
|
||||||
|
%12 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Index", ptr %2
|
||||||
|
%13 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::Byte", ptr %11, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %12
|
||||||
|
%14 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Byte", ptr %13
|
||||||
|
store %"AAAAAAAAAAAAAAAAAAAAAA==::Byte" %14, ptr %3
|
||||||
|
%15 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Byte", ptr %3
|
||||||
|
call void @"0zNZN147MN2wzMAQ6NS2dQ==::print"(%"AAAAAAAAAAAAAAAAAAAAAA==::Byte" %15)
|
||||||
|
%16 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Index", ptr %2
|
||||||
|
%17 = add %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %16, 1
|
||||||
|
store %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %17, ptr %2
|
||||||
|
br label %4
|
||||||
|
18:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
declare void @"0zNZN147MN2wzMAQ6NS2dQ==::print"(%"AAAAAAAAAAAAAAAAAAAAAA==::Byte" %x)
|
||||||
|
define void @"0zNZN147MN2wzMAQ6NS2dQ==::g"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %str) {
|
||||||
|
0:
|
||||||
|
%1 = alloca %"AAAAAAAAAAAAAAAAAAAAAA==::String"
|
||||||
|
store %"AAAAAAAAAAAAAAAAAAAAAA==::String" %str, ptr %1
|
||||||
|
%2 = alloca %"AAAAAAAAAAAAAAAAAAAAAA==::Index"
|
||||||
|
store %"AAAAAAAAAAAAAAAAAAAAAA==::Index" zeroinitializer, ptr %2
|
||||||
|
%3 = alloca %"AAAAAAAAAAAAAAAAAAAAAA==::Byte"
|
||||||
|
br label %4
|
||||||
|
4:
|
||||||
|
%5 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %1, i32 0, i32 1
|
||||||
|
%6 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Index", ptr %5
|
||||||
|
%7 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Index", ptr %2
|
||||||
|
%8 = icmp ult %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %7, %6
|
||||||
|
br i1 %8, label %9, label %18
|
||||||
|
9:
|
||||||
|
%10 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %1, i32 0, i32 0
|
||||||
|
%11 = load ptr, ptr %10
|
||||||
|
%12 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Index", ptr %2
|
||||||
|
%13 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::Byte", ptr %11, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %12
|
||||||
|
%14 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Byte", ptr %13
|
||||||
|
store %"AAAAAAAAAAAAAAAAAAAAAA==::Byte" %14, ptr %3
|
||||||
|
%15 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Byte", ptr %3
|
||||||
|
call void @"0zNZN147MN2wzMAQ6NS2dQ==::print"(%"AAAAAAAAAAAAAAAAAAAAAA==::Byte" %15)
|
||||||
|
%16 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Index", ptr %2
|
||||||
|
%17 = add %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %16, 1
|
||||||
|
store %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %17, ptr %2
|
||||||
|
br label %4
|
||||||
|
18:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
[print x:Byte]
|
||||||
|
[f str:String] = for c:Byte in str { [print c] }
|
||||||
|
[g str:String] = for i:Index c:Byte in str [print c]
|
||||||
|
`)}
|
||||||
|
|
||||||
|
func TestForBreak (test *testing.T) {
|
||||||
|
testString (test,
|
||||||
|
`%"AAAAAAAAAAAAAAAAAAAAAA==::Byte" = type i8
|
||||||
|
%"AAAAAAAAAAAAAAAAAAAAAA==::Index" = type i64
|
||||||
|
%"AAAAAAAAAAAAAAAAAAAAAA==::String" = type { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }
|
||||||
|
define %"AAAAAAAAAAAAAAAAAAAAAA==::Byte" @"0zNZN147MN2wzMAQ6NS2dQ==::f"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %str) {
|
||||||
|
0:
|
||||||
|
%1 = alloca %"AAAAAAAAAAAAAAAAAAAAAA==::String"
|
||||||
|
store %"AAAAAAAAAAAAAAAAAAAAAA==::String" %str, ptr %1
|
||||||
|
%2 = alloca %"AAAAAAAAAAAAAAAAAAAAAA==::Index"
|
||||||
|
%3 = alloca %"AAAAAAAAAAAAAAAAAAAAAA==::Byte"
|
||||||
|
br label %4
|
||||||
|
4:
|
||||||
|
%5 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %1, i32 0, i32 1
|
||||||
|
%6 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Index", ptr %5
|
||||||
|
%7 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Index", ptr %2
|
||||||
|
%8 = icmp ult %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %7, %6
|
||||||
|
br i1 %8, label %9, label %18
|
||||||
|
9:
|
||||||
|
%10 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %1, i32 0, i32 0
|
||||||
|
%11 = load ptr, ptr %10
|
||||||
|
%12 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Index", ptr %2
|
||||||
|
%13 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::Byte", ptr %11, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %12
|
||||||
|
%14 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Byte", ptr %13
|
||||||
|
store %"AAAAAAAAAAAAAAAAAAAAAA==::Byte" %14, ptr %3
|
||||||
|
%15 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Byte", ptr %3
|
||||||
|
%16 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Index", ptr %2
|
||||||
|
%17 = add %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %16, 1
|
||||||
|
store %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %17, ptr %2
|
||||||
|
br label %18
|
||||||
|
18:
|
||||||
|
%19 = phi %"AAAAAAAAAAAAAAAAAAAAAA==::Byte" [ %15, %9 ], [ zeroinitializer, %4 ]
|
||||||
|
ret %"AAAAAAAAAAAAAAAAAAAAAA==::Byte" %19
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
[f str:String]:Byte = for c:Byte in str [break c]
|
||||||
|
`)}
|
||||||
|
@ -97,6 +97,8 @@ func (this *generator) generateExpressionAny (expression entity.Expression) (reg
|
|||||||
return this.generateMatch(expression, resultModeAny)
|
return this.generateMatch(expression, resultModeAny)
|
||||||
case *entity.Loop:
|
case *entity.Loop:
|
||||||
return this.generateLoop(expression, resultModeAny)
|
return this.generateLoop(expression, resultModeAny)
|
||||||
|
case *entity.For:
|
||||||
|
return this.generateFor(expression, resultModeAny)
|
||||||
|
|
||||||
// we get nothing from these
|
// we get nothing from these
|
||||||
case *entity.Assignment:
|
case *entity.Assignment:
|
||||||
@ -161,6 +163,9 @@ func (this *generator) generateExpressionVal (expression entity.Expression) (llv
|
|||||||
case *entity.Loop:
|
case *entity.Loop:
|
||||||
val, _, err := this.generateLoop(expression, resultModeVal)
|
val, _, err := this.generateLoop(expression, resultModeVal)
|
||||||
return val, err
|
return val, err
|
||||||
|
case *entity.For:
|
||||||
|
val, _, err := this.generateFor(expression, resultModeVal)
|
||||||
|
return val, err
|
||||||
case *entity.LiteralInt:
|
case *entity.LiteralInt:
|
||||||
return this.generateLiteralInt(expression)
|
return this.generateLiteralInt(expression)
|
||||||
case *entity.LiteralFloat:
|
case *entity.LiteralFloat:
|
||||||
@ -233,6 +238,9 @@ func (this *generator) generateExpressionLoc (expression entity.Expression) (llv
|
|||||||
case *entity.Loop:
|
case *entity.Loop:
|
||||||
loc, _, err := this.generateLoop(expression, resultModeLoc)
|
loc, _, err := this.generateLoop(expression, resultModeLoc)
|
||||||
return loc, err
|
return loc, err
|
||||||
|
case *entity.For:
|
||||||
|
loc, _, err := this.generateFor(expression, resultModeLoc)
|
||||||
|
return loc, err
|
||||||
case *entity.LiteralArray:
|
case *entity.LiteralArray:
|
||||||
return this.generateLiteralArrayLoc(expression, nil)
|
return this.generateLiteralArrayLoc(expression, nil)
|
||||||
case *entity.LiteralString:
|
case *entity.LiteralString:
|
||||||
|
@ -3,7 +3,7 @@ package generator
|
|||||||
// import "fmt"
|
// import "fmt"
|
||||||
import "git.tebibyte.media/fspl/fspl/llvm"
|
import "git.tebibyte.media/fspl/fspl/llvm"
|
||||||
import "git.tebibyte.media/fspl/fspl/entity"
|
import "git.tebibyte.media/fspl/fspl/entity"
|
||||||
// import "git.tebibyte.media/fspl/fspl/analyzer"
|
import "git.tebibyte.media/fspl/fspl/analyzer"
|
||||||
|
|
||||||
func (this *generator) generateBlock (block *entity.Block, mode resultMode) (llvm.Value, bool, error) {
|
func (this *generator) generateBlock (block *entity.Block, mode resultMode) (llvm.Value, bool, error) {
|
||||||
if len(block.Steps) == 0 { return nil, false, nil }
|
if len(block.Steps) == 0 { return nil, false, nil }
|
||||||
@ -19,14 +19,28 @@ func (this *generator) generateBlock (block *entity.Block, mode resultMode) (llv
|
|||||||
|
|
||||||
func (this *generator) generateBreak (brk *entity.Break) (llvm.Value, error) {
|
func (this *generator) generateBreak (brk *entity.Break) (llvm.Value, error) {
|
||||||
loopEntry := this.blockManager.topLoop()
|
loopEntry := this.blockManager.topLoop()
|
||||||
|
var irValue llvm.Value
|
||||||
if brk.Value != nil {
|
if brk.Value != nil {
|
||||||
value, loc, err := this.generateExpression(brk.Value, loopEntry.mode)
|
var err error
|
||||||
|
var loc bool
|
||||||
|
|
||||||
|
thisMode := loopEntry.mode
|
||||||
|
if thisMode == resultModeAny && len(loopEntry.breakBlocks) > 0 {
|
||||||
|
// there have been previous break statements, so be
|
||||||
|
// consistent with the already decided result mode
|
||||||
|
loc := loopEntry.loc
|
||||||
|
if loc { thisMode = resultModeLoc
|
||||||
|
} else { thisMode = resultModeVal }
|
||||||
|
}
|
||||||
|
irValue, loc, err = this.generateExpression(brk.Value, loopEntry.mode)
|
||||||
if err != nil { return nil, err }
|
if err != nil { return nil, err }
|
||||||
loopEntry.value = value
|
|
||||||
loopEntry.loc = loc
|
loopEntry.loc = loc
|
||||||
}
|
}
|
||||||
|
|
||||||
loopEntry.stub = this.blockManager.Block
|
loopEntry.breakBlocks = append(loopEntry.breakBlocks, &llvm.Incoming {
|
||||||
|
X: irValue,
|
||||||
|
Predecessor: this.blockManager.Block,
|
||||||
|
})
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,23 +192,135 @@ func (this *generator) generateMatch (match *entity.Match, mode resultMode) (llv
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *generator) generateLoop (loop *entity.Loop, mode resultMode) (llvm.Value, bool, error) {
|
func (this *generator) generateLoop (loop *entity.Loop, mode resultMode) (llvm.Value, bool, error) {
|
||||||
|
irResultType, err := this.generateType(loop.Type())
|
||||||
|
if err != nil { return nil, false, err }
|
||||||
|
|
||||||
previous := this.blockManager.Block
|
previous := this.blockManager.Block
|
||||||
body := this.blockManager.newBlock()
|
body := this.blockManager.newBlock()
|
||||||
|
exit := this.blockManager.newBlock()
|
||||||
previous.NewBr(body)
|
previous.NewBr(body)
|
||||||
|
|
||||||
loopEntry := this.blockManager.pushLoop(mode)
|
loopEntry := this.blockManager.pushLoop(mode)
|
||||||
defer this.blockManager.popLoop()
|
defer this.blockManager.popLoop()
|
||||||
|
|
||||||
_, _, err := this.generateExpressionAny(loop.Body)
|
this.blockManager.Block = body
|
||||||
|
_, _, err = this.generateExpressionAny(loop.Body)
|
||||||
|
if err != nil { return nil, false, err }
|
||||||
|
final := this.blockManager.Block
|
||||||
|
|
||||||
|
// discard/obtain results
|
||||||
|
this.blockManager.Block = exit
|
||||||
|
var irValue llvm.Value
|
||||||
|
if _, void := irResultType.(*llvm.TypeVoid); void {
|
||||||
|
loopEntry.tieUp(exit)
|
||||||
|
} else {
|
||||||
|
irValue = loopEntry.tieUpPhi(irResultType, exit)
|
||||||
|
}
|
||||||
|
if !final.Terminated () {
|
||||||
|
final.NewBr(body)
|
||||||
|
}
|
||||||
|
return irValue, loopEntry.loc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *generator) generateFor (loop *entity.For, mode resultMode) (llvm.Value, bool, error) {
|
||||||
|
irIndexType, err := this.generateTypeIndex()
|
||||||
|
if err != nil { return nil, false, err }
|
||||||
|
irResultType, err := this.generateType(loop.Type())
|
||||||
if err != nil { return nil, false, err }
|
if err != nil { return nil, false, err }
|
||||||
|
|
||||||
this.blockManager.NewBr(body)
|
// element index
|
||||||
exit := this.blockManager.newBlock()
|
var irIndexLoc llvm.Value
|
||||||
|
if loop.Index != nil {
|
||||||
|
irIndexLoc, err = this.blockManager.addDeclaration (
|
||||||
|
loop.Index, llvm.NewConstZeroInitializer(irIndexType))
|
||||||
|
if err != nil { return nil, false, err }
|
||||||
|
} else {
|
||||||
|
irIndexLoc = this.blockManager.newAllocaFront(irIndexType)
|
||||||
|
}
|
||||||
|
|
||||||
if loopEntry.stub != nil {
|
// element value
|
||||||
loopEntry.stub.SetTerminator(&llvm.TerminatorBr {
|
irElementLoc, err := this.blockManager.addDeclaration(loop.Element, nil)
|
||||||
Target: exit,
|
if err != nil { return nil, false, err }
|
||||||
|
irElementType, err := this.generateType(loop.Element.Type())
|
||||||
|
if err != nil { return nil, false, err }
|
||||||
|
|
||||||
|
// source slice/array
|
||||||
|
irOverLoc, err := this.generateExpressionLoc(loop.Over)
|
||||||
|
if err != nil { return nil, false, err }
|
||||||
|
irOverType, err := this.generateType(loop.Over.Type())
|
||||||
|
if err != nil { return nil, false, err }
|
||||||
|
baseOverType := analyzer.ReduceToBase(loop.Over.Type())
|
||||||
|
array, isArray := baseOverType.(*entity.TypeArray)
|
||||||
|
|
||||||
|
previous := this.blockManager.Block
|
||||||
|
header := this.blockManager.newBlock()
|
||||||
|
body := this.blockManager.newBlock()
|
||||||
|
exit := this.blockManager.newBlock()
|
||||||
|
previous.NewBr(header)
|
||||||
|
|
||||||
|
loopEntry := this.blockManager.pushLoop(mode)
|
||||||
|
defer this.blockManager.popLoop()
|
||||||
|
|
||||||
|
// check bounds
|
||||||
|
this.blockManager.Block = header
|
||||||
|
var irLength llvm.Value
|
||||||
|
if isArray {
|
||||||
|
irLength = llvm.NewConstInt(irIndexType, int64(array.Length))
|
||||||
|
} else {
|
||||||
|
irLength = this.blockManager.NewLoad (
|
||||||
|
irIndexType,
|
||||||
|
this.getSliceLengthFieldLoc(irOverLoc, irOverType))
|
||||||
|
}
|
||||||
|
this.blockManager.NewCondBr (
|
||||||
|
this.blockManager.NewICmp (
|
||||||
|
llvm.IPredicateULT,
|
||||||
|
this.blockManager.NewLoad(irIndexType, irIndexLoc),
|
||||||
|
irLength),
|
||||||
|
body, exit)
|
||||||
|
|
||||||
|
// set element
|
||||||
|
this.blockManager.Block = body
|
||||||
|
var irDataLoc llvm.Value
|
||||||
|
if isArray {
|
||||||
|
irDataLoc = irOverLoc
|
||||||
|
} else {
|
||||||
|
irDataLoc = this.getSliceDataAddress(irOverLoc, irOverType)
|
||||||
|
}
|
||||||
|
irElementLocInData := this.blockManager.NewGetElementPtr (
|
||||||
|
irElementType, irDataLoc,
|
||||||
|
this.blockManager.NewLoad(irIndexType, irIndexLoc))
|
||||||
|
this.blockManager.NewStore (
|
||||||
|
this.blockManager.NewLoad(irElementType, irElementLocInData),
|
||||||
|
irElementLoc)
|
||||||
|
|
||||||
|
// generate loop body
|
||||||
|
this.blockManager.Block = body
|
||||||
|
_, _, err = this.generateExpressionAny(loop.Body)
|
||||||
|
if err != nil { return nil, false, err }
|
||||||
|
|
||||||
|
// increment index
|
||||||
|
this.blockManager.NewStore (
|
||||||
|
this.blockManager.NewAdd (
|
||||||
|
this.blockManager.NewLoad(irIndexType, irIndexLoc),
|
||||||
|
llvm.NewConstInt(irIndexType, 1)),
|
||||||
|
irIndexLoc)
|
||||||
|
|
||||||
|
// discard/obtain results
|
||||||
|
final := this.blockManager.Block
|
||||||
|
this.blockManager.Block = exit
|
||||||
|
var irValue llvm.Value
|
||||||
|
if _, void := irResultType.(*llvm.TypeVoid); void {
|
||||||
|
loopEntry.tieUp(exit)
|
||||||
|
} else {
|
||||||
|
irValue = loopEntry.tieUpPhi(irResultType, exit, &llvm.Incoming {
|
||||||
|
X: llvm.NewConstZeroInitializer(irResultType),
|
||||||
|
Predecessor: header,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return loopEntry.value, loopEntry.loc, nil
|
|
||||||
|
// loop back around
|
||||||
|
if !final.Terminated () {
|
||||||
|
final.NewBr(header)
|
||||||
|
}
|
||||||
|
return irValue, loopEntry.loc, nil
|
||||||
}
|
}
|
||||||
|
@ -141,28 +141,28 @@ define void @"0zNZN147MN2wzMAQ6NS2dQ==::print"(%"AAAAAAAAAAAAAAAAAAAAAA==::Strin
|
|||||||
%4 = getelementptr { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }, ptr %1, i32 0, i32 1
|
%4 = getelementptr { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }, ptr %1, i32 0, i32 1
|
||||||
%5 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Index", ptr %4
|
%5 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Index", ptr %4
|
||||||
%6 = icmp ult %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %5, 1
|
%6 = icmp ult %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %5, 1
|
||||||
br i1 %6, label %7, label %8
|
br i1 %6, label %8, label %9
|
||||||
7:
|
7:
|
||||||
br label %21
|
|
||||||
8:
|
|
||||||
%9 = getelementptr { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }, ptr %1, i32 0, i32 0
|
|
||||||
%10 = load ptr, ptr %9
|
|
||||||
%11 = call %"AAAAAAAAAAAAAAAAAAAAAA==::Index" @write(i32 1, ptr %10, i64 1)
|
|
||||||
%12 = getelementptr { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }, ptr %1, i32 0, i32 0
|
|
||||||
%13 = load ptr, ptr %12
|
|
||||||
%14 = getelementptr { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }, ptr %1, i32 0, i32 1
|
|
||||||
%15 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Index", ptr %14
|
|
||||||
%16 = sub %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %15, 1
|
|
||||||
%17 = getelementptr i8, ptr %13, i64 1
|
|
||||||
%18 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %2, i32 0, i32 0
|
|
||||||
store ptr %17, ptr %18
|
|
||||||
%19 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %2, i32 0, i32 1
|
|
||||||
store %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %16, ptr %19
|
|
||||||
%20 = load %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %2
|
|
||||||
store %"AAAAAAAAAAAAAAAAAAAAAA==::String" %20, ptr %1
|
|
||||||
br label %3
|
|
||||||
21:
|
|
||||||
ret void
|
ret void
|
||||||
|
8:
|
||||||
|
br label %7
|
||||||
|
9:
|
||||||
|
%10 = getelementptr { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }, ptr %1, i32 0, i32 0
|
||||||
|
%11 = load ptr, ptr %10
|
||||||
|
%12 = call %"AAAAAAAAAAAAAAAAAAAAAA==::Index" @write(i32 1, ptr %11, i64 1)
|
||||||
|
%13 = getelementptr { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }, ptr %1, i32 0, i32 0
|
||||||
|
%14 = load ptr, ptr %13
|
||||||
|
%15 = getelementptr { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }, ptr %1, i32 0, i32 1
|
||||||
|
%16 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Index", ptr %15
|
||||||
|
%17 = sub %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %16, 1
|
||||||
|
%18 = getelementptr i8, ptr %14, i64 1
|
||||||
|
%19 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %2, i32 0, i32 0
|
||||||
|
store ptr %18, ptr %19
|
||||||
|
%20 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %2, i32 0, i32 1
|
||||||
|
store %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %17, ptr %20
|
||||||
|
%21 = load %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %2
|
||||||
|
store %"AAAAAAAAAAAAAAAAAAAAAA==::String" %21, ptr %1
|
||||||
|
br label %3
|
||||||
}
|
}
|
||||||
declare %"AAAAAAAAAAAAAAAAAAAAAA==::Index" @write(i32 %file, ptr %buffer, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %count)
|
declare %"AAAAAAAAAAAAAAAAAAAAAA==::Index" @write(i32 %file, ptr %buffer, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %count)
|
||||||
`,
|
`,
|
||||||
|
@ -15,7 +15,6 @@ define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
|||||||
store i64 9186060094042213285, ptr %3
|
store i64 9186060094042213285, ptr %3
|
||||||
%5 = load double, ptr %1
|
%5 = load double, ptr %1
|
||||||
store double %5, ptr %4
|
store double %5, ptr %4
|
||||||
%6 = load %"0zNZN147MN2wzMAQ6NS2dQ==::U", ptr %2
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
@ -118,6 +118,7 @@ func (this *treeParser) parseExpressionRootIdent () (entity.Expression, error) {
|
|||||||
case "if": return this.parseIfElse()
|
case "if": return this.parseIfElse()
|
||||||
case "match": return this.parseMatch()
|
case "match": return this.parseMatch()
|
||||||
case "loop": return this.parseLoop()
|
case "loop": return this.parseLoop()
|
||||||
|
case "for": return this.parseFor()
|
||||||
default:
|
default:
|
||||||
this.Next()
|
this.Next()
|
||||||
switch this.Kind() {
|
switch this.Kind() {
|
||||||
@ -615,3 +616,38 @@ func (this *treeParser) parseLoop () (*entity.Loop, error) {
|
|||||||
Body: body,
|
Body: body,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *treeParser) parseFor () (*entity.For, error) {
|
||||||
|
err := this.ExpectValue(lexer.Ident, "for")
|
||||||
|
if err != nil { return nil, err }
|
||||||
|
this.Next()
|
||||||
|
loop := &entity.For {
|
||||||
|
Pos: this.Pos(),
|
||||||
|
}
|
||||||
|
|
||||||
|
firstDeclaration, err := this.parseDeclaration()
|
||||||
|
if err != nil { return nil, err }
|
||||||
|
|
||||||
|
if this.ValueIs("in") {
|
||||||
|
loop.Element = firstDeclaration
|
||||||
|
} else {
|
||||||
|
loop.Index = firstDeclaration
|
||||||
|
element, err := this.parseDeclaration()
|
||||||
|
if err != nil { return nil, err }
|
||||||
|
loop.Element = element
|
||||||
|
}
|
||||||
|
|
||||||
|
err = this.ExpectValue(lexer.Ident, "in")
|
||||||
|
if err != nil { return nil, err }
|
||||||
|
this.Next()
|
||||||
|
|
||||||
|
over, err := this.parseExpression()
|
||||||
|
if err != nil { return nil, err }
|
||||||
|
loop.Over = over
|
||||||
|
|
||||||
|
body, err := this.parseExpression()
|
||||||
|
if err != nil { return nil, err }
|
||||||
|
loop.Body = body
|
||||||
|
|
||||||
|
return loop, nil
|
||||||
|
}
|
||||||
|
@ -72,6 +72,8 @@ testString (test,
|
|||||||
- [ifElse]:Int = if true then 4 else 5
|
- [ifElse]:Int = if true then 4 else 5
|
||||||
- [dangleElse]:Int = if true then if false then 3 else 5
|
- [dangleElse]:Int = if true then if false then 3 else 5
|
||||||
- [loop]:Int = {i:Int=0 if [< i 3] then return 1 loop {[print 3] if [> i 3] then break 0 i=[++ i]}}
|
- [loop]:Int = {i:Int=0 if [< i 3] then return 1 loop {[print 3] if [> i 3] then break 0 i=[++ i]}}
|
||||||
|
- [for s:String] = for i:Index e:Byte in str if [> i 3] then [break e]
|
||||||
|
- [for s:String] = for e:Byte in s [break e]
|
||||||
- [matchToInt u:(| Int F64)]:Int = match u | u:Int u | u:F64 [~ Int u]`,
|
- [matchToInt u:(| Int F64)]:Int = match u | u:Int u | u:F64 [~ Int u]`,
|
||||||
// input
|
// input
|
||||||
`
|
`
|
||||||
@ -118,6 +120,10 @@ testString (test,
|
|||||||
i = [++ i]
|
i = [++ i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
[for s:String] = for i:Index e:Byte in str
|
||||||
|
if [> i 3] then [break e]
|
||||||
|
[for s:String] = for e:Byte in s
|
||||||
|
[break e]
|
||||||
[matchToInt u:(| Int F64)]:Int = match u
|
[matchToInt u:(| Int F64)]:Int = match u
|
||||||
| u:Int u
|
| u:Int u
|
||||||
| u:F64 [~Int u]
|
| u:F64 [~Int u]
|
||||||
|
Loading…
Reference in New Issue
Block a user