mirror of
https://github.com/bearlanguageorg/bear.git
synced 2026-08-26 14:57:18 +00:00
Enhacnementsand toolchain
This commit is contained in:
@@ -0,0 +1,311 @@
|
||||
// assembler.v — assembles VuurRaaf assembly (.vasm) into VROBJ object files.
|
||||
//
|
||||
// Assembly syntax:
|
||||
// ; comment
|
||||
// .global main ; export the label as a linkable symbol
|
||||
// main: ; label definition (local unless .global)
|
||||
// push_int 42
|
||||
// push_str "hello"
|
||||
// call helper 1 ; call <symbol-or-label> <argc>
|
||||
// println
|
||||
// halt
|
||||
//
|
||||
// Jumps (jmp/jz/jnz) target local labels and are resolved here; calls to
|
||||
// exported symbols become relocations resolved by the linker.
|
||||
module assembler
|
||||
|
||||
import os
|
||||
import obj
|
||||
|
||||
pub fn assemble(src string) !obj.Obj {
|
||||
lines := src.split_into_lines()
|
||||
// pass 1: collect labels, globals, and code offsets
|
||||
mut labels := map[string]int{}
|
||||
mut globals := map[string]bool{}
|
||||
mut offset := 0
|
||||
for raw in lines {
|
||||
line := clean(raw)
|
||||
if line.len == 0 {
|
||||
continue
|
||||
}
|
||||
if line.starts_with('.global') {
|
||||
name := line.all_after('.global').trim_space()
|
||||
if name.len == 0 {
|
||||
return error('`.global` needs a name')
|
||||
}
|
||||
globals[name] = true
|
||||
continue
|
||||
}
|
||||
if line.ends_with(':') {
|
||||
name := line[..line.len - 1].trim_space()
|
||||
if name in labels {
|
||||
return error('duplicate label "${name}"')
|
||||
}
|
||||
labels[name] = offset
|
||||
continue
|
||||
}
|
||||
offset += instr_len(line)!
|
||||
}
|
||||
// pass 2: emit code
|
||||
mut o := obj.Obj{}
|
||||
for raw in lines {
|
||||
line := clean(raw)
|
||||
if line.len == 0 || line.starts_with('.global') || line.ends_with(':') {
|
||||
continue
|
||||
}
|
||||
parts := split_line(line)!
|
||||
op := parts[0]
|
||||
arg := if parts.len > 1 { parts[1] } else { '' }
|
||||
match op {
|
||||
'halt' {
|
||||
o.code << u8(0)
|
||||
}
|
||||
'push_int' {
|
||||
val := parse_int(arg, 'push_int')!
|
||||
o.code << u8(1)
|
||||
o.code << obj.encode_i64(val)
|
||||
}
|
||||
'push_str' {
|
||||
s := unquote(arg)!
|
||||
o.code << u8(2)
|
||||
o.code << obj.encode_i64(0) // placeholder — rebased by the linker
|
||||
o.relocs << obj.Reloc{ offset: u32(o.code.len) - 8, name: s, kind: 1 }
|
||||
}
|
||||
'load' {
|
||||
o.code << u8(3)
|
||||
o.code << obj.encode_i64(parse_int(arg, 'load')!)
|
||||
}
|
||||
'store' {
|
||||
o.code << u8(4)
|
||||
o.code << obj.encode_i64(parse_int(arg, 'store')!)
|
||||
}
|
||||
'pop' {
|
||||
o.code << u8(5)
|
||||
}
|
||||
'dup' {
|
||||
o.code << u8(6)
|
||||
}
|
||||
'add' {
|
||||
o.code << u8(7)
|
||||
}
|
||||
'sub' {
|
||||
o.code << u8(8)
|
||||
}
|
||||
'mul' {
|
||||
o.code << u8(9)
|
||||
}
|
||||
'div' {
|
||||
o.code << u8(10)
|
||||
}
|
||||
'mod' {
|
||||
o.code << u8(11)
|
||||
}
|
||||
'neg' {
|
||||
o.code << u8(12)
|
||||
}
|
||||
'eq' {
|
||||
o.code << u8(13)
|
||||
}
|
||||
'ne' {
|
||||
o.code << u8(14)
|
||||
}
|
||||
'lt' {
|
||||
o.code << u8(15)
|
||||
}
|
||||
'le' {
|
||||
o.code << u8(16)
|
||||
}
|
||||
'gt' {
|
||||
o.code << u8(17)
|
||||
}
|
||||
'ge' {
|
||||
o.code << u8(18)
|
||||
}
|
||||
'and' {
|
||||
o.code << u8(19)
|
||||
}
|
||||
'or' {
|
||||
o.code << u8(20)
|
||||
}
|
||||
'not' {
|
||||
o.code << u8(21)
|
||||
}
|
||||
'jmp' {
|
||||
o.code << u8(22)
|
||||
o.code << obj.encode_i64(i64(label(labels, arg)!))
|
||||
}
|
||||
'jz' {
|
||||
o.code << u8(23)
|
||||
o.code << obj.encode_i64(i64(label(labels, arg)!))
|
||||
}
|
||||
'jnz' {
|
||||
o.code << u8(24)
|
||||
o.code << obj.encode_i64(i64(label(labels, arg)!))
|
||||
}
|
||||
'call' {
|
||||
o.code << u8(25)
|
||||
o.code << obj.encode_i64(0) // placeholder for the target
|
||||
if arg in labels && arg !in globals {
|
||||
obj.patch_i64(mut o.code, u32(o.code.len) - 8, i64(labels[arg]))
|
||||
} else {
|
||||
// reference to an exported symbol — resolved at link time
|
||||
o.relocs << obj.Reloc{ offset: u32(o.code.len) - 8, name: arg, kind: 0 }
|
||||
}
|
||||
argc := if parts.len > 2 { parse_int(parts[2], 'call argc')! } else { i64(0) }
|
||||
o.code << obj.encode_i64(argc)
|
||||
}
|
||||
'ret' {
|
||||
o.code << u8(26)
|
||||
}
|
||||
'retv' {
|
||||
o.code << u8(27)
|
||||
}
|
||||
'print' {
|
||||
o.code << u8(28)
|
||||
}
|
||||
'println' {
|
||||
o.code << u8(29)
|
||||
}
|
||||
'assert' {
|
||||
o.code << u8(30)
|
||||
}
|
||||
'enter' {
|
||||
o.code << u8(31)
|
||||
o.code << obj.encode_i64(parse_int(arg, 'enter')!)
|
||||
}
|
||||
else {
|
||||
return error('unknown instruction "${op}"')
|
||||
}
|
||||
}
|
||||
}
|
||||
// export the globals as symbols
|
||||
for name in globals.keys() {
|
||||
entry := labels[name] or { return error('.global "${name}" is not a defined label') }
|
||||
o.symbols << obj.Symbol{ name: name, entry: entry }
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
pub fn assemble_file(path string) !obj.Obj {
|
||||
src := os.read_file(path)!
|
||||
return assemble(src)!
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
fn clean(line string) string {
|
||||
// strip ';' comments and trim
|
||||
if idx := line.index(';') {
|
||||
return line[..idx].trim_space()
|
||||
}
|
||||
return line.trim_space()
|
||||
}
|
||||
|
||||
// split_line splits an assembly line into whitespace-separated fields while
|
||||
// keeping a double-quoted string (which may contain spaces) as a single field.
|
||||
fn split_line(line string) ![]string {
|
||||
mut parts := []string{}
|
||||
mut i := 0
|
||||
for i < line.len {
|
||||
for i < line.len && (line[i] == ` ` || line[i] == `\t`) {
|
||||
i++
|
||||
}
|
||||
if i >= line.len {
|
||||
break
|
||||
}
|
||||
if line[i] == `"` {
|
||||
// scan to the closing quote
|
||||
start := i
|
||||
i++
|
||||
for i < line.len && line[i] != `"` {
|
||||
i++
|
||||
}
|
||||
i++
|
||||
parts << line[start..if i <= line.len { i } else { line.len }]
|
||||
} else {
|
||||
start := i
|
||||
for i < line.len && line[i] != ` ` && line[i] != `\t` {
|
||||
i++
|
||||
}
|
||||
parts << line[start..i]
|
||||
}
|
||||
}
|
||||
return parts
|
||||
}
|
||||
|
||||
fn instr_len(line string) !int {
|
||||
parts := split_line(line)!
|
||||
op := parts[0]
|
||||
match op {
|
||||
'halt', 'pop', 'dup', 'add', 'sub', 'mul', 'div', 'mod', 'neg', 'eq', 'ne', 'lt', 'le',
|
||||
'gt', 'ge', 'and', 'or', 'not', 'ret', 'retv', 'print', 'println', 'assert' {
|
||||
return 1
|
||||
}
|
||||
'push_int', 'push_str', 'load', 'store', 'jmp', 'jz', 'jnz', 'enter' {
|
||||
return 9
|
||||
}
|
||||
'call' {
|
||||
return 17 // opcode + target(8) + argc(8)
|
||||
}
|
||||
else {
|
||||
return error('unknown instruction "${op}"')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn label(labels map[string]int, name string) !int {
|
||||
return labels[name] or { return error('unknown label "${name}"') }
|
||||
}
|
||||
|
||||
fn parse_int(s string, what string) !i64 {
|
||||
if s.len == 0 {
|
||||
return error('${what}: bad integer "${s}"')
|
||||
}
|
||||
start := if s[0] == `-` { 1 } else { 0 }
|
||||
if start >= s.len {
|
||||
return error('${what}: bad integer "${s}"')
|
||||
}
|
||||
for i in start..s.len {
|
||||
if s[i] < `0` || s[i] > `9` {
|
||||
return error('${what}: bad integer "${s}"')
|
||||
}
|
||||
}
|
||||
return s.i64()
|
||||
}
|
||||
|
||||
fn unquote(s string) !string {
|
||||
if s.len < 2 || s[0] != `"` || s[s.len - 1] != `"` {
|
||||
return error('expected a string literal, got "${s}"')
|
||||
}
|
||||
inner := s[1..s.len - 1]
|
||||
mut out := ''
|
||||
mut i := 0
|
||||
for i < inner.len {
|
||||
c := inner[i]
|
||||
if c == `\\` && i + 1 < inner.len {
|
||||
i++
|
||||
match inner[i] {
|
||||
`n` {
|
||||
out += '\n'
|
||||
}
|
||||
`t` {
|
||||
out += '\t'
|
||||
}
|
||||
`"` {
|
||||
out += '"'
|
||||
}
|
||||
`\\` {
|
||||
out += '\\'
|
||||
}
|
||||
else {
|
||||
out += '\\${inner[i].ascii_str()}'
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out += c.ascii_str()
|
||||
}
|
||||
i++
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user