Compare commits
3 Commits
3efbb3ecd1
...
a17ffa184a
| Author | SHA1 | Date | |
|---|---|---|---|
| a17ffa184a | |||
| bb42e1fcf7 | |||
| 86de997be3 |
@@ -8,7 +8,7 @@ A very cool language
|
|||||||
* Growing [standard library](https://github.com/antpiasecki/zern/tree/main/src/std)
|
* Growing [standard library](https://github.com/antpiasecki/zern/tree/main/src/std)
|
||||||
* Produces tiny static executables (11KB for `hello.zr`)
|
* Produces tiny static executables (11KB for `hello.zr`)
|
||||||
* No libc required!
|
* No libc required!
|
||||||
* Has the pipe operator
|
* Has the pipe operator, variadics, dynamic arrays, hashmaps, etc.
|
||||||
|
|
||||||
## Syntax
|
## Syntax
|
||||||
```rust
|
```rust
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::{collections::HashMap, fmt::Write};
|
use std::{collections::HashMap, fmt::Write};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
parser::{Expr, Stmt},
|
parser::{Expr, Params, Stmt},
|
||||||
symbol_table::SymbolTable,
|
symbol_table::SymbolTable,
|
||||||
tokenizer::{Token, TokenType, ZernError, error},
|
tokenizer::{Token, TokenType, ZernError, error},
|
||||||
};
|
};
|
||||||
@@ -151,21 +151,6 @@ _builtin_syscall:
|
|||||||
mov r9, [rsp+8]
|
mov r9, [rsp+8]
|
||||||
syscall
|
syscall
|
||||||
ret
|
ret
|
||||||
|
|
||||||
section .text.io.printf
|
|
||||||
io.printf:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
sub rsp, 48
|
|
||||||
mov [rsp], rsi
|
|
||||||
mov [rsp + 8], rdx
|
|
||||||
mov [rsp + 16], rcx
|
|
||||||
mov [rsp + 24], r8
|
|
||||||
mov [rsp + 32], r9
|
|
||||||
lea rsi, [rsp]
|
|
||||||
call io._printf_impl
|
|
||||||
leave
|
|
||||||
ret
|
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -317,19 +302,35 @@ _builtin_environ:
|
|||||||
emit!(&mut self.output, " mov rbp, rsp");
|
emit!(&mut self.output, " mov rbp, rsp");
|
||||||
emit!(&mut self.output, " sub rsp, 256"); // TODO: eww
|
emit!(&mut self.output, " sub rsp, 256"); // TODO: eww
|
||||||
|
|
||||||
for (i, param) in params.iter().enumerate() {
|
match params {
|
||||||
let offset = env
|
Params::Normal(params) => {
|
||||||
.define_var(param.var_name.lexeme.clone(), param.var_type.lexeme.clone());
|
for (i, param) in params.iter().enumerate() {
|
||||||
if let Some(reg) = REGISTERS.get(i) {
|
let offset = env.define_var(
|
||||||
emit!(&mut self.output, " mov QWORD [rbp-{}], {}", offset, reg);
|
param.var_name.lexeme.clone(),
|
||||||
} else {
|
param.var_type.lexeme.clone(),
|
||||||
let stack_offset = 16 + 8 * (i - REGISTERS.len());
|
);
|
||||||
emit!(
|
if let Some(reg) = REGISTERS.get(i) {
|
||||||
&mut self.output,
|
emit!(&mut self.output, " mov QWORD [rbp-{}], {}", offset, reg);
|
||||||
" mov rax, QWORD [rbp+{}]",
|
} else {
|
||||||
stack_offset
|
let stack_offset = 16 + 8 * (i - REGISTERS.len());
|
||||||
);
|
emit!(
|
||||||
emit!(&mut self.output, " mov QWORD [rbp-{}], rax", offset);
|
&mut self.output,
|
||||||
|
" mov rax, QWORD [rbp+{}]",
|
||||||
|
stack_offset
|
||||||
|
);
|
||||||
|
emit!(&mut self.output, " mov QWORD [rbp-{}], rax", offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Params::Variadic => {
|
||||||
|
emit!(&mut self.output, " sub rsp, 48");
|
||||||
|
emit!(&mut self.output, " mov [rbp - 8], rdi");
|
||||||
|
emit!(&mut self.output, " mov [rbp - 16], rsi");
|
||||||
|
emit!(&mut self.output, " mov [rbp - 24], rdx");
|
||||||
|
emit!(&mut self.output, " mov [rbp - 32], rcx");
|
||||||
|
emit!(&mut self.output, " mov [rbp - 40], r8");
|
||||||
|
emit!(&mut self.output, " mov [rbp - 48], r9");
|
||||||
|
env.next_offset += 48;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -619,6 +620,12 @@ _builtin_environ:
|
|||||||
paren: _,
|
paren: _,
|
||||||
args,
|
args,
|
||||||
} => {
|
} => {
|
||||||
|
if let Expr::Variable(callee_name) = &**callee
|
||||||
|
&& callee_name.lexeme == "_var_arg"
|
||||||
|
{
|
||||||
|
return self.emit_var_arg(env, &args[0]);
|
||||||
|
}
|
||||||
|
|
||||||
for arg in args {
|
for arg in args {
|
||||||
self.compile_expr(env, arg)?;
|
self.compile_expr(env, arg)?;
|
||||||
emit!(&mut self.output, " push rax");
|
emit!(&mut self.output, " push rax");
|
||||||
@@ -732,7 +739,6 @@ _builtin_environ:
|
|||||||
Expr::New(struct_name) => {
|
Expr::New(struct_name) => {
|
||||||
let struct_fields = &self.symbol_table.structs[&struct_name.lexeme];
|
let struct_fields = &self.symbol_table.structs[&struct_name.lexeme];
|
||||||
|
|
||||||
// TODO: panic on mem.alloc error
|
|
||||||
let memory_size = struct_fields.len() * 8;
|
let memory_size = struct_fields.len() * 8;
|
||||||
emit!(&mut self.output, " mov rdi, {}", memory_size);
|
emit!(&mut self.output, " mov rdi, {}", memory_size);
|
||||||
emit!(&mut self.output, " call mem.alloc");
|
emit!(&mut self.output, " call mem.alloc");
|
||||||
@@ -789,4 +795,33 @@ _builtin_environ:
|
|||||||
|
|
||||||
Ok(field.offset)
|
Ok(field.offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn emit_var_arg(&mut self, env: &mut Env, index_expr: &Expr) -> Result<(), ZernError> {
|
||||||
|
self.compile_expr(env, index_expr)?;
|
||||||
|
emit!(&mut self.output, " mov r10, rax");
|
||||||
|
|
||||||
|
let stack_label = self.label();
|
||||||
|
let done_label = self.label();
|
||||||
|
|
||||||
|
emit!(&mut self.output, " cmp r10, 6");
|
||||||
|
emit!(&mut self.output, " jge {}", stack_label);
|
||||||
|
|
||||||
|
// < 6
|
||||||
|
emit!(&mut self.output, " mov rax, r10");
|
||||||
|
emit!(&mut self.output, " inc rax");
|
||||||
|
emit!(&mut self.output, " shl rax, 3");
|
||||||
|
emit!(&mut self.output, " neg rax");
|
||||||
|
emit!(&mut self.output, " mov rax, [rbp + rax]");
|
||||||
|
emit!(&mut self.output, " jmp {}", done_label);
|
||||||
|
|
||||||
|
// >= 6
|
||||||
|
emit!(&mut self.output, "{}:", stack_label);
|
||||||
|
emit!(&mut self.output, " mov rax, r10");
|
||||||
|
emit!(&mut self.output, " sub rax, 6");
|
||||||
|
emit!(&mut self.output, " shl rax, 3");
|
||||||
|
emit!(&mut self.output, " mov rax, [rbp + 16 + rax]");
|
||||||
|
|
||||||
|
emit!(&mut self.output, "{}:", done_label);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,12 @@ pub struct Param {
|
|||||||
pub var_name: Token,
|
pub var_name: Token,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Params {
|
||||||
|
Normal(Vec<Param>),
|
||||||
|
Variadic,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Stmt {
|
pub enum Stmt {
|
||||||
Expression(Expr),
|
Expression(Expr),
|
||||||
@@ -38,7 +44,7 @@ pub enum Stmt {
|
|||||||
},
|
},
|
||||||
Function {
|
Function {
|
||||||
name: Token,
|
name: Token,
|
||||||
params: Vec<Param>,
|
params: Params,
|
||||||
return_type: Token,
|
return_type: Token,
|
||||||
body: Box<Stmt>,
|
body: Box<Stmt>,
|
||||||
exported: bool,
|
exported: bool,
|
||||||
@@ -164,17 +170,24 @@ impl Parser {
|
|||||||
let name = self.consume(TokenType::Identifier, "expected function name")?;
|
let name = self.consume(TokenType::Identifier, "expected function name")?;
|
||||||
self.consume(TokenType::LeftBracket, "expected '[' after function name")?;
|
self.consume(TokenType::LeftBracket, "expected '[' after function name")?;
|
||||||
|
|
||||||
|
let mut is_variadic = false;
|
||||||
let mut params = vec![];
|
let mut params = vec![];
|
||||||
if !self.check(&TokenType::RightBracket) {
|
if !self.check(&TokenType::RightBracket) {
|
||||||
loop {
|
if self.match_token(&[TokenType::DoubleDot]) {
|
||||||
let var_name = self.consume(TokenType::Identifier, "expected parameter name")?;
|
is_variadic = true;
|
||||||
self.consume(TokenType::Colon, "expected ':' after parameter name")?;
|
} else {
|
||||||
|
loop {
|
||||||
|
let var_name =
|
||||||
|
self.consume(TokenType::Identifier, "expected parameter name")?;
|
||||||
|
self.consume(TokenType::Colon, "expected ':' after parameter name")?;
|
||||||
|
|
||||||
let var_type = self.consume(TokenType::Identifier, "expected parameter type")?;
|
let var_type =
|
||||||
|
self.consume(TokenType::Identifier, "expected parameter type")?;
|
||||||
|
|
||||||
params.push(Param { var_type, var_name });
|
params.push(Param { var_type, var_name });
|
||||||
if !self.match_token(&[TokenType::Comma]) {
|
if !self.match_token(&[TokenType::Comma]) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,7 +202,11 @@ impl Parser {
|
|||||||
|
|
||||||
Ok(Stmt::Function {
|
Ok(Stmt::Function {
|
||||||
name,
|
name,
|
||||||
params,
|
params: if is_variadic {
|
||||||
|
Params::Variadic
|
||||||
|
} else {
|
||||||
|
Params::Normal(params)
|
||||||
|
},
|
||||||
return_type,
|
return_type,
|
||||||
body,
|
body,
|
||||||
exported,
|
exported,
|
||||||
|
|||||||
108
src/std/std.zr
108
src/std/std.zr
@@ -5,6 +5,7 @@ const ERR_READ_FAILED = 3
|
|||||||
const ERR_WRITE_FAILED = 4
|
const ERR_WRITE_FAILED = 4
|
||||||
const ERR_ALLOC_FAILED = 5
|
const ERR_ALLOC_FAILED = 5
|
||||||
const ERR_CONNECTION_FAILED = 6
|
const ERR_CONNECTION_FAILED = 6
|
||||||
|
const ERR_KEY_ERROR = 7
|
||||||
|
|
||||||
func err.code[] : i64
|
func err.code[] : i64
|
||||||
return mem.read64(_builtin_err_code())
|
return mem.read64(_builtin_err_code())
|
||||||
@@ -263,24 +264,26 @@ func mem.write16be[x: ptr, d: i64] : void
|
|||||||
func mem.write64[x: ptr, d: any] : 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
|
func io.printf[..] : void
|
||||||
func io._printf_impl[s: ptr, args: ptr] : void
|
let s: ptr = _var_arg(0) as ptr
|
||||||
|
let i: i64 = 1
|
||||||
|
|
||||||
while s[0]
|
while s[0]
|
||||||
if s[0] == '%'
|
if s[0] == '%'
|
||||||
s = s + 1
|
s = s + 1
|
||||||
|
|
||||||
if s[0] == 'd'
|
if s[0] == 'd'
|
||||||
io.print_i64(mem.read64(args))
|
io.print_i64(_var_arg(i))
|
||||||
args = args + 8
|
i = i + 1
|
||||||
else if s[0] == 'x'
|
else if s[0] == 'x'
|
||||||
io.print_i64_hex(mem.read64(args))
|
io.print_i64_hex(_var_arg(i))
|
||||||
args = args + 8
|
i = i + 1
|
||||||
else if s[0] == 's'
|
else if s[0] == 's'
|
||||||
io.print(mem.read64(args) as str)
|
io.print(_var_arg(i) as str)
|
||||||
args = args + 8
|
i = i + 1
|
||||||
else if s[0] == 'c'
|
else if s[0] == 'c'
|
||||||
io.print_char(mem.read64(args) as u8)
|
io.print_char(_var_arg(i) as u8)
|
||||||
args = args + 8
|
i = i + 1
|
||||||
else if s[0] == '%'
|
else if s[0] == '%'
|
||||||
io.print_char('%')
|
io.print_char('%')
|
||||||
else if s[0] == 0
|
else if s[0] == 0
|
||||||
@@ -846,6 +849,89 @@ func array.concat[a: Array, b: Array] : Array
|
|||||||
array.set(new_array, a->size + i, array.nth(b, i))
|
array.set(new_array, a->size + i, array.nth(b, i))
|
||||||
return new_array
|
return new_array
|
||||||
|
|
||||||
|
const HashMap._TABLE_SIZE = 100
|
||||||
|
|
||||||
|
struct HashMap
|
||||||
|
table: Array
|
||||||
|
|
||||||
|
struct HashMap._Node
|
||||||
|
key: str
|
||||||
|
value: any
|
||||||
|
next: HashMap._Node
|
||||||
|
|
||||||
|
func HashMap.new[] : HashMap
|
||||||
|
let map: HashMap = new HashMap
|
||||||
|
map->table = array.new_preallocated_and_zeroed(HashMap._TABLE_SIZE)
|
||||||
|
return map
|
||||||
|
|
||||||
|
func HashMap.insert[map: HashMap, key: str, value: any] : void
|
||||||
|
let index: i64 = HashMap._djb2(key)
|
||||||
|
let current: HashMap._Node = array.nth(map->table, index)
|
||||||
|
|
||||||
|
while current as ptr
|
||||||
|
if str.equal(current->key, key)
|
||||||
|
current->value = value
|
||||||
|
return 0
|
||||||
|
current = current->next
|
||||||
|
|
||||||
|
let new_node: HashMap._Node = new HashMap._Node
|
||||||
|
new_node->key = str.make_copy(key)
|
||||||
|
new_node->value = value
|
||||||
|
new_node->next = array.nth(map->table, index)
|
||||||
|
array.set(map->table, index, new_node)
|
||||||
|
|
||||||
|
func HashMap.get?[map: HashMap, key: str] : any
|
||||||
|
err.clear()
|
||||||
|
let index: i64 = HashMap._djb2(key)
|
||||||
|
let current: HashMap._Node = array.nth(map->table, index)
|
||||||
|
|
||||||
|
while current as ptr
|
||||||
|
if str.equal(current->key, key)
|
||||||
|
return current->value
|
||||||
|
current = current->next
|
||||||
|
|
||||||
|
err.set(ERR_KEY_ERROR, "key not found")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
func HashMap.delete[map: HashMap, key: str] : void
|
||||||
|
let index: i64 = HashMap._djb2(key)
|
||||||
|
let current: HashMap._Node = array.nth(map->table, index)
|
||||||
|
let prev: HashMap._Node = 0 as HashMap._Node
|
||||||
|
|
||||||
|
while current as ptr
|
||||||
|
if str.equal(current->key, key)
|
||||||
|
if prev as ptr
|
||||||
|
prev->next = current->next
|
||||||
|
else
|
||||||
|
array.set(map->table, index, current->next)
|
||||||
|
|
||||||
|
mem.free(current->key)
|
||||||
|
mem.free(current)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
prev = current
|
||||||
|
current = current->next
|
||||||
|
|
||||||
|
func HashMap._djb2[key: str] : i64
|
||||||
|
let hash: i64 = 5381
|
||||||
|
let key_len: i64 = str.len(key)
|
||||||
|
for i in 0..key_len
|
||||||
|
hash = ((hash << 5) + hash) + key[i]
|
||||||
|
hash = (hash & 0x7fffffffffffffff) // prevent negative
|
||||||
|
return hash % HashMap._TABLE_SIZE
|
||||||
|
|
||||||
|
func HashMap.free[map: HashMap] : void
|
||||||
|
for i in 0..HashMap._TABLE_SIZE
|
||||||
|
let current: HashMap._Node = array.nth(map->table, i)
|
||||||
|
while current as ptr
|
||||||
|
let tmp: HashMap._Node = current
|
||||||
|
current = current->next
|
||||||
|
mem.free(tmp->key)
|
||||||
|
mem.free(tmp)
|
||||||
|
|
||||||
|
array.free(map->table)
|
||||||
|
mem.free(map)
|
||||||
|
|
||||||
func alg.quicksort[arr: Array] : void
|
func alg.quicksort[arr: Array] : void
|
||||||
alg._do_quicksort(arr, 0, arr->size - 1)
|
alg._do_quicksort(arr, 0, arr->size - 1)
|
||||||
|
|
||||||
@@ -923,7 +1009,7 @@ func os.urandom?[n: i64]: 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_OPEN_FAILED, "os.urandom?: failed to open /dev/urandom")
|
||||||
return 0 as ptr
|
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)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
parser::Stmt,
|
parser::{Params, Stmt},
|
||||||
tokenizer::{ZernError, error},
|
tokenizer::{ZernError, error},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -54,8 +54,8 @@ impl SymbolTable {
|
|||||||
FnType::new("void", vec!["ptr", "i64"]),
|
FnType::new("void", vec!["ptr", "i64"]),
|
||||||
),
|
),
|
||||||
("_builtin_syscall".into(), FnType::new_variadic("i64")),
|
("_builtin_syscall".into(), FnType::new_variadic("i64")),
|
||||||
("io.printf".into(), FnType::new_variadic("void")),
|
|
||||||
("_builtin_environ".into(), FnType::new("ptr", vec![])),
|
("_builtin_environ".into(), FnType::new("ptr", vec![])),
|
||||||
|
("_var_arg".into(), FnType::new("any", vec!["i64"])),
|
||||||
]),
|
]),
|
||||||
constants: HashMap::new(),
|
constants: HashMap::new(),
|
||||||
structs: HashMap::new(),
|
structs: HashMap::new(),
|
||||||
@@ -66,10 +66,7 @@ impl SymbolTable {
|
|||||||
match stmt {
|
match stmt {
|
||||||
Stmt::Const { name, value } => {
|
Stmt::Const { name, value } => {
|
||||||
if self.is_name_defined(&name.lexeme) {
|
if self.is_name_defined(&name.lexeme) {
|
||||||
return error!(
|
return error!(name.loc, format!("tried to redefine '{}'", name.lexeme));
|
||||||
name.loc,
|
|
||||||
format!("tried to redefine constant '{}'", name.lexeme)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if value.lexeme.starts_with("0x") {
|
if value.lexeme.starts_with("0x") {
|
||||||
self.constants.insert(
|
self.constants.insert(
|
||||||
@@ -103,13 +100,24 @@ impl SymbolTable {
|
|||||||
if self.is_name_defined(&name.lexeme) {
|
if self.is_name_defined(&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(
|
match params {
|
||||||
name.lexeme.clone(),
|
Params::Normal(params) => self.functions.insert(
|
||||||
FnType {
|
name.lexeme.clone(),
|
||||||
return_type: return_type.lexeme.clone(),
|
FnType {
|
||||||
params: Some(params.iter().map(|x| x.var_type.lexeme.clone()).collect()),
|
return_type: return_type.lexeme.clone(),
|
||||||
},
|
params: Some(
|
||||||
);
|
params.iter().map(|x| x.var_type.lexeme.clone()).collect(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Params::Variadic => self.functions.insert(
|
||||||
|
name.lexeme.clone(),
|
||||||
|
FnType {
|
||||||
|
return_type: return_type.lexeme.clone(),
|
||||||
|
params: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
Stmt::Struct { name, fields } => {
|
Stmt::Struct { name, fields } => {
|
||||||
if self.is_name_defined(&name.lexeme) {
|
if self.is_name_defined(&name.lexeme) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
parser::{Expr, Stmt},
|
parser::{Expr, Params, Stmt},
|
||||||
symbol_table::{SymbolTable, Type},
|
symbol_table::{SymbolTable, Type},
|
||||||
tokenizer::{TokenType, ZernError, error},
|
tokenizer::{TokenType, ZernError, error},
|
||||||
};
|
};
|
||||||
@@ -166,8 +166,39 @@ impl<'a> TypeChecker<'a> {
|
|||||||
body,
|
body,
|
||||||
exported: _,
|
exported: _,
|
||||||
} => {
|
} => {
|
||||||
if name.lexeme == "main" && return_type.lexeme != "i64" {
|
if name.lexeme == "main" {
|
||||||
return error!(&name.loc, "main must return i64");
|
if return_type.lexeme != "i64" {
|
||||||
|
return error!(&name.loc, "main function must return i64");
|
||||||
|
}
|
||||||
|
|
||||||
|
match params {
|
||||||
|
Params::Normal(params) => {
|
||||||
|
if !params.is_empty() && params.len() != 2 {
|
||||||
|
return error!(
|
||||||
|
&name.loc,
|
||||||
|
"main function must accept 0 or 2 parameters"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.len() == 2 {
|
||||||
|
if params[0].var_type.lexeme != "i64" {
|
||||||
|
return error!(
|
||||||
|
&name.loc,
|
||||||
|
"first parameter of the main function must be a i64"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if params[1].var_type.lexeme != "ptr" {
|
||||||
|
return error!(
|
||||||
|
&name.loc,
|
||||||
|
"second parameter of the main function must be a ptr"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Params::Variadic => {
|
||||||
|
return error!(&name.loc, "main function cannot be variadic");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.is_valid_type_name(&return_type.lexeme) {
|
if !self.is_valid_type_name(&return_type.lexeme) {
|
||||||
@@ -181,15 +212,23 @@ impl<'a> TypeChecker<'a> {
|
|||||||
|
|
||||||
env.push_scope();
|
env.push_scope();
|
||||||
|
|
||||||
for param in params {
|
match params {
|
||||||
if !self.is_valid_type_name(¶m.var_type.lexeme) {
|
Params::Normal(params) => {
|
||||||
return error!(
|
for param in params {
|
||||||
¶m.var_name.loc,
|
if !self.is_valid_type_name(¶m.var_type.lexeme) {
|
||||||
"unrecognized type: ".to_owned() + ¶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());
|
env.define_var(
|
||||||
|
param.var_name.lexeme.clone(),
|
||||||
|
param.var_type.lexeme.clone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Params::Variadic => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.typecheck_stmt(env, body)?;
|
self.typecheck_stmt(env, body)?;
|
||||||
|
|||||||
21
test.zr
21
test.zr
@@ -4,22 +4,17 @@ func run_test[x: str] : void
|
|||||||
|
|
||||||
for i in 0..build_blacklist->size
|
for i in 0..build_blacklist->size
|
||||||
if str.equal(x, array.nth(build_blacklist, i))
|
if str.equal(x, array.nth(build_blacklist, i))
|
||||||
io.print("Skipping ")
|
io.printf("Skipping %s...\n", x)
|
||||||
io.print(x)
|
|
||||||
io.println("...")
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
io.print("Building ")
|
io.printf("Building %s...\n", x)
|
||||||
io.print(x)
|
|
||||||
io.print("... ")
|
|
||||||
|
|
||||||
let build_start_time: i64 = os.time()
|
let build_start_time: i64 = os.time()
|
||||||
if must(os.run_shell_command?(str.concat("./target/release/zern ", x))) != 0
|
if must(os.run_shell_command?(str.concat("./target/release/zern ", x))) != 0
|
||||||
os.exit(1)
|
os.exit(1)
|
||||||
let build_end_time: i64 = os.time()
|
let build_end_time: i64 = os.time()
|
||||||
|
|
||||||
io.print_i64(build_end_time - build_start_time)
|
io.printf("%dms\n", build_end_time - build_start_time)
|
||||||
io.println("ms")
|
|
||||||
|
|
||||||
for i in 0..run_blacklist->size
|
for i in 0..run_blacklist->size
|
||||||
if str.find(x, array.nth(run_blacklist, i)) != -1
|
if str.find(x, array.nth(run_blacklist, i)) != -1
|
||||||
@@ -28,9 +23,7 @@ func run_test[x: str] : void
|
|||||||
io.println("...")
|
io.println("...")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
io.print("Running ")
|
io.printf("Running %s...\n", x)
|
||||||
io.print(x)
|
|
||||||
io.println("...")
|
|
||||||
|
|
||||||
let run_cmd: str = "./out"
|
let run_cmd: str = "./out"
|
||||||
if str.equal(x, "examples/curl.zr")
|
if str.equal(x, "examples/curl.zr")
|
||||||
@@ -43,11 +36,7 @@ func run_test[x: str] : void
|
|||||||
os.exit(1)
|
os.exit(1)
|
||||||
let run_end_time: i64 = os.time()
|
let run_end_time: i64 = os.time()
|
||||||
|
|
||||||
io.print("Running ")
|
io.printf("Running %s took %dms\n", x, run_end_time - run_start_time)
|
||||||
io.print(x)
|
|
||||||
io.print(" took ")
|
|
||||||
io.print_i64(run_end_time - run_start_time)
|
|
||||||
io.println("ms")
|
|
||||||
|
|
||||||
func run_directory[dir: str] : void
|
func run_directory[dir: str] : void
|
||||||
let files: Array = must(os.list_directory?(dir))
|
let files: Array = must(os.list_directory?(dir))
|
||||||
|
|||||||
Reference in New Issue
Block a user