Implement jsx instruction

This commit is contained in:
Andrew Morris
2024-02-29 17:54:11 +11:00
parent 190591defa
commit 30c5a52de7
4 changed files with 218 additions and 5 deletions

View File

@@ -7,6 +7,7 @@ use crate::builtins::type_error_builtin::ToTypeError;
use crate::bytecode_decoder::BytecodeDecoder;
use crate::bytecode_decoder::BytecodeType;
use crate::cat_stack_frame::CatStackFrame;
use crate::jsx_element::JsxElement;
use crate::native_function::ThisWrapper;
use crate::operations;
use crate::operations::op_delete;
@@ -14,6 +15,7 @@ use crate::stack_frame::FrameStepOk;
use crate::stack_frame::FrameStepResult;
use crate::stack_frame::{CallResult, StackFrame, StackFrameTrait};
use crate::vs_object::VsObject;
use crate::vs_value::ToDynamicVal;
use crate::vs_value::ToVal;
use crate::vs_value::{LoadFunctionResult, Val, ValTrait};
@@ -626,7 +628,60 @@ impl StackFrameTrait for BytecodeStackFrame {
return Ok(FrameStepOk::Continue);
}
Jsx => todo!(),
Jsx => {
let tag = self.decoder.decode_val(&mut self.registers);
let attrs_val = self.decoder.decode_val(&mut self.registers);
let children_val = self.decoder.decode_val(&mut self.registers);
let mut attrs = Vec::<(String, Val)>::new();
match attrs_val {
Val::Array(array) => {
for attr in &array.elements {
match attr {
Val::Array(array) => {
if array.elements.len() != 2 {
return Err("Unexpected non-pair attribute".to_type_error());
}
let name = match &array.elements[0] {
Val::String(str) => str.to_string(),
_ => return Err("Unexpected non-string attribute name".to_type_error()),
};
let value = array.elements[1].clone();
attrs.push((name, value));
}
_ => return Err("Unexpected non-array attrs".to_type_error()),
}
}
}
_ => return Err("Unexpected non-array attrs".to_type_error()),
}
let children = match children_val {
Val::Array(array) => array.elements.clone(),
_ => return Err("Unexpected non-array children".to_type_error()),
};
let res = JsxElement {
tag: match tag {
Val::Void => None,
Val::String(str) => Some(str.to_string()),
_ => return Err("Unexpected non-string tag".to_type_error()),
},
attrs,
children,
}
.to_dynamic_val();
if let Some(i) = self.decoder.decode_register_index() {
self.registers[i] = res;
}
return Ok(FrameStepOk::Continue);
}
};
Ok(FrameStepOk::Continue)

View File

@@ -0,0 +1,160 @@
use std::{fmt, rc::Rc};
use num_bigint::BigInt;
use crate::{
builtins::type_error_builtin::ToTypeError,
vs_array::VsArray,
vs_class::VsClass,
vs_value::{Val, VsType},
LoadFunctionResult, ValTrait,
};
#[derive(Clone)]
pub struct JsxElement {
pub tag: Option<String>,
pub attrs: Vec<(String, Val)>,
pub children: Vec<Val>,
}
impl ValTrait for JsxElement {
fn typeof_(&self) -> VsType {
VsType::Object
}
fn to_number(&self) -> f64 {
f64::NAN
}
fn to_index(&self) -> Option<usize> {
None
}
fn is_primitive(&self) -> bool {
false
}
fn is_truthy(&self) -> bool {
true
}
fn is_nullish(&self) -> bool {
false
}
fn bind(&self, _params: Vec<Val>) -> Option<Val> {
None
}
fn as_bigint_data(&self) -> Option<BigInt> {
None
}
fn as_array_data(&self) -> Option<Rc<VsArray>> {
None
}
fn as_class_data(&self) -> Option<Rc<VsClass>> {
None
}
fn load_function(&self) -> LoadFunctionResult {
LoadFunctionResult::NotAFunction
}
fn sub(&self, _key: &Val) -> Result<Val, Val> {
Ok(Val::Undefined)
}
fn has(&self, _key: &Val) -> Option<bool> {
Some(false)
}
fn submov(&mut self, _key: &Val, _value: Val) -> Result<(), Val> {
Err("Cannot assign to subscript of jsx element".to_type_error())
}
fn pretty_fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.tag.is_none() && self.children.is_empty() {
return write!(f, "\x1b[36m<></>\x1b[39m");
}
let tag_str = match &self.tag {
Some(str) => str.clone(),
None => "".to_owned(),
};
if self.children.is_empty() {
write!(f, "\x1b[36m<{}\x1b[39m", tag_str)?;
write_attributes_pretty(f, &self.attrs)?;
write!(f, " \x1b[36m/>\x1b[39m")
} else {
write!(f, "\x1b[36m<{}\x1b[39m", tag_str)?;
write_attributes_pretty(f, &self.attrs)?;
write!(f, "\x1b[36m>\x1b[39m")?;
for child in &self.children {
if let Some(jsx_child) = try_to_jsx_element(child) {
jsx_child.pretty_fmt(f)?;
} else {
write!(f, "{}", child)?;
}
}
write!(f, "\x1b[36m</{}>\x1b[39m", tag_str)
}
}
fn codify(&self) -> String {
self.to_string()
}
}
impl fmt::Display for JsxElement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let tag_str = match &self.tag {
Some(str) => str.clone(),
None => "".to_owned(),
};
if self.children.is_empty() {
write!(f, "<{}", tag_str)?;
write_attributes(f, &self.attrs)?;
write!(f, " />")
} else {
write!(f, "<{}", tag_str)?;
write_attributes(f, &self.attrs)?;
write!(f, ">")?;
for child in &self.children {
write!(f, "{}", child)?;
}
write!(f, "</{}>", tag_str)
}
}
}
fn write_attributes(f: &mut fmt::Formatter<'_>, attrs: &Vec<(String, Val)>) -> fmt::Result {
for (key, val) in attrs {
write!(f, " {}=\x1b[33m{}\x1b[39m", key, val)?;
}
Ok(())
}
fn write_attributes_pretty(f: &mut fmt::Formatter<'_>, attrs: &Vec<(String, Val)>) -> fmt::Result {
for (key, val) in attrs {
write!(f, " {}=\x1b[33m{}\x1b[39m", key, val)?;
}
Ok(())
}
pub fn try_to_jsx_element(val: &Val) -> Option<Rc<JsxElement>> {
match val {
Val::Dynamic(dynamic) => dynamic.as_any().downcast_ref().cloned(),
Val::StoragePtr(ptr) => try_to_jsx_element(&ptr.get()),
_ => None,
}
}

View File

@@ -11,6 +11,7 @@ mod first_stack_frame;
mod generator;
mod helpers;
mod iteration;
mod jsx_element;
mod make_generator_frame;
mod native_frame_function;
mod native_function;

View File

@@ -54,10 +54,7 @@ pub fn format_from_path(file_path: &str) -> RunFormat {
.unwrap_or("");
match ext {
"ts" => RunFormat::TypeScript,
"mts" => RunFormat::TypeScript,
"js" => RunFormat::TypeScript,
"mjs" => RunFormat::TypeScript,
"ts" | "tsx" | "mts" | "js" | "jsx" | "mjs" => RunFormat::TypeScript,
"vsm" => RunFormat::Assembly,
"vsb" => RunFormat::Bytecode,
_ => std::panic!("Unrecognized file extension \"{}\"", ext),