feat: some boilerplate for typechecking
This commit is contained in:
parent
ed2ef4fa9b
commit
4df3de0a03
|
@ -4,7 +4,7 @@ use std::{
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
};
|
};
|
||||||
|
|
||||||
use aiken_lang::error::ParseError;
|
use aiken_lang::{error::ParseError, tipo};
|
||||||
use miette::{EyreContext, LabeledSpan, MietteHandlerOpts, RgbColors, SourceCode};
|
use miette::{EyreContext, LabeledSpan, MietteHandlerOpts, RgbColors, SourceCode};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -16,10 +16,17 @@ pub enum Error {
|
||||||
first: PathBuf,
|
first: PathBuf,
|
||||||
second: PathBuf,
|
second: PathBuf,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("file operation failed")]
|
#[error("file operation failed")]
|
||||||
FileIo { error: io::Error, path: PathBuf },
|
FileIo { error: io::Error, path: PathBuf },
|
||||||
|
|
||||||
#[error("cyclical module imports")]
|
#[error("cyclical module imports")]
|
||||||
ImportCycle { modules: Vec<String> },
|
ImportCycle { modules: Vec<String> },
|
||||||
|
|
||||||
|
/// Useful for returning many [`Error::Parse`] at once
|
||||||
|
#[error("a list of errors")]
|
||||||
|
List(Vec<Self>),
|
||||||
|
|
||||||
#[error("failed to parse")]
|
#[error("failed to parse")]
|
||||||
Parse {
|
Parse {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
|
@ -29,9 +36,13 @@ pub enum Error {
|
||||||
#[source]
|
#[source]
|
||||||
error: Box<ParseError>,
|
error: Box<ParseError>,
|
||||||
},
|
},
|
||||||
/// Useful for returning many [`Error::Parse`] at once
|
|
||||||
#[error("a list of errors")]
|
#[error("type checking failed")]
|
||||||
List(Vec<Self>),
|
Type {
|
||||||
|
path: PathBuf,
|
||||||
|
src: String,
|
||||||
|
error: tipo::error::Error,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Error {
|
impl Debug for Error {
|
||||||
|
@ -56,11 +67,12 @@ impl Debug for Error {
|
||||||
impl miette::Diagnostic for Error {
|
impl miette::Diagnostic for Error {
|
||||||
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
|
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
|
||||||
match self {
|
match self {
|
||||||
Error::DuplicateModule { .. } => Some(Box::new("aiken::project::duplicate_module")),
|
Error::DuplicateModule { .. } => Some(Box::new("aiken::module::duplicate")),
|
||||||
Error::FileIo { .. } => None,
|
Error::FileIo { .. } => None,
|
||||||
Error::ImportCycle { .. } => Some(Box::new("aiken::project::cyclical_import")),
|
Error::ImportCycle { .. } => Some(Box::new("aiken::module::cyclical")),
|
||||||
Error::Parse { .. } => Some(Box::new("aiken::parser")),
|
|
||||||
Error::List(_) => None,
|
Error::List(_) => None,
|
||||||
|
Error::Parse { .. } => Some(Box::new("aiken::parser")),
|
||||||
|
Error::Type { .. } => Some(Box::new("aiken::typecheck")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,8 +88,9 @@ impl miette::Diagnostic for Error {
|
||||||
"try moving the shared code to a separate module that the others can depend on\n- {}",
|
"try moving the shared code to a separate module that the others can depend on\n- {}",
|
||||||
modules.join("\n- ")
|
modules.join("\n- ")
|
||||||
))),
|
))),
|
||||||
Error::Parse { error, .. } => error.kind.help(),
|
|
||||||
Error::List(_) => None,
|
Error::List(_) => None,
|
||||||
|
Error::Parse { error, .. } => error.kind.help(),
|
||||||
|
Error::Type { .. } => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,8 +99,9 @@ impl miette::Diagnostic for Error {
|
||||||
Error::DuplicateModule { .. } => None,
|
Error::DuplicateModule { .. } => None,
|
||||||
Error::FileIo { .. } => None,
|
Error::FileIo { .. } => None,
|
||||||
Error::ImportCycle { .. } => None,
|
Error::ImportCycle { .. } => None,
|
||||||
Error::Parse { error, .. } => error.labels(),
|
|
||||||
Error::List(_) => None,
|
Error::List(_) => None,
|
||||||
|
Error::Parse { error, .. } => error.labels(),
|
||||||
|
Error::Type { error, .. } => error.labels(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,8 +110,9 @@ impl miette::Diagnostic for Error {
|
||||||
Error::DuplicateModule { .. } => None,
|
Error::DuplicateModule { .. } => None,
|
||||||
Error::FileIo { .. } => None,
|
Error::FileIo { .. } => None,
|
||||||
Error::ImportCycle { .. } => None,
|
Error::ImportCycle { .. } => None,
|
||||||
Error::Parse { src, .. } => Some(src),
|
|
||||||
Error::List(_) => None,
|
Error::List(_) => None,
|
||||||
|
Error::Parse { src, .. } => Some(src),
|
||||||
|
Error::Type { src, .. } => Some(src),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,3 +2,6 @@ pub mod config;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod module;
|
pub mod module;
|
||||||
pub mod project;
|
pub mod project;
|
||||||
|
|
||||||
|
pub use aiken_lang;
|
||||||
|
pub use uplc;
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
ops::Deref,
|
ops::{Deref, DerefMut},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
};
|
};
|
||||||
|
|
||||||
use aiken_lang::ast::{ModuleKind, UntypedModule};
|
use aiken_lang::ast::{ModuleKind, TypedModule, UntypedModule};
|
||||||
use petgraph::{algo, graph::NodeIndex, Direction, Graph};
|
use petgraph::{algo, graph::NodeIndex, Direction, Graph};
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct ParsedModule {
|
pub struct ParsedModule {
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
@ -120,6 +119,12 @@ impl Deref for ParsedModules {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DerefMut for ParsedModules {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn find_cycle(
|
fn find_cycle(
|
||||||
origin: NodeIndex,
|
origin: NodeIndex,
|
||||||
parent: NodeIndex,
|
parent: NodeIndex,
|
||||||
|
@ -149,3 +154,13 @@ fn find_cycle(
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CheckedModule {
|
||||||
|
pub name: String,
|
||||||
|
pub code: String,
|
||||||
|
pub input_path: PathBuf,
|
||||||
|
pub kind: ModuleKind,
|
||||||
|
pub ast: TypedModule,
|
||||||
|
// pub extra: ModuleExtra,
|
||||||
|
}
|
||||||
|
|
|
@ -4,12 +4,12 @@ use std::{
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
use aiken_lang::ast::ModuleKind;
|
use aiken_lang::{ast::ModuleKind, builtins, tipo};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::Config,
|
config::Config,
|
||||||
error::Error,
|
error::Error,
|
||||||
module::{ParsedModule, ParsedModules},
|
module::{CheckedModule, ParsedModule, ParsedModules},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -20,20 +20,43 @@ pub struct Source {
|
||||||
pub kind: ModuleKind,
|
pub kind: ModuleKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum Warning {
|
||||||
|
Type {
|
||||||
|
path: PathBuf,
|
||||||
|
src: String,
|
||||||
|
warning: tipo::error::Warning,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Warning {
|
||||||
|
pub fn from_type_warning(warning: tipo::error::Warning, path: PathBuf, src: String) -> Warning {
|
||||||
|
Warning::Type { path, warning, src }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Project {
|
pub struct Project {
|
||||||
config: Config,
|
config: Config,
|
||||||
|
defined_modules: HashMap<String, PathBuf>,
|
||||||
|
module_types: HashMap<String, tipo::Module>,
|
||||||
root: PathBuf,
|
root: PathBuf,
|
||||||
sources: Vec<Source>,
|
sources: Vec<Source>,
|
||||||
defined_modules: HashMap<String, PathBuf>,
|
warnings: Vec<Warning>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Project {
|
impl Project {
|
||||||
pub fn new(config: Config, root: PathBuf) -> Project {
|
pub fn new(config: Config, root: PathBuf) -> Project {
|
||||||
|
let mut module_types = HashMap::new();
|
||||||
|
|
||||||
|
module_types.insert("aiken".to_string(), builtins::prelude());
|
||||||
|
|
||||||
Project {
|
Project {
|
||||||
config,
|
config,
|
||||||
|
defined_modules: HashMap::new(),
|
||||||
|
module_types,
|
||||||
root,
|
root,
|
||||||
sources: vec![],
|
sources: vec![],
|
||||||
defined_modules: HashMap::new(),
|
warnings: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +67,20 @@ impl Project {
|
||||||
|
|
||||||
let processing_sequence = parsed_modules.sequence()?;
|
let processing_sequence = parsed_modules.sequence()?;
|
||||||
|
|
||||||
|
let checked_modules = self.type_check(parsed_modules, processing_sequence)?;
|
||||||
|
|
||||||
|
println!("{:?}", checked_modules);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_source_files(&mut self) -> Result<(), Error> {
|
||||||
|
let lib = self.root.join("lib");
|
||||||
|
let scripts = self.root.join("scripts");
|
||||||
|
|
||||||
|
self.aiken_files(&scripts, ModuleKind::Script)?;
|
||||||
|
self.aiken_files(&lib, ModuleKind::Lib)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,14 +141,62 @@ impl Project {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_source_files(&mut self) -> Result<(), Error> {
|
fn type_check(
|
||||||
let lib = self.root.join("lib");
|
&mut self,
|
||||||
let scripts = self.root.join("scripts");
|
mut parsed_modules: ParsedModules,
|
||||||
|
processing_sequence: Vec<String>,
|
||||||
|
) -> Result<Vec<CheckedModule>, Error> {
|
||||||
|
let mut modules = Vec::with_capacity(parsed_modules.len() + 1);
|
||||||
|
|
||||||
self.aiken_files(&scripts, ModuleKind::Script)?;
|
for name in processing_sequence {
|
||||||
self.aiken_files(&lib, ModuleKind::Lib)?;
|
if let Some(ParsedModule {
|
||||||
|
name,
|
||||||
|
path,
|
||||||
|
code,
|
||||||
|
kind,
|
||||||
|
package,
|
||||||
|
ast,
|
||||||
|
}) = parsed_modules.remove(&name)
|
||||||
|
{
|
||||||
|
let mut type_warnings = Vec::new();
|
||||||
|
|
||||||
Ok(())
|
let ast = tipo::infer::module(
|
||||||
|
ast,
|
||||||
|
kind,
|
||||||
|
&self.config.name,
|
||||||
|
&self.module_types,
|
||||||
|
&mut type_warnings,
|
||||||
|
)
|
||||||
|
.map_err(|error| Error::Type {
|
||||||
|
path: path.clone(),
|
||||||
|
src: code.clone(),
|
||||||
|
error,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Register any warnings emitted as type warnings
|
||||||
|
let type_warnings = type_warnings
|
||||||
|
.into_iter()
|
||||||
|
.map(|w| Warning::from_type_warning(w, path.clone(), code.clone()));
|
||||||
|
|
||||||
|
self.warnings.extend(type_warnings);
|
||||||
|
|
||||||
|
// Register the types from this module so they can be imported into
|
||||||
|
// other modules.
|
||||||
|
self.module_types
|
||||||
|
.insert(name.clone(), ast.type_info.clone());
|
||||||
|
|
||||||
|
modules.push(CheckedModule {
|
||||||
|
kind,
|
||||||
|
// extra,
|
||||||
|
name,
|
||||||
|
code,
|
||||||
|
ast,
|
||||||
|
input_path: path,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(modules)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn aiken_files(&mut self, dir: &Path, kind: ModuleKind) -> Result<(), Error> {
|
fn aiken_files(&mut self, dir: &Path, kind: ModuleKind) -> Result<(), Error> {
|
||||||
|
|
|
@ -481,6 +481,7 @@ pub struct IfBranch<Expr> {
|
||||||
pub location: Span,
|
pub location: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct TypedRecordUpdateArg {
|
pub struct TypedRecordUpdateArg {
|
||||||
pub label: String,
|
pub label: String,
|
||||||
pub location: Span,
|
pub location: Span,
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
pub enum Origin {
|
|
||||||
Src,
|
|
||||||
Test,
|
|
||||||
}
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::{ast::ModuleKind, tipo};
|
||||||
|
|
||||||
|
pub fn prelude() -> tipo::Module {
|
||||||
|
let mut prelude = tipo::Module {
|
||||||
|
name: vec!["gleam".to_string()],
|
||||||
|
package: "".to_string(),
|
||||||
|
kind: ModuleKind::Lib,
|
||||||
|
types: HashMap::new(),
|
||||||
|
types_constructors: HashMap::new(),
|
||||||
|
values: HashMap::new(),
|
||||||
|
accessors: HashMap::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
prelude
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ use crate::{
|
||||||
tipo::{ModuleValueConstructor, PatternConstructor, Type, ValueConstructor},
|
tipo::{ModuleValueConstructor, PatternConstructor, Type, ValueConstructor},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum TypedExpr {
|
pub enum TypedExpr {
|
||||||
Int {
|
Int {
|
||||||
location: Span,
|
location: Span,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
pub mod build;
|
pub mod builtins;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod expr;
|
pub mod expr;
|
||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::{cell::RefCell, collections::HashMap, sync::Arc};
|
use std::{cell::RefCell, collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
use crate::{
|
use crate::ast::{Constant, FieldMap, ModuleKind, Span, TypedConstant};
|
||||||
ast::{Constant, FieldMap, Span, TypedConstant},
|
|
||||||
build::Origin,
|
pub mod error;
|
||||||
};
|
pub mod infer;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
|
@ -105,9 +105,10 @@ pub enum ValueConstructorVariant {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Module {
|
pub struct Module {
|
||||||
pub name: Vec<String>,
|
pub name: Vec<String>,
|
||||||
pub origin: Origin,
|
pub kind: ModuleKind,
|
||||||
pub package: String,
|
pub package: String,
|
||||||
pub types: HashMap<String, TypeConstructor>,
|
pub types: HashMap<String, TypeConstructor>,
|
||||||
pub types_constructors: HashMap<String, Vec<String>>,
|
pub types_constructors: HashMap<String, Vec<String>>,
|
||||||
|
@ -115,6 +116,7 @@ pub struct Module {
|
||||||
pub accessors: HashMap<String, AccessorsMap>,
|
pub accessors: HashMap<String, AccessorsMap>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct TypeConstructor {
|
pub struct TypeConstructor {
|
||||||
pub public: bool,
|
pub public: bool,
|
||||||
pub origin: Span,
|
pub origin: Span,
|
||||||
|
@ -123,12 +125,14 @@ pub struct TypeConstructor {
|
||||||
pub typ: Arc<Type>,
|
pub typ: Arc<Type>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct AccessorsMap {
|
pub struct AccessorsMap {
|
||||||
pub public: bool,
|
pub public: bool,
|
||||||
pub tipo: Arc<Type>,
|
pub tipo: Arc<Type>,
|
||||||
pub accessors: HashMap<String, RecordAccessor>,
|
pub accessors: HashMap<String, RecordAccessor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct RecordAccessor {
|
pub struct RecordAccessor {
|
||||||
// TODO: smaller int. Doesn't need to be this big
|
// TODO: smaller int. Doesn't need to be this big
|
||||||
pub index: u64,
|
pub index: u64,
|
||||||
|
@ -136,6 +140,7 @@ pub struct RecordAccessor {
|
||||||
pub tipo: Arc<Type>,
|
pub tipo: Arc<Type>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum PatternConstructor {
|
pub enum PatternConstructor {
|
||||||
Record {
|
Record {
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -143,6 +148,7 @@ pub enum PatternConstructor {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum ModuleValueConstructor {
|
pub enum ModuleValueConstructor {
|
||||||
Record {
|
Record {
|
||||||
name: String,
|
name: String,
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use miette::Diagnostic;
|
||||||
|
|
||||||
|
use crate::ast::{Span, TodoKind};
|
||||||
|
|
||||||
|
use super::Type;
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error, Diagnostic)]
|
||||||
|
pub enum Error {}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum Warning {
|
||||||
|
Todo {
|
||||||
|
kind: TodoKind,
|
||||||
|
location: Span,
|
||||||
|
typ: Arc<Type>,
|
||||||
|
},
|
||||||
|
|
||||||
|
ImplicitlyDiscardedResult {
|
||||||
|
location: Span,
|
||||||
|
},
|
||||||
|
|
||||||
|
UnusedLiteral {
|
||||||
|
location: Span,
|
||||||
|
},
|
||||||
|
|
||||||
|
NoFieldsRecordUpdate {
|
||||||
|
location: Span,
|
||||||
|
},
|
||||||
|
|
||||||
|
AllFieldsRecordUpdate {
|
||||||
|
location: Span,
|
||||||
|
},
|
||||||
|
|
||||||
|
UnusedType {
|
||||||
|
location: Span,
|
||||||
|
imported: bool,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
UnusedConstructor {
|
||||||
|
location: Span,
|
||||||
|
imported: bool,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
UnusedImportedValue {
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
UnusedImportedModule {
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
UnusedPrivateModuleConstant {
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
UnusedPrivateFunction {
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
UnusedVariable {
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::ast::{ModuleKind, TypedModule, UntypedModule};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
error::{Error, Warning},
|
||||||
|
Module,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn module(
|
||||||
|
// ids: &UniqueIdGenerator,
|
||||||
|
mut module: UntypedModule,
|
||||||
|
kind: ModuleKind,
|
||||||
|
package: &str,
|
||||||
|
modules: &HashMap<String, Module>,
|
||||||
|
warnings: &mut Vec<Warning>,
|
||||||
|
) -> Result<TypedModule, Error> {
|
||||||
|
todo!()
|
||||||
|
}
|
|
@ -5,7 +5,7 @@ pub type Datum {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Redeemer {
|
pub type Redeemer {
|
||||||
Buy,
|
Buy
|
||||||
Sell
|
Sell
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue