Skip to main content

sim_codec_python/
codec.rs

1//! Loadable `codec/python` runtime object.
2
3use crate::{
4    PYTHON_CODEC_ID, decode_python, decode_python_located, decode_python_tree, encode_python,
5};
6use sim_codec::{
7    CodecDefaultDecode, CodecRuntime, DecodeBudget, Decoder, Encoder, Input, LocatedDecoder,
8    Output, ReadCx, TreeDecoder, TreeEncoder, codec_value, validate_expr_tree,
9};
10use sim_kernel::{
11    AbiVersion, Dependency, Error, Export, Expr, Lib, LibManifest, LibTarget, Linker, LocatedExpr,
12    LocatedExprTree, Result, Symbol, Version, WriteCx,
13};
14use std::sync::Arc;
15
16/// Runtime codec object for `codec/python`.
17#[derive(Default)]
18pub struct PythonCodec;
19
20impl Decoder for PythonCodec {
21    fn decode(&self, cx: &mut ReadCx<'_>, input: Input) -> Result<Expr> {
22        let source = input_text(cx.codec, input)?;
23        let mut budget = DecodeBudget::new(cx.limits);
24        decode_python(cx, &source, &mut budget)
25    }
26}
27impl LocatedDecoder for PythonCodec {
28    fn decode_located(
29        &self,
30        cx: &mut ReadCx<'_>,
31        input: Input,
32        source_id: String,
33    ) -> Result<LocatedExpr> {
34        decode_python_located(cx, source_id, input)
35    }
36}
37impl TreeDecoder for PythonCodec {
38    fn decode_tree(
39        &self,
40        cx: &mut ReadCx<'_>,
41        input: Input,
42        source_id: String,
43    ) -> Result<LocatedExprTree> {
44        decode_python_tree(cx, source_id, input)
45    }
46}
47impl Encoder for PythonCodec {
48    fn encode(&self, _cx: &mut WriteCx<'_>, expr: &Expr) -> Result<Output> {
49        encode_python(expr)
50    }
51}
52impl TreeEncoder for PythonCodec {
53    fn encode_tree(&self, cx: &mut WriteCx<'_>, tree: &LocatedExprTree) -> Result<Output> {
54        validate_expr_tree(cx.codec, tree)?;
55        if cx.options.lossless_origin
56            && let Some(origin) = &tree.origin
57            && let Some(bytes) = cx.cx.sources().slice(origin)
58        {
59            return Ok(Output::Text(
60                std::str::from_utf8(bytes)
61                    .map_err(|error| Error::CodecError {
62                        codec: cx.codec,
63                        message: format!("python source origin is not UTF-8: {error}"),
64                    })?
65                    .to_owned(),
66            ));
67        }
68        encode_python(&tree.expr)
69    }
70}
71
72/// Host-registered library installing `codec/python`.
73pub struct PythonCodecLib {
74    symbol: Symbol,
75    codec_id: sim_kernel::CodecId,
76}
77impl PythonCodecLib {
78    /// Construct with a host-assigned codec id.
79    pub fn new(codec_id: sim_kernel::CodecId) -> Self {
80        Self {
81            symbol: Symbol::qualified("codec", "python"),
82            codec_id,
83        }
84    }
85}
86impl Default for PythonCodecLib {
87    fn default() -> Self {
88        Self::new(PYTHON_CODEC_ID)
89    }
90}
91impl Lib for PythonCodecLib {
92    fn manifest(&self) -> LibManifest {
93        LibManifest {
94            id: self.symbol.clone(),
95            version: Version(env!("CARGO_PKG_VERSION").into()),
96            abi: AbiVersion { major: 0, minor: 1 },
97            target: LibTarget::HostRegistered,
98            requires: Vec::<Dependency>::new(),
99            capabilities: Vec::new(),
100            exports: vec![Export::Codec {
101                symbol: self.symbol.clone(),
102                codec_id: Some(self.codec_id),
103            }],
104        }
105    }
106    fn load(&self, _cx: &mut sim_kernel::LoadCx, linker: &mut Linker) -> Result<()> {
107        let expr_shape =
108            sim_codec::resolve_expr_shape(linker, &Symbol::qualified("codec", "PythonSurface"))?;
109        let options_shape = sim_codec::resolve_options_shape(linker)?;
110        let codec = Arc::new(PythonCodec);
111        linker.codec_value(
112            self.symbol.clone(),
113            codec_value(CodecRuntime {
114                id: self.codec_id,
115                symbol: self.symbol.clone(),
116                decoder: Some(codec.clone()),
117                located_decoder: Some(codec.clone()),
118                tree_decoder: Some(codec.clone()),
119                encoder: Some(codec.clone()),
120                located_encoder: None,
121                tree_encoder: Some(codec),
122                expr_shape,
123                options_shape,
124                default_decode: CodecDefaultDecode::TermInEvalDatumOtherwise,
125            }),
126        )?;
127        Ok(())
128    }
129}
130
131fn input_text(codec: sim_kernel::CodecId, input: Input) -> Result<String> {
132    match input {
133        Input::Text(text) => Ok(text),
134        Input::Bytes(bytes) => String::from_utf8(bytes).map_err(|error| Error::CodecError {
135            codec,
136            message: format!("codec input is not valid UTF-8: {error}"),
137        }),
138    }
139}