mirror of
https://github.com/bearlanguageorg/bear.git
synced 2026-08-26 14:57:18 +00:00
Production pass: floats, GC, type checking, and tooling
Adds floats, bitwise ops, UTF-8 strings with methods, try/catch, closures, generics validation, a compile-time type checker, a mark-and-sweep GC, source-level debug info, constant folding, and the repl/fmt/package-manager commands. 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
// repl.v — an interactive read-eval-print loop for VuurRaaf.
|
||||
//
|
||||
// The REPL keeps two growing buffers: top-level declarations (fn / struct /
|
||||
// enum / const / import / interface) and persistent statements (let, assert,
|
||||
// assignment). Every other line — expressions, println, if/while/for blocks,
|
||||
// match, try — is transient: it is evaluated once against the accumulated
|
||||
// state and its output is not repeated on later lines. Expressions are
|
||||
// echoed like Python's REPL (1 + 1 prints 2); statement keywords run for
|
||||
// their side effects.
|
||||
//
|
||||
// > let x = 5
|
||||
// > x * 2
|
||||
// 10
|
||||
// > fn square(n) { return n * n }
|
||||
// > square(9)
|
||||
// 81
|
||||
//
|
||||
// Multi-line input is gathered while braces are unbalanced:
|
||||
//
|
||||
// > fn fib(n) {
|
||||
// ... if n < 2 { return n }
|
||||
// ... return fib(n - 1) + fib(n - 2)
|
||||
// ... }
|
||||
module main
|
||||
|
||||
import os
|
||||
import obj
|
||||
import compiler
|
||||
import linker
|
||||
import vm
|
||||
|
||||
fn toolchain_repl() ! {
|
||||
println('VuurRaaf ${version} — type :help for commands, :quit to exit')
|
||||
mut top := '' // fn / struct / enum / const / import declarations
|
||||
mut body := '' // persistent statements (let / assert / assignment)
|
||||
mut pending := '' // accumulated multi-line input
|
||||
mut depth := 0 // brace balance of pending input
|
||||
for {
|
||||
prompt := if depth > 0 { '... ' } else { '> ' }
|
||||
line := os.input(prompt)
|
||||
if line == '' {
|
||||
continue
|
||||
}
|
||||
trimmed := line.trim_space()
|
||||
if trimmed.starts_with(':') && depth == 0 {
|
||||
act := repl_meta(trimmed)
|
||||
if act == 1 {
|
||||
break
|
||||
}
|
||||
if act == 2 {
|
||||
top = ''
|
||||
body = ''
|
||||
println('session reset')
|
||||
}
|
||||
continue
|
||||
}
|
||||
// gather multi-line input until braces balance
|
||||
pending += line + '\n'
|
||||
depth += brace_delta(line)
|
||||
if depth > 0 {
|
||||
continue
|
||||
}
|
||||
block := pending
|
||||
pending = ''
|
||||
depth = 0
|
||||
match repl_classify(block) {
|
||||
'top' {
|
||||
// fn / struct / enum / const / import — compile-time only
|
||||
candidate := top + '\n' + block
|
||||
if repl_compile(candidate, body, '') {
|
||||
top = candidate
|
||||
}
|
||||
}
|
||||
'body' {
|
||||
candidate := body + '\n' + block
|
||||
if repl_compile(top, candidate, '') {
|
||||
body = candidate
|
||||
}
|
||||
}
|
||||
else {
|
||||
// transient: run once against the accumulated state
|
||||
tail := if repl_is_expr(block) { 'println(${block.trim_space()})' } else { block }
|
||||
repl_compile(top, body, tail)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// repl_meta handles :quit / :reset / :help. Returns 1 to quit, 2 to reset,
|
||||
// and 0 otherwise.
|
||||
fn repl_meta(cmd string) int {
|
||||
match cmd {
|
||||
':q', ':quit', ':exit' {
|
||||
println('bye')
|
||||
return 1
|
||||
}
|
||||
':reset' {
|
||||
return 2
|
||||
}
|
||||
':help', ':h' {
|
||||
println(' :help this help')
|
||||
println(' :quit :exit leave the REPL')
|
||||
println(' :reset clear all definitions')
|
||||
println(' expressions are echoed; let/assign/fn persist')
|
||||
}
|
||||
else {
|
||||
eprintln('repl: unknown command "${cmd}" (:help for a list)')
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// repl_compile compiles top declarations plus a main body (with an optional
|
||||
// trailing tail statement), links, runs, and reports errors without exiting.
|
||||
// Returns true when the program compiled and ran cleanly.
|
||||
fn repl_compile(top string, body string, tail string) bool {
|
||||
src := top + '\nfn main() {\n' + body + '\n' + tail + '\n}'
|
||||
o := compiler.compile(src) or {
|
||||
eprintln('repl: ${err.msg()}')
|
||||
return false
|
||||
}
|
||||
tmp_obj := os.join_path(os.temp_dir(), 'vr_repl_${os.getpid()}.vobj')
|
||||
tmp_bin := os.join_path(os.temp_dir(), 'vr_repl_${os.getpid()}.vbin')
|
||||
defer {
|
||||
os.rm(tmp_obj) or {}
|
||||
os.rm(tmp_bin) or {}
|
||||
}
|
||||
obj.write(tmp_obj, o) or {
|
||||
eprintln('repl: ${err.msg()}')
|
||||
return false
|
||||
}
|
||||
linker.link([tmp_obj], tmp_bin) or {
|
||||
eprintln('repl: ${err.msg()}')
|
||||
return false
|
||||
}
|
||||
bin := obj.read_bin(tmp_bin) or {
|
||||
eprintln('repl: ${err.msg()}')
|
||||
return false
|
||||
}
|
||||
vm.run(bin, 'main', false) or {
|
||||
eprintln('repl: ${err.msg()}')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// repl_classify buckets a (possibly multi-line) input block.
|
||||
fn repl_classify(block string) string {
|
||||
toks := compiler.tokenize(block) or { return 'transient' }
|
||||
if toks.len == 0 {
|
||||
return 'transient'
|
||||
}
|
||||
match toks[0].kind {
|
||||
.kw_fn, .kw_struct, .kw_enum, .kw_const, .kw_import, .kw_interface {
|
||||
return 'top'
|
||||
}
|
||||
.kw_let, .kw_assert {
|
||||
return 'body'
|
||||
}
|
||||
.ident {
|
||||
// assignment persists; anything else is an expression
|
||||
if toks.len >= 2 && toks[1].kind in [.assign, .plus_eq, .minus_eq, .star_eq, .slash_eq] {
|
||||
return 'body'
|
||||
}
|
||||
return 'transient'
|
||||
}
|
||||
else {
|
||||
return 'transient'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// repl_is_expr reports whether a transient block is a bare expression that
|
||||
// should be echoed (as opposed to a statement keyword like println or if).
|
||||
fn repl_is_expr(block string) bool {
|
||||
toks := compiler.tokenize(block) or { return false }
|
||||
if toks.len == 0 {
|
||||
return false
|
||||
}
|
||||
match toks[0].kind {
|
||||
.kw_print, .kw_println, .kw_if, .kw_while, .kw_for, .kw_match, .kw_break, .kw_continue, .kw_return, .kw_try, .kw_throw {
|
||||
return false
|
||||
}
|
||||
else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// brace_delta counts the brace balance of a line using the tokenizer, so
|
||||
// braces inside string literals do not confuse continuation detection.
|
||||
fn brace_delta(line string) int {
|
||||
toks := compiler.tokenize(line) or { return 0 }
|
||||
mut d := 0
|
||||
for t in toks {
|
||||
if t.kind == .lbrace {
|
||||
d++
|
||||
} else if t.kind == .rbrace {
|
||||
d--
|
||||
}
|
||||
}
|
||||
return d
|
||||
}
|
||||
Reference in New Issue
Block a user