174 lines
4.7 KiB
Rust
174 lines
4.7 KiB
Rust
use std::{rc::Rc, str::FromStr};
|
|
|
|
use crate::{
|
|
ast::{Constant, Name, Program, Term},
|
|
builtins::DefaultFunction,
|
|
};
|
|
|
|
use interner::Interner;
|
|
use pallas_primitives::{alonzo::PlutusData, Fragment};
|
|
use peg::{error::ParseError, str::LineCol};
|
|
|
|
mod interner;
|
|
|
|
/// Parse a `Program` from a str.
|
|
pub fn program(src: &str) -> Result<Program<Name>, ParseError<LineCol>> {
|
|
// initialize the string interner to get unique name
|
|
let mut interner = Interner::new();
|
|
|
|
// run the generated parser
|
|
let mut program = uplc::program(src)?;
|
|
|
|
// assign proper unique ids in place
|
|
interner.program(&mut program);
|
|
|
|
Ok(program)
|
|
}
|
|
|
|
pub fn term(src: &str) -> Result<Term<Name>, ParseError<LineCol>> {
|
|
// initialize the string interner to get unique name
|
|
let mut interner = Interner::new();
|
|
|
|
// run the generated parser
|
|
let mut term = uplc::term(src)?;
|
|
|
|
// assign proper unique ids in place
|
|
interner.term(&mut term);
|
|
|
|
Ok(term)
|
|
}
|
|
|
|
peg::parser! {
|
|
grammar uplc() for str {
|
|
pub rule program() -> Program<Name>
|
|
= _* "(" _* "program" _+ v:version() _+ t:term() _* ")" _* {
|
|
Program {version: v, term: t}
|
|
}
|
|
|
|
rule version() -> (usize, usize, usize)
|
|
= major:number() "." minor:number() "." patch:number() {
|
|
(major as usize, minor as usize, patch as usize)
|
|
}
|
|
|
|
pub rule term() -> Term<Name>
|
|
= constant()
|
|
/ builtin()
|
|
/ var()
|
|
/ lambda()
|
|
/ apply()
|
|
/ delay()
|
|
/ force()
|
|
/ error()
|
|
|
|
rule constant() -> Term<Name>
|
|
= "(" _* "con" _+ con:(
|
|
constant_integer()
|
|
/ constant_bytestring()
|
|
/ constant_string()
|
|
/ constant_unit()
|
|
/ constant_bool()
|
|
/ constant_data()
|
|
) _* ")" {
|
|
Term::Constant(con)
|
|
}
|
|
|
|
rule builtin() -> Term<Name>
|
|
= "(" _* "builtin" _+ b:ident() _* ")" {
|
|
Term::Builtin(DefaultFunction::from_str(&b).unwrap())
|
|
}
|
|
|
|
rule var() -> Term<Name>
|
|
= n:name() { Term::Var(n) }
|
|
|
|
rule lambda() -> Term<Name>
|
|
= "(" _* "lam" _+ parameter_name:name() _+ t:term() _* ")" {
|
|
Term::Lambda { parameter_name, body: Rc::new(t) }
|
|
}
|
|
|
|
#[cache_left_rec]
|
|
rule apply() -> Term<Name>
|
|
= "[" _* initial:term() _+ terms:(t:term() _* { t })+ "]" {
|
|
terms
|
|
.into_iter()
|
|
.fold(initial, |lhs, rhs| Term::Apply {
|
|
function: Rc::new(lhs),
|
|
argument: Rc::new(rhs)
|
|
})
|
|
}
|
|
|
|
rule delay() -> Term<Name>
|
|
= "(" _* "delay" _* t:term() _* ")" { Term::Delay(Rc::new(t)) }
|
|
|
|
rule force() -> Term<Name>
|
|
= "(" _* "force" _* t:term() _* ")" { Term::Force(Rc::new(t)) }
|
|
|
|
rule error() -> Term<Name>
|
|
= "(" _* "error" _* ")" { Term::Error }
|
|
|
|
rule constant_integer() -> Constant
|
|
= "integer" _+ i:big_number() { Constant::Integer(i as i128) }
|
|
|
|
rule constant_bytestring() -> Constant
|
|
= "bytestring" _+ "#" i:ident()* {
|
|
Constant::ByteString(hex::decode(String::from_iter(i)).unwrap())
|
|
}
|
|
|
|
rule constant_string() -> Constant
|
|
= "string" _+ "\"" s:[^ '"']* "\"" { Constant::String(String::from_iter(s)) }
|
|
|
|
rule constant_bool() -> Constant
|
|
= "bool" _+ b:$("True" / "False") { Constant::Bool(b == "True") }
|
|
|
|
rule constant_unit() -> Constant
|
|
= "unit" _+ "()" { Constant::Unit }
|
|
|
|
rule number() -> isize
|
|
= n:$("-"* ['0'..='9']+) {? n.parse().or(Err("isize")) }
|
|
|
|
rule big_number() -> i128
|
|
= n:$("-"* ['0'..='9']+) {? n.parse().or(Err("i128")) }
|
|
|
|
rule constant_data() -> Constant
|
|
= "data" _+ "#" i:ident()* {
|
|
Constant::Data(
|
|
PlutusData::decode_fragment(
|
|
hex::decode(String::from_iter(i)).unwrap().as_slice()
|
|
).unwrap()
|
|
)
|
|
}
|
|
|
|
rule name() -> Name
|
|
= text:ident() { Name { text, unique: 0.into() } }
|
|
|
|
rule ident() -> String
|
|
= i:['a'..='z' | 'A'..='Z' | '0'..='9' | '_']+ {
|
|
String::from_iter(i)
|
|
}
|
|
|
|
rule _ = [' ' | '\n']
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use crate::ast::{Constant, Name, Program, Term};
|
|
|
|
#[test]
|
|
fn parse_program() {
|
|
let code = r#"
|
|
(program 11.22.33
|
|
(con integer 11)
|
|
)
|
|
"#;
|
|
let program = super::program(code).unwrap();
|
|
|
|
assert_eq!(
|
|
program,
|
|
Program::<Name> {
|
|
version: (11, 22, 33),
|
|
term: Term::Constant(Constant::Integer(11)),
|
|
}
|
|
);
|
|
}
|
|
}
|