mirror of
https://github.com/bearlanguageorg/bear.git
synced 2026-08-26 14:57:18 +00:00
Tier Three: Added threads/jobs
This commit is contained in:
@@ -80,6 +80,7 @@ pub mut:
|
||||
pub struct Stmt {
|
||||
pub mut:
|
||||
kind StmtKind
|
||||
mutable bool // let_stmt/destruct_stmt: bound with `mut` (reassignable)
|
||||
target string
|
||||
idx_target string // for_in_stmt: index variable name (empty when unused)
|
||||
destruct_targets []string // destruct_stmt: names to bind
|
||||
|
||||
+37
-7
@@ -40,14 +40,15 @@ struct FnSig {
|
||||
|
||||
struct Checker {
|
||||
mut:
|
||||
types map[string]TypeInfo // current scope: local name -> type
|
||||
fns map[string]FnSig
|
||||
structs map[string][]string
|
||||
enums map[string][]string
|
||||
consts map[string]TypeInfo
|
||||
types map[string]TypeInfo // current scope: local name -> type
|
||||
mutable map[string]bool // local names bound with `mut`, params, loop vars; only these may be reassigned
|
||||
fns map[string]FnSig
|
||||
structs map[string][]string
|
||||
enums map[string][]string
|
||||
consts map[string]TypeInfo
|
||||
loop_depth int
|
||||
checked map[string]bool // imported files already checked
|
||||
modules map[string]bool // imported module names (bare `import os`)
|
||||
checked map[string]bool // imported files already checked
|
||||
modules map[string]bool // imported module names (bare `import os`)
|
||||
}
|
||||
|
||||
// check validates a parsed program and returns an error on the first problem.
|
||||
@@ -148,9 +149,12 @@ fn (mut c Checker) check_fn(fd FnDecl) ! {
|
||||
// receiver and parameters are untyped (unknown) — the runtime is dynamic
|
||||
if fd.recv_name.len > 0 {
|
||||
c.types[fd.recv_name] = TypeInfo{ kind: .struct_t }
|
||||
c.mutable[fd.recv_name] = true
|
||||
}
|
||||
for p in fd.params {
|
||||
c.types[p] = TypeInfo{ kind: .unknown }
|
||||
// parameters are reassignable: callers pass values, bodies may rewrite
|
||||
c.mutable[p] = true
|
||||
}
|
||||
for st in fd.body {
|
||||
c.check_stmt(st)!
|
||||
@@ -165,11 +169,13 @@ fn (mut c Checker) check_stmt(st Stmt) ! {
|
||||
.let_stmt {
|
||||
t := c.check_expr(st.expr)!
|
||||
c.types[st.target] = t
|
||||
c.mutable[st.target] = st.mutable // false for `let`, true for `mut`
|
||||
}
|
||||
.destruct_stmt {
|
||||
base := c.check_expr(st.expr)!
|
||||
for name in st.destruct_targets {
|
||||
c.types[name] = if st.destruct_field { TypeInfo{ kind: .unknown } } else { TypeInfo{ kind: .unknown } }
|
||||
c.mutable[name] = false
|
||||
}
|
||||
_ = base
|
||||
}
|
||||
@@ -177,6 +183,9 @@ fn (mut c Checker) check_stmt(st Stmt) ! {
|
||||
if st.target !in c.types {
|
||||
return error('unknown variable "${st.target}" (line ${st.line})')
|
||||
}
|
||||
if !(st.target in c.mutable) || !c.mutable[st.target] {
|
||||
return error('cannot reassign immutable "${st.target}" — declare it with `mut` (line ${st.line})')
|
||||
}
|
||||
_ = c.check_expr(st.expr)!
|
||||
}
|
||||
.index_assign {
|
||||
@@ -225,6 +234,7 @@ fn (mut c Checker) check_stmt(st Stmt) ! {
|
||||
c.expect_numeric(start_t, 'range start', st.line)!
|
||||
c.expect_numeric(end_t, 'range end', st.line)!
|
||||
c.types[st.target] = TypeInfo{ kind: .int_t }
|
||||
c.mutable[st.target] = true // the loop advances it; bodies may too
|
||||
c.loop_depth++
|
||||
for s in st.body {
|
||||
c.check_stmt(s)!
|
||||
@@ -243,8 +253,10 @@ fn (mut c Checker) check_stmt(st Stmt) ! {
|
||||
}
|
||||
c.types[st.target] = TypeInfo{ kind: .unknown }
|
||||
}
|
||||
c.mutable[st.target] = true // loop vars are reassigned by iteration
|
||||
if st.idx_target.len > 0 {
|
||||
c.types[st.idx_target] = TypeInfo{ kind: .int_t }
|
||||
c.mutable[st.idx_target] = true
|
||||
}
|
||||
c.loop_depth++
|
||||
for s in st.body {
|
||||
@@ -295,6 +307,10 @@ fn (mut c Checker) check_expr(e Expr) !TypeInfo {
|
||||
c.types[e.name]
|
||||
} else if e.name in c.consts {
|
||||
c.consts[e.name]
|
||||
} else if e.name in c.fns {
|
||||
// a bare function name used as a value (e.g. the first argument
|
||||
// of `spawn`) evaluates to a closure
|
||||
TypeInfo{ kind: .unknown }
|
||||
} else {
|
||||
return error('unknown variable "${e.name}" (line ${e.line})')
|
||||
}
|
||||
@@ -539,6 +555,16 @@ fn (mut c Checker) check_call(e Expr) !TypeInfo {
|
||||
// host builtins (native) — validate arity from the spec table
|
||||
bid, bargc := builtin_spec(e.name)
|
||||
if bid >= 0 {
|
||||
if e.name == 'spawn' {
|
||||
// variadic: a function followed by zero or more arguments
|
||||
if e.args.len < 1 {
|
||||
return error('spawn() expects a function plus zero or more arguments (line ${e.line})')
|
||||
}
|
||||
for a in e.args {
|
||||
_ = c.check_expr(a)!
|
||||
}
|
||||
return builtin_result_type(e.name)
|
||||
}
|
||||
if e.args.len != bargc {
|
||||
return error('${e.name}() takes exactly ${bargc} argument(s) (line ${e.line})')
|
||||
}
|
||||
@@ -601,6 +627,10 @@ fn builtin_result_type(name string) TypeInfo {
|
||||
// stdlib: JSON + string formatting
|
||||
'json_encode', 'format', 'replace', 'pad', 'pad_left', 'repeat' { TypeInfo{ kind: .string_t } }
|
||||
'json_decode', 'split_lines' { TypeInfo{ kind: .unknown } }
|
||||
'sb_new', 'sb_add', 'sb_str' { TypeInfo{ kind: .string_t } }
|
||||
'sb_len' { TypeInfo{ kind: .int_t } }
|
||||
'spawn' { TypeInfo{ kind: .int_t } }
|
||||
'spawn_join' { TypeInfo{ kind: .unknown } }
|
||||
'cwd', 'json_pretty' { TypeInfo{ kind: .string_t } }
|
||||
'build_is_dir' { TypeInfo{ kind: .int_t } }
|
||||
// build-module builtins (.vrmm)
|
||||
|
||||
+44
-5
@@ -45,6 +45,7 @@ mut:
|
||||
enter_off u32
|
||||
next_lbl int
|
||||
modules map[string]bool // imported module names (bare `import os`)
|
||||
fn_names map[string]bool // top-level function names usable as closure values
|
||||
captures []string // enclosing locals captured by the closure being compiled
|
||||
}
|
||||
|
||||
@@ -117,6 +118,10 @@ fn gen(prog Program) !obj.Obj {
|
||||
}
|
||||
g.relocs << obj.Reloc{ offset: u32(code_off) + r.offset, name: rname, kind: r.kind }
|
||||
}
|
||||
// record imported function names so a bare name can be used as a value
|
||||
for s in imported.symbols {
|
||||
g.fn_names[s.name] = true
|
||||
}
|
||||
// merge debug info, rebasing offsets into this object's code space
|
||||
for l in imported.lines {
|
||||
g.lines << obj.LineInfo{ off: u32(code_off) + l.off, line: l.line }
|
||||
@@ -127,6 +132,9 @@ fn gen(prog Program) !obj.Obj {
|
||||
g.dbg_locals << obj.DbgLocal{ fn: prefix + l.fn, name: l.name, slot: l.slot }
|
||||
}
|
||||
}
|
||||
for fd in prog.fns {
|
||||
g.fn_names[fd.name] = true
|
||||
}
|
||||
for fd in prog.fns {
|
||||
g.captures = []string{} // top-level functions capture nothing
|
||||
g.gen_fn(fd)!
|
||||
@@ -800,12 +808,20 @@ fn (mut g Gen) gen_expr(e Expr) ! {
|
||||
// check if it's an enum variant (e.g., Color.red)
|
||||
g.code << op_push_i
|
||||
g.code << obj.encode_i64(i64(g.enum_vals[e.name]))
|
||||
} else {
|
||||
idx := g.locals[e.name] or {
|
||||
return error('unknown variable "${e.name}" at line ${e.line}')
|
||||
}
|
||||
} else if e.name in g.locals {
|
||||
idx := g.locals[e.name]
|
||||
g.code << op_load
|
||||
g.code << obj.encode_i64(i64(idx))
|
||||
} else if e.name in g.fn_names {
|
||||
// a bare top-level function name used as a value (e.g. the first
|
||||
// argument of `spawn`) — emit a zero-capture closure whose entry
|
||||
// is resolved by the linker.
|
||||
g.code << op_closure
|
||||
g.code << obj.encode_i64(0)
|
||||
g.relocs << obj.Reloc{ offset: u32(g.code.len) - 8, name: e.name, kind: 0 }
|
||||
g.code << obj.encode_i64(0) // no captured locals
|
||||
} else {
|
||||
return error('unknown variable "${e.name}" at line ${e.line}')
|
||||
}
|
||||
}
|
||||
.unary {
|
||||
@@ -911,6 +927,20 @@ fn (mut g Gen) gen_call(e Expr) ! {
|
||||
// host builtins (file I/O, OS, math, collections) go through op_native
|
||||
bid, bargc := builtin_spec(e.name)
|
||||
if bid >= 0 {
|
||||
// spawn takes a variable number of arguments (the function followed by
|
||||
// its call arguments), so emit the real argc rather than the spec's
|
||||
if e.name == 'spawn' {
|
||||
if e.args.len < 1 {
|
||||
return error('spawn expects a function plus zero or more arguments (line ${e.line})')
|
||||
}
|
||||
for a in e.args {
|
||||
g.gen_expr(a)!
|
||||
}
|
||||
g.code << op_native
|
||||
g.code << obj.encode_i64(i64(bid))
|
||||
g.code << obj.encode_i64(i64(e.args.len))
|
||||
return
|
||||
}
|
||||
if e.args.len != bargc {
|
||||
return error('${e.name}() takes exactly ${bargc} argument(s) (line ${e.line})')
|
||||
}
|
||||
@@ -1002,6 +1032,14 @@ fn builtin_spec(name string) (int, int) {
|
||||
'pad' { native_pad, 2 }
|
||||
'pad_left' { native_pad_left, 2 }
|
||||
'repeat' { native_repeat, 2 }
|
||||
// string builder
|
||||
'sb_new' { native_sb_new, 0 }
|
||||
'sb_add' { native_sb_add, 2 }
|
||||
'sb_str' { native_sb_str, 1 }
|
||||
'sb_len' { native_sb_len, 1 }
|
||||
// concurrency
|
||||
'spawn' { native_spawn, 1 }
|
||||
'spawn_join' { native_spawn_join, 1 }
|
||||
'build_is_dir' { native_build_is_dir, 1 }
|
||||
'cwd' { native_cwd, 0 }
|
||||
'json_pretty' { native_json_pretty, 1 }
|
||||
@@ -1261,7 +1299,8 @@ fn (mut g Gen) expr_type(e Expr) string {
|
||||
'upper', 'lower', 'trim', 'str', 'getenv', 'read_file', 'join' { 'string' }
|
||||
'build_compile', 'build_assemble', 'build_link', 'build_exec', 'build_base',
|
||||
'build_dir', 'build_join', 'build_root' { 'string' }
|
||||
'json_encode', 'format', 'replace', 'pad', 'pad_left', 'repeat' { 'string' }
|
||||
'json_encode', 'format', 'replace', 'pad', 'pad_left', 'repeat', 'sb_new', 'sb_add', 'sb_str' { 'string' }
|
||||
'sb_len' { 'int' }
|
||||
'cwd', 'json_pretty' { 'string' }
|
||||
else { '' }
|
||||
}
|
||||
|
||||
@@ -285,6 +285,7 @@ fn (mut l Lexer) lex_ident(line int, col int) Tok {
|
||||
'fn' { TokKind.kw_fn }
|
||||
'struct' { TokKind.kw_struct }
|
||||
'let' { TokKind.kw_let }
|
||||
'mut' { TokKind.kw_mut }
|
||||
'if' { TokKind.kw_if }
|
||||
'else' { TokKind.kw_else }
|
||||
'while' { TokKind.kw_while }
|
||||
|
||||
@@ -172,3 +172,13 @@ const native_path_abs = 185
|
||||
const native_path_rel = 186
|
||||
const native_exec_full = 187
|
||||
const native_weekday = 188
|
||||
|
||||
// string builder (efficient repeated concatenation)
|
||||
const native_sb_new = 189
|
||||
const native_sb_add = 190
|
||||
const native_sb_str = 191
|
||||
const native_sb_len = 192
|
||||
|
||||
// concurrency: spawn/join threads
|
||||
const native_spawn = 193
|
||||
const native_spawn_join = 194
|
||||
|
||||
+5
-3
@@ -315,9 +315,11 @@ fn (mut p Parser) parse_block() ![]Stmt {
|
||||
fn (mut p Parser) parse_stmt() !Stmt {
|
||||
t := p.cur()
|
||||
match t.kind {
|
||||
.kw_let {
|
||||
.kw_let, .kw_mut {
|
||||
mutable := t.kind == .kw_mut
|
||||
p.advance()
|
||||
// destructuring let: `let { a, b } = expr` or `let [a, b] = expr`
|
||||
// mutable with destructuring is not supported (kept simple); `let {
|
||||
// a, b } = x` and `let [a, b] = x` stay immutable-style bindings
|
||||
if p.cur().kind == .lbrace || p.cur().kind == .lbracket {
|
||||
is_field := p.cur().kind == .lbrace
|
||||
p.advance()
|
||||
@@ -344,7 +346,7 @@ fn (mut p Parser) parse_stmt() !Stmt {
|
||||
name := p.expect(.ident, 'variable name')!
|
||||
p.expect(.assign, "'='")!
|
||||
e := p.parse_expr()!
|
||||
return Stmt{ kind: .let_stmt, target: name.lit, expr: e, line: t.line }
|
||||
return Stmt{ kind: .let_stmt, target: name.lit, expr: e, mutable: mutable, line: t.line }
|
||||
} .kw_if {
|
||||
return p.parse_if(t)!
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ pub enum TokKind {
|
||||
kw_fn
|
||||
kw_struct
|
||||
kw_let
|
||||
kw_mut
|
||||
kw_if
|
||||
kw_else
|
||||
kw_while
|
||||
|
||||
Reference in New Issue
Block a user