Skip to content

Commit

Permalink
Fix script mode
Browse files Browse the repository at this point in the history
  • Loading branch information
Rexagon committed Aug 21, 2023
1 parent df8c0a3 commit a5f4dd2
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 22 deletions.
8 changes: 5 additions & 3 deletions cli/src/args.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::rc::Rc;

use anyhow::{Context as _, Result};
use anyhow::Result;

use fift::core::*;

Expand Down Expand Up @@ -53,8 +53,10 @@ struct CmdArgCont(Vec<Rc<dyn StackValue>>);
impl ContImpl for CmdArgCont {
fn run(self: Rc<Self>, ctx: &mut Context) -> Result<Option<Cont>> {
let n = ctx.stack.pop_smallint_range(0, 999999)? as usize;
let arg = self.0.get(n).context("Cmd arg index out of range")?.clone();
ctx.stack.push_raw(arg)?;
match self.0.get(n).cloned() {
None => ctx.stack.push_null()?,
Some(value) => ctx.stack.push_raw(value)?,
}
Ok(None)
}

Expand Down
2 changes: 1 addition & 1 deletion cli/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ impl Environment for SystemEnvironment {
}

fn write_file(&mut self, name: &str, contents: &[u8]) -> std::io::Result<()> {
std::fs::write(self.resolve_file(name)?, contents)?;
std::fs::write(name, contents)?;
Ok(())
}

Expand Down
16 changes: 9 additions & 7 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,21 @@ fn main() -> Result<ExitCode> {
.unwrap_or_else(|| std::env::var("FIFTPATH").unwrap_or_default()),
);

let interactive = app.interactive || app.source_files.is_empty();
let interactive = app.interactive || rest.is_empty() && app.source_files.is_empty();

// Prepare the source block which will be executed
let mut stdout: Box<dyn std::io::Write> = Box::new(std::io::stdout());

let mut source_blocks = Vec::new();

if interactive && std::io::stdin().is_terminal() {
let mut line_reader = LineReader::new()?;
stdout = line_reader.create_external_printer()?;
source_blocks.push(SourceBlock::new("<stdin>", line_reader));
} else if app.source_files.is_empty() {
source_blocks.push(SourceBlock::new("<stdin>", std::io::stdin().lock()));
if interactive {
if std::io::stdin().is_terminal() {
let mut line_reader = LineReader::new()?;
stdout = line_reader.create_external_printer()?;
source_blocks.push(SourceBlock::new("<stdin>", line_reader));
} else {
source_blocks.push(SourceBlock::new("<stdin>", std::io::stdin().lock()));
}
}

if let Some(path) = rest.first() {
Expand Down
44 changes: 44 additions & 0 deletions src/modules/arithmetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,39 @@ impl Arithmetic {
stack.push(r)
}

#[cmd(name = "*/", stack, args(r = Rounding::Floor))]
#[cmd(name = "*/r", stack, args(r = Rounding::Nearest))]
#[cmd(name = "*/c", stack, args(r = Rounding::Ceil))]
fn interpret_times_div(stack: &mut Stack, r: Rounding) -> Result<()> {
let z = stack.pop_int()?;
let y = stack.pop_int()?;
let mut x = stack.pop_int()?;
*Rc::make_mut(&mut x) *= y.as_ref();
stack.push(divmod(&x, &z, r).0)
}

#[cmd(name = "*/mod", stack, args(r = Rounding::Floor))]
#[cmd(name = "*/rmod", stack, args(r = Rounding::Nearest))]
#[cmd(name = "*/cmod", stack, args(r = Rounding::Ceil))]
fn interpret_times_divmod(stack: &mut Stack, r: Rounding) -> Result<()> {
let z = stack.pop_int()?;
let y = stack.pop_int()?;
let mut x = stack.pop_int()?;
*Rc::make_mut(&mut x) *= y.as_ref();
let (q, r) = divmod(&x, &z, r);
stack.push(q)?;
stack.push(r)
}

#[cmd(name = "*mod", stack, args(r = Rounding::Floor))]
fn interpret_times_mod(stack: &mut Stack, r: Rounding) -> Result<()> {
let z = stack.pop_int()?;
let y = stack.pop_int()?;
let mut x = stack.pop_int()?;
*Rc::make_mut(&mut x) *= y.as_ref();
stack.push(divmod(&x, &z, r).1)
}

#[cmd(name = "1<<", stack, args(negate = false, minus_one = false))]
#[cmd(name = "-1<<", stack, args(negate = true, minus_one = false))]
#[cmd(name = "1<<1-", stack, args(negate = false, minus_one = true))]
Expand Down Expand Up @@ -164,6 +197,17 @@ impl Arithmetic {
stack.push_raw(x)
}

#[cmd(name = "<</", stack, args(r = Rounding::Floor))]
#[cmd(name = "<</r", stack, args(r = Rounding::Nearest))]
#[cmd(name = "<</c", stack, args(r = Rounding::Ceil))]
fn interpret_lshift_div(stack: &mut Stack, r: Rounding) -> Result<()> {
let z = stack.pop_smallint_range(0, 256)?;
let y = stack.pop_int()?;
let mut x = stack.pop_int()?;
*Rc::make_mut(&mut x) <<= z;
stack.push(divmod(&x, &y, r).0)
}

// TODO: mul shift, shift div

// === Logical ===
Expand Down
6 changes: 1 addition & 5 deletions src/modules/crypto.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use anyhow::{Context as _, Result};
use crc::Crc;
use everscale_crypto::ed25519;

use crate::core::*;
use crate::util::{CRC_16, CRC_32, CRC_32_C};

pub struct Crypto;

Expand Down Expand Up @@ -106,7 +106,3 @@ fn pop_signature(stack: &mut Stack) -> Result<[u8; 64]> {
let b = stack.pop_bytes()?;
b.as_slice().try_into().ok().context("Invalid signature")
}

const CRC_16: Crc<u16> = Crc::<u16>::new(&crc::CRC_16_XMODEM);
const CRC_32: Crc<u32> = Crc::<u32>::new(&crc::CRC_32_ISO_HDLC);
const CRC_32_C: Crc<u32> = Crc::<u32>::new(&crc::CRC_32_ISCSI);
119 changes: 113 additions & 6 deletions src/modules/string_utils.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use std::rc::Rc;
use std::str::FromStr;

use anyhow::Result;
use everscale_types::models::StdAddr;
use everscale_types::prelude::HashBytes;
use num_bigint::{BigInt, Sign};
use num_traits::Num;
use sha2::Digest;
Expand Down Expand Up @@ -453,16 +456,120 @@ impl StringUtils {
}
}

#[cmd(name = "B>base64", stack)]
fn interpret_bytes_to_base64(stack: &mut Stack) -> Result<()> {
#[cmd(name = "B>base64", stack, args(url = false))]
#[cmd(name = "B>base64url", stack, args(url = true))]
fn interpret_bytes_to_base64(stack: &mut Stack, url: bool) -> Result<()> {
let bytes = stack.pop_bytes()?;
stack.push(encode_base64(&*bytes))
stack.push(if url {
encode_base64_url(&*bytes)
} else {
encode_base64(&*bytes)
})
}

#[cmd(name = "base64>B", stack)]
fn interpret_base64_to_bytes(stack: &mut Stack) -> Result<()> {
#[cmd(name = "base64>B", stack, args(url = false))]
#[cmd(name = "base64url>B", stack, args(url = true))]
fn interpret_base64_to_bytes(stack: &mut Stack, url: bool) -> Result<()> {
let string = stack.pop_string()?;
let bytes = decode_base64(&*string)?;
let bytes = if url {
decode_base64_url(&*string)?
} else {
decode_base64(&*string)?
};
stack.push(bytes)
}

#[cmd(name = "smca>$", stack)]
fn interpret_pack_std_smc_addr(stack: &mut Stack) -> Result<()> {
let mode = stack.pop_smallint_range(0, 7)? as u8;
let int = stack.pop_int()?;
anyhow::ensure!(int.sign() != Sign::Minus, "Expected non-negative integer");
anyhow::ensure!(int.bits() <= 256, "Integer does not fit into the buffer");

let workchain = stack.pop_smallint_signed_range(-0x80, 0x7f)? as i8;
let testnet = mode & 2 != 0;
let bounceable = mode & 1 == 0;
let url_safe = mode & 4 != 0;

let mut bytes = int.to_bytes_be().1;
bytes.resize(32, 0);
bytes.reverse();

let mut buffer = [0u8; 36];
buffer[0] = 0x51 - (bounceable as u8) * 0x40 + (testnet as u8) * 0x80;
buffer[1] = workchain as u8;
buffer[2..34].copy_from_slice(&bytes);

let crc = CRC_16.checksum(&buffer[..34]);
buffer[34] = (crc >> 8) as u8;
buffer[35] = crc as u8;

stack.push(if url_safe {
encode_base64_url(buffer)
} else {
encode_base64(buffer)
})
}

#[cmd(name = "$>smca", stack)]
fn interpret_unpack_std_smc_addr(stack: &mut Stack) -> Result<()> {
struct AddrFlags {
testnet: bool,
bounceable: bool,
}

fn unpack_base64_addr(s: &str) -> Result<(AddrFlags, StdAddr)> {
anyhow::ensure!(s.len() == 48, "Invalid address string length");

let buffer = match decode_base64(s) {
Ok(buffer) => buffer,
Err(e) => match decode_base64_url(s) {
Ok(buffer) => buffer,
Err(_) => return Err(e.into()),
},
};
anyhow::ensure!(buffer.len() == 36, "Invalid decoder buffer length");

let crc = CRC_16.checksum(&buffer[..34]);
anyhow::ensure!(
crc == ((buffer[34] as u16) << 8) | buffer[35] as u16,
"CRC mismatch"
);
let flags = buffer[0];
anyhow::ensure!(flags & 0x3f != 0x11, "Invalid flags");
let flags = AddrFlags {
testnet: flags & 0x80 != 0,
bounceable: flags & 0x40 == 0,
};

Ok((
flags,
StdAddr::new(
buffer[1] as i8,
HashBytes(buffer[2..34].try_into().unwrap()),
),
))
}

let string = stack.pop_string()?;
let (flags, addr) = 'addr: {
if string.contains(':') {
let flags = AddrFlags {
testnet: false,
bounceable: true,
};
if let Ok(addr) = StdAddr::from_str(&string) {
break 'addr (flags, addr);
}
} else if let Ok(addr) = unpack_base64_addr(&string) {
break 'addr addr;
};
return stack.push_bool(false);
};

stack.push_int(addr.workchain)?;
stack.push_int(BigInt::from_bytes_be(Sign::Plus, addr.address.as_slice()))?;
stack.push_int(((flags.testnet as u8) << 1) + !flags.bounceable as u8)?;
stack.push_bool(true)
}
}
23 changes: 23 additions & 0 deletions src/util.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
use anyhow::Result;
use crc::Crc;
use everscale_types::cell::MAX_BIT_LEN;
use everscale_types::prelude::*;
use num_bigint::BigInt;
use num_traits::{Num, ToPrimitive};
use unicode_segmentation::UnicodeSegmentation;

pub const CRC_16: Crc<u16> = Crc::<u16>::new(&crc::CRC_16_XMODEM);
pub const CRC_32: Crc<u32> = Crc::<u32>::new(&crc::CRC_32_ISO_HDLC);
pub const CRC_32_C: Crc<u32> = Crc::<u32>::new(&crc::CRC_32_ISCSI);

pub struct ImmediateInt {
pub num: BigInt,
pub denom: Option<BigInt>,
Expand Down Expand Up @@ -100,6 +105,24 @@ pub(crate) fn decode_base64<T: AsRef<[u8]>>(data: T) -> Result<Vec<u8>, base64::
decode_base64_impl(data.as_ref())
}

#[inline]
pub(crate) fn encode_base64_url<T: AsRef<[u8]>>(data: T) -> String {
use base64::Engine;
fn encode_base64_impl(data: &[u8]) -> String {
base64::engine::general_purpose::URL_SAFE.encode(data)
}
encode_base64_impl(data.as_ref())
}

#[inline]
pub(crate) fn decode_base64_url<T: AsRef<[u8]>>(data: T) -> Result<Vec<u8>, base64::DecodeError> {
use base64::Engine;
fn decode_base64_impl(data: &[u8]) -> std::result::Result<Vec<u8>, base64::DecodeError> {
base64::engine::general_purpose::URL_SAFE.decode(data)
}
decode_base64_impl(data.as_ref())
}

pub trait DisplaySliceExt<'s> {
fn display_slice_tree<'a: 's>(&'a self, limit: usize) -> DisplayCellSlice<'a, 's>;

Expand Down

0 comments on commit a5f4dd2

Please sign in to comment.