• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

forst-lang / forst / 15376924484

01 Jun 2025 04:02PM UTC coverage: 24.659% (-2.5%) from 27.2%
15376924484

Pull #13

github

haveyaseen
feat: Add control flow, references, and map literals

Add support for map literals, references, and control flow

- Add MapLiteralNode and ReferenceNode to structural hasher

- Add support for hashing map entries in deterministic order

- Add support for var declarations with type annotations

- Add support for if statements with init, else-if, and else blocks

- Add support for references (&) to variables and struct literals

- Add Node.js invocation example with TypeScript

- Add support for increment/decrement operators (++, --)

- Add parse-only support for channel send operations (<-)

- Add support for semicolons after initialization statements
Pull Request #13: feat: Add control flow, references, and map literals

18 of 483 new or added lines in 20 files covered. (3.73%)

3 existing lines in 3 files now uncovered.

1192 of 4834 relevant lines covered (24.66%)

2.38 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

65.25
/forst/internal/parser/function.go
1
package parser
2

3
import (
4
        "forst/internal/ast"
5
)
6

7
func (p *Parser) parseParameterType() ast.TypeNode {
4✔
8
        next := p.peek()
4✔
9
        // TODO: Even if the next token is a dot this could be a type without any constraints
4✔
10
        if next.Type == ast.TokenDot || next.Type == ast.TokenLParen {
4✔
11
                assertion := p.parseAssertionChain(true)
×
12
                return ast.TypeNode{
×
13
                        Ident:     ast.TypeAssertion,
×
14
                        Assertion: &assertion,
×
15
                }
×
16
        }
×
17
        return p.parseType(TypeIdentOpts{AllowLowercaseTypes: false})
4✔
18
}
19

20
func (p *Parser) parseDestructuredParameter() ast.ParamNode {
×
21
        p.expect(ast.TokenLBrace)
×
22
        fields := []string{}
×
23

×
24
        // Parse fields until we hit closing brace
×
25
        for p.current().Type != ast.TokenRBrace {
×
26
                name := p.expect(ast.TokenIdentifier)
×
27
                fields = append(fields, name.Value)
×
28

×
29
                // Handle comma between fields
×
30
                if p.current().Type == ast.TokenComma {
×
31
                        p.advance()
×
32
                }
×
33
        }
34

35
        p.expect(ast.TokenRBrace)
×
36
        p.expect(ast.TokenColon)
×
37

×
38
        paramType := p.parseParameterType()
×
39

×
40
        return ast.DestructuredParamNode{
×
41
                Fields: fields,
×
42
                Type:   paramType,
×
43
        }
×
44
}
45

46
func (p *Parser) parseSimpleParameter() ast.ParamNode {
4✔
47
        ident := p.expect(ast.TokenIdentifier)
4✔
48
        // If the next token is a colon, consume it; otherwise, assume the next token is the type
4✔
49
        if p.current().Type == ast.TokenColon {
8✔
50
                p.advance()
4✔
51
        }
4✔
52
        typ := p.parseParameterType()
4✔
53
        return ast.SimpleParamNode{
4✔
54
                Ident: ast.Ident{ID: ast.Identifier(ident.Value)},
4✔
55
                Type:  typ,
4✔
56
        }
4✔
57
}
58

59
func (p *Parser) parseParameter() ast.ParamNode {
4✔
60
        switch p.current().Type {
4✔
61
        case ast.TokenIdentifier:
4✔
62
                return p.parseSimpleParameter()
4✔
63
        case ast.TokenLBrace:
×
64
                return p.parseDestructuredParameter()
×
65
        default:
×
66
                panic(parseErrorMessage(p.current(), "Expected parameter"))
×
67
        }
68
}
69

70
// Parse function parameters
71
func (p *Parser) parseFunctionSignature() []ast.ParamNode {
3✔
72
        p.expect(ast.TokenLParen)
3✔
73
        params := []ast.ParamNode{}
3✔
74

3✔
75
        // Handle empty parameter list
3✔
76
        if p.current().Type == ast.TokenRParen {
3✔
77
                p.advance()
×
78
                return params
×
79
        }
×
80

81
        // Parse parameters
82
        for {
6✔
83
                param := p.parseParameter()
3✔
84
                switch param.(type) {
3✔
85
                case ast.DestructuredParamNode:
×
86
                        logParsedNodeWithMessage(param, "Parsed destructured function param")
×
87
                default:
3✔
88
                        logParsedNodeWithMessage(param, "Parsed function param")
3✔
89
                }
90
                params = append(params, param)
3✔
91

3✔
92
                // Check if there are more parameters
3✔
93
                if p.current().Type == ast.TokenComma {
3✔
94
                        p.advance()
×
95
                } else {
3✔
96
                        break
3✔
97
                }
98
        }
99

100
        p.expect(ast.TokenRParen)
3✔
101
        return params
3✔
102
}
103

104
func (p *Parser) parseReturnType() []ast.TypeNode {
3✔
105
        returnType := []ast.TypeNode{}
3✔
106
        if p.current().Type == ast.TokenColon {
3✔
107
                p.advance() // Consume the colon
×
NEW
108
                returnType = append(returnType, p.parseType(TypeIdentOpts{AllowLowercaseTypes: false}))
×
109
        }
×
110
        return returnType
3✔
111
}
112

113
func (p *Parser) parseReturnStatement() ast.ReturnNode {
3✔
114
        p.advance() // Move past `return`
3✔
115

3✔
116
        returnExpression := p.parseExpression()
3✔
117

3✔
118
        return ast.ReturnNode{
3✔
119
                Value: returnExpression,
3✔
120
                Type:  ast.TypeNode{Ident: ast.TypeImplicit},
3✔
121
        }
3✔
122
}
3✔
123

124
func (p *Parser) parseFunctionBody() []ast.Node {
3✔
125
        return p.parseBlock(&BlockContext{AllowReturn: true})
3✔
126
}
3✔
127

128
// Parse a function definition
129
func (p *Parser) parseFunctionDefinition() ast.FunctionNode {
3✔
130
        p.expect(ast.TokenFunction)           // Expect `fn`
3✔
131
        name := p.expect(ast.TokenIdentifier) // Function name
3✔
132

3✔
133
        p.context.Scope.functionName = name.Value
3✔
134

3✔
135
        params := p.parseFunctionSignature() // Parse function parameters
3✔
136

3✔
137
        returnType := p.parseReturnType()
3✔
138

3✔
139
        body := p.parseFunctionBody()
3✔
140

3✔
141
        node := ast.FunctionNode{
3✔
142
                Ident:       ast.Ident{ID: ast.Identifier(name.Value)},
3✔
143
                ReturnTypes: returnType,
3✔
144
                Params:      params,
3✔
145
                Body:        body,
3✔
146
        }
3✔
147

3✔
148
        return node
3✔
149
}
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc