-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
219d1ad
commit 7a1b3a6
Showing
7 changed files
with
241 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
[workspace] | ||
members = [ | ||
"emscripten_em_js", | ||
"remglk", | ||
"remglk_capi", | ||
] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
[package] | ||
name = "emscripten_em_js" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
authors = ["Dannii Willis <[email protected]>"] | ||
description = "Rust versions of the Emscripten EM_JS and EM_ASYNC_JS macros" | ||
homepage = "https://github.com/curiousdannii/remglk-rs" | ||
license = "MIT" | ||
repository = "https://github.com/curiousdannii/remglk-rs" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[lib] | ||
proc-macro = true | ||
|
||
[dependencies] | ||
quote = "1.0.35" | ||
syn = {version="2.0.48", features=["full"]} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
/* | ||
Rust versions of the Emscripten EM_JS and EM_ASYNC_JS macros | ||
============================================================ | ||
Copyright (c) 2024 Dannii Willis | ||
MIT licenced | ||
https://github.com/curiousdannii/remglk-rs | ||
*/ | ||
|
||
//! em_js!() declares a Javascript function. It is largely similar to the Emscripten macro `EM_JS`. | ||
//! | ||
//! ```c | ||
//! EM_JS(int, add, (int x, int y), { | ||
//! return x + y; | ||
//! }) | ||
//! ``` | ||
//! | ||
//! But instead of separate parameters, it takes a whole Rust function declaration, formatted as usual. | ||
//! The Javascript code must be included as a string. If your JS code uses double quotes, you can use a | ||
//! raw string. | ||
//! | ||
//! ``` | ||
//! em_js!(fn add(x: i32, y: i32) -> i32 { r#" | ||
//! return x + y; | ||
//! "# }) | ||
//! ``` | ||
//! | ||
//! You may also declare async functions. Unlike in Emscripten where you would use the `EM_ASYNC_JS` | ||
//! macro, these use the same macro, just declare the function as `async`: | ||
//! | ||
//! ``` | ||
//! em_js!(async fn add(x: i32, y: i32) -> i32 { r#" | ||
//! return x + y; | ||
//! "# }) | ||
//! ``` | ||
//! | ||
//! Supported types: | ||
//! | ||
//! | Type | Input | Output | | ||
//! |-------|-------|--------| | ||
//! | [f64] | Y | Y | | ||
//! | [i32] | Y | Y | | ||
use proc_macro::TokenStream; | ||
use quote::{format_ident, quote}; | ||
use syn::{Block, Expr, FnArg, ItemFn, Lit, Pat, Stmt, Type}; | ||
use syn::punctuated::Punctuated; | ||
use syn::token::Comma; | ||
|
||
/** em_js!() declares a Javascript function. It is largely similar to the Emscripten macro `EM_JS`. | ||
* | ||
* For examples, and supported types, see [the module documentation](crate). | ||
*/ | ||
#[proc_macro] | ||
pub fn em_js(input: TokenStream) -> TokenStream { | ||
let parsed = syn::parse::<ItemFn>(input).unwrap(); | ||
let name = parsed.sig.ident; | ||
let link_name = name.to_string(); | ||
let js_name = format_ident!("__em_js__{}{}", if parsed.sig.asyncness.is_some() {"__asyncjs__"} else {""}, name); | ||
let inputs = parsed.sig.inputs; | ||
let output = parsed.sig.output; | ||
let body = format!("({})<::>{{{}}}", rust_args_to_c(&inputs), get_body_str(parsed.block.as_ref())); | ||
|
||
let result = quote! { | ||
extern "C" { | ||
#[link_name = #link_name] | ||
pub fn #name(#inputs) #output; | ||
} | ||
|
||
#[link_section = ".em_js"] | ||
#[no_mangle] | ||
#[used] | ||
static #js_name: &str = #body; | ||
}; | ||
|
||
// Do I need to manually emit bytes? https://github.com/rust-lang/rust/issues/70239 | ||
|
||
result.into() | ||
} | ||
|
||
fn get_body_str(block: &Block) -> String { | ||
let body = &block.stmts[0]; | ||
if let Stmt::Expr(Expr::Lit(lit), _) = body { | ||
if let Lit::Str(body) = &lit.lit { | ||
return body.value().to_owned(); | ||
} | ||
} | ||
panic!("em_js body was not string"); | ||
} | ||
|
||
fn rust_args_to_c(args: &Punctuated<FnArg, Comma>) -> String { | ||
let mut results: Vec<String> = vec![]; | ||
for arg in args.iter() { | ||
let c_type = if let FnArg::Typed(arg) = arg { | ||
let name = if let Pat::Ident(name) = arg.pat.as_ref() { | ||
&name.ident | ||
} | ||
else { | ||
unreachable!(); | ||
}; | ||
let rust_type = if let Type::Path(path) = arg.ty.as_ref() { | ||
path.path.segments.first().unwrap().ident.to_string() | ||
} | ||
else { | ||
unreachable!(); | ||
}; | ||
let c_type = match rust_type.as_str() { | ||
"f64" => "double", | ||
"i32" => "int", | ||
other => panic!("unsupported argument type: {}", other), | ||
}; | ||
format!("{} {}", c_type, name) | ||
} | ||
else { | ||
panic!("self arg in em_js"); | ||
}; | ||
results.push(c_type); | ||
} | ||
results.join(", ") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
/* | ||
Emglken system | ||
============== | ||
Copyright (c) 2024 Dannii Willis | ||
MIT licenced | ||
https://github.com/curiousdannii/remglk-rs | ||
*/ | ||
|
||
use std::collections::HashMap; | ||
use std::env::temp_dir; | ||
use std::fs; | ||
use std::io::{self, BufRead}; | ||
use std::path::Path; | ||
|
||
use emscripten_em_js::em_js; | ||
|
||
use super::*; | ||
use remglk::GlkSystem; | ||
use glkapi::protocol::{Event, SystemFileRef, Update}; | ||
|
||
pub type GlkApi = glkapi::GlkApi<EmglkenSystem>; | ||
|
||
pub fn glkapi() -> &'static Mutex<GlkApi> { | ||
static GLKAPI: OnceLock<Mutex<GlkApi>> = OnceLock::new(); | ||
GLKAPI.get_or_init(|| { | ||
Mutex::new(GlkApi::new(EmglkenSystem::default())) | ||
}) | ||
} | ||
|
||
#[derive(Default)] | ||
pub struct EmglkenSystem { | ||
cache: HashMap<String, Box<[u8]>>, | ||
tempfile_counter: u32, | ||
} | ||
|
||
impl GlkSystem for EmglkenSystem { | ||
fn fileref_construct(&mut self, filename: String, filetype: FileType, gameid: Option<String>) -> SystemFileRef { | ||
SystemFileRef { | ||
filename, | ||
gameid, | ||
usage: Some(filetype), | ||
..Default::default() | ||
} | ||
} | ||
|
||
fn fileref_delete(&mut self, fileref: &SystemFileRef) { | ||
unimplemented!() | ||
} | ||
|
||
fn fileref_exists(&mut self, fileref: &SystemFileRef) -> bool { | ||
unimplemented!() | ||
} | ||
|
||
fn fileref_read(&mut self, fileref: &SystemFileRef) -> Option<Box<[u8]>> { | ||
unimplemented!() | ||
} | ||
|
||
fn fileref_temporary(&mut self, filetype: FileType) -> SystemFileRef { | ||
unimplemented!() | ||
} | ||
|
||
fn fileref_write_buffer(&mut self, fileref: &SystemFileRef, buf: Box<[u8]>) { | ||
unimplemented!() | ||
} | ||
|
||
fn flush_writeable_files(&mut self) { | ||
unimplemented!() | ||
} | ||
|
||
fn get_glkote_event(&mut self) -> Event { | ||
unimplemented!() | ||
} | ||
|
||
fn send_glkote_update(&mut self, update: Update) { | ||
unimplemented!() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters