line counting in bytecode

This commit is contained in:
Sky Johnson 2025-06-11 19:09:35 -05:00
parent aee29ac884
commit 2179a39e2c
3 changed files with 112 additions and 3 deletions

View File

@ -232,3 +232,18 @@ func GetOperandCount(op Opcode) int {
func InstructionSize(op Opcode) int { func InstructionSize(op Opcode) int {
return 1 + (GetOperandCount(op) * 2) // 1 byte opcode + 2 bytes per operand return 1 + (GetOperandCount(op) * 2) // 1 byte opcode + 2 bytes per operand
} }
var opcodeNames = map[Opcode]string{
OpLoadConst: "OP_LOAD_CONST",
OpLoadLocal: "OP_LOAD_LOCAL",
OpStoreLocal: "OP_STORE_LOCAL",
OpAdd: "OP_ADD",
OpSub: "OP_SUB",
OpMul: "OP_MUL",
OpDiv: "OP_DIV",
OpJump: "OP_JUMP",
OpJumpIfTrue: "OP_JUMP_TRUE",
OpJumpIfFalse: "OP_JUMP_FALSE",
OpReturn: "OP_RETURN",
OpEcho: "OP_ECHO",
}

View File

@ -38,6 +38,11 @@ func (c *Compiler) Compile(program *parser.Program) (*Chunk, []CompileError) {
// Statement compilation // Statement compilation
func (c *Compiler) compileStatement(stmt parser.Statement) { func (c *Compiler) compileStatement(stmt parser.Statement) {
// Extract line from any statement that has position info
if lineNode := c.getLineFromNode(stmt); lineNode != 0 {
c.current.SetLine(lineNode)
}
switch s := stmt.(type) { switch s := stmt.(type) {
case *parser.StructStatement: case *parser.StructStatement:
c.compileStructStatement(s) c.compileStructStatement(s)
@ -72,6 +77,10 @@ func (c *Compiler) compileStatement(stmt parser.Statement) {
// Expression compilation // Expression compilation
func (c *Compiler) compileExpression(expr parser.Expression) { func (c *Compiler) compileExpression(expr parser.Expression) {
if lineNode := c.getLineFromNode(expr); lineNode != 0 {
c.current.SetLine(lineNode)
}
switch e := expr.(type) { switch e := expr.(type) {
case *parser.Identifier: case *parser.Identifier:
c.compileIdentifier(e) c.compileIdentifier(e)
@ -836,14 +845,94 @@ func (c *Compiler) findStructFieldIndex(structID uint16, fieldName string) int {
return -1 return -1
} }
// Enhanced error reporting
func (c *Compiler) addError(message string) { func (c *Compiler) addError(message string) {
c.errors = append(c.errors, CompileError{ c.errors = append(c.errors, CompileError{
Message: message, Message: message,
Line: 0, // TODO: Add line tracking Line: c.current.CurrentLine,
Column: 0, // TODO: Add column tracking Column: 0, // Column tracking would need more work
}) })
} }
// Error reporting // Error reporting
func (c *Compiler) Errors() []CompileError { return c.errors } func (c *Compiler) Errors() []CompileError { return c.errors }
func (c *Compiler) HasErrors() bool { return len(c.errors) > 0 } func (c *Compiler) HasErrors() bool { return len(c.errors) > 0 }
// Helper to extract line info from AST nodes
func (c *Compiler) getLineFromNode(node any) int {
// Since AST nodes don't store position, we'd need to modify the parser
// For now, track during compilation by passing line info through
return 0 // Placeholder
}
// Bytecode disassembler helper for debugging
func DisassembleInstruction(chunk *Chunk, offset int) int {
if offset >= len(chunk.Code) {
return offset
}
line := 0
if offset < len(chunk.Lines) {
line = chunk.Lines[offset]
}
fmt.Printf("%04d ", offset)
if offset > 0 && line == chunk.Lines[offset-1] {
fmt.Print(" | ")
} else {
fmt.Printf("%4d ", line)
}
op := Opcode(chunk.Code[offset])
fmt.Printf("%-16s", opcodeNames[op])
switch op {
case OpLoadConst, OpLoadLocal, OpStoreLocal:
operand := uint16(chunk.Code[offset+1]) | (uint16(chunk.Code[offset+2]) << 8)
fmt.Printf(" %4d", operand)
if op == OpLoadConst && int(operand) < len(chunk.Constants) {
fmt.Printf(" '")
printValue(chunk.Constants[operand])
fmt.Printf("'")
}
return offset + 3
case OpJump, OpJumpIfTrue, OpJumpIfFalse:
jump := uint16(chunk.Code[offset+1]) | (uint16(chunk.Code[offset+2]) << 8)
fmt.Printf(" %4d -> %d", jump, offset+3+int(jump))
return offset + 3
default:
return offset + 1
}
}
func printValue(value Value) {
switch value.Type {
case ValueNil:
fmt.Print("nil")
case ValueBool:
fmt.Print(value.Data.(bool))
case ValueNumber:
fmt.Printf("%.2f", value.Data.(float64))
case ValueString:
fmt.Print(value.Data.(string))
default:
fmt.Printf("<%s>", valueTypeString(value.Type))
}
}
func valueTypeString(vt ValueType) string {
switch vt {
case ValueTable:
return "table"
case ValueFunction:
return "function"
case ValueStruct:
return "struct"
case ValueArray:
return "array"
case ValueUpvalue:
return "upvalue"
default:
return "unknown"
}
}

View File

@ -24,6 +24,7 @@ type CompilerState struct {
LoopStart int // Start of current loop for continue LoopStart int // Start of current loop for continue
LoopDepth int // Current loop nesting depth LoopDepth int // Current loop nesting depth
parent *CompilerState // Parent compiler state for nested functions parent *CompilerState // Parent compiler state for nested functions
CurrentLine int // Current source line being compiled
} }
// Local represents a local variable during compilation // Local represents a local variable during compilation
@ -216,7 +217,7 @@ func (cs *CompilerState) valueKey(value Value) string {
// Bytecode emission methods // Bytecode emission methods
func (cs *CompilerState) EmitByte(byte uint8) { func (cs *CompilerState) EmitByte(byte uint8) {
cs.Chunk.Code = append(cs.Chunk.Code, byte) cs.Chunk.Code = append(cs.Chunk.Code, byte)
cs.Chunk.Lines = append(cs.Chunk.Lines, 0) // Line will be set by caller cs.Chunk.Lines = append(cs.Chunk.Lines, cs.CurrentLine)
} }
func (cs *CompilerState) EmitBytes(bytes ...uint8) { func (cs *CompilerState) EmitBytes(bytes ...uint8) {
@ -290,3 +291,7 @@ func (cs *CompilerState) EmitContinue() {
cs.ContinueJumps = append(cs.ContinueJumps, jumpOffset) cs.ContinueJumps = append(cs.ContinueJumps, jumpOffset)
} }
} }
func (cs *CompilerState) SetLine(line int) {
cs.CurrentLine = line
}