basic for loop + examples -> era3

This commit is contained in:
Neemek 2026-07-13 10:01:13 +02:00
parent dd7af341a3
commit 0bc8b2ef55
Signed by: neemek
GPG key ID: 84FFE4D7D40AB25E
24 changed files with 222 additions and 201 deletions

View file

@ -153,6 +153,7 @@ func (c *Compiler) add(instruction Bytecode) {
c.advance(1)
}
// addConstant add both a constant (if it is not already defined), and add the index of it to the bytecode
func (c *Compiler) addConstant(value Value) {
chunk := c.Chunk
for i := 0; i < len(chunk.Constants); i++ {
@ -165,6 +166,10 @@ func (c *Compiler) addConstant(value Value) {
chunk.Constants = append(chunk.Constants, value)
if len(chunk.Constants) > 256 {
panic("too many constants (>256)")
}
c.add(Bytecode(len(chunk.Constants) - 1))
}
@ -472,7 +477,6 @@ func (c *Compiler) compile(tree Node) (TypeSignature, error) {
c.add(InstructionJumpFalse)
jumpValuePos = c.ip
c.advance(2)
}
c.add(InstructionPop)
@ -492,6 +496,74 @@ func (c *Compiler) compile(tree Node) (TypeSignature, error) {
return &CompositeSignature{dt, &NilSignature{}}, nil
case ForNodeType:
n := tree.(*ForNode)
is, err := c.compile(n.iterator)
if err != nil {
return nil, err
}
iteratorSignature := &FunctionSignature{
[]TypeSignature{},
&TupleSignature{
[]TypeSignature{
&AnySignature{},
&BooleanSignature{},
},
},
}
if !iteratorSignature.Contains(is) {
return nil, c.error(fmt.Sprintf("cannot iterate with non-iterator %s (must be %s)", is, iteratorSignature), n.iterator)
}
outputSig := is.(*FunctionSignature).Out.(*TupleSignature).Contents[0]
ipos := c.ip
c.addDescend()
c.add(InstructionDuplicate)
c.add(InstructionCall)
c.add(InstructionDestructureTuple)
// if no more items; jump to end
c.add(InstructionJumpFalse)
jmpValuePos := c.ip
c.advance(2)
if n.counter.Type() != ReferenceNodeType {
return nil, c.error("cannot use non-variable as a counter", n.counter)
}
name := n.counter.(*ReferenceNode).name
c.add(InstructionDeclareLocal)
c.addConstant(&StringValue{
name,
})
c.add(InstructionPop)
c.registerVar(name, outputSig)
_, err = c.compile(n.logic)
if err != nil {
return nil, err
}
c.add(InstructionPop)
c.addAscend()
c.add(InstructionLoop)
c.addU16(uint16(c.ip - ipos + 2))
// end of loop
c.putU16(jmpValuePos, uint16(c.ip-jmpValuePos-2))
c.add(InstructionPop)
c.add(InstructionPop)
c.add(InstructionNil)
return &NilSignature{}, nil
case AssignNodeType:
n := tree.(*AssignNode)

View file

@ -57,6 +57,8 @@ const (
TokenElse
TokenImport
TokenType
TokenFor
TokenIn
TokenComma
TokenDot
@ -178,6 +180,10 @@ func (t TokenKind) String() string {
return "newline"
case TokenType:
return "type"
case TokenFor:
return "for"
case TokenIn:
return "in"
}
panic("UNDEFINED TOKENTYPE STRING CONVERSION")
@ -196,6 +202,8 @@ var Keywords = map[string]TokenKind{
"while": TokenWhile,
"breakpoint": TokenBreakpoint,
"type": TokenType,
"for": TokenFor,
"in": TokenIn,
}
type Lexer struct {

View file

@ -34,6 +34,7 @@ const (
BlockNodeType
ConditionalNodeType
LoopNodeType
ForNodeType
AssignNodeType
InvokeNodeType
CallNodeType
@ -501,7 +502,7 @@ func (n ConditionalNode) Bounds() (Pos, Pos) {
return n.start, n.end
}
// LoopNode Loops (for/while)
// LoopNode While loops
type LoopNode struct {
condition Node
do Node
@ -522,6 +523,28 @@ func (n LoopNode) Bounds() (Pos, Pos) {
return n.start, n.end
}
// ForNode For loops
type ForNode struct {
counter Node
iterator Node
logic Node
start Pos
end Pos
}
func (n ForNode) Type() NodeType {
return ForNodeType
}
func (n ForNode) String() string {
return fmt.Sprintf("for %s in %s; %s", n.counter, n.iterator, n.logic)
}
func (n ForNode) Bounds() (Pos, Pos) {
return n.start, n.end
}
// AssignNode assignment
type AssignNode struct {
dest Node

View file

@ -383,6 +383,38 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
p.prev.End,
}, nil
case TokenFor:
p.advance()
start := p.prev.Start
counter, err := p.expression(false)
if err != nil {
return nil, err
}
if err := p.expect(TokenIn, "for-loops must be for each item in an iterator"); err != nil {
return nil, err
}
iterator, err := p.expression(false)
if err != nil {
return nil, err
}
logic, err := p.expression(true)
if err != nil {
return nil, err
}
return &ForNode{
counter,
iterator,
logic,
start,
p.prev.End,
}, nil
default:
s, err := p.binary()
if err != nil {

View file

@ -742,7 +742,7 @@ func (v *BuiltinFunctionValue) Type() ValueType {
}
func (v *BuiltinFunctionValue) String() string {
return fmt.Sprintf("<function name=%s builtin>", v.Name)
return fmt.Sprintf("<function builtin name=%s>", v.Name)
}
func (v *BuiltinFunctionValue) DebugString() string {

View file

@ -133,6 +133,9 @@ const (
// InstructionFormTuple pop n+1 (u16) items from the stack, and create a new tuple with the items. The top value
// on the stack is the last value in the tuple.
InstructionFormTuple
// InstructionDestructureTuple pop a tuple, and push all its items to the stack, with the top item on the stack
// being the last item in the tuple.
InstructionDestructureTuple
// InstructionIndexList index into a list. The lower item is the container, and the top item
// is the index. [..., container, index] -> [..., item]
@ -251,6 +254,8 @@ func (b Bytecode) String() string {
return "INDEX_LIST"
case InstructionIndexTuple:
return "INDEX_TUPLE"
case InstructionDestructureTuple:
return "DESTRUCTURE_TUPLE"
}
return "UNDEFINED"
}
@ -605,10 +610,7 @@ var DefaultGlobals = map[string]Value{
&StringSignature{},
),
},
quickComposite(
&FloatSignature{},
&NilSignature{},
),
&FloatSignature{},
},
func(vm *VM, _ Value, args []Value) (Value, error) {
switch v := args[0].(type) {
@ -620,7 +622,7 @@ var DefaultGlobals = map[string]Value{
case *StringValue:
num, err := strconv.ParseFloat(v.Text, FloatSize)
if err != nil {
return &NilValue{}, nil
return &FloatValue{}, nil
}
return &FloatValue{num}, nil
@ -1014,6 +1016,11 @@ func (vm *VM) Next() bool {
items,
})
case InstructionDestructureTuple:
t := vm.Stack.Pop().(*TupleValue)
vm.Stack.Push(t.Items...)
case InstructionDescend:
vm.descend()