feo3boy_opcodes/compiler/
args.rs

1//! Provides utilities for working with args to the microcode.
2
3use std::any::{Any, TypeId};
4use std::fmt;
5
6use proc_macro2::TokenStream;
7use quote::{quote, ToTokens};
8
9/// Function type used to look up crate names for literal values.
10pub type CrateFetcher = fn(&str) -> TokenStream;
11
12/// Helper for converting a type to a literal representation.
13pub trait Literal: fmt::Debug + Any {
14    /// Generate output representing this literal as a stream of tokens. Takes a function
15    /// that can be used to look up crate names.
16    fn constant_value(&self, crates: CrateFetcher) -> TokenStream;
17}
18
19impl Literal for u8 {
20    fn constant_value(&self, _crates: CrateFetcher) -> TokenStream {
21        proc_macro2::Literal::u8_suffixed(*self).into_token_stream()
22    }
23}
24
25impl Literal for u16 {
26    fn constant_value(&self, _crates: CrateFetcher) -> TokenStream {
27        proc_macro2::Literal::u16_suffixed(*self).into_token_stream()
28    }
29}
30
31impl Literal for usize {
32    fn constant_value(&self, _crates: CrateFetcher) -> TokenStream {
33        proc_macro2::Literal::usize_suffixed(*self).into_token_stream()
34    }
35}
36
37impl Literal for bool {
38    fn constant_value(&self, _crates: CrateFetcher) -> TokenStream {
39        if *self {
40            quote! { true }
41        } else {
42            quote! { false }
43        }
44    }
45}
46
47/// A literal with object-safe clone-ability.
48pub struct DynLiteral {
49    literal: Box<dyn Literal>,
50    clone: fn(&dyn Literal) -> Box<dyn Literal>,
51}
52
53impl DynLiteral {
54    pub fn new<L: Literal + Clone>(val: L) -> Self {
55        Self {
56            literal: Box::new(val),
57            clone: Self::dyn_clone::<L>,
58        }
59    }
60
61    // Helper function to clone the contained value. Safety relies on DynLiteral always
62    // using the dyn_clone type corresponding to the real contained type.
63    fn dyn_clone<L: Literal + Clone>(val: &dyn Literal) -> Box<dyn Literal> {
64        assert!(TypeId::of::<L>() == val.type_id(), "Wrong type");
65        // Change the pointer type back to type L. This is safe because we verified the
66        // TypeId.
67        let val = val as *const dyn Literal as *const L;
68        // Deref the pointer to get a regular reference. This is safe because we got this
69        // pointer from a & reference to the value, so it is definitely non-null, and we
70        // verified the TypeId.
71        let val = unsafe { &*val };
72        Box::new(val.clone())
73    }
74}
75
76impl Clone for DynLiteral {
77    fn clone(&self) -> Self {
78        let cloned = (self.clone)(self.literal.as_ref());
79        Self {
80            literal: cloned,
81            clone: self.clone,
82        }
83    }
84}
85
86impl fmt::Debug for DynLiteral {
87    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88        self.literal.fmt(f)
89    }
90}
91
92impl Literal for DynLiteral {
93    fn constant_value(&self, crates: CrateFetcher) -> TokenStream {
94        self.literal.constant_value(crates)
95    }
96}
97
98/// An argument to a microcode operator.
99#[derive(Debug, Clone)]
100pub enum Arg<StackT> {
101    /// A value popped off of the stack.
102    StackValue(StackT),
103    /// A literal value.
104    Literal(DynLiteral),
105}