Skip to content

Commit 9a44dd1

Browse files
committed
0.3 - many fixes, add support for Perl and Ruby
1 parent da99270 commit 9a44dd1

File tree

8 files changed

+455
-174
lines changed

8 files changed

+455
-174
lines changed

Cargo.toml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "inline-vbs"
3-
version = "0.2.1"
3+
version = "0.3.0"
44
authors = ["Tom Niget <niget.tom@gmail.com>"]
55
description = "Embed VBScript code inside Rust"
66
homepage = "https://github.com/zdimension/inline-vbs"
@@ -15,13 +15,13 @@ exclude = [
1515
]
1616

1717
[dependencies]
18-
inline-vbs-macros = { version = "0.2", path = "./macros" }
19-
cxx = "1.0.65"
20-
winapi = { version = "0.3.9", features = ["oaidl", "wtypes", "oleauto"] }
21-
widestring = "0.5.1"
22-
enumn = "0.1.3"
18+
inline-vbs-macros = { version = "0.3", path = "./macros" }
19+
variant-rs = { version = "0.2.1" }
20+
windows = { version = "0.43.0", features = ["Win32_System_Com", "Win32_System_Ole", "Win32_Foundation"] }
21+
cxx = "1.0.85"
22+
widestring = "1.0.2"
23+
enumn = "0.1.6"
2324
dep_doc = "0.1.1"
24-
variant-rs = "0.1"
2525

2626
[build-dependencies]
2727
cxx-build = "1.0"
@@ -33,4 +33,4 @@ members = [
3333

3434
[package.metadata.docs.rs]
3535
default-target = "x86_64-pc-windows-msvc"
36-
targets = ["x86_64-pc-windows-msvc"]
36+
targets = ["x86_64-pc-windows-msvc"]

README.md

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# inline-vbs
22

3-
`inline-vbs` is a crate that allows you to embed VBScript code inside Rust code files. It uses
3+
`inline-vbs` is a crate that allows you to embed VBScript, JScript and many other languages inside Rust code files. It uses
44
the [Active Scripting](https://docs.microsoft.com/en-us/archive/msdn-magazine/2000/december/active-scripting-apis-add-powerful-custom-debugging-to-your-script-hosting-app) COM APIs to dynamically parse and execute (optionally, evaluate) code.
55

66
## Basic usage
@@ -18,20 +18,32 @@ Macros:
1818
* `vbs!` - Executes a statement or evaluates an expression (depending on context)
1919
* `vbs_!` - Evaluates an expression
2020
* `vbs_raw!` - Executes a statement (string input instead of tokens, use for multiline code)
21-
See more examples in [tests/tests.rs](tests/tests.rs)
21+
See more examples in [tests/tests.rs](tests/tests.rs).
2222

2323
## Installation
2424
Add this to your `Cargo.toml`:
2525
```toml
2626
[dependencies]
27-
inline-vbs = "0.2.1"
27+
inline-vbs = "0.3.0"
2828
```
2929

3030
**Important:** You need to have the MSVC Build Tools installed on your computer, as required by [cc](https://github.com/rust-lang/cc-rs).
3131

32+
### Language support
33+
34+
VBScript (`vbs!`) and JScript (`js!`) are available out of the box on 32-bit and 64-bit.
35+
36+
Other Active Scripting engines exist:
37+
- Ruby (`ruby!`): ActiveScriptRuby [1.8 (tested, 32-bit only)](https://www.artonx.org/data/asr/ActiveRuby.msi)
38+
- [2.4 (32 or 64-bit) (untested!)](https://www.artonx.org/data/asr/), you need to change the CLSID in [src/vbs.cpp](src/vbs.cpp)
39+
- Perl (`perl!`): [ActivePerl 5.20 (32-bit)](https://raw.githubusercontent.com/PengjieRen/LibSum/master/ActivePerl-5.20.2.2002-MSWin32-x86-64int-299195.msi)
40+
41+
Note: install an engine matching the bitness of your program; by default Rust on Windows builds
42+
64-bit programs, which can only use 64-bit libraries. If you want to use a 32-bit library, you
43+
need to build your program with `--target i686-pc-windows-msvc`.
44+
3245
## Limitations
33-
Many. Most notably, `IDispatch` objects (i.e. what `CreateObject` returns) can't be passed to
34-
the engine (`let x = vbs! { CreateObject("WScript.Shell") }; vbs! { y = 'x }` won't work).
46+
Many.
3547

3648
## Motivation
3749
N/A

include/vbs.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
#pragma once
22

3+
//#include <stdint.h>
4+
35
#include "rust/cxx.h"
46
#include <Windows.h>
57

8+
enum class ScriptLang : uint8_t;
9+
610
int init();
7-
int parse_wrapper(rust::Str code, char* output);
11+
int parse_wrapper(rust::Str code, char* output, ScriptLang lang);
812
rust::String error_to_string(int hr);
9-
int set_variable(rust::Str name, char* val);
13+
int set_variable(rust::Str name, char* val, ScriptLang lang);

macros/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "inline-vbs-macros"
3-
version = "0.2.0"
3+
version = "0.3.0"
44
authors = ["Tom Niget <niget.tom@gmail.com>"]
55
description = "Macros for the `inline-vbs` crate"
66
homepage = "https://github.com/zdimension/inline-vbs"
@@ -16,5 +16,6 @@ proc-macro = true
1616

1717
[dependencies]
1818
litrs = "0.2.3"
19+
paste = "1.0.11"
1920
proc-macro2 = "1.0"
2021
quote = "1.0.15"

macros/src/lib.rs

Lines changed: 69 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,41 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2828
#![feature(proc_macro_span)]
2929

3030
use litrs::StringLit;
31+
use paste::paste;
3132
use proc_macro::LineColumn;
3233
use proc_macro::Span;
33-
use proc_macro2::TokenStream;
3434
use proc_macro2::{Delimiter, Ident, Spacing, TokenTree};
35-
use quote::quote;
35+
use proc_macro2::{Punct, TokenStream};
3636
use quote::quote_spanned;
37+
use quote::{quote, ToTokens, TokenStreamExt};
3738
use std::collections::BTreeMap;
3839
use std::fmt::Write;
3940

41+
#[derive(Copy, Clone, Debug)]
42+
#[repr(C)]
43+
enum ScriptLang {
44+
VBScript,
45+
JScript,
46+
Perl,
47+
Ruby,
48+
}
49+
50+
impl ToTokens for ScriptLang {
51+
fn to_tokens(&self, tokens: &mut TokenStream) {
52+
tokens.append(Ident::new("inline_vbs", proc_macro2::Span::call_site()));
53+
tokens.append(Punct::new(':', Spacing::Joint));
54+
tokens.append(Punct::new(':', Spacing::Alone));
55+
tokens.append(Ident::new("ScriptLang", proc_macro2::Span::call_site()));
56+
tokens.append(Punct::new(':', Spacing::Joint));
57+
tokens.append(Punct::new(':', Spacing::Alone));
58+
let lang = format!("{:?}", self);
59+
tokens.append(Ident::new(&lang, proc_macro2::Span::call_site()));
60+
}
61+
}
62+
4063
extern crate proc_macro;
4164

42-
fn macro_impl(input: TokenStream) -> Result<TokenStream, TokenStream> {
65+
fn macro_impl(input: TokenStream, lang: ScriptLang) -> Result<TokenStream, TokenStream> {
4366
let mut x = EmbedVbs::new();
4467

4568
x.add(input)?;
@@ -53,53 +76,60 @@ fn macro_impl(input: TokenStream) -> Result<TokenStream, TokenStream> {
5376

5477
Ok(quote! {
5578
{
56-
#(::inline_vbs::set_variable(#varname, #var).unwrap();)*
57-
::inline_vbs::Runner::run_code(#code)
79+
#(::inline_vbs::set_variable(#varname, #var, #lang).expect("Couldn't set variable");)*
80+
::inline_vbs::Runner::run_code(#code, #lang)
5881
}
5982
})
6083
}
6184

62-
#[proc_macro]
63-
pub fn vbs(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
64-
proc_macro::TokenStream::from(process_tokens(input))
65-
}
85+
macro_rules! lang {
86+
($name:ident, $val:ident) => {
87+
#[proc_macro]
88+
pub fn $name(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
89+
proc_macro::TokenStream::from(process_tokens(input, ScriptLang::$val))
90+
}
6691

67-
fn process_tokens(input: proc_macro::TokenStream) -> TokenStream {
68-
match macro_impl(TokenStream::from(input)) {
69-
Ok(tokens) => tokens,
70-
Err(tokens) => tokens,
71-
}
72-
}
92+
paste! {
93+
#[proc_macro]
94+
pub fn [< $name _ >](input: proc_macro::TokenStream) -> proc_macro::TokenStream {
95+
let call = process_tokens(input, ScriptLang::$val);
96+
(quote! {
97+
{
98+
let res: Variant = #call;
99+
res
100+
}
101+
})
102+
.into()
103+
}
73104

74-
#[proc_macro]
75-
pub fn vbs_(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
76-
let call = process_tokens(input);
77-
(quote! {
78-
{
79-
let res: Variant = #call;
80-
res
105+
#[proc_macro]
106+
pub fn [< $name _raw >](input: proc_macro::TokenStream) -> proc_macro::TokenStream {
107+
let input = input.into_iter().collect::<Vec<_>>();
108+
if input.len() != 1 {
109+
let msg = format!("expected exactly one input token, got {}", input.len());
110+
return quote! { compile_error!(#msg) }.into();
111+
}
112+
let string_lit = match StringLit::try_from(&input[0]) {
113+
Err(e) => return e.to_compile_error(),
114+
Ok(lit) => lit,
115+
};
116+
let code = string_lit.value();
117+
quote! [::inline_vbs::run_code(#code, ScriptLang::$val)].into()
118+
}
81119
}
82-
})
83-
.into()
120+
};
84121
}
85122

86-
#[proc_macro]
87-
pub fn vbs_raw(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
88-
let input = input.into_iter().collect::<Vec<_>>();
123+
lang!(vbs, VBScript);
124+
lang!(js, JScript);
125+
lang!(perl, Perl);
126+
lang!(ruby, Ruby);
89127

90-
if input.len() != 1 {
91-
let msg = format!("expected exactly one input token, got {}", input.len());
92-
return quote! { compile_error!(#msg) }.into();
128+
fn process_tokens(input: proc_macro::TokenStream, lang: ScriptLang) -> TokenStream {
129+
match macro_impl(TokenStream::from(input), lang) {
130+
Ok(tokens) => tokens,
131+
Err(tokens) => tokens,
93132
}
94-
95-
let string_lit = match StringLit::try_from(&input[0]) {
96-
Err(e) => return e.to_compile_error(),
97-
Ok(lit) => lit,
98-
};
99-
100-
let code = string_lit.value();
101-
102-
quote! [::inline_vbs::run_code(#code)].into()
103133
}
104134

105135
struct EmbedVbs {

src/lib.rs

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,28 +41,39 @@ use std::os::raw::c_char;
4141
pub use variant_rs::variant::ToVariant;
4242
pub use variant_rs::variant::Variant;
4343

44-
pub use winapi::um::oaidl::VARIANT;
45-
use winapi::um::oleauto::VariantInit;
44+
use windows::Win32::System::Com::VARIANT;
45+
use windows::Win32::System::Ole::VariantInit;
4646

47-
pub use inline_vbs_macros::{vbs, vbs_, vbs_raw};
47+
pub use inline_vbs_macros::*;
4848

4949
#[allow(clippy::upper_case_acronyms)]
5050
type HRESULT = i32;
5151

5252
#[cxx::bridge]
5353
mod ffi {
54+
enum ScriptLang {
55+
VBScript,
56+
JScript,
57+
Perl,
58+
Ruby,
59+
Last,
60+
}
61+
5462
unsafe extern "C++" {
5563
include!("inline-vbs/include/vbs.h");
5664

5765
pub fn init() -> i32;
58-
pub unsafe fn parse_wrapper(str: &str, output: *mut c_char) -> i32;
66+
pub unsafe fn parse_wrapper(str: &str, output: *mut c_char, lang: ScriptLang) -> i32;
5967
pub fn error_to_string(error: i32) -> String;
60-
pub unsafe fn set_variable(name: &str, val: *mut c_char) -> i32;
68+
pub unsafe fn set_variable(name: &str, val: *mut c_char, lang: ScriptLang) -> i32;
6169
}
6270
}
6371

72+
pub use ffi::ScriptLang;
73+
6474
fn decode_hr<T>(hr: i32, val: T) -> Result<T, String> {
6575
if hr != 0 {
76+
print!("Error: 0x{:08x}\n", hr);
6677
Err(ffi::error_to_string(hr))
6778
} else {
6879
Ok(val)
@@ -76,41 +87,45 @@ fn or_die(hr: HRESULT) {
7687
}
7788

7889
pub trait Runner {
79-
fn run_code(code: &str) -> Self;
90+
fn run_code(code: &str, lang: ScriptLang) -> Self;
8091
}
8192

8293
impl Runner for Variant {
83-
fn run_code(code: &str) -> Self {
94+
fn run_code(code: &str, lang: ScriptLang) -> Self {
8495
unsafe {
8596
or_die(ffi::init());
8697
let mut variant: VARIANT = std::mem::zeroed();
8798
VariantInit(&mut variant);
88-
let result =
89-
ffi::parse_wrapper(code.trim(), (&mut variant) as *mut VARIANT as *mut c_char);
90-
decode_hr(result, ()).unwrap();
91-
variant.try_into().unwrap()
99+
let result = ffi::parse_wrapper(
100+
code.trim(),
101+
(&mut variant) as *mut VARIANT as *mut c_char,
102+
lang,
103+
);
104+
decode_hr(result, ()).expect("Non-success HRESULT");
105+
variant.try_into().expect("Can't decode VARIANT")
92106
}
93107
}
94108
}
95109

96110
impl Runner for () {
97-
fn run_code(code: &str) -> Self {
111+
fn run_code(code: &str, lang: ffi::ScriptLang) -> Self {
98112
unsafe {
99113
or_die(ffi::init());
100-
let result = ffi::parse_wrapper(code, std::ptr::null_mut());
101-
decode_hr(result, ()).unwrap();
114+
let result = ffi::parse_wrapper(code, std::ptr::null_mut(), lang);
115+
decode_hr(result, ()).expect("Non-success HRESULT");
102116
}
103117
}
104118
}
105119

106-
pub fn set_variable(name: &str, val: impl ToVariant) -> Result<(), String> {
120+
pub fn set_variable(name: &str, val: impl ToVariant, lang: ScriptLang) -> Result<(), String> {
107121
unsafe {
108-
let _: () = Runner::run_code(format!("Dim {}", name).as_str());
122+
//let _: () = Runner::run_code(format!("Dim {}", name).as_str());
123+
or_die(ffi::init());
109124
let var = val.to_variant();
110125
let ptr: VARIANT = var
111126
.try_into()
112127
.map_err(|e| format!("Variant conversion error: {:?}", e))?;
113-
let result = ffi::set_variable(name, &ptr as *const VARIANT as *mut _);
128+
let result = ffi::set_variable(name, &ptr as *const VARIANT as *mut _, lang);
114129
decode_hr(result, ())
115130
}
116131
}

0 commit comments

Comments
 (0)