#[derive(Debug, Clone, PartialEq)]
pub struct IdrisData {
pub name: String,
pub params: Vec<(String, IdrisType)>,
pub kind: IdrisType,
pub constructors: Vec<IdrisConstructor>,
pub visibility: Visibility,
pub doc: Option<String>,
}
impl IdrisData {
pub fn emit(&self, indent: usize) -> String {
let pad = " ".repeat(indent);
let mut out = String::new();
if let Some(doc) = &self.doc {
for line in doc.lines() {
out.push_str(&format!("{}||| {}\n", pad, line));
}
}
let params_str: String = self
.params
.iter()
.map(|(n, t)| format!(" ({} : {})", n, t))
.collect();
out.push_str(&format!(
"{}{}data {}{} : {} where\n",
pad, self.visibility, self.name, params_str, self.kind
));
for ctor in &self.constructors {
if let Some(doc) = &ctor.doc {
out.push_str(&format!("{} ||| {}\n", pad, doc));
}
out.push_str(&format!("{} {} : {}\n", pad, ctor.name, ctor.ty));
}
out
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct IdrisRecord {
pub name: String,
pub params: Vec<(String, IdrisType)>,
pub kind: IdrisType,
pub constructor: String,
pub fields: Vec<(String, IdrisType)>,
pub visibility: Visibility,
pub doc: Option<String>,
}
impl IdrisRecord {
pub fn emit(&self, indent: usize) -> String {
let pad = " ".repeat(indent);
let mut out = String::new();
if let Some(doc) = &self.doc {
for line in doc.lines() {
out.push_str(&format!("{}||| {}\n", pad, line));
}
}
let params_str: String = self
.params
.iter()
.map(|(n, t)| format!(" ({} : {})", n, t))
.collect();
out.push_str(&format!(
"{}{}record {}{} : {} where\n",
pad, self.visibility, self.name, params_str, self.kind
));
out.push_str(&format!("{} constructor {}\n", pad, self.constructor));
for (fname, ftype) in &self.fields {
out.push_str(&format!("{} {} : {}\n", pad, fname, ftype));
}
out
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct IdrisNamespaceBlock {
pub name: String,
pub decls: Vec<IdrisDecl>,
}
impl IdrisNamespaceBlock {
#[allow(dead_code)]
pub fn new(name: impl Into<String>) -> Self {
IdrisNamespaceBlock {
name: name.into(),
decls: Vec::new(),
}
}
#[allow(dead_code)]
pub fn add(&mut self, decl: IdrisDecl) {
self.decls.push(decl);
}
#[allow(dead_code)]
pub fn emit(&self, backend: &IdrisBackend) -> String {
let mut out = format!("namespace {}\n", self.name);
for decl in &self.decls {
for line in backend.emit_decl(decl).lines() {
out.push_str(" ");
out.push_str(line);
out.push('\n');
}
}
out
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub enum IdrisTactic {
Intro(String),
Intros,
Exact(IdrisExpr),
Refl,
Trivial,
Decide,
Rewrite(String),
RewriteBack(String),
Apply(String),
Cases(String),
Induction(String),
Search,
Auto,
With(String),
Let(String, IdrisExpr),
Have(String, IdrisType),
Focus(usize),
Claim(String, IdrisType),
Unfold(String),
Compute,
Normals,
Fail(String),
Seq(Vec<IdrisTactic>),
}
#[derive(Debug, Clone, PartialEq)]
pub struct IdrisFunction {
pub name: String,
pub type_sig: IdrisType,
pub clauses: Vec<(Vec<IdrisPattern>, IdrisExpr)>,
pub totality: Totality,
pub visibility: Visibility,
pub pragmas: Vec<String>,
pub doc: Option<String>,
}
impl IdrisFunction {
pub fn new(name: impl Into<String>, ty: IdrisType, body: IdrisExpr) -> Self {
IdrisFunction {
name: name.into(),
type_sig: ty,
clauses: vec![(vec![], body)],
totality: Totality::Default,
visibility: Visibility::Default,
pragmas: vec![],
doc: None,
}
}
pub fn with_clauses(
name: impl Into<String>,
ty: IdrisType,
clauses: Vec<(Vec<IdrisPattern>, IdrisExpr)>,
) -> Self {
IdrisFunction {
name: name.into(),
type_sig: ty,
clauses,
totality: Totality::Default,
visibility: Visibility::Default,
pragmas: vec![],
doc: None,
}
}
pub fn emit(&self, indent: usize) -> String {
let pad = " ".repeat(indent);
let mut out = String::new();
if let Some(doc) = &self.doc {
for line in doc.lines() {
out.push_str(&format!("{}||| {}\n", pad, line));
}
}
for pragma in &self.pragmas {
out.push_str(&format!("{}%{}\n", pad, pragma));
}
let tot = format!("{}", self.totality);
if !tot.is_empty() {
out.push_str(&format!("{}{}", pad, tot));
}
out.push_str(&format!(
"{}{}{} : {}\n",
pad, self.visibility, self.name, self.type_sig
));
for (pats, rhs) in &self.clauses {
if pats.is_empty() {
out.push_str(&format!("{}{} = {}\n", pad, self.name, rhs));
} else {
let pat_str = pats
.iter()
.map(|p| format!("{}", p))
.collect::<Vec<_>>()
.join(" ");
out.push_str(&format!("{}{} {} = {}\n", pad, self.name, pat_str, rhs));
}
}
out
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct IdrisImport {
pub path: Vec<String>,
pub alias: Option<String>,
pub public: bool,
}
impl IdrisImport {
pub fn new(path: Vec<String>) -> Self {
IdrisImport {
path,
alias: None,
public: false,
}
}
pub fn public_import(path: Vec<String>) -> Self {
IdrisImport {
path,
alias: None,
public: true,
}
}
pub fn emit(&self) -> String {
let path_str = self.path.join(".");
let prefix = if self.public {
"public import "
} else {
"import "
};
if let Some(alias) = &self.alias {
format!("{}{} as {}", prefix, path_str, alias)
} else {
format!("{}{}", prefix, path_str)
}
}
}
#[allow(dead_code)]
pub struct IdrisPatternBuilder;
impl IdrisPatternBuilder {
#[allow(dead_code)]
pub fn con(name: impl Into<String>, args: Vec<IdrisPattern>) -> IdrisPattern {
IdrisPattern::Con(name.into(), args)
}
#[allow(dead_code)]
pub fn var(name: impl Into<String>) -> IdrisPattern {
IdrisPattern::Var(name.into())
}
#[allow(dead_code)]
pub fn wildcard() -> IdrisPattern {
IdrisPattern::Wildcard
}
#[allow(dead_code)]
pub fn lit(l: IdrisLiteral) -> IdrisPattern {
IdrisPattern::Lit(l)
}
#[allow(dead_code)]
pub fn tuple(pats: Vec<IdrisPattern>) -> IdrisPattern {
IdrisPattern::Tuple(pats)
}
#[allow(dead_code)]
pub fn as_pat(name: impl Into<String>, pat: IdrisPattern) -> IdrisPattern {
IdrisPattern::As(name.into(), Box::new(pat))
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum IdrisType {
Type,
Integer,
Nat,
Bool,
String,
Char,
Double,
List(Box<IdrisType>),
Vect(Box<IdrisExpr>, Box<IdrisType>),
Pair(Box<IdrisType>, Box<IdrisType>),
Unit,
Function(Box<IdrisType>, Box<IdrisType>),
Linear(Box<IdrisType>, Box<IdrisType>),
Erased(Box<IdrisType>, Box<IdrisType>),
Pi(String, Box<IdrisType>, Box<IdrisType>),
Data(String, Vec<IdrisType>),
Interface(String, Vec<IdrisType>),
Var(String),
IO(Box<IdrisType>),
Maybe(Box<IdrisType>),
Either(Box<IdrisType>, Box<IdrisType>),
}
impl IdrisType {
pub(super) fn needs_parens(&self) -> bool {
matches!(
self,
IdrisType::Function(_, _)
| IdrisType::Linear(_, _)
| IdrisType::Erased(_, _)
| IdrisType::Pi(_, _, _)
| IdrisType::Interface(_, _)
) || matches!(self, IdrisType::Data(_, args) if ! args.is_empty())
}
pub(super) fn fmt_parens(&self) -> String {
if self.needs_parens() {
format!("({})", self)
} else {
format!("{}", self)
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct IdrisConstructor {
pub name: String,
pub ty: IdrisType,
pub doc: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct IdrisInterface {
pub name: String,
pub constraints: Vec<(String, Vec<IdrisType>)>,
pub params: Vec<(String, IdrisType)>,
pub methods: Vec<(String, IdrisType)>,
pub defaults: Vec<IdrisFunction>,
pub visibility: Visibility,
pub doc: Option<String>,
}
impl IdrisInterface {
pub fn emit(&self, indent: usize) -> String {
let pad = " ".repeat(indent);
let mut out = String::new();
if let Some(doc) = &self.doc {
for line in doc.lines() {
out.push_str(&format!("{}||| {}\n", pad, line));
}
}
let constraints_str: String = self
.constraints
.iter()
.map(|(c, args)| {
if args.is_empty() {
c.clone()
} else {
format!(
"{} {}",
c,
args.iter()
.map(|a| format!("{}", a))
.collect::<Vec<_>>()
.join(" ")
)
}
})
.collect::<Vec<_>>()
.join(", ");
let params_str: String = self
.params
.iter()
.map(|(n, t)| format!(" ({} : {})", n, t))
.collect();
if constraints_str.is_empty() {
out.push_str(&format!(
"{}{}interface {}{} where\n",
pad, self.visibility, self.name, params_str
));
} else {
out.push_str(&format!(
"{}{}interface {} => {}{} where\n",
pad, self.visibility, constraints_str, self.name, params_str
));
}
for (mname, mtype) in &self.methods {
out.push_str(&format!("{} {} : {}\n", pad, mname, mtype));
}
for default in &self.defaults {
out.push_str(&default.emit(indent + 2));
}
out
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct IdrisParametersBlock {
pub params: Vec<(String, IdrisType)>,
pub decls: Vec<IdrisDecl>,
}
impl IdrisParametersBlock {
#[allow(dead_code)]
pub fn new(params: Vec<(String, IdrisType)>) -> Self {
IdrisParametersBlock {
params,
decls: Vec::new(),
}
}
#[allow(dead_code)]
pub fn add(&mut self, decl: IdrisDecl) {
self.decls.push(decl);
}
#[allow(dead_code)]
pub fn emit(&self, backend: &IdrisBackend) -> String {
let params_str: Vec<String> = self
.params
.iter()
.map(|(n, t)| format!("({} : {})", n, t))
.collect();
let mut out = format!("parameters {}\n", params_str.join(" "));
for decl in &self.decls {
for line in backend.emit_decl(decl).lines() {
out.push_str(" ");
out.push_str(line);
out.push('\n');
}
}
out
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct IdrisImplementation {
pub name: Option<String>,
pub interface: String,
pub type_args: Vec<IdrisType>,
pub constraints: Vec<IdrisType>,
pub clauses: Vec<IdrisFunction>,
pub visibility: Visibility,
}
impl IdrisImplementation {
#[allow(dead_code)]
pub fn new(interface: impl Into<String>, type_args: Vec<IdrisType>) -> Self {
IdrisImplementation {
name: None,
interface: interface.into(),
type_args,
constraints: Vec::new(),
clauses: Vec::new(),
visibility: Visibility::Default,
}
}
#[allow(dead_code)]
pub fn add_method(&mut self, func: IdrisFunction) {
self.clauses.push(func);
}
#[allow(dead_code)]
pub fn emit(&self, backend: &IdrisBackend) -> String {
let name_part = self
.name
.as_ref()
.map(|n| format!("[{}] ", n))
.unwrap_or_default();
let constraints_str = if self.constraints.is_empty() {
String::new()
} else {
let cs: Vec<String> = self.constraints.iter().map(|c| format!("{}", c)).collect();
format!("{} => ", cs.join(", "))
};
let type_args_str: Vec<String> = self.type_args.iter().map(|t| format!("{}", t)).collect();
let mut out = format!(
"{}implementation {}{}{} {} where\n",
self.visibility,
name_part,
constraints_str,
self.interface,
type_args_str.join(" "),
);
for clause in &self.clauses {
let decl = IdrisDecl::Func(clause.clone());
for line in backend.emit_decl(&decl).lines() {
out.push_str(" ");
out.push_str(line);
out.push('\n');
}
}
out
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[allow(dead_code)]
pub enum IdrisPragma {
Name(String, Vec<String>),
AutoImplicit,
DefaultTotal,
DefaultPartial,
DefaultCovering,
Inline,
NoInline,
Hint,
Extern,
Builtin(String),
Foreign { backend: String, impl_str: String },
Transform(String),
Deprecate(Option<String>),
Hide(String),
UnboundImplicitsOff,
AmbiguityDepth(u32),
SearchTimeout(u32),
Logging { topic: String, level: u32 },
Language(String),
RunElab(String),
}
#[derive(Debug, Clone, PartialEq)]
pub enum IdrisPattern {
Wildcard,
Var(String),
Con(String, Vec<IdrisPattern>),
Lit(IdrisLiteral),
Tuple(Vec<IdrisPattern>),
Nil,
Cons(Box<IdrisPattern>, Box<IdrisPattern>),
As(String, Box<IdrisPattern>),
Implicit(Box<IdrisPattern>),
Dot(Box<IdrisExpr>),
}
#[derive(Debug, Clone, PartialEq)]
pub enum IdrisDecl {
Func(IdrisFunction),
Data(IdrisData),
Record(IdrisRecord),
Interface(IdrisInterface),
Implementation(IdrisImpl),
Namespace(String, Vec<IdrisDecl>),
Mutual(Vec<IdrisDecl>),
Parameters(Vec<(String, IdrisType)>, Vec<IdrisDecl>),
Pragma(String),
Postulate(String, IdrisType, Visibility),
Comment(String),
Blank,
}
impl IdrisDecl {
pub fn emit(&self, indent: usize) -> String {
let pad = " ".repeat(indent);
match self {
IdrisDecl::Func(f) => f.emit(indent),
IdrisDecl::Data(d) => d.emit(indent),
IdrisDecl::Record(r) => r.emit(indent),
IdrisDecl::Interface(i) => i.emit(indent),
IdrisDecl::Implementation(im) => im.emit(indent),
IdrisDecl::Namespace(name, decls) => {
let mut out = format!("{}namespace {} where\n", pad, name);
for d in decls {
out.push_str(&d.emit(indent + 2));
}
out
}
IdrisDecl::Mutual(decls) => {
let mut out = format!("{}mutual\n", pad);
for d in decls {
out.push_str(&d.emit(indent + 2));
}
out
}
IdrisDecl::Parameters(params, decls) => {
let params_str: String = params
.iter()
.map(|(n, t)| format!("({} : {})", n, t))
.collect::<Vec<_>>()
.join(" ");
let mut out = format!("{}parameters {}\n", pad, params_str);
for d in decls {
out.push_str(&d.emit(indent + 2));
}
out
}
IdrisDecl::Pragma(s) => format!("{}%{}\n", pad, s),
IdrisDecl::Postulate(name, ty, vis) => {
format!("{}{}postulate {} : {}\n", pad, vis, name, ty)
}
IdrisDecl::Comment(s) => format!("{}-- {}\n", pad, s),
IdrisDecl::Blank => String::from("\n"),
}
}
}
#[allow(dead_code)]
pub struct IdrisStdlibSnippets;
impl IdrisStdlibSnippets {
#[allow(dead_code)]
pub fn map_maybe_fn() -> IdrisFunction {
IdrisFunction::with_clauses(
"mapMaybe",
IdrisTypeBuilder::arrow(vec![
IdrisTypeBuilder::arrow(vec![
IdrisType::Var("a".to_string()),
IdrisTypeBuilder::maybe(IdrisType::Var("b".to_string())),
]),
IdrisTypeBuilder::list(IdrisType::Var("a".to_string())),
IdrisTypeBuilder::list(IdrisType::Var("b".to_string())),
]),
vec![
(
vec![
IdrisPatternBuilder::wildcard(),
IdrisPatternBuilder::con("Nil", vec![]),
],
IdrisExpr::Var("Nil".to_string()),
),
(
vec![
IdrisPatternBuilder::var("f"),
IdrisPatternBuilder::con(
"::",
vec![
IdrisPatternBuilder::var("x"),
IdrisPatternBuilder::var("xs"),
],
),
],
IdrisExprBuilder::case_of(
IdrisExprBuilder::app_chain(
IdrisExpr::Var("f".to_string()),
vec![IdrisExpr::Var("x".to_string())],
),
vec![
(
IdrisPatternBuilder::con("Nothing", vec![]),
IdrisExprBuilder::app_chain(
IdrisExpr::Var("mapMaybe".to_string()),
vec![
IdrisExpr::Var("f".to_string()),
IdrisExpr::Var("xs".to_string()),
],
),
),
(
IdrisPatternBuilder::con(
"Just",
vec![IdrisPatternBuilder::var("y")],
),
IdrisExprBuilder::app_chain(
IdrisExpr::Var("::".to_string()),
vec![
IdrisExpr::Var("y".to_string()),
IdrisExprBuilder::app_chain(
IdrisExpr::Var("mapMaybe".to_string()),
vec![
IdrisExpr::Var("f".to_string()),
IdrisExpr::Var("xs".to_string()),
],
),
],
),
),
],
),
),
],
)
}
#[allow(dead_code)]
pub fn foldr_fn() -> String {
"foldr : (a -> b -> b) -> b -> List a -> b\nfoldr f z [] = z\nfoldr f z (x :: xs) = f x (foldr f z xs)\n"
.to_string()
}
#[allow(dead_code)]
pub fn zip_fn() -> String {
"zip : List a -> List b -> List (a, b)\nzip [] _ = []\nzip _ [] = []\nzip (x :: xs) (y :: ys) = (x, y) :: zip xs ys\n"
.to_string()
}
#[allow(dead_code)]
pub fn nub_fn() -> String {
"nub : DecEq a => List a -> List a\nnub [] = []\nnub (x :: xs) = x :: nub (filter (/= x) xs)\n"
.to_string()
}
#[allow(dead_code)]
pub fn show_nat_instance(type_name: &str) -> String {
format!("Show {} where\n show x = show (toNat x)\n", type_name)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum IdrisLiteral {
Int(i64),
Nat(u64),
Float(f64),
Char(char),
Str(String),
True,
False,
Unit,
}
#[derive(Debug, Clone, PartialEq)]
pub enum IdrisExpr {
Lit(IdrisLiteral),
Var(String),
Qualified(Vec<String>),
App(Box<IdrisExpr>, Box<IdrisExpr>),
Lam(Vec<String>, Box<IdrisExpr>),
Let(String, Box<IdrisExpr>, Box<IdrisExpr>),
CaseOf(Box<IdrisExpr>, Vec<(IdrisPattern, IdrisExpr)>),
IfThenElse(Box<IdrisExpr>, Box<IdrisExpr>, Box<IdrisExpr>),
Do(Vec<IdrisDoStmt>),
Tuple(Vec<IdrisExpr>),
ListLit(Vec<IdrisExpr>),
Annot(Box<IdrisExpr>, Box<IdrisType>),
Idiom(Box<IdrisExpr>),
ProofTerm(Box<IdrisExpr>),
WithPattern(Box<IdrisExpr>, Vec<(IdrisPattern, IdrisExpr)>),
Infix(String, Box<IdrisExpr>, Box<IdrisExpr>),
Hole(String),
Refl,
AnonHole,
RecordUpdate(Box<IdrisExpr>, Vec<(String, IdrisExpr)>),
Neg(Box<IdrisExpr>),
}
impl IdrisExpr {
pub(super) fn needs_parens(&self) -> bool {
matches!(
self,
IdrisExpr::App(_, _)
| IdrisExpr::Lam(_, _)
| IdrisExpr::Let(_, _, _)
| IdrisExpr::CaseOf(_, _)
| IdrisExpr::IfThenElse(_, _, _)
| IdrisExpr::Do(_)
| IdrisExpr::Infix(_, _, _)
| IdrisExpr::Annot(_, _)
| IdrisExpr::Neg(_)
)
}
pub(super) fn fmt_arg(&self) -> String {
if self.needs_parens() {
format!("({})", self)
} else {
format!("{}", self)
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct IdrisImpl {
pub impl_name: Option<String>,
pub constraints: Vec<(String, Vec<IdrisType>)>,
pub interface: String,
pub args: Vec<IdrisType>,
pub methods: Vec<IdrisFunction>,
pub visibility: Visibility,
}
impl IdrisImpl {
pub fn emit(&self, indent: usize) -> String {
let pad = " ".repeat(indent);
let mut out = String::new();
let constraints_str: String = self
.constraints
.iter()
.map(|(c, args)| {
if args.is_empty() {
c.clone()
} else {
format!(
"{} {}",
c,
args.iter()
.map(|a| format!("{}", a))
.collect::<Vec<_>>()
.join(" ")
)
}
})
.collect::<Vec<_>>()
.join(", ");
let args_str: String = self.args.iter().map(|a| format!(" {}", a)).collect();
let name_str = self
.impl_name
.as_ref()
.map(|n| format!(" [{}]", n))
.unwrap_or_default();
if constraints_str.is_empty() {
out.push_str(&format!(
"{}{}implementation{} {}{} where\n",
pad, self.visibility, name_str, self.interface, args_str
));
} else {
out.push_str(&format!(
"{}{}implementation{} {} => {}{} where\n",
pad, self.visibility, name_str, constraints_str, self.interface, args_str
));
}
for method in &self.methods {
out.push_str(&method.emit(indent + 2));
}
out
}
}
#[derive(Debug, Default)]
pub struct IdrisBackend {
pub add_header: bool,
pub default_total: bool,
pub auto_implicit: bool,
pub options: IdrisBackendOptions,
}
impl IdrisBackend {
pub fn new() -> Self {
IdrisBackend {
add_header: false,
default_total: false,
auto_implicit: false,
options: IdrisBackendOptions {
blank_between_decls: true,
emit_docs: true,
},
}
}
pub fn proof_mode() -> Self {
IdrisBackend {
add_header: true,
default_total: true,
auto_implicit: true,
options: IdrisBackendOptions {
blank_between_decls: true,
emit_docs: true,
},
}
}
pub fn emit_module(&self, module: &IdrisModule) -> String {
let mut out = String::new();
if self.add_header {
out.push_str("-- AUTO-GENERATED by OxiLean\n\n");
}
if let Some(doc) = &module.doc {
for line in doc.lines() {
out.push_str(&format!("||| {}\n", line));
}
}
let module_str = module.module_name.join(".");
out.push_str(&format!("module {}\n\n", module_str));
if self.default_total {
out.push_str("%default total\n\n");
}
if self.auto_implicit {
out.push_str("%auto_implicit_depth 50\n\n");
}
for imp in &module.imports {
out.push_str(&imp.emit());
out.push('\n');
}
if !module.imports.is_empty() {
out.push('\n');
}
for decl in &module.declarations {
let s = decl.emit(0);
out.push_str(&s);
if self.options.blank_between_decls
&& !matches!(decl, IdrisDecl::Blank | IdrisDecl::Comment(_))
{
out.push('\n');
}
}
out
}
pub fn emit_decl(&self, decl: &IdrisDecl) -> String {
decl.emit(0)
}
pub fn emit_function(&self, func: &IdrisFunction) -> String {
func.emit(0)
}
pub fn emit_data(&self, data: &IdrisData) -> String {
data.emit(0)
}
pub fn emit_record(&self, rec: &IdrisRecord) -> String {
rec.emit(0)
}
pub fn emit_interface(&self, iface: &IdrisInterface) -> String {
iface.emit(0)
}
pub fn emit_impl(&self, im: &IdrisImpl) -> String {
im.emit(0)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Visibility {
PublicExport,
Export,
Private,
Default,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Totality {
Total,
Partial,
Covering,
Default,
}
#[derive(Debug, Clone, PartialEq)]
pub enum IdrisDoStmt {
Bind(String, IdrisExpr),
Let(String, IdrisExpr),
Expr(IdrisExpr),
LetTyped(String, IdrisType, IdrisExpr),
Ignore(IdrisExpr),
}
#[derive(Debug, Default, Clone)]
pub struct IdrisBackendOptions {
pub blank_between_decls: bool,
pub emit_docs: bool,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct IdrisInterfaceExt {
pub name: String,
pub params: Vec<(String, IdrisType)>,
pub constraints: Vec<IdrisType>,
pub methods: Vec<(String, IdrisType, Option<IdrisExpr>)>,
pub visibility: Visibility,
pub doc: Option<String>,
}
impl IdrisInterfaceExt {
#[allow(dead_code)]
pub fn new(name: impl Into<String>, params: Vec<(String, IdrisType)>) -> Self {
IdrisInterfaceExt {
name: name.into(),
params,
constraints: Vec::new(),
methods: Vec::new(),
visibility: Visibility::PublicExport,
doc: None,
}
}
#[allow(dead_code)]
pub fn add_method(&mut self, name: impl Into<String>, ty: IdrisType) {
self.methods.push((name.into(), ty, None));
}
#[allow(dead_code)]
pub fn add_method_with_default(
&mut self,
name: impl Into<String>,
ty: IdrisType,
default: IdrisExpr,
) {
self.methods.push((name.into(), ty, Some(default)));
}
#[allow(dead_code)]
pub fn emit(&self) -> String {
let mut out = String::new();
if let Some(doc) = &self.doc {
for line in doc.lines() {
out.push_str(&format!("||| {}\n", line));
}
}
let params_str: Vec<String> = self
.params
.iter()
.map(|(n, t)| format!("({} : {})", n, t))
.collect();
let constraints_str = if self.constraints.is_empty() {
String::new()
} else {
let cs: Vec<String> = self.constraints.iter().map(|c| format!("{}", c)).collect();
format!("{} => ", cs.join(", "))
};
out.push_str(&format!(
"{}interface {}{} {} where\n",
self.visibility,
constraints_str,
self.name,
params_str.join(" ")
));
for (mname, mty, mdefault) in &self.methods {
out.push_str(&format!(" {} : {}\n", mname, mty));
if let Some(def) = mdefault {
out.push_str(&format!(" {} _ = {}\n", mname, def));
}
}
out
}
}
#[allow(dead_code)]
pub struct IdrisTypeBuilder;
impl IdrisTypeBuilder {
#[allow(dead_code)]
pub fn arrow(types: Vec<IdrisType>) -> IdrisType {
assert!(!types.is_empty(), "arrow type must have at least one type");
let mut it = types.into_iter().rev();
let mut result = it
.next()
.expect("types is non-empty; guaranteed by assert above");
for ty in it {
result = IdrisType::Function(Box::new(ty), Box::new(result));
}
result
}
#[allow(dead_code)]
pub fn app(head: impl Into<String>, args: Vec<IdrisType>) -> IdrisType {
IdrisType::Data(head.into(), args)
}
#[allow(dead_code)]
pub fn vect(n_expr: impl Into<String>, elem_ty: IdrisType) -> IdrisType {
IdrisType::Data(
"Vect".to_string(),
vec![IdrisType::Var(n_expr.into()), elem_ty],
)
}
#[allow(dead_code)]
pub fn list(elem_ty: IdrisType) -> IdrisType {
IdrisType::List(Box::new(elem_ty))
}
#[allow(dead_code)]
pub fn maybe(ty: IdrisType) -> IdrisType {
IdrisType::Data("Maybe".to_string(), vec![ty])
}
#[allow(dead_code)]
pub fn either(left: IdrisType, right: IdrisType) -> IdrisType {
IdrisType::Data("Either".to_string(), vec![left, right])
}
#[allow(dead_code)]
pub fn pair(a: IdrisType, b: IdrisType) -> IdrisType {
IdrisType::Data("Pair".to_string(), vec![a, b])
}
#[allow(dead_code)]
pub fn io(ty: IdrisType) -> IdrisType {
IdrisType::Data("IO".to_string(), vec![ty])
}
#[allow(dead_code)]
pub fn nat() -> IdrisType {
IdrisType::Nat
}
#[allow(dead_code)]
pub fn bool() -> IdrisType {
IdrisType::Bool
}
#[allow(dead_code)]
pub fn string() -> IdrisType {
IdrisType::String
}
#[allow(dead_code)]
pub fn integer() -> IdrisType {
IdrisType::Integer
}
#[allow(dead_code)]
pub fn pi(param: impl Into<String>, domain: IdrisType, codomain: IdrisType) -> IdrisType {
IdrisType::Pi(param.into(), Box::new(domain), Box::new(codomain))
}
}
#[derive(Debug)]
#[allow(dead_code)]
pub struct IdrisModuleBuilder {
pub(super) module: IdrisModule,
}
impl IdrisModuleBuilder {
#[allow(dead_code)]
pub fn new(parts: Vec<String>) -> Self {
IdrisModuleBuilder {
module: IdrisModule::new(parts),
}
}
#[allow(dead_code)]
pub fn import(mut self, imp: IdrisImport) -> Self {
self.module.import(imp);
self
}
#[allow(dead_code)]
pub fn public_import(mut self, parts: Vec<String>) -> Self {
self.module.import(IdrisImport::public_import(parts));
self
}
#[allow(dead_code)]
pub fn decl(mut self, decl: IdrisDecl) -> Self {
self.module.add(decl);
self
}
#[allow(dead_code)]
pub fn pragma(mut self, pragma: IdrisPragma) -> Self {
self.module.add(IdrisDecl::Pragma(format!("{}", pragma)));
self
}
#[allow(dead_code)]
pub fn comment(mut self, text: impl Into<String>) -> Self {
self.module.add(IdrisDecl::Comment(text.into()));
self
}
#[allow(dead_code)]
pub fn build(self) -> IdrisModule {
self.module
}
}
#[allow(dead_code)]
pub struct IdrisExprBuilder;
impl IdrisExprBuilder {
#[allow(dead_code)]
pub fn app_chain(func: IdrisExpr, args: Vec<IdrisExpr>) -> IdrisExpr {
args.into_iter().fold(func, |acc, arg| {
IdrisExpr::App(Box::new(acc), Box::new(arg))
})
}
#[allow(dead_code)]
pub fn case_of(scrutinee: IdrisExpr, alts: Vec<(IdrisPattern, IdrisExpr)>) -> IdrisExpr {
IdrisExpr::CaseOf(Box::new(scrutinee), alts)
}
#[allow(dead_code)]
pub fn let_chain(bindings: Vec<(String, IdrisExpr)>, body: IdrisExpr) -> IdrisExpr {
bindings.into_iter().rev().fold(body, |acc, (name, val)| {
IdrisExpr::Let(name, Box::new(val), Box::new(acc))
})
}
#[allow(dead_code)]
pub fn lam(params: Vec<impl Into<String>>, body: IdrisExpr) -> IdrisExpr {
IdrisExpr::Lam(
params.into_iter().map(|p| p.into()).collect(),
Box::new(body),
)
}
#[allow(dead_code)]
pub fn do_block(stmts: Vec<IdrisDoStmt>) -> IdrisExpr {
IdrisExpr::Do(stmts)
}
#[allow(dead_code)]
pub fn if_then_else(cond: IdrisExpr, then_e: IdrisExpr, else_e: IdrisExpr) -> IdrisExpr {
IdrisExpr::IfThenElse(Box::new(cond), Box::new(then_e), Box::new(else_e))
}
#[allow(dead_code)]
pub fn tuple(elems: Vec<IdrisExpr>) -> IdrisExpr {
IdrisExpr::Tuple(elems)
}
#[allow(dead_code)]
pub fn list_lit(elems: Vec<IdrisExpr>) -> IdrisExpr {
IdrisExpr::ListLit(elems)
}
#[allow(dead_code)]
pub fn annot(expr: IdrisExpr, ty: IdrisType) -> IdrisExpr {
IdrisExpr::Annot(Box::new(expr), Box::new(ty))
}
#[allow(dead_code)]
pub fn infix(op: impl Into<String>, l: IdrisExpr, r: IdrisExpr) -> IdrisExpr {
IdrisExpr::Infix(op.into(), Box::new(l), Box::new(r))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[allow(dead_code)]
pub enum IdrisAttribute {
Auto,
Interface,
Search,
Totality(Totality),
Inline,
Static,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct IdrisCodegenConfig {
pub emit_docs: bool,
pub emit_logging: bool,
pub default_totality: Totality,
pub auto_inline: bool,
pub auto_inline_limit: usize,
pub emit_name_pragmas: bool,
pub emit_header_comment: bool,
pub target_backend: String,
}
#[derive(Debug, Clone, Default)]
#[allow(dead_code)]
pub struct IdrisModuleMetrics {
pub num_functions: usize,
pub num_data_types: usize,
pub num_records: usize,
pub num_imports: usize,
pub total_clauses: usize,
pub num_mutual_blocks: usize,
pub num_pragmas: usize,
}
impl IdrisModuleMetrics {
#[allow(dead_code)]
pub fn compute(module: &IdrisModule) -> Self {
let mut m = IdrisModuleMetrics {
num_imports: module.imports.len(),
..Default::default()
};
for decl in &module.declarations {
Self::count_decl(decl, &mut m);
}
m
}
pub(super) fn count_decl(decl: &IdrisDecl, m: &mut IdrisModuleMetrics) {
match decl {
IdrisDecl::Func(f) => {
m.num_functions += 1;
m.total_clauses += f.clauses.len();
}
IdrisDecl::Data(_) => {
m.num_data_types += 1;
}
IdrisDecl::Record(_) => {
m.num_records += 1;
}
IdrisDecl::Mutual(decls) => {
m.num_mutual_blocks += 1;
for d in decls {
Self::count_decl(d, m);
}
}
IdrisDecl::Pragma(_) => {
m.num_pragmas += 1;
}
_ => {}
}
}
#[allow(dead_code)]
pub fn summary(&self) -> String {
format!(
"functions={} data_types={} records={} imports={} total_clauses={} mutual_blocks={} pragmas={}",
self.num_functions, self.num_data_types, self.num_records, self.num_imports,
self.total_clauses, self.num_mutual_blocks, self.num_pragmas,
)
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct IdrisProofScript {
pub theorem_name: String,
pub goal_type: IdrisType,
pub tactics: Vec<IdrisTactic>,
}
impl IdrisProofScript {
#[allow(dead_code)]
pub fn new(theorem_name: impl Into<String>, goal_type: IdrisType) -> Self {
IdrisProofScript {
theorem_name: theorem_name.into(),
goal_type,
tactics: Vec::new(),
}
}
#[allow(dead_code)]
pub fn push(&mut self, tactic: IdrisTactic) {
self.tactics.push(tactic);
}
#[allow(dead_code)]
pub fn emit(&self) -> String {
let mut out = format!(
"{} : {}\n{} = ?{}_proof\n",
self.theorem_name, self.goal_type, self.theorem_name, self.theorem_name
);
out.push_str(&format!("-- Proof sketch for {}:\n", self.theorem_name));
for tac in &self.tactics {
out.push_str(&format!("-- {}\n", tac));
}
out
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Quantity {
Zero,
One,
Unrestricted,
}
#[derive(Debug, Clone)]
pub struct IdrisModule {
pub module_name: Vec<String>,
pub imports: Vec<IdrisImport>,
pub declarations: Vec<IdrisDecl>,
pub doc: Option<String>,
}
impl IdrisModule {
pub fn new(name: Vec<String>) -> Self {
IdrisModule {
module_name: name,
imports: vec![],
declarations: vec![],
doc: None,
}
}
pub fn with_doc(mut self, doc: impl Into<String>) -> Self {
self.doc = Some(doc.into());
self
}
pub fn import(&mut self, imp: IdrisImport) {
self.imports.push(imp);
}
pub fn add(&mut self, decl: IdrisDecl) {
self.declarations.push(decl);
}
pub fn blank(&mut self) {
self.declarations.push(IdrisDecl::Blank);
}
}