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")
|
||||
return 1
|
||||
|
||||
let url: str = mem.read64(argv + 8)
|
||||
let url: str = mem.read64(argv + 8) as str
|
||||
|
||||
if str.len(url) <= 7
|
||||
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, host)
|
||||
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)
|
||||
|
||||
let header_buf: str = mem.alloc(8192)
|
||||
@@ -45,7 +45,7 @@ func main[argc: i64, argv: ptr] : i64
|
||||
let end_index: i64 = -1
|
||||
|
||||
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
|
||||
break
|
||||
let current_size: i64 = header_size + n
|
||||
@@ -59,7 +59,7 @@ func main[argc: i64, argv: ptr] : i64
|
||||
header_size = current_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)
|
||||
|
||||
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)
|
||||
|
||||
for i in (start)..(len-remaining+1)
|
||||
let v: i64 = bank[i] - '0'
|
||||
let v: i64 = (bank[i] - '0') as i64
|
||||
if v > largest
|
||||
largest = v
|
||||
largest_idx = i
|
||||
|
||||
@@ -2,7 +2,7 @@ func main[] : i64
|
||||
let s: i64 = must(net.listen?(net.pack_addr(127, 0, 0, 1), 8000))
|
||||
|
||||
io.println("Listening on port 8000...")
|
||||
let resp: str = mem.alloc(60000)
|
||||
let resp: ptr = mem.alloc(60000)
|
||||
while true
|
||||
let conn: i64 = net.accept(s)
|
||||
if conn < 0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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...")
|
||||
while true
|
||||
|
||||
109
src/analyzer.rs
109
src/analyzer.rs
@@ -5,25 +5,57 @@ use crate::{
|
||||
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 functions: HashMap<String, i32>,
|
||||
pub functions: HashMap<String, FnType>,
|
||||
pub constants: HashMap<String, u64>,
|
||||
pub structs: HashMap<String, HashMap<String, usize>>,
|
||||
pub structs: HashMap<String, HashMap<String, StructField>>,
|
||||
}
|
||||
|
||||
impl Analyzer {
|
||||
pub fn new() -> Analyzer {
|
||||
Analyzer {
|
||||
functions: HashMap::from([
|
||||
("_builtin_heap_head".into(), 0),
|
||||
("_builtin_heap_tail".into(), 0),
|
||||
("_builtin_err_code".into(), 0),
|
||||
("_builtin_err_msg".into(), 0),
|
||||
("_builtin_read64".into(), 1),
|
||||
("_builtin_set64".into(), 2),
|
||||
("_builtin_syscall".into(), -1),
|
||||
("io.printf".into(), -1),
|
||||
("_builtin_environ".into(), 0),
|
||||
("_builtin_heap_head".into(), FnType::new("ptr", vec![])),
|
||||
("_builtin_heap_tail".into(), FnType::new("ptr", vec![])),
|
||||
("_builtin_err_code".into(), FnType::new("ptr", vec![])),
|
||||
("_builtin_err_msg".into(), FnType::new("ptr", vec![])),
|
||||
("_builtin_read64".into(), FnType::new("i64", vec!["ptr"])),
|
||||
(
|
||||
"_builtin_set64".into(),
|
||||
FnType::new("void", vec!["ptr", "i64"]),
|
||||
),
|
||||
("_builtin_syscall".into(), FnType::new_variadic("i64")),
|
||||
("io.printf".into(), FnType::new_variadic("void")),
|
||||
("_builtin_environ".into(), FnType::new("ptr", vec![])),
|
||||
]),
|
||||
constants: HashMap::new(),
|
||||
structs: HashMap::new(),
|
||||
@@ -34,7 +66,7 @@ impl Analyzer {
|
||||
if let Stmt::Function {
|
||||
name,
|
||||
params,
|
||||
return_type: _,
|
||||
return_type,
|
||||
body: _,
|
||||
exported: _,
|
||||
} = stmt
|
||||
@@ -42,8 +74,13 @@ impl Analyzer {
|
||||
if self.functions.contains_key(&name.lexeme) {
|
||||
return error!(name.loc, format!("tried to redefine '{}'", name.lexeme));
|
||||
}
|
||||
self.functions
|
||||
.insert(name.lexeme.clone(), params.len() as i32);
|
||||
self.functions.insert(
|
||||
name.lexeme.clone(),
|
||||
FnType {
|
||||
return_type: return_type.lexeme.clone(),
|
||||
params: Some(params.iter().map(|x| x.var_type.lexeme.clone()).collect()),
|
||||
},
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -83,6 +120,7 @@ impl Analyzer {
|
||||
}
|
||||
}
|
||||
Stmt::If {
|
||||
keyword: _,
|
||||
condition,
|
||||
then_branch,
|
||||
else_branch,
|
||||
@@ -91,7 +129,11 @@ impl Analyzer {
|
||||
self.analyze_stmt(then_branch)?;
|
||||
self.analyze_stmt(else_branch)?;
|
||||
}
|
||||
Stmt::While { condition, body } => {
|
||||
Stmt::While {
|
||||
keyword: _,
|
||||
condition,
|
||||
body,
|
||||
} => {
|
||||
self.analyze_expr(condition)?;
|
||||
self.analyze_stmt(body)?;
|
||||
}
|
||||
@@ -108,7 +150,7 @@ impl Analyzer {
|
||||
|
||||
self.analyze_stmt(body)?;
|
||||
}
|
||||
Stmt::Return(expr) => {
|
||||
Stmt::Return { expr, keyword: _ } => {
|
||||
self.analyze_expr(expr)?;
|
||||
}
|
||||
Stmt::For {
|
||||
@@ -127,14 +169,21 @@ impl Analyzer {
|
||||
if self.functions.contains_key(&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 } => {
|
||||
let mut fields_map: HashMap<String, usize> = HashMap::new();
|
||||
let mut fields_map: HashMap<String, StructField> = HashMap::new();
|
||||
|
||||
let mut offset: usize = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -172,11 +221,18 @@ impl Analyzer {
|
||||
if let Expr::Variable(callee_name) = *callee.clone() {
|
||||
if self.functions.contains_key(&callee_name.lexeme) {
|
||||
// its a function (defined/builtin/extern)
|
||||
if let Some(arity) = self.functions.get(&callee_name.lexeme) {
|
||||
if *arity >= 0 && *arity != args.len() as i32 {
|
||||
if let Some(fn_type) = self.functions.get(&callee_name.lexeme) {
|
||||
// if its None, its variadic
|
||||
if let Some(params) = &fn_type.params
|
||||
&& params.len() != args.len()
|
||||
{
|
||||
return error!(
|
||||
&paren.loc,
|
||||
format!("expected {} arguments, got {}", arity, args.len())
|
||||
format!(
|
||||
"expected {} arguments, got {}",
|
||||
params.len(),
|
||||
args.len()
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -203,7 +259,11 @@ impl Analyzer {
|
||||
self.analyze_expr(expr)?;
|
||||
}
|
||||
}
|
||||
Expr::Index { expr, index } => {
|
||||
Expr::Index {
|
||||
expr,
|
||||
bracket: _,
|
||||
index,
|
||||
} => {
|
||||
self.analyze_expr(expr)?;
|
||||
self.analyze_expr(index)?;
|
||||
}
|
||||
@@ -214,6 +274,9 @@ impl Analyzer {
|
||||
Expr::MemberAccess { left, field: _ } => {
|
||||
self.analyze_expr(left)?;
|
||||
}
|
||||
Expr::Cast { expr, type_name: _ } => {
|
||||
self.analyze_expr(expr)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -69,25 +69,22 @@ macro_rules! emit {
|
||||
|
||||
static REGISTERS: [&str; 6] = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"];
|
||||
|
||||
// TODO: currently they are all just 64 bit values
|
||||
static BUILTIN_TYPES: [&str; 7] = ["void", "u8", "i64", "str", "bool", "ptr", "any"];
|
||||
|
||||
pub struct CodegenX86_64 {
|
||||
pub struct CodegenX86_64<'a> {
|
||||
output: String,
|
||||
data_section: String,
|
||||
label_counter: usize,
|
||||
data_counter: usize,
|
||||
pub analyzer: Analyzer,
|
||||
pub analyzer: &'a Analyzer,
|
||||
}
|
||||
|
||||
impl CodegenX86_64 {
|
||||
pub fn new() -> CodegenX86_64 {
|
||||
impl<'a> CodegenX86_64<'a> {
|
||||
pub fn new(analyzer: &'a Analyzer) -> CodegenX86_64<'a> {
|
||||
CodegenX86_64 {
|
||||
output: String::new(),
|
||||
data_section: String::new(),
|
||||
label_counter: 0,
|
||||
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)?;
|
||||
let offset = env.define_var(name.lexeme.clone(), var_type);
|
||||
emit!(&mut self.output, " mov QWORD [rbp-{}], rax", offset);
|
||||
@@ -267,6 +260,7 @@ _builtin_environ:
|
||||
env.pop_scope();
|
||||
}
|
||||
Stmt::If {
|
||||
keyword: _,
|
||||
condition,
|
||||
then_branch,
|
||||
else_branch,
|
||||
@@ -283,7 +277,11 @@ _builtin_environ:
|
||||
self.compile_stmt(env, else_branch)?;
|
||||
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_end_label = env.loop_end_label.clone();
|
||||
let old_loop_continue_label = env.loop_continue_label.clone();
|
||||
@@ -306,7 +304,7 @@ _builtin_environ:
|
||||
Stmt::Function {
|
||||
name,
|
||||
params,
|
||||
return_type,
|
||||
return_type: _,
|
||||
body,
|
||||
exported,
|
||||
} => {
|
||||
@@ -319,21 +317,7 @@ _builtin_environ:
|
||||
emit!(&mut self.output, " mov rbp, rsp");
|
||||
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() {
|
||||
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
|
||||
.define_var(param.var_name.lexeme.clone(), param.var_type.lexeme.clone());
|
||||
if let Some(reg) = REGISTERS.get(i) {
|
||||
@@ -360,7 +344,7 @@ _builtin_environ:
|
||||
emit!(&mut self.output, " ret");
|
||||
}
|
||||
}
|
||||
Stmt::Return(expr) => {
|
||||
Stmt::Return { expr, keyword: _ } => {
|
||||
self.compile_expr(env, expr)?;
|
||||
emit!(&mut self.output, " mov rsp, rbp");
|
||||
emit!(&mut self.output, " pop rbp");
|
||||
@@ -578,15 +562,9 @@ _builtin_environ:
|
||||
self.analyzer.constants[&name.lexeme]
|
||||
);
|
||||
} else {
|
||||
// TODO: move to analyzer
|
||||
let var = match env.get_var(&name.lexeme) {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
return error!(
|
||||
name.loc,
|
||||
format!("undefined variable: {}", &name.lexeme)
|
||||
);
|
||||
}
|
||||
None => unreachable!("this should be caught in the typechecker"),
|
||||
};
|
||||
emit!(
|
||||
&mut self.output,
|
||||
@@ -600,15 +578,9 @@ _builtin_environ:
|
||||
|
||||
match left.as_ref() {
|
||||
Expr::Variable(name) => {
|
||||
// TODO: move to analyzer
|
||||
let var = match env.get_var(&name.lexeme) {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
return error!(
|
||||
name.loc,
|
||||
format!("undefined variable: {}", &name.lexeme)
|
||||
);
|
||||
}
|
||||
None => unreachable!(),
|
||||
};
|
||||
emit!(
|
||||
&mut self.output,
|
||||
@@ -616,7 +588,11 @@ _builtin_environ:
|
||||
var.stack_offset,
|
||||
);
|
||||
}
|
||||
Expr::Index { expr, index } => {
|
||||
Expr::Index {
|
||||
expr,
|
||||
bracket: _,
|
||||
index,
|
||||
} => {
|
||||
emit!(&mut self.output, " push rax");
|
||||
self.compile_expr(env, expr)?;
|
||||
emit!(&mut self.output, " push rax");
|
||||
@@ -710,7 +686,11 @@ _builtin_environ:
|
||||
}
|
||||
emit!(&mut self.output, " pop rax");
|
||||
}
|
||||
Expr::Index { expr, index } => {
|
||||
Expr::Index {
|
||||
expr,
|
||||
bracket: _,
|
||||
index,
|
||||
} => {
|
||||
self.compile_expr(env, expr)?;
|
||||
emit!(&mut self.output, " push rax");
|
||||
self.compile_expr(env, index)?;
|
||||
@@ -761,20 +741,13 @@ _builtin_environ:
|
||||
self.compile_expr(env, left)?;
|
||||
emit!(&mut self.output, " mov rax, QWORD [rax+{}]", offset);
|
||||
}
|
||||
Expr::Cast { expr, type_name: _ } => {
|
||||
self.compile_expr(env, expr)?;
|
||||
}
|
||||
}
|
||||
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(
|
||||
&self,
|
||||
env: &mut Env,
|
||||
@@ -803,11 +776,11 @@ _builtin_environ:
|
||||
}
|
||||
};
|
||||
|
||||
let offset = match fields.get(&field.lexeme) {
|
||||
Some(o) => *o,
|
||||
let field = match fields.get(&field.lexeme) {
|
||||
Some(o) => o,
|
||||
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 parser;
|
||||
mod tokenizer;
|
||||
mod typechecker;
|
||||
|
||||
use std::{
|
||||
fs,
|
||||
@@ -13,55 +14,15 @@ use tokenizer::ZernError;
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
macro_rules! compile_std {
|
||||
($codegen:expr, $( $name:literal ),* $(,)? ) => {
|
||||
$(
|
||||
compile_file_to(
|
||||
$codegen,
|
||||
$name,
|
||||
include_str!(concat!("std/", $name)).into()
|
||||
)?;
|
||||
)*
|
||||
macro_rules! parse_std_file {
|
||||
($statements:expr, $filename:expr) => {
|
||||
let source: String = include_str!($filename).into();
|
||||
let tokenizer = tokenizer::Tokenizer::new($filename.to_owned(), source);
|
||||
let parser = parser::Parser::new(tokenizer.tokenize()?);
|
||||
$statements.extend(parser.parse()?);
|
||||
};
|
||||
}
|
||||
|
||||
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> {
|
||||
let source = match fs::read_to_string(&args.path) {
|
||||
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 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)?;
|
||||
compile_std!(&mut codegen, "syscalls.zr", "std.zr", "net.zr");
|
||||
compile_file_to(&mut codegen, filename, source)?;
|
||||
for stmt in statements {
|
||||
codegen.compile_stmt(&mut codegen_x86_64::Env::new(), &stmt)?;
|
||||
}
|
||||
|
||||
if !args.output_asm {
|
||||
let out = args.out.unwrap_or_else(|| "out".into());
|
||||
@@ -116,6 +101,17 @@ fn compile_file(args: Args) -> Result<(), ZernError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_command(cmd: String) {
|
||||
if !Command::new("sh")
|
||||
.args(["-c", &cmd])
|
||||
.status()
|
||||
.unwrap()
|
||||
.success()
|
||||
{
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about = None)]
|
||||
struct Args {
|
||||
|
||||
@@ -20,11 +20,13 @@ pub enum Stmt {
|
||||
},
|
||||
Block(Vec<Stmt>),
|
||||
If {
|
||||
keyword: Token,
|
||||
condition: Expr,
|
||||
then_branch: Box<Stmt>,
|
||||
else_branch: Box<Stmt>,
|
||||
},
|
||||
While {
|
||||
keyword: Token,
|
||||
condition: Expr,
|
||||
body: Box<Stmt>,
|
||||
},
|
||||
@@ -41,7 +43,10 @@ pub enum Stmt {
|
||||
body: Box<Stmt>,
|
||||
exported: bool,
|
||||
},
|
||||
Return(Expr),
|
||||
Return {
|
||||
expr: Expr,
|
||||
keyword: Token,
|
||||
},
|
||||
Break,
|
||||
Continue,
|
||||
Extern(Token),
|
||||
@@ -83,6 +88,7 @@ pub enum Expr {
|
||||
ArrayLiteral(Vec<Expr>),
|
||||
Index {
|
||||
expr: Box<Expr>,
|
||||
bracket: Token,
|
||||
index: Box<Expr>,
|
||||
},
|
||||
AddrOf {
|
||||
@@ -94,6 +100,10 @@ pub enum Expr {
|
||||
left: Box<Expr>,
|
||||
field: Token,
|
||||
},
|
||||
Cast {
|
||||
expr: Box<Expr>,
|
||||
type_name: Token,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct Parser {
|
||||
@@ -257,7 +267,11 @@ impl Parser {
|
||||
} else if self.match_token(&[TokenType::KeywordFor]) {
|
||||
self.for_statement()
|
||||
} 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]) {
|
||||
Ok(Stmt::Break)
|
||||
} else if self.match_token(&[TokenType::KeywordContinue]) {
|
||||
@@ -268,6 +282,7 @@ impl Parser {
|
||||
}
|
||||
|
||||
fn if_statement(&mut self) -> Result<Stmt, ZernError> {
|
||||
let keyword = self.previous().clone();
|
||||
let condition = self.expression()?;
|
||||
let then_branch = self.block()?;
|
||||
let else_branch = if self.match_token(&[TokenType::KeywordElse]) {
|
||||
@@ -280,6 +295,7 @@ impl Parser {
|
||||
Box::new(Stmt::Block(vec![]))
|
||||
};
|
||||
Ok(Stmt::If {
|
||||
keyword,
|
||||
condition,
|
||||
then_branch: Box::new(then_branch),
|
||||
else_branch,
|
||||
@@ -287,9 +303,11 @@ impl Parser {
|
||||
}
|
||||
|
||||
fn while_statement(&mut self) -> Result<Stmt, ZernError> {
|
||||
let keyword = self.previous().clone();
|
||||
let condition = self.expression()?;
|
||||
let body = self.block()?;
|
||||
Ok(Stmt::While {
|
||||
keyword,
|
||||
condition,
|
||||
body: Box::new(body),
|
||||
})
|
||||
@@ -438,7 +456,7 @@ impl Parser {
|
||||
}
|
||||
|
||||
fn factor(&mut self) -> Result<Expr, ZernError> {
|
||||
let mut expr = self.unary()?;
|
||||
let mut expr = self.cast()?;
|
||||
|
||||
while self.match_token(&[
|
||||
TokenType::Star,
|
||||
@@ -459,6 +477,20 @@ impl Parser {
|
||||
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> {
|
||||
if self.match_token(&[TokenType::Xor]) {
|
||||
let op = self.previous().clone();
|
||||
@@ -504,9 +536,10 @@ impl Parser {
|
||||
};
|
||||
} else if self.match_token(&[TokenType::LeftBracket]) {
|
||||
let index = self.expression()?;
|
||||
self.consume(TokenType::RightBracket, "expected ']' after index")?;
|
||||
let bracket = self.consume(TokenType::RightBracket, "expected ']' after index")?;
|
||||
expr = Expr::Index {
|
||||
expr: Box::new(expr),
|
||||
bracket,
|
||||
index: Box::new(index),
|
||||
}
|
||||
} 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
|
||||
mem.free(s)
|
||||
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)
|
||||
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
|
||||
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()
|
||||
let s: net.UDPSocket = net.create_udp_client?(packed_host, port)
|
||||
if err.check()
|
||||
return 0
|
||||
return 0 as net.UDPSocket
|
||||
|
||||
if _builtin_syscall(SYS_bind, s->fd, s->addr, 16) < 0
|
||||
net.UDPSocket.close_and_free(s)
|
||||
err.set(ERR_SYSCALL_FAILED, "net.udp_listen?: failed to bind")
|
||||
return 0
|
||||
err.set(ERR_SYSCALL_FAILED, "net.create_udp_server?: failed to bind")
|
||||
return 0 as net.UDPSocket
|
||||
|
||||
return s
|
||||
|
||||
@@ -166,7 +166,7 @@ func net.encode_dns_name[domain: str] : io.Buffer
|
||||
let part_len: i64 = i - part_start
|
||||
buf->data[out_pos] = part_len & 0xff
|
||||
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
|
||||
part_start = 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)
|
||||
|
||||
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)
|
||||
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())
|
||||
|
||||
func err.msg[] : str
|
||||
return mem.read64(_builtin_err_msg())
|
||||
return mem.read64(_builtin_err_msg()) as str
|
||||
|
||||
func err.check[] : bool
|
||||
return err.code() != 0
|
||||
|
||||
func err.clear[] : void
|
||||
err.set(0, 0)
|
||||
err.set(0, 0 as str)
|
||||
|
||||
func err.set[code: i64, msg: str] : void
|
||||
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
|
||||
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->free = true
|
||||
new_blk->next = blk->next
|
||||
new_blk->prev = blk
|
||||
|
||||
if blk->next
|
||||
if blk->next as ptr
|
||||
let next: mem.Block = blk->next
|
||||
next->prev = new_blk
|
||||
else
|
||||
@@ -70,25 +70,27 @@ func mem._request_space?[size: i64] : mem.Block
|
||||
// PROT_READ | PROT_WRITE = 3
|
||||
// MAP_PRIVATE | MAP_ANONYMOUS = 34
|
||||
// fd = -1, offset = 0
|
||||
let blk: mem.Block = _builtin_syscall(SYS_mmap, 0, alloc_size, 3, 34, -1, 0)
|
||||
if blk == -1
|
||||
let blk_ptr: ptr = _builtin_syscall(SYS_mmap, 0, alloc_size, 3, 34, -1, 0) as ptr
|
||||
if blk_ptr == -1
|
||||
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->free = false
|
||||
blk->next = 0
|
||||
blk->prev = 0
|
||||
blk->next = 0 as mem.Block
|
||||
blk->prev = 0 as mem.Block
|
||||
|
||||
let tail: mem.Block = mem.read64(_builtin_heap_tail())
|
||||
if tail
|
||||
let tail: mem.Block = mem.read64(_builtin_heap_tail()) as mem.Block
|
||||
if tail as ptr
|
||||
tail->next = blk
|
||||
blk->prev = tail
|
||||
|
||||
mem.write64(_builtin_heap_tail(), blk)
|
||||
return blk
|
||||
|
||||
func mem.alloc?[size: i64] : ptr
|
||||
func mem.alloc?[size: i64] : any
|
||||
err.clear()
|
||||
if size <= 0
|
||||
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)
|
||||
|
||||
let cur: mem.Block = mem.read64(_builtin_heap_head())
|
||||
while cur
|
||||
let cur: mem.Block = mem.read64(_builtin_heap_head()) as mem.Block
|
||||
while cur as ptr
|
||||
if cur->free && cur->size >= size
|
||||
mem._split_block(cur, size)
|
||||
cur->free = false
|
||||
return cur + MEM_BLOCK_SIZE
|
||||
return cur as ptr + MEM_BLOCK_SIZE
|
||||
cur = cur->next
|
||||
|
||||
let blk: mem.Block = mem._request_space?(size)
|
||||
@@ -113,51 +115,51 @@ func mem.alloc?[size: i64] : ptr
|
||||
|
||||
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))
|
||||
|
||||
func mem.free[x: ptr] : void
|
||||
if !x
|
||||
func mem.free[x: any] : void
|
||||
if !(x as ptr)
|
||||
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
|
||||
|
||||
let next: mem.Block = blk->next
|
||||
if next && next->free
|
||||
let expected_next: mem.Block = blk + MEM_BLOCK_SIZE + blk->size
|
||||
if expected_next == next
|
||||
if next as ptr && next->free
|
||||
let expected_next: mem.Block = (blk as ptr + MEM_BLOCK_SIZE + blk->size) as mem.Block
|
||||
if expected_next as ptr == next as ptr
|
||||
blk->size = blk->size + MEM_BLOCK_SIZE + next->size
|
||||
blk->next = next->next
|
||||
if next->next
|
||||
if next->next as ptr
|
||||
let b: mem.Block = next->next
|
||||
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)
|
||||
|
||||
let prev: mem.Block = blk->prev
|
||||
if prev && prev->free
|
||||
let expected_blk: mem.Block = prev + MEM_BLOCK_SIZE + prev->size
|
||||
if expected_blk == blk
|
||||
if prev as ptr && prev->free
|
||||
let expected_blk: mem.Block = (prev as ptr + MEM_BLOCK_SIZE + prev->size) as mem.Block
|
||||
if expected_blk as ptr == blk as ptr
|
||||
prev->size = prev->size + MEM_BLOCK_SIZE + blk->size
|
||||
prev->next = blk->next
|
||||
if blk->next
|
||||
if blk->next as ptr
|
||||
let b: mem.Block = blk->next
|
||||
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)
|
||||
blk = prev
|
||||
|
||||
let block_total: i64 = blk->size + MEM_BLOCK_SIZE
|
||||
if (blk & 4095) == 0 && (block_total & 4095) == 0
|
||||
if blk->prev
|
||||
if (blk as i64 & 4095) == 0 && (block_total & 4095) == 0
|
||||
if blk->prev as ptr
|
||||
let b: mem.Block = blk->prev
|
||||
b->next = blk->next
|
||||
else
|
||||
mem.write64(_builtin_heap_head(), blk->next)
|
||||
if blk->next
|
||||
if blk->next as ptr
|
||||
let b: mem.Block = blk->next
|
||||
b->prev = blk->prev
|
||||
else
|
||||
@@ -171,38 +173,38 @@ func mem.realloc?[x: ptr, new_size: i64] : ptr
|
||||
|
||||
if new_size < 0
|
||||
err.set(ERR_ALLOC_FAILED, "mem.realloc? called with negative size")
|
||||
return 0
|
||||
return 0 as ptr
|
||||
|
||||
if new_size == 0
|
||||
mem.free(x)
|
||||
return 0
|
||||
return 0 as ptr
|
||||
|
||||
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
|
||||
mem._split_block(blk, new_size)
|
||||
return x
|
||||
|
||||
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
|
||||
if combined >= new_size
|
||||
blk->size = combined
|
||||
blk->next = next->next
|
||||
if next->next
|
||||
if next->next as ptr
|
||||
let b: mem.Block = next->next
|
||||
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._split_block(blk, new_size)
|
||||
return x
|
||||
|
||||
let new_ptr: ptr = mem.alloc?(new_size)
|
||||
if err.check()
|
||||
return 0
|
||||
return 0 as ptr
|
||||
|
||||
mem.copy(x, new_ptr, math.min(blk->size, new_size))
|
||||
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
|
||||
for i in 0..size
|
||||
x[i] = 0
|
||||
x[i] = 0 as u8
|
||||
|
||||
func mem.copy[src: ptr, dst: ptr, n: i64] : void
|
||||
if dst > src
|
||||
@@ -230,16 +232,16 @@ func mem.read8[x: ptr] : u8
|
||||
return x[0]
|
||||
|
||||
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
|
||||
return (x[0] << 8) | x[1]
|
||||
return (x[0] << 8) as i64 | x[1]
|
||||
|
||||
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
|
||||
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
|
||||
return _builtin_read64(x)
|
||||
@@ -258,11 +260,11 @@ func mem.write16be[x: ptr, d: i64] : void
|
||||
x[0] = (d >> 8) & 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)
|
||||
|
||||
// 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]
|
||||
if s[0] == '%'
|
||||
s = s + 1
|
||||
@@ -274,10 +276,10 @@ func io._printf_impl[s: str, args: ptr] : void
|
||||
io.print_i64_hex(mem.read64(args))
|
||||
args = args + 8
|
||||
else if s[0] == 's'
|
||||
io.print(mem.read64(args))
|
||||
io.print(mem.read64(args) as str)
|
||||
args = args + 8
|
||||
else if s[0] == 'c'
|
||||
io.print_char(mem.read64(args))
|
||||
io.print_char(mem.read64(args) as u8)
|
||||
args = args + 8
|
||||
else if s[0] == '%'
|
||||
io.print_char('%')
|
||||
@@ -287,11 +289,11 @@ func io._printf_impl[s: str, args: ptr] : void
|
||||
io.print_char(s[0])
|
||||
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)
|
||||
|
||||
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
|
||||
io.print(x)
|
||||
@@ -322,7 +324,7 @@ func io.println_i64[x: i64] : void
|
||||
mem.free(s)
|
||||
|
||||
func io.read_char[] : u8
|
||||
let c: u8 = 0
|
||||
let c: u8 = 0 as u8
|
||||
_builtin_syscall(SYS_read, 0, ^c, 1)
|
||||
return c
|
||||
|
||||
@@ -333,7 +335,7 @@ func io.read_line[]: str
|
||||
if n < 0
|
||||
n = 0
|
||||
buffer[n] = 0
|
||||
buffer = must(mem.realloc?(buffer, n + 1))
|
||||
buffer = must(mem.realloc?(buffer as ptr, n + 1))
|
||||
return buffer
|
||||
|
||||
struct io.Buffer
|
||||
@@ -347,7 +349,7 @@ func io.Buffer.alloc?[size: i64] : io.Buffer
|
||||
buffer->data = mem.alloc?(size)
|
||||
if err.check()
|
||||
mem.free(buffer)
|
||||
return 0
|
||||
return 0 as io.Buffer
|
||||
return buffer
|
||||
|
||||
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)
|
||||
if fd < 0
|
||||
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)
|
||||
_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()
|
||||
_builtin_syscall(SYS_close, fd)
|
||||
return 0
|
||||
return 0 as str
|
||||
|
||||
let n: i64 = _builtin_syscall(SYS_read, fd, buffer, size)
|
||||
_builtin_syscall(SYS_close, fd)
|
||||
@@ -378,7 +380,7 @@ func io.read_text_file?[path: str] : str
|
||||
if n != size
|
||||
mem.free(buffer)
|
||||
err.set(ERR_READ_FAILED, "io.read_text_file?: failed to read file")
|
||||
return 0
|
||||
return 0 as str
|
||||
|
||||
buffer[n] = 0
|
||||
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)
|
||||
if fd < 0
|
||||
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
|
||||
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()
|
||||
io.Buffer.free(buf)
|
||||
_builtin_syscall(SYS_close, fd)
|
||||
return 0
|
||||
return 0 as io.Buffer
|
||||
|
||||
let n: i64 = _builtin_syscall(SYS_read, fd, buf->data, buf->size)
|
||||
_builtin_syscall(SYS_close, fd)
|
||||
@@ -406,12 +408,12 @@ func io.read_binary_file?[path: str] : io.Buffer
|
||||
if n != buf->size
|
||||
io.Buffer.free(buf)
|
||||
err.set(ERR_READ_FAILED, "io.read_binary_file?: failed to read file")
|
||||
return 0
|
||||
return 0 as io.Buffer
|
||||
|
||||
return buf
|
||||
|
||||
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
|
||||
err.clear()
|
||||
@@ -435,7 +437,7 @@ func str.len[s: str] : i64
|
||||
func str.make_copy[s: str] : str
|
||||
let size: i64 = str.len(s) + 1
|
||||
let dup: str = mem.alloc(size)
|
||||
mem.copy(s, dup, size)
|
||||
mem.copy(s as ptr, dup as ptr, size)
|
||||
return dup
|
||||
|
||||
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 b_len: i64 = str.len(b)
|
||||
let out: str = mem.alloc(a_len + b_len + 1)
|
||||
mem.copy(a, out, a_len)
|
||||
mem.copy(b, out + a_len, b_len)
|
||||
mem.copy(a as ptr, out as ptr, a_len)
|
||||
mem.copy(b as ptr, out as ptr + a_len, b_len)
|
||||
out[a_len + b_len] = 0
|
||||
return out
|
||||
|
||||
@@ -504,7 +506,7 @@ func str.substr[s: str, start: i64, length: i64] : str
|
||||
panic("str.substr out of bounds")
|
||||
|
||||
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
|
||||
return out
|
||||
|
||||
@@ -591,7 +593,7 @@ func str.from_i64[n: i64] : str
|
||||
if neg
|
||||
buf[end] = '-'
|
||||
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)
|
||||
return s
|
||||
|
||||
@@ -662,7 +664,7 @@ func str.hex_encode[s: str, s_len: i64] : str
|
||||
out[j] = 0
|
||||
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'
|
||||
return d - 'a' + 10
|
||||
if d >= 'A' && d <= 'F'
|
||||
@@ -769,10 +771,10 @@ struct Array
|
||||
func array.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
|
||||
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)
|
||||
xs->size = 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
|
||||
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
|
||||
array.set(new_array, i, array.nth(xs, start + i))
|
||||
return new_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
|
||||
array.set(new_array, i, array.nth(a, i))
|
||||
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 i: i64 = low - 1
|
||||
for j in (low)..high
|
||||
if array.nth(arr, j) <= pivot
|
||||
if array.nth(arr, j) as i64 <= pivot
|
||||
i = i + 1
|
||||
let temp: i64 = array.nth(arr ,i)
|
||||
array.set(arr, i, array.nth(arr, j))
|
||||
@@ -858,20 +860,20 @@ func alg.count[arr: Array, item: any] : i64
|
||||
count = count + 1
|
||||
return count
|
||||
|
||||
func alg.map[arr: Array, fn: ptr] : Array
|
||||
let out: Array = array.new_with_size_zeroed(arr->size)
|
||||
func alg.map[arr: Array, fn: fnptr] : Array
|
||||
let out: Array = array.new_preallocated_and_zeroed(arr->size)
|
||||
for i in 0..arr->size
|
||||
array.set(out, i, fn(array.nth(arr, i)))
|
||||
return out
|
||||
|
||||
func alg.filter[arr: Array, fn: ptr] : Array
|
||||
func alg.filter[arr: Array, fn: fnptr] : Array
|
||||
let out: Array = []
|
||||
for i in 0..arr->size
|
||||
if fn(array.nth(arr, i))
|
||||
array.push(out, array.nth(arr, i))
|
||||
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
|
||||
acc = fn(acc, array.nth(arr, i))
|
||||
return acc
|
||||
@@ -900,13 +902,13 @@ func os.urandom?[n: i64]: ptr
|
||||
err.clear()
|
||||
let buffer: ptr = mem.alloc(n)
|
||||
if err.check()
|
||||
return 0
|
||||
return 0 as ptr
|
||||
|
||||
let fd: i64 = _builtin_syscall(SYS_openat, -100, "/dev/urandom", 0, 0)
|
||||
if fd < 0
|
||||
mem.free(buffer)
|
||||
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)
|
||||
_builtin_syscall(SYS_close, fd)
|
||||
@@ -914,7 +916,7 @@ func os.urandom?[n: i64]: ptr
|
||||
if n != bytes_read
|
||||
mem.free(buffer)
|
||||
err.set(ERR_READ_FAILED, "os.urandom?: failed to read /dev/urandom")
|
||||
return 0
|
||||
return 0 as ptr
|
||||
|
||||
return buffer
|
||||
|
||||
@@ -966,7 +968,7 @@ func os.list_directory?[path: str] : Array
|
||||
let fd: i64 = _builtin_syscall(SYS_openat, -100, path, 0, 0)
|
||||
if fd < 0
|
||||
err.set(ERR_OPEN_FAILED, "os.list_directory?: failed to open dir")
|
||||
return 0
|
||||
return 0 as Array
|
||||
|
||||
let files: Array = []
|
||||
let buf: ptr = mem.alloc(1024)
|
||||
@@ -981,14 +983,14 @@ func os.list_directory?[path: str] : Array
|
||||
|
||||
_builtin_syscall(SYS_close, fd)
|
||||
err.set(ERR_SYSCALL_FAILED, "os.list_directory?: getdents64() failed")
|
||||
return 0
|
||||
return 0 as Array
|
||||
else if n == 0
|
||||
break
|
||||
|
||||
let pos = 0
|
||||
while pos < n
|
||||
let len: i64 = mem.read16(buf + pos + 16)
|
||||
let name: str = buf + pos + 19
|
||||
let name: ptr = buf + pos + 19
|
||||
if name[0]
|
||||
let skip: bool = false
|
||||
// skip if name is exactly '.' or '..'
|
||||
@@ -999,7 +1001,7 @@ func os.list_directory?[path: str] : Array
|
||||
if name[2] == 0
|
||||
skip = true
|
||||
if !skip
|
||||
array.push(files, str.make_copy(name))
|
||||
array.push(files, str.make_copy(name as str))
|
||||
pos = pos + len
|
||||
|
||||
mem.free(buf)
|
||||
|
||||
@@ -55,6 +55,7 @@ pub enum TokenType {
|
||||
KeywordExport,
|
||||
KeywordStruct,
|
||||
KeywordNew,
|
||||
KeywordAs,
|
||||
|
||||
Indent,
|
||||
Dedent,
|
||||
@@ -371,6 +372,7 @@ impl Tokenizer {
|
||||
"export" => TokenType::KeywordExport,
|
||||
"struct" => TokenType::KeywordStruct,
|
||||
"new" => TokenType::KeywordNew,
|
||||
"as" => TokenType::KeywordAs,
|
||||
"true" => TokenType::True,
|
||||
"false" => TokenType::False,
|
||||
_ => 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