feat: rename to aiken and add e2e tests for uplc
This commit is contained in:
parent
1ef116fcda
commit
984c253f31
|
@ -1,2 +1 @@
|
||||||
/target
|
/target
|
||||||
*.flat
|
|
|
@ -3,12 +3,12 @@
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ahash"
|
name = "aiken"
|
||||||
version = "0.3.8"
|
version = "0.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"const-random",
|
"anyhow",
|
||||||
|
"clap",
|
||||||
|
"uplc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -40,21 +40,6 @@ version = "1.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg-if"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "chumsky"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8d02796e4586c6c41aeb68eae9bfb4558a522c35f1430c14b40136c3706e09e4"
|
|
||||||
dependencies = [
|
|
||||||
"ahash",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "3.1.18"
|
version = "3.1.18"
|
||||||
|
@ -94,53 +79,14 @@ dependencies = [
|
||||||
"os_str_bytes",
|
"os_str_bytes",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "const-random"
|
|
||||||
version = "0.1.13"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f590d95d011aa80b063ffe3253422ed5aa462af4e9867d43ce8337562bac77c4"
|
|
||||||
dependencies = [
|
|
||||||
"const-random-macro",
|
|
||||||
"proc-macro-hack",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "const-random-macro"
|
|
||||||
version = "0.1.13"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "615f6e27d000a2bffbc7f2f6a8669179378fa27ee4d0a509e985dfc0a7defb40"
|
|
||||||
dependencies = [
|
|
||||||
"getrandom",
|
|
||||||
"lazy_static",
|
|
||||||
"proc-macro-hack",
|
|
||||||
"tiny-keccak",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crunchy"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flat"
|
name = "flat"
|
||||||
version = "0.0.0"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "getrandom"
|
|
||||||
version = "0.2.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"wasi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
|
@ -190,15 +136,6 @@ version = "0.2.126"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "neptune"
|
|
||||||
version = "0.0.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"clap",
|
|
||||||
"uplc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "os_str_bytes"
|
name = "os_str_bytes"
|
||||||
version = "6.0.1"
|
version = "6.0.1"
|
||||||
|
@ -256,12 +193,6 @@ dependencies = [
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro-hack"
|
|
||||||
version = "0.5.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.39"
|
version = "1.0.39"
|
||||||
|
@ -357,15 +288,6 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tiny-keccak"
|
|
||||||
version = "2.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
|
|
||||||
dependencies = [
|
|
||||||
"crunchy",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -374,9 +296,8 @@ checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uplc"
|
name = "uplc"
|
||||||
version = "0.1.0"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chumsky",
|
|
||||||
"flat",
|
"flat",
|
||||||
"hex",
|
"hex",
|
||||||
"peg",
|
"peg",
|
||||||
|
@ -391,12 +312,6 @@ version = "0.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasi"
|
|
||||||
version = "0.10.2+wasi-snapshot-preview1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
|
13
README.md
13
README.md
|
@ -1,22 +1,23 @@
|
||||||
# Neptune
|
# AIKEN
|
||||||
|
|
||||||
Experiments with Plutus Core
|
A cardano smart contract language and toolchain
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
These are generic milestones and the listed ordering
|
These are generic milestones and the listed ordering
|
||||||
is not necessariy the implementation order
|
is not necessariy the implementation order or full scope.
|
||||||
|
|
||||||
- [ ] compile plutus core into it's on chain encoding
|
- [x] compile plutus core into it's on chain encoding
|
||||||
- [ ] reverse the on chain encoding into plutus core
|
- [x] reverse the on chain encoding into plutus core
|
||||||
|
- [ ] Plutus Core interpreter
|
||||||
- [ ] create a higher level syntax with inspiration from
|
- [ ] create a higher level syntax with inspiration from
|
||||||
- JS
|
- JS
|
||||||
- ReasonML
|
- ReasonML
|
||||||
- Elm
|
- Elm
|
||||||
- Roc
|
- Roc
|
||||||
- Rust
|
- Rust
|
||||||
|
- Gleam
|
||||||
- [ ] Language Server
|
- [ ] Language Server
|
||||||
- [ ] Plutus Core interpreter
|
|
||||||
|
|
||||||
## Resources
|
## Resources
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "neptune"
|
name = "aiken"
|
||||||
version = "0.0.0"
|
version = "0.0.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
|
@ -12,8 +12,16 @@ pub enum Cli {
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub enum UplcCommand {
|
pub enum UplcCommand {
|
||||||
Flat { input: PathBuf },
|
Flat {
|
||||||
Unflat { input: PathBuf },
|
input: PathBuf,
|
||||||
|
#[clap(short, long)]
|
||||||
|
print: bool,
|
||||||
|
},
|
||||||
|
Unflat {
|
||||||
|
input: PathBuf,
|
||||||
|
#[clap(short, long)]
|
||||||
|
print: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Cli {
|
impl Default for Cli {
|
||||||
|
|
|
@ -3,14 +3,14 @@ use uplc::{
|
||||||
parser,
|
parser,
|
||||||
};
|
};
|
||||||
|
|
||||||
use neptune::{Cli, UplcCommand};
|
use aiken::{Cli, UplcCommand};
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
let args = Cli::default();
|
let args = Cli::default();
|
||||||
|
|
||||||
match args {
|
match args {
|
||||||
Cli::Uplc(uplc) => match uplc {
|
Cli::Uplc(uplc) => match uplc {
|
||||||
UplcCommand::Flat { input } => {
|
UplcCommand::Flat { input, print } => {
|
||||||
let code = std::fs::read_to_string(&input)?;
|
let code = std::fs::read_to_string(&input)?;
|
||||||
|
|
||||||
let program = parser::program(&code)?;
|
let program = parser::program(&code)?;
|
||||||
|
@ -19,26 +19,28 @@ fn main() -> anyhow::Result<()> {
|
||||||
|
|
||||||
let bytes = program.to_flat()?;
|
let bytes = program.to_flat()?;
|
||||||
|
|
||||||
for (i, byte) in bytes.iter().enumerate() {
|
if print {
|
||||||
print!("{:08b}", byte);
|
for (i, byte) in bytes.iter().enumerate() {
|
||||||
|
print!("{:08b}", byte);
|
||||||
|
|
||||||
if (i + 1) % 4 == 0 {
|
if (i + 1) % 4 == 0 {
|
||||||
println!();
|
println!();
|
||||||
} else {
|
} else {
|
||||||
print!(" ");
|
print!(" ");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
UplcCommand::Unflat { input } => {
|
UplcCommand::Unflat { input, print } => {
|
||||||
let bytes = std::fs::read(&input)?;
|
let bytes = std::fs::read(&input)?;
|
||||||
|
|
||||||
let program = Program::<FakeNamedDeBruijn>::from_flat(&bytes)?;
|
let program = Program::<FakeNamedDeBruijn>::from_flat(&bytes)?;
|
||||||
|
|
||||||
let encoded_flat = program.to_flat()?;
|
if print {
|
||||||
println!("{}", encoded_flat.len());
|
println!("{:#?}", program);
|
||||||
assert!(bytes == encoded_flat)
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "flat"
|
name = "flat"
|
||||||
version = "0.0.0"
|
version = "0.0.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uplc"
|
name = "uplc"
|
||||||
version = "0.1.0"
|
version = "0.0.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chumsky = "0.8.0"
|
|
||||||
flat = { path = "../flat" }
|
flat = { path = "../flat" }
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
peg = "0.8.0"
|
peg = "0.8.0"
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
(program 11.22.33
|
|
||||||
[
|
|
||||||
(
|
|
||||||
lam x (lam x x)) (con string "PT8"
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
|
@ -1,7 +1,8 @@
|
||||||
#![recursion_limit = "10000"]
|
|
||||||
|
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
pub mod builtins;
|
pub mod builtins;
|
||||||
mod debruijn;
|
mod debruijn;
|
||||||
mod flat;
|
mod flat;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test;
|
||||||
|
|
|
@ -1,20 +1,31 @@
|
||||||
use std::{collections::HashMap, str::FromStr};
|
use std::str::FromStr;
|
||||||
|
|
||||||
use chumsky::{
|
|
||||||
prelude::{end, filter, just, recursive, Simple},
|
|
||||||
text::{ident, int, keyword, TextParser},
|
|
||||||
Parser,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{Constant, Name, Program, Term, Unique},
|
ast::{Constant, Name, Program, Term},
|
||||||
builtins::DefaultFunction,
|
builtins::DefaultFunction,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use interner::Interner;
|
||||||
|
use peg::{error::ParseError, str::LineCol};
|
||||||
|
|
||||||
|
mod interner;
|
||||||
|
|
||||||
|
pub fn program(src: &str) -> Result<Program<Name>, ParseError<LineCol>> {
|
||||||
|
let mut interner = Interner::new();
|
||||||
|
|
||||||
|
let mut program = uplc::program(src)?;
|
||||||
|
|
||||||
|
interner.program(&mut program);
|
||||||
|
|
||||||
|
Ok(program)
|
||||||
|
}
|
||||||
|
|
||||||
peg::parser! {
|
peg::parser! {
|
||||||
grammar parser() for str {
|
grammar uplc() for str {
|
||||||
pub rule program() -> Program<Name>
|
pub rule program() -> Program<Name>
|
||||||
= "(" _* "program" _+ v:version() _+ t:term() _* ")" { Program {version: v, term: t} }
|
= _* "(" _* "program" _+ v:version() _+ t:term() _* ")" _* {
|
||||||
|
Program {version: v, term: t}
|
||||||
|
}
|
||||||
|
|
||||||
rule version() -> (usize, usize, usize)
|
rule version() -> (usize, usize, usize)
|
||||||
= major:number() "." minor:number() "." patch:number() {
|
= major:number() "." minor:number() "." patch:number() {
|
||||||
|
@ -43,7 +54,9 @@ peg::parser! {
|
||||||
}
|
}
|
||||||
|
|
||||||
rule builtin() -> Term<Name>
|
rule builtin() -> Term<Name>
|
||||||
= "(" _* "builtin" _+ b:ident() _* ")" { Term::Builtin(DefaultFunction::from_str(&b).unwrap()) }
|
= "(" _* "builtin" _+ b:ident() _* ")" {
|
||||||
|
Term::Builtin(DefaultFunction::from_str(&b).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
rule var() -> Term<Name>
|
rule var() -> Term<Name>
|
||||||
= n:name() { Term::Var(n) }
|
= n:name() { Term::Var(n) }
|
||||||
|
@ -77,7 +90,9 @@ peg::parser! {
|
||||||
= "integer" _+ i:number() { Constant::Integer(i as isize) }
|
= "integer" _+ i:number() { Constant::Integer(i as isize) }
|
||||||
|
|
||||||
rule constant_bytestring() -> Constant
|
rule constant_bytestring() -> Constant
|
||||||
= "bytestring" _+ "#" i:ident()* { Constant::ByteString(hex::decode(String::from_iter(i)).unwrap()) }
|
= "bytestring" _+ "#" i:ident()* {
|
||||||
|
Constant::ByteString(hex::decode(String::from_iter(i)).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
rule constant_string() -> Constant
|
rule constant_string() -> Constant
|
||||||
= "string" _+ "\"" s:[^ '"']* "\"" { Constant::String(String::from_iter(s)) }
|
= "string" _+ "\"" s:[^ '"']* "\"" { Constant::String(String::from_iter(s)) }
|
||||||
|
@ -94,195 +109,19 @@ peg::parser! {
|
||||||
rule name() -> Name
|
rule name() -> Name
|
||||||
= text:ident() { Name { text, unique: 0.into() } }
|
= text:ident() { Name { text, unique: 0.into() } }
|
||||||
|
|
||||||
rule ident() -> String = i:['a'..='z' | 'A'..='Z' | '0'..='9' | '_']+ { String::from_iter(i) }
|
rule ident() -> String
|
||||||
|
= i:['a'..='z' | 'A'..='Z' | '0'..='9' | '_']+ {
|
||||||
|
String::from_iter(i)
|
||||||
|
}
|
||||||
|
|
||||||
rule _ = [' ' | '\n']
|
rule _ = [' ' | '\n']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ParserState {
|
|
||||||
identifiers: HashMap<String, Unique>,
|
|
||||||
current: Unique,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ParserState {
|
|
||||||
fn new() -> Self {
|
|
||||||
ParserState {
|
|
||||||
identifiers: HashMap::new(),
|
|
||||||
current: Unique::new(0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn intern(&mut self, text: &str) -> Unique {
|
|
||||||
if let Some(u) = self.identifiers.get(text) {
|
|
||||||
*u
|
|
||||||
} else {
|
|
||||||
let unique = self.current;
|
|
||||||
|
|
||||||
self.identifiers.insert(text.to_string(), unique);
|
|
||||||
|
|
||||||
self.current.increment();
|
|
||||||
|
|
||||||
unique
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn program(src: &str) -> Result<Program<Name>, peg::error::ParseError<peg::str::LineCol>> {
|
|
||||||
parser::program(src)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn program_() -> impl Parser<char, Program<Name>, Error = Simple<char>> {
|
|
||||||
keyword("program")
|
|
||||||
.ignore_then(version().padded())
|
|
||||||
.then(term())
|
|
||||||
.map(|(version, term)| Program { version, term })
|
|
||||||
.delimited_by(just('(').padded(), just(')').padded())
|
|
||||||
.then_ignore(end())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn version() -> impl Parser<char, (usize, usize, usize), Error = Simple<char>> {
|
|
||||||
int(10)
|
|
||||||
.then_ignore(just('.'))
|
|
||||||
.then(int(10))
|
|
||||||
.then_ignore(just('.'))
|
|
||||||
.then(int(10))
|
|
||||||
.map(|((major, minor), patch)| {
|
|
||||||
(
|
|
||||||
major.parse::<usize>().unwrap(),
|
|
||||||
minor.parse::<usize>().unwrap(),
|
|
||||||
patch.parse::<usize>().unwrap(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn term() -> impl Parser<char, Term<Name>, Error = Simple<char>> {
|
|
||||||
recursive(|term| {
|
|
||||||
let delay = keyword("delay")
|
|
||||||
.ignore_then(term.clone().padded())
|
|
||||||
.delimited_by(just('(').padded(), just(')').padded())
|
|
||||||
.map(|t| dbg!(Term::Delay(Box::new(t))));
|
|
||||||
|
|
||||||
let force = keyword("force")
|
|
||||||
.ignore_then(term.clone().padded())
|
|
||||||
.delimited_by(just('(').padded(), just(')').padded())
|
|
||||||
.map(|t| dbg!(Term::Force(Box::new(t))));
|
|
||||||
|
|
||||||
let lambda = keyword("lam")
|
|
||||||
.ignore_then(name().padded())
|
|
||||||
.then(term.clone())
|
|
||||||
.delimited_by(just('(').padded(), just(')').padded())
|
|
||||||
.map(|(parameter_name, t)| {
|
|
||||||
dbg!(Term::Lambda {
|
|
||||||
parameter_name,
|
|
||||||
body: Box::new(t),
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
let apply = term
|
|
||||||
.clone()
|
|
||||||
.padded()
|
|
||||||
.then(term.clone().padded().repeated())
|
|
||||||
.delimited_by(just('[').padded(), just(']').padded())
|
|
||||||
.foldl(|lhs, rhs| Term::Apply {
|
|
||||||
function: Box::new(lhs),
|
|
||||||
argument: Box::new(rhs),
|
|
||||||
});
|
|
||||||
|
|
||||||
constant()
|
|
||||||
.or(builtin())
|
|
||||||
.or(var())
|
|
||||||
.or(lambda)
|
|
||||||
.or(apply)
|
|
||||||
.or(delay)
|
|
||||||
.or(force)
|
|
||||||
.or(error())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn constant() -> impl Parser<char, Term<Name>, Error = Simple<char>> {
|
|
||||||
keyword("con")
|
|
||||||
.ignore_then(
|
|
||||||
constant_integer()
|
|
||||||
.or(constant_bytestring())
|
|
||||||
.or(constant_string())
|
|
||||||
.or(constant_unit())
|
|
||||||
.or(constant_bool()),
|
|
||||||
)
|
|
||||||
.delimited_by(just('(').padded(), just(')').padded())
|
|
||||||
.map(Term::Constant)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn builtin() -> impl Parser<char, Term<Name>, Error = Simple<char>> {
|
|
||||||
keyword("builtin")
|
|
||||||
.ignore_then(ident().padded())
|
|
||||||
.delimited_by(just('(').padded(), just(')').padded())
|
|
||||||
.map(|builtin_name: String| {
|
|
||||||
Term::Builtin(DefaultFunction::from_str(&builtin_name).unwrap())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn var() -> impl Parser<char, Term<Name>, Error = Simple<char>> {
|
|
||||||
name().map(Term::Var)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn error() -> impl Parser<char, Term<Name>, Error = Simple<char>> {
|
|
||||||
keyword("error")
|
|
||||||
.ignored()
|
|
||||||
.delimited_by(just('(').padded(), just(')').padded())
|
|
||||||
.map(|_| Term::Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name() -> impl Parser<char, Name, Error = Simple<char>> {
|
|
||||||
ident().map(|text| Name {
|
|
||||||
text,
|
|
||||||
unique: 0.into(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn constant_integer() -> impl Parser<char, Constant, Error = Simple<char>> {
|
|
||||||
keyword("integer")
|
|
||||||
.padded()
|
|
||||||
.ignore_then(int(10))
|
|
||||||
.map(|d: String| Constant::Integer(d.parse::<isize>().unwrap()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn constant_bytestring() -> impl Parser<char, Constant, Error = Simple<char>> {
|
|
||||||
keyword("bytestring")
|
|
||||||
.padded()
|
|
||||||
.ignore_then(just('#'))
|
|
||||||
.ignore_then(int(16))
|
|
||||||
.map(|b: String| Constant::ByteString(hex::decode(b).unwrap()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn constant_string() -> impl Parser<char, Constant, Error = Simple<char>> {
|
|
||||||
keyword("string")
|
|
||||||
.padded()
|
|
||||||
.ignore_then(just('"'))
|
|
||||||
.ignore_then(filter(|c| *c != '"').repeated())
|
|
||||||
.then_ignore(just('"'))
|
|
||||||
.collect::<String>()
|
|
||||||
.map(Constant::String)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn constant_unit() -> impl Parser<char, Constant, Error = Simple<char>> {
|
|
||||||
keyword("unit")
|
|
||||||
.padded()
|
|
||||||
.ignore_then(just('('))
|
|
||||||
.ignore_then(just(')'))
|
|
||||||
.ignored()
|
|
||||||
.map(|_| Constant::Unit)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn constant_bool() -> impl Parser<char, Constant, Error = Simple<char>> {
|
|
||||||
keyword("bool")
|
|
||||||
.padded()
|
|
||||||
.ignore_then(just("True").or(just("False")))
|
|
||||||
.map(|b| Constant::Bool(b == "True"))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use crate::ast::{Constant, Name, Program, Term};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_program() {
|
fn parse_program() {
|
||||||
let code = r#"
|
let code = r#"
|
||||||
|
@ -290,12 +129,14 @@ mod test {
|
||||||
(con integer 11)
|
(con integer 11)
|
||||||
)
|
)
|
||||||
"#;
|
"#;
|
||||||
let result = super::program(code);
|
let program = super::program(code).unwrap();
|
||||||
|
|
||||||
assert!(result.is_ok());
|
assert_eq!(
|
||||||
|
program,
|
||||||
let program = result.unwrap();
|
Program::<Name> {
|
||||||
|
version: (11, 22, 33),
|
||||||
assert_eq!(program.version, (11, 22, 33));
|
term: Term::Constant(Constant::Integer(11)),
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::ast::{Name, Program, Term, Unique};
|
||||||
|
|
||||||
|
pub struct Interner {
|
||||||
|
identifiers: HashMap<String, Unique>,
|
||||||
|
current: Unique,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interner {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Interner {
|
||||||
|
identifiers: HashMap::new(),
|
||||||
|
current: Unique::new(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn program(&mut self, program: &mut Program<Name>) {
|
||||||
|
self.term(&mut program.term);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn term(&mut self, term: &mut Term<Name>) {
|
||||||
|
match term {
|
||||||
|
Term::Var(name) => name.unique = self.intern(&name.text),
|
||||||
|
Term::Delay(term) => self.term(term),
|
||||||
|
Term::Lambda {
|
||||||
|
parameter_name,
|
||||||
|
body,
|
||||||
|
} => {
|
||||||
|
parameter_name.unique = self.intern(¶meter_name.text);
|
||||||
|
self.term(body);
|
||||||
|
}
|
||||||
|
Term::Apply { function, argument } => {
|
||||||
|
self.term(function);
|
||||||
|
self.term(argument);
|
||||||
|
}
|
||||||
|
Term::Constant(_) => (),
|
||||||
|
Term::Force(term) => self.term(term),
|
||||||
|
Term::Error => (),
|
||||||
|
Term::Builtin(_) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn intern(&mut self, text: &str) -> Unique {
|
||||||
|
if let Some(u) = self.identifiers.get(text) {
|
||||||
|
*u
|
||||||
|
} else {
|
||||||
|
let unique = self.current;
|
||||||
|
|
||||||
|
self.identifiers.insert(text.to_string(), unique);
|
||||||
|
|
||||||
|
self.current.increment();
|
||||||
|
|
||||||
|
unique
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
// e2e encoding/decoding tests
|
||||||
|
use crate::{
|
||||||
|
ast::{DeBruijn, Program},
|
||||||
|
parser,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn integer() {
|
||||||
|
let bytes = include_bytes!("../test_data/basic/integer/integer.flat");
|
||||||
|
let code = include_str!("../test_data/basic/integer/integer.uplc");
|
||||||
|
|
||||||
|
let parsed_program = parser::program(code).unwrap();
|
||||||
|
|
||||||
|
let debruijn_program: Program<DeBruijn> = parsed_program.try_into().unwrap();
|
||||||
|
|
||||||
|
let decoded_program: Program<DeBruijn> = Program::from_flat(bytes).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(debruijn_program, decoded_program);
|
||||||
|
|
||||||
|
let encoded_program = debruijn_program.to_flat().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(encoded_program, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn jpg() {
|
||||||
|
let bytes = include_bytes!("../test_data/jpg/jpg.flat");
|
||||||
|
let code = include_str!("../test_data/jpg/jpg.uplc");
|
||||||
|
|
||||||
|
let parsed_program = parser::program(code).unwrap();
|
||||||
|
|
||||||
|
let debruijn_program: Program<DeBruijn> = parsed_program.try_into().unwrap();
|
||||||
|
|
||||||
|
let decoded_program: Program<DeBruijn> = Program::from_flat(bytes).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(debruijn_program, decoded_program);
|
||||||
|
|
||||||
|
let encoded_program = debruijn_program.to_flat().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(encoded_program, bytes);
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
!H<05>
|
|
@ -0,0 +1,3 @@
|
||||||
|
(program 11.22.33
|
||||||
|
(con integer 11)
|
||||||
|
)
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue