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)
|
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 (kbd/load-font font-path) #f)
|
||||||
|
|
||||||
(define-syntax self (syntax-rules () ((self func arg ...) (kbd/call kbd/this 'func arg ...))))
|
(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
|
(define-syntax kbd/defmember
|
||||||
(syntax-rules ()
|
(syntax-rules ()
|
||||||
|
|
|
@ -24,10 +24,25 @@ pub struct PopupInfo<'b> {
|
||||||
font_db: &'b mut FontDb,
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Class<E: Engine> {
|
pub struct Class<E: Engine> {
|
||||||
pub supers: Vec<String>,
|
pub supers: Vec<String>,
|
||||||
pub methods: HashMap<String, E::Func>,
|
pub methods: HashMap<String, Method<E>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> Default for Class<E> {
|
impl<E: Engine> Default for Class<E> {
|
||||||
|
@ -219,7 +234,7 @@ impl<E: Engine> Keyboard<E> {
|
||||||
.classes
|
.classes
|
||||||
.get_mut("en")
|
.get_mut("en")
|
||||||
.and_then(|cls| cls.methods.get_mut("kbd/width-changed"))
|
.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();
|
func.call(self, vec![Value::Symbol("en".into())]).unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
graphics, image,
|
graphics, image,
|
||||||
|
keyboard::Method,
|
||||||
script::{Args, Engine, ErrorTrait, Func, Value},
|
script::{Args, Engine, ErrorTrait, Func, Value},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -35,11 +38,14 @@ fn register<E: Engine>(
|
||||||
};
|
};
|
||||||
func.add_ref(vm);
|
func.add_ref(vm);
|
||||||
println!("register {cls:?} {name:?} {func:?}");
|
println!("register {cls:?} {name:?} {func:?}");
|
||||||
vm.classes
|
let meth = vm
|
||||||
|
.classes
|
||||||
.entry(cls.into_owned())
|
.entry(cls.into_owned())
|
||||||
.or_default()
|
.or_default()
|
||||||
.methods
|
.methods
|
||||||
.insert(name.into_owned(), func);
|
.entry(name.into_owned())
|
||||||
|
.or_default();
|
||||||
|
meth.func = Some(func);
|
||||||
println!("done");
|
println!("done");
|
||||||
|
|
||||||
Ok(Value::Null)
|
Ok(Value::Null)
|
||||||
|
@ -86,15 +92,36 @@ fn class_define<E: Engine>(
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
println!("register {cls:?} {supers:?}");
|
println!("register {cls:?} {supers:?}");
|
||||||
|
|
||||||
vm.classes
|
let mut meths = HashMap::<_, Method<_>>::new();
|
||||||
.entry(cls.into_owned())
|
// TODO: multiple inheritance ideally needs bfs rather than dfs
|
||||||
.or_default()
|
// and resolving diamonds requires deduplicating bases, and ideally toposorting them
|
||||||
.supers
|
// this is fine for when a single method has only a single super tho
|
||||||
.extend(supers);
|
for sup in &supers {
|
||||||
|
if let Some(sup) = vm.classes.get(sup) {
|
||||||
|
for (name, meth) in &sup.methods {
|
||||||
|
meths
|
||||||
|
.entry(name.clone())
|
||||||
|
.or_default()
|
||||||
|
.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");
|
println!("done");
|
||||||
Ok(Value::Null)
|
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>(
|
fn call<E: Engine>(
|
||||||
vm: &mut Keyboard<E>,
|
vm: &mut Keyboard<E>,
|
||||||
mut args: Args<Keyboard<E>>,
|
mut args: Args<Keyboard<E>>,
|
||||||
|
@ -106,12 +133,27 @@ fn call<E: Engine>(
|
||||||
let name = args
|
let name = args
|
||||||
.pop()
|
.pop()
|
||||||
.ok_or_else(|| ErrorTrait::invalid_argc("kbd/call"))?;
|
.ok_or_else(|| ErrorTrait::invalid_argc("kbd/call"))?;
|
||||||
let Value::Symbol(cls) = cls else {
|
let (cls_s, func_idx) = match &cls {
|
||||||
return Err(ErrorTrait::invalid_argt(
|
Value::Symbol(cls) => (cls, None),
|
||||||
"kbd/call",
|
Value::RevArray(cls1) => match <&[_; 3]>::try_from(&cls1[..]) {
|
||||||
"symbol",
|
Ok([Value::Int(idx), Value::Symbol(super_name), Value::Symbol(cls)]) => {
|
||||||
&format!("{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 {
|
let Value::Symbol(name) = name else {
|
||||||
return Err(ErrorTrait::invalid_argt(
|
return Err(ErrorTrait::invalid_argt(
|
||||||
|
@ -120,72 +162,79 @@ fn call<E: Engine>(
|
||||||
&format!("{name:?}"),
|
&format!("{name:?}"),
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
fn call_func<E: Engine>(
|
let is_super = matches!(func_idx, Some((x, _)) if x == &name);
|
||||||
func: &mut E::Func,
|
|
||||||
engine: &mut E,
|
|
||||||
args: Vec<Value<E>>,
|
|
||||||
) -> Result<Value<E>, E::Error> {
|
|
||||||
func.call(engine, args)
|
|
||||||
}
|
|
||||||
let func = vm
|
let func = vm
|
||||||
.classes
|
.classes
|
||||||
.get_mut(cls.as_ref())
|
.get_mut(cls_s.as_ref())
|
||||||
.and_then(|cls| cls.methods.get_mut(name.as_ref()).cloned());
|
.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 {
|
if let Some(mut func) = func {
|
||||||
args.push(Value::Symbol(cls));
|
args.push(cls);
|
||||||
args.reverse();
|
args.reverse();
|
||||||
call_func(&mut func, vm, args)
|
call_func(&mut func, vm, args)
|
||||||
} else {
|
} else {
|
||||||
Err(ErrorTrait::from_str(
|
Err(ErrorTrait::from_str(
|
||||||
"kbd/call",
|
"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>(
|
fn super_fn<E: Engine>(
|
||||||
vm: &mut Keyboard<E>,
|
_vm: &mut Keyboard<E>,
|
||||||
args: Args<Keyboard<E>>,
|
args: Args<Keyboard<E>>,
|
||||||
) -> Result<Value<Keyboard<E>>, <Keyboard<E> as Engine>::Error> {
|
) -> Result<Value<Keyboard<E>>, <Keyboard<E> as Engine>::Error> {
|
||||||
let Ok::<[Value<_>; 2], _>([k, mut v]) = args.try_into() else {
|
let Ok::<[Value<_>; 2], _>([cls, name]) = args.try_into() else {
|
||||||
return Err(ErrorTrait::invalid_argc("kbd/set-user-data"));
|
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/super",
|
||||||
|
"symbol",
|
||||||
|
&format!("{cls:?}"),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let Value::Symbol(name) = name else {
|
||||||
return Err(ErrorTrait::invalid_argt(
|
return Err(ErrorTrait::invalid_argt(
|
||||||
"kbd/set-user-data",
|
"kbd/super",
|
||||||
"symbol",
|
"symbol",
|
||||||
&format!("{k:?}"),
|
&format!("{name:?}"),
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
println!("setting user data {k:?} to {v:?}");
|
Ok(match func_idx {
|
||||||
if let Some(mut val) = vm.user_data.insert(k.into_owned(), v.clone()) {
|
Some((func, idx)) if func == name => Value::RevArray(vec![
|
||||||
// FIXME: theoretically this can unref data that's referenced elsewhere
|
Value::Int(idx + 1),
|
||||||
val.unref(vm);
|
Value::Symbol(func),
|
||||||
}
|
Value::Symbol(cls),
|
||||||
v.add_ref(vm);
|
]),
|
||||||
Ok(Value::Null)
|
_ => Value::RevArray(vec![Value::Int(0), Value::Symbol(name), Value::Symbol(cls)]),
|
||||||
}
|
})
|
||||||
|
|
||||||
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)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_to_i64<E: Engine>(name: &str, a: Value<E>, b: Value<E>) -> Result<i64, E::Error> {
|
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/register", register);
|
||||||
engine.register_func("kbd/class-define", class_define);
|
engine.register_func("kbd/class-define", class_define);
|
||||||
engine.register_func("kbd/call", call);
|
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_func("kbd/make-text", make_text);
|
||||||
engine.register(":round-corners", Value::new_symbol(":round-corners"));
|
engine.register(":round-corners", Value::new_symbol(":round-corners"));
|
||||||
engine.register_func("kbd/make-rect", make_rect);
|
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/width", width);
|
||||||
engine.register_func("kbd/load-font", load_font);
|
engine.register_func("kbd/load-font", load_font);
|
||||||
engine.register_func("kbd/set-layout", set_layout);
|
engine.register_func("kbd/set-layout", set_layout);
|
||||||
|
engine.register_func("kbd/super", super_fn);
|
||||||
engine.gc();
|
engine.gc();
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,6 +104,9 @@ impl<E: Engine> Value<E> {
|
||||||
}
|
}
|
||||||
pub fn add_car(&mut self, car: Self) {
|
pub fn add_car(&mut self, car: Self) {
|
||||||
match self {
|
match self {
|
||||||
|
Self::Null => {
|
||||||
|
*self = Self::RevArray(vec![car]);
|
||||||
|
}
|
||||||
Self::RevArray(ref mut x) => {
|
Self::RevArray(ref mut x) => {
|
||||||
x.push(car);
|
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/load-font "/home/user/.nix-profile/share/fonts/noto/NotoSans[wdth,wght].ttf")
|
||||||
|
|
||||||
(kbd/defclass en ()
|
(define state #f)
|
||||||
((init) (kbd/set-user-data 'state 'lower))
|
|
||||||
((state) (display (or (kbd/get-user-data 'state) 'lower)) (or (kbd/get-user-data 'state) 'lower))
|
(kbd/defclass en (latin)
|
||||||
|
((init) (set! state 'lower))
|
||||||
|
((state) (or 'state 'lower))
|
||||||
((show-upper) (or (symbol=? (self state) 'upper) (symbol=? (self state) 'capslock)))
|
((show-upper) (or (symbol=? (self state) 'upper) (symbol=? (self state) 'capslock)))
|
||||||
((show-lower) (symbol=? (self state) 'lower))
|
((show-lower) (symbol=? (self state) 'lower))
|
||||||
((show-symbols) (symbol=? (self state) 'symbols))
|
((show-symbols) (symbol=? (self state) 'symbols))
|
||||||
|
@ -87,5 +89,4 @@
|
||||||
; signals
|
; signals
|
||||||
|
|
||||||
((kbd/width-changed)
|
((kbd/width-changed)
|
||||||
(display "a !!!")
|
|
||||||
(kbd/set-layout (self kbd-layout))))
|
(kbd/set-layout (self kbd-layout))))
|
||||||
|
|
Loading…
Reference in a new issue