Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 19 additions & 9 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,32 @@
- Don't add comments explaining each line of code. If you need to add comments to describe a block of statements then you should extract them to
a function with meaningful name.

- Each bal/go file should have the correct license header

## Symbols

- IMPORTANT: never store `model.Symbol` as the key in a map, always use a `model.SymbolRef`
- Don't call operations on symbols directly instead call them via compiler context

## Interpreter stages

1. Generate Syntax Tree
2. Do symbol resolution
3. Do type resolution
4. Generate Control Flow Graph (CFG)
5. Do semantic analysis
6. Analyze CFG
2. Generate Abstract syntax tree (AST)
3. Do symbol resolution
4. Do type resolution
5. Type Narrowing
6. Semantic analysis
7. Generate Control Flow Graph (CFG)
8. Analyze CFG
- Reachability analysis
- Explicit return analysis
7. Generate BIR
8. Interpret generated BIR
9. Desugar AST
10. Generate BIR
11. Interpret generated BIR

Stages up to 10 are considered front end, and stage 11 is backend

Stages up to 7 are considered front end.
Execution of these stages is defined in `module_context.go` (and `testphases/phases.go` for corpus tests)

## Tests

Expand All @@ -51,9 +59,11 @@ Stages up to 7 are considered front end.

- You can run interpreter as `go run ./cli/cmd run [flags] <path to bal file>`

## Profiling
## Profiling

- In order to profile a `.bal` file first you need to get a debug build (`go build -tags debug -o bal-debug ./cli/cmd`)
- Then run the debug build against the bal file `./bal-debug [flag] -prof <path to bal file>`.

### Opening interactive profiler on log running processes

- After running the interpreter with profiling flags run `go tool pprof -http=:8080 http://localhost:6060/debug/pprof/profile?seconds=30` and open `localhost:8080` in the browser
1 change: 1 addition & 0 deletions ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ func UnMask(mask Flags) common.Set[model.Flag] {

type BNodeWithSymbol interface {
model.NodeWithSymbol
BLangNode
SetSymbol(symbolRef model.SymbolRef)
}

Expand Down
40 changes: 13 additions & 27 deletions ast/corpus_ast_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@
// specific language governing permissions and limitations
// under the License.

package ast
package ast_test

import (
debugcommon "ballerina-lang-go/common"
"ballerina-lang-go/ast"
"ballerina-lang-go/context"
"ballerina-lang-go/model"
"ballerina-lang-go/parser"
"ballerina-lang-go/semtypes"
"ballerina-lang-go/test_util"
"ballerina-lang-go/test_util/testphases"
"flag"
"fmt"
"testing"
Expand Down Expand Up @@ -50,20 +50,14 @@ func testASTGeneration(t *testing.T, testCase test_util.TestCase) {
}
}()

debugCtx := debugcommon.DebugContext{
Channel: make(chan string),
}
cx := context.NewCompilerContext(semtypes.CreateTypeEnv())
syntaxTree, err := parser.GetSyntaxTree(cx, &debugCtx, testCase.InputPath)
result, err := testphases.RunPipeline(cx, testphases.PhaseAST, testCase.InputPath)
if err != nil {
t.Errorf("error getting syntax tree for %s: %v", testCase.InputPath, err)
}
compilationUnit := GetCompilationUnit(cx, syntaxTree)
if compilationUnit == nil {
t.Errorf("compilation unit is nil for %s", testCase.InputPath)
t.Errorf("pipeline failed for %s: %v", testCase.InputPath, err)
return
}
prettyPrinter := PrettyPrinter{}
actualAST := prettyPrinter.Print(compilationUnit)
prettyPrinter := ast.PrettyPrinter{}
actualAST := prettyPrinter.Print(result.CompilationUnit)

// If update flag is set, update expected file
if *update {
Expand Down Expand Up @@ -99,7 +93,7 @@ type walkTestVisitor struct {
nodeCount int
}

func (v *walkTestVisitor) Visit(node BLangNode) Visitor {
func (v *walkTestVisitor) Visit(node ast.BLangNode) ast.Visitor {
if node == nil {
return nil
}
Expand All @@ -109,7 +103,7 @@ func (v *walkTestVisitor) Visit(node BLangNode) Visitor {
return v
}

func (v *walkTestVisitor) VisitTypeData(typeData *model.TypeData) Visitor {
func (v *walkTestVisitor) VisitTypeData(typeData *model.TypeData) ast.Visitor {
return v
}

Expand All @@ -133,23 +127,15 @@ func testWalkTraversal(t *testing.T, testCase test_util.TestCase) {
}
}()

debugCtx := debugcommon.DebugContext{
Channel: make(chan string),
}
cx := context.NewCompilerContext(semtypes.CreateTypeEnv())
syntaxTree, err := parser.GetSyntaxTree(cx, &debugCtx, testCase.InputPath)
result, err := testphases.RunPipeline(cx, testphases.PhaseAST, testCase.InputPath)
if err != nil {
t.Errorf("error getting syntax tree for %s: %v", testCase.InputPath, err)
return
}
compilationUnit := GetCompilationUnit(cx, syntaxTree)
if compilationUnit == nil {
t.Errorf("compilation unit is nil for %s", testCase.InputPath)
t.Errorf("pipeline failed for %s: %v", testCase.InputPath, err)
return
}

visitor := &walkTestVisitor{visitedTypes: make(map[string]int)}
Walk(visitor, compilationUnit)
ast.Walk(visitor, result.CompilationUnit)

if visitor.nodeCount == 0 {
t.Errorf("Walk visited 0 nodes for %s", testCase.InputPath)
Expand Down
29 changes: 29 additions & 0 deletions ast/expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,13 @@
PositionalArgs []BLangExpression
// TODO: Add support for NamedArgs
}

BLangTypeTestExpr struct {
BLangExpressionBase
Expr BLangExpression
Type model.TypeData
isNegation bool
}
)

var (
Expand Down Expand Up @@ -316,6 +323,8 @@
_ BLangExpression = &BLangTypeConversionExpr{}
_ BLangExpression = &BLangErrorConstructorExpr{}
_ BLangNode = &BLangErrorConstructorExpr{}
_ BLangExpression = &BLangTypeTestExpr{}
_ model.TypeTestExpressionNode = &BLangTypeTestExpr{}
)

var (
Expand Down Expand Up @@ -954,6 +963,26 @@
panic("not implemented")
}

func (this *BLangTypeTestExpr) GetKind() model.NodeKind {
return model.NodeKind_TYPE_TEST_EXPR

Check warning on line 967 in ast/expressions.go

View check run for this annotation

Codecov / codecov/patch

ast/expressions.go#L966-L967

Added lines #L966 - L967 were not covered by tests
}

func (this *BLangTypeTestExpr) IsNegation() bool {
return this.isNegation
}

func (this *BLangTypeTestExpr) GetExpression() model.ExpressionNode {
return this.Expr

Check warning on line 975 in ast/expressions.go

View check run for this annotation

Codecov / codecov/patch

ast/expressions.go#L974-L975

Added lines #L974 - L975 were not covered by tests
}

func (this *BLangTypeTestExpr) GetType() model.TypeData {
return this.Type

Check warning on line 979 in ast/expressions.go

View check run for this annotation

Codecov / codecov/patch

ast/expressions.go#L978-L979

Added lines #L978 - L979 were not covered by tests
}

func (this *BLangTypeTestExpr) SetTypeCheckedType(ty BType) {
panic("not implemented")

Check warning on line 983 in ast/expressions.go

View check run for this annotation

Codecov / codecov/patch

ast/expressions.go#L982-L983

Added lines #L982 - L983 were not covered by tests
}

func createBLangUnaryExpr(location Location, operator model.OperatorKind, expr BLangExpression) *BLangUnaryExpr {
exprNode := &BLangUnaryExpr{}
exprNode.pos = location
Expand Down
9 changes: 8 additions & 1 deletion ast/node_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2089,7 +2089,14 @@
}

func (n *NodeBuilder) TransformTypeTestExpression(typeTestExpressionNode *tree.TypeTestExpressionNode) BLangNode {
panic("TransformTypeTestExpression unimplemented")
typeTestExpr := &BLangTypeTestExpr{}
if typeTestExpressionNode.IsKeyword().Kind() == common.NOT_IS_KEYWORD {
typeTestExpr.isNegation = true

Check warning on line 2094 in ast/node_builder.go

View check run for this annotation

Codecov / codecov/patch

ast/node_builder.go#L2094

Added line #L2094 was not covered by tests
}
typeTestExpr.Expr = n.createExpression(typeTestExpressionNode.Expression())
typeTestExpr.Type = model.TypeData{TypeDescriptor: n.createTypeNode(typeTestExpressionNode.TypeDescriptor())}
typeTestExpr.SetPosition(getPosition(typeTestExpressionNode))
return typeTestExpr
}

func (n *NodeBuilder) TransformRemoteMethodCallAction(remoteMethodCallActionNode *tree.RemoteMethodCallActionNode) BLangNode {
Expand Down
18 changes: 18 additions & 0 deletions ast/pretty_printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@
p.printListConstructorExpr(t)
case *BLangTypeConversionExpr:
p.printTypeConversionExpr(t)
case *BLangTypeTestExpr:
p.printTypeTestExpr(t)
default:
fmt.Println(p.buffer.String())
panic("Unsupported node type: " + reflect.TypeOf(t).String())
Expand Down Expand Up @@ -525,6 +527,22 @@
p.endNode()
}

func (p *PrettyPrinter) printTypeTestExpr(node *BLangTypeTestExpr) {
p.startNode()
if node.isNegation {
p.printString("type-test-expr !is")

Check warning on line 533 in ast/pretty_printer.go

View check run for this annotation

Codecov / codecov/patch

ast/pretty_printer.go#L533

Added line #L533 was not covered by tests
} else {
p.printString("type-test-expr is")
}
p.indentLevel++
p.PrintInner(node.Expr.(BLangNode))
if node.Type.TypeDescriptor != nil {
p.PrintInner(node.Type.TypeDescriptor.(BLangNode))
}
p.indentLevel--
p.endNode()
}

// While loop printer
func (p *PrettyPrinter) printWhile(node *BLangWhile) {
p.startNode()
Expand Down
26 changes: 26 additions & 0 deletions ast/statements.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
type BLangStatement = model.StatementNode

type (
// TODO: make this private
BLangStatementBase struct {
BLangNodeBase
}
Expand Down Expand Up @@ -141,6 +142,23 @@
_ BLangNode = &BLangSimpleVariableDef{}
)

var (
_ BLangStatement = &BLangAssignment{}
_ BLangStatement = &BLangBlockStmt{}
_ BLangStatement = &BLangBreak{}
_ BLangStatement = &BLangCompoundAssignment{}
_ BLangStatement = &BLangContinue{}
_ BLangStatement = &BLangDo{}
_ BLangStatement = &BLangExpressionStmt{}
_ BLangStatement = &BLangIf{}
_ BLangStatement = &BLangWhile{}
_ BLangStatement = &BLangForeach{}
_ BLangStatement = &BLangSimpleVariableDef{}
_ BLangStatement = &BLangReturn{}
)

func (*BLangStatementBase) IsStatement() {}

Check warning on line 160 in ast/statements.go

View check run for this annotation

Codecov / codecov/patch

ast/statements.go#L160

Added line #L160 was not covered by tests

func (this *BLangAssignment) GetVariable() model.ExpressionNode {
// migrated from BLangAssignment.java:48:5
return this.VarRef
Expand Down Expand Up @@ -202,6 +220,14 @@
return model.NodeKind_BREAK
}

func (this *BLangCompoundAssignment) IsDeclaredWithVar() bool {
return false

Check warning on line 224 in ast/statements.go

View check run for this annotation

Codecov / codecov/patch

ast/statements.go#L223-L224

Added lines #L223 - L224 were not covered by tests
}

func (this *BLangCompoundAssignment) SetDeclaredWithVar(_ bool) {
panic("compound assignemnt can't be declared with var")

Check warning on line 228 in ast/statements.go

View check run for this annotation

Codecov / codecov/patch

ast/statements.go#L227-L228

Added lines #L227 - L228 were not covered by tests
}

func (this *BLangCompoundAssignment) GetOperatorKind() model.OperatorKind {
// migrated from BLangCompoundAssignment.java:59:5
return this.OpKind
Expand Down
6 changes: 6 additions & 0 deletions ast/walk.go
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,12 @@ func Walk(v Visitor, node BLangNode) {
Walk(v, node.TypeDescriptor.(BLangNode))
}

case *BLangTypeTestExpr:
if node.Expr != nil {
Walk(v, node.Expr.(BLangNode))
}
WalkTypeData(v, &node.Type)

case *BLangCommitExpr:
panic("unimplemented")

Expand Down
20 changes: 19 additions & 1 deletion bir/bir_gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,8 @@ func handleExpression(ctx *stmtContext, curBB *BIRBasicBlock, expr ast.BLangExpr
return listConstructorExpression(ctx, curBB, expr)
case *ast.BLangTypeConversionExpr:
return typeConversionExpression(ctx, curBB, expr)
case *ast.BLangTypeTestExpr:
return typeTestExpression(ctx, curBB, expr)
default:
panic("unexpected expression type")
}
Expand All @@ -544,6 +546,22 @@ func typeConversionExpression(ctx *stmtContext, curBB *BIRBasicBlock, expr *ast.
}
}

func typeTestExpression(ctx *stmtContext, curBB *BIRBasicBlock, expr *ast.BLangTypeTestExpr) expressionEffect {
exprEffect := handleExpression(ctx, curBB, expr.Expr)
curBB = exprEffect.block
resultOperand := ctx.addTempVar(expr.GetDeterminedType())
typeTest := &TypeTest{}
typeTest.LhsOp = resultOperand
typeTest.RhsOp = exprEffect.result
typeTest.Type = expr.Type.Type
typeTest.IsNegation = expr.IsNegation()
curBB.Instructions = append(curBB.Instructions, typeTest)
return expressionEffect{
result: resultOperand,
block: curBB,
}
}

func listConstructorExpression(ctx *stmtContext, bb *BIRBasicBlock, expr *ast.BLangListConstructorExpr) expressionEffect {
initValues := make([]*BIROperand, len(expr.Exprs))
for i, expr := range expr.Exprs {
Expand Down Expand Up @@ -762,7 +780,7 @@ func binaryExpression(ctx *stmtContext, curBB *BIRBasicBlock, expr *ast.BLangBin

func simpleVariableReference(ctx *stmtContext, curBB *BIRBasicBlock, expr *ast.BLangSimpleVarRef) expressionEffect {
varName := expr.VariableName.GetValue()
symRef := expr.Symbol()
symRef := ctx.birCx.CompilerContext.UnnarrowedSymbol(expr.Symbol())

// Try local variable lookup first
if operand, ok := ctx.varMap[symRef]; ok {
Expand Down
3 changes: 1 addition & 2 deletions bir/codec/corpus_codec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,7 @@ func testBIRSerialization(t *testing.T, testPair test_util.TestCase) {
semantics.ResolveSymbols(cx, pkg, importedSymbols)

// Step 5: Resolve types
typeResolver := semantics.NewTypeResolver(cx, importedSymbols)
typeResolver.ResolveTypes(cx, pkg)
semantics.ResolveTypes(cx, pkg, importedSymbols)

// Step 6: Generate control flow graph
cfg := semantics.CreateControlFlowGraph(cx, pkg)
Expand Down
Loading