turn Assign into a statement

This commit is contained in:
2026-05-28 08:49:18 +02:00
parent 45253bf004
commit fa5ff0aaa5
6 changed files with 129 additions and 136 deletions

View File

@@ -290,7 +290,7 @@ func main[argc: i64, argv: ptr] : i64
c := chip8_create()
buffer : io.Buffer = must(io.read_binary_file?(path))
buffer : Blob = must(io.read_binary_file?(path))
for i in 0..buffer->size
c->memory[0x200 + i] = buffer->data[i]

View File

@@ -236,6 +236,45 @@ _builtin_environ:
let offset = env.define_var(name.lexeme.clone(), var_type);
emit!(&mut self.output, " mov QWORD [rbp-{}], rax", offset);
}
Stmt::Assign { left, op, value } => {
self.compile_expr(env, value)?;
match &left.kind {
ExprKind::Variable(name) => {
// already ensured by the typechecker
let var = env.get_var(&name.lexeme).unwrap();
emit!(
&mut self.output,
" mov QWORD [rbp-{}], rax",
var.stack_offset,
);
}
ExprKind::Index {
expr,
bracket: _,
index,
} => {
emit!(&mut self.output, " push rax");
self.compile_expr(env, expr)?;
emit!(&mut self.output, " push rax");
self.compile_expr(env, index)?;
emit!(&mut self.output, " pop rbx");
emit!(&mut self.output, " add rbx, rax");
emit!(&mut self.output, " pop rax");
emit!(&mut self.output, " mov BYTE [rbx], al");
}
ExprKind::MemberAccess { left, field } => {
emit!(&mut self.output, " push rax");
let offset = self.get_field_offset(left, field)?;
self.compile_expr(env, left)?;
emit!(&mut self.output, " pop rbx");
emit!(&mut self.output, " mov QWORD [rax+{}], rbx", offset);
}
_ => return error!(&op.loc, "invalid assignment target"),
};
}
Stmt::Const { name: _, value: _ } => {
// handled in SymbolTable
}
@@ -573,45 +612,6 @@ _builtin_environ:
);
}
}
ExprKind::Assign { left, op, value } => {
self.compile_expr(env, value)?;
match &left.kind {
ExprKind::Variable(name) => {
// already ensured by the typechecker
let var = env.get_var(&name.lexeme).unwrap();
emit!(
&mut self.output,
" mov QWORD [rbp-{}], rax",
var.stack_offset,
);
}
ExprKind::Index {
expr,
bracket: _,
index,
} => {
emit!(&mut self.output, " push rax");
self.compile_expr(env, expr)?;
emit!(&mut self.output, " push rax");
self.compile_expr(env, index)?;
emit!(&mut self.output, " pop rbx");
emit!(&mut self.output, " add rbx, rax");
emit!(&mut self.output, " pop rax");
emit!(&mut self.output, " mov BYTE [rbx], al");
}
ExprKind::MemberAccess { left, field } => {
emit!(&mut self.output, " push rax");
let offset = self.get_field_offset(left, field)?;
self.compile_expr(env, left)?;
emit!(&mut self.output, " pop rbx");
emit!(&mut self.output, " mov QWORD [rax+{}], rbx", offset);
}
_ => return error!(&op.loc, "invalid assignment target"),
};
}
ExprKind::Call {
callee,
paren: _,

View File

@@ -22,6 +22,11 @@ pub enum Stmt {
var_type: Option<Token>,
initializer: Expr,
},
Assign {
left: Expr,
op: Token,
value: Expr,
},
Const {
name: Token,
value: Token,
@@ -101,11 +106,6 @@ pub enum ExprKind {
right: Box<Expr>,
},
Variable(Token),
Assign {
left: Box<Expr>,
op: Token,
value: Box<Expr>,
},
Call {
callee: Box<Expr>,
paren: Token,
@@ -312,7 +312,18 @@ impl Parser {
} else if self.match_token(&[TokenType::KeywordContinue]) {
Ok(Stmt::Continue)
} else {
Ok(Stmt::Expression(self.expression()?))
let expr = self.expression()?;
if self.match_token(&[TokenType::Equal]) {
let op = self.previous().clone();
let value = self.expression()?;
Ok(Stmt::Assign {
left: expr,
op,
value,
})
} else {
Ok(Stmt::Expression(expr))
}
}
}
@@ -365,24 +376,7 @@ impl Parser {
}
fn expression(&mut self) -> Result<Expr, ZernError> {
self.assignment()
}
fn assignment(&mut self) -> Result<Expr, ZernError> {
let expr = self.pipe()?;
if self.match_token(&[TokenType::Equal]) {
let equals = self.previous().clone();
let value = self.assignment()?;
return Ok(Expr::new(ExprKind::Assign {
left: Box::new(expr),
op: equals,
value: Box::new(value),
}));
}
Ok(expr)
self.pipe()
}
fn pipe(&mut self) -> Result<Expr, ZernError> {

View File

@@ -156,9 +156,9 @@ const DNS_TYPE_A = 1
const DNS_CLASS_IN = 1
const DNS_RECURSION_DESIRED = 256
func net.encode_dns_name[domain: str] : io.Buffer
func net.encode_dns_name[domain: str] : Blob
domain_len := str.len(domain)
buf : io.Buffer = must(io.Buffer.alloc?(domain_len + 2))
buf : Blob = must(Blob.alloc?(domain_len + 2))
out_pos := 0
part_start := 0
i := 0
@@ -175,9 +175,9 @@ func net.encode_dns_name[domain: str] : io.Buffer
buf->data[out_pos] = 0
return buf
func net.build_dns_query[domain_name: str, record_type: i64] : io.Buffer
func net.build_dns_query[domain_name: str, record_type: i64] : Blob
name := net.encode_dns_name(domain_name)
out : io.Buffer = must(io.Buffer.alloc?(12 + name->size + 4))
out : Blob = must(Blob.alloc?(12 + name->size + 4))
// header
id := math.abs(os.urandom_i64() % 65536)
@@ -193,7 +193,7 @@ func net.build_dns_query[domain_name: str, record_type: i64] : io.Buffer
mem.write16be(out->data + 12 + name->size, record_type)
mem.write16be(out->data + 12 + name->size + 2, DNS_CLASS_IN)
io.Buffer.free(name)
Blob.free(name)
return out
// TODO: dont resolve if its already an IP
@@ -207,7 +207,7 @@ func net.resolve?[domain: str] : i64
return -1
net.udp_send_to(s, s->addr, query->data, query->size)
io.Buffer.free(query)
Blob.free(query)
pkt := net.udp_receive(s, 1024)
net.UDPSocket.close_and_free(s)

View File

@@ -333,21 +333,21 @@ func io.read_line[]: str
buffer = must(mem.realloc?(buffer as ptr, n + 1))
return buffer
struct io.Buffer
struct Blob
data: ptr
size: i64
func io.Buffer.alloc?[size: i64] : io.Buffer
func Blob.alloc?[size: i64] : Blob
err.clear()
buffer := new* io.Buffer
buffer := new* Blob
buffer->size = size
buffer->data = mem.alloc?(size)
if err.check()
mem.free(buffer)
return 0 as io.Buffer
return 0 as Blob
return buffer
func io.Buffer.free[buf: io.Buffer] : void
func Blob.free[buf: Blob] : void
mem.free(buf->data)
mem.free(buf)
@@ -396,30 +396,30 @@ func io.read_text_file?[path: str] : str
buffer[n] = 0
return buffer
func io.read_binary_file?[path: str] : io.Buffer
func io.read_binary_file?[path: str] : Blob
err.clear()
fd := _builtin_syscall(SYS_openat, -100, path, 0, 0)
if fd < 0
err.set(ERR_OPEN_FAILED, "io.read_binary_file?: failed to open file")
return 0 as io.Buffer
return 0 as Blob
buf := new* io.Buffer
buf := new* Blob
buf->size = _builtin_syscall(SYS_lseek, fd, 0, 2)
_builtin_syscall(SYS_lseek, fd, 0, 0)
buf->data = mem.alloc?(buf->size)
if err.check()
io.Buffer.free(buf)
Blob.free(buf)
_builtin_syscall(SYS_close, fd)
return 0 as io.Buffer
return 0 as Blob
n := _builtin_syscall(SYS_read, fd, buf->data, buf->size)
_builtin_syscall(SYS_close, fd)
if n != buf->size
io.Buffer.free(buf)
Blob.free(buf)
err.set(ERR_READ_FAILED, "io.read_binary_file?: failed to read file")
return 0 as io.Buffer
return 0 as Blob
return buf

View File

@@ -111,6 +111,59 @@ impl<'a> TypeChecker<'a> {
env.define_var(name.lexeme.clone(), actual_type);
}
Stmt::Assign { left, op, value } => {
let value_type = self.typecheck_expr(env, value)?;
match &left.kind {
ExprKind::Variable(name) => {
let existing_var_type = match env.get_var_type(&name.lexeme) {
Some(x) => x,
None => {
return error!(
name.loc,
format!("undefined variable: {}", &name.lexeme)
);
}
};
expect_type!(value_type.clone(), *existing_var_type, name.loc);
}
ExprKind::Index {
expr,
bracket,
index,
} => {
expect_types!(self.typecheck_expr(env, expr)?, ["ptr", "str"], bracket.loc);
expect_types!(self.typecheck_expr(env, index)?, ["i64", "u8"], bracket.loc);
expect_types!(value_type.clone(), ["u8", "i64"], bracket.loc);
}
ExprKind::MemberAccess { left, field } => {
let left_type = self.typecheck_expr(env, left)?;
let fields = match self.symbol_table.structs.get(&left_type) {
Some(f) => f,
None => {
return error!(
&field.loc,
format!("unknown struct type: {}", left_type)
);
}
};
let f = match fields.get(&field.lexeme) {
Some(o) => o,
None => {
return error!(
&field.loc,
format!("unknown field: {}", &field.lexeme)
);
}
};
expect_type!(value_type.clone(), f.field_type, field.loc);
}
_ => return error!(&op.loc, "invalid assignment target"),
}
}
Stmt::Const { name: _, value: _ } => {
// handled in SymbolTable
}
@@ -354,60 +407,6 @@ impl<'a> TypeChecker<'a> {
}
}
}
ExprKind::Assign { left, op, value } => {
let value_type = self.typecheck_expr(env, value)?;
match &left.kind {
ExprKind::Variable(name) => {
let existing_var_type = match env.get_var_type(&name.lexeme) {
Some(x) => x,
None => {
return error!(
name.loc,
format!("undefined variable: {}", &name.lexeme)
);
}
};
expect_type!(value_type.clone(), *existing_var_type, name.loc);
}
ExprKind::Index {
expr,
bracket,
index,
} => {
expect_types!(self.typecheck_expr(env, expr)?, ["ptr", "str"], bracket.loc);
expect_types!(self.typecheck_expr(env, index)?, ["i64", "u8"], bracket.loc);
expect_types!(value_type.clone(), ["u8", "i64"], bracket.loc);
}
ExprKind::MemberAccess { left, field } => {
let left_type = self.typecheck_expr(env, left)?;
let fields = match self.symbol_table.structs.get(&left_type) {
Some(f) => f,
None => {
return error!(
&field.loc,
format!("unknown struct type: {}", left_type)
);
}
};
let f = match fields.get(&field.lexeme) {
Some(o) => o,
None => {
return error!(
&field.loc,
format!("unknown field: {}", &field.lexeme)
);
}
};
expect_type!(value_type.clone(), f.field_type, field.loc);
}
_ => return error!(&op.loc, "invalid assignment target"),
}
Ok(value_type)
}
ExprKind::Call {
callee,
paren,