From e93e9e7ff4a73bc98a0dcd410eff87dffff3aa1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Sat, 30 Jan 2021 12:10:49 +0100 Subject: external functions proc macro --- Cargo.lock | 9 ++++++ Cargo.toml | 1 + src/compiler.rs | 2 +- src/error.rs | 4 +++ src/lib.rs | 3 +- src/main.rs | 33 +++++++++---------- src/stack.rs | 3 ++ src/vm.rs | 12 ++++--- tests/simple.tdy | 5 ++- tihdy_derive/Cargo.toml | 14 ++++++++ tihdy_derive/src/lib.rs | 86 +++++++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 145 insertions(+), 27 deletions(-) create mode 100644 src/stack.rs create mode 100644 tihdy_derive/Cargo.toml create mode 100644 tihdy_derive/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 00a0067..3e3759e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -525,6 +525,15 @@ dependencies = [ "criterion", "logos", "owo-colors", + "tihdy_derive", +] + +[[package]] +name = "tihdy_derive" +version = "0.1.0" +dependencies = [ + "quote", + "syn", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index ddafc76..e78987e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ name = "tihdy" [dependencies] logos = "0.11.4" owo-colors = { git="https://github.com/FredTheDino/owo-colors.git" } +tihdy_derive = { path = "tihdy_derive" } criterion = { version = "0.3", optional = true } diff --git a/src/compiler.rs b/src/compiler.rs index 866e0a3..559068d 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -182,7 +182,7 @@ impl Frame { } } -pub type RustFunction = fn(&[Value]) -> Value; +pub type RustFunction = fn(&[Value], bool) -> Result; #[derive(Debug, Clone)] pub struct Blob { diff --git a/src/error.rs b/src/error.rs index d8d4664..b489cd1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -11,6 +11,7 @@ use crate::vm::{Op, Value}; #[derive(Debug, Clone)] pub enum ErrorKind { TypeError(Op, Vec), + ExternTypeMismatch(String, Vec), RuntimeTypeError(Op, Vec), Assert, InvalidProgram, @@ -36,6 +37,9 @@ impl fmt::Display for ErrorKind { .fold(String::new(), |a, v| { format!("{}{:?}, ", a, v) }); write!(f, "{} Cannot apply {:?} to types {}", "Type Error".bold(), op, types) } + ErrorKind::ExternTypeMismatch(name, types) => { + write!(f, "{} Extern function '{}' doesn't accept argument(s) with type(s) {:?}", "Type Error".bold(), name, types) + } ErrorKind::RuntimeTypeError(op, values) => { let values = values .iter() diff --git a/src/lib.rs b/src/lib.rs index e68adff..17f680c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,10 @@ use std::path::Path; pub mod compiler; +pub mod error; pub mod tokenizer; pub mod vm; -mod error; - use compiler::RustFunction; use error::Error; use tokenizer::TokenStream; diff --git a/src/main.rs b/src/main.rs index a97cd43..10b88ba 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,6 @@ use std::path::{Path, PathBuf}; use tihdy::run_file; -use tihdy::vm::Value; struct Args { file: Option, @@ -11,12 +10,14 @@ struct Args { fn main() { let args = parse_args(); let file = args.file.unwrap_or_else(|| Path::new("tests/simple.tdy").to_owned()); - if let Err(errs) = run_file(&file, args.print, vec![(String::from("hello"), hello)]) { - for err in errs.iter() { - println!("{}", err); - } - println!(" {} errors occured.", errs.len()); + let errs = match run_file(&file, args.print, vec![(String::from("extern_test"), extern_test)]) { + Err(it) => it, + _ => return, + }; + for err in errs.iter() { + println!("{}", err); } + println!(" {} errors occured.", errs.len()); } fn parse_args() -> Args { @@ -38,14 +39,12 @@ fn parse_args() -> Args { args } -pub fn hello(parameters: &[Value]) -> Value { - match parameters { - [Value::String(s)] => { - println!("{}", s); - } - _ => { - println!("Bad parameters"); - } - } - Value::Nil -} +tihdy_derive::extern_function!( + extern_test + [tihdy::vm::Value::Float(x), tihdy::vm::Value::Float(y)] -> tihdy::vm::Type::Float => { + Ok(tihdy::vm::Value::Float(x + y)) + }, + [tihdy::vm::Value::Float(x)] -> tihdy::vm::Type::Float => { + Ok(tihdy::vm::Value::Float(*x)) + }, +); diff --git a/src/stack.rs b/src/stack.rs new file mode 100644 index 0000000..a75c775 --- /dev/null +++ b/src/stack.rs @@ -0,0 +1,3 @@ +struct Stack { + +} diff --git a/src/vm.rs b/src/vm.rs index da044a5..3559a2d 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -6,8 +6,9 @@ use std::path::{Path, PathBuf}; use std::rc::Rc; use std::cell::RefCell; +pub use crate::compiler::Type; + use crate::compiler::RustFunction; -use crate::compiler::Type; use crate::error::{Error, ErrorKind}; use crate::compiler::{Prog, Blob}; @@ -629,7 +630,7 @@ impl VM { } Value::ExternFunction(slot) => { let extern_func = self.extern_functions[slot]; - let res = extern_func(&self.stack[new_base+1..]); + let res = extern_func(&self.stack[new_base+1..], false).unwrap(); //FIXME self.stack.truncate(new_base); self.stack.push(res); } @@ -869,8 +870,11 @@ impl VM { self.stack.truncate(new_base + 1); } - Value::ExternFunction(_slot) => { - self.stack.truncate(new_base + 1); + Value::ExternFunction(slot) => { + let extern_func = self.extern_functions[slot]; + let res = extern_func(&self.stack[new_base+1..], true).unwrap(); //FIXME + self.stack.truncate(new_base); + self.stack.push(res); } _ => { error!(self, diff --git a/tests/simple.tdy b/tests/simple.tdy index 8b68da1..a1f255c 100644 --- a/tests/simple.tdy +++ b/tests/simple.tdy @@ -1,3 +1,2 @@ -hello() -hello("a") -hello("a", "b") +print extern_test(3.0) +print extern_test(3.0, 4.0) diff --git a/tihdy_derive/Cargo.toml b/tihdy_derive/Cargo.toml new file mode 100644 index 0000000..411f1ab --- /dev/null +++ b/tihdy_derive/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "tihdy_derive" +version = "0.1.0" +authors = ["Gustav Sörnäs "] +edition = "2018" + +[lib] +proc-macro = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +syn = { version = "1.0", features=["full"] } +quote = "1.0" diff --git a/tihdy_derive/src/lib.rs b/tihdy_derive/src/lib.rs new file mode 100644 index 0000000..cc28d64 --- /dev/null +++ b/tihdy_derive/src/lib.rs @@ -0,0 +1,86 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::{Expr, Pat, Token, parse::{Parse, ParseStream, Result}, parse_macro_input, punctuated::Punctuated}; + +struct ExternBlock { + pattern: Pat, + _arrow: Token![->], + return_ty: Expr, + _fat_arrow: Token![=>], + block: Expr, + _comma: Token![,], +} + +struct ExternFunction { + function: syn::Ident, + blocks: Vec +} + +impl Parse for ExternBlock { + fn parse(input: ParseStream) -> Result { + Ok(Self { + pattern: input.parse()?, + _arrow: input.parse()?, + return_ty: input.parse()?, + _fat_arrow: input.parse()?, + block: input.parse()?, + _comma: input.parse()?, + }) + } +} + +impl Parse for ExternFunction { + fn parse(input: ParseStream) -> Result { + let mut res = Self { + function: input.parse()?, + blocks: Vec::new(), + }; + while !input.is_empty() { + res.blocks.push(input.parse()?); + } + Ok(res) + } +} + +#[proc_macro] +pub fn extern_function(tokens: TokenStream) -> TokenStream { + let parsed: ExternFunction = parse_macro_input!(tokens); + let function = parsed.function; + + let typecheck_blocks: Vec<_> = parsed.blocks.iter().map(|block| { + let pat = block.pattern.clone(); + let ty = block.return_ty.clone(); + quote! { + #pat => { Ok(#ty.as_value()) } + } + }).collect(); + + let eval_blocks: Vec<_> = parsed.blocks.iter().map(|block| { + let pat = block.pattern.clone(); + let expr = block.block.clone(); + quote! { + #pat => #expr + } + }).collect(); + + let tokens = quote! { + pub fn #function ( + __values: &[tihdy::vm::Value], + __typecheck: bool + ) -> ::std::result::Result + { + if __typecheck { + match __values { + #(#typecheck_blocks),* + _ => Err(tihdy::error::ErrorKind::ExternTypeMismatch(stringify!(#function).to_string(), __values.iter().map(|v| tihdy::vm::Type::from(v)).collect())) + } + } else { + match __values { + #(#eval_blocks),* + _ => Err(tihdy::error::ErrorKind::ExternTypeMismatch(stringify!(#function).to_string(), __values.iter().map(|v| tihdy::vm::Type::from(v)).collect())) + } + } + } + }; + TokenStream::from(tokens) +} -- cgit v1.2.1