fix bugs found by the fuzzer
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -2,3 +2,5 @@
|
||||
/out*
|
||||
/TODO
|
||||
/vscode
|
||||
/fuzz
|
||||
/perf.data*
|
||||
|
||||
@@ -15,8 +15,8 @@ use tokenizer::ZernError;
|
||||
fn compile_file(args: Args) -> Result<(), ZernError> {
|
||||
let source = match fs::read_to_string(&args.path) {
|
||||
Ok(x) => x,
|
||||
Err(_) => {
|
||||
eprintln!("\x1b[91mERROR\x1b[0m: failed to open {}", args.path);
|
||||
Err(e) => {
|
||||
eprintln!("\x1b[91mERROR\x1b[0m: failed to open {}: {e}", args.path);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -154,6 +154,7 @@ pub struct Parser {
|
||||
tokens: Vec<Token>,
|
||||
current: usize,
|
||||
is_inside_function: bool,
|
||||
depth: usize,
|
||||
}
|
||||
|
||||
impl Parser {
|
||||
@@ -162,6 +163,7 @@ impl Parser {
|
||||
tokens,
|
||||
current: 0,
|
||||
is_inside_function: false,
|
||||
depth: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -446,7 +448,14 @@ impl Parser {
|
||||
}
|
||||
|
||||
fn expression(&mut self) -> Result<Expr, ZernError> {
|
||||
self.or_and()
|
||||
self.depth += 1;
|
||||
if self.depth > 200 {
|
||||
return error!(self.previous().loc, "maximum expression depth reached");
|
||||
}
|
||||
|
||||
let out = self.or_and();
|
||||
self.depth -= 1;
|
||||
out
|
||||
}
|
||||
|
||||
fn or_and(&mut self) -> Result<Expr, ZernError> {
|
||||
|
||||
@@ -5,31 +5,35 @@ use crate::{
|
||||
tokenizer::{ZernError, error},
|
||||
};
|
||||
|
||||
pub type Type = String;
|
||||
|
||||
pub struct StructField {
|
||||
pub offset: usize,
|
||||
pub field_type: Type,
|
||||
pub field_type: String,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum FnParams {
|
||||
Normal(Vec<String>),
|
||||
Variadic,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FnType {
|
||||
pub return_type: Type,
|
||||
pub params: Option<Vec<Type>>,
|
||||
pub return_type: String,
|
||||
pub params: FnParams,
|
||||
}
|
||||
|
||||
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()),
|
||||
params: FnParams::Normal(params.iter().map(|x| x.to_string()).collect()),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_variadic(return_type: &str) -> FnType {
|
||||
FnType {
|
||||
return_type: return_type.to_string(),
|
||||
params: None,
|
||||
params: FnParams::Variadic,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,9 +78,15 @@ impl SymbolTable {
|
||||
return error!(name.loc, format!("tried to redefine '{}'", name.lexeme));
|
||||
}
|
||||
let mut value = if value.lexeme.starts_with("0x") {
|
||||
u64::from_str_radix(&value.lexeme[2..], 16).unwrap()
|
||||
match u64::from_str_radix(&value.lexeme[2..], 16) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return error!(value.loc, "failed to parse hex numeric constant"),
|
||||
}
|
||||
} else {
|
||||
value.lexeme.parse().unwrap()
|
||||
match value.lexeme.parse() {
|
||||
Ok(v) => v,
|
||||
Err(_) => return error!(value.loc, "failed to parse numeric constant"),
|
||||
}
|
||||
} as i64;
|
||||
if *neg {
|
||||
value = -value;
|
||||
@@ -110,7 +120,7 @@ impl SymbolTable {
|
||||
name.lexeme.clone(),
|
||||
FnType {
|
||||
return_type,
|
||||
params: Some(
|
||||
params: FnParams::Normal(
|
||||
params.iter().map(|x| x.var_type.lexeme.clone()).collect(),
|
||||
),
|
||||
},
|
||||
@@ -119,7 +129,7 @@ impl SymbolTable {
|
||||
name.lexeme.clone(),
|
||||
FnType {
|
||||
return_type,
|
||||
params: None,
|
||||
params: FnParams::Variadic,
|
||||
},
|
||||
),
|
||||
};
|
||||
|
||||
@@ -245,6 +245,9 @@ impl Tokenizer {
|
||||
return error!(self.loc, "unterminated char literal");
|
||||
}
|
||||
_ = self.match_char('\\'); // if its an escape sequence skip \ and read one more
|
||||
if self.eof() {
|
||||
return error!(self.loc, "unterminated char literal");
|
||||
}
|
||||
self.advance();
|
||||
if !self.match_char('\'') {
|
||||
return error!(self.loc, "expected ' after char literal");
|
||||
@@ -257,6 +260,12 @@ impl Tokenizer {
|
||||
while !self.eof() {
|
||||
if self.peek() == '\\' {
|
||||
self.advance();
|
||||
if self.eof() {
|
||||
return error!(
|
||||
self.loc,
|
||||
format!("unterminated string, started at {}", start_loc)
|
||||
);
|
||||
}
|
||||
} else if self.peek() == '"' {
|
||||
break;
|
||||
} else if self.peek() == '\n' {
|
||||
@@ -340,7 +349,10 @@ impl Tokenizer {
|
||||
fn scan_number(&mut self) -> Result<(), ZernError> {
|
||||
let mut is_float = false;
|
||||
|
||||
if self.match_char('x') {
|
||||
if self.source[self.current - 1] == '0' && self.match_char('x') {
|
||||
if !self.peek().is_ascii_hexdigit() {
|
||||
return error!(self.loc, "expected a digit after '0x'");
|
||||
}
|
||||
while self.peek().is_ascii_hexdigit() {
|
||||
self.advance();
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
parser::{Expr, ExprKind, Params, Stmt},
|
||||
symbol_table::{SymbolTable, Type},
|
||||
symbol_table::{FnParams, SymbolTable},
|
||||
tokenizer::{TokenType, ZernError, error},
|
||||
};
|
||||
|
||||
@@ -36,7 +36,7 @@ macro_rules! expect_types {
|
||||
static BUILTIN_TYPES: [&str; 8] = ["void", "u8", "i64", "f64", "str", "bool", "ptr", "any"];
|
||||
|
||||
pub struct Env {
|
||||
scopes: Vec<HashMap<String, Type>>,
|
||||
scopes: Vec<HashMap<String, String>>,
|
||||
}
|
||||
|
||||
impl Env {
|
||||
@@ -59,7 +59,7 @@ impl Env {
|
||||
self.scopes.last_mut().unwrap().insert(name, var_type);
|
||||
}
|
||||
|
||||
fn get_var_type(&self, name: &str) -> Option<&Type> {
|
||||
fn get_var_type(&self, name: &str) -> Option<&String> {
|
||||
for scope in self.scopes.iter().rev() {
|
||||
if let Some(var) = scope.get(name) {
|
||||
return Some(var);
|
||||
@@ -365,7 +365,7 @@ impl<'a> TypeChecker<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn typecheck_expr(&mut self, env: &mut Env, expr: &Expr) -> Result<Type, ZernError> {
|
||||
pub fn typecheck_expr(&mut self, env: &mut Env, expr: &Expr) -> Result<String, ZernError> {
|
||||
let expr_type = match &expr.kind {
|
||||
ExprKind::Binary { left, op, right } => {
|
||||
let left_type = self.typecheck_expr(env, left)?;
|
||||
@@ -464,24 +464,31 @@ impl<'a> TypeChecker<'a> {
|
||||
if let ExprKind::Variable(callee_name) = &callee.kind {
|
||||
if let Some(fn_type) = self.symbol_table.functions.get(&callee_name.lexeme) {
|
||||
// its a function (defined/builtin/extern)
|
||||
if let Some(params) = fn_type.params.clone() {
|
||||
if params.len() != args.len() {
|
||||
return error!(
|
||||
&paren.loc,
|
||||
format!(
|
||||
"expected {} arguments, got {}",
|
||||
params.len(),
|
||||
args.len()
|
||||
)
|
||||
);
|
||||
match &fn_type.params {
|
||||
FnParams::Normal(params) => {
|
||||
if params.len() != args.len() {
|
||||
return error!(
|
||||
&paren.loc,
|
||||
format!(
|
||||
"expected {} arguments, got {}",
|
||||
params.len(),
|
||||
args.len()
|
||||
)
|
||||
);
|
||||
}
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
expect_type!(
|
||||
self.typecheck_expr(env, arg)?,
|
||||
params[i],
|
||||
paren.loc
|
||||
);
|
||||
}
|
||||
}
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
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)?;
|
||||
FnParams::Variadic => {
|
||||
// cant check arg types
|
||||
for arg in args {
|
||||
self.typecheck_expr(env, arg)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(fn_type.return_type.clone())
|
||||
@@ -587,35 +594,38 @@ impl<'a> TypeChecker<'a> {
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(params) = &func_type.params {
|
||||
if params.len() != args.len() + 1 {
|
||||
return error!(
|
||||
method.loc,
|
||||
format!(
|
||||
"expected {} arguments, got {}",
|
||||
params.len() - 1,
|
||||
args.len()
|
||||
)
|
||||
);
|
||||
match &func_type.params {
|
||||
FnParams::Normal(params) => {
|
||||
if params.len() != args.len() + 1 {
|
||||
return error!(
|
||||
method.loc,
|
||||
format!(
|
||||
"expected {} arguments, got {}",
|
||||
params.len() - 1,
|
||||
args.len()
|
||||
)
|
||||
);
|
||||
}
|
||||
if params[0] != receiver_type {
|
||||
return error!(
|
||||
method.loc,
|
||||
format!(
|
||||
"first parameter of the method must be of type {}",
|
||||
receiver_type
|
||||
)
|
||||
);
|
||||
}
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
expect_type!(self.typecheck_expr(env, arg)?, params[i + 1], method.loc);
|
||||
}
|
||||
Ok(func_type.return_type.clone())
|
||||
}
|
||||
if params[0] != receiver_type {
|
||||
return error!(
|
||||
method.loc,
|
||||
format!(
|
||||
"first parameter of the method must be of type {}",
|
||||
receiver_type
|
||||
)
|
||||
);
|
||||
FnParams::Variadic => {
|
||||
for arg in args {
|
||||
self.typecheck_expr(env, arg)?;
|
||||
}
|
||||
Ok(func_type.return_type.clone())
|
||||
}
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
expect_type!(self.typecheck_expr(env, arg)?, params[i + 1], method.loc);
|
||||
}
|
||||
Ok(func_type.return_type.clone())
|
||||
} else {
|
||||
for arg in args {
|
||||
self.typecheck_expr(env, arg)?;
|
||||
}
|
||||
Ok(func_type.return_type.clone())
|
||||
}
|
||||
}
|
||||
}?;
|
||||
|
||||
Reference in New Issue
Block a user