#![deny(unsafe_code)]
#![deny(missing_docs)]
#![deny(rustdoc::broken_intra_doc_links)]
use ast::{Expr, FnDef, Pattern, Program, Stmt, Type};
use check::TypeError;
use circuit::Circuit;
use compile::CompilerError;
use eval::{resolve_const_type, EvalError, Evaluator};
use literal::Literal;
use parse::ParseError;
use scan::{scan, ScanError};
use std::{
collections::HashMap,
fmt::{Display, Write as _},
};
use token::MetaInfo;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
pub type UntypedProgram = Program<()>;
pub type UntypedFnDef = FnDef<()>;
pub type UntypedStmt = Stmt<()>;
pub type UntypedExpr = Expr<()>;
pub type UntypedPattern = Pattern<()>;
pub type TypedProgram = Program<Type>;
pub type TypedFnDef = FnDef<Type>;
pub type TypedStmt = Stmt<Type>;
pub type TypedExpr = Expr<Type>;
pub type TypedPattern = Pattern<Type>;
pub mod ast;
pub mod check;
pub mod circuit;
pub mod compile;
pub mod env;
pub mod eval;
pub mod literal;
pub mod parse;
pub mod scan;
pub mod token;
pub fn check(prg: &str) -> Result<TypedProgram, Error> {
Ok(scan(prg)?.parse()?.type_check()?)
}
pub fn compile(prg: &str) -> Result<GarbleProgram, Error> {
let program = check(prg)?;
let (circuit, main) = program.compile("main")?;
let main = main.clone();
Ok(GarbleProgram {
program,
main,
circuit,
consts: HashMap::new(),
const_sizes: HashMap::new(),
})
}
pub fn compile_with_constants(
prg: &str,
consts: HashMap<String, HashMap<String, Literal>>,
) -> Result<GarbleProgram, Error> {
let program = check(prg)?;
let (circuit, main, const_sizes) = program.compile_with_constants("main", consts.clone())?;
let main = main.clone();
Ok(GarbleProgram {
program,
main,
circuit,
consts,
const_sizes,
})
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct GarbleProgram {
pub program: TypedProgram,
pub main: TypedFnDef,
pub circuit: Circuit,
pub consts: HashMap<String, HashMap<String, Literal>>,
pub const_sizes: HashMap<String, usize>,
}
#[derive(Debug, Clone)]
pub struct GarbleArgument<'a>(Literal, &'a TypedProgram, &'a HashMap<String, usize>);
impl GarbleProgram {
pub fn evaluator(&self) -> Evaluator<'_> {
Evaluator::new(&self.program, &self.main, &self.circuit, &self.const_sizes)
}
pub fn literal_arg(
&self,
arg_index: usize,
literal: Literal,
) -> Result<GarbleArgument<'_>, EvalError> {
let Some(param) = self.main.params.get(arg_index) else {
return Err(EvalError::InvalidArgIndex(arg_index));
};
let ty = resolve_const_type(¶m.ty, &self.const_sizes);
if !literal.is_of_type(&self.program, &ty) {
return Err(EvalError::InvalidLiteralType(literal, ty));
}
Ok(GarbleArgument(literal, &self.program, &self.const_sizes))
}
pub fn parse_arg(
&self,
arg_index: usize,
literal: &str,
) -> Result<GarbleArgument<'_>, EvalError> {
let Some(param) = self.main.params.get(arg_index) else {
return Err(EvalError::InvalidArgIndex(arg_index));
};
let literal = Literal::parse(&self.program, ¶m.ty, literal)
.map_err(EvalError::LiteralParseError)?;
Ok(GarbleArgument(literal, &self.program, &self.const_sizes))
}
pub fn parse_output(&self, bits: &[bool]) -> Result<Literal, EvalError> {
Literal::from_result_bits(&self.program, &self.main.ty, bits, &self.const_sizes)
}
}
impl GarbleArgument<'_> {
pub fn as_bits(&self) -> Vec<bool> {
self.0.as_bits(self.1, self.2)
}
pub fn as_literal(&self) -> Literal {
self.0.clone()
}
}
impl Display for GarbleArgument<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
#[derive(Debug, Clone)]
pub enum CompileTimeError {
ScanErrors(Vec<ScanError>),
ParseError(Vec<ParseError>),
TypeError(Vec<TypeError>),
CompilerError(Vec<CompilerError>),
}
#[derive(Debug, Clone)]
pub enum Error {
FnNotFound(String),
CompileTimeError(CompileTimeError),
EvalError(EvalError),
}
impl From<Vec<ScanError>> for CompileTimeError {
fn from(e: Vec<ScanError>) -> Self {
Self::ScanErrors(e)
}
}
impl From<Vec<ParseError>> for CompileTimeError {
fn from(e: Vec<ParseError>) -> Self {
Self::ParseError(e)
}
}
impl From<Vec<TypeError>> for CompileTimeError {
fn from(e: Vec<TypeError>) -> Self {
Self::TypeError(e)
}
}
impl From<Vec<CompilerError>> for CompileTimeError {
fn from(e: Vec<CompilerError>) -> Self {
Self::CompilerError(e)
}
}
impl<E: Into<CompileTimeError>> From<E> for Error {
fn from(e: E) -> Self {
Error::CompileTimeError(e.into())
}
}
impl From<EvalError> for Error {
fn from(e: EvalError) -> Self {
Self::EvalError(e)
}
}
impl EvalError {
pub fn prettify(&self, prg: &str) -> String {
match self {
EvalError::Panic(panic) => {
let mut msg = "".to_string();
let meta = panic.panicked_at;
writeln!(
msg,
"Panic due to {} on line {}:{}.\n",
panic.reason,
meta.start.0 + 1,
meta.start.1 + 1
)
.unwrap();
msg += &prettify_meta(prg, meta);
msg
}
_ => format!("{self}"),
}
}
}
impl Error {
pub fn prettify(&self, prg: &str) -> String {
match self {
Error::FnNotFound(fn_name) => {
format!("Could not find any function with name '{fn_name}'")
}
Error::CompileTimeError(e) => e.prettify(prg),
Error::EvalError(e) => e.prettify(prg),
}
}
}
impl CompileTimeError {
pub fn prettify(&self, prg: &str) -> String {
let mut errs_for_display = vec![];
match self {
CompileTimeError::ScanErrors(errs) => {
for ScanError(e, meta) in errs {
errs_for_display.push(("Scan error", format!("{e}"), Some(*meta)));
}
}
CompileTimeError::ParseError(errs) => {
for ParseError(e, meta) in errs {
errs_for_display.push(("Parse error", format!("{e}"), Some(*meta)));
}
}
CompileTimeError::TypeError(errs) => {
for TypeError(e, meta) in errs {
errs_for_display.push(("Type error", format!("{e}"), Some(*meta)));
}
}
CompileTimeError::CompilerError(errs) => {
for e in errs {
match e {
CompilerError::MissingConstant(_, _, meta) => {
errs_for_display.push(("Compiler error", format!("{e}"), Some(*meta)))
}
e => errs_for_display.push(("Compiler error", format!("{e}"), None)),
}
}
}
}
let mut msg = "".to_string();
for (err_type, err, meta) in errs_for_display {
if let Some(meta) = meta {
writeln!(
msg,
"\n{} on line {}:{}.",
err_type,
meta.start.0 + 1,
meta.start.1 + 1
)
.unwrap();
} else {
writeln!(msg, "\n{}:", err_type).unwrap();
}
writeln!(msg, "{err}:").unwrap();
if let Some(meta) = meta {
msg += &prettify_meta(prg, meta);
}
}
msg
}
}
fn prettify_meta(prg: &str, meta: MetaInfo) -> String {
let mut msg = "".to_string();
if prg.is_empty() {
return msg;
}
let lines: Vec<&str> = prg.lines().collect();
for l in (meta.start.0 as i64 - 2)..(meta.end.0 as i64 + 2) {
let line_start = meta.start.0 as i64;
let line_end = meta.end.0 as i64;
let line_should_be_highlighted =
l >= line_start && (l < line_end || (l == line_end && meta.end.1 > 0));
if l >= 0 && (l as usize) < lines.len() {
if line_should_be_highlighted {
writeln!(msg, "{: >4} > | {}", l + 1, lines[l as usize]).unwrap();
} else {
writeln!(msg, " | {}", lines[l as usize]).unwrap();
}
}
if line_should_be_highlighted {
msg += " > | ";
let col_start = if l == line_start { meta.start.1 } else { 0 };
let col_end = if l == line_end {
meta.end.1
} else {
lines[l as usize].len()
};
for _ in 0..col_start {
msg += " ";
}
for _ in col_start..col_end {
msg += "^";
}
msg += "\n";
}
}
msg
}