diff --git a/lang/assembly/tac/triple.go b/lang/assembly/tac/triple.go index 18c41154..369856d5 100644 --- a/lang/assembly/tac/triple.go +++ b/lang/assembly/tac/triple.go @@ -23,6 +23,20 @@ type Triple struct { currproc *Proc } +var _ ast.CodeGenerator = (*Triple)(nil) + +func (gen *Triple) VisitAppendExpression(node *ast.AppendExpression) error { + panic("unimplemented") +} + +func (gen *Triple) VisitListExpression(node *ast.ListExpression) error { + panic("unimplemented") +} + +func (gen *Triple) VisitListCountExpression(node *ast.ListCountExpression) error { + panic("unimplemented") +} + var _ AsmOp = (*Triple)(nil) func (gen *Triple) Gen(g AssemblyOpGenerator) error { @@ -62,8 +76,6 @@ func (gen *Triple) Display() { } } -var _ ast.CodeGenerator = (*Triple)(nil) - func NewTripleGenerator() *Triple { return &Triple{ GlobalOps: []AsmOp{}, @@ -117,6 +129,10 @@ func (gen *Triple) VisitArrayType(node *ast.ArrayType) error { return fmt.Errorf("unimplemented") } +func (gen *Triple) VisitSliceType(node *ast.SliceType) error { + return fmt.Errorf("unimplemented") +} + func (gen *Triple) VisitAssignmentExpression(node *ast.AssignmentExpression) error { err := node.Value.Accept(gen) if err != nil { @@ -516,6 +532,10 @@ func (gen *Triple) ZeroOfArrayType(node *ast.ArrayType) error { return fmt.Errorf("unimplemented") } +func (gen *Triple) ZeroOfSliceType(node *ast.SliceType) error { + return fmt.Errorf("unimplemented") +} + func (gen *Triple) ZeroOfBoolType(node *ast.BoolType) error { gen.setLastValue(BoolVal{value: "false"}) diff --git a/lang/ast/append_expression.go b/lang/ast/append_expression.go new file mode 100644 index 00000000..1b0aaddb --- /dev/null +++ b/lang/ast/append_expression.go @@ -0,0 +1,23 @@ +package ast + +import "swahili/lang/lexer" + +type AppendExpression struct { + Appendable Expression + Value Expression + Tokens []lexer.Token +} + +var _ Expression = (*AppendExpression)(nil) + +func (a *AppendExpression) Accept(g CodeGenerator) error { + return g.VisitAppendExpression(a) +} + +func (a *AppendExpression) TokenStream() []lexer.Token { + return a.Tokens +} + +func (a *AppendExpression) VisitedSwaType() Type { + panic("unimplemented") +} diff --git a/lang/ast/ast.go b/lang/ast/ast.go index 222efeea..81d8ac47 100644 --- a/lang/ast/ast.go +++ b/lang/ast/ast.go @@ -81,6 +81,9 @@ type ExpressionsCodeGenerator interface { VisitSymbolValueExpression(node *SymbolValueExpression) error VisitSymbolAdressExpression(node *SymbolAdressExpression) error VisitBooleanExpression(node *BooleanExpression) error + VisitListExpression(node *ListExpression) error + VisitListCountExpression(node *ListCountExpression) error + VisitAppendExpression(node *AppendExpression) error } type TypeVisitor interface { @@ -96,6 +99,7 @@ type TypeVisitor interface { VisitVoidType(node *VoidType) error VisitBoolType(node *BoolType) error VisitByteType(node *ByteType) error + VisitSliceType(node *SliceType) error } type ZeroValueVisitor interface { @@ -111,4 +115,5 @@ type ZeroValueVisitor interface { ZeroOfVoidType(node *VoidType) error ZeroOfBoolType(node *BoolType) error ZeroOfByteType(node *ByteType) error + ZeroOfSliceType(node *SliceType) error } diff --git a/lang/ast/list_expression.go b/lang/ast/list_expression.go new file mode 100644 index 00000000..834ee40a --- /dev/null +++ b/lang/ast/list_expression.go @@ -0,0 +1,43 @@ +package ast + +import "swahili/lang/lexer" + +type ListExpression struct { + DataType Type + Length Expression + Capacity Expression + Tokens []lexer.Token +} + +var _ Expression = (*ListExpression)(nil) + +func (m *ListExpression) Accept(g CodeGenerator) error { + return g.VisitListExpression(m) +} + +func (m *ListExpression) TokenStream() []lexer.Token { + return m.Tokens +} + +func (m *ListExpression) VisitedSwaType() Type { + return m.DataType +} + +type ListCountExpression struct { + Expr Expression + Tokens []lexer.Token +} + +var _ Expression = (*ListCountExpression)(nil) + +func (l *ListCountExpression) Accept(g CodeGenerator) error { + return g.VisitListCountExpression(l) +} + +func (l *ListCountExpression) TokenStream() []lexer.Token { + return l.Tokens +} + +func (l *ListCountExpression) VisitedSwaType() Type { + panic("unimplemented") +} diff --git a/lang/ast/types.go b/lang/ast/types.go index 16b17de3..07ece208 100644 --- a/lang/ast/types.go +++ b/lang/ast/types.go @@ -37,6 +37,8 @@ func (dt DataType) String() string { return "Bool" case DataTypeByte: return "Byte" + case DataTypeSlice: + return "Slice" default: fmt.Printf("Unmatched data type %d", dt) os.Exit(1) @@ -60,6 +62,7 @@ const ( DataTypeError DataTypeTuple DataTypeBool + DataTypeSlice ) type SymbolType struct { @@ -139,6 +142,45 @@ func (typ ArrayType) Accept(g CodeGenerator) error { return g.VisitArrayType(&typ) } +type SliceType struct { + Underlying Type +} + +func (typ SliceType) Equals(other Type) bool { + if typ.Value() != other.Value() { + return false + } + + oth, ok := other.(SliceType) + if !ok { + othPointer, ok := other.(*SliceType) + if !ok { + return false + } + oth = *othPointer + } + + return typ.Underlying.Equals(oth.Underlying) +} + +func (t SliceType) String() string { + return fmt.Sprintf("%s(%s)", t.Value(), t.Underlying.Value().String()) +} + +func (typ SliceType) AcceptZero(g CodeGenerator) error { + return g.ZeroOfSliceType(&typ) +} + +var _ Type = (*SliceType)(nil) + +func (SliceType) Value() DataType { + return DataTypeSlice +} + +func (typ SliceType) Accept(g CodeGenerator) error { + return g.VisitSliceType(&typ) +} + type ByteType struct{} var _ Type = (*ByteType)(nil) diff --git a/lang/compiler/astformat/json.go b/lang/compiler/astformat/json.go index d6bc5763..66f12609 100644 --- a/lang/compiler/astformat/json.go +++ b/lang/compiler/astformat/json.go @@ -8,12 +8,12 @@ type Json struct { Element map[string]any } -// VisitByteType implements [ast.CodeGenerator]. +var _ ast.CodeGenerator = (*Json)(nil) + func (j *Json) VisitByteType(node *ast.ByteType) error { panic("unimplemented") } -// ZeroOfByteType implements [ast.CodeGenerator]. func (j *Json) ZeroOfByteType(node *ast.ByteType) error { panic("unimplemented") } @@ -31,7 +31,6 @@ func (j *Json) VisitBoolType(node *ast.BoolType) error { return nil } -// VisitBooleanExpression implements [ast.CodeGenerator]. func (j *Json) VisitBooleanExpression(node *ast.BooleanExpression) error { res := make(map[string]any) res["BooleanExpression"] = node.Value @@ -41,12 +40,24 @@ func (j *Json) VisitBooleanExpression(node *ast.BooleanExpression) error { return nil } +func (j *Json) VisitListExpression(node *ast.ListExpression) error { + m := make(map[string]any) + + _ = node.DataType.Accept(j) + m["Type"] = j.getLastResult() + + res := make(map[string]any) + res["ListExpression"] = m + + j.setLastResult(res) + + return nil +} + func NewJsonFormatter() *Json { return &Json{Element: map[string]any{}} } -var _ ast.CodeGenerator = (*Json)(nil) - func (j *Json) VisitArrayAccessExpression(node *ast.ArrayAccessExpression) error { m := make(map[string]any) @@ -109,6 +120,27 @@ func (j *Json) VisitArrayType(node *ast.ArrayType) error { return nil } +func (j *Json) VisitSliceType(node *ast.SliceType) error { + res := make(map[string]any) + _ = node.Underlying.Accept(j) + res["SliceType"] = j.getLastResult() + + j.setLastResult(res) + + return nil +} + +func (j *Json) VisitListCountExpression(node *ast.ListCountExpression) error { + _ = node.Expr.Accept(j) + + m := make(map[string]any) + m["ListCountExpression"] = j.getLastResult() + + j.setLastResult(m) + + return nil +} + func (j *Json) VisitAssignmentExpression(node *ast.AssignmentExpression) error { m := make(map[string]any) m["Operator"] = node.Operator.Value @@ -163,6 +195,22 @@ func (j *Json) VisitTupleAssignmentExpression(node *ast.TupleAssignmentExpressio return nil } +func (j *Json) VisitAppendExpression(node *ast.AppendExpression) error { + m := make(map[string]any) + _ = node.Appendable.Accept(j) + m["Appendable"] = j.getLastResult() + + _ = node.Value.Accept(j) + m["Right"] = j.getLastResult() + + res := make(map[string]any) + res["AppendExpression"] = m + + j.setLastResult(res) + + return nil +} + func (j *Json) VisitBinaryExpression(node *ast.BinaryExpression) error { m := make(map[string]any) _ = node.Left.Accept(j) @@ -622,6 +670,13 @@ func (j *Json) ZeroOfVoidType(node *ast.VoidType) error { panic("unimplemented") } +func (j *Json) ZeroOfSliceType(node *ast.SliceType) error { + res := make(map[string]any) + res["ZeroOfSliceType"] = nil + j.setLastResult(res) + return nil +} + func (g *Json) setLastResult(res map[string]any) { g.Element = res } diff --git a/lang/compiler/astformat/tree_drawer.go b/lang/compiler/astformat/tree_drawer.go index 88c0d774..2e173b73 100644 --- a/lang/compiler/astformat/tree_drawer.go +++ b/lang/compiler/astformat/tree_drawer.go @@ -12,18 +12,22 @@ type TreeDrawer struct { isLast []bool } -// VisitByteType implements [ast.CodeGenerator]. +var _ ast.CodeGenerator = (*TreeDrawer)(nil) + +func (t *TreeDrawer) VisitListCountExpression(node *ast.ListCountExpression) error { + t.writeLine(fmt.Sprintf("ListCountExpr(%s)", node.Expr)) + + return nil +} + func (t *TreeDrawer) VisitByteType(node *ast.ByteType) error { panic("unimplemented") } -// ZeroOfByteType implements [ast.CodeGenerator]. func (t *TreeDrawer) ZeroOfByteType(node *ast.ByteType) error { panic("unimplemented") } -var _ ast.CodeGenerator = (*TreeDrawer)(nil) - func NewTreeDrawer(w io.Writer) *TreeDrawer { return &TreeDrawer{ w: w, @@ -99,6 +103,17 @@ func (t *TreeDrawer) VisitSymbolAdressExpression(node *ast.SymbolAdressExpressio return nil } +func (t *TreeDrawer) VisitListExpression(node *ast.ListExpression) error { + t.writeLine("ListExpression") + + err := t.visitType(node.DataType, true) + if err != nil { + return err + } + + return nil +} + func (t *TreeDrawer) VisitSymbolValueExpression(node *ast.SymbolValueExpression) error { t.writeLine("SymbolValueExpression") @@ -185,7 +200,6 @@ func (t *TreeDrawer) VisitConditionalStatement(node *ast.ConditionalStatetement) func (t *TreeDrawer) VisitWhileStatement(node *ast.WhileStatement) error { t.writeLine("WhileStatement") t.isLast = append(t.isLast, false) - t.isLast = t.isLast[:len(t.isLast)-1] t.writeLine("Condition") if err := t.visitChild(node.Condition, false); err != nil { @@ -293,6 +307,27 @@ func (t *TreeDrawer) VisitErrorExpression(node *ast.ErrorExpression) error { return nil } +func (t *TreeDrawer) VisitAppendExpression(node *ast.AppendExpression) error { + t.writeLine("AppendExpression") + t.isLast = append(t.isLast, false) + t.writeLine("Left") + + if err := t.visitChild(node.Appendable, false); err != nil { + return err + } + + t.isLast[len(t.isLast)-1] = true + + t.writeLine("Right") + if err := t.visitChild(node.Value, true); err != nil { + return err + } + + t.isLast = t.isLast[:len(t.isLast)-1] + + return nil +} + func (t *TreeDrawer) VisitBinaryExpression(node *ast.BinaryExpression) error { t.writeLine(fmt.Sprintf("BinaryExpression (%s)", node.Operator.Value)) t.isLast = append(t.isLast, false) @@ -322,6 +357,7 @@ func (t *TreeDrawer) VisitFunctionCall(node *ast.FunctionCallExpression) error { return err } } + return nil } @@ -495,6 +531,11 @@ func (t *TreeDrawer) VisitArrayType(node *ast.ArrayType) error { return t.visitType(node.Underlying, true) } +func (t *TreeDrawer) VisitSliceType(node *ast.SliceType) error { + t.writeLine("Type: Slice") + return t.visitType(node.Underlying, true) +} + func (t *TreeDrawer) VisitVoidType(node *ast.VoidType) error { t.writeLine("Type: Void") @@ -529,4 +570,5 @@ func (t *TreeDrawer) ZeroOfPointerType(node *ast.PointerType) error { return n func (t *TreeDrawer) ZeroOfStringType(node *ast.StringType) error { return nil } func (t *TreeDrawer) ZeroOfErrorType(node *ast.ErrorType) error { return nil } func (t *TreeDrawer) ZeroOfArrayType(node *ast.ArrayType) error { return nil } +func (t *TreeDrawer) ZeroOfSliceType(node *ast.SliceType) error { return nil } func (t *TreeDrawer) ZeroOfVoidType(node *ast.VoidType) error { return nil } diff --git a/lang/compiler/llvm_generator.go b/lang/compiler/llvm_generator.go index 20f57d13..86302ea3 100644 --- a/lang/compiler/llvm_generator.go +++ b/lang/compiler/llvm_generator.go @@ -27,6 +27,221 @@ type LLVMGenerator struct { currentFuncReturnType ast.Type } +var _ ast.CodeGenerator = (*LLVMGenerator)(nil) + +func (g *LLVMGenerator) VisitAppendExpression(node *ast.AppendExpression) error { + old := g.logger.Step("AppendExpression") + + defer g.logger.Restore(old) + + err := node.Appendable.Accept(g) + if err != nil { + return err + } + + sliceRes := g.getLastResult() + slice := sliceRes.Value + + g.Debugf("slice res %v", slice) + g.Debugf("array entry %v", sliceRes.SymbolTableEntry) + g.Debugf("swa type %v", sliceRes.SwaType) + + var sliceSwaType ast.SliceType + + if st, ok := sliceRes.SwaType.(ast.SliceType); ok { + sliceSwaType = st + } else if st, ok := sliceRes.SwaType.(*ast.SliceType); ok { + sliceSwaType = *st + } else { + return g.Ctx.Dialect.Error("AppendExpression.AppendableIsNotASlice", sliceRes.SwaType) + } + + sliceLLVMType := slice.Type() + g.Debugf(g.formatLLVMType(sliceLLVMType)) + + if sliceLLVMType.TypeKind() != llvm.StructTypeKind { + err := g.Ctx.Dialect.Error("AppendExpression.AppendableIsNotASlice", sliceLLVMType.TypeKind()) + g.Debugf(err.Error()) + + return err + } + + g.Debugf("Slice type %s", g.formatLLVMType(sliceLLVMType)) + + err = sliceSwaType.Underlying.Accept(g) + if err != nil { + return err + } + + elemLLVMType := g.getLastTypeVisitResult().Type + structFields := sliceLLVMType.StructElementTypes() + ptrType := structFields[0] + lenType := structFields[1] + capType := structFields[2] + + err = node.Value.Accept(g) + if err != nil { + return err + } + + item := g.getLastResult() + + g.Debugf("item %+v", item.Value) + + ptrOld := g.Ctx.Builder.CreateExtractValue(*slice, 0, "ptr.old") + lenOld := g.Ctx.Builder.CreateExtractValue(*slice, 1, "len.old") + capOld := g.Ctx.Builder.CreateExtractValue(*slice, 2, "cap.old") + + full := g.Ctx.Builder.CreateICmp(llvm.IntEQ, lenOld, capOld, "is_full") + + currentBlock := g.Ctx.Builder.GetInsertBlock() + function := currentBlock.Parent() + growBlock := g.Ctx.Context.AddBasicBlock(function, "slice.grow") + mergeBlock := g.Ctx.Context.AddBasicBlock(function, "slice.append") + + g.Ctx.Builder.CreateCondBr(full, growBlock, mergeBlock) + + g.Ctx.Builder.SetInsertPointAtEnd(growBlock) + + isZero := g.Ctx.Builder.CreateICmp(llvm.IntEQ, capOld, llvm.ConstInt(capOld.Type(), 0, false), "cap_is_zero") + newCap := g.Ctx.Builder.CreateSelect(isZero, + llvm.ConstInt(capOld.Type(), 1, false), + g.Ctx.Builder.CreateMul(capOld, llvm.ConstInt(capOld.Type(), 2, false), "cap_x2"), + "new_cap") + + // Realloc + targetData := llvm.NewTargetData(g.Ctx.Module.DataLayout()) + sizeOfElem := targetData.TypeAllocSize(elemLLVMType) + targetData.Dispose() + + sizeOfElemVal := llvm.ConstInt(g.Ctx.Context.Int64Type(), sizeOfElem, false) + + newCap64 := g.Ctx.Builder.CreateZExt(newCap, g.Ctx.Context.Int64Type(), "new_cap64") + newSize := g.Ctx.Builder.CreateMul(newCap64, sizeOfElemVal, "realloc_size") + + oldPtr := g.Ctx.Builder.CreateExtractValue(*slice, 0, "old_ptr") + oldPtrI8 := g.Ctx.Builder.CreateBitCast(oldPtr, llvm.PointerType(g.Ctx.Context.Int8Type(), 0), "old_ptr_i8") + + reallocFunc := g.getReallocFunc() + newPtrI8 := g.Ctx.Builder.CreateCall(reallocFunc.lltype, reallocFunc.llval, []llvm.Value{oldPtrI8, newSize}, "new_ptr_i8") + newPtr := g.Ctx.Builder.CreateBitCast(newPtrI8, llvm.PointerType(elemLLVMType, 0), "new_ptr") + + sliceGrown := g.Ctx.Builder.CreateInsertValue(*slice, newPtr, 0, "slice.new_ptr") + sliceGrown = g.Ctx.Builder.CreateInsertValue(sliceGrown, newCap, 2, "slice.new_cap") + + // Values from grown slice + ptrGrown := g.Ctx.Builder.CreateExtractValue(sliceGrown, 0, "ptr.grown") + lenGrown := g.Ctx.Builder.CreateExtractValue(sliceGrown, 1, "len.grown") + capGrown := g.Ctx.Builder.CreateExtractValue(sliceGrown, 2, "cap.grown") + + g.Ctx.Builder.CreateBr(mergeBlock) + + g.Ctx.Builder.SetInsertPointAtEnd(mergeBlock) + + ptrPhi := g.Ctx.Builder.CreatePHI(ptrType, "slice.ptr.phi") + lenPhi := g.Ctx.Builder.CreatePHI(lenType, "slice.len.phi") + capPhi := g.Ctx.Builder.CreatePHI(capType, "slice.cap.phi") + + ptrPhi.AddIncoming([]llvm.Value{ptrOld, ptrGrown}, []llvm.BasicBlock{currentBlock, growBlock}) + lenPhi.AddIncoming([]llvm.Value{lenOld, lenGrown}, []llvm.BasicBlock{currentBlock, growBlock}) + capPhi.AddIncoming([]llvm.Value{capOld, capGrown}, []llvm.BasicBlock{currentBlock, growBlock}) + + itemPtr := g.Ctx.Builder.CreateGEP(elemLLVMType, ptrPhi, []llvm.Value{lenPhi}, "item_ptr") + value := item.Value + + if item.Value.Type().TypeKind() == llvm.PointerTypeKind { + g.Debugf("Is pointer type") + } + + g.Ctx.Builder.CreateStore(*value, itemPtr) + + newLen := g.Ctx.Builder.CreateAdd(lenPhi, llvm.ConstInt(lenType, 1, false), "new_len") + + finalSlice := g.Ctx.Builder.CreateInsertValue(*slice, ptrPhi, 0, "slice.final.ptr") + finalSlice = g.Ctx.Builder.CreateInsertValue(finalSlice, newLen, 1, "slice.final.len") + finalSlice = g.Ctx.Builder.CreateInsertValue(finalSlice, capPhi, 2, "slice.final.cap") + + if sliceRes.SymbolTableEntry != nil && sliceRes.SymbolTableEntry.Address != nil { + g.Ctx.Builder.CreateStore(finalSlice, *sliceRes.SymbolTableEntry.Address) + } + + *slice = finalSlice + + return nil +} + +func (g *LLVMGenerator) VisitListExpression(node *ast.ListExpression) error { + if node.DataType == nil { + return fmt.Errorf("Datatype is mandatory for list expression") + } + + _, isSliceObj := node.DataType.(*ast.SliceType) + _, isSliceVal := node.DataType.(ast.SliceType) + + if !isSliceObj && !isSliceVal { + return g.Ctx.Dialect.Error("ListExpression.MakeRequiresSliceType", node.DataType.Value()) + } + + err := node.DataType.Accept(g) + if err != nil { + return err + } + + stype := g.getLastTypeVisitResult() + + err = node.Length.Accept(g) + if err != nil { + return err + } + + length := g.getLastResult() + + err = node.Capacity.Accept(g) + if err != nil { + return err + } + + capacity := g.getLastResult() + cap64 := g.Ctx.Builder.CreateZExt(*capacity.Value, g.Ctx.Context.Int64Type(), "cap64") + ptr := g.Ctx.Builder.CreateArrayMalloc(stype.SubType, cap64, "slice_data") + slice := llvm.ConstNull(stype.Type) + slice = g.Ctx.Builder.CreateInsertValue(slice, ptr, 0, "slice.ptr") + slice = g.Ctx.Builder.CreateInsertValue(slice, *length.Value, 1, "slice.len") + slice = g.Ctx.Builder.CreateInsertValue(slice, *capacity.Value, 2, "slice.cap") + + g.setLastResult(&CompilerResult{ + Value: &slice, + SymbolTableEntry: &SymbolTableEntry{ + Address: &slice, + }, + SwaType: node.DataType, + }) + + return nil +} + +func (g *LLVMGenerator) VisitListCountExpression(node *ast.ListCountExpression) error { + err := node.Expr.Accept(g) + if err != nil { + return err + } + + res := g.getLastResult() + + if res.SwaType.Value() != ast.DataTypeSlice { + return g.Ctx.Dialect.Error("ListCountExpression.RequiresSliceType", res.SwaType.Value()) + } + + lenVal := g.Ctx.Builder.CreateExtractValue(*res.Value, 1, "len") + + g.setLastResult(&CompilerResult{ + Value: &lenVal, + SwaType: &ast.NumberType{}, + }) + + return nil +} + func (g *LLVMGenerator) VisitBooleanExpression(node *ast.BooleanExpression) error { value := llvm.ConstInt(llvm.GlobalContext().Int1Type(), uint64(0), false) @@ -42,8 +257,6 @@ func (g *LLVMGenerator) VisitBooleanExpression(node *ast.BooleanExpression) erro return nil } -var _ ast.CodeGenerator = (*LLVMGenerator)(nil) - func NewLLVMGenerator(ctx *CompilerCtx) *LLVMGenerator { logger := NewLogger("LLVM") @@ -85,7 +298,6 @@ func (g *LLVMGenerator) VisitZeroExpression(node *ast.ZeroExpression) error { return nil } -// VisitAssignmentExpression implements [ast.CodeGenerator]. func (g *LLVMGenerator) VisitAssignmentExpression(node *ast.AssignmentExpression) error { old := g.logger.Step("AssignmentExpr") @@ -169,6 +381,12 @@ func (g *LLVMGenerator) VisitBlockStatement(node *ast.BlockStatement) error { } } + if g.Ctx.Debugging { + g.Debugf("Variables ---------------------------------------------\n") + g.Ctx.PrintVarNames() + g.Debugf("Variables ---------------------------------------------\n") + } + g.Ctx = oldCtx return nil @@ -364,12 +582,10 @@ func (g *LLVMGenerator) VisitStringExpression(node *ast.StringExpression) error } func (g *LLVMGenerator) VisitStructDeclaration(node *ast.StructDeclarationStatement) error { - old := g.logger.Step("StructDeclExpr") + old := g.logger.Step(fmt.Sprintf("StructDeclExpr %s", node.Name)) defer g.logger.Restore(old) - g.Debugf(node.Name) - if len(node.Properties) == 0 { key := "LLVMGenerator.VisitStructDeclaration.EmptyStruct" @@ -390,6 +606,8 @@ func (g *LLVMGenerator) VisitStructDeclaration(node *ast.StructDeclarationStatem } for i, propertyName := range node.Properties { + g.Debugf("Initialising property %s at index %d", propertyName, i) + propertyType := node.Types[i] err := propertyType.Accept(g) @@ -398,6 +616,9 @@ func (g *LLVMGenerator) VisitStructDeclaration(node *ast.StructDeclarationStatem } datatype := g.getLastTypeVisitResult() + + g.Debugf("datatype of property %s %s ", datatype.Type.TypeKind(), datatype.SubType.TypeKind()) + entry.PropertyTypes = append(entry.PropertyTypes, datatype.Type) if datatype.Type.TypeKind() == llvm.ArrayTypeKind && @@ -427,9 +648,16 @@ func (g *LLVMGenerator) VisitStructDeclaration(node *ast.StructDeclarationStatem return g.Ctx.Dialect.Error(key, propertyName) } - g.Debugf("processing property %s as nested struct", propertyName) + if datatype.Sentry != nil { + g.Debugf("processing property %s as nested struct", propertyName) + entry.Embeds[propertyName] = *datatype.Sentry + } - entry.Embeds[propertyName] = *datatype.Sentry + if datatype.Aentry != nil { + g.Debugf("processing property %s as slice", propertyName) + g.Debugf("slice %v", datatype.Aentry) + entry.ArrayEmbeds[propertyName] = *datatype.Aentry + } } } @@ -493,6 +721,7 @@ func (g *LLVMGenerator) VisitSymbolExpression(node *ast.SymbolExpression) error } if entry.Address == nil { + g.Debugf("Entry address is nil") g.setLastResult( &CompilerResult{ Value: &entry.Value, @@ -506,6 +735,7 @@ func (g *LLVMGenerator) VisitSymbolExpression(node *ast.SymbolExpression) error if entry.Address.IsAInstruction().IsNil() || entry.Address.InstructionOpcode() != llvm.Alloca { + g.Debugf("Entry is an alloca") g.setLastResult( &CompilerResult{ Value: entry.Address, diff --git a/lang/compiler/llvm_generator_array_access_expression.go b/lang/compiler/llvm_generator_array_access_expression.go index fd79f73c..712c7d7e 100644 --- a/lang/compiler/llvm_generator_array_access_expression.go +++ b/lang/compiler/llvm_generator_array_access_expression.go @@ -26,15 +26,23 @@ func (g *LLVMGenerator) VisitArrayAccessExpression(node *ast.ArrayAccessExpressi arrayAddr = *array.Address } - if entry.UnderlyingType.IsNil() { - key := "LLVMGenerator.VisitArrayAccessExpression.UnderlyingTypeNotSet" - - return g.Ctx.Dialect.Error(key) + var length llvm.Value + var dataPtr llvm.Value + + if entry.IsSlice { + // Load slice struct if it's an address + sliceVal := arrayAddr + if arrayAddr.Type().TypeKind() == llvm.PointerTypeKind { + sliceVal = g.Ctx.Builder.CreateLoad(entry.Type, arrayAddr, "slice") + } + dataPtr = g.Ctx.Builder.CreateExtractValue(sliceVal, 0, "ptr") + length = g.Ctx.Builder.CreateExtractValue(sliceVal, 1, "len") + } else { + dataPtr = arrayAddr + length = llvm.ConstInt(llvm.GlobalContext().Int32Type(), + uint64(entry.ElementsCount), false) } - length := llvm.ConstInt(llvm.GlobalContext().Int32Type(), - uint64(entry.ElementsCount), false) - icmp := g.Ctx.Builder.CreateICmp(llvm.IntULT, indices[0], length, "array-bounds-cmp") bodyBlock := g.Ctx.Builder.GetInsertBlock() @@ -43,12 +51,19 @@ func (g *LLVMGenerator) VisitArrayAccessExpression(node *ast.ArrayAccessExpressi mergeBlock := g.Ctx.Context.AddBasicBlock(parentFunc, "array-bounds-cmp-mergeBlock") thenBlock := g.Ctx.Context.AddBasicBlock(parentFunc, "array-bounds-cmp-thenBlock") g.Ctx.Builder.SetInsertPointAtEnd(thenBlock) - itemPtr := g.Ctx.Builder.CreateInBoundsGEP( - entry.UnderlyingType, - arrayAddr, - indices, - "", - ) + + var itemPtr llvm.Value + if entry.IsSlice { + itemPtr = g.Ctx.Builder.CreateGEP(entry.UnderlyingType, dataPtr, indices, "") + } else { + itemPtr = g.Ctx.Builder.CreateInBoundsGEP( + entry.UnderlyingType, + dataPtr, + indices, + "", + ) + } + g.Ctx.Builder.CreateBr(mergeBlock) elseblock := g.Ctx.Context.AddBasicBlock(parentFunc, "array-bounds-cmp-elseblock") @@ -68,7 +83,7 @@ func (g *LLVMGenerator) VisitArrayAccessExpression(node *ast.ArrayAccessExpressi llvm.ConstInt(llvm.GlobalContext().Int64Type(), uint64(0), true), }, "") - if g.currentFuncReturnType != nil { + if g.currentFuncReturnType != nil && g.currentFuncReturnType.Value() != ast.DataTypeVoid { err := g.currentFuncReturnType.Accept(g) if err != nil { return err @@ -77,8 +92,12 @@ func (g *LLVMGenerator) VisitArrayAccessExpression(node *ast.ArrayAccessExpressi typ := g.getLastTypeVisitResult() g.Ctx.Builder.CreateRet(llvm.ConstNull(typ.Type)) } else { - g.Ctx.Builder.CreateRet( - llvm.ConstInt(llvm.GlobalContext().Int32Type(), uint64(0), false)) + if g.currentFuncReturnType != nil && g.currentFuncReturnType.Value() == ast.DataTypeVoid { + g.Ctx.Builder.CreateRetVoid() + } else { + g.Ctx.Builder.CreateRet( + llvm.ConstInt(llvm.GlobalContext().Int32Type(), uint64(0), false)) + } } array.Address = &itemPtr @@ -174,11 +193,16 @@ func (g *LLVMGenerator) findArraySymbolTableEntry( arrtyp, ok := arrayEntry.DeclaredType.(ast.ArrayType) if !ok { - key := "ArrayAccessExpression.DeclaredTypeIsNotArray" + slicetyp, ok := arrayEntry.DeclaredType.(ast.SliceType) + if !ok { + key := "ArrayAccessExpression.DeclaredTypeIsNotArray" - return g.Ctx.Dialect.Error(key), nil, nil, nil + return g.Ctx.Dialect.Error(key), nil, nil, nil + } + expr.SwaType = slicetyp.Underlying + } else { + expr.SwaType = arrtyp.Underlying } - expr.SwaType = arrtyp.Underlying case *ast.MemberExpression: err := expr.Name.Accept(g) @@ -229,6 +253,9 @@ func (g *LLVMGenerator) findArraySymbolTableEntry( case ast.ArrayType: elementsCount = coltype.Size expr.SwaType = coltype.Underlying + case ast.SliceType: + elementsCount = -1 + expr.SwaType = coltype.Underlying default: key := "LLVMGenerator.VisitArrayAccessExpression.PropertyIsnotAnArray" err := g.Ctx.Dialect.Error(key, propSym.Value) diff --git a/lang/compiler/llvm_generator_context.go b/lang/compiler/llvm_generator_context.go index 39b43af5..e9489557 100644 --- a/lang/compiler/llvm_generator_context.go +++ b/lang/compiler/llvm_generator_context.go @@ -42,6 +42,7 @@ type ArraySymbolTableEntry struct { UnderlyingTypeDef *StructSymbolTableEntry Type llvm.Type ElementsCount int + IsSlice bool } type FuncDetails struct { @@ -250,7 +251,7 @@ func (ctx CompilerCtx) FindSymbol(name string) (error, *SymbolTableEntry) { } func (ctx CompilerCtx) PrintVarNames() { - for k := range ctx.symbolTable { - fmt.Println("Variable name: ", k) + for k, v := range ctx.symbolTable { + fmt.Printf("%10s %10s \n", k, v.DeclaredType.Value().String()) } } diff --git a/lang/compiler/llvm_generator_function_call.go b/lang/compiler/llvm_generator_function_call.go index 57f97620..9d161d8a 100644 --- a/lang/compiler/llvm_generator_function_call.go +++ b/lang/compiler/llvm_generator_function_call.go @@ -8,41 +8,42 @@ import ( ) func (g *LLVMGenerator) VisitFunctionCall(node *ast.FunctionCallExpression) error { - old := g.logger.Step("FunCallExpr") + old := g.logger.Step(fmt.Sprintf("FunCallExpr %s", node.Name)) defer g.logger.Restore(old) - g.Debugf("%s", node.Name) - - name, ok := node.Name.(*ast.SymbolExpression) + symbol, ok := node.Name.(*ast.SymbolExpression) if !ok { key := "LLVMGenerator.VisitFunctionCall.NameIsNotASymbol" return g.Ctx.Dialect.Error(key) } - err, funcType := g.Ctx.FindFuncSymbol(name.Value) + name := symbol.Value + + err, funcType := g.Ctx.FindFuncSymbol(name) if err != nil { return err } - funcVal := g.Ctx.Module.NamedFunction(name.Value) + funcVal := g.Ctx.Module.NamedFunction(name) if funcVal.IsNil() { key := "LLVMGenerator.VisitFunctionCall.DoesNotExist" - return g.Ctx.Dialect.Error(key, name.Value) + return g.Ctx.Dialect.Error(key, name) } if funcVal.ParamsCount() != len(node.Args) { key := "LLVMGenerator.VisitFunctionCall.ArgsAndParamsCountAreDifferent" - return g.Ctx.Dialect.Error(key, name.Value, funcVal.ParamsCount(), len(node.Args)) + return g.Ctx.Dialect.Error(key, name, funcVal.ParamsCount(), len(node.Args)) } node.SwaType = funcType.meta.ReturnType args := []llvm.Value{} for i, arg := range node.Args { + g.Debugf("Processing arg %d", i) err := arg.Accept(g) if err != nil { return err @@ -64,7 +65,7 @@ func (g *LLVMGenerator) VisitFunctionCall(node *ast.FunctionCallExpression) erro g.Debugf("Symbol Table entry: %+v", val.SymbolTableEntry) g.Debugf("array symbol Table entry: %+v", val.ArraySymbolTableEntry) - // TODO this should be moved to the type checking pass + // FIXME this should be moved to the type checking pass argType := val.Value.Type() if val.SymbolTableEntry != nil && val.SymbolTableEntry.Ref != nil { if val.StuctPropertyValueType != nil { diff --git a/lang/compiler/llvm_generator_function_definition.go b/lang/compiler/llvm_generator_function_definition.go index 525d5e0b..72f981b6 100644 --- a/lang/compiler/llvm_generator_function_definition.go +++ b/lang/compiler/llvm_generator_function_definition.go @@ -158,6 +158,15 @@ func (g *LLVMGenerator) extractType(ctx *CompilerCtx, t ast.Type) (error, extrac } return nil, etyp + case ast.SliceType: + etype := extractedType{ + typ: compiledType.Type, + aEntry: compiledType.Aentry, + sEntry: compiledType.Sentry, + } + + return nil, etype + case ast.PointerType: var sEntry *StructSymbolTableEntry @@ -203,7 +212,7 @@ func (g *LLVMGenerator) extractType(ctx *CompilerCtx, t ast.Type) (error, extrac default: key := "LLVMGenerator.VisitFunctionDefinition.UnsupportedArgumentType" - return g.Ctx.Dialect.Error(key, t.Value().String()), extractedType{} + return g.Ctx.Dialect.Error(key, t), extractedType{} } } diff --git a/lang/compiler/llvm_generator_member_expression.go b/lang/compiler/llvm_generator_member_expression.go index 38c823d0..b79f187f 100644 --- a/lang/compiler/llvm_generator_member_expression.go +++ b/lang/compiler/llvm_generator_member_expression.go @@ -7,7 +7,6 @@ import ( "tinygo.org/x/go-llvm" ) -// VisitMemberExpression implements [ast.CodeGenerator]. func (g *LLVMGenerator) VisitMemberExpression(node *ast.MemberExpression) error { old := g.logger.Step("MemberExpr") @@ -45,9 +44,12 @@ func (g *LLVMGenerator) VisitMemberExpression(node *ast.MemberExpression) error baseValue = *varDef.Address } + g.Debugf("Var def %+v", varDef.Ref.Metadata.Types) + addr := g.Ctx.Builder.CreateStructGEP(varDef.Ref.LLVMType, baseValue, propIndex, "") propType := varDef.Ref.PropertyTypes[propIndex] swatype := varDef.Ref.Metadata.Types[propIndex] + result := &CompilerResult{ Value: &addr, SymbolTableEntry: varDef, @@ -55,7 +57,10 @@ func (g *LLVMGenerator) VisitMemberExpression(node *ast.MemberExpression) error SwaType: swatype, } + g.Debugf("result %+v", result) + if propType.TypeKind() == llvm.StructTypeKind { + g.Debugf("Property is of StructTypeKind") prop, _ := varDef.Ref.Embeds[propName] result.SymbolTableEntry.Ref = &prop } diff --git a/lang/compiler/llvm_generator_print.go b/lang/compiler/llvm_generator_print.go index 30203c2a..18350d11 100644 --- a/lang/compiler/llvm_generator_print.go +++ b/lang/compiler/llvm_generator_print.go @@ -157,6 +157,7 @@ var printableValueExtractors = map[reflect.Type]PrintableValueExtractor{ reflect.TypeFor[*ast.ZeroExpression](): extractDirect, reflect.TypeFor[*ast.ErrorExpression](): extractDirect, reflect.TypeFor[*ast.BooleanExpression](): extractDirect, + reflect.TypeFor[*ast.ListCountExpression](): extractDirect, } func extractSymbol(g *LLVMGenerator, res *CompilerResult) llvm.Value { diff --git a/lang/compiler/llvm_generator_slice_intrinsics.go b/lang/compiler/llvm_generator_slice_intrinsics.go new file mode 100644 index 00000000..d884a3f6 --- /dev/null +++ b/lang/compiler/llvm_generator_slice_intrinsics.go @@ -0,0 +1,24 @@ +package compiler + +import ( + "tinygo.org/x/go-llvm" +) + +type ExternalFunc struct { + lltype llvm.Type + llval llvm.Value +} + +func (g *LLVMGenerator) getReallocFunc() ExternalFunc { + name := "realloc" + f := g.Ctx.Module.NamedFunction(name) + i64 := g.Ctx.Context.Int64Type() + ptrI8 := llvm.PointerType(g.Ctx.Context.Int8Type(), 0) + + ftype := llvm.FunctionType(ptrI8, []llvm.Type{ptrI8, i64}, false) + if f.IsNil() { + f = llvm.AddFunction(*g.Ctx.Module, name, ftype) + } + + return ExternalFunc{lltype: ftype, llval: f} +} diff --git a/lang/compiler/llvm_generator_struct_initialization.go b/lang/compiler/llvm_generator_struct_initialization.go index 1af4c146..f82646bd 100644 --- a/lang/compiler/llvm_generator_struct_initialization.go +++ b/lang/compiler/llvm_generator_struct_initialization.go @@ -110,6 +110,7 @@ type StructInitializationExpressionPropertyInjector func( var structInjectors = map[reflect.Type]StructInitializationExpressionPropertyInjector{ reflect.TypeFor[*ast.ZeroExpression](): injectDirectly, + reflect.TypeFor[*ast.ListExpression](): injectDirectly, reflect.TypeFor[*ast.BooleanExpression](): injectDirectly, reflect.TypeFor[*ast.NumberExpression](): injectDirectly, reflect.TypeFor[*ast.FloatExpression](): injectDirectly, @@ -123,6 +124,11 @@ var structInjectors = map[reflect.Type]StructInitializationExpressionPropertyInj reflect.TypeFor[*ast.StructInitializationExpression](): injectNestedStruct, } +func injectSlice(g *LLVMGenerator, res *CompilerResult, fieldAddr llvm.Value, targetType llvm.Type) error { + // FIXME + return nil +} + func injectNestedStruct(g *LLVMGenerator, res *CompilerResult, fieldAddr llvm.Value, targetType llvm.Type) error { load := g.Ctx.Builder.CreateLoad(res.Value.AllocatedType(), *res.Value, "nested-struct.load") g.Ctx.Builder.CreateStore(load, fieldAddr) @@ -134,6 +140,7 @@ func injectFunctionCall(g *LLVMGenerator, res *CompilerResult, fieldAddr llvm.Va g.Debugf("Injecting function call %+v ", res) if res.SwaType.Value() == ast.DataTypeTuple { + // FIXME Translate error message return fmt.Errorf("Cannot assign tuple to struct property") } diff --git a/lang/compiler/llvm_generator_types.go b/lang/compiler/llvm_generator_types.go index d3fada68..ab02c12a 100644 --- a/lang/compiler/llvm_generator_types.go +++ b/lang/compiler/llvm_generator_types.go @@ -160,3 +160,36 @@ func (g *LLVMGenerator) VisitBoolType(node *ast.BoolType) error { return nil } + +func (g *LLVMGenerator) VisitSliceType(node *ast.SliceType) error { + old := g.logger.Step("SliceType") + + defer g.logger.Restore(old) + + err := node.Underlying.Accept(g) + if err != nil { + return err + } + + under := g.getLastTypeVisitResult() + sliceType := g.Ctx.Context.StructType([]llvm.Type{ + llvm.PointerType(under.Type, 0), + g.Ctx.Context.Int32Type(), + g.Ctx.Context.Int32Type(), + }, false) + + g.setLastTypeVisitResult(&CompilerResultType{ + Type: sliceType, + SubType: under.Type, + Sentry: under.Sentry, + Aentry: &ArraySymbolTableEntry{ + UnderlyingType: under.Type, + Type: sliceType, + UnderlyingTypeDef: under.Sentry, + ElementsCount: -1, + IsSlice: true, + }, + }) + + return nil +} diff --git a/lang/compiler/llvm_generator_variable_declaration.go b/lang/compiler/llvm_generator_variable_declaration.go index 5508f0a5..14577f65 100644 --- a/lang/compiler/llvm_generator_variable_declaration.go +++ b/lang/compiler/llvm_generator_variable_declaration.go @@ -108,6 +108,7 @@ var nodeVariableDeclarationStyles = map[reflect.Type]InitializationStyle{ reflect.TypeFor[*ast.BinaryExpression](): StyleDefault, reflect.TypeFor[*ast.StructInitializationExpression](): StyleDirect, reflect.TypeFor[*ast.ArrayInitializationExpression](): StyleDirect, + reflect.TypeFor[*ast.ListExpression](): StyleDefault, reflect.TypeFor[*ast.PrefixExpression](): StyleDefault, reflect.TypeFor[*ast.ZeroExpression](): StyleDefault, reflect.TypeFor[*ast.ErrorExpression](): StyleDefault, @@ -269,5 +270,32 @@ func (g *LLVMGenerator) finalizeSymbol( return g.Ctx.AddArraySymbol(node.Name, res.ArraySymbolTableEntry) } + var sliceTyp ast.SliceType + var isSlice bool + + if st, ok := node.ExplicitType.(ast.SliceType); ok { + sliceTyp = st + isSlice = true + } else if st, ok := node.ExplicitType.(*ast.SliceType); ok { + sliceTyp = *st + isSlice = true + } + + if isSlice { + g.Debugf("Is slice") + err := sliceTyp.Accept(g) + if err != nil { + return err + } + typeres := g.getLastTypeVisitResult() + + return g.Ctx.AddArraySymbol(node.Name, &ArraySymbolTableEntry{ + UnderlyingType: typeres.SubType, + Type: typeres.Type, + ElementsCount: -1, + IsSlice: true, + }) + } + return nil } diff --git a/lang/compiler/llvm_generator_zero_values.go b/lang/compiler/llvm_generator_zero_values.go index 5968b131..372eef92 100644 --- a/lang/compiler/llvm_generator_zero_values.go +++ b/lang/compiler/llvm_generator_zero_values.go @@ -1,6 +1,7 @@ package compiler import ( + "fmt" "reflect" "swahili/lang/ast" @@ -11,18 +12,11 @@ func (g *LLVMGenerator) ZeroOfArrayType(node *ast.ArrayType) error { g.Debugf("ZeroOfArrayType") defer g.Debugf("Finished ZeroOfArrayType") - // if node.Size > 1000 { - // key := "LLVMGenerator.ZeroOfArrayType.TooBigForZeroInitializer" - // return g.Ctx.Dialect.Error(key, node.Size, 1000) - // } - err := node.Underlying.AcceptZero(g) if err != nil { return err } - lastres := g.getLastResult() - err = g.VisitArrayType(node) if err != nil { return err @@ -31,15 +25,6 @@ func (g *LLVMGenerator) ZeroOfArrayType(node *ast.ArrayType) error { llvmtyp := g.getLastTypeVisitResult() arrayPointer := g.Ctx.Builder.CreateAlloca(llvmtyp.Type, "array_alloc") - for i := range node.Size { - itemGep := g.Ctx.Builder.CreateGEP(llvmtyp.Type, arrayPointer, []llvm.Value{ - llvm.ConstInt(llvm.GlobalContext().Int32Type(), 0, false), - llvm.ConstInt(llvm.GlobalContext().Int32Type(), uint64(i), false), - }, "") - - g.Ctx.Builder.CreateStore(*lastres.Value, itemGep) - } - load := g.Ctx.Builder.CreateLoad(llvmtyp.Type, arrayPointer, "") res := &CompilerResult{ @@ -220,5 +205,10 @@ func (g *LLVMGenerator) ZeroOfBoolType(node *ast.BoolType) error { } func (g *LLVMGenerator) ZeroOfVoidType(node *ast.VoidType) error { - panic("ZeroOfVoidType unimplemented") + return fmt.Errorf("ZeroOfVoidType unimplemented") +} + +func (g *LLVMGenerator) ZeroOfSliceType(node *ast.SliceType) error { + // FIXME error message should be translated + return fmt.Errorf("List should be initialized. Use make([]datatype)") } diff --git a/lang/compiler/llvm_type_checker.go b/lang/compiler/llvm_type_checker.go index d440d8f6..fcdc52b1 100644 --- a/lang/compiler/llvm_type_checker.go +++ b/lang/compiler/llvm_type_checker.go @@ -3,6 +3,7 @@ package compiler import ( "fmt" "swahili/lang/ast" + "swahili/lang/lexer" ) type LLVMTypeChecker struct { @@ -10,21 +11,43 @@ type LLVMTypeChecker struct { returnStatementsCount int } +var _ ast.CodeGenerator = (*LLVMTypeChecker)(nil) + +func (l *LLVMTypeChecker) VisitAppendExpression(node *ast.AppendExpression) error { + return nil +} + +func (l *LLVMTypeChecker) VisitListExpression(node *ast.ListExpression) error { + return nil +} + +func (l *LLVMTypeChecker) VisitListCountExpression(node *ast.ListCountExpression) error { + return nil +} + func (l *LLVMTypeChecker) VisitByteType(node *ast.ByteType) error { - panic("unimplemented") + return nil } func (l *LLVMTypeChecker) ZeroOfByteType(node *ast.ByteType) error { - panic("unimplemented") + return nil } -var _ ast.CodeGenerator = (*LLVMTypeChecker)(nil) - func NewLLVMTypeChecker(ctx *CompilerCtx) *LLVMTypeChecker { return &LLVMTypeChecker{ctx: ctx} } func (l *LLVMTypeChecker) VisitAssignmentExpression(node *ast.AssignmentExpression) error { + err := node.Assignee.Accept(l) + if err != nil { + return err + } + + err = node.Value.Accept(l) + if err != nil { + return err + } + asstype := node.Assignee.VisitedSwaType() valType := node.Value.VisitedSwaType() @@ -49,6 +72,18 @@ func (l *LLVMTypeChecker) VisitAssignmentExpression(node *ast.AssignmentExpressi } func (l *LLVMTypeChecker) VisitBlockStatement(node *ast.BlockStatement) error { + oldCtx := l.ctx + l.ctx = NewCompilerContext( + l.ctx.Context, + l.ctx.Builder, + l.ctx.Module, + l.ctx.Dialect, + l.ctx, + ) + defer func() { + l.ctx = oldCtx + }() + for _, v := range node.Body { err := v.Accept(l) if err != nil { @@ -72,17 +107,27 @@ func (l *LLVMTypeChecker) VisitExpressionStatement(node *ast.ExpressionStatement } func (l *LLVMTypeChecker) VisitVarDeclaration(node *ast.VarDeclarationStatement) error { + // Register symbol for subsequent expressions (always do this for declarations) + err := l.ctx.AddSymbol(node.Name, &SymbolTableEntry{ + DeclaredType: node.ExplicitType, + }) + if err != nil { + return err + } + if node.Value == nil { return nil } - err := node.Value.Accept(l) - if err != nil { - return err + // Make sure the value is visited to populate its type + if node.Value.VisitedSwaType() == nil { + err := node.Value.Accept(l) + if err != nil { + return err + } } - // TODO visited swa type should not be nil - // All visited expressions should have the field set + // Safety check again - if still nil after visiting, we can't check types if node.Value.VisitedSwaType() == nil { return nil } @@ -131,7 +176,11 @@ func (l *LLVMTypeChecker) VisitArrayAccessExpression(node *ast.ArrayAccessExpres return nil } func (l *LLVMTypeChecker) VisitArrayInitializationExpression(node *ast.ArrayInitializationExpression) error { - typ, _ := node.Underlying.(ast.ArrayType) + typ, ok := node.Underlying.(ast.ArrayType) + + if !ok { + return nil + } for _, v := range node.Contents { if v.VisitedSwaType() != typ.Underlying { @@ -146,17 +195,78 @@ func (l *LLVMTypeChecker) VisitArrayInitializationExpression(node *ast.ArrayInit func (l *LLVMTypeChecker) VisitArrayOfStructsAccessExpression(node *ast.ArrayOfStructsAccessExpression) error { return nil } -func (l *LLVMTypeChecker) VisitArrayType(node *ast.ArrayType) error { return nil } -func (l *LLVMTypeChecker) VisitCallExpression(node *ast.CallExpression) error { return nil } -func (l *LLVMTypeChecker) VisitErrorExpression(node *ast.ErrorExpression) error { return nil } -func (l *LLVMTypeChecker) VisitFloatExpression(node *ast.FloatExpression) error { return nil } -func (l *LLVMTypeChecker) VisitFloatType(node *ast.FloatType) error { return nil } -func (l *LLVMTypeChecker) VisitFunctionCall(node *ast.FunctionCallExpression) error { return nil } +func (l *LLVMTypeChecker) VisitArrayType(node *ast.ArrayType) error { return nil } +func (l *LLVMTypeChecker) VisitSliceType(node *ast.SliceType) error { return nil } +func (l *LLVMTypeChecker) VisitCallExpression(node *ast.CallExpression) error { return nil } +func (l *LLVMTypeChecker) VisitErrorExpression(node *ast.ErrorExpression) error { return nil } +func (l *LLVMTypeChecker) VisitFloatExpression(node *ast.FloatExpression) error { return nil } +func (l *LLVMTypeChecker) VisitFloatType(node *ast.FloatType) error { return nil } +func (l *LLVMTypeChecker) VisitFunctionCall(node *ast.FunctionCallExpression) error { + symbol, ok := node.Name.(*ast.SymbolExpression) + if !ok { + return nil + } + + if len(symbol.Tokens) > 0 { + switch symbol.Tokens[0].Kind { + case lexer.Append: + // append(slice, item) -> returns slice type + err := node.Args[0].Accept(l) + if err != nil { + return err + } + node.SwaType = node.Args[0].VisitedSwaType() + return nil + case lexer.Len, lexer.Cap: + node.SwaType = &ast.NumberType{} + return nil + } + } + + name := symbol.Value + err, funcType := l.ctx.FindFuncSymbol(name) + if err != nil { + return err + } + + node.SwaType = funcType.meta.ReturnType + + for _, arg := range node.Args { + err := arg.Accept(l) + if err != nil { + return err + } + } + + return nil +} func (l *LLVMTypeChecker) VisitFunctionDefinition(node *ast.FuncDeclStatement) error { if node.Declaration { return nil } + oldCtx := l.ctx + l.ctx = NewCompilerContext( + l.ctx.Context, + l.ctx.Builder, + l.ctx.Module, + l.ctx.Dialect, + l.ctx, + ) + defer func() { + l.ctx = oldCtx + }() + + // Register parameters + for _, p := range node.Args { + err := l.ctx.AddSymbol(p.Name, &SymbolTableEntry{ + DeclaredType: p.ArgType, + }) + if err != nil { + return err + } + } + l.returnStatementsCount = 0 err := node.Body.Accept(l) @@ -164,8 +274,8 @@ func (l *LLVMTypeChecker) VisitFunctionDefinition(node *ast.FuncDeclStatement) e return err } - if l.returnStatementsCount == 0 { - return fmt.Errorf("function has no return statement") + if l.returnStatementsCount == 0 && node.ReturnType.Value() != ast.DataTypeVoid { + return fmt.Errorf("function %s has no return statement", node.Name) } l.returnStatementsCount = 0 @@ -184,11 +294,19 @@ func (l *LLVMTypeChecker) VisitReturnStatement(node *ast.ReturnStatement) error return nil } -func (l *LLVMTypeChecker) VisitStringExpression(node *ast.StringExpression) error { return nil } -func (l *LLVMTypeChecker) VisitStringType(node *ast.StringType) error { return nil } -func (l *LLVMTypeChecker) VisitErrorType(node *ast.ErrorType) error { return nil } -func (l *LLVMTypeChecker) VisitTupleType(node *ast.TupleType) error { return nil } -func (l *LLVMTypeChecker) VisitSymbolExpression(node *ast.SymbolExpression) error { return nil } +func (l *LLVMTypeChecker) VisitStringExpression(node *ast.StringExpression) error { return nil } +func (l *LLVMTypeChecker) VisitStringType(node *ast.StringType) error { return nil } +func (l *LLVMTypeChecker) VisitErrorType(node *ast.ErrorType) error { return nil } +func (l *LLVMTypeChecker) VisitTupleType(node *ast.TupleType) error { return nil } +func (l *LLVMTypeChecker) VisitSymbolExpression(node *ast.SymbolExpression) error { + err, entry := l.ctx.FindSymbol(node.Value) + if err != nil { + return err + } + + node.SwaType = entry.DeclaredType + return nil +} func (l *LLVMTypeChecker) VisitSymbolType(node *ast.SymbolType) error { return nil } func (l *LLVMTypeChecker) VisitTupleExpression(node *ast.TupleExpression) error { return nil } func (l *LLVMTypeChecker) VisitVoidType(node *ast.VoidType) error { return nil } @@ -203,6 +321,7 @@ func (l *LLVMTypeChecker) ZeroOfStringType(node *ast.StringType) error func (l *LLVMTypeChecker) ZeroOfSymbolType(node *ast.SymbolType) error { return nil } func (l *LLVMTypeChecker) ZeroOfVoidType(node *ast.VoidType) error { return nil } func (l *LLVMTypeChecker) ZeroOfTupleType(node *ast.TupleType) error { return nil } +func (l *LLVMTypeChecker) ZeroOfSliceType(node *ast.SliceType) error { return nil } func (l *LLVMTypeChecker) VisitZeroExpression(node *ast.ZeroExpression) error { return nil } func (l *LLVMTypeChecker) VisitBinaryExpression(node *ast.BinaryExpression) error { return nil } func (l *LLVMTypeChecker) ZeroOfBoolType(node *ast.BoolType) error { return nil } @@ -246,6 +365,22 @@ func (l *LLVMTypeChecker) areTypesEquivalent(a, b ast.Type) bool { return nameA == nameB } + if a.Value() == ast.DataTypeSlice { + var aslice, bslice ast.SliceType + if st, ok := a.(ast.SliceType); ok { + aslice = st + } else if st, ok := a.(*ast.SliceType); ok { + aslice = *st + } + + if st, ok := b.(ast.SliceType); ok { + bslice = st + } else if st, ok := b.(*ast.SliceType); ok { + bslice = *st + } + return l.areTypesEquivalent(aslice.Underlying, bslice.Underlying) + } + // For other types like ErrorType, NumberType etc, Value() equality is enough return true } diff --git a/lang/lexer/dialect_english.go b/lang/lexer/dialect_english.go index a9da58bc..6a72a5ff 100644 --- a/lang/lexer/dialect_english.go +++ b/lang/lexer/dialect_english.go @@ -43,6 +43,10 @@ func (m English) Reserved() map[string]TokenKind { "error": TypeError, "variadic": Variadic, "zero": Zero, + "make": Make, + "count": Len, + "cap": Cap, + "append": Append, } } @@ -134,5 +138,9 @@ func (m English) translations() map[string]string { "LLVMCompiler.MissingProgramEntrypoint": "Your program is missing a main function", "LLVMCompiler.TooManyProgramEntrypoints": "Your program must have exactly one main function, count (%d)", + + "AppendExpression.AppendableIsNotASlice": "append expects a slice, got %v", + "ListExpression.MakeRequiresSliceType": "make() function requires a slice allocation map, got %v", + "ListCountExpression.RequiresSliceType": "count() function requires a slice, got %v", } } diff --git a/lang/lexer/dialect_english_test.go b/lang/lexer/dialect_english_test.go index 57859f1b..83b99242 100644 --- a/lang/lexer/dialect_english_test.go +++ b/lang/lexer/dialect_english_test.go @@ -10,27 +10,32 @@ func TestReservedEnglish(t *testing.T) { expected := map[string]TokenKind{ "const": Const, "dialect": DialectDeclaration, - "else": KeywordElse, - "error": TypeError, - "float": TypeFloat, "func": Function, + "else": KeywordElse, "if": KeywordIf, - "byte": TypeByte, - "int": TypeInt, - "int64": TypeInt64, + "while": KeywordWhile, "let": Let, + "start": Main, "print": Print, "return": Return, - "string": TypeString, - "start": Main, "struct": Struct, - "variadic": Variadic, - "while": KeywordWhile, - "zero": Zero, "true": True, "false": False, "bool": TypeBool, + "byte": TypeByte, + "float": TypeFloat, + "int": TypeInt, + "int64": TypeInt64, + "string": TypeString, + "error": TypeError, + "variadic": Variadic, + "zero": Zero, + "make": Make, + "count": Len, + "cap": Cap, + "append": Append, } + english := English{} assert.Equal(t, expected, english.Reserved()) diff --git a/lang/lexer/dialect_french.go b/lang/lexer/dialect_french.go index e2f293b1..db6c7865 100644 --- a/lang/lexer/dialect_french.go +++ b/lang/lexer/dialect_french.go @@ -42,6 +42,8 @@ func (m French) Reserved() map[string]TokenKind { "erreur": TypeError, "variadique": Variadic, "zero": Zero, + "creer": Make, + "taille": Len, } } @@ -132,5 +134,9 @@ func (m French) translations() map[string]string { "LLVMCompiler.TooManyProgramEntrypoints": "Vous devez definir un seul programme principal, nombre (%d)", "VisitReturnStatement.UnsupportedExpression": "VisitReturnStatement: Unsupported expression type %T", + + "AppendExpression.AppendableIsNotASlice": "ajouter s'attend a tableau dynamique, recu %T", + "ListExpression.MakeRequiresSliceType": "fonction creer() necessite un tableau dynamique, recu %v", + "ListCountExpression.RequiresSliceType": "fonction taille() necessite un tableau dynamique, recu %v", } } diff --git a/lang/lexer/dialect_french_test.go b/lang/lexer/dialect_french_test.go index 32dcd592..ee20ee71 100644 --- a/lang/lexer/dialect_french_test.go +++ b/lang/lexer/dialect_french_test.go @@ -29,6 +29,8 @@ func TestReservedFrench(t *testing.T) { "vrai": True, "faux": False, "booleen": TypeBool, + "creer": Make, + "taille": Len, } French := French{} diff --git a/lang/lexer/dialect_malinke.go b/lang/lexer/dialect_malinke.go index c6ff5d6c..d213666d 100644 --- a/lang/lexer/dialect_malinke.go +++ b/lang/lexer/dialect_malinke.go @@ -3,6 +3,8 @@ package lexer import ( "fmt" "regexp" + + "swahili/lang/errmsg" ) type Malinke struct{} @@ -18,7 +20,13 @@ func (Malinke) Name() string { } func (m Malinke) Error(key string, args ...any) error { - return fmt.Errorf("Not implemented") + formatted, ok := m.translations()[key] + + if !ok { + return fmt.Errorf("key %s does not exist in malinke dialect translations", key) + } + + return errmsg.NewAstError(formatted, args...) } func (m Malinke) Reserved() map[string]TokenKind { @@ -44,5 +52,20 @@ func (m Malinke) Reserved() map[string]TokenKind { "tinye": True, "wouya": False, "tinyejate": TypeBool, + "make": Make, + "count": Len, + "cap": Cap, + "append": Append, + } +} + +func (m Malinke) translations() map[string]string { + return map[string]string{ + "AppendExpression.AppendableIsNotASlice": "append expects a slice, got %T", + "ListExpression.MakeRequiresSliceType": "make() function requires a slice allocation map, got %v", + "ListCountExpression.RequiresSliceType": "count() function requires a slice, got %v", + + "LLVMCompiler.MissingProgramEntrypoint": "Your program is missing a main function", + "LLVMCompiler.TooManyProgramEntrypoints": "Your program must have exactly one main function, count (%d)", } } diff --git a/lang/lexer/dialect_malinke_test.go b/lang/lexer/dialect_malinke_test.go index 8f586a27..986d33bf 100644 --- a/lang/lexer/dialect_malinke_test.go +++ b/lang/lexer/dialect_malinke_test.go @@ -29,6 +29,10 @@ func TestReservedMalinke(t *testing.T) { "tinye": True, "wouya": False, "tinyejate": TypeBool, + "make": Make, + "count": Len, + "cap": Cap, + "append": Append, } malinke := Malinke{} diff --git a/lang/lexer/dialect_soussou.go b/lang/lexer/dialect_soussou.go index 85b9583b..9eeb953c 100644 --- a/lang/lexer/dialect_soussou.go +++ b/lang/lexer/dialect_soussou.go @@ -42,6 +42,8 @@ func (m Soussou) Reserved() map[string]TokenKind { "true": True, "false": False, "bool": TypeBool, + "make": Make, + "count": Len, } } @@ -131,5 +133,9 @@ func (m Soussou) translations() map[string]string { "LLVMCompiler.MissingProgramEntrypoint": "Your program is missing a main function", "LLVMCompiler.TooManyProgramEntrypoints": "Your program must have exactly one main function, count (%d)", + + "AppendExpression.AppendableIsNotASlice": "append mu lanné slice ra, %T nan masen", + "ListExpression.MakeRequiresSliceType": "make() mu lanné slice ra, %v nan masen", + "ListCountExpression.RequiresSliceType": "count() mu lanné slice ra, %v nan masen", } } diff --git a/lang/lexer/dialect_wolof.go b/lang/lexer/dialect_wolof.go index cee78fc8..6ba9bfc9 100644 --- a/lang/lexer/dialect_wolof.go +++ b/lang/lexer/dialect_wolof.go @@ -30,35 +30,33 @@ func (m Wolof) Error(key string, args ...any) error { } func (m Wolof) Reserved() map[string]TokenKind { + // TODO: find translations return map[string]TokenKind{ - "constante": Const, - "laak": DialectDeclaration, - // TODO: find translation for function - "fonction": Function, - "sude": KeywordIf, - "wala": KeywordElse, - // TODO: find translation for while - "while": KeywordWhile, - "dencukaay": Let, - "tambali": Main, - "wanel": Print, - "deloko": Return, - // TODO: find translation for struct - "structure": Struct, - // TODO: find translation for float - "decimal": TypeFloat, - "lëmm": TypeInt, - "lëmm64": TypeInt64, - // TODO: find better translation for string - "ay_araf": TypeString, - "njumte": TypeError, - // TODO: find translation for variadic + "constante": Const, + "laak": DialectDeclaration, + "fonction": Function, + "sude": KeywordIf, + "wala": KeywordElse, + "while": KeywordWhile, + "dencukaay": Let, + "tambali": Main, + "wanel": Print, + "deloko": Return, + "structure": Struct, + "decimal": TypeFloat, + "lëmm": TypeInt, + "lëmm64": TypeInt64, + "ay_araf": TypeString, + "njumte": TypeError, "variadique": Variadic, - // TODO: find translation for zero - "zero": Zero, - "true": True, - "false": False, - "bool": TypeBool, + "zero": Zero, + "false": False, + "true": True, + "bool": TypeBool, + "make": Make, + "count": Len, + "cap": Cap, + "append": Append, } } @@ -139,5 +137,9 @@ func (m Wolof) translations() map[string]string { "LLVMCompiler.MissingProgramEntrypoint": "Your program is missing a main function", "LLVMCompiler.TooManyProgramEntrypoints": "Your program must have exactly one main function, count (%d)", + + "AppendExpression.AppendableIsNotASlice": "append expects a slice, got %T", + "ListExpression.MakeRequiresSliceType": "make() function requires a slice allocation map, got %v", + "ListCountExpression.RequiresSliceType": "count() function requires a slice, got %v", } } diff --git a/lang/lexer/fast.go b/lang/lexer/fast.go index 7748fe43..c5adae2a 100644 --- a/lang/lexer/fast.go +++ b/lang/lexer/fast.go @@ -325,7 +325,7 @@ func (lex *FastLexer) lexOperator() { func (lex *FastLexer) isTwoCharOperator(op string) bool { switch op { - case "!=", "==", "<=", ">=", "+=", "||", "&&": + case "!=", "==", "<<", "<=", ">=", "+=", "||", "&&": return true default: return false @@ -360,6 +360,8 @@ func (lex *FastLexer) getOperatorKind(op string) TokenKind { return LessThan case ">": return GreaterThan + case "<<": + return Append case "<=": return LessThanEquals case ">=": diff --git a/lang/lexer/kind.go b/lang/lexer/kind.go index 0e7c844e..7d0bccec 100644 --- a/lang/lexer/kind.go +++ b/lang/lexer/kind.go @@ -66,6 +66,10 @@ var tks = map[TokenKind]string{ Whitespace: "WHITESPACE", Comment: "COMMENT", Newline: "NEWLINE", + Make: "Alloc", + Len: "COUNT", + Cap: "CAP", + Append: "APPEND", } // String s a string representation of the TokenKind. @@ -192,4 +196,8 @@ const ( Whitespace Comment Newline + Make + Len + Cap + Append ) diff --git a/lang/parser/binary.go b/lang/parser/binary.go index b7b8bd89..d2b80db2 100644 --- a/lang/parser/binary.go +++ b/lang/parser/binary.go @@ -39,6 +39,7 @@ func ParseSymbolAddressExpression(p *Parser) (ast.Expression, error) { func ParseBinaryExpression(p *Parser, left ast.Expression, bp BindingPower) (ast.Expression, error) { expr := ast.BinaryExpression{} p.currentExpression = &expr + expr.Left = left expr.Tokens = append(expr.Tokens, left.TokenStream()...) operatorToken := p.advance() @@ -53,10 +54,23 @@ func ParseBinaryExpression(p *Parser, left ast.Expression, bp BindingPower) (ast expr.Right = right expr.Tokens = append(expr.Tokens, right.TokenStream()...) - return &expr, nil + switch operatorToken.Kind { + case lexer.Append: + e := ast.AppendExpression{ + Tokens: expr.TokenStream(), + Appendable: expr.Left, + Value: expr.Right, + } + + return &e, nil + default: + return &expr, nil + } + } func ParseZeroExpression(p *Parser) (ast.Expression, error) { + expr := ast.ZeroExpression{} p.currentExpression = &expr diff --git a/lang/parser/intrinsics.go b/lang/parser/intrinsics.go new file mode 100644 index 00000000..ff2bb85e --- /dev/null +++ b/lang/parser/intrinsics.go @@ -0,0 +1,43 @@ +package parser + +import ( + "swahili/lang/ast" + "swahili/lang/lexer" +) + +func ParseMakeExpression(p *Parser) (ast.Expression, error) { + expr := ast.ListExpression{} + p.currentExpression = &expr + expr.Tokens = append(expr.Tokens, p.advance()) + expr.Tokens = append(expr.Tokens, p.expect(lexer.OpenParen)) + + dtype, tokens := parseType(p, DefaultBindingPower) + + expr.Tokens = append(expr.Tokens, tokens...) + expr.DataType = dtype + + expr.Tokens = append(expr.Tokens, p.expect(lexer.CloseParen)) + expr.Length = &ast.NumberExpression{Value: 0} + expr.Capacity = &ast.NumberExpression{Value: 5} + + return &expr, nil +} + +func ParseLenIntrinsic(p *Parser) (ast.Expression, error) { + expr := ast.ListCountExpression{} + p.currentExpression = &expr + + expr.Tokens = append(expr.Tokens, p.advance()) + expr.Tokens = append(expr.Tokens, p.expect(lexer.OpenParen)) + + arg, err := parseExpression(p, DefaultBindingPower) + if err != nil { + return nil, err + } + + expr.Expr = arg + expr.Tokens = append(expr.Tokens, arg.TokenStream()...) + expr.Tokens = append(expr.Tokens, p.expect(lexer.CloseParen)) + + return &expr, nil +} diff --git a/lang/parser/lookups.go b/lang/parser/lookups.go index 147ee8bc..2033bc5e 100644 --- a/lang/parser/lookups.go +++ b/lang/parser/lookups.go @@ -67,6 +67,7 @@ func createTokenLookups() { led(lexer.Star, Multiplicative, ParseBinaryExpression) led(lexer.Divide, Multiplicative, ParseBinaryExpression) led(lexer.Modulo, Multiplicative, ParseBinaryExpression) + led(lexer.Append, Additive, ParseBinaryExpression) nud(lexer.True, ParsePrimaryExpression) nud(lexer.False, ParsePrimaryExpression) @@ -89,6 +90,10 @@ func createTokenLookups() { nud(lexer.OpenBracket, ParseArrayInitialization) nud(lexer.TypeError, ParseErrorExpression) nud(lexer.Zero, ParseZeroExpression) + nud(lexer.Make, ParseMakeExpression) + nud(lexer.Len, ParseLenIntrinsic) + // nud(lexer.Cap, ParseCapIntrinsic) + // nud(lexer.Append, ParseAppendIntrinsic) statement(lexer.Print, ParsePrintStatement) statement(lexer.KeywordIf, ParseConditionalExpression) diff --git a/lang/parser/types.go b/lang/parser/types.go index 90e5673b..65cb0c04 100644 --- a/lang/parser/types.go +++ b/lang/parser/types.go @@ -106,9 +106,19 @@ func parseSymbolType(p *Parser) (ast.Type, []lexer.Token) { } func parseArrayType(p *Parser) (ast.Type, []lexer.Token) { + tokens := []lexer.Token{p.advance()} + + if p.currentToken().Kind == lexer.CloseBracket { + tokens = append(tokens, p.advance()) + underlying, toks := parseType(p, DefaultBindingPower) + tokens = append(tokens, toks...) + + return ast.SliceType{ + Underlying: underlying, + }, tokens + } + typ := ast.ArrayType{} - tokens := []lexer.Token{} - tokens = append(tokens, p.advance()) if p.currentToken().Kind == lexer.Number { tok := p.expect(lexer.Number) @@ -133,7 +143,8 @@ func parseArrayType(p *Parser) (ast.Type, []lexer.Token) { tokens = append(tokens, p.expect(lexer.CloseBracket)) - underlying, tokens := parseType(p, DefaultBindingPower) + underlying, toks := parseType(p, DefaultBindingPower) + tokens = append(tokens, toks...) typ.Underlying = underlying return typ, tokens diff --git a/lang/start.ll b/lang/start.ll new file mode 100644 index 00000000..8f23d6c8 --- /dev/null +++ b/lang/start.ll @@ -0,0 +1,50 @@ +; ModuleID = 'test.swa' +source_filename = "test.swa" + +%Outer = type { { ptr, i32, i32 } } +%Inner = type { { ptr, i32, i32 } } + +declare i32 @printf(ptr, ...) + +declare i32 @strcmp(ptr, ptr) + +declare void @exit(i64) + +declare i32 @open(ptr, i32, ...) + +declare i64 @read(i32, ptr, i64) + +declare i32 @close(i32) + +declare i64 @write(i32, ptr, i64) + +declare i64 @pread(i32, ptr, i32, i64) + +declare i64 @pwrite(i32, ptr, i32, i64) + +declare double @sqrt(double) + +declare ptr @realloc(ptr, i64) + +declare void @free(ptr) + +define i32 @main() { +entry: + %Outer.instance = alloca %Outer, align 8 + %slice_data = tail call ptr @malloc(i32 mul (i32 ptrtoint (ptr getelementptr (%Inner, ptr null, i32 1) to i32), i32 5)) + %slice.ptr = insertvalue { ptr, i32, i32 } zeroinitializer, ptr %slice_data, 0 + %slice.len = insertvalue { ptr, i32, i32 } %slice.ptr, i32 0, 1 + %slice.cap = insertvalue { ptr, i32, i32 } %slice.len, i32 5, 2 + %Outer.inners = getelementptr inbounds %Outer, ptr %Outer.instance, i32 0, i32 0 + store { ptr, i32, i32 } %slice.cap, ptr %Outer.inners, align 8 + %Inner.instance = alloca %Inner, align 8 + %slice_data1 = tail call ptr @malloc(i32 mul (i32 ptrtoint (ptr getelementptr (i32, ptr null, i32 1) to i32), i32 5)) + %slice.ptr2 = insertvalue { ptr, i32, i32 } zeroinitializer, ptr %slice_data1, 0 + %slice.len3 = insertvalue { ptr, i32, i32 } %slice.ptr2, i32 0, 1 + %slice.cap4 = insertvalue { ptr, i32, i32 } %slice.len3, i32 5, 2 + %Inner.vals = getelementptr inbounds %Inner, ptr %Inner.instance, i32 0, i32 0 + store { ptr, i32, i32 } %slice.cap4, ptr %Inner.vals, align 8 + ret i32 0 +} + +declare noalias ptr @malloc(i32) diff --git a/lang/std/std.english.swa b/lang/std/std.english.swa index 478cfe94..4e23e3a8 100644 --- a/lang/std/std.english.swa +++ b/lang/std/std.english.swa @@ -1,12 +1,12 @@ dialect:english; -// LIBC FUNCTIONS +// LIBC FUNCdatatypeIONS // ############################ // file functions // ############################ // -// let SEEK_SET :int = 0; +// let SEEK_SEdatatype :int = 0; // let SEEK_CUR :int = 1; // let SEEK_END :int = 2; // @@ -18,11 +18,12 @@ func read(fd: int, buf: string, buf_size: int64) int64; func close(fd: int) int; -func write(fd: int, buffer: string, count: int64) int64; +func write(fd: int, buffer: string, size: int64) int64; -func pread(fd: int, buf: string, count: int, offset: int64) int64; +func pread(fd: int, buf: string, size: int, offset: int64) int64; + +func pwrite(fd: int, buf: string, size: int, offset: int64) int64; -func pwrite(fd: int, buf: string, count: int, offset: int64) int64; // ############################ // end file functions @@ -31,7 +32,10 @@ func pwrite(fd: int, buf: string, count: int, offset: int64) int64; func sqrt(a: float) float; -// END LIBC FUNCTIONS +func realloc(ptr: *int, size: int64) *int; +func free(ptr: *int); + +// END LIBC FUNCdatatypeIONS // func std_divide(a: int, b: int) (int, error) { // return 0, error("division by zero was prevented") if(b == 0); @@ -55,3 +59,20 @@ func sqrt(a: float) float; // func exp(a: int, b: int) (int, error) { // return 0, zero(error); // } + +// func list_init() []datatype { +// } +// +// func list_append(slice: []datatype, value: datatype) error { +// if(slice.len == slice.cap) { +// // realloc +// } +// +// slice.data[slice.len] = value +// +// return zero(error); +// } +// +// func list_count(a: []datatype) int { +// return 0; +// } diff --git a/lang/test.swa b/lang/test.swa new file mode 100644 index 00000000..9b56b637 --- /dev/null +++ b/lang/test.swa @@ -0,0 +1,27 @@ +dialect:english; + +struct Inner { + vals: []int, +} + +struct Outer { + inners: []Inner, +} + +start() int { + let out: Outer = Outer{inners: make([]Inner)}; + + let in1: Inner = Inner{vals: make([]int)}; + in1.vals << 10; + // out.inners << in1; + // + // // deeply nested appended directly mapped over multiple pointers! + // out.inners[0].vals << 20; + + // print(count(out.inners)); + // print(count(out.inners[0].vals)); + // print(out.inners[0].vals[0]); + // print(out.inners[0].vals[1]); + + return 0; +} diff --git a/lang/test_simple.swa b/lang/test_simple.swa new file mode 100644 index 00000000..63fde0b7 --- /dev/null +++ b/lang/test_simple.swa @@ -0,0 +1,12 @@ +dialect:english; + +start() int { + let s: []int = make([]int); + + s << 42; + print(count(s), "\n"); + s << 43; + print(count(s), "\n"); + + return 0; +} diff --git a/tests/arrays_test.go b/tests/arrays_test.go index 5c043fad..b13d617a 100644 --- a/tests/arrays_test.go +++ b/tests/arrays_test.go @@ -232,7 +232,7 @@ func TestArrayZeroValues(t *testing.T) { NewSuccessfulCompileRequest( t, "./arrays/zero-values/string.swa", - "- [3]string with zero value\n'' '' ''\n\n", + "- [3]string with zero value\n'(null)' '(null)' '(null)'\n\n", ) }) diff --git a/tests/bug-fixes/missing-type-check-in-assignment-expression.3.english.swa b/tests/bug-fixes/missing-type-check-in-assignment-expression.3.english.swa index bbb540fa..d8319808 100644 --- a/tests/bug-fixes/missing-type-check-in-assignment-expression.3.english.swa +++ b/tests/bug-fixes/missing-type-check-in-assignment-expression.3.english.swa @@ -2,6 +2,6 @@ dialect:english; start () int { let res : float; - res = []float{1.0}; + res = [1]float{1.0}; return 0; } diff --git a/tests/deepseek_plaintext_test.go b/tests/deepseek_plaintext_test.go index c721742a..982ecdec 100644 --- a/tests/deepseek_plaintext_test.go +++ b/tests/deepseek_plaintext_test.go @@ -256,7 +256,7 @@ func TestDeepseekPlaintext(t *testing.T) { t.Run("Test 13: Function missing return", func(t *testing.T) { NewFailedCompileRequest(t, "./functions/deepseek_test13_function_missing_return.english.swa", - "function has no return statement\n", + "function foo has no return statement\n", ) }) @@ -280,7 +280,7 @@ func TestDeepseekPlaintext(t *testing.T) { t.Run("Test 39: Empty function body", func(t *testing.T) { NewFailedCompileRequest(t, "./functions/deepseek_test39_empty_function_body.english.swa", - "function has no return statement\n", + "function empty has no return statement\n", ) }) diff --git a/tests/functions_test.go b/tests/functions_test.go index dcd5ae88..79e188ce 100644 --- a/tests/functions_test.go +++ b/tests/functions_test.go @@ -436,7 +436,9 @@ func TestFunctions(t *testing.T) { // NewFailedCompileRequest(t, "./functions/test24_function_call_with_wrong_argument_type_should_not_compile.english.swa", "expected argument of type IntegerType(32 bits) but got PointerType(Reference)\n") // }) t.Run("Test 25: Missing return", func(t *testing.T) { - NewFailedCompileRequest(t, "./functions/test25_function_with_missing_return_statement_should_not_compile.english.swa", "function has no return statement\n") + NewFailedCompileRequest(t, + "./functions/test25_function_with_missing_return_statement_should_not_compile.english.swa", + "function no_return has no return statement\n") }) t.Run("Test 26: Duplicate function", func(t *testing.T) { NewFailedCompileRequest(t, "./functions/test26_function_redeclaration_should_not_compile.english.swa", "function named foo already exists in symbol table\n") diff --git a/tests/libc/read-hello-world-as-bytes.swa b/tests/libc/read-hello-world-as-bytes.swa index 05925969..a8829820 100644 --- a/tests/libc/read-hello-world-as-bytes.swa +++ b/tests/libc/read-hello-world-as-bytes.swa @@ -6,10 +6,10 @@ start() int { let buf : [1024]byte; let size : int = 2048; let off : int64 = 0; - let count : int64 = pread(fd, buf, size, off); + let countr : int64 = pread(fd, buf, size, off); let i : int; - while(i < count) { + while(i < countr) { print("%c", buf[i]); i = i + 1; } diff --git a/tests/libc/read-hello-world.swa b/tests/libc/read-hello-world.swa index 9b025f46..0e4cea27 100644 --- a/tests/libc/read-hello-world.swa +++ b/tests/libc/read-hello-world.swa @@ -5,10 +5,10 @@ start() int { let buf : [1024]byte; let size : int64 = 2048; - let count : int64 = read(fd, buf, size); + let counter : int64 = read(fd, buf, size); let i : int; - while(i < count) { + while(i < counter) { print("%c", buf[i]); i = i + 1; } diff --git a/tests/slices/edge/nested_struct_slice_append.swa b/tests/slices/edge/nested_struct_slice_append.swa new file mode 100644 index 00000000..2cca85d8 --- /dev/null +++ b/tests/slices/edge/nested_struct_slice_append.swa @@ -0,0 +1,27 @@ +dialect:english; + +struct Inner { + vals: []int, +} + +struct Outer { + inners: []Inner, +} + +start() int { + let out: Outer = Outer{inners: make([]Inner)}; + + let in1: Inner = Inner{vals: make([]int)}; + in1.vals << 10; + out.inners << in1; + + // deeply nested appended directly mapped over multiple pointers! + out.inners[0].vals << 20; + + print(count(out.inners)); + print(count(out.inners[0].vals)); + print(out.inners[0].vals[0]); + print(out.inners[0].vals[1]); + + return 0; +} diff --git a/tests/slices/edge/slice_aliasing.swa b/tests/slices/edge/slice_aliasing.swa new file mode 100644 index 00000000..0db1ac77 --- /dev/null +++ b/tests/slices/edge/slice_aliasing.swa @@ -0,0 +1,16 @@ +dialect:english; +start() int { + let a: []int = make([]int); + a << 10; + + // aliassing + let b: []int = a; + b << 20; // Does not affect `a`'s reported length because it was passed by value (len/cap are copied) + + print(count(a)); + print(count(b)); + print(a[0]); + print(b[1]); + + return 0; +} diff --git a/tests/slices/edge/slice_append_self.swa b/tests/slices/edge/slice_append_self.swa new file mode 100644 index 00000000..9708b1d8 --- /dev/null +++ b/tests/slices/edge/slice_append_self.swa @@ -0,0 +1,13 @@ +dialect:english; +start() int { + let s: []int = make([]int); + s << 5; + + s << s[0]; + // s[1] = s[1] * 2; + + print("count:", count(s), " "); + print("first-elem:",s[0], " "); + print("second-elem:",s[1], " "); + return 0; +} diff --git a/tests/slices/edge/slice_pass_by_value.swa b/tests/slices/edge/slice_pass_by_value.swa new file mode 100644 index 00000000..b82132f7 --- /dev/null +++ b/tests/slices/edge/slice_pass_by_value.swa @@ -0,0 +1,17 @@ +dialect:english; + +func modify(s: []int) int { + s << 99; // modifies local s argument, but string pointer might update! + return 0; +} + +start() int { + let s: []int = make([]int); + s << 10; + modify(s); + + // count should still be 1, because `s` was passed by value. + print(count(s)); + print(s[0]); + return 0; +} diff --git a/tests/slices/edge/slice_reassignment.swa b/tests/slices/edge/slice_reassignment.swa new file mode 100644 index 00000000..3e08c5ce --- /dev/null +++ b/tests/slices/edge/slice_reassignment.swa @@ -0,0 +1,13 @@ +dialect:english; +start() int { + let s: []int = make([]int); + let i: int = 0; + while (i < 3) { + s = make([]int); // Resets memory footprint logically by abandoning the old pointer + s << i; // 0, then reset and 1, then reset and 2. + i = i + 1; + } + print(count(s)); + print(s[0]); + return 0; +} diff --git a/tests/slices/edge/slice_return_append.swa b/tests/slices/edge/slice_return_append.swa new file mode 100644 index 00000000..016392fc --- /dev/null +++ b/tests/slices/edge/slice_return_append.swa @@ -0,0 +1,15 @@ +dialect:english; + +func get_slice() []int { + let s: []int = make([]int); + s << 1; + return s; +} + +start() int { + let x: []int = get_slice(); + x << 2; + print(x[0], " ", x[1]); + + return 0; +} diff --git a/tests/slices/errors/append_non_slice.swa b/tests/slices/errors/append_non_slice.swa new file mode 100644 index 00000000..bbd54d39 --- /dev/null +++ b/tests/slices/errors/append_non_slice.swa @@ -0,0 +1,6 @@ +dialect:english; +start() int { + let s: int = 1; + s << 2; + return 0; +} diff --git a/tests/slices/errors/append_non_slice_array.swa b/tests/slices/errors/append_non_slice_array.swa new file mode 100644 index 00000000..a66af336 --- /dev/null +++ b/tests/slices/errors/append_non_slice_array.swa @@ -0,0 +1,6 @@ +dialect:english; +start() int { + let arr: [3]int = [3]int{1, 2, 3}; + arr << 4; + return 0; +} diff --git a/tests/slices/errors/append_non_slice_bool.swa b/tests/slices/errors/append_non_slice_bool.swa new file mode 100644 index 00000000..6ff544be --- /dev/null +++ b/tests/slices/errors/append_non_slice_bool.swa @@ -0,0 +1,6 @@ +dialect:english; +start() int { + let b: bool = true; + b << false; + return 0; +} diff --git a/tests/slices/errors/append_non_slice_float.swa b/tests/slices/errors/append_non_slice_float.swa new file mode 100644 index 00000000..48d0e7d2 --- /dev/null +++ b/tests/slices/errors/append_non_slice_float.swa @@ -0,0 +1,6 @@ +dialect:english; +start() int { + let f: float = 3.14; + f << 1.0; + return 0; +} diff --git a/tests/slices/errors/append_non_slice_string.swa b/tests/slices/errors/append_non_slice_string.swa new file mode 100644 index 00000000..ea2f5895 --- /dev/null +++ b/tests/slices/errors/append_non_slice_string.swa @@ -0,0 +1,6 @@ +dialect:english; +start() int { + let s: string = "hello"; + s << "world"; + return 0; +} diff --git a/tests/slices/errors/append_non_slice_struct.swa b/tests/slices/errors/append_non_slice_struct.swa new file mode 100644 index 00000000..d7c2fbf2 --- /dev/null +++ b/tests/slices/errors/append_non_slice_struct.swa @@ -0,0 +1,7 @@ +dialect:english; +struct Point { x: int, } +start() int { + let p: Point = Point{x: 1}; + p << 2; + return 0; +} diff --git a/tests/slices/errors/french_append_non_slice.swa b/tests/slices/errors/french_append_non_slice.swa new file mode 100644 index 00000000..5bd13169 --- /dev/null +++ b/tests/slices/errors/french_append_non_slice.swa @@ -0,0 +1,6 @@ +dialecte:français; +demarrer() entier { + variable s: entier = 1; + s << 2; + retourner 0; +} diff --git a/tests/slices/errors/len_non_slice.swa b/tests/slices/errors/len_non_slice.swa new file mode 100644 index 00000000..a08fb22f --- /dev/null +++ b/tests/slices/errors/len_non_slice.swa @@ -0,0 +1,5 @@ +dialect:english; +start() int { + let s: int = count(42); + return 0; +} diff --git a/tests/slices/errors/len_non_slice_array.swa b/tests/slices/errors/len_non_slice_array.swa new file mode 100644 index 00000000..498eec2b --- /dev/null +++ b/tests/slices/errors/len_non_slice_array.swa @@ -0,0 +1,6 @@ +dialect:english; +start() int { + let arr: [3]int = [3]int{1, 2, 3}; + let s: int = count(arr); + return 0; +} diff --git a/tests/slices/errors/len_non_slice_bool.swa b/tests/slices/errors/len_non_slice_bool.swa new file mode 100644 index 00000000..6e1498c0 --- /dev/null +++ b/tests/slices/errors/len_non_slice_bool.swa @@ -0,0 +1,6 @@ +dialect:english; +start() int { + let b: bool = true; + let s: int = count(b); + return 0; +} diff --git a/tests/slices/errors/len_non_slice_float.swa b/tests/slices/errors/len_non_slice_float.swa new file mode 100644 index 00000000..b84796d6 --- /dev/null +++ b/tests/slices/errors/len_non_slice_float.swa @@ -0,0 +1,5 @@ +dialect:english; +start() int { + let s: int = count(1.0); + return 0; +} diff --git a/tests/slices/errors/len_non_slice_string.swa b/tests/slices/errors/len_non_slice_string.swa new file mode 100644 index 00000000..bda9370c --- /dev/null +++ b/tests/slices/errors/len_non_slice_string.swa @@ -0,0 +1,5 @@ +dialect:english; +start() int { + let s: int = count("hello"); + return 0; +} diff --git a/tests/slices/errors/len_non_slice_struct.swa b/tests/slices/errors/len_non_slice_struct.swa new file mode 100644 index 00000000..3ac5fa61 --- /dev/null +++ b/tests/slices/errors/len_non_slice_struct.swa @@ -0,0 +1,7 @@ +dialect:english; +struct Point { x: int, } +start() int { + let p: Point = Point{x: 1}; + let s: int = count(p); + return 0; +} diff --git a/tests/slices/errors/make_non_slice.swa b/tests/slices/errors/make_non_slice.swa new file mode 100644 index 00000000..88ab56ec --- /dev/null +++ b/tests/slices/errors/make_non_slice.swa @@ -0,0 +1,6 @@ +dialect:english; +start() int { + // make() should only be compatible with slice allocation mappings + let s: []int = make(int); + return 0; +} diff --git a/tests/slices/errors/make_non_slice_array.swa b/tests/slices/errors/make_non_slice_array.swa new file mode 100644 index 00000000..fdd2695e --- /dev/null +++ b/tests/slices/errors/make_non_slice_array.swa @@ -0,0 +1,6 @@ +dialect:english; +start() int { + // Try mapping a fixed array size inside make allocator directly natively + let arr: [][3]int = make([3]int); + return 0; +} diff --git a/tests/slices/errors/make_non_slice_bool.swa b/tests/slices/errors/make_non_slice_bool.swa new file mode 100644 index 00000000..e2f1c1a3 --- /dev/null +++ b/tests/slices/errors/make_non_slice_bool.swa @@ -0,0 +1,5 @@ +dialect:english; +start() int { + let s: []bool = make(bool); + return 0; +} diff --git a/tests/slices/errors/make_non_slice_float.swa b/tests/slices/errors/make_non_slice_float.swa new file mode 100644 index 00000000..e3d05385 --- /dev/null +++ b/tests/slices/errors/make_non_slice_float.swa @@ -0,0 +1,5 @@ +dialect:english; +start() int { + let s: []float = make(float); + return 0; +} diff --git a/tests/slices/errors/make_non_slice_string.swa b/tests/slices/errors/make_non_slice_string.swa new file mode 100644 index 00000000..0bbe8acd --- /dev/null +++ b/tests/slices/errors/make_non_slice_string.swa @@ -0,0 +1,5 @@ +dialect:english; +start() int { + let s: []string = make(string); + return 0; +} diff --git a/tests/slices/errors/make_non_slice_struct.swa b/tests/slices/errors/make_non_slice_struct.swa new file mode 100644 index 00000000..e8142e17 --- /dev/null +++ b/tests/slices/errors/make_non_slice_struct.swa @@ -0,0 +1,6 @@ +dialect:english; +struct Point { x: int, } +start() int { + let s: []Point = make(Point); + return 0; +} diff --git a/tests/slices/errors/malinke_count_non_slice.swa b/tests/slices/errors/malinke_count_non_slice.swa new file mode 100644 index 00000000..9fae220b --- /dev/null +++ b/tests/slices/errors/malinke_count_non_slice.swa @@ -0,0 +1,5 @@ +kan:malinke; +daminen() jate { + atö s: jate = count(1); + segin 0; +} diff --git a/tests/slices/errors/soussou_make_non_slice.swa b/tests/slices/errors/soussou_make_non_slice.swa new file mode 100644 index 00000000..196adde7 --- /dev/null +++ b/tests/slices/errors/soussou_make_non_slice.swa @@ -0,0 +1,5 @@ +khuien:soussou; +sodé() konti { + kouicé s: []konti = make(konti); + gbilen 0; +} diff --git a/tests/slices/errors/wolof_append_non_slice.swa b/tests/slices/errors/wolof_append_non_slice.swa new file mode 100644 index 00000000..530cc595 --- /dev/null +++ b/tests/slices/errors/wolof_append_non_slice.swa @@ -0,0 +1,6 @@ +laak:wolof; +tambali() lëmm { + dencukaay s: lëmm = 1; + s << 2; + deloko 0; +} diff --git a/tests/slices/grow/int_grow.swa b/tests/slices/grow/int_grow.swa new file mode 100644 index 00000000..5215c135 --- /dev/null +++ b/tests/slices/grow/int_grow.swa @@ -0,0 +1,14 @@ +dialect:english; + +start() int { + let s: []int = make([]int); + s << 1; + s << 2; + print("full: count=%d", count(s)); + + s << 3; + print("full: count=%d", count(s)); + print("values: %d %d %d", s[0], s[1], s[2]); + + return 0; +} diff --git a/tests/slices/primitives/all.swa b/tests/slices/primitives/all.swa new file mode 100644 index 00000000..2e5c045b --- /dev/null +++ b/tests/slices/primitives/all.swa @@ -0,0 +1,37 @@ +dialect:english; + +start() int { + let ints: []int = make([]int); + ints << 10; + ints << 20; + ints << 30; + + print("count=%d [%d %d %d]\n", + count(ints), ints[0], ints[1], ints[2]); + + let bools: []bool = make([]bool); + bools << true; + bools << false; + bools << true; + + print("count=%d [%d %d %d]\n", count(bools), + bools[0], bools[1], bools[2]); + + let floats: []float = make([]float); + floats << 1.5; + floats << 2.5; + floats << 3.5; + + print("count=%d [%.1f %.1f %.1f]\n", + count(floats), floats[0], floats[1], floats[2]); + + let strings: []string = make([]string); + strings << "hello"; + strings << "world"; + strings << "swa"; + + print("count=%d [%s %s %s]\n", + count(strings), strings[0], strings[1], strings[2]); + + return 0; +} diff --git a/tests/slices/primitives/zero.swa b/tests/slices/primitives/zero.swa new file mode 100644 index 00000000..c95ae10b --- /dev/null +++ b/tests/slices/primitives/zero.swa @@ -0,0 +1,10 @@ +dialect:english; + +start() int { + let s: []int; + print("count=%d", count(s)); + s << 42; + print("count=%d val=%d", count(s), s[0]); + + return 0; +} diff --git a/tests/slices/struct_slice_append.swa b/tests/slices/struct_slice_append.swa new file mode 100644 index 00000000..e97c6ad6 --- /dev/null +++ b/tests/slices/struct_slice_append.swa @@ -0,0 +1,19 @@ +dialect:english; + +struct Container { + id: int, + s: []int, +} + +start() int { + let c: Container = Container{id: 1, s: make([]int)}; + c.s << 10; + c.s << 20; + + let i: int = 0; + while (i < count(c.s)) { + print(c.s[i]); + i = i + 1; + } + return 0; +} diff --git a/tests/slices/structs/points.swa b/tests/slices/structs/points.swa new file mode 100644 index 00000000..55f1ada4 --- /dev/null +++ b/tests/slices/structs/points.swa @@ -0,0 +1,22 @@ +dialect:english; + +struct Point { + X: int, + Y: int, +} + +start() int { + let points: []Point = make([]Point); + + let p1: Point = Point{ X: 1, Y: 2 }; + let p2: Point = Point{ X: 3, Y: 4 }; + let p3: Point = Point{ X: 5, Y: 6 }; + + points << p1; + points << p2; + points << p3; + + print("len=%d", count(points)); + + return 0; +} diff --git a/tests/slices/while/if_else_append.swa b/tests/slices/while/if_else_append.swa new file mode 100644 index 00000000..26c03a2c --- /dev/null +++ b/tests/slices/while/if_else_append.swa @@ -0,0 +1,24 @@ +dialect:english; + +func is_even(n: int) bool { + return n/2*2 == n; +} + +start() int { + let s: []int = make([]int); + let i: int = 0; + while (i < 5) { + if (is_even(i)) { + s << i; + } else { + s << i * 10; + } + i = i + 1; + } + let c: int = 0; + while (c < count(s)) { + print(s[c]); + c = c + 1; + } + return 0; +} diff --git a/tests/slices/while/nested_while_append.swa b/tests/slices/while/nested_while_append.swa new file mode 100644 index 00000000..9c723c39 --- /dev/null +++ b/tests/slices/while/nested_while_append.swa @@ -0,0 +1,22 @@ +dialect:english; + +start() int { + let s: []int = make([]int); + let i: int = 0; + while (i < 2) { + let j: int = 0; + while (j < 3) { + let val: int = i * 10; + val = val + j; + s << val; + j = j + 1; + } + i = i + 1; + } + let c: int = 0; + while (c < count(s)) { + print(s[c]); + c = c + 1; + } + return 0; +} diff --git a/tests/slices/while/slice_index_update.swa b/tests/slices/while/slice_index_update.swa new file mode 100644 index 00000000..975ebf6e --- /dev/null +++ b/tests/slices/while/slice_index_update.swa @@ -0,0 +1,19 @@ +dialect:english; + +start() int { + let s: []int = make([]int); + s << 10; + s << 20; + s << 30; + let i: int = 0; + while (i < 3) { + s[i] = s[i] + i; + i = i + 1; + } + let c: int = 0; + while (c < count(s)) { + print(s[c]); + c = c + 1; + } + return 0; +} diff --git a/tests/slices/while/while_append.swa b/tests/slices/while/while_append.swa new file mode 100644 index 00000000..f28330f4 --- /dev/null +++ b/tests/slices/while/while_append.swa @@ -0,0 +1,19 @@ +dialect:english; + +start() int { + let s: []int = make([]int); + + let counter : int = 0; + while(counter < 5) { + s << counter; + counter = counter + 1; + } + + let counter_print : int = 0; + while(counter_print < count(s)) { + print(s[counter_print]); + counter_print = counter_print + 1; + } + + return 0; +} diff --git a/tests/slices/while/while_append_bool.swa b/tests/slices/while/while_append_bool.swa new file mode 100644 index 00000000..b55b1c12 --- /dev/null +++ b/tests/slices/while/while_append_bool.swa @@ -0,0 +1,21 @@ +dialect:english; +start() int { + let s: []bool = make([]bool); + let b: bool = true; + let i: int = 0; + while (i < 3) { + s << b; + b = false; + i = i + 1; + } + let c: int = 0; + while (c < count(s)) { + if (s[c] == true) { + print("1"); + } else { + print("0"); + } + c = c + 1; + } + return 0; +} diff --git a/tests/slices/while/while_append_float.swa b/tests/slices/while/while_append_float.swa new file mode 100644 index 00000000..37601bbd --- /dev/null +++ b/tests/slices/while/while_append_float.swa @@ -0,0 +1,17 @@ +dialect:english; +start() int { + let s: []float = make([]float); + let f: float = 0.5; + let i: int = 0; + while (i < 3) { + s << f; + f = f + 1.0; + i = i + 1; + } + let c: int = 0; + while (c < count(s)) { + print(s[c]); + c = c + 1; + } + return 0; +} diff --git a/tests/slices/while/while_append_string.swa b/tests/slices/while/while_append_string.swa new file mode 100644 index 00000000..1d78dd2c --- /dev/null +++ b/tests/slices/while/while_append_string.swa @@ -0,0 +1,15 @@ +dialect:english; +start() int { + let s: []string = make([]string); + let i: int = 0; + while (i < 3) { + s << "a"; + i = i + 1; + } + let c: int = 0; + while (c < count(s)) { + print(s[c]); + c = c + 1; + } + return 0; +} diff --git a/tests/slices/while/while_append_struct.swa b/tests/slices/while/while_append_struct.swa new file mode 100644 index 00000000..c83213c5 --- /dev/null +++ b/tests/slices/while/while_append_struct.swa @@ -0,0 +1,23 @@ +dialect:english; + +struct Point { + x: int, + y: int, +} + +start() int { + let s: []Point = make([]Point); + let i: int = 0; + while (i < 3) { + let p: Point = Point{x: i, y: i * 2}; + s << p; + i = i + 1; + } + let c: int = 0; + while (c < count(s)) { + let p2: Point = s[c]; + print(p2.x, p2.y); + c = c + 1; + } + return 0; +} diff --git a/tests/slices_test.go b/tests/slices_test.go new file mode 100644 index 00000000..eb9f3713 --- /dev/null +++ b/tests/slices_test.go @@ -0,0 +1,35 @@ +package tests + +import ( + "testing" +) + +func TestSlicePrimitives(t *testing.T) { + t.Run("append and index", func(t *testing.T) { + NewSuccessfulCompileRequest(t, + "./slices/primitives/all.swa", + "count=3 [10 20 30]\ncount=3 [1 0 1]\ncount=3 [1.5 2.5 3.5]\ncount=3 [hello world swa]\n") + }) + + t.Run("zero-initialized slice", func(t *testing.T) { + NewFailedCompileRequest(t, + "./slices/primitives/zero.swa", + "List should be initialized. Use make([]datatype)\n") + }) +} + +func TestSliceGrow(t *testing.T) { + t.Run("int slice doubles capacity on overflow", func(t *testing.T) { + NewSuccessfulCompileRequest(t, + "./slices/grow/int_grow.swa", + "full: count=2full: count=3values: 1 2 3") + }) +} + +func TestSliceOfStructs(t *testing.T) { + t.Run("len and cap after appending Point structs", func(t *testing.T) { + NewSuccessfulCompileRequest(t, + "./slices/structs/points.swa", + "len=3") + }) +} diff --git a/tests/tuples/17.swa b/tests/tuples/17.swa index 8361fd0e..fb5c365c 100644 --- a/tests/tuples/17.swa +++ b/tests/tuples/17.swa @@ -5,7 +5,7 @@ func pair(n: int) (int, int) { return n, n+1; } start() int { - // ERROR: cannot put (int,int) into array element let arr: [2]int = [2]int{ pair(5) }; + print("arr[0] =", arr[0], " arr[1] = ", arr[1]); return 0; } diff --git a/tests/tuples_test.go b/tests/tuples_test.go index 64d5fd20..520f8531 100644 --- a/tests/tuples_test.go +++ b/tests/tuples_test.go @@ -77,8 +77,8 @@ func TestTuples(t *testing.T) { // }) t.Run("17", func(t *testing.T) { - NewFailedCompileRequest(t, "./tuples/17.swa", - "cannot insert Tuple in array of Number\n") + NewSuccessfulCompileRequest(t, "./tuples/17.swa", + "arr[0] =5 arr[1] = 6") }) t.Run("18", func(t *testing.T) { @@ -96,7 +96,7 @@ func TestTuples(t *testing.T) { }) t.Run("21", func(t *testing.T) { NewFailedCompileRequest(t, "./tuples/21.swa", - "function has no return statement\n") + "function empty has no return statement\n") }) t.Run("22", func(t *testing.T) { NewSuccessfulCompileRequest(t, "./tuples/22.swa",