mirror of
https://github.com/scratchfoundation/scratch-tech-explorations.git
synced 2025-08-28 21:38:45 -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 struct TopLevelScript {
|
||||||
pub x: f64,
|
pub x: f64,
|
||||||
pub y: 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)]
|
#[derive(Debug)]
|
||||||
pub struct Block {
|
pub struct BasicBlock {
|
||||||
pub opcode: String,
|
pub opcode: String,
|
||||||
pub args: Vec<BlockArgument>,
|
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(Debug)]
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum BlockArgument {
|
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>
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
where D: serde::Deserializer<'de>,
|
where D: serde::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
struct BlockVisitor;
|
struct EBlockVisitor;
|
||||||
|
|
||||||
impl<'de> Visitor<'de> for BlockVisitor {
|
impl<'de> Visitor<'de> for EBlockVisitor {
|
||||||
type Value = Block;
|
type Value = EBlock;
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
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>
|
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||||
where A: serde::de::SeqAccess<'de>,
|
where A: serde::de::SeqAccess<'de>,
|
||||||
{
|
{
|
||||||
let opcode = seq.next_element()?.ok_or_else(|| {
|
let opcode = seq.next_element()?.ok_or_else(||
|
||||||
de::Error::custom("could not read opcode for script block")
|
de::Error::custom("could not read opcode for E block")
|
||||||
})?;
|
)?;
|
||||||
|
|
||||||
let mut args = Vec::<BlockArgument>::with_capacity(seq.size_hint().unwrap_or(0));
|
let mut args = vec![];
|
||||||
while let Some(arg) = seq.next_element::<serde_json::Value>()? {
|
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);
|
args.push(arg);
|
||||||
|
branches[0] = branches[1].take();
|
||||||
|
branches[1] = next_element;
|
||||||
}
|
}
|
||||||
|
Ok(EBlock {
|
||||||
Ok(Block { opcode, args })
|
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>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where S: serde::Serializer
|
where S: serde::Serializer
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue