implement basic inheritance
This commit is contained in:
parent
dfcc06fb31
commit
318abf6c6d
|
@ -23,3 +23,12 @@ popup.setLayout(layout: Layout | null)
|
|||
|
||||
keyboard.setLayout(layout: Layout | null)
|
||||
|
||||
|
||||
TODO: multiple inheritance, diamond problem
|
||||
(it's technically allowed but the semantics aren't very good)
|
||||
|
||||
MAYBE PLANNED: associated data (currently classes instances are
|
||||
singletons, which really simplifies ffi)
|
||||
|
||||
NOT PLANNED: dynamic classes (i.e. updating classes after their
|
||||
definition). You can probably hack something up if you really need it.
|
||||
|
|
2
defs.scm
2
defs.scm
|
@ -10,7 +10,7 @@
|
|||
; (define (kbd/load-font font-path) #f)
|
||||
|
||||
(define-syntax self (syntax-rules () ((self func arg ...) (kbd/call kbd/this 'func arg ...))))
|
||||
(define-syntax super (syntax-rules () ((super func arg ...) (kbd/call (kbd/super kbd/this) 'func arg ...))))
|
||||
(define-syntax super (syntax-rules () ((super func arg ...) (kbd/call (kbd/super kbd/this 'func) 'func arg ...))))
|
||||
|
||||
(define-syntax kbd/defmember
|
||||
(syntax-rules ()
|
||||
|
|
|
@ -24,10 +24,25 @@ pub struct PopupInfo<'b> {
|
|||
font_db: &'b mut FontDb,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Method<E: Engine> {
|
||||
func: Option<E::Func>,
|
||||
super_funcs: Vec<E::Func>,
|
||||
}
|
||||
|
||||
impl<E: Engine> Default for Method<E> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
func: None,
|
||||
super_funcs: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Class<E: Engine> {
|
||||
pub supers: Vec<String>,
|
||||
pub methods: HashMap<String, E::Func>,
|
||||
pub methods: HashMap<String, Method<E>>,
|
||||
}
|
||||
|
||||
impl<E: Engine> Default for Class<E> {
|
||||
|
@ -219,7 +234,7 @@ impl<E: Engine> Keyboard<E> {
|
|||
.classes
|
||||
.get_mut("en")
|
||||
.and_then(|cls| cls.methods.get_mut("kbd/width-changed"))
|
||||
.cloned()
|
||||
.and_then(|x| x.func.clone())
|
||||
{
|
||||
func.call(self, vec![Value::Symbol("en".into())]).unwrap();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
graphics, image,
|
||||
keyboard::Method,
|
||||
script::{Args, Engine, ErrorTrait, Func, Value},
|
||||
};
|
||||
|
||||
|
@ -35,11 +38,14 @@ fn register<E: Engine>(
|
|||
};
|
||||
func.add_ref(vm);
|
||||
println!("register {cls:?} {name:?} {func:?}");
|
||||
vm.classes
|
||||
let meth = vm
|
||||
.classes
|
||||
.entry(cls.into_owned())
|
||||
.or_default()
|
||||
.methods
|
||||
.insert(name.into_owned(), func);
|
||||
.entry(name.into_owned())
|
||||
.or_default();
|
||||
meth.func = Some(func);
|
||||
println!("done");
|
||||
|
||||
Ok(Value::Null)
|
||||
|
@ -86,15 +92,36 @@ fn class_define<E: Engine>(
|
|||
.collect::<Result<Vec<_>, _>>()?;
|
||||
println!("register {cls:?} {supers:?}");
|
||||
|
||||
vm.classes
|
||||
.entry(cls.into_owned())
|
||||
let mut meths = HashMap::<_, Method<_>>::new();
|
||||
// TODO: multiple inheritance ideally needs bfs rather than dfs
|
||||
// and resolving diamonds requires deduplicating bases, and ideally toposorting them
|
||||
// this is fine for when a single method has only a single super tho
|
||||
for sup in &supers {
|
||||
if let Some(sup) = vm.classes.get(sup) {
|
||||
for (name, meth) in &sup.methods {
|
||||
meths
|
||||
.entry(name.clone())
|
||||
.or_default()
|
||||
.supers
|
||||
.extend(supers);
|
||||
.super_funcs
|
||||
.extend(meth.super_funcs.iter().chain(meth.func.iter()).cloned());
|
||||
}
|
||||
}
|
||||
}
|
||||
let entry = vm.classes.entry(cls.into_owned()).or_default();
|
||||
entry.supers.extend(supers);
|
||||
entry.methods.extend(meths);
|
||||
println!("done");
|
||||
Ok(Value::Null)
|
||||
}
|
||||
|
||||
fn call_func<E: Engine>(
|
||||
func: &mut E::Func,
|
||||
engine: &mut E,
|
||||
args: Vec<Value<E>>,
|
||||
) -> Result<Value<E>, E::Error> {
|
||||
func.call(engine, args)
|
||||
}
|
||||
|
||||
fn call<E: Engine>(
|
||||
vm: &mut Keyboard<E>,
|
||||
mut args: Args<Keyboard<E>>,
|
||||
|
@ -106,12 +133,27 @@ fn call<E: Engine>(
|
|||
let name = args
|
||||
.pop()
|
||||
.ok_or_else(|| ErrorTrait::invalid_argc("kbd/call"))?;
|
||||
let Value::Symbol(cls) = cls else {
|
||||
let (cls_s, func_idx) = match &cls {
|
||||
Value::Symbol(cls) => (cls, None),
|
||||
Value::RevArray(cls1) => match <&[_; 3]>::try_from(&cls1[..]) {
|
||||
Ok([Value::Int(idx), Value::Symbol(super_name), Value::Symbol(cls)]) => {
|
||||
(cls, Some((super_name, idx)))
|
||||
}
|
||||
_ => {
|
||||
return Err(ErrorTrait::invalid_argt(
|
||||
"kbd/call",
|
||||
"symbol",
|
||||
&format!("{cls:?}"),
|
||||
));
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
return Err(ErrorTrait::invalid_argt(
|
||||
"kbd/call",
|
||||
"symbol",
|
||||
&format!("{cls:?}"),
|
||||
));
|
||||
}
|
||||
};
|
||||
let Value::Symbol(name) = name else {
|
||||
return Err(ErrorTrait::invalid_argt(
|
||||
|
@ -120,72 +162,79 @@ fn call<E: Engine>(
|
|||
&format!("{name:?}"),
|
||||
));
|
||||
};
|
||||
fn call_func<E: Engine>(
|
||||
func: &mut E::Func,
|
||||
engine: &mut E,
|
||||
args: Vec<Value<E>>,
|
||||
) -> Result<Value<E>, E::Error> {
|
||||
func.call(engine, args)
|
||||
}
|
||||
let is_super = matches!(func_idx, Some((x, _)) if x == &name);
|
||||
let func = vm
|
||||
.classes
|
||||
.get_mut(cls.as_ref())
|
||||
.and_then(|cls| cls.methods.get_mut(name.as_ref()).cloned());
|
||||
.get_mut(cls_s.as_ref())
|
||||
.and_then(|cls| cls.methods.get_mut(name.as_ref()))
|
||||
.and_then(|x| match func_idx {
|
||||
Some((_, idx)) if is_super => usize::try_from(*idx)
|
||||
.ok()
|
||||
.and_then(|idx| x.super_funcs.iter().rev().nth(idx))
|
||||
.cloned(),
|
||||
_ => x
|
||||
.func
|
||||
.iter()
|
||||
.chain(x.super_funcs.iter().rev())
|
||||
.next()
|
||||
.cloned(),
|
||||
});
|
||||
if let Some(mut func) = func {
|
||||
args.push(Value::Symbol(cls));
|
||||
args.push(cls);
|
||||
args.reverse();
|
||||
call_func(&mut func, vm, args)
|
||||
} else {
|
||||
Err(ErrorTrait::from_str(
|
||||
"kbd/call",
|
||||
&format!("{cls}/{name} is not defined"),
|
||||
&if is_super {
|
||||
format!("super for {cls_s}/{name} not found")
|
||||
} else {
|
||||
format!("{cls_s}/{name} is not defined")
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn set_user_data<E: Engine>(
|
||||
vm: &mut Keyboard<E>,
|
||||
fn super_fn<E: Engine>(
|
||||
_vm: &mut Keyboard<E>,
|
||||
args: Args<Keyboard<E>>,
|
||||
) -> Result<Value<Keyboard<E>>, <Keyboard<E> as Engine>::Error> {
|
||||
let Ok::<[Value<_>; 2], _>([k, mut v]) = args.try_into() else {
|
||||
return Err(ErrorTrait::invalid_argc("kbd/set-user-data"));
|
||||
let Ok::<[Value<_>; 2], _>([cls, name]) = args.try_into() else {
|
||||
return Err(ErrorTrait::invalid_argc("kbd/super"));
|
||||
};
|
||||
let Value::Symbol(k) = k else {
|
||||
let (cls, func_idx) = match cls {
|
||||
Value::Symbol(cls) => (cls, None),
|
||||
Value::RevArray(cls1) => match <[_; 3]>::try_from(cls1) {
|
||||
Ok([Value::Int(idx), Value::Symbol(super_name), Value::Symbol(cls)]) => {
|
||||
(cls, Some((super_name, idx)))
|
||||
}
|
||||
_ => {
|
||||
return Err(ErrorTrait::from_str("kbd/super", "invalid class"));
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
return Err(ErrorTrait::invalid_argt(
|
||||
"kbd/set-user-data",
|
||||
"kbd/super",
|
||||
"symbol",
|
||||
&format!("{k:?}"),
|
||||
&format!("{cls:?}"),
|
||||
));
|
||||
}
|
||||
};
|
||||
let Value::Symbol(name) = name else {
|
||||
return Err(ErrorTrait::invalid_argt(
|
||||
"kbd/super",
|
||||
"symbol",
|
||||
&format!("{name:?}"),
|
||||
));
|
||||
};
|
||||
println!("setting user data {k:?} to {v:?}");
|
||||
if let Some(mut val) = vm.user_data.insert(k.into_owned(), v.clone()) {
|
||||
// FIXME: theoretically this can unref data that's referenced elsewhere
|
||||
val.unref(vm);
|
||||
}
|
||||
v.add_ref(vm);
|
||||
Ok(Value::Null)
|
||||
}
|
||||
|
||||
fn get_user_data<E: Engine>(
|
||||
vm: &mut Keyboard<E>,
|
||||
args: Args<Keyboard<E>>,
|
||||
) -> Result<Value<Keyboard<E>>, <Keyboard<E> as Engine>::Error> {
|
||||
let Ok::<[Value<_>; 1], _>([k]) = args.try_into() else {
|
||||
return Err(ErrorTrait::invalid_argc("kbd/get-user-data"));
|
||||
};
|
||||
let Value::Symbol(k) = k else {
|
||||
return Err(ErrorTrait::invalid_argt(
|
||||
"kbd/get-user-data",
|
||||
"symbol",
|
||||
&format!("{k:?}"),
|
||||
));
|
||||
};
|
||||
println!("getting user data {k:?}");
|
||||
Ok(vm
|
||||
.user_data
|
||||
.get(k.as_ref())
|
||||
.cloned()
|
||||
.unwrap_or(Value::Bool(false)))
|
||||
Ok(match func_idx {
|
||||
Some((func, idx)) if func == name => Value::RevArray(vec![
|
||||
Value::Int(idx + 1),
|
||||
Value::Symbol(func),
|
||||
Value::Symbol(cls),
|
||||
]),
|
||||
_ => Value::RevArray(vec![Value::Int(0), Value::Symbol(name), Value::Symbol(cls)]),
|
||||
})
|
||||
}
|
||||
|
||||
fn add_to_i64<E: Engine>(name: &str, a: Value<E>, b: Value<E>) -> Result<i64, E::Error> {
|
||||
|
@ -456,8 +505,6 @@ pub fn init_builtins<E: 'static + Engine>(engine: &mut Keyboard<E>) {
|
|||
engine.register_func("kbd/register", register);
|
||||
engine.register_func("kbd/class-define", class_define);
|
||||
engine.register_func("kbd/call", call);
|
||||
engine.register_func("kbd/set-user-data", set_user_data);
|
||||
engine.register_func("kbd/get-user-data", get_user_data);
|
||||
engine.register_func("kbd/make-text", make_text);
|
||||
engine.register(":round-corners", Value::new_symbol(":round-corners"));
|
||||
engine.register_func("kbd/make-rect", make_rect);
|
||||
|
@ -465,5 +512,6 @@ pub fn init_builtins<E: 'static + Engine>(engine: &mut Keyboard<E>) {
|
|||
engine.register_func("kbd/width", width);
|
||||
engine.register_func("kbd/load-font", load_font);
|
||||
engine.register_func("kbd/set-layout", set_layout);
|
||||
engine.register_func("kbd/super", super_fn);
|
||||
engine.gc();
|
||||
}
|
||||
|
|
|
@ -104,6 +104,9 @@ impl<E: Engine> Value<E> {
|
|||
}
|
||||
pub fn add_car(&mut self, car: Self) {
|
||||
match self {
|
||||
Self::Null => {
|
||||
*self = Self::RevArray(vec![car]);
|
||||
}
|
||||
Self::RevArray(ref mut x) => {
|
||||
x.push(car);
|
||||
}
|
||||
|
|
9
tmp.scm
9
tmp.scm
|
@ -1,8 +1,10 @@
|
|||
(kbd/load-font "/home/user/.nix-profile/share/fonts/noto/NotoSans[wdth,wght].ttf")
|
||||
|
||||
(kbd/defclass en ()
|
||||
((init) (kbd/set-user-data 'state 'lower))
|
||||
((state) (display (or (kbd/get-user-data 'state) 'lower)) (or (kbd/get-user-data 'state) 'lower))
|
||||
(define state #f)
|
||||
|
||||
(kbd/defclass en (latin)
|
||||
((init) (set! state 'lower))
|
||||
((state) (or 'state 'lower))
|
||||
((show-upper) (or (symbol=? (self state) 'upper) (symbol=? (self state) 'capslock)))
|
||||
((show-lower) (symbol=? (self state) 'lower))
|
||||
((show-symbols) (symbol=? (self state) 'symbols))
|
||||
|
@ -87,5 +89,4 @@
|
|||
; signals
|
||||
|
||||
((kbd/width-changed)
|
||||
(display "a !!!")
|
||||
(kbd/set-layout (self kbd-layout))))
|
||||
|
|
Loading…
Reference in a new issue