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() 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 for i in 0..buffer->size
c->memory[0x200 + i] = buffer->data[i] 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); let offset = env.define_var(name.lexeme.clone(), var_type);
emit!(&mut self.output, " mov QWORD [rbp-{}], rax", offset); 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: _ } => { Stmt::Const { name: _, value: _ } => {
// handled in SymbolTable // 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 { ExprKind::Call {
callee, callee,
paren: _, paren: _,

View File

@@ -22,6 +22,11 @@ pub enum Stmt {
var_type: Option<Token>, var_type: Option<Token>,
initializer: Expr, initializer: Expr,
}, },
Assign {
left: Expr,
op: Token,
value: Expr,
},
Const { Const {
name: Token, name: Token,
value: Token, value: Token,
@@ -101,11 +106,6 @@ pub enum ExprKind {
right: Box<Expr>, right: Box<Expr>,
}, },
Variable(Token), Variable(Token),
Assign {
left: Box<Expr>,
op: Token,
value: Box<Expr>,
},
Call { Call {
callee: Box<Expr>, callee: Box<Expr>,
paren: Token, paren: Token,
@@ -312,7 +312,18 @@ impl Parser {
} else if self.match_token(&[TokenType::KeywordContinue]) { } else if self.match_token(&[TokenType::KeywordContinue]) {
Ok(Stmt::Continue) Ok(Stmt::Continue)
} else { } 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> { fn expression(&mut self) -> Result<Expr, ZernError> {
self.assignment() self.pipe()
}
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)
} }
fn pipe(&mut self) -> Result<Expr, ZernError> { 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_CLASS_IN = 1
const DNS_RECURSION_DESIRED = 256 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) 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 out_pos := 0
part_start := 0 part_start := 0
i := 0 i := 0
@@ -175,9 +175,9 @@ func net.encode_dns_name[domain: str] : io.Buffer
buf->data[out_pos] = 0 buf->data[out_pos] = 0
return buf 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) 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 // header
id := math.abs(os.urandom_i64() % 65536) 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, record_type)
mem.write16be(out->data + 12 + name->size + 2, DNS_CLASS_IN) mem.write16be(out->data + 12 + name->size + 2, DNS_CLASS_IN)
io.Buffer.free(name) Blob.free(name)
return out return out
// TODO: dont resolve if its already an IP // TODO: dont resolve if its already an IP
@@ -207,7 +207,7 @@ func net.resolve?[domain: str] : i64
return -1 return -1
net.udp_send_to(s, s->addr, query->data, query->size) net.udp_send_to(s, s->addr, query->data, query->size)
io.Buffer.free(query) Blob.free(query)
pkt := net.udp_receive(s, 1024) pkt := net.udp_receive(s, 1024)
net.UDPSocket.close_and_free(s) 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)) buffer = must(mem.realloc?(buffer as ptr, n + 1))
return buffer return buffer
struct io.Buffer struct Blob
data: ptr data: ptr
size: i64 size: i64
func io.Buffer.alloc?[size: i64] : io.Buffer func Blob.alloc?[size: i64] : Blob
err.clear() err.clear()
buffer := new* io.Buffer buffer := new* Blob
buffer->size = size buffer->size = size
buffer->data = mem.alloc?(size) buffer->data = mem.alloc?(size)
if err.check() if err.check()
mem.free(buffer) mem.free(buffer)
return 0 as io.Buffer return 0 as Blob
return buffer return buffer
func io.Buffer.free[buf: io.Buffer] : void func Blob.free[buf: Blob] : void
mem.free(buf->data) mem.free(buf->data)
mem.free(buf) mem.free(buf)
@@ -396,30 +396,30 @@ func io.read_text_file?[path: str] : str
buffer[n] = 0 buffer[n] = 0
return buffer return buffer
func io.read_binary_file?[path: str] : io.Buffer func io.read_binary_file?[path: str] : Blob
err.clear() err.clear()
fd := _builtin_syscall(SYS_openat, -100, path, 0, 0) fd := _builtin_syscall(SYS_openat, -100, path, 0, 0)
if fd < 0 if fd < 0
err.set(ERR_OPEN_FAILED, "io.read_binary_file?: failed to open file") 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) buf->size = _builtin_syscall(SYS_lseek, fd, 0, 2)
_builtin_syscall(SYS_lseek, fd, 0, 0) _builtin_syscall(SYS_lseek, fd, 0, 0)
buf->data = mem.alloc?(buf->size) buf->data = mem.alloc?(buf->size)
if err.check() if err.check()
io.Buffer.free(buf) Blob.free(buf)
_builtin_syscall(SYS_close, fd) _builtin_syscall(SYS_close, fd)
return 0 as io.Buffer return 0 as Blob
n := _builtin_syscall(SYS_read, fd, buf->data, buf->size) n := _builtin_syscall(SYS_read, fd, buf->data, buf->size)
_builtin_syscall(SYS_close, fd) _builtin_syscall(SYS_close, fd)
if n != buf->size if n != buf->size
io.Buffer.free(buf) Blob.free(buf)
err.set(ERR_READ_FAILED, "io.read_binary_file?: failed to read file") err.set(ERR_READ_FAILED, "io.read_binary_file?: failed to read file")
return 0 as io.Buffer return 0 as Blob
return buf return buf

View File

@@ -111,6 +111,59 @@ impl<'a> TypeChecker<'a> {
env.define_var(name.lexeme.clone(), actual_type); 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: _ } => { Stmt::Const { name: _, value: _ } => {
// handled in SymbolTable // 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 { ExprKind::Call {
callee, callee,
paren, paren,