Compare commits
13 Commits
677521ca98
...
71a53b8183
| Author | SHA1 | Date | |
|---|---|---|---|
| 71a53b8183 | |||
| 625fc0d811 | |||
| 0dbe7560b6 | |||
| e774f06e65 | |||
| 2137d49abd | |||
| 3efb3820bc | |||
| 4154901339 | |||
| 664a51c88d | |||
| 15a68a41d1 | |||
| a3f480dc1d | |||
| 5846f7e730 | |||
| 13fa93c611 | |||
| 69c254382d |
@@ -3,7 +3,7 @@ func main[argc: i64, argv: ptr] : i64
|
|||||||
io.println("ERROR: url is missing")
|
io.println("ERROR: url is missing")
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
let url: str = mem.read64(argv + 8)
|
let url: str = mem.read64(argv + 8) as str
|
||||||
|
|
||||||
if str.len(url) <= 7
|
if str.len(url) <= 7
|
||||||
io.println("ERROR: invalid url (Did you forget \"http://\"?)")
|
io.println("ERROR: invalid url (Did you forget \"http://\"?)")
|
||||||
@@ -36,7 +36,7 @@ func main[argc: i64, argv: ptr] : i64
|
|||||||
req = str.concat(req, " HTTP/1.0\r\nHost: ")
|
req = str.concat(req, " HTTP/1.0\r\nHost: ")
|
||||||
req = str.concat(req, host)
|
req = str.concat(req, host)
|
||||||
req = str.concat(req, "\r\nConnection: close\r\n\r\n")
|
req = str.concat(req, "\r\nConnection: close\r\n\r\n")
|
||||||
net.send(s, req, str.len(req))
|
net.send(s, req as ptr, str.len(req))
|
||||||
mem.free(req)
|
mem.free(req)
|
||||||
|
|
||||||
let header_buf: str = mem.alloc(8192)
|
let header_buf: str = mem.alloc(8192)
|
||||||
@@ -45,7 +45,7 @@ func main[argc: i64, argv: ptr] : i64
|
|||||||
let end_index: i64 = -1
|
let end_index: i64 = -1
|
||||||
|
|
||||||
while !found && header_size < 8192
|
while !found && header_size < 8192
|
||||||
let n: i64 = net.read(s, header_buf + header_size, 8192 - header_size)
|
let n: i64 = net.read(s, header_buf as ptr + header_size, 8192 - header_size)
|
||||||
if n <= 0
|
if n <= 0
|
||||||
break
|
break
|
||||||
let current_size: i64 = header_size + n
|
let current_size: i64 = header_size + n
|
||||||
@@ -59,7 +59,7 @@ func main[argc: i64, argv: ptr] : i64
|
|||||||
header_size = current_size
|
header_size = current_size
|
||||||
|
|
||||||
if end_index < header_size
|
if end_index < header_size
|
||||||
io.print_sized(header_buf + end_index, header_size - end_index)
|
io.print_sized(header_buf as ptr + end_index, header_size as i64 - end_index)
|
||||||
mem.free(header_buf)
|
mem.free(header_buf)
|
||||||
|
|
||||||
let buffer: ptr = mem.alloc(4096)
|
let buffer: ptr = mem.alloc(4096)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ func part2_rec[bank: str, start: i64, remaining: i64] : i64
|
|||||||
let len: i64 = str.len(bank)
|
let len: i64 = str.len(bank)
|
||||||
|
|
||||||
for i in (start)..(len-remaining+1)
|
for i in (start)..(len-remaining+1)
|
||||||
let v: i64 = bank[i] - '0'
|
let v: i64 = (bank[i] - '0') as i64
|
||||||
if v > largest
|
if v > largest
|
||||||
largest = v
|
largest = v
|
||||||
largest_idx = i
|
largest_idx = i
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ func main[] : i64
|
|||||||
let s: i64 = must(net.listen?(net.pack_addr(127, 0, 0, 1), 8000))
|
let s: i64 = must(net.listen?(net.pack_addr(127, 0, 0, 1), 8000))
|
||||||
|
|
||||||
io.println("Listening on port 8000...")
|
io.println("Listening on port 8000...")
|
||||||
let resp: str = mem.alloc(60000)
|
let resp: ptr = mem.alloc(60000)
|
||||||
while true
|
while true
|
||||||
let conn: i64 = net.accept(s)
|
let conn: i64 = net.accept(s)
|
||||||
if conn < 0
|
if conn < 0
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
func main[] : i64
|
func main[] : i64
|
||||||
let s: i64 = must(net.udp_listen?(net.pack_addr(127, 0, 0, 1), 8000))
|
let s: net.UDPSocket = must(net.create_udp_server?(net.pack_addr(127, 0, 0, 1), 8000))
|
||||||
|
|
||||||
io.println("Listening on port 8000...")
|
io.println("Listening on port 8000...")
|
||||||
while true
|
while true
|
||||||
|
|||||||
109
src/analyzer.rs
109
src/analyzer.rs
@@ -5,25 +5,57 @@ use crate::{
|
|||||||
tokenizer::{ZernError, error},
|
tokenizer::{ZernError, error},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub type Type = String;
|
||||||
|
|
||||||
|
pub struct StructField {
|
||||||
|
pub offset: usize,
|
||||||
|
pub field_type: Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct FnType {
|
||||||
|
pub return_type: Type,
|
||||||
|
pub params: Option<Vec<Type>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FnType {
|
||||||
|
fn new(return_type: &str, params: Vec<&str>) -> FnType {
|
||||||
|
FnType {
|
||||||
|
return_type: return_type.to_string(),
|
||||||
|
params: Some(params.iter().map(|x| x.to_string()).collect()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_variadic(return_type: &str) -> FnType {
|
||||||
|
FnType {
|
||||||
|
return_type: return_type.to_string(),
|
||||||
|
params: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Analyzer {
|
pub struct Analyzer {
|
||||||
pub functions: HashMap<String, i32>,
|
pub functions: HashMap<String, FnType>,
|
||||||
pub constants: HashMap<String, u64>,
|
pub constants: HashMap<String, u64>,
|
||||||
pub structs: HashMap<String, HashMap<String, usize>>,
|
pub structs: HashMap<String, HashMap<String, StructField>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Analyzer {
|
impl Analyzer {
|
||||||
pub fn new() -> Analyzer {
|
pub fn new() -> Analyzer {
|
||||||
Analyzer {
|
Analyzer {
|
||||||
functions: HashMap::from([
|
functions: HashMap::from([
|
||||||
("_builtin_heap_head".into(), 0),
|
("_builtin_heap_head".into(), FnType::new("ptr", vec![])),
|
||||||
("_builtin_heap_tail".into(), 0),
|
("_builtin_heap_tail".into(), FnType::new("ptr", vec![])),
|
||||||
("_builtin_err_code".into(), 0),
|
("_builtin_err_code".into(), FnType::new("ptr", vec![])),
|
||||||
("_builtin_err_msg".into(), 0),
|
("_builtin_err_msg".into(), FnType::new("ptr", vec![])),
|
||||||
("_builtin_read64".into(), 1),
|
("_builtin_read64".into(), FnType::new("i64", vec!["ptr"])),
|
||||||
("_builtin_set64".into(), 2),
|
(
|
||||||
("_builtin_syscall".into(), -1),
|
"_builtin_set64".into(),
|
||||||
("io.printf".into(), -1),
|
FnType::new("void", vec!["ptr", "i64"]),
|
||||||
("_builtin_environ".into(), 0),
|
),
|
||||||
|
("_builtin_syscall".into(), FnType::new_variadic("i64")),
|
||||||
|
("io.printf".into(), FnType::new_variadic("void")),
|
||||||
|
("_builtin_environ".into(), FnType::new("ptr", vec![])),
|
||||||
]),
|
]),
|
||||||
constants: HashMap::new(),
|
constants: HashMap::new(),
|
||||||
structs: HashMap::new(),
|
structs: HashMap::new(),
|
||||||
@@ -34,7 +66,7 @@ impl Analyzer {
|
|||||||
if let Stmt::Function {
|
if let Stmt::Function {
|
||||||
name,
|
name,
|
||||||
params,
|
params,
|
||||||
return_type: _,
|
return_type,
|
||||||
body: _,
|
body: _,
|
||||||
exported: _,
|
exported: _,
|
||||||
} = stmt
|
} = stmt
|
||||||
@@ -42,8 +74,13 @@ impl Analyzer {
|
|||||||
if self.functions.contains_key(&name.lexeme) {
|
if self.functions.contains_key(&name.lexeme) {
|
||||||
return error!(name.loc, format!("tried to redefine '{}'", name.lexeme));
|
return error!(name.loc, format!("tried to redefine '{}'", name.lexeme));
|
||||||
}
|
}
|
||||||
self.functions
|
self.functions.insert(
|
||||||
.insert(name.lexeme.clone(), params.len() as i32);
|
name.lexeme.clone(),
|
||||||
|
FnType {
|
||||||
|
return_type: return_type.lexeme.clone(),
|
||||||
|
params: Some(params.iter().map(|x| x.var_type.lexeme.clone()).collect()),
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -83,6 +120,7 @@ impl Analyzer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stmt::If {
|
Stmt::If {
|
||||||
|
keyword: _,
|
||||||
condition,
|
condition,
|
||||||
then_branch,
|
then_branch,
|
||||||
else_branch,
|
else_branch,
|
||||||
@@ -91,7 +129,11 @@ impl Analyzer {
|
|||||||
self.analyze_stmt(then_branch)?;
|
self.analyze_stmt(then_branch)?;
|
||||||
self.analyze_stmt(else_branch)?;
|
self.analyze_stmt(else_branch)?;
|
||||||
}
|
}
|
||||||
Stmt::While { condition, body } => {
|
Stmt::While {
|
||||||
|
keyword: _,
|
||||||
|
condition,
|
||||||
|
body,
|
||||||
|
} => {
|
||||||
self.analyze_expr(condition)?;
|
self.analyze_expr(condition)?;
|
||||||
self.analyze_stmt(body)?;
|
self.analyze_stmt(body)?;
|
||||||
}
|
}
|
||||||
@@ -108,7 +150,7 @@ impl Analyzer {
|
|||||||
|
|
||||||
self.analyze_stmt(body)?;
|
self.analyze_stmt(body)?;
|
||||||
}
|
}
|
||||||
Stmt::Return(expr) => {
|
Stmt::Return { expr, keyword: _ } => {
|
||||||
self.analyze_expr(expr)?;
|
self.analyze_expr(expr)?;
|
||||||
}
|
}
|
||||||
Stmt::For {
|
Stmt::For {
|
||||||
@@ -127,14 +169,21 @@ impl Analyzer {
|
|||||||
if self.functions.contains_key(&name.lexeme) {
|
if self.functions.contains_key(&name.lexeme) {
|
||||||
return error!(name.loc, format!("tried to redefine '{}'", name.lexeme));
|
return error!(name.loc, format!("tried to redefine '{}'", name.lexeme));
|
||||||
}
|
}
|
||||||
self.functions.insert(name.lexeme.clone(), -1);
|
self.functions
|
||||||
|
.insert(name.lexeme.clone(), FnType::new_variadic("any"));
|
||||||
}
|
}
|
||||||
Stmt::Struct { name, fields } => {
|
Stmt::Struct { name, fields } => {
|
||||||
let mut fields_map: HashMap<String, usize> = HashMap::new();
|
let mut fields_map: HashMap<String, StructField> = HashMap::new();
|
||||||
|
|
||||||
let mut offset: usize = 0;
|
let mut offset: usize = 0;
|
||||||
for field in fields {
|
for field in fields {
|
||||||
fields_map.insert(field.var_name.lexeme.clone(), offset);
|
fields_map.insert(
|
||||||
|
field.var_name.lexeme.clone(),
|
||||||
|
StructField {
|
||||||
|
offset,
|
||||||
|
field_type: field.var_type.lexeme.clone(),
|
||||||
|
},
|
||||||
|
);
|
||||||
offset += 8;
|
offset += 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,11 +221,18 @@ impl Analyzer {
|
|||||||
if let Expr::Variable(callee_name) = *callee.clone() {
|
if let Expr::Variable(callee_name) = *callee.clone() {
|
||||||
if self.functions.contains_key(&callee_name.lexeme) {
|
if self.functions.contains_key(&callee_name.lexeme) {
|
||||||
// its a function (defined/builtin/extern)
|
// its a function (defined/builtin/extern)
|
||||||
if let Some(arity) = self.functions.get(&callee_name.lexeme) {
|
if let Some(fn_type) = self.functions.get(&callee_name.lexeme) {
|
||||||
if *arity >= 0 && *arity != args.len() as i32 {
|
// if its None, its variadic
|
||||||
|
if let Some(params) = &fn_type.params
|
||||||
|
&& params.len() != args.len()
|
||||||
|
{
|
||||||
return error!(
|
return error!(
|
||||||
&paren.loc,
|
&paren.loc,
|
||||||
format!("expected {} arguments, got {}", arity, args.len())
|
format!(
|
||||||
|
"expected {} arguments, got {}",
|
||||||
|
params.len(),
|
||||||
|
args.len()
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -203,7 +259,11 @@ impl Analyzer {
|
|||||||
self.analyze_expr(expr)?;
|
self.analyze_expr(expr)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Index { expr, index } => {
|
Expr::Index {
|
||||||
|
expr,
|
||||||
|
bracket: _,
|
||||||
|
index,
|
||||||
|
} => {
|
||||||
self.analyze_expr(expr)?;
|
self.analyze_expr(expr)?;
|
||||||
self.analyze_expr(index)?;
|
self.analyze_expr(index)?;
|
||||||
}
|
}
|
||||||
@@ -214,6 +274,9 @@ impl Analyzer {
|
|||||||
Expr::MemberAccess { left, field: _ } => {
|
Expr::MemberAccess { left, field: _ } => {
|
||||||
self.analyze_expr(left)?;
|
self.analyze_expr(left)?;
|
||||||
}
|
}
|
||||||
|
Expr::Cast { expr, type_name: _ } => {
|
||||||
|
self.analyze_expr(expr)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,25 +69,22 @@ macro_rules! emit {
|
|||||||
|
|
||||||
static REGISTERS: [&str; 6] = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"];
|
static REGISTERS: [&str; 6] = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"];
|
||||||
|
|
||||||
// TODO: currently they are all just 64 bit values
|
pub struct CodegenX86_64<'a> {
|
||||||
static BUILTIN_TYPES: [&str; 7] = ["void", "u8", "i64", "str", "bool", "ptr", "any"];
|
|
||||||
|
|
||||||
pub struct CodegenX86_64 {
|
|
||||||
output: String,
|
output: String,
|
||||||
data_section: String,
|
data_section: String,
|
||||||
label_counter: usize,
|
label_counter: usize,
|
||||||
data_counter: usize,
|
data_counter: usize,
|
||||||
pub analyzer: Analyzer,
|
pub analyzer: &'a Analyzer,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CodegenX86_64 {
|
impl<'a> CodegenX86_64<'a> {
|
||||||
pub fn new() -> CodegenX86_64 {
|
pub fn new(analyzer: &'a Analyzer) -> CodegenX86_64<'a> {
|
||||||
CodegenX86_64 {
|
CodegenX86_64 {
|
||||||
output: String::new(),
|
output: String::new(),
|
||||||
data_section: String::new(),
|
data_section: String::new(),
|
||||||
label_counter: 0,
|
label_counter: 0,
|
||||||
data_counter: 1,
|
data_counter: 1,
|
||||||
analyzer: Analyzer::new(),
|
analyzer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,10 +245,6 @@ _builtin_environ:
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if !self.is_valid_type_name(&var_type) {
|
|
||||||
return error!(&name.loc, "unrecognized type: ".to_owned() + &var_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.compile_expr(env, initializer)?;
|
self.compile_expr(env, initializer)?;
|
||||||
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);
|
||||||
@@ -267,6 +260,7 @@ _builtin_environ:
|
|||||||
env.pop_scope();
|
env.pop_scope();
|
||||||
}
|
}
|
||||||
Stmt::If {
|
Stmt::If {
|
||||||
|
keyword: _,
|
||||||
condition,
|
condition,
|
||||||
then_branch,
|
then_branch,
|
||||||
else_branch,
|
else_branch,
|
||||||
@@ -283,7 +277,11 @@ _builtin_environ:
|
|||||||
self.compile_stmt(env, else_branch)?;
|
self.compile_stmt(env, else_branch)?;
|
||||||
emit!(&mut self.output, "{}:", end_label);
|
emit!(&mut self.output, "{}:", end_label);
|
||||||
}
|
}
|
||||||
Stmt::While { condition, body } => {
|
Stmt::While {
|
||||||
|
keyword: _,
|
||||||
|
condition,
|
||||||
|
body,
|
||||||
|
} => {
|
||||||
let old_loop_begin_label = env.loop_begin_label.clone();
|
let old_loop_begin_label = env.loop_begin_label.clone();
|
||||||
let old_loop_end_label = env.loop_end_label.clone();
|
let old_loop_end_label = env.loop_end_label.clone();
|
||||||
let old_loop_continue_label = env.loop_continue_label.clone();
|
let old_loop_continue_label = env.loop_continue_label.clone();
|
||||||
@@ -306,7 +304,7 @@ _builtin_environ:
|
|||||||
Stmt::Function {
|
Stmt::Function {
|
||||||
name,
|
name,
|
||||||
params,
|
params,
|
||||||
return_type,
|
return_type: _,
|
||||||
body,
|
body,
|
||||||
exported,
|
exported,
|
||||||
} => {
|
} => {
|
||||||
@@ -319,21 +317,7 @@ _builtin_environ:
|
|||||||
emit!(&mut self.output, " mov rbp, rsp");
|
emit!(&mut self.output, " mov rbp, rsp");
|
||||||
emit!(&mut self.output, " sub rsp, 256"); // TODO
|
emit!(&mut self.output, " sub rsp, 256"); // TODO
|
||||||
|
|
||||||
if !self.is_valid_type_name(&return_type.lexeme) {
|
|
||||||
return error!(
|
|
||||||
&return_type.loc,
|
|
||||||
"unrecognized type: ".to_owned() + &return_type.lexeme
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i, param) in params.iter().enumerate() {
|
for (i, param) in params.iter().enumerate() {
|
||||||
if !self.is_valid_type_name(¶m.var_type.lexeme) {
|
|
||||||
return error!(
|
|
||||||
¶m.var_name.loc,
|
|
||||||
"unrecognized type: ".to_owned() + ¶m.var_type.lexeme
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let offset = env
|
let offset = env
|
||||||
.define_var(param.var_name.lexeme.clone(), param.var_type.lexeme.clone());
|
.define_var(param.var_name.lexeme.clone(), param.var_type.lexeme.clone());
|
||||||
if let Some(reg) = REGISTERS.get(i) {
|
if let Some(reg) = REGISTERS.get(i) {
|
||||||
@@ -360,7 +344,7 @@ _builtin_environ:
|
|||||||
emit!(&mut self.output, " ret");
|
emit!(&mut self.output, " ret");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stmt::Return(expr) => {
|
Stmt::Return { expr, keyword: _ } => {
|
||||||
self.compile_expr(env, expr)?;
|
self.compile_expr(env, expr)?;
|
||||||
emit!(&mut self.output, " mov rsp, rbp");
|
emit!(&mut self.output, " mov rsp, rbp");
|
||||||
emit!(&mut self.output, " pop rbp");
|
emit!(&mut self.output, " pop rbp");
|
||||||
@@ -578,15 +562,9 @@ _builtin_environ:
|
|||||||
self.analyzer.constants[&name.lexeme]
|
self.analyzer.constants[&name.lexeme]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// TODO: move to analyzer
|
|
||||||
let var = match env.get_var(&name.lexeme) {
|
let var = match env.get_var(&name.lexeme) {
|
||||||
Some(x) => x,
|
Some(x) => x,
|
||||||
None => {
|
None => unreachable!("this should be caught in the typechecker"),
|
||||||
return error!(
|
|
||||||
name.loc,
|
|
||||||
format!("undefined variable: {}", &name.lexeme)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
emit!(
|
emit!(
|
||||||
&mut self.output,
|
&mut self.output,
|
||||||
@@ -600,15 +578,9 @@ _builtin_environ:
|
|||||||
|
|
||||||
match left.as_ref() {
|
match left.as_ref() {
|
||||||
Expr::Variable(name) => {
|
Expr::Variable(name) => {
|
||||||
// TODO: move to analyzer
|
|
||||||
let var = match env.get_var(&name.lexeme) {
|
let var = match env.get_var(&name.lexeme) {
|
||||||
Some(x) => x,
|
Some(x) => x,
|
||||||
None => {
|
None => unreachable!(),
|
||||||
return error!(
|
|
||||||
name.loc,
|
|
||||||
format!("undefined variable: {}", &name.lexeme)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
emit!(
|
emit!(
|
||||||
&mut self.output,
|
&mut self.output,
|
||||||
@@ -616,7 +588,11 @@ _builtin_environ:
|
|||||||
var.stack_offset,
|
var.stack_offset,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Expr::Index { expr, index } => {
|
Expr::Index {
|
||||||
|
expr,
|
||||||
|
bracket: _,
|
||||||
|
index,
|
||||||
|
} => {
|
||||||
emit!(&mut self.output, " push rax");
|
emit!(&mut self.output, " push rax");
|
||||||
self.compile_expr(env, expr)?;
|
self.compile_expr(env, expr)?;
|
||||||
emit!(&mut self.output, " push rax");
|
emit!(&mut self.output, " push rax");
|
||||||
@@ -710,7 +686,11 @@ _builtin_environ:
|
|||||||
}
|
}
|
||||||
emit!(&mut self.output, " pop rax");
|
emit!(&mut self.output, " pop rax");
|
||||||
}
|
}
|
||||||
Expr::Index { expr, index } => {
|
Expr::Index {
|
||||||
|
expr,
|
||||||
|
bracket: _,
|
||||||
|
index,
|
||||||
|
} => {
|
||||||
self.compile_expr(env, expr)?;
|
self.compile_expr(env, expr)?;
|
||||||
emit!(&mut self.output, " push rax");
|
emit!(&mut self.output, " push rax");
|
||||||
self.compile_expr(env, index)?;
|
self.compile_expr(env, index)?;
|
||||||
@@ -761,20 +741,13 @@ _builtin_environ:
|
|||||||
self.compile_expr(env, left)?;
|
self.compile_expr(env, left)?;
|
||||||
emit!(&mut self.output, " mov rax, QWORD [rax+{}]", offset);
|
emit!(&mut self.output, " mov rax, QWORD [rax+{}]", offset);
|
||||||
}
|
}
|
||||||
|
Expr::Cast { expr, type_name: _ } => {
|
||||||
|
self.compile_expr(env, expr)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_valid_type_name(&self, name: &str) -> bool {
|
|
||||||
if BUILTIN_TYPES.contains(&name) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if self.analyzer.structs.contains_key(name) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_field_offset(
|
fn get_field_offset(
|
||||||
&self,
|
&self,
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
@@ -803,11 +776,11 @@ _builtin_environ:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let offset = match fields.get(&field.lexeme) {
|
let field = match fields.get(&field.lexeme) {
|
||||||
Some(o) => *o,
|
Some(o) => o,
|
||||||
None => return error!(&field.loc, format!("unknown field: {}", &field.lexeme)),
|
None => return error!(&field.loc, format!("unknown field: {}", &field.lexeme)),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(offset)
|
Ok(field.offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
94
src/main.rs
94
src/main.rs
@@ -2,6 +2,7 @@ mod analyzer;
|
|||||||
mod codegen_x86_64;
|
mod codegen_x86_64;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod tokenizer;
|
mod tokenizer;
|
||||||
|
mod typechecker;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
@@ -13,55 +14,15 @@ use tokenizer::ZernError;
|
|||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
macro_rules! compile_std {
|
macro_rules! parse_std_file {
|
||||||
($codegen:expr, $( $name:literal ),* $(,)? ) => {
|
($statements:expr, $filename:expr) => {
|
||||||
$(
|
let source: String = include_str!($filename).into();
|
||||||
compile_file_to(
|
let tokenizer = tokenizer::Tokenizer::new($filename.to_owned(), source);
|
||||||
$codegen,
|
let parser = parser::Parser::new(tokenizer.tokenize()?);
|
||||||
$name,
|
$statements.extend(parser.parse()?);
|
||||||
include_str!(concat!("std/", $name)).into()
|
|
||||||
)?;
|
|
||||||
)*
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_file_to(
|
|
||||||
codegen: &mut codegen_x86_64::CodegenX86_64,
|
|
||||||
filename: &str,
|
|
||||||
source: String,
|
|
||||||
) -> Result<(), ZernError> {
|
|
||||||
let tokenizer = tokenizer::Tokenizer::new(filename.to_owned(), source);
|
|
||||||
let tokens = tokenizer.tokenize()?;
|
|
||||||
|
|
||||||
let parser = parser::Parser::new(tokens);
|
|
||||||
let statements = parser.parse()?;
|
|
||||||
|
|
||||||
for stmt in &statements {
|
|
||||||
codegen.analyzer.register_function(stmt)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
for stmt in &statements {
|
|
||||||
codegen.analyzer.analyze_stmt(stmt)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
for stmt in statements {
|
|
||||||
// top level statements are all function/const/extern declarations so a new env for each
|
|
||||||
codegen.compile_stmt(&mut codegen_x86_64::Env::new(), &stmt)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_command(cmd: String) {
|
|
||||||
if !Command::new("sh")
|
|
||||||
.args(["-c", &cmd])
|
|
||||||
.status()
|
|
||||||
.unwrap()
|
|
||||||
.success()
|
|
||||||
{
|
|
||||||
process::exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compile_file(args: Args) -> Result<(), ZernError> {
|
fn compile_file(args: Args) -> Result<(), ZernError> {
|
||||||
let source = match fs::read_to_string(&args.path) {
|
let source = match fs::read_to_string(&args.path) {
|
||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
@@ -73,10 +34,34 @@ fn compile_file(args: Args) -> Result<(), ZernError> {
|
|||||||
|
|
||||||
let filename = Path::new(&args.path).file_name().unwrap().to_str().unwrap();
|
let filename = Path::new(&args.path).file_name().unwrap().to_str().unwrap();
|
||||||
|
|
||||||
let mut codegen = codegen_x86_64::CodegenX86_64::new();
|
let mut statements = Vec::new();
|
||||||
|
|
||||||
|
parse_std_file!(statements, "std/syscalls.zr");
|
||||||
|
parse_std_file!(statements, "std/std.zr");
|
||||||
|
parse_std_file!(statements, "std/net.zr");
|
||||||
|
|
||||||
|
let tokenizer = tokenizer::Tokenizer::new(filename.to_owned(), source);
|
||||||
|
let parser = parser::Parser::new(tokenizer.tokenize()?);
|
||||||
|
statements.extend(parser.parse()?);
|
||||||
|
|
||||||
|
let mut analyzer = analyzer::Analyzer::new();
|
||||||
|
for stmt in &statements {
|
||||||
|
analyzer.register_function(stmt)?;
|
||||||
|
}
|
||||||
|
for stmt in &statements {
|
||||||
|
analyzer.analyze_stmt(stmt)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut typechecker = typechecker::TypeChecker::new(&analyzer);
|
||||||
|
for stmt in &statements {
|
||||||
|
typechecker.typecheck_stmt(&mut typechecker::Env::new(), stmt)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut codegen = codegen_x86_64::CodegenX86_64::new(&analyzer);
|
||||||
codegen.emit_prologue(args.use_gcc)?;
|
codegen.emit_prologue(args.use_gcc)?;
|
||||||
compile_std!(&mut codegen, "syscalls.zr", "std.zr", "net.zr");
|
for stmt in statements {
|
||||||
compile_file_to(&mut codegen, filename, source)?;
|
codegen.compile_stmt(&mut codegen_x86_64::Env::new(), &stmt)?;
|
||||||
|
}
|
||||||
|
|
||||||
if !args.output_asm {
|
if !args.output_asm {
|
||||||
let out = args.out.unwrap_or_else(|| "out".into());
|
let out = args.out.unwrap_or_else(|| "out".into());
|
||||||
@@ -116,6 +101,17 @@ fn compile_file(args: Args) -> Result<(), ZernError> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_command(cmd: String) {
|
||||||
|
if !Command::new("sh")
|
||||||
|
.args(["-c", &cmd])
|
||||||
|
.status()
|
||||||
|
.unwrap()
|
||||||
|
.success()
|
||||||
|
{
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(version, about, long_about = None)]
|
#[command(version, about, long_about = None)]
|
||||||
struct Args {
|
struct Args {
|
||||||
|
|||||||
@@ -20,11 +20,13 @@ pub enum Stmt {
|
|||||||
},
|
},
|
||||||
Block(Vec<Stmt>),
|
Block(Vec<Stmt>),
|
||||||
If {
|
If {
|
||||||
|
keyword: Token,
|
||||||
condition: Expr,
|
condition: Expr,
|
||||||
then_branch: Box<Stmt>,
|
then_branch: Box<Stmt>,
|
||||||
else_branch: Box<Stmt>,
|
else_branch: Box<Stmt>,
|
||||||
},
|
},
|
||||||
While {
|
While {
|
||||||
|
keyword: Token,
|
||||||
condition: Expr,
|
condition: Expr,
|
||||||
body: Box<Stmt>,
|
body: Box<Stmt>,
|
||||||
},
|
},
|
||||||
@@ -41,7 +43,10 @@ pub enum Stmt {
|
|||||||
body: Box<Stmt>,
|
body: Box<Stmt>,
|
||||||
exported: bool,
|
exported: bool,
|
||||||
},
|
},
|
||||||
Return(Expr),
|
Return {
|
||||||
|
expr: Expr,
|
||||||
|
keyword: Token,
|
||||||
|
},
|
||||||
Break,
|
Break,
|
||||||
Continue,
|
Continue,
|
||||||
Extern(Token),
|
Extern(Token),
|
||||||
@@ -83,6 +88,7 @@ pub enum Expr {
|
|||||||
ArrayLiteral(Vec<Expr>),
|
ArrayLiteral(Vec<Expr>),
|
||||||
Index {
|
Index {
|
||||||
expr: Box<Expr>,
|
expr: Box<Expr>,
|
||||||
|
bracket: Token,
|
||||||
index: Box<Expr>,
|
index: Box<Expr>,
|
||||||
},
|
},
|
||||||
AddrOf {
|
AddrOf {
|
||||||
@@ -94,6 +100,10 @@ pub enum Expr {
|
|||||||
left: Box<Expr>,
|
left: Box<Expr>,
|
||||||
field: Token,
|
field: Token,
|
||||||
},
|
},
|
||||||
|
Cast {
|
||||||
|
expr: Box<Expr>,
|
||||||
|
type_name: Token,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Parser {
|
pub struct Parser {
|
||||||
@@ -257,7 +267,11 @@ impl Parser {
|
|||||||
} else if self.match_token(&[TokenType::KeywordFor]) {
|
} else if self.match_token(&[TokenType::KeywordFor]) {
|
||||||
self.for_statement()
|
self.for_statement()
|
||||||
} else if self.match_token(&[TokenType::KeywordReturn]) {
|
} else if self.match_token(&[TokenType::KeywordReturn]) {
|
||||||
Ok(Stmt::Return(self.expression()?))
|
let keyword = self.previous().clone();
|
||||||
|
Ok(Stmt::Return {
|
||||||
|
expr: self.expression()?,
|
||||||
|
keyword,
|
||||||
|
})
|
||||||
} else if self.match_token(&[TokenType::KeywordBreak]) {
|
} else if self.match_token(&[TokenType::KeywordBreak]) {
|
||||||
Ok(Stmt::Break)
|
Ok(Stmt::Break)
|
||||||
} else if self.match_token(&[TokenType::KeywordContinue]) {
|
} else if self.match_token(&[TokenType::KeywordContinue]) {
|
||||||
@@ -268,6 +282,7 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn if_statement(&mut self) -> Result<Stmt, ZernError> {
|
fn if_statement(&mut self) -> Result<Stmt, ZernError> {
|
||||||
|
let keyword = self.previous().clone();
|
||||||
let condition = self.expression()?;
|
let condition = self.expression()?;
|
||||||
let then_branch = self.block()?;
|
let then_branch = self.block()?;
|
||||||
let else_branch = if self.match_token(&[TokenType::KeywordElse]) {
|
let else_branch = if self.match_token(&[TokenType::KeywordElse]) {
|
||||||
@@ -280,6 +295,7 @@ impl Parser {
|
|||||||
Box::new(Stmt::Block(vec![]))
|
Box::new(Stmt::Block(vec![]))
|
||||||
};
|
};
|
||||||
Ok(Stmt::If {
|
Ok(Stmt::If {
|
||||||
|
keyword,
|
||||||
condition,
|
condition,
|
||||||
then_branch: Box::new(then_branch),
|
then_branch: Box::new(then_branch),
|
||||||
else_branch,
|
else_branch,
|
||||||
@@ -287,9 +303,11 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn while_statement(&mut self) -> Result<Stmt, ZernError> {
|
fn while_statement(&mut self) -> Result<Stmt, ZernError> {
|
||||||
|
let keyword = self.previous().clone();
|
||||||
let condition = self.expression()?;
|
let condition = self.expression()?;
|
||||||
let body = self.block()?;
|
let body = self.block()?;
|
||||||
Ok(Stmt::While {
|
Ok(Stmt::While {
|
||||||
|
keyword,
|
||||||
condition,
|
condition,
|
||||||
body: Box::new(body),
|
body: Box::new(body),
|
||||||
})
|
})
|
||||||
@@ -438,7 +456,7 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn factor(&mut self) -> Result<Expr, ZernError> {
|
fn factor(&mut self) -> Result<Expr, ZernError> {
|
||||||
let mut expr = self.unary()?;
|
let mut expr = self.cast()?;
|
||||||
|
|
||||||
while self.match_token(&[
|
while self.match_token(&[
|
||||||
TokenType::Star,
|
TokenType::Star,
|
||||||
@@ -459,6 +477,20 @@ impl Parser {
|
|||||||
Ok(expr)
|
Ok(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cast(&mut self) -> Result<Expr, ZernError> {
|
||||||
|
let mut expr = self.unary()?;
|
||||||
|
|
||||||
|
while self.match_token(&[TokenType::KeywordAs]) {
|
||||||
|
let type_name = self.consume(TokenType::Identifier, "expected type after 'as'")?;
|
||||||
|
expr = Expr::Cast {
|
||||||
|
expr: Box::new(expr),
|
||||||
|
type_name,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
|
||||||
fn unary(&mut self) -> Result<Expr, ZernError> {
|
fn unary(&mut self) -> Result<Expr, ZernError> {
|
||||||
if self.match_token(&[TokenType::Xor]) {
|
if self.match_token(&[TokenType::Xor]) {
|
||||||
let op = self.previous().clone();
|
let op = self.previous().clone();
|
||||||
@@ -504,9 +536,10 @@ impl Parser {
|
|||||||
};
|
};
|
||||||
} else if self.match_token(&[TokenType::LeftBracket]) {
|
} else if self.match_token(&[TokenType::LeftBracket]) {
|
||||||
let index = self.expression()?;
|
let index = self.expression()?;
|
||||||
self.consume(TokenType::RightBracket, "expected ']' after index")?;
|
let bracket = self.consume(TokenType::RightBracket, "expected ']' after index")?;
|
||||||
expr = Expr::Index {
|
expr = Expr::Index {
|
||||||
expr: Box::new(expr),
|
expr: Box::new(expr),
|
||||||
|
bracket,
|
||||||
index: Box::new(index),
|
index: Box::new(index),
|
||||||
}
|
}
|
||||||
} else if self.match_token(&[TokenType::Arrow]) {
|
} else if self.match_token(&[TokenType::Arrow]) {
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ func net.create_udp_client?[packed_host: i64, port: i64] : net.UDPSocket
|
|||||||
if s->fd < 0
|
if s->fd < 0
|
||||||
mem.free(s)
|
mem.free(s)
|
||||||
err.set(ERR_SYSCALL_FAILED, "net.create_udp_client?: failed to create a socket")
|
err.set(ERR_SYSCALL_FAILED, "net.create_udp_client?: failed to create a socket")
|
||||||
return 0
|
return 0 as net.UDPSocket
|
||||||
|
|
||||||
s->addr = mem.alloc(16)
|
s->addr = mem.alloc(16)
|
||||||
mem.zero(s->addr, 16)
|
mem.zero(s->addr, 16)
|
||||||
@@ -115,16 +115,16 @@ func net.create_udp_client?[packed_host: i64, port: i64] : net.UDPSocket
|
|||||||
s->addr[7] = packed_host & 255
|
s->addr[7] = packed_host & 255
|
||||||
return s
|
return s
|
||||||
|
|
||||||
func net.udp_listen?[packed_host: i64, port: i64] : net.UDPSocket
|
func net.create_udp_server?[packed_host: i64, port: i64] : net.UDPSocket
|
||||||
err.clear()
|
err.clear()
|
||||||
let s: net.UDPSocket = net.create_udp_client?(packed_host, port)
|
let s: net.UDPSocket = net.create_udp_client?(packed_host, port)
|
||||||
if err.check()
|
if err.check()
|
||||||
return 0
|
return 0 as net.UDPSocket
|
||||||
|
|
||||||
if _builtin_syscall(SYS_bind, s->fd, s->addr, 16) < 0
|
if _builtin_syscall(SYS_bind, s->fd, s->addr, 16) < 0
|
||||||
net.UDPSocket.close_and_free(s)
|
net.UDPSocket.close_and_free(s)
|
||||||
err.set(ERR_SYSCALL_FAILED, "net.udp_listen?: failed to bind")
|
err.set(ERR_SYSCALL_FAILED, "net.create_udp_server?: failed to bind")
|
||||||
return 0
|
return 0 as net.UDPSocket
|
||||||
|
|
||||||
return s
|
return s
|
||||||
|
|
||||||
@@ -166,7 +166,7 @@ func net.encode_dns_name[domain: str] : io.Buffer
|
|||||||
let part_len: i64 = i - part_start
|
let part_len: i64 = i - part_start
|
||||||
buf->data[out_pos] = part_len & 0xff
|
buf->data[out_pos] = part_len & 0xff
|
||||||
out_pos = out_pos + 1
|
out_pos = out_pos + 1
|
||||||
mem.copy(domain + part_start, buf->data + out_pos, part_len)
|
mem.copy(domain as ptr + part_start, buf->data + out_pos, part_len)
|
||||||
out_pos = out_pos + part_len
|
out_pos = out_pos + part_len
|
||||||
part_start = i + 1
|
part_start = i + 1
|
||||||
i = i + 1
|
i = i + 1
|
||||||
@@ -221,6 +221,6 @@ func net.resolve?[domain: str] : i64
|
|||||||
|
|
||||||
pos = pos + 12 // skip name(2), type(2), class(2), ttl(4), rdlength(2)
|
pos = pos + 12 // skip name(2), type(2), class(2), ttl(4), rdlength(2)
|
||||||
|
|
||||||
let out: i64 = net.pack_addr(pkt->data[pos], pkt->data[pos+1], pkt->data[pos+2], pkt->data[pos+3])
|
let out: i64 = net.pack_addr(pkt->data[pos] as i64, pkt->data[pos+1] as i64, pkt->data[pos+2] as i64, pkt->data[pos+3] as i64)
|
||||||
net.UDPPacket.free(pkt)
|
net.UDPPacket.free(pkt)
|
||||||
return out
|
return out
|
||||||
|
|||||||
172
src/std/std.zr
172
src/std/std.zr
@@ -10,13 +10,13 @@ func err.code[] : i64
|
|||||||
return mem.read64(_builtin_err_code())
|
return mem.read64(_builtin_err_code())
|
||||||
|
|
||||||
func err.msg[] : str
|
func err.msg[] : str
|
||||||
return mem.read64(_builtin_err_msg())
|
return mem.read64(_builtin_err_msg()) as str
|
||||||
|
|
||||||
func err.check[] : bool
|
func err.check[] : bool
|
||||||
return err.code() != 0
|
return err.code() != 0
|
||||||
|
|
||||||
func err.clear[] : void
|
func err.clear[] : void
|
||||||
err.set(0, 0)
|
err.set(0, 0 as str)
|
||||||
|
|
||||||
func err.set[code: i64, msg: str] : void
|
func err.set[code: i64, msg: str] : void
|
||||||
mem.write64(_builtin_err_code(), code)
|
mem.write64(_builtin_err_code(), code)
|
||||||
@@ -47,13 +47,13 @@ func mem._align[x: i64] : i64
|
|||||||
|
|
||||||
func mem._split_block[blk: mem.Block, needed: i64] : void
|
func mem._split_block[blk: mem.Block, needed: i64] : void
|
||||||
if blk->size >= needed + MEM_BLOCK_SIZE + 8
|
if blk->size >= needed + MEM_BLOCK_SIZE + 8
|
||||||
let new_blk: mem.Block = blk + MEM_BLOCK_SIZE + needed
|
let new_blk: mem.Block = (blk as ptr + MEM_BLOCK_SIZE + needed) as mem.Block
|
||||||
new_blk->size = blk->size - needed - MEM_BLOCK_SIZE
|
new_blk->size = blk->size - needed - MEM_BLOCK_SIZE
|
||||||
new_blk->free = true
|
new_blk->free = true
|
||||||
new_blk->next = blk->next
|
new_blk->next = blk->next
|
||||||
new_blk->prev = blk
|
new_blk->prev = blk
|
||||||
|
|
||||||
if blk->next
|
if blk->next as ptr
|
||||||
let next: mem.Block = blk->next
|
let next: mem.Block = blk->next
|
||||||
next->prev = new_blk
|
next->prev = new_blk
|
||||||
else
|
else
|
||||||
@@ -70,25 +70,27 @@ func mem._request_space?[size: i64] : mem.Block
|
|||||||
// PROT_READ | PROT_WRITE = 3
|
// PROT_READ | PROT_WRITE = 3
|
||||||
// MAP_PRIVATE | MAP_ANONYMOUS = 34
|
// MAP_PRIVATE | MAP_ANONYMOUS = 34
|
||||||
// fd = -1, offset = 0
|
// fd = -1, offset = 0
|
||||||
let blk: mem.Block = _builtin_syscall(SYS_mmap, 0, alloc_size, 3, 34, -1, 0)
|
let blk_ptr: ptr = _builtin_syscall(SYS_mmap, 0, alloc_size, 3, 34, -1, 0) as ptr
|
||||||
if blk == -1
|
if blk_ptr == -1
|
||||||
err.set(ERR_ALLOC_FAILED, "mem._request_space: mmap failed")
|
err.set(ERR_ALLOC_FAILED, "mem._request_space: mmap failed")
|
||||||
return 0
|
return 0 as mem.Block
|
||||||
|
|
||||||
|
let blk: mem.Block = blk_ptr as mem.Block
|
||||||
|
|
||||||
blk->size = alloc_size - MEM_BLOCK_SIZE
|
blk->size = alloc_size - MEM_BLOCK_SIZE
|
||||||
blk->free = false
|
blk->free = false
|
||||||
blk->next = 0
|
blk->next = 0 as mem.Block
|
||||||
blk->prev = 0
|
blk->prev = 0 as mem.Block
|
||||||
|
|
||||||
let tail: mem.Block = mem.read64(_builtin_heap_tail())
|
let tail: mem.Block = mem.read64(_builtin_heap_tail()) as mem.Block
|
||||||
if tail
|
if tail as ptr
|
||||||
tail->next = blk
|
tail->next = blk
|
||||||
blk->prev = tail
|
blk->prev = tail
|
||||||
|
|
||||||
mem.write64(_builtin_heap_tail(), blk)
|
mem.write64(_builtin_heap_tail(), blk)
|
||||||
return blk
|
return blk
|
||||||
|
|
||||||
func mem.alloc?[size: i64] : ptr
|
func mem.alloc?[size: i64] : any
|
||||||
err.clear()
|
err.clear()
|
||||||
if size <= 0
|
if size <= 0
|
||||||
err.set(ERR_ALLOC_FAILED, "mem.alloc? called with non-positive size")
|
err.set(ERR_ALLOC_FAILED, "mem.alloc? called with non-positive size")
|
||||||
@@ -96,12 +98,12 @@ func mem.alloc?[size: i64] : ptr
|
|||||||
|
|
||||||
size = mem._align(size)
|
size = mem._align(size)
|
||||||
|
|
||||||
let cur: mem.Block = mem.read64(_builtin_heap_head())
|
let cur: mem.Block = mem.read64(_builtin_heap_head()) as mem.Block
|
||||||
while cur
|
while cur as ptr
|
||||||
if cur->free && cur->size >= size
|
if cur->free && cur->size >= size
|
||||||
mem._split_block(cur, size)
|
mem._split_block(cur, size)
|
||||||
cur->free = false
|
cur->free = false
|
||||||
return cur + MEM_BLOCK_SIZE
|
return cur as ptr + MEM_BLOCK_SIZE
|
||||||
cur = cur->next
|
cur = cur->next
|
||||||
|
|
||||||
let blk: mem.Block = mem._request_space?(size)
|
let blk: mem.Block = mem._request_space?(size)
|
||||||
@@ -113,51 +115,51 @@ func mem.alloc?[size: i64] : ptr
|
|||||||
|
|
||||||
mem._split_block(blk, size)
|
mem._split_block(blk, size)
|
||||||
|
|
||||||
return blk + MEM_BLOCK_SIZE
|
return blk as ptr + MEM_BLOCK_SIZE
|
||||||
|
|
||||||
func mem.alloc[size: i64] : ptr
|
func mem.alloc[size: i64] : any
|
||||||
return must(mem.alloc?(size))
|
return must(mem.alloc?(size))
|
||||||
|
|
||||||
func mem.free[x: ptr] : void
|
func mem.free[x: any] : void
|
||||||
if !x
|
if !(x as ptr)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
let blk: mem.Block = x - MEM_BLOCK_SIZE
|
let blk: mem.Block = (x as ptr - MEM_BLOCK_SIZE) as mem.Block
|
||||||
blk->free = true
|
blk->free = true
|
||||||
|
|
||||||
let next: mem.Block = blk->next
|
let next: mem.Block = blk->next
|
||||||
if next && next->free
|
if next as ptr && next->free
|
||||||
let expected_next: mem.Block = blk + MEM_BLOCK_SIZE + blk->size
|
let expected_next: mem.Block = (blk as ptr + MEM_BLOCK_SIZE + blk->size) as mem.Block
|
||||||
if expected_next == next
|
if expected_next as ptr == next as ptr
|
||||||
blk->size = blk->size + MEM_BLOCK_SIZE + next->size
|
blk->size = blk->size + MEM_BLOCK_SIZE + next->size
|
||||||
blk->next = next->next
|
blk->next = next->next
|
||||||
if next->next
|
if next->next as ptr
|
||||||
let b: mem.Block = next->next
|
let b: mem.Block = next->next
|
||||||
b->prev = blk
|
b->prev = blk
|
||||||
if mem.read64(_builtin_heap_tail()) == next
|
if mem.read64(_builtin_heap_tail()) == next as ptr
|
||||||
mem.write64(_builtin_heap_tail(), blk)
|
mem.write64(_builtin_heap_tail(), blk)
|
||||||
|
|
||||||
let prev: mem.Block = blk->prev
|
let prev: mem.Block = blk->prev
|
||||||
if prev && prev->free
|
if prev as ptr && prev->free
|
||||||
let expected_blk: mem.Block = prev + MEM_BLOCK_SIZE + prev->size
|
let expected_blk: mem.Block = (prev as ptr + MEM_BLOCK_SIZE + prev->size) as mem.Block
|
||||||
if expected_blk == blk
|
if expected_blk as ptr == blk as ptr
|
||||||
prev->size = prev->size + MEM_BLOCK_SIZE + blk->size
|
prev->size = prev->size + MEM_BLOCK_SIZE + blk->size
|
||||||
prev->next = blk->next
|
prev->next = blk->next
|
||||||
if blk->next
|
if blk->next as ptr
|
||||||
let b: mem.Block = blk->next
|
let b: mem.Block = blk->next
|
||||||
b->prev = prev
|
b->prev = prev
|
||||||
if mem.read64(_builtin_heap_tail()) == blk
|
if mem.read64(_builtin_heap_tail()) == blk as ptr
|
||||||
mem.write64(_builtin_heap_tail(), prev)
|
mem.write64(_builtin_heap_tail(), prev)
|
||||||
blk = prev
|
blk = prev
|
||||||
|
|
||||||
let block_total: i64 = blk->size + MEM_BLOCK_SIZE
|
let block_total: i64 = blk->size + MEM_BLOCK_SIZE
|
||||||
if (blk & 4095) == 0 && (block_total & 4095) == 0
|
if (blk as i64 & 4095) == 0 && (block_total & 4095) == 0
|
||||||
if blk->prev
|
if blk->prev as ptr
|
||||||
let b: mem.Block = blk->prev
|
let b: mem.Block = blk->prev
|
||||||
b->next = blk->next
|
b->next = blk->next
|
||||||
else
|
else
|
||||||
mem.write64(_builtin_heap_head(), blk->next)
|
mem.write64(_builtin_heap_head(), blk->next)
|
||||||
if blk->next
|
if blk->next as ptr
|
||||||
let b: mem.Block = blk->next
|
let b: mem.Block = blk->next
|
||||||
b->prev = blk->prev
|
b->prev = blk->prev
|
||||||
else
|
else
|
||||||
@@ -171,38 +173,38 @@ func mem.realloc?[x: ptr, new_size: i64] : ptr
|
|||||||
|
|
||||||
if new_size < 0
|
if new_size < 0
|
||||||
err.set(ERR_ALLOC_FAILED, "mem.realloc? called with negative size")
|
err.set(ERR_ALLOC_FAILED, "mem.realloc? called with negative size")
|
||||||
return 0
|
return 0 as ptr
|
||||||
|
|
||||||
if new_size == 0
|
if new_size == 0
|
||||||
mem.free(x)
|
mem.free(x)
|
||||||
return 0
|
return 0 as ptr
|
||||||
|
|
||||||
new_size = mem._align(new_size)
|
new_size = mem._align(new_size)
|
||||||
|
|
||||||
let blk: mem.Block = x - MEM_BLOCK_SIZE
|
let blk: mem.Block = (x - MEM_BLOCK_SIZE) as mem.Block
|
||||||
if blk->size >= new_size
|
if blk->size >= new_size
|
||||||
mem._split_block(blk, new_size)
|
mem._split_block(blk, new_size)
|
||||||
return x
|
return x
|
||||||
|
|
||||||
let next: mem.Block = blk->next
|
let next: mem.Block = blk->next
|
||||||
let expected_next: mem.Block = blk + MEM_BLOCK_SIZE + blk->size
|
let expected_next: mem.Block = (blk as ptr + MEM_BLOCK_SIZE + blk->size) as mem.Block
|
||||||
|
|
||||||
if next && next->free && expected_next == next
|
if next as ptr && next->free && expected_next as ptr == next as ptr
|
||||||
let combined: i64 = blk->size + MEM_BLOCK_SIZE + next->size
|
let combined: i64 = blk->size + MEM_BLOCK_SIZE + next->size
|
||||||
if combined >= new_size
|
if combined >= new_size
|
||||||
blk->size = combined
|
blk->size = combined
|
||||||
blk->next = next->next
|
blk->next = next->next
|
||||||
if next->next
|
if next->next as ptr
|
||||||
let b: mem.Block = next->next
|
let b: mem.Block = next->next
|
||||||
b->prev = blk
|
b->prev = blk
|
||||||
if mem.read64(_builtin_heap_tail()) == next
|
if mem.read64(_builtin_heap_tail()) == next as ptr
|
||||||
mem.write64(_builtin_heap_tail(), blk)
|
mem.write64(_builtin_heap_tail(), blk)
|
||||||
mem._split_block(blk, new_size)
|
mem._split_block(blk, new_size)
|
||||||
return x
|
return x
|
||||||
|
|
||||||
let new_ptr: ptr = mem.alloc?(new_size)
|
let new_ptr: ptr = mem.alloc?(new_size)
|
||||||
if err.check()
|
if err.check()
|
||||||
return 0
|
return 0 as ptr
|
||||||
|
|
||||||
mem.copy(x, new_ptr, math.min(blk->size, new_size))
|
mem.copy(x, new_ptr, math.min(blk->size, new_size))
|
||||||
mem.free(x)
|
mem.free(x)
|
||||||
@@ -214,7 +216,7 @@ func mem.zero_and_free[x: ptr, size: i64] : void
|
|||||||
|
|
||||||
func mem.zero[x: ptr, size: i64] : void
|
func mem.zero[x: ptr, size: i64] : void
|
||||||
for i in 0..size
|
for i in 0..size
|
||||||
x[i] = 0
|
x[i] = 0 as u8
|
||||||
|
|
||||||
func mem.copy[src: ptr, dst: ptr, n: i64] : void
|
func mem.copy[src: ptr, dst: ptr, n: i64] : void
|
||||||
if dst > src
|
if dst > src
|
||||||
@@ -230,16 +232,16 @@ func mem.read8[x: ptr] : u8
|
|||||||
return x[0]
|
return x[0]
|
||||||
|
|
||||||
func mem.read16[x: ptr] : i64
|
func mem.read16[x: ptr] : i64
|
||||||
return x[0] | (x[1] << 8)
|
return x[0] as i64 | (x[1] << 8)
|
||||||
|
|
||||||
func mem.read16be[x: ptr] : i64
|
func mem.read16be[x: ptr] : i64
|
||||||
return (x[0] << 8) | x[1]
|
return (x[0] << 8) as i64 | x[1]
|
||||||
|
|
||||||
func mem.read32[x: ptr] : i64
|
func mem.read32[x: ptr] : i64
|
||||||
return x[0] | (x[1] << 8) | (x[2] << 16) | (x[3] << 24)
|
return x[0] as i64 | (x[1] << 8) | (x[2] << 16) | (x[3] << 24)
|
||||||
|
|
||||||
func mem.read32be[x: ptr] : i64
|
func mem.read32be[x: ptr] : i64
|
||||||
return (x[0] << 24) | (x[1] << 16) | (x[2] << 8) | x[3]
|
return (x[0] as i64 << 24) | (x[1] << 16) | (x[2] << 8) | x[3]
|
||||||
|
|
||||||
func mem.read64[x: ptr] : i64
|
func mem.read64[x: ptr] : i64
|
||||||
return _builtin_read64(x)
|
return _builtin_read64(x)
|
||||||
@@ -258,11 +260,11 @@ func mem.write16be[x: ptr, d: i64] : void
|
|||||||
x[0] = (d >> 8) & 0xff
|
x[0] = (d >> 8) & 0xff
|
||||||
x[1] = d & 0xff
|
x[1] = d & 0xff
|
||||||
|
|
||||||
func mem.write64[x: ptr, d: i64] : void
|
func mem.write64[x: ptr, d: any] : void
|
||||||
_builtin_set64(x, d)
|
_builtin_set64(x, d)
|
||||||
|
|
||||||
// see emit_prologue for the io.printf wrapper
|
// see emit_prologue for the io.printf wrapper
|
||||||
func io._printf_impl[s: str, args: ptr] : void
|
func io._printf_impl[s: ptr, args: ptr] : void
|
||||||
while s[0]
|
while s[0]
|
||||||
if s[0] == '%'
|
if s[0] == '%'
|
||||||
s = s + 1
|
s = s + 1
|
||||||
@@ -274,10 +276,10 @@ func io._printf_impl[s: str, args: ptr] : void
|
|||||||
io.print_i64_hex(mem.read64(args))
|
io.print_i64_hex(mem.read64(args))
|
||||||
args = args + 8
|
args = args + 8
|
||||||
else if s[0] == 's'
|
else if s[0] == 's'
|
||||||
io.print(mem.read64(args))
|
io.print(mem.read64(args) as str)
|
||||||
args = args + 8
|
args = args + 8
|
||||||
else if s[0] == 'c'
|
else if s[0] == 'c'
|
||||||
io.print_char(mem.read64(args))
|
io.print_char(mem.read64(args) as u8)
|
||||||
args = args + 8
|
args = args + 8
|
||||||
else if s[0] == '%'
|
else if s[0] == '%'
|
||||||
io.print_char('%')
|
io.print_char('%')
|
||||||
@@ -287,11 +289,11 @@ func io._printf_impl[s: str, args: ptr] : void
|
|||||||
io.print_char(s[0])
|
io.print_char(s[0])
|
||||||
s = s + 1
|
s = s + 1
|
||||||
|
|
||||||
func io.print_sized[x: str, size: i64] : void
|
func io.print_sized[x: ptr, size: i64] : void
|
||||||
_builtin_syscall(SYS_write, 1, x, size)
|
_builtin_syscall(SYS_write, 1, x, size)
|
||||||
|
|
||||||
func io.print[x: str] : void
|
func io.print[x: str] : void
|
||||||
io.print_sized(x, str.len(x))
|
io.print_sized(x as ptr, str.len(x))
|
||||||
|
|
||||||
func io.println[x: str] : void
|
func io.println[x: str] : void
|
||||||
io.print(x)
|
io.print(x)
|
||||||
@@ -322,7 +324,7 @@ func io.println_i64[x: i64] : void
|
|||||||
mem.free(s)
|
mem.free(s)
|
||||||
|
|
||||||
func io.read_char[] : u8
|
func io.read_char[] : u8
|
||||||
let c: u8 = 0
|
let c: u8 = 0 as u8
|
||||||
_builtin_syscall(SYS_read, 0, ^c, 1)
|
_builtin_syscall(SYS_read, 0, ^c, 1)
|
||||||
return c
|
return c
|
||||||
|
|
||||||
@@ -333,7 +335,7 @@ func io.read_line[]: str
|
|||||||
if n < 0
|
if n < 0
|
||||||
n = 0
|
n = 0
|
||||||
buffer[n] = 0
|
buffer[n] = 0
|
||||||
buffer = must(mem.realloc?(buffer, n + 1))
|
buffer = must(mem.realloc?(buffer as ptr, n + 1))
|
||||||
return buffer
|
return buffer
|
||||||
|
|
||||||
struct io.Buffer
|
struct io.Buffer
|
||||||
@@ -347,7 +349,7 @@ func io.Buffer.alloc?[size: i64] : io.Buffer
|
|||||||
buffer->data = mem.alloc?(size)
|
buffer->data = mem.alloc?(size)
|
||||||
if err.check()
|
if err.check()
|
||||||
mem.free(buffer)
|
mem.free(buffer)
|
||||||
return 0
|
return 0 as io.Buffer
|
||||||
return buffer
|
return buffer
|
||||||
|
|
||||||
func io.Buffer.free[buf: io.Buffer] : void
|
func io.Buffer.free[buf: io.Buffer] : void
|
||||||
@@ -362,15 +364,15 @@ func io.read_text_file?[path: str] : str
|
|||||||
let fd: i64 = _builtin_syscall(SYS_openat, -100, path, 0, 0)
|
let fd: i64 = _builtin_syscall(SYS_openat, -100, path, 0, 0)
|
||||||
if fd < 0
|
if fd < 0
|
||||||
err.set(ERR_OPEN_FAILED, "io.read_text_file?: failed to open file")
|
err.set(ERR_OPEN_FAILED, "io.read_text_file?: failed to open file")
|
||||||
return 0
|
return 0 as str
|
||||||
|
|
||||||
let size: i64 = _builtin_syscall(SYS_lseek, fd, 0, 2)
|
let size: i64 = _builtin_syscall(SYS_lseek, fd, 0, 2)
|
||||||
_builtin_syscall(SYS_lseek, fd, 0, 0)
|
_builtin_syscall(SYS_lseek, fd, 0, 0)
|
||||||
|
|
||||||
let buffer: str = mem.alloc?(size + 1)
|
let buffer: str = mem.alloc?(size + 1) as str
|
||||||
if err.check()
|
if err.check()
|
||||||
_builtin_syscall(SYS_close, fd)
|
_builtin_syscall(SYS_close, fd)
|
||||||
return 0
|
return 0 as str
|
||||||
|
|
||||||
let n: i64 = _builtin_syscall(SYS_read, fd, buffer, size)
|
let n: i64 = _builtin_syscall(SYS_read, fd, buffer, size)
|
||||||
_builtin_syscall(SYS_close, fd)
|
_builtin_syscall(SYS_close, fd)
|
||||||
@@ -378,7 +380,7 @@ func io.read_text_file?[path: str] : str
|
|||||||
if n != size
|
if n != size
|
||||||
mem.free(buffer)
|
mem.free(buffer)
|
||||||
err.set(ERR_READ_FAILED, "io.read_text_file?: failed to read file")
|
err.set(ERR_READ_FAILED, "io.read_text_file?: failed to read file")
|
||||||
return 0
|
return 0 as str
|
||||||
|
|
||||||
buffer[n] = 0
|
buffer[n] = 0
|
||||||
return buffer
|
return buffer
|
||||||
@@ -388,7 +390,7 @@ func io.read_binary_file?[path: str] : io.Buffer
|
|||||||
let fd: i64 = _builtin_syscall(SYS_openat, -100, path, 0, 0)
|
let fd: i64 = _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
|
return 0 as io.Buffer
|
||||||
|
|
||||||
let buf: io.Buffer = new io.Buffer
|
let buf: io.Buffer = new io.Buffer
|
||||||
buf->size = _builtin_syscall(SYS_lseek, fd, 0, 2)
|
buf->size = _builtin_syscall(SYS_lseek, fd, 0, 2)
|
||||||
@@ -398,7 +400,7 @@ func io.read_binary_file?[path: str] : io.Buffer
|
|||||||
if err.check()
|
if err.check()
|
||||||
io.Buffer.free(buf)
|
io.Buffer.free(buf)
|
||||||
_builtin_syscall(SYS_close, fd)
|
_builtin_syscall(SYS_close, fd)
|
||||||
return 0
|
return 0 as io.Buffer
|
||||||
|
|
||||||
let n: i64 = _builtin_syscall(SYS_read, fd, buf->data, buf->size)
|
let n: i64 = _builtin_syscall(SYS_read, fd, buf->data, buf->size)
|
||||||
_builtin_syscall(SYS_close, fd)
|
_builtin_syscall(SYS_close, fd)
|
||||||
@@ -406,12 +408,12 @@ func io.read_binary_file?[path: str] : io.Buffer
|
|||||||
if n != buf->size
|
if n != buf->size
|
||||||
io.Buffer.free(buf)
|
io.Buffer.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
|
return 0 as io.Buffer
|
||||||
|
|
||||||
return buf
|
return buf
|
||||||
|
|
||||||
func io.write_file?[path: str, content: str] : void
|
func io.write_file?[path: str, content: str] : void
|
||||||
io.write_binary_file?(path, content, str.len(content))
|
io.write_binary_file?(path, content as ptr, str.len(content))
|
||||||
|
|
||||||
func io.write_binary_file?[path: str, content: ptr, size: i64] : void
|
func io.write_binary_file?[path: str, content: ptr, size: i64] : void
|
||||||
err.clear()
|
err.clear()
|
||||||
@@ -435,7 +437,7 @@ func str.len[s: str] : i64
|
|||||||
func str.make_copy[s: str] : str
|
func str.make_copy[s: str] : str
|
||||||
let size: i64 = str.len(s) + 1
|
let size: i64 = str.len(s) + 1
|
||||||
let dup: str = mem.alloc(size)
|
let dup: str = mem.alloc(size)
|
||||||
mem.copy(s, dup, size)
|
mem.copy(s as ptr, dup as ptr, size)
|
||||||
return dup
|
return dup
|
||||||
|
|
||||||
func str.equal[a: str, b: str] : bool
|
func str.equal[a: str, b: str] : bool
|
||||||
@@ -471,8 +473,8 @@ func str.concat[a: str, b: str] : str
|
|||||||
let a_len: i64 = str.len(a)
|
let a_len: i64 = str.len(a)
|
||||||
let b_len: i64 = str.len(b)
|
let b_len: i64 = str.len(b)
|
||||||
let out: str = mem.alloc(a_len + b_len + 1)
|
let out: str = mem.alloc(a_len + b_len + 1)
|
||||||
mem.copy(a, out, a_len)
|
mem.copy(a as ptr, out as ptr, a_len)
|
||||||
mem.copy(b, out + a_len, b_len)
|
mem.copy(b as ptr, out as ptr + a_len, b_len)
|
||||||
out[a_len + b_len] = 0
|
out[a_len + b_len] = 0
|
||||||
return out
|
return out
|
||||||
|
|
||||||
@@ -504,7 +506,7 @@ func str.substr[s: str, start: i64, length: i64] : str
|
|||||||
panic("str.substr out of bounds")
|
panic("str.substr out of bounds")
|
||||||
|
|
||||||
let out: str = mem.alloc(length + 1)
|
let out: str = mem.alloc(length + 1)
|
||||||
mem.copy(s + start, out, length)
|
mem.copy(s as ptr + start, out as ptr, length)
|
||||||
out[length] = 0
|
out[length] = 0
|
||||||
return out
|
return out
|
||||||
|
|
||||||
@@ -591,7 +593,7 @@ func str.from_i64[n: i64] : str
|
|||||||
if neg
|
if neg
|
||||||
buf[end] = '-'
|
buf[end] = '-'
|
||||||
end = end - 1
|
end = end - 1
|
||||||
let s: str = str.make_copy(buf + end + 1)
|
let s: str = str.make_copy((buf as ptr + end + 1) as str)
|
||||||
mem.free(buf)
|
mem.free(buf)
|
||||||
return s
|
return s
|
||||||
|
|
||||||
@@ -662,7 +664,7 @@ func str.hex_encode[s: str, s_len: i64] : str
|
|||||||
out[j] = 0
|
out[j] = 0
|
||||||
return out
|
return out
|
||||||
|
|
||||||
func str._hex_digit_to_int[d: u8] : i64
|
func str._hex_digit_to_int[d: u8] : u8
|
||||||
if d >= 'a' && d <= 'f'
|
if d >= 'a' && d <= 'f'
|
||||||
return d - 'a' + 10
|
return d - 'a' + 10
|
||||||
if d >= 'A' && d <= 'F'
|
if d >= 'A' && d <= 'F'
|
||||||
@@ -769,10 +771,10 @@ struct Array
|
|||||||
func array.new[] : Array
|
func array.new[] : Array
|
||||||
return new Array
|
return new Array
|
||||||
|
|
||||||
func array.new_with_size_zeroed[size: i64] : Array
|
func array.new_preallocated_and_zeroed[size: i64] : Array
|
||||||
let xs: Array = new Array
|
let xs: Array = new Array
|
||||||
if size > 0
|
if size > 0
|
||||||
xs->data = must(mem.realloc?(xs->data, size * 8))
|
xs->data = must(mem.realloc?(xs->data, size * 8)) as ptr
|
||||||
mem.zero(xs->data, size * 8)
|
mem.zero(xs->data, size * 8)
|
||||||
xs->size = size
|
xs->size = size
|
||||||
xs->capacity = size
|
xs->capacity = size
|
||||||
@@ -815,13 +817,13 @@ func array.slice[xs: Array, start: i64, length: i64] : Array
|
|||||||
if start < 0 || length < 0 || start + length > xs->size
|
if start < 0 || length < 0 || start + length > xs->size
|
||||||
panic("array.slice out of bounds")
|
panic("array.slice out of bounds")
|
||||||
|
|
||||||
let new_array: Array = array.new_with_size_zeroed(length)
|
let new_array: Array = array.new_preallocated_and_zeroed(length)
|
||||||
for i in 0..length
|
for i in 0..length
|
||||||
array.set(new_array, i, array.nth(xs, start + i))
|
array.set(new_array, i, array.nth(xs, start + i))
|
||||||
return new_array
|
return new_array
|
||||||
|
|
||||||
func array.concat[a: Array, b: Array] : Array
|
func array.concat[a: Array, b: Array] : Array
|
||||||
let new_array: Array = array.new_with_size_zeroed(a->size + b->size)
|
let new_array: Array = array.new_preallocated_and_zeroed(a->size + b->size)
|
||||||
for i in 0..a->size
|
for i in 0..a->size
|
||||||
array.set(new_array, i, array.nth(a, i))
|
array.set(new_array, i, array.nth(a, i))
|
||||||
for i in 0..b->size
|
for i in 0..b->size
|
||||||
@@ -841,7 +843,7 @@ func alg._partition[arr: Array, low: i64, high: i64] : i64
|
|||||||
let pivot: i64 = array.nth(arr, high)
|
let pivot: i64 = array.nth(arr, high)
|
||||||
let i: i64 = low - 1
|
let i: i64 = low - 1
|
||||||
for j in (low)..high
|
for j in (low)..high
|
||||||
if array.nth(arr, j) <= pivot
|
if array.nth(arr, j) as i64 <= pivot
|
||||||
i = i + 1
|
i = i + 1
|
||||||
let temp: i64 = array.nth(arr ,i)
|
let temp: i64 = array.nth(arr ,i)
|
||||||
array.set(arr, i, array.nth(arr, j))
|
array.set(arr, i, array.nth(arr, j))
|
||||||
@@ -858,20 +860,20 @@ func alg.count[arr: Array, item: any] : i64
|
|||||||
count = count + 1
|
count = count + 1
|
||||||
return count
|
return count
|
||||||
|
|
||||||
func alg.map[arr: Array, fn: ptr] : Array
|
func alg.map[arr: Array, fn: fnptr] : Array
|
||||||
let out: Array = array.new_with_size_zeroed(arr->size)
|
let out: Array = array.new_preallocated_and_zeroed(arr->size)
|
||||||
for i in 0..arr->size
|
for i in 0..arr->size
|
||||||
array.set(out, i, fn(array.nth(arr, i)))
|
array.set(out, i, fn(array.nth(arr, i)))
|
||||||
return out
|
return out
|
||||||
|
|
||||||
func alg.filter[arr: Array, fn: ptr] : Array
|
func alg.filter[arr: Array, fn: fnptr] : Array
|
||||||
let out: Array = []
|
let out: Array = []
|
||||||
for i in 0..arr->size
|
for i in 0..arr->size
|
||||||
if fn(array.nth(arr, i))
|
if fn(array.nth(arr, i))
|
||||||
array.push(out, array.nth(arr, i))
|
array.push(out, array.nth(arr, i))
|
||||||
return out
|
return out
|
||||||
|
|
||||||
func alg.reduce[arr: Array, fn: ptr, acc: any] : any
|
func alg.reduce[arr: Array, fn: fnptr, acc: any] : any
|
||||||
for i in 0..arr->size
|
for i in 0..arr->size
|
||||||
acc = fn(acc, array.nth(arr, i))
|
acc = fn(acc, array.nth(arr, i))
|
||||||
return acc
|
return acc
|
||||||
@@ -900,13 +902,13 @@ func os.urandom?[n: i64]: ptr
|
|||||||
err.clear()
|
err.clear()
|
||||||
let buffer: ptr = mem.alloc(n)
|
let buffer: ptr = mem.alloc(n)
|
||||||
if err.check()
|
if err.check()
|
||||||
return 0
|
return 0 as ptr
|
||||||
|
|
||||||
let fd: i64 = _builtin_syscall(SYS_openat, -100, "/dev/urandom", 0, 0)
|
let fd: i64 = _builtin_syscall(SYS_openat, -100, "/dev/urandom", 0, 0)
|
||||||
if fd < 0
|
if fd < 0
|
||||||
mem.free(buffer)
|
mem.free(buffer)
|
||||||
err.set(ERR_READ_FAILED, "os.urandom?: failed to open /dev/urandom")
|
err.set(ERR_READ_FAILED, "os.urandom?: failed to open /dev/urandom")
|
||||||
return 0
|
return 0 as ptr
|
||||||
|
|
||||||
let bytes_read: i64 = _builtin_syscall(SYS_read, fd, buffer, n)
|
let bytes_read: i64 = _builtin_syscall(SYS_read, fd, buffer, n)
|
||||||
_builtin_syscall(SYS_close, fd)
|
_builtin_syscall(SYS_close, fd)
|
||||||
@@ -914,7 +916,7 @@ func os.urandom?[n: i64]: ptr
|
|||||||
if n != bytes_read
|
if n != bytes_read
|
||||||
mem.free(buffer)
|
mem.free(buffer)
|
||||||
err.set(ERR_READ_FAILED, "os.urandom?: failed to read /dev/urandom")
|
err.set(ERR_READ_FAILED, "os.urandom?: failed to read /dev/urandom")
|
||||||
return 0
|
return 0 as ptr
|
||||||
|
|
||||||
return buffer
|
return buffer
|
||||||
|
|
||||||
@@ -966,7 +968,7 @@ func os.list_directory?[path: str] : Array
|
|||||||
let fd: i64 = _builtin_syscall(SYS_openat, -100, path, 0, 0)
|
let fd: i64 = _builtin_syscall(SYS_openat, -100, path, 0, 0)
|
||||||
if fd < 0
|
if fd < 0
|
||||||
err.set(ERR_OPEN_FAILED, "os.list_directory?: failed to open dir")
|
err.set(ERR_OPEN_FAILED, "os.list_directory?: failed to open dir")
|
||||||
return 0
|
return 0 as Array
|
||||||
|
|
||||||
let files: Array = []
|
let files: Array = []
|
||||||
let buf: ptr = mem.alloc(1024)
|
let buf: ptr = mem.alloc(1024)
|
||||||
@@ -981,14 +983,14 @@ func os.list_directory?[path: str] : Array
|
|||||||
|
|
||||||
_builtin_syscall(SYS_close, fd)
|
_builtin_syscall(SYS_close, fd)
|
||||||
err.set(ERR_SYSCALL_FAILED, "os.list_directory?: getdents64() failed")
|
err.set(ERR_SYSCALL_FAILED, "os.list_directory?: getdents64() failed")
|
||||||
return 0
|
return 0 as Array
|
||||||
else if n == 0
|
else if n == 0
|
||||||
break
|
break
|
||||||
|
|
||||||
let pos = 0
|
let pos = 0
|
||||||
while pos < n
|
while pos < n
|
||||||
let len: i64 = mem.read16(buf + pos + 16)
|
let len: i64 = mem.read16(buf + pos + 16)
|
||||||
let name: str = buf + pos + 19
|
let name: ptr = buf + pos + 19
|
||||||
if name[0]
|
if name[0]
|
||||||
let skip: bool = false
|
let skip: bool = false
|
||||||
// skip if name is exactly '.' or '..'
|
// skip if name is exactly '.' or '..'
|
||||||
@@ -999,7 +1001,7 @@ func os.list_directory?[path: str] : Array
|
|||||||
if name[2] == 0
|
if name[2] == 0
|
||||||
skip = true
|
skip = true
|
||||||
if !skip
|
if !skip
|
||||||
array.push(files, str.make_copy(name))
|
array.push(files, str.make_copy(name as str))
|
||||||
pos = pos + len
|
pos = pos + len
|
||||||
|
|
||||||
mem.free(buf)
|
mem.free(buf)
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ pub enum TokenType {
|
|||||||
KeywordExport,
|
KeywordExport,
|
||||||
KeywordStruct,
|
KeywordStruct,
|
||||||
KeywordNew,
|
KeywordNew,
|
||||||
|
KeywordAs,
|
||||||
|
|
||||||
Indent,
|
Indent,
|
||||||
Dedent,
|
Dedent,
|
||||||
@@ -371,6 +372,7 @@ impl Tokenizer {
|
|||||||
"export" => TokenType::KeywordExport,
|
"export" => TokenType::KeywordExport,
|
||||||
"struct" => TokenType::KeywordStruct,
|
"struct" => TokenType::KeywordStruct,
|
||||||
"new" => TokenType::KeywordNew,
|
"new" => TokenType::KeywordNew,
|
||||||
|
"as" => TokenType::KeywordAs,
|
||||||
"true" => TokenType::True,
|
"true" => TokenType::True,
|
||||||
"false" => TokenType::False,
|
"false" => TokenType::False,
|
||||||
_ => TokenType::Identifier,
|
_ => TokenType::Identifier,
|
||||||
|
|||||||
481
src/typechecker.rs
Normal file
481
src/typechecker.rs
Normal file
@@ -0,0 +1,481 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
analyzer::{Analyzer, Type},
|
||||||
|
parser::{Expr, Stmt},
|
||||||
|
tokenizer::{TokenType, ZernError, error},
|
||||||
|
};
|
||||||
|
|
||||||
|
macro_rules! expect_type {
|
||||||
|
($expr_type:expr, $expected:expr, $loc:expr) => {{
|
||||||
|
let actual = $expr_type;
|
||||||
|
if $expected != "any" && actual != "any" && actual != $expected {
|
||||||
|
return error!(
|
||||||
|
$loc,
|
||||||
|
format!("expected type '{}', got '{}'", $expected, actual)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! expect_types {
|
||||||
|
($expr_type:expr, [$( $expected:expr ),+], $loc:expr) => {
|
||||||
|
if $expr_type != "any" && $( $expr_type != $expected )&&+ {
|
||||||
|
return error!(
|
||||||
|
$loc,
|
||||||
|
format!(
|
||||||
|
"expected one of [{}], got '{}'",
|
||||||
|
[$( $expected ),+].join(", "),
|
||||||
|
$expr_type
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static BUILTIN_TYPES: [&str; 8] = ["void", "u8", "i64", "str", "bool", "ptr", "fnptr", "any"];
|
||||||
|
|
||||||
|
pub struct Env {
|
||||||
|
scopes: Vec<HashMap<String, Type>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Env {
|
||||||
|
pub fn new() -> Env {
|
||||||
|
Env {
|
||||||
|
scopes: vec![HashMap::new()],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_scope(&mut self) {
|
||||||
|
self.scopes.push(HashMap::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop_scope(&mut self) {
|
||||||
|
assert!(!self.scopes.is_empty());
|
||||||
|
self.scopes.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn define_var(&mut self, name: String, var_type: String) {
|
||||||
|
self.scopes.last_mut().unwrap().insert(name, var_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_var_type(&self, name: &str) -> Option<&Type> {
|
||||||
|
for scope in self.scopes.iter().rev() {
|
||||||
|
if let Some(var) = scope.get(name) {
|
||||||
|
return Some(var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TypeChecker<'a> {
|
||||||
|
analyzer: &'a Analyzer,
|
||||||
|
current_function_return_type: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TypeChecker<'a> {
|
||||||
|
pub fn new(analyzer: &'a Analyzer) -> TypeChecker<'a> {
|
||||||
|
TypeChecker {
|
||||||
|
analyzer,
|
||||||
|
current_function_return_type: String::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn typecheck_stmt(&mut self, env: &mut Env, stmt: &Stmt) -> Result<(), ZernError> {
|
||||||
|
match stmt {
|
||||||
|
Stmt::Expression(expr) => {
|
||||||
|
self.typecheck_expr(env, expr)?;
|
||||||
|
}
|
||||||
|
Stmt::Let {
|
||||||
|
name,
|
||||||
|
var_type,
|
||||||
|
initializer,
|
||||||
|
} => {
|
||||||
|
let mut actual_type = self.typecheck_expr(env, initializer)?;
|
||||||
|
if let Some(var_type) = var_type {
|
||||||
|
if !self.is_valid_type_name(&var_type.lexeme) {
|
||||||
|
return error!(
|
||||||
|
&name.loc,
|
||||||
|
"unrecognized type: ".to_owned() + &var_type.lexeme
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect_type!(actual_type.clone(), var_type.lexeme, var_type.loc);
|
||||||
|
|
||||||
|
if actual_type == "any" {
|
||||||
|
actual_type = var_type.lexeme.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
env.define_var(name.lexeme.clone(), actual_type);
|
||||||
|
}
|
||||||
|
Stmt::Const { name: _, value: _ } => {
|
||||||
|
// handled in the analyzer
|
||||||
|
}
|
||||||
|
Stmt::Block(stmts) => {
|
||||||
|
env.push_scope();
|
||||||
|
for stmt in stmts {
|
||||||
|
self.typecheck_stmt(env, stmt)?;
|
||||||
|
}
|
||||||
|
env.pop_scope();
|
||||||
|
}
|
||||||
|
Stmt::If {
|
||||||
|
keyword,
|
||||||
|
condition,
|
||||||
|
then_branch,
|
||||||
|
else_branch,
|
||||||
|
} => {
|
||||||
|
expect_types!(
|
||||||
|
self.typecheck_expr(env, condition)?,
|
||||||
|
["i64", "u8", "ptr", "bool"],
|
||||||
|
keyword.loc
|
||||||
|
);
|
||||||
|
self.typecheck_stmt(env, then_branch)?;
|
||||||
|
self.typecheck_stmt(env, else_branch)?;
|
||||||
|
}
|
||||||
|
Stmt::While {
|
||||||
|
keyword,
|
||||||
|
condition,
|
||||||
|
body,
|
||||||
|
} => {
|
||||||
|
expect_types!(
|
||||||
|
self.typecheck_expr(env, condition)?,
|
||||||
|
["i64", "u8", "ptr", "bool"],
|
||||||
|
keyword.loc
|
||||||
|
);
|
||||||
|
self.typecheck_stmt(env, body)?;
|
||||||
|
}
|
||||||
|
Stmt::For {
|
||||||
|
var,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
body,
|
||||||
|
} => {
|
||||||
|
expect_type!(self.typecheck_expr(env, start)?, "i64", var.loc);
|
||||||
|
expect_type!(self.typecheck_expr(env, end)?, "i64", var.loc);
|
||||||
|
|
||||||
|
env.push_scope();
|
||||||
|
env.define_var(var.lexeme.clone(), "i64".into());
|
||||||
|
self.typecheck_stmt(env, body)?;
|
||||||
|
env.pop_scope();
|
||||||
|
}
|
||||||
|
Stmt::Function {
|
||||||
|
name: _,
|
||||||
|
params,
|
||||||
|
return_type,
|
||||||
|
body,
|
||||||
|
exported: _,
|
||||||
|
} => {
|
||||||
|
if !self.is_valid_type_name(&return_type.lexeme) {
|
||||||
|
return error!(
|
||||||
|
&return_type.loc,
|
||||||
|
"unrecognized type: ".to_owned() + &return_type.lexeme
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.current_function_return_type = return_type.lexeme.clone();
|
||||||
|
|
||||||
|
env.push_scope();
|
||||||
|
|
||||||
|
for param in params {
|
||||||
|
if !self.is_valid_type_name(¶m.var_type.lexeme) {
|
||||||
|
return error!(
|
||||||
|
¶m.var_name.loc,
|
||||||
|
"unrecognized type: ".to_owned() + ¶m.var_type.lexeme
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
env.define_var(param.var_name.lexeme.clone(), param.var_type.lexeme.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.typecheck_stmt(env, body)?;
|
||||||
|
|
||||||
|
env.pop_scope();
|
||||||
|
}
|
||||||
|
Stmt::Return { expr, keyword } => {
|
||||||
|
let expected = if self.current_function_return_type == "void" {
|
||||||
|
"i64".into()
|
||||||
|
} else {
|
||||||
|
self.current_function_return_type.clone()
|
||||||
|
};
|
||||||
|
expect_type!(self.typecheck_expr(env, expr)?, expected, keyword.loc);
|
||||||
|
}
|
||||||
|
Stmt::Break => {}
|
||||||
|
Stmt::Continue => {}
|
||||||
|
Stmt::Extern(_) => {
|
||||||
|
// handled in the analyzer
|
||||||
|
}
|
||||||
|
Stmt::Struct { name: _, fields } => {
|
||||||
|
for field in fields {
|
||||||
|
if !self.is_valid_type_name(&field.var_type.lexeme) {
|
||||||
|
return error!(
|
||||||
|
&field.var_type.loc,
|
||||||
|
format!("unknown type: {}", &field.var_type.lexeme)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn typecheck_expr(&self, env: &mut Env, expr: &Expr) -> Result<Type, ZernError> {
|
||||||
|
match expr {
|
||||||
|
Expr::Binary { left, op, right } => {
|
||||||
|
let left_type = self.typecheck_expr(env, left)?;
|
||||||
|
|
||||||
|
match op.token_type {
|
||||||
|
TokenType::Plus | TokenType::Minus => {
|
||||||
|
expect_types!(left_type, ["i64", "ptr", "u8"], op.loc);
|
||||||
|
expect_types!(
|
||||||
|
self.typecheck_expr(env, right)?,
|
||||||
|
["i64", "ptr", "u8"],
|
||||||
|
op.loc
|
||||||
|
);
|
||||||
|
Ok(left_type)
|
||||||
|
}
|
||||||
|
TokenType::Star
|
||||||
|
| TokenType::Slash
|
||||||
|
| TokenType::Mod
|
||||||
|
| TokenType::Xor
|
||||||
|
| TokenType::BitAnd
|
||||||
|
| TokenType::BitOr
|
||||||
|
| TokenType::ShiftLeft
|
||||||
|
| TokenType::ShiftRight => {
|
||||||
|
expect_types!(left_type, ["i64", "u8"], op.loc);
|
||||||
|
expect_types!(self.typecheck_expr(env, right)?, ["i64", "u8"], op.loc);
|
||||||
|
Ok(left_type)
|
||||||
|
}
|
||||||
|
TokenType::DoubleEqual
|
||||||
|
| TokenType::NotEqual
|
||||||
|
| TokenType::Greater
|
||||||
|
| TokenType::GreaterEqual
|
||||||
|
| TokenType::Less
|
||||||
|
| TokenType::LessEqual => {
|
||||||
|
expect_types!(left_type, ["i64", "ptr", "u8"], op.loc);
|
||||||
|
expect_types!(
|
||||||
|
self.typecheck_expr(env, right)?,
|
||||||
|
["i64", "ptr", "u8"],
|
||||||
|
op.loc
|
||||||
|
);
|
||||||
|
Ok("bool".into())
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::Logical { left, op, right } => {
|
||||||
|
expect_types!(
|
||||||
|
self.typecheck_expr(env, left)?,
|
||||||
|
["bool", "i64", "ptr"],
|
||||||
|
op.loc
|
||||||
|
);
|
||||||
|
expect_types!(
|
||||||
|
self.typecheck_expr(env, right)?,
|
||||||
|
["bool", "i64", "ptr"],
|
||||||
|
op.loc
|
||||||
|
);
|
||||||
|
Ok("bool".into())
|
||||||
|
}
|
||||||
|
Expr::Grouping(expr) => self.typecheck_expr(env, expr),
|
||||||
|
Expr::Literal(token) => match token.token_type {
|
||||||
|
TokenType::Number => Ok("i64".into()),
|
||||||
|
TokenType::Char => Ok("u8".into()),
|
||||||
|
TokenType::String => Ok("str".into()),
|
||||||
|
TokenType::True => Ok("bool".into()),
|
||||||
|
TokenType::False => Ok("bool".into()),
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
Expr::Unary { op, right } => {
|
||||||
|
let right_type = self.typecheck_expr(env, right)?;
|
||||||
|
match op.token_type {
|
||||||
|
TokenType::Minus => {
|
||||||
|
expect_type!(right_type, "i64", op.loc);
|
||||||
|
Ok("i64".into())
|
||||||
|
}
|
||||||
|
TokenType::Bang => {
|
||||||
|
expect_types!(right_type, ["bool", "i64", "ptr", "u8"], op.loc);
|
||||||
|
Ok("bool".into())
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::Variable(name) => {
|
||||||
|
if self.analyzer.constants.contains_key(&name.lexeme) {
|
||||||
|
Ok("i64".into())
|
||||||
|
} else {
|
||||||
|
match env.get_var_type(&name.lexeme) {
|
||||||
|
Some(x) => Ok(x.clone()),
|
||||||
|
None => error!(name.loc, format!("undefined variable: {}", &name.lexeme)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::Assign { left, op, value } => {
|
||||||
|
let value_type = self.typecheck_expr(env, value)?;
|
||||||
|
|
||||||
|
match left.as_ref() {
|
||||||
|
Expr::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);
|
||||||
|
}
|
||||||
|
Expr::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);
|
||||||
|
}
|
||||||
|
Expr::MemberAccess { left, field } => {
|
||||||
|
let left_type = self.typecheck_expr(env, left)?;
|
||||||
|
|
||||||
|
let fields = match self.analyzer.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)
|
||||||
|
}
|
||||||
|
Expr::Call {
|
||||||
|
callee,
|
||||||
|
paren,
|
||||||
|
args,
|
||||||
|
} => {
|
||||||
|
if let Expr::Variable(callee_name) = &**callee {
|
||||||
|
if self.analyzer.functions.contains_key(&callee_name.lexeme) {
|
||||||
|
let fn_type = &self.analyzer.functions[&callee_name.lexeme];
|
||||||
|
// its a function (defined/builtin/extern)
|
||||||
|
if let Some(params) = fn_type.params.clone() {
|
||||||
|
for (i, arg) in args.iter().enumerate() {
|
||||||
|
// arity is checked in the analyzer
|
||||||
|
expect_type!(self.typecheck_expr(env, arg)?, params[i], paren.loc);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// its a variadic function, cant check arg types
|
||||||
|
for arg in args {
|
||||||
|
self.typecheck_expr(env, arg)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(fn_type.return_type.clone())
|
||||||
|
} else {
|
||||||
|
// its a variable containing function address
|
||||||
|
expect_type!(self.typecheck_expr(env, callee)?, "fnptr", paren.loc);
|
||||||
|
|
||||||
|
for arg in args {
|
||||||
|
self.typecheck_expr(env, arg)?;
|
||||||
|
}
|
||||||
|
Ok("any".into())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// its an expression that evalutes to function address
|
||||||
|
expect_type!(self.typecheck_expr(env, callee)?, "fnptr", paren.loc);
|
||||||
|
|
||||||
|
for arg in args {
|
||||||
|
self.typecheck_expr(env, arg)?;
|
||||||
|
}
|
||||||
|
Ok("any".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::ArrayLiteral(exprs) => {
|
||||||
|
for expr in exprs {
|
||||||
|
self.typecheck_expr(env, expr)?;
|
||||||
|
}
|
||||||
|
Ok("Array".into())
|
||||||
|
}
|
||||||
|
Expr::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);
|
||||||
|
Ok("u8".into())
|
||||||
|
}
|
||||||
|
Expr::AddrOf { op, expr } => match expr.as_ref() {
|
||||||
|
Expr::Variable(name) => {
|
||||||
|
if self.analyzer.functions.contains_key(&name.lexeme) {
|
||||||
|
Ok("fnptr".into())
|
||||||
|
} else {
|
||||||
|
Ok("ptr".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
error!(&op.loc, "can only take address of variables and functions")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Expr::New(struct_name) => {
|
||||||
|
if !self.analyzer.structs.contains_key(&struct_name.lexeme) {
|
||||||
|
return error!(
|
||||||
|
&struct_name.loc,
|
||||||
|
format!("unknown struct name: {}", &struct_name.lexeme)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(struct_name.lexeme.clone())
|
||||||
|
}
|
||||||
|
Expr::MemberAccess { left, field } => {
|
||||||
|
let left_type = self.typecheck_expr(env, left)?;
|
||||||
|
|
||||||
|
let fields = match self.analyzer.structs.get(&left_type) {
|
||||||
|
Some(f) => f,
|
||||||
|
None => {
|
||||||
|
return error!(&field.loc, format!("unknown struct type: {}", left_type));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let field = match fields.get(&field.lexeme) {
|
||||||
|
Some(o) => o,
|
||||||
|
None => return error!(&field.loc, format!("unknown field: {}", &field.lexeme)),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(field.field_type.clone())
|
||||||
|
}
|
||||||
|
Expr::Cast { expr, type_name } => {
|
||||||
|
self.typecheck_expr(env, expr)?;
|
||||||
|
if !self.is_valid_type_name(&type_name.lexeme) {
|
||||||
|
return error!(
|
||||||
|
&type_name.loc,
|
||||||
|
format!("unknown type: {}", &type_name.lexeme)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(type_name.lexeme.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_valid_type_name(&self, name: &str) -> bool {
|
||||||
|
if BUILTIN_TYPES.contains(&name) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if self.analyzer.structs.contains_key(name) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user