diff options
| -rw-r--r-- | Cargo.lock | 9 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | src/compiler.rs | 2 | ||||
| -rw-r--r-- | src/error.rs | 4 | ||||
| -rw-r--r-- | src/lib.rs | 3 | ||||
| -rw-r--r-- | src/main.rs | 33 | ||||
| -rw-r--r-- | src/stack.rs | 3 | ||||
| -rw-r--r-- | src/vm.rs | 12 | ||||
| -rw-r--r-- | tests/simple.tdy | 5 | ||||
| -rw-r--r-- | tihdy_derive/Cargo.toml | 14 | ||||
| -rw-r--r-- | tihdy_derive/src/lib.rs | 86 |
11 files changed, 145 insertions, 27 deletions
@@ -525,6 +525,15 @@ dependencies = [ "criterion", "logos", "owo-colors", + "tihdy_derive", +] + +[[package]] +name = "tihdy_derive" +version = "0.1.0" +dependencies = [ + "quote", + "syn", ] [[package]] @@ -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<Value, ErrorKind>; #[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<Type>), + ExternTypeMismatch(String, Vec<Type>), RuntimeTypeError(Op, Vec<Value>), 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() @@ -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<PathBuf>, @@ -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<T> { + +} @@ -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 <gustav@sornas.net>"] +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<ExternBlock> +} + +impl Parse for ExternBlock { + fn parse(input: ParseStream) -> Result<Self> { + 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<Self> { + 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<tihdy::vm::Value, tihdy::error::ErrorKind> + { + 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) +} |
