diff --git a/compiler/bytecode.go b/compiler/bytecode.go index cb45384..2bfd41f 100644 --- a/compiler/bytecode.go +++ b/compiler/bytecode.go @@ -232,3 +232,18 @@ func GetOperandCount(op Opcode) int { func InstructionSize(op Opcode) int { 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", +} diff --git a/compiler/compiler.go b/compiler/compiler.go index 9c39a07..ffbf77a 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -38,6 +38,11 @@ func (c *Compiler) Compile(program *parser.Program) (*Chunk, []CompileError) { // Statement compilation 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) { case *parser.StructStatement: c.compileStructStatement(s) @@ -72,6 +77,10 @@ func (c *Compiler) compileStatement(stmt parser.Statement) { // Expression compilation func (c *Compiler) compileExpression(expr parser.Expression) { + if lineNode := c.getLineFromNode(expr); lineNode != 0 { + c.current.SetLine(lineNode) + } + switch e := expr.(type) { case *parser.Identifier: c.compileIdentifier(e) @@ -836,14 +845,94 @@ func (c *Compiler) findStructFieldIndex(structID uint16, fieldName string) int { return -1 } +// Enhanced error reporting func (c *Compiler) addError(message string) { c.errors = append(c.errors, CompileError{ Message: message, - Line: 0, // TODO: Add line tracking - Column: 0, // TODO: Add column tracking + Line: c.current.CurrentLine, + Column: 0, // Column tracking would need more work }) } // Error reporting func (c *Compiler) Errors() []CompileError { return c.errors } 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" + } +} diff --git a/compiler/state.go b/compiler/state.go index bfe9823..2e6c98b 100644 --- a/compiler/state.go +++ b/compiler/state.go @@ -24,6 +24,7 @@ type CompilerState struct { LoopStart int // Start of current loop for continue LoopDepth int // Current loop nesting depth parent *CompilerState // Parent compiler state for nested functions + CurrentLine int // Current source line being compiled } // Local represents a local variable during compilation @@ -216,7 +217,7 @@ func (cs *CompilerState) valueKey(value Value) string { // Bytecode emission methods func (cs *CompilerState) EmitByte(byte uint8) { 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) { @@ -290,3 +291,7 @@ func (cs *CompilerState) EmitContinue() { cs.ContinueJumps = append(cs.ContinueJumps, jumpOffset) } } + +func (cs *CompilerState) SetLine(line int) { + cs.CurrentLine = line +}