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)
|
||||
case *entity.Loop:
|
||||
return this.analyzeLoop(into, mode, expression)
|
||||
case *entity.For:
|
||||
return this.analyzeFor(into, mode, expression)
|
||||
case *entity.Break:
|
||||
return this.analyzeBreak(into, mode, expression)
|
||||
case *entity.Return:
|
||||
|
@ -808,6 +808,52 @@ func (this *Tree) analyzeLoop (
|
||||
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 (
|
||||
into entity.Type,
|
||||
mode strictness,
|
||||
|
@ -16,7 +16,7 @@ func (this *scopeContextManager) popScopeContext () {
|
||||
*this = (*this)[:len(*this) - 1]
|
||||
}
|
||||
|
||||
func (this *scopeContextManager) pushLoop (loop *entity.Loop) {
|
||||
func (this *scopeContextManager) pushLoop (loop entity.Breakable) {
|
||||
this.assertPopulated()
|
||||
(*this)[len(*this) - 1].pushLoop(loop)
|
||||
}
|
||||
@ -26,7 +26,7 @@ func (this *scopeContextManager) popLoop () {
|
||||
(*this)[len(*this) - 1].popLoop()
|
||||
}
|
||||
|
||||
func (this *scopeContextManager) topLoop () (*entity.Loop, bool) {
|
||||
func (this *scopeContextManager) topLoop () (entity.Breakable, bool) {
|
||||
this.assertPopulated()
|
||||
return (*this)[len(*this) - 1].topLoop()
|
||||
}
|
||||
@ -71,11 +71,11 @@ func (this *scopeContextManager) assertPopulated () {
|
||||
// entities.
|
||||
type scopeContext struct {
|
||||
scopes []entity.Scoped
|
||||
loops []*entity.Loop
|
||||
loops []entity.Breakable
|
||||
declaration entity.TopLevel
|
||||
}
|
||||
|
||||
func (this *scopeContext) pushLoop (loop *entity.Loop) {
|
||||
func (this *scopeContext) pushLoop (loop entity.Breakable) {
|
||||
this.loops = append(this.loops, loop)
|
||||
}
|
||||
|
||||
@ -84,7 +84,7 @@ func (this *scopeContext) popLoop () {
|
||||
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 }
|
||||
return this.loops[len(this.loops) - 1], true
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
package compiler
|
||||
|
||||
import "time"
|
||||
import "io/fs"
|
||||
import "embed"
|
||||
import "strings"
|
||||
import "testing"
|
||||
import "context"
|
||||
import "os/exec"
|
||||
import "path/filepath"
|
||||
import "git.tebibyte.media/fspl/fspl/entity"
|
||||
@ -73,9 +75,9 @@ func testUnit (
|
||||
// instantiate and configure the compiler
|
||||
compOutputBuilder := new(strings.Builder)
|
||||
comp := defaultCompiler()
|
||||
defer func () {
|
||||
outputCompilerLog := func () {
|
||||
test.Log("COMPILER LOG (main unit):\n" + compOutputBuilder.String())
|
||||
} ()
|
||||
}
|
||||
comp.Writer = compOutputBuilder
|
||||
comp.Output = filepath.Join(temp, "output.o")
|
||||
|
||||
@ -83,8 +85,10 @@ func testUnit (
|
||||
// compile to object file
|
||||
err := comp.CompileUnit(address)
|
||||
if err != nil {
|
||||
outputCompilerLog()
|
||||
test.Fatal("compiler returned error:", errors.Format(err))
|
||||
}
|
||||
outputCompilerLog()
|
||||
|
||||
// link the object file into an executable
|
||||
executablePath := filepath.Join(temp, "output")
|
||||
@ -101,14 +105,21 @@ func testUnit (
|
||||
test.Fatal("error linking executable:", err)
|
||||
}
|
||||
|
||||
// run the executable file and check its output
|
||||
executableCommand := exec.Command(executablePath, args...)
|
||||
// run the executable file (with timeout) and check its output
|
||||
timeoutDuration := 10 * time.Millisecond
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeoutDuration)
|
||||
defer cancel()
|
||||
executableCommand := exec.CommandContext(ctx, executablePath, args...)
|
||||
stdoutBuilder := new(strings.Builder)
|
||||
executableCommand.Stdin = strings.NewReader(stdin)
|
||||
executableCommand.Stdout = stdoutBuilder
|
||||
test.Log("running executable command: ", executableCommand)
|
||||
err = executableCommand.Run()
|
||||
|
||||
if ctx.Err() == context.DeadlineExceeded {
|
||||
test.Errorf("timeout of %v exceeded!", timeoutDuration)
|
||||
}
|
||||
|
||||
// check error, compare exit code
|
||||
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||
code := exitErr.ExitCode()
|
||||
|
@ -91,3 +91,54 @@ testUnit (test,
|
||||
"", "false\n",
|
||||
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
|
||||
statements only apply to the closest containing loop. The value of the loop's
|
||||
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 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
|
||||
@ -362,6 +368,7 @@ this without hand-writing a parser.
|
||||
["else" <expression>]
|
||||
<match> -> "match" <expression> <case>*
|
||||
<loop> -> "loop" <expression>
|
||||
<for> -> "for" <expression> [<expression>] in <expression> <expression>
|
||||
<break> -> "[" "break" [<expression>] "]"
|
||||
<return> -> "[" "return" [<expression>] "]"
|
||||
<assignment> -> <expression> "=" <expression>
|
||||
|
@ -439,7 +439,14 @@ func (this *Match) String () string {
|
||||
return out
|
||||
}
|
||||
|
||||
// Breakable is any expression that can be halted using a break.
|
||||
type Breakable interface {
|
||||
Expression
|
||||
breakable()
|
||||
}
|
||||
|
||||
var _ Expression = &Loop { }
|
||||
var _ Breakable = &Loop { }
|
||||
// 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
|
||||
// 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
|
||||
}
|
||||
func (*Loop) expression(){}
|
||||
func (*Loop) breakable(){}
|
||||
func (this *Loop) Position () errors.Position { return this.Pos }
|
||||
func (this *Loop) Type () Type { return this.Ty }
|
||||
func (this *Loop) HasExplicitType () bool {
|
||||
@ -467,6 +475,44 @@ func (this *Loop) String () string {
|
||||
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 { }
|
||||
// Break allows breaking out of loops. It has no value and may not be assigned
|
||||
// to anything. It is never a valid location expression.
|
||||
@ -476,7 +522,7 @@ type Break struct {
|
||||
Value Expression
|
||||
|
||||
// Semantics
|
||||
Loop *Loop
|
||||
Loop Breakable
|
||||
}
|
||||
func (*Break) expression(){}
|
||||
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)
|
||||
if err != nil { return nil, err }
|
||||
this.blockManager.NewStore(source, destDataFieldLoc)
|
||||
return this.blockManager.NewLoad(irDestType, irDestLoc), nil
|
||||
|
||||
if destinationSpecified {
|
||||
return nil, nil
|
||||
} else {
|
||||
return this.blockManager.NewLoad(irDestType, irDestLoc), nil
|
||||
}
|
||||
|
||||
// conversion from any type to pointer
|
||||
case *entity.TypePointer:
|
||||
@ -192,6 +197,8 @@ func (this *generator) generateAssignmentToDestination ( // TODO: add -Val suffi
|
||||
switch sourceTypeBase := analyzer.ReduceToBase(source.Type()).(type) {
|
||||
// conversion from array to slice
|
||||
case *entity.TypeArray:
|
||||
irIndexType, err := this.generateTypeIndex()
|
||||
if err != nil { return nil, err }
|
||||
array, err := this.generateExpressionLoc(source)
|
||||
if err != nil { return nil, err }
|
||||
irDestType, err := this.generateType(destType)
|
||||
@ -204,10 +211,12 @@ func (this *generator) generateAssignmentToDestination ( // TODO: add -Val suffi
|
||||
destLengthField := this.getSliceLengthFieldLoc(irDestLoc, irDestType)
|
||||
this.blockManager.NewStore(array, destDataField)
|
||||
this.blockManager.NewStore (
|
||||
llvm.NewConstInt(llvm.I32, int64(sourceTypeBase.Length)),
|
||||
llvm.NewConstInt(irIndexType, int64(sourceTypeBase.Length)),
|
||||
destLengthField)
|
||||
|
||||
if !destinationSpecified {
|
||||
|
||||
if destinationSpecified {
|
||||
return nil, nil
|
||||
} else {
|
||||
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"
|
||||
|
||||
type loopEntry struct {
|
||||
value llvm.Value
|
||||
stub *llvm.Block
|
||||
mode resultMode
|
||||
loc bool
|
||||
breakBlocks []*llvm.Incoming // incoming value/block pairs of breaks
|
||||
mode resultMode // result mode for break statements
|
||||
loc bool // for when mode is resultModeAny
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
@ -76,7 +76,7 @@ testString (test,
|
||||
`)
|
||||
}
|
||||
|
||||
func TestLoop (test *testing.T) {
|
||||
func TestLoopSimple (test *testing.T) {
|
||||
testString (test,
|
||||
`define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
0:
|
||||
@ -105,7 +105,8 @@ testString (test,
|
||||
2:
|
||||
br label %3
|
||||
3:
|
||||
store i64 5, ptr %1
|
||||
%4 = phi i64 [ 5, %2 ]
|
||||
store i64 %4, ptr %1
|
||||
ret void
|
||||
}
|
||||
`,
|
||||
@ -128,19 +129,20 @@ testString (test,
|
||||
2:
|
||||
%3 = load i64, ptr %1
|
||||
%4 = icmp slt i64 %3, 3
|
||||
br i1 %4, label %5, label %8
|
||||
br i1 %4, label %7, label %10
|
||||
5:
|
||||
%6 = load i64, ptr %1
|
||||
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:
|
||||
%6 = phi i64 [ %8, %7 ]
|
||||
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:
|
||||
%4 = load i64, ptr %1
|
||||
%5 = icmp eq i64 %4, 5
|
||||
br i1 %5, label %6, label %9
|
||||
br i1 %5, label %8, label %11
|
||||
6:
|
||||
br label %11
|
||||
7:
|
||||
%8 = phi i64 [ %10, %9 ]
|
||||
store i64 %8, ptr %2
|
||||
br label %3
|
||||
%7 = phi i64 [ 0, %8 ]
|
||||
ret i64 %7
|
||||
8:
|
||||
br label %6
|
||||
9:
|
||||
%10 = load i64, ptr %1
|
||||
br label %7
|
||||
%10 = phi i64 [ %12, %11 ]
|
||||
store i64 %10, ptr %2
|
||||
br label %3
|
||||
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)
|
||||
case *entity.Loop:
|
||||
return this.generateLoop(expression, resultModeAny)
|
||||
case *entity.For:
|
||||
return this.generateFor(expression, resultModeAny)
|
||||
|
||||
// we get nothing from these
|
||||
case *entity.Assignment:
|
||||
@ -161,6 +163,9 @@ func (this *generator) generateExpressionVal (expression entity.Expression) (llv
|
||||
case *entity.Loop:
|
||||
val, _, err := this.generateLoop(expression, resultModeVal)
|
||||
return val, err
|
||||
case *entity.For:
|
||||
val, _, err := this.generateFor(expression, resultModeVal)
|
||||
return val, err
|
||||
case *entity.LiteralInt:
|
||||
return this.generateLiteralInt(expression)
|
||||
case *entity.LiteralFloat:
|
||||
@ -233,6 +238,9 @@ func (this *generator) generateExpressionLoc (expression entity.Expression) (llv
|
||||
case *entity.Loop:
|
||||
loc, _, err := this.generateLoop(expression, resultModeLoc)
|
||||
return loc, err
|
||||
case *entity.For:
|
||||
loc, _, err := this.generateFor(expression, resultModeLoc)
|
||||
return loc, err
|
||||
case *entity.LiteralArray:
|
||||
return this.generateLiteralArrayLoc(expression, nil)
|
||||
case *entity.LiteralString:
|
||||
|
@ -3,7 +3,7 @@ package generator
|
||||
// import "fmt"
|
||||
import "git.tebibyte.media/fspl/fspl/llvm"
|
||||
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) {
|
||||
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) {
|
||||
loopEntry := this.blockManager.topLoop()
|
||||
var irValue llvm.Value
|
||||
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 }
|
||||
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
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
irResultType, err := this.generateType(loop.Type())
|
||||
if err != nil { return nil, false, err }
|
||||
|
||||
previous := this.blockManager.Block
|
||||
body := this.blockManager.newBlock()
|
||||
exit := this.blockManager.newBlock()
|
||||
previous.NewBr(body)
|
||||
|
||||
loopEntry := this.blockManager.pushLoop(mode)
|
||||
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 }
|
||||
|
||||
this.blockManager.NewBr(body)
|
||||
exit := this.blockManager.newBlock()
|
||||
// element index
|
||||
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 {
|
||||
loopEntry.stub.SetTerminator(&llvm.TerminatorBr {
|
||||
Target: exit,
|
||||
// element value
|
||||
irElementLoc, err := this.blockManager.addDeclaration(loop.Element, nil)
|
||||
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
|
||||
%5 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Index", ptr %4
|
||||
%6 = icmp ult %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %5, 1
|
||||
br i1 %6, label %7, label %8
|
||||
br i1 %6, label %8, label %9
|
||||
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
|
||||
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)
|
||||
`,
|
||||
|
@ -15,7 +15,6 @@ define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
store i64 9186060094042213285, ptr %3
|
||||
%5 = load double, ptr %1
|
||||
store double %5, ptr %4
|
||||
%6 = load %"0zNZN147MN2wzMAQ6NS2dQ==::U", ptr %2
|
||||
ret void
|
||||
}
|
||||
`,
|
||||
|
@ -118,6 +118,7 @@ func (this *treeParser) parseExpressionRootIdent () (entity.Expression, error) {
|
||||
case "if": return this.parseIfElse()
|
||||
case "match": return this.parseMatch()
|
||||
case "loop": return this.parseLoop()
|
||||
case "for": return this.parseFor()
|
||||
default:
|
||||
this.Next()
|
||||
switch this.Kind() {
|
||||
@ -615,3 +616,38 @@ func (this *treeParser) parseLoop () (*entity.Loop, error) {
|
||||
Body: body,
|
||||
}, 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
|
||||
- [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]}}
|
||||
- [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]`,
|
||||
// input
|
||||
`
|
||||
@ -118,6 +120,10 @@ testString (test,
|
||||
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]
|
||||
|
Loading…
Reference in New Issue
Block a user