mirror of
https://github.com/scratchfoundation/scratch-tech-explorations.git
synced 2025-07-30 15:48:56 -04:00
feat: parse scripts structurally, change Block to enum of block shapes
This commit is contained in:
parent
15b2fb6084
commit
bbd9cce395
1 changed files with 199 additions and 21 deletions
220
src/sb2/mod.rs
220
src/sb2/mod.rs
|
@ -213,61 +213,239 @@ pub struct Info {
|
|||
pub struct TopLevelScript {
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
pub script: Script,
|
||||
pub script: Vec<Block>,
|
||||
}
|
||||
|
||||
type Script = Vec<Block>;
|
||||
#[derive(Debug)]
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Block {
|
||||
DefineProcedure(DefineProcedure),
|
||||
EBlock(EBlock),
|
||||
CBlock(CBlock),
|
||||
BasicBlock(BasicBlock),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Block {
|
||||
pub struct BasicBlock {
|
||||
pub opcode: String,
|
||||
pub args: Vec<BlockArgument>,
|
||||
}
|
||||
type BlockArgument = serde_json::Value;
|
||||
|
||||
/*
|
||||
#[derive(Debug)]
|
||||
pub struct CBlock {
|
||||
pub opcode: String,
|
||||
pub args: Vec<BlockArgument>,
|
||||
pub branch: Vec<Block>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EBlock {
|
||||
pub opcode: String,
|
||||
pub args: Vec<BlockArgument>,
|
||||
pub branch0: Vec<Block>,
|
||||
pub branch1: Vec<Block>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct DefineProcedure {
|
||||
pub opcode: String, // always "procDef"
|
||||
pub spec: String,
|
||||
pub parameter_names: Vec<String>,
|
||||
pub default_arg_values: Vec<LiteralValue>,
|
||||
pub run_without_screen_refresh: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum BlockArgument {
|
||||
Literal(serde_json::Value),
|
||||
Boolean(bool),
|
||||
Number(f64),
|
||||
String(String),
|
||||
Reporter(BasicBlock),
|
||||
}
|
||||
*/
|
||||
|
||||
impl<'de> Deserialize<'de> for Block {
|
||||
#[derive(Debug)]
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum LiteralValue {
|
||||
Boolean(bool),
|
||||
Number(f64),
|
||||
String(String),
|
||||
}
|
||||
|
||||
impl From<&str> for BlockArgument {
|
||||
fn from(value: &str) -> Self {
|
||||
BlockArgument::String(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for EBlock {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where D: serde::Deserializer<'de>,
|
||||
{
|
||||
struct BlockVisitor;
|
||||
struct EBlockVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for BlockVisitor {
|
||||
type Value = Block;
|
||||
impl<'de> Visitor<'de> for EBlockVisitor {
|
||||
type Value = EBlock;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("an array containing an opcode optionally followed by inputs")
|
||||
formatter.write_str("a script block with two branches (an 'E block')")
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
where A: serde::de::SeqAccess<'de>,
|
||||
{
|
||||
let opcode = seq.next_element()?.ok_or_else(|| {
|
||||
de::Error::custom("could not read opcode for script block")
|
||||
})?;
|
||||
let opcode = seq.next_element()?.ok_or_else(||
|
||||
de::Error::custom("could not read opcode for E block")
|
||||
)?;
|
||||
|
||||
let mut args = Vec::<BlockArgument>::with_capacity(seq.size_hint().unwrap_or(0));
|
||||
while let Some(arg) = seq.next_element::<serde_json::Value>()? {
|
||||
let mut args = vec![];
|
||||
let mut branches: [serde_json::Value; 2] = [
|
||||
seq.next_element()?.ok_or_else(||
|
||||
de::Error::custom("could not find first branch for E block")
|
||||
)?,
|
||||
seq.next_element()?.ok_or_else(||
|
||||
de::Error::custom("could not find second branch for E block")
|
||||
)?,
|
||||
];
|
||||
while let Some(next_element) = seq.next_element::<serde_json::Value>()? {
|
||||
let arg = serde_json::from_value::<BlockArgument>(branches[0].take()).map_err(|_|
|
||||
de::Error::custom("could not interpret argument for E block")
|
||||
)?;
|
||||
args.push(arg);
|
||||
branches[0] = branches[1].take();
|
||||
branches[1] = next_element;
|
||||
}
|
||||
|
||||
Ok(Block { opcode, args })
|
||||
Ok(EBlock {
|
||||
opcode,
|
||||
args,
|
||||
branch0: serde_json::from_value(branches[0].take()).map_err(|_|
|
||||
de::Error::custom("could not interpret first branch for E block")
|
||||
)?,
|
||||
branch1: serde_json::from_value(branches[1].take()).map_err(|_|
|
||||
de::Error::custom("could not interpret second branch for E block")
|
||||
)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_seq(BlockVisitor)
|
||||
deserializer.deserialize_seq(EBlockVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Block {
|
||||
impl Serialize for EBlock {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: serde::Serializer
|
||||
{
|
||||
let mut state = serializer.serialize_seq(Some(3 + self.args.len()))?;
|
||||
state.serialize_element(&self.opcode)?;
|
||||
for arg in &self.args {
|
||||
state.serialize_element(arg)?;
|
||||
}
|
||||
state.serialize_element(&self.branch0)?;
|
||||
state.serialize_element(&self.branch1)?;
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for CBlock {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where D: serde::Deserializer<'de>,
|
||||
{
|
||||
struct CBlockVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for CBlockVisitor {
|
||||
type Value = CBlock;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("a script block with one branch (a 'C block')")
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
where A: serde::de::SeqAccess<'de>,
|
||||
{
|
||||
let opcode = seq.next_element()?.ok_or_else(||
|
||||
de::Error::custom("could not read opcode for C block")
|
||||
)?;
|
||||
|
||||
let mut args = vec![];
|
||||
let mut branch = seq.next_element()?.ok_or_else(||
|
||||
de::Error::custom("could not find branch for C block")
|
||||
)?;
|
||||
while let Some(next_element) = seq.next_element::<serde_json::Value>()? {
|
||||
let arg = serde_json::from_value::<BlockArgument>(branch).map_err(|_|
|
||||
de::Error::custom("could not interpret argument for C block")
|
||||
)?;
|
||||
args.push(arg);
|
||||
branch = next_element;
|
||||
}
|
||||
Ok(CBlock {
|
||||
opcode,
|
||||
args,
|
||||
branch: serde_json::from_value(branch).map_err(|_|
|
||||
de::Error::custom("could not interpret first branch for C block")
|
||||
)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_seq(CBlockVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for CBlock {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: serde::Serializer
|
||||
{
|
||||
let mut state = serializer.serialize_seq(Some(2 + self.args.len()))?;
|
||||
state.serialize_element(&self.opcode)?;
|
||||
for arg in &self.args {
|
||||
state.serialize_element(arg)?;
|
||||
}
|
||||
state.serialize_element(&self.branch)?;
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for BasicBlock {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where D: serde::Deserializer<'de>,
|
||||
{
|
||||
struct BasicBlockVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for BasicBlockVisitor {
|
||||
type Value = BasicBlock;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("a script block with no branches")
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
where A: serde::de::SeqAccess<'de>,
|
||||
{
|
||||
let opcode = seq.next_element()?.ok_or_else(||
|
||||
de::Error::custom("could not read opcode for block")
|
||||
)?;
|
||||
|
||||
let mut args = vec![];
|
||||
while let Some(next_argument) = seq.next_element()? {
|
||||
args.push(next_argument);
|
||||
}
|
||||
Ok(BasicBlock {
|
||||
opcode,
|
||||
args,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_seq(BasicBlockVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for BasicBlock {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: serde::Serializer
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue