allow dynamic calls
This commit is contained in:
@@ -120,20 +120,31 @@ impl Analyzer {
|
|||||||
paren,
|
paren,
|
||||||
args,
|
args,
|
||||||
} => {
|
} => {
|
||||||
let callee = match callee.as_ref() {
|
if let Expr::Variable(callee_name) = *callee.clone() {
|
||||||
Expr::Variable(name) => name.lexeme.clone(),
|
if callee_name.lexeme.starts_with("_builtin_")
|
||||||
_ => return error!(&paren.loc, "tried to call a non-constant expression"),
|
|| self.functions.contains_key(&callee_name.lexeme)
|
||||||
};
|
{
|
||||||
|
// its a function (defined/builtin/extern)
|
||||||
if let Some(arity) = self.functions.get(&callee) {
|
if let Some(arity) = self.functions.get(&callee_name.lexeme) {
|
||||||
if *arity >= 0 && *arity != args.len() as i32 {
|
if *arity >= 0 && *arity != args.len() as i32 {
|
||||||
return error!(
|
return error!(
|
||||||
&paren.loc,
|
&paren.loc,
|
||||||
format!("expected {} arguments, got {}", arity, args.len())
|
format!("expected {} arguments, got {}", arity, args.len())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if !callee.starts_with("_builtin_") {
|
} else if !callee_name.lexeme.starts_with("_builtin_") {
|
||||||
return error!(&paren.loc, format!("undefined function: {}", callee));
|
return error!(
|
||||||
|
&paren.loc,
|
||||||
|
format!("undefined function: {}", callee_name.lexeme)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// its a variable containing function address
|
||||||
|
self.analyze_expr(callee)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// its an expression that evalutes to function address
|
||||||
|
self.analyze_expr(callee)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
for arg in args {
|
for arg in args {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use std::{collections::HashMap, fmt::Write};
|
use std::{collections::HashMap, fmt::Write};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
analyzer::Analyzer,
|
||||||
parser::{Expr, Stmt},
|
parser::{Expr, Stmt},
|
||||||
tokenizer::{TokenType, ZernError, error},
|
tokenizer::{TokenType, ZernError, error},
|
||||||
};
|
};
|
||||||
@@ -64,20 +65,22 @@ macro_rules! emit {
|
|||||||
|
|
||||||
static REGISTERS: [&str; 6] = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"];
|
static REGISTERS: [&str; 6] = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"];
|
||||||
|
|
||||||
pub struct CodegenX86_64 {
|
pub struct CodegenX86_64<'a> {
|
||||||
output: String,
|
output: String,
|
||||||
data_section: String,
|
data_section: String,
|
||||||
label_counter: usize,
|
label_counter: usize,
|
||||||
data_counter: usize,
|
data_counter: usize,
|
||||||
|
pub analyzer: &'a mut Analyzer,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CodegenX86_64 {
|
impl<'a> CodegenX86_64<'a> {
|
||||||
pub fn new() -> CodegenX86_64 {
|
pub fn new(analyzer: &'a mut 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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -470,14 +473,9 @@ _builtin_environ:
|
|||||||
}
|
}
|
||||||
Expr::Call {
|
Expr::Call {
|
||||||
callee,
|
callee,
|
||||||
paren,
|
paren: _,
|
||||||
args,
|
args,
|
||||||
} => {
|
} => {
|
||||||
let callee = match *callee {
|
|
||||||
Expr::Variable(name) => name.lexeme,
|
|
||||||
_ => return error!(&paren.loc, "tried to call a non-constant expression"),
|
|
||||||
};
|
|
||||||
|
|
||||||
for arg in &args {
|
for arg in &args {
|
||||||
self.compile_expr(env, arg.clone())?;
|
self.compile_expr(env, arg.clone())?;
|
||||||
emit!(&mut self.output, " push rax");
|
emit!(&mut self.output, " push rax");
|
||||||
@@ -507,19 +505,22 @@ _builtin_environ:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match env.get_var(&callee) {
|
if let Expr::Variable(callee_name) = &*callee {
|
||||||
Some(var) => {
|
if callee_name.lexeme.starts_with("_builtin_")
|
||||||
emit!(
|
|| self.analyzer.functions.contains_key(&callee_name.lexeme)
|
||||||
&mut self.output,
|
{
|
||||||
" mov rax, QWORD [rbp-{}]",
|
// its a function (defined/builtin/extern)
|
||||||
var.stack_offset,
|
emit!(&mut self.output, " call {}", callee_name.lexeme);
|
||||||
);
|
} else {
|
||||||
|
// its a variable containing function address
|
||||||
|
self.compile_expr(env, *callee)?;
|
||||||
emit!(&mut self.output, " call rax");
|
emit!(&mut self.output, " call rax");
|
||||||
}
|
}
|
||||||
None => {
|
} else {
|
||||||
emit!(&mut self.output, " call {}", callee);
|
// its an expression that evalutes to function address
|
||||||
|
self.compile_expr(env, *callee)?;
|
||||||
|
emit!(&mut self.output, " call rax");
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
if arg_count > 6 {
|
if arg_count > 6 {
|
||||||
let num_stack = arg_count - 6;
|
let num_stack = arg_count - 6;
|
||||||
|
|||||||
16
src/main.rs
16
src/main.rs
@@ -14,7 +14,6 @@ use tokenizer::ZernError;
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
fn compile_file_to(
|
fn compile_file_to(
|
||||||
analyzer: &mut analyzer::Analyzer,
|
|
||||||
codegen: &mut codegen_x86_64::CodegenX86_64,
|
codegen: &mut codegen_x86_64::CodegenX86_64,
|
||||||
filename: &str,
|
filename: &str,
|
||||||
source: String,
|
source: String,
|
||||||
@@ -26,11 +25,11 @@ fn compile_file_to(
|
|||||||
let statements = parser.parse()?;
|
let statements = parser.parse()?;
|
||||||
|
|
||||||
for stmt in &statements {
|
for stmt in &statements {
|
||||||
analyzer.register_function(stmt)?;
|
codegen.analyzer.register_function(stmt)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
for stmt in &statements {
|
for stmt in &statements {
|
||||||
analyzer.analyze_stmt(stmt)?;
|
codegen.analyzer.analyze_stmt(stmt)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
for stmt in statements {
|
for stmt in statements {
|
||||||
@@ -62,15 +61,10 @@ 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 analyzer = analyzer::Analyzer::new();
|
let mut analyzer = analyzer::Analyzer::new();
|
||||||
let mut codegen = codegen_x86_64::CodegenX86_64::new();
|
let mut codegen = codegen_x86_64::CodegenX86_64::new(&mut analyzer);
|
||||||
codegen.emit_prologue()?;
|
codegen.emit_prologue()?;
|
||||||
compile_file_to(
|
compile_file_to(&mut codegen, "std.zr", include_str!("std.zr").into())?;
|
||||||
&mut analyzer,
|
compile_file_to(&mut codegen, filename, source)?;
|
||||||
&mut codegen,
|
|
||||||
"std.zr",
|
|
||||||
include_str!("std.zr").into(),
|
|
||||||
)?;
|
|
||||||
compile_file_to(&mut analyzer, &mut codegen, filename, source)?;
|
|
||||||
|
|
||||||
if !args.output_asm {
|
if !args.output_asm {
|
||||||
fs::write(format!("{}.s", args.out), codegen.get_output()).unwrap();
|
fs::write(format!("{}.s", args.out), codegen.get_output()).unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user