mirror of
https://github.com/HigherOrderCO/Bend.git
synced 2024-11-04 19:43:51 +03:00
Merge pull request #288 from HigherOrderCO/feature/sc-662/add-logic-for-fold-and-bend-syntax2
[sc-662] Add fold and bend to bend syntax
This commit is contained in:
commit
d41ddfa363
@ -67,7 +67,7 @@ impl<'a> TermParser<'a> {
|
||||
// adt declaration
|
||||
let (nam, adt) = self.parse_datatype(builtin)?;
|
||||
let end_idx = *self.index();
|
||||
book.add_adt(nam, adt).map_err(|e| add_ctx_to_msg(&e, ini_idx, end_idx, self.input()))?;
|
||||
self.with_ctx(book.add_adt(nam, adt), ini_idx, end_idx)?;
|
||||
} else {
|
||||
// function declaration rule
|
||||
let (name, rule) = self.parse_rule()?;
|
||||
@ -499,8 +499,8 @@ impl<'a> TermParser<'a> {
|
||||
let nam = self.parse_bend_name()?;
|
||||
let end_idx = *self.index();
|
||||
if nam.contains("__") {
|
||||
let ctx = highlight_error(ini_idx, end_idx, self.input());
|
||||
Err(format!("Top-level names are not allowed to contain \"__\".\n{ctx}"))
|
||||
let msg = "Top-level names are not allowed to contain \"__\".".to_string();
|
||||
self.with_ctx(Err(msg), ini_idx, end_idx)
|
||||
} else {
|
||||
Ok(nam)
|
||||
}
|
||||
@ -523,22 +523,22 @@ impl<'a> TermParser<'a> {
|
||||
/// Parses a tag where it may or may not be valid.
|
||||
///
|
||||
/// If it is not valid, the returned callback can be used to issue an error.
|
||||
fn parse_tag(&mut self) -> Result<(Option<Tag>, impl FnOnce(&Self) -> Result<(), String>), String> {
|
||||
fn parse_tag(&mut self) -> Result<(Option<Tag>, impl FnOnce(&mut Self) -> Result<(), String>), String> {
|
||||
let index = self.index;
|
||||
let tag = if self.skip_peek_one() == Some('#')
|
||||
&& !self.peek_many(2).is_some_and(|x| x.chars().nth(1).unwrap().is_ascii_digit())
|
||||
{
|
||||
let ctx = highlight_error(index, index + 1, self.input);
|
||||
return Err(format!("Tagged terms not supported for hvm32.\n{ctx}"));
|
||||
let msg = "Tagged terms not supported for hvm32.".to_string();
|
||||
return self.with_ctx(Err(msg), index, index + 1);
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let end_index = self.index;
|
||||
let has_tag = tag.is_some();
|
||||
Ok((tag, move |slf: &Self| {
|
||||
Ok((tag, move |slf: &mut Self| {
|
||||
if has_tag {
|
||||
let ctx = highlight_error(index, end_index, slf.input);
|
||||
Err(format!("\x1b[1m- unexpected tag:\x1b[0m{}", ctx))
|
||||
let msg = "\x1b[1m- unexpected tag:\x1b[0m".to_string();
|
||||
slf.with_ctx(Err(msg), index, end_index)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
@ -606,112 +606,9 @@ impl<'a> TermParser<'a> {
|
||||
}
|
||||
|
||||
fn num_range_err<T>(&mut self, ini_idx: usize, typ: &str) -> Result<T, String> {
|
||||
let ctx = highlight_error(ini_idx, *self.index(), self.input());
|
||||
Err(format!("\x1b[1mNumber literal outside of range for {}.\x1b[0m\n{}", typ, ctx))
|
||||
}
|
||||
|
||||
/* Utils */
|
||||
|
||||
/// Checks if the next characters in the input start with the given string.
|
||||
/// Skips trivia.
|
||||
fn skip_starts_with(&mut self, text: &str) -> bool {
|
||||
self.skip_trivia();
|
||||
self.starts_with(text)
|
||||
}
|
||||
|
||||
fn skip_peek_one(&mut self) -> Option<char> {
|
||||
self.skip_trivia();
|
||||
self.peek_one()
|
||||
}
|
||||
|
||||
/// Parses a list-like structure like "[x1, x2, x3,]".
|
||||
///
|
||||
/// `parser` is a function that parses an element of the list.
|
||||
///
|
||||
/// If `hard_sep` the separator between elements is mandatory.
|
||||
/// Always accepts trailing separators.
|
||||
///
|
||||
/// `min_els` determines how many elements must be parsed at minimum.
|
||||
fn list_like<T>(
|
||||
&mut self,
|
||||
parser: impl Fn(&mut Self) -> Result<T, String>,
|
||||
start: &str,
|
||||
end: &str,
|
||||
sep: &str,
|
||||
hard_sep: bool,
|
||||
min_els: usize,
|
||||
) -> Result<Vec<T>, String> {
|
||||
self.consume(start)?;
|
||||
let mut els = vec![];
|
||||
for i in 0 .. min_els {
|
||||
els.push(parser(self)?);
|
||||
if hard_sep && !(i == min_els - 1 && self.skip_starts_with(end)) {
|
||||
self.consume(sep)?;
|
||||
} else {
|
||||
self.try_consume(sep);
|
||||
}
|
||||
}
|
||||
|
||||
while !self.try_consume(end) {
|
||||
els.push(parser(self)?);
|
||||
if hard_sep && !self.skip_starts_with(end) {
|
||||
self.consume(sep)?;
|
||||
} else {
|
||||
self.try_consume(sep);
|
||||
}
|
||||
}
|
||||
Ok(els)
|
||||
}
|
||||
|
||||
fn labelled<T>(
|
||||
&mut self,
|
||||
parser: impl Fn(&mut Self) -> Result<T, String>,
|
||||
label: &str,
|
||||
) -> Result<T, String> {
|
||||
match parser(self) {
|
||||
Ok(val) => Ok(val),
|
||||
Err(_) => self.expected(label),
|
||||
}
|
||||
}
|
||||
|
||||
fn expected_spanned<T>(&mut self, exp: &str, ini_idx: usize, end_idx: usize) -> Result<T, String> {
|
||||
let ctx = highlight_error(ini_idx, end_idx, self.input());
|
||||
let is_eof = self.is_eof();
|
||||
let detected = DisplayFn(|f| if is_eof { write!(f, " end of input") } else { write!(f, "\n{ctx}") });
|
||||
Err(format!("\x1b[1m- expected:\x1b[0m {}\n\x1b[1m- detected:\x1b[0m{}", exp, detected))
|
||||
}
|
||||
|
||||
/// Consumes text if the input starts with it. Otherwise, do nothing.
|
||||
fn try_consume(&mut self, text: &str) -> bool {
|
||||
self.skip_trivia();
|
||||
if self.starts_with(text) {
|
||||
self.consume(text).unwrap();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_u32(&mut self) -> Result<u32, String> {
|
||||
self.skip_trivia();
|
||||
let radix = match self.peek_many(2) {
|
||||
Some("0x") => {
|
||||
self.advance_many(2);
|
||||
16
|
||||
}
|
||||
Some("0b") => {
|
||||
self.advance_many(2);
|
||||
2
|
||||
}
|
||||
_ => 10,
|
||||
};
|
||||
let num_str = self.take_while(move |c| c.is_digit(radix) || c == '_');
|
||||
let num_str = num_str.chars().filter(|c| *c != '_').collect::<String>();
|
||||
if num_str.is_empty() {
|
||||
self.expected("numeric digit")
|
||||
} else {
|
||||
u32::from_str_radix(&num_str, radix).map_err(|e| e.to_string())
|
||||
}
|
||||
let msg = format!("\x1b[1mNumber literal outside of range for {}.\x1b[0m", typ);
|
||||
let end_idx = *self.index();
|
||||
self.with_ctx(Err(msg), ini_idx, end_idx)
|
||||
}
|
||||
}
|
||||
|
||||
@ -786,11 +683,6 @@ impl Book {
|
||||
}
|
||||
}
|
||||
|
||||
fn add_ctx_to_msg(msg: &str, ini_idx: usize, end_idx: usize, file: &str) -> String {
|
||||
let ctx = highlight_error(ini_idx, end_idx, file);
|
||||
format!("{msg}\n{ctx}")
|
||||
}
|
||||
|
||||
impl<'a> ParserCommons<'a> for TermParser<'a> {}
|
||||
|
||||
pub trait ParserCommons<'a>: Parser<'a> {
|
||||
@ -823,10 +715,22 @@ pub trait ParserCommons<'a>: Parser<'a> {
|
||||
}
|
||||
|
||||
fn expected_spanned<T>(&mut self, exp: &str, ini_idx: usize, end_idx: usize) -> Result<T, String> {
|
||||
let ctx = highlight_error(ini_idx, end_idx, self.input());
|
||||
let is_eof = self.is_eof();
|
||||
let detected = DisplayFn(|f| if is_eof { write!(f, " end of input") } else { write!(f, "\n{ctx}") });
|
||||
Err(format!("\x1b[1m- expected:\x1b[0m {}\n\x1b[1m- detected:\x1b[0m{}", exp, detected))
|
||||
let detected = DisplayFn(|f| if is_eof { write!(f, " end of input") } else { Ok(()) });
|
||||
let msg = format!("\x1b[1m- expected:\x1b[0m {}\n\x1b[1m- detected:\x1b[0m{}", exp, detected);
|
||||
self.with_ctx(Err(msg), ini_idx, end_idx)
|
||||
}
|
||||
|
||||
fn with_ctx<T>(
|
||||
&mut self,
|
||||
res: Result<T, impl std::fmt::Display>,
|
||||
ini_idx: usize,
|
||||
end_idx: usize,
|
||||
) -> Result<T, String> {
|
||||
res.map_err(|msg| {
|
||||
let ctx = highlight_error(ini_idx, end_idx, self.input());
|
||||
format!("{msg}\n{ctx}")
|
||||
})
|
||||
}
|
||||
|
||||
/// Consumes text if the input starts with it. Otherwise, do nothing.
|
||||
@ -925,4 +829,26 @@ pub trait ParserCommons<'a>: Parser<'a> {
|
||||
};
|
||||
Ok(opr)
|
||||
}
|
||||
|
||||
fn parse_u32(&mut self) -> Result<u32, String> {
|
||||
self.skip_trivia();
|
||||
let radix = match self.peek_many(2) {
|
||||
Some("0x") => {
|
||||
self.advance_many(2);
|
||||
16
|
||||
}
|
||||
Some("0b") => {
|
||||
self.advance_many(2);
|
||||
2
|
||||
}
|
||||
_ => 10,
|
||||
};
|
||||
let num_str = self.take_while(move |c| c.is_digit(radix) || c == '_');
|
||||
let num_str = num_str.chars().filter(|c| *c != '_').collect::<String>();
|
||||
if num_str.is_empty() {
|
||||
self.expected("numeric digit")
|
||||
} else {
|
||||
u32::from_str_radix(&num_str, radix).map_err(|e| e.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
mod order_kwargs;
|
||||
pub mod parser;
|
||||
pub mod to_lang;
|
||||
pub mod to_fun;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use interner::global::GlobalString;
|
||||
@ -8,7 +8,7 @@ use interner::global::GlobalString;
|
||||
use crate::fun::{CtrField, Name, Op};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Term {
|
||||
pub enum Expr {
|
||||
// "None"
|
||||
None,
|
||||
// [a-zA-Z_]+
|
||||
@ -16,19 +16,19 @@ pub enum Term {
|
||||
// [0-9_]+
|
||||
Num { val: u32 },
|
||||
// {fun}({args},{kwargs},)
|
||||
Call { fun: Box<Term>, args: Vec<Term>, kwargs: Vec<(Name, Term)> },
|
||||
Call { fun: Box<Expr>, args: Vec<Expr>, kwargs: Vec<(Name, Expr)> },
|
||||
// "lambda" {names}* ":" {bod}
|
||||
Lam { names: Vec<Name>, bod: Box<Term> },
|
||||
Lam { names: Vec<Name>, bod: Box<Expr> },
|
||||
// {lhs} {op} {rhs}
|
||||
Bin { op: Op, lhs: Box<Term>, rhs: Box<Term> },
|
||||
Bin { op: Op, lhs: Box<Expr>, rhs: Box<Expr> },
|
||||
// "\"" ... "\""
|
||||
Str { val: GlobalString },
|
||||
// "[" ... "]"
|
||||
Lst { els: Vec<Term> },
|
||||
Lst { els: Vec<Expr> },
|
||||
// "(" ... ")"
|
||||
Tup { els: Vec<Term> },
|
||||
Tup { els: Vec<Expr> },
|
||||
// "[" {term} "for" {bind} "in" {iter} ("if" {cond})? "]"
|
||||
Comprehension { term: Box<Term>, bind: Name, iter: Box<Term>, cond: Option<Box<Term>> },
|
||||
Comprehension { term: Box<Expr>, bind: Name, iter: Box<Expr>, cond: Option<Box<Expr>> },
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@ -56,32 +56,37 @@ pub enum InPlaceOp {
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Stmt {
|
||||
// {pat} = {val} ";" {nxt}
|
||||
Assign { pat: AssignPattern, val: Box<Term>, nxt: Box<Stmt> },
|
||||
Assign { pat: AssignPattern, val: Box<Expr>, nxt: Box<Stmt> },
|
||||
// {var} += {val} ";" {nxt}
|
||||
InPlace { op: InPlaceOp, var: Name, val: Box<Term>, nxt: Box<Stmt> },
|
||||
InPlace { op: InPlaceOp, var: Name, val: Box<Expr>, nxt: Box<Stmt> },
|
||||
// "if" {cond} ":"
|
||||
// {then}
|
||||
// "else" ":"
|
||||
// {otherwise}
|
||||
If { cond: Box<Term>, then: Box<Stmt>, otherwise: Box<Stmt> },
|
||||
If { cond: Box<Expr>, then: Box<Stmt>, otherwise: Box<Stmt> },
|
||||
// "match" {arg} ":" ("as" {bind})?
|
||||
// case {lft} ":" {rgt}
|
||||
Match { arg: Box<Term>, bind: Option<Name>, arms: Vec<MatchArm> },
|
||||
Match { arg: Box<Expr>, bind: Option<Name>, arms: Vec<MatchArm> },
|
||||
// "switch" {arg} ("as" {bind})?
|
||||
// case 0..wildcard ":" {rgt}
|
||||
Switch { arg: Box<Term>, bind: Option<Name>, arms: Vec<Stmt> },
|
||||
// "fold" {fun} {arg} ("as" {bind})? ":" {arms}
|
||||
Switch { arg: Box<Expr>, bind: Option<Name>, arms: Vec<Stmt> },
|
||||
// "bend" ({bind} ("="" {init})?)* "while" {cond} ":"
|
||||
// {step}
|
||||
// "then" ":"
|
||||
// {base}
|
||||
Bend { bind: Vec<Option<Name>>, init: Vec<Expr>, cond: Box<Expr>, step: Box<Stmt>, base: Box<Stmt> },
|
||||
// "fold" {arg} ("as" {bind})? ":" {arms}
|
||||
// case {lft} ":" {rgt}
|
||||
Fold { fun: Name, arg: Box<Term>, bind: Option<Name>, arms: Vec<MatchArm> },
|
||||
Fold { arg: Box<Expr>, bind: Option<Name>, arms: Vec<MatchArm> },
|
||||
// "do" {fun} ":" {block}
|
||||
Do { fun: Name, block: Vec<MBind> },
|
||||
// "return" {expr} ";"
|
||||
Return { term: Box<Term> },
|
||||
Return { term: Box<Expr> },
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum MBind {
|
||||
Ask { pat: AssignPattern, val: Box<Term> },
|
||||
Ask { pat: AssignPattern, val: Box<Expr> },
|
||||
Stmt { stmt: Box<Stmt> },
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ use indexmap::IndexMap;
|
||||
|
||||
use crate::fun::Name;
|
||||
|
||||
use super::{Definition, Enum, MBind, Program, Stmt, Term, Variant};
|
||||
use super::{Definition, Enum, Expr, MBind, Program, Stmt, Variant};
|
||||
|
||||
struct Ctx<'a> {
|
||||
variants: &'a IndexMap<Name, Name>,
|
||||
@ -70,6 +70,14 @@ impl Stmt {
|
||||
arm.rgt.order_kwargs(ctx);
|
||||
}
|
||||
}
|
||||
Stmt::Bend { bind: _, init, cond, step, base } => {
|
||||
for init in init {
|
||||
init.order_kwargs(ctx);
|
||||
}
|
||||
cond.order_kwargs(ctx);
|
||||
step.order_kwargs(ctx);
|
||||
base.order_kwargs(ctx);
|
||||
}
|
||||
Stmt::Do { block, .. } => {
|
||||
for bind in block {
|
||||
match bind {
|
||||
@ -83,11 +91,11 @@ impl Stmt {
|
||||
}
|
||||
}
|
||||
|
||||
impl Term {
|
||||
impl Expr {
|
||||
fn order_kwargs(&mut self, ctx: &Ctx) {
|
||||
match self {
|
||||
Term::Call { fun, args, kwargs } => {
|
||||
if let Term::Var { nam } = &**fun {
|
||||
Expr::Call { fun, args, kwargs } => {
|
||||
if let Expr::Var { nam } = &**fun {
|
||||
if let Some(fetch) = ctx.fetch(nam) {
|
||||
match fetch {
|
||||
Fetch::Variant(variant) => go_order_kwargs(variant.fields.iter().map(|f| &f.nam), kwargs, args),
|
||||
@ -99,22 +107,22 @@ impl Term {
|
||||
args.iter_mut().for_each(|a| a.order_kwargs(ctx));
|
||||
}
|
||||
}
|
||||
Term::Lam { bod, .. } => bod.order_kwargs(ctx),
|
||||
Term::Bin { lhs, rhs, .. } => {
|
||||
Expr::Lam { bod, .. } => bod.order_kwargs(ctx),
|
||||
Expr::Bin { lhs, rhs, .. } => {
|
||||
lhs.order_kwargs(ctx);
|
||||
rhs.order_kwargs(ctx);
|
||||
}
|
||||
Term::Lst { els } | Term::Tup { els } => els.iter_mut().for_each(|e| e.order_kwargs(ctx)),
|
||||
Term::Comprehension { .. } => {}
|
||||
Term::None | Term::Var { .. } | Term::Num { .. } | Term::Str { .. } => {}
|
||||
Expr::Lst { els } | Expr::Tup { els } => els.iter_mut().for_each(|e| e.order_kwargs(ctx)),
|
||||
Expr::Comprehension { .. } => {}
|
||||
Expr::None | Expr::Var { .. } | Expr::Num { .. } | Expr::Str { .. } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn go_order_kwargs<'a>(
|
||||
names: impl Iterator<Item = &'a Name>,
|
||||
kwargs: &mut Vec<(Name, Term)>,
|
||||
args: &mut Vec<Term>,
|
||||
kwargs: &mut Vec<(Name, Expr)>,
|
||||
args: &mut Vec<Expr>,
|
||||
) {
|
||||
let mut index_map = IndexMap::new();
|
||||
for (index, field) in names.enumerate() {
|
||||
|
@ -7,7 +7,7 @@ use crate::{
|
||||
};
|
||||
|
||||
use super::{
|
||||
AssignPattern, Definition, Enum, InPlaceOp, MBind, MatchArm, Program, Stmt, Term, TopLevel, Variant,
|
||||
AssignPattern, Definition, Enum, Expr, InPlaceOp, MBind, MatchArm, Program, Stmt, TopLevel, Variant,
|
||||
};
|
||||
|
||||
const PREC: &[&[Op]] =
|
||||
@ -46,24 +46,46 @@ impl<'a> Parser<'a> for PyParser<'a> {
|
||||
fn index(&mut self) -> &mut usize {
|
||||
&mut self.index
|
||||
}
|
||||
|
||||
/// Generates an error message for parsing failures, including the highlighted context.
|
||||
///
|
||||
/// Override to have our own error message.
|
||||
fn expected<T>(&mut self, exp: &str) -> Result<T, String> {
|
||||
let ini_idx = *self.index();
|
||||
let end_idx = *self.index() + 1;
|
||||
self.expected_spanned(exp, ini_idx, end_idx)
|
||||
}
|
||||
|
||||
/// Consumes an instance of the given string, erroring if it is not found.
|
||||
///
|
||||
/// Override to have our own error message.
|
||||
fn consume(&mut self, text: &str) -> Result<(), String> {
|
||||
self.skip_trivia();
|
||||
if self.input().get(*self.index() ..).unwrap_or_default().starts_with(text) {
|
||||
*self.index() += text.len();
|
||||
Ok(())
|
||||
} else {
|
||||
self.expected(format!("'{text}'").as_str())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PyParser<'a> {
|
||||
// A primary can be considered as a constant or literal expression.
|
||||
fn parse_primary_py(&mut self) -> Result<Term, String> {
|
||||
fn parse_expression(&mut self) -> Result<Expr, String> {
|
||||
self.skip_trivia();
|
||||
let Some(head) = self.skip_peek_one() else { return self.expected("primary term")? };
|
||||
let res = match head {
|
||||
'(' => {
|
||||
self.consume("(")?;
|
||||
let head = self.parse_term_py()?;
|
||||
let head = self.parse_infix_or_lambda()?;
|
||||
if self.skip_starts_with(",") {
|
||||
let mut els = vec![head];
|
||||
while self.try_consume(",") {
|
||||
els.push(self.parse_term_py()?);
|
||||
els.push(self.parse_infix_or_lambda()?);
|
||||
}
|
||||
self.consume(")")?;
|
||||
Term::Tup { els }
|
||||
Expr::Tup { els }
|
||||
} else {
|
||||
self.consume(")")?;
|
||||
head
|
||||
@ -73,62 +95,62 @@ impl<'a> PyParser<'a> {
|
||||
'\"' => {
|
||||
let str = self.parse_quoted_string()?;
|
||||
let val = STRINGS.get(str);
|
||||
Term::Str { val }
|
||||
Expr::Str { val }
|
||||
}
|
||||
c if c.is_ascii_digit() => {
|
||||
let val = self.parse_u64()?;
|
||||
Term::Num { val: val as u32 }
|
||||
Expr::Num { val: val as u32 }
|
||||
}
|
||||
_ => {
|
||||
if self.try_consume("True") {
|
||||
return Ok(Term::Num { val: 1 });
|
||||
return Ok(Expr::Num { val: 1 });
|
||||
} else if self.try_consume("False") {
|
||||
return Ok(Term::Num { val: 0 });
|
||||
return Ok(Expr::Num { val: 0 });
|
||||
}
|
||||
Term::Var { nam: self.parse_bend_name()? }
|
||||
Expr::Var { nam: self.parse_bend_name()? }
|
||||
}
|
||||
};
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn list_or_comprehension(&mut self) -> Result<Term, String> {
|
||||
fn list_or_comprehension(&mut self) -> Result<Expr, String> {
|
||||
self.consume("[")?;
|
||||
let head = self.parse_term_py()?;
|
||||
let head = self.parse_infix_or_lambda()?;
|
||||
if self.try_consume_keyword("for") {
|
||||
let bind = self.parse_bend_name()?;
|
||||
self.consume("in")?;
|
||||
let iter = self.parse_term_py()?;
|
||||
let iter = self.parse_infix_or_lambda()?;
|
||||
let mut cond = None;
|
||||
if self.try_consume_keyword("if") {
|
||||
cond = Some(Box::new(self.parse_term_py()?));
|
||||
cond = Some(Box::new(self.parse_infix_or_lambda()?));
|
||||
}
|
||||
self.consume("]")?;
|
||||
Ok(Term::Comprehension { term: Box::new(head), bind, iter: Box::new(iter), cond })
|
||||
Ok(Expr::Comprehension { term: Box::new(head), bind, iter: Box::new(iter), cond })
|
||||
} else {
|
||||
let mut head = vec![head];
|
||||
self.try_consume(",");
|
||||
let tail = self.list_like(|p| p.parse_term_py(), "", "]", ",", true, 0)?;
|
||||
let tail = self.list_like(|p| p.parse_infix_or_lambda(), "", "]", ",", true, 0)?;
|
||||
head.extend(tail);
|
||||
Ok(Term::Lst { els: head })
|
||||
Ok(Expr::Lst { els: head })
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_term_py(&mut self) -> Result<Term, String> {
|
||||
fn parse_infix_or_lambda(&mut self) -> Result<Expr, String> {
|
||||
self.skip_trivia();
|
||||
if self.try_consume_keyword("lambda") {
|
||||
let names = self.list_like(|p| p.parse_bend_name(), "", ":", ",", true, 1)?;
|
||||
let bod = self.parse_term_py()?;
|
||||
Ok(Term::Lam { names, bod: Box::new(bod) })
|
||||
let bod = self.parse_infix_or_lambda()?;
|
||||
Ok(Expr::Lam { names, bod: Box::new(bod) })
|
||||
} else {
|
||||
self.parse_infix_py(0)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_call(&mut self) -> Result<Term, String> {
|
||||
fn parse_call(&mut self) -> Result<Expr, String> {
|
||||
self.skip_trivia();
|
||||
let mut args = Vec::new();
|
||||
let mut kwargs = Vec::new();
|
||||
let fun = self.parse_primary_py()?;
|
||||
let fun = self.parse_expression()?;
|
||||
if self.try_consume("(") {
|
||||
loop {
|
||||
if self.try_consume(",") {
|
||||
@ -138,14 +160,9 @@ impl<'a> PyParser<'a> {
|
||||
break;
|
||||
}
|
||||
|
||||
let arg = self.parse_term_py()?;
|
||||
if self.try_consume("=") {
|
||||
if let Term::Var { nam } = arg {
|
||||
let value = self.parse_term_py()?;
|
||||
kwargs.push((nam, value));
|
||||
} else {
|
||||
return Err("Unexpected '='".to_string());
|
||||
}
|
||||
let (bind, arg) = self.parse_named_arg()?;
|
||||
if let Some(bind) = bind {
|
||||
kwargs.push((bind, arg));
|
||||
} else {
|
||||
args.push(arg);
|
||||
}
|
||||
@ -154,11 +171,28 @@ impl<'a> PyParser<'a> {
|
||||
if args.is_empty() && kwargs.is_empty() {
|
||||
Ok(fun)
|
||||
} else {
|
||||
Ok(Term::Call { fun: Box::new(fun), args, kwargs })
|
||||
Ok(Expr::Call { fun: Box::new(fun), args, kwargs })
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_infix_py(&mut self, prec: usize) -> Result<Term, String> {
|
||||
fn parse_named_arg(&mut self) -> Result<(Option<Name>, Expr), String> {
|
||||
let arg = self.parse_infix_or_lambda()?;
|
||||
if self.try_consume("=") {
|
||||
if let Expr::Var { nam } = arg {
|
||||
let bind = Some(nam);
|
||||
let arg = self.parse_infix_or_lambda()?;
|
||||
Ok((bind, arg))
|
||||
} else {
|
||||
let msg = "Unexpected '=' in unnamed argument.".to_string();
|
||||
let idx = *self.index();
|
||||
self.with_ctx(Err(msg), idx, idx + 1)
|
||||
}
|
||||
} else {
|
||||
Ok((None, arg))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_infix_py(&mut self, prec: usize) -> Result<Expr, String> {
|
||||
maybe_grow(|| {
|
||||
self.skip_trivia();
|
||||
if prec > PREC.len() - 1 {
|
||||
@ -170,7 +204,7 @@ impl<'a> PyParser<'a> {
|
||||
let op = self.parse_oper()?;
|
||||
let rhs = self.parse_infix_py(prec + 1)?;
|
||||
self.skip_trivia();
|
||||
lhs = Term::Bin { op, lhs: Box::new(lhs), rhs: Box::new(rhs) };
|
||||
lhs = Expr::Bin { op, lhs: Box::new(lhs), rhs: Box::new(rhs) };
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@ -191,15 +225,42 @@ impl<'a> PyParser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn skip_newlines(&mut self) -> Result<(), String> {
|
||||
while let Some(c) = self.peek_one() {
|
||||
if c == '\n' {
|
||||
fn skip_newlines(&mut self) -> usize {
|
||||
loop {
|
||||
let num_spaces = self.advance_inline_trivia();
|
||||
if self.peek_one() == Some('\r') {
|
||||
self.advance_one();
|
||||
}
|
||||
if self.peek_one() == Some('\n') {
|
||||
self.advance_one();
|
||||
} else {
|
||||
break;
|
||||
return num_spaces;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn advance_inline_trivia(&mut self) -> usize {
|
||||
let mut char_count = 0;
|
||||
while let Some(c) = self.peek_one() {
|
||||
if " \t".contains(c) {
|
||||
self.advance_one();
|
||||
char_count += 1;
|
||||
continue;
|
||||
}
|
||||
if c == '/' && self.input().get(*self.index() ..).unwrap_or_default().starts_with("//") {
|
||||
while let Some(c) = self.peek_one() {
|
||||
if c != '\n' {
|
||||
self.advance_one();
|
||||
char_count += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
char_count
|
||||
}
|
||||
|
||||
fn skip_exact_indent(&mut self, Indent(mut count): &Indent, block: bool) -> Result<bool, String> {
|
||||
@ -242,6 +303,8 @@ impl<'a> PyParser<'a> {
|
||||
self.parse_switch_py(indent)
|
||||
} else if self.try_consume_keyword("fold") {
|
||||
self.parse_fold_py(indent)
|
||||
} else if self.try_consume_keyword("bend") {
|
||||
self.parse_bend(indent)
|
||||
} else if self.try_consume_keyword("do") {
|
||||
self.parse_do_py(indent)
|
||||
} else {
|
||||
@ -258,7 +321,7 @@ impl<'a> PyParser<'a> {
|
||||
if self.skip_starts_with("=") {
|
||||
// it's actually an assignment
|
||||
self.consume("=")?;
|
||||
let val = self.parse_term_py()?;
|
||||
let val = self.parse_infix_or_lambda()?;
|
||||
self.consume(";")?;
|
||||
let nxt = self.parse_stmt_py(indent)?;
|
||||
return Ok(Stmt::Assign { pat: AssignPattern::Var(name), val: Box::new(val), nxt: Box::new(nxt) });
|
||||
@ -282,7 +345,7 @@ impl<'a> PyParser<'a> {
|
||||
return self.expected("in-place operator");
|
||||
}
|
||||
|
||||
let val = self.parse_term_py()?;
|
||||
let val = self.parse_infix_or_lambda()?;
|
||||
self.consume(";")?;
|
||||
let nxt = self.parse_stmt_py(indent)?;
|
||||
Ok(Stmt::InPlace { op: in_place, var: name, val: Box::new(val), nxt: Box::new(nxt) })
|
||||
@ -293,13 +356,13 @@ impl<'a> PyParser<'a> {
|
||||
}
|
||||
|
||||
fn parse_return_py(&mut self) -> Result<Stmt, String> {
|
||||
let term = self.parse_term_py()?;
|
||||
let term = self.parse_infix_or_lambda()?;
|
||||
self.consume(";")?;
|
||||
Ok(Stmt::Return { term: Box::new(term) })
|
||||
}
|
||||
|
||||
fn parse_if_py(&mut self, indent: &mut Indent) -> Result<Stmt, String> {
|
||||
let cond = self.parse_term_py()?;
|
||||
let cond = self.parse_infix_or_lambda()?;
|
||||
self.consume(":")?;
|
||||
indent.enter_level();
|
||||
let then = self.parse_stmt_py(indent)?;
|
||||
@ -313,17 +376,8 @@ impl<'a> PyParser<'a> {
|
||||
Ok(Stmt::If { cond: Box::new(cond), then: Box::new(then), otherwise: Box::new(otherwise) })
|
||||
}
|
||||
|
||||
fn parse_as_bind(&mut self) -> Result<Option<Name>, String> {
|
||||
let mut bind = None;
|
||||
if self.try_consume_keyword("as") {
|
||||
bind = Some(self.parse_bend_name()?);
|
||||
}
|
||||
Ok(bind)
|
||||
}
|
||||
|
||||
fn parse_match_py(&mut self, indent: &mut Indent) -> Result<Stmt, String> {
|
||||
let scrutinee = self.parse_primary_py()?;
|
||||
let bind = self.parse_as_bind()?;
|
||||
let (bind, arg) = self.parse_named_arg()?;
|
||||
self.consume(":")?;
|
||||
let mut arms = Vec::new();
|
||||
indent.enter_level();
|
||||
@ -334,7 +388,7 @@ impl<'a> PyParser<'a> {
|
||||
arms.push(self.parse_case_py(indent)?);
|
||||
}
|
||||
indent.exit_level();
|
||||
Ok(Stmt::Match { arg: Box::new(scrutinee), bind, arms })
|
||||
Ok(Stmt::Match { arg: Box::new(arg), bind, arms })
|
||||
}
|
||||
|
||||
fn name_or_wildcard(&mut self) -> Result<Option<Name>, String> {
|
||||
@ -352,7 +406,6 @@ impl<'a> PyParser<'a> {
|
||||
}
|
||||
|
||||
fn parse_case_py(&mut self, indent: &mut Indent) -> Result<MatchArm, String> {
|
||||
self.consume("case")?;
|
||||
let pat = self.name_or_wildcard()?;
|
||||
self.consume(":")?;
|
||||
indent.enter_level();
|
||||
@ -362,8 +415,7 @@ impl<'a> PyParser<'a> {
|
||||
}
|
||||
|
||||
fn parse_switch_py(&mut self, indent: &mut Indent) -> Result<Stmt, String> {
|
||||
let arg = self.parse_term_py()?;
|
||||
let bind = self.parse_as_bind()?;
|
||||
let (bind, arg) = self.parse_named_arg()?;
|
||||
self.consume(":")?;
|
||||
let mut arms = Vec::new();
|
||||
|
||||
@ -372,7 +424,6 @@ impl<'a> PyParser<'a> {
|
||||
let mut expected_num = 0;
|
||||
|
||||
while should_continue && self.skip_exact_indent(indent, true)? {
|
||||
self.consume("case")?;
|
||||
if let Some(c) = self.skip_peek_one() {
|
||||
match c {
|
||||
'_' => {
|
||||
@ -405,9 +456,7 @@ impl<'a> PyParser<'a> {
|
||||
}
|
||||
|
||||
fn parse_fold_py(&mut self, indent: &mut Indent) -> Result<Stmt, String> {
|
||||
let fun = self.parse_bend_name()?;
|
||||
let arg = self.parse_term_py()?;
|
||||
let bind = self.parse_as_bind()?;
|
||||
let (bind, arg) = self.parse_named_arg()?;
|
||||
self.consume(":")?;
|
||||
let mut arms = Vec::new();
|
||||
indent.enter_level();
|
||||
@ -418,7 +467,23 @@ impl<'a> PyParser<'a> {
|
||||
arms.push(self.parse_case_py(indent)?);
|
||||
}
|
||||
indent.exit_level();
|
||||
Ok(Stmt::Fold { fun, arg: Box::new(arg), bind, arms })
|
||||
Ok(Stmt::Fold { arg: Box::new(arg), bind, arms })
|
||||
}
|
||||
|
||||
fn parse_bend(&mut self, indent: &mut Indent) -> Result<Stmt, String> {
|
||||
let args = self.list_like(|p| p.parse_named_arg(), "", "while", ",", true, 0)?;
|
||||
let (bind, init) = args.into_iter().unzip();
|
||||
let cond = self.parse_infix_or_lambda()?;
|
||||
self.consume(":")?;
|
||||
indent.enter_level();
|
||||
let step = self.parse_stmt_py(indent)?;
|
||||
indent.exit_level();
|
||||
self.consume("then")?;
|
||||
self.consume(":")?;
|
||||
indent.enter_level();
|
||||
let base = self.parse_stmt_py(indent)?;
|
||||
indent.exit_level();
|
||||
Ok(Stmt::Bend { bind, init, cond: Box::new(cond), step: Box::new(step), base: Box::new(base) })
|
||||
}
|
||||
|
||||
fn parse_do_py(&mut self, indent: &mut Indent) -> Result<Stmt, String> {
|
||||
@ -435,7 +500,7 @@ impl<'a> PyParser<'a> {
|
||||
if self.try_consume("!") {
|
||||
let pat = self.parse_assign_pattern_py()?;
|
||||
self.consume("=")?;
|
||||
let val = self.parse_term_py()?;
|
||||
let val = self.parse_infix_or_lambda()?;
|
||||
self.consume(";")?;
|
||||
block.push(MBind::Ask { pat, val: Box::new(val) });
|
||||
} else {
|
||||
@ -450,7 +515,7 @@ impl<'a> PyParser<'a> {
|
||||
fn parse_assignment_py(&mut self, indent: &mut Indent) -> Result<Stmt, String> {
|
||||
let pat = self.parse_assign_pattern_py()?;
|
||||
self.consume("=")?;
|
||||
let val = self.parse_term_py()?;
|
||||
let val = self.parse_infix_or_lambda()?;
|
||||
self.consume(";")?;
|
||||
let nxt = self.parse_stmt_py(indent)?;
|
||||
Ok(Stmt::Assign { pat, val: Box::new(val), nxt: Box::new(nxt) })
|
||||
@ -522,16 +587,18 @@ impl<'a> PyParser<'a> {
|
||||
let mut defs = IndexMap::<Name, Definition>::new();
|
||||
let mut variants = IndexMap::<Name, Name>::new();
|
||||
|
||||
while {
|
||||
self.skip_newlines()?;
|
||||
true
|
||||
} && !self.is_eof()
|
||||
{
|
||||
loop {
|
||||
let spaces = self.skip_newlines();
|
||||
if self.is_eof() {
|
||||
break;
|
||||
}
|
||||
if spaces != 0 {
|
||||
self.expected("Indentation error")?;
|
||||
}
|
||||
match self.parse_top_level_py(&mut Indent(0))? {
|
||||
TopLevel::Def(def) => Self::add_def_py(&mut defs, def)?,
|
||||
TopLevel::Enum(r#enum) => Self::add_enum_py(&mut enums, &mut variants, r#enum)?,
|
||||
}
|
||||
self.skip_spaces();
|
||||
}
|
||||
|
||||
Ok(Program { enums, defs, variants })
|
||||
@ -576,7 +643,6 @@ mod test {
|
||||
use super::PyParser;
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn parse_def() {
|
||||
let src = r#"
|
||||
enum Point:
|
||||
@ -596,17 +662,17 @@ def inc(n):
|
||||
n += 1;
|
||||
return n;
|
||||
|
||||
def inc_list(list):
|
||||
return [x+1 for x in list];
|
||||
//def inc_list(list):
|
||||
// return [x+1 for x in list];
|
||||
|
||||
def lam():
|
||||
return lambda x, y: x;
|
||||
|
||||
def do_match(b):
|
||||
match b as bool:
|
||||
case True:
|
||||
match b:
|
||||
True:
|
||||
return 1;
|
||||
case False:
|
||||
False:
|
||||
return 0;
|
||||
|
||||
def true():
|
||||
@ -620,27 +686,33 @@ def fib(n):
|
||||
|
||||
def swt(n):
|
||||
switch n:
|
||||
case 0:
|
||||
0:
|
||||
return 42;
|
||||
case _:
|
||||
_:
|
||||
return 1;
|
||||
|
||||
def fld(list):
|
||||
fold List.fold list:
|
||||
case List.cons:
|
||||
fold list:
|
||||
List.cons:
|
||||
return 1;
|
||||
case List.nil:
|
||||
List.nil:
|
||||
return 2;
|
||||
|
||||
def main():
|
||||
do IO.bind:
|
||||
!x = IO.read();
|
||||
return x;
|
||||
def bnd():
|
||||
bend x = 0 while x < 10:
|
||||
return List.cons(x go(x + 1));
|
||||
then:
|
||||
return List.nil();
|
||||
|
||||
//def main():
|
||||
// do IO.bind:
|
||||
// !x = IO.read();
|
||||
// return x;
|
||||
"#;
|
||||
let mut parser = PyParser::new(src);
|
||||
let mut program = parser.parse_program_py().inspect_err(|e| println!("{e}")).unwrap();
|
||||
program.order_kwargs();
|
||||
let out = program.to_lang(crate::fun::Book::default());
|
||||
let out = program.to_fun(crate::fun::Book::default());
|
||||
println!("{out}");
|
||||
}
|
||||
}
|
||||
|
126
src/imp/to_fun.rs
Normal file
126
src/imp/to_fun.rs
Normal file
@ -0,0 +1,126 @@
|
||||
use super::{AssignPattern, Definition, Expr, Program, Stmt};
|
||||
use crate::fun;
|
||||
|
||||
impl Program {
|
||||
pub fn to_fun(self, mut book: fun::Book) -> fun::Book {
|
||||
for (def_name, def) in self.defs {
|
||||
book.defs.insert(def_name, def.to_fun());
|
||||
}
|
||||
|
||||
for (enum_name, r#enum) in self.enums {
|
||||
for variant in r#enum.variants.iter() {
|
||||
book.ctrs.insert(variant.0.clone(), enum_name.clone());
|
||||
}
|
||||
let ctrs = r#enum.variants.into_iter().map(|(k, v)| (k, v.fields)).collect();
|
||||
let adt = fun::Adt { ctrs, builtin: false };
|
||||
book.adts.insert(enum_name, adt);
|
||||
}
|
||||
|
||||
book
|
||||
}
|
||||
}
|
||||
|
||||
impl Definition {
|
||||
pub fn to_fun(self) -> fun::Definition {
|
||||
let rule = fun::Rule {
|
||||
pats: self.params.into_iter().map(|param| fun::Pattern::Var(Some(param))).collect(),
|
||||
body: self.body.to_fun(),
|
||||
};
|
||||
|
||||
fun::Definition { name: self.name, rules: vec![rule], builtin: false }
|
||||
}
|
||||
}
|
||||
|
||||
impl AssignPattern {
|
||||
pub fn to_fun(self) -> fun::Pattern {
|
||||
match self {
|
||||
AssignPattern::Var(name) => fun::Pattern::Var(Some(name)),
|
||||
AssignPattern::Tup(names) => fun::Pattern::Fan(
|
||||
fun::FanKind::Tup,
|
||||
fun::Tag::Static,
|
||||
names.into_iter().map(|name| fun::Pattern::Var(Some(name))).collect(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Stmt {
|
||||
pub fn to_fun(self) -> fun::Term {
|
||||
match self {
|
||||
Stmt::Assign { pat, val, nxt } => {
|
||||
let pat = pat.to_fun();
|
||||
let val = val.to_fun();
|
||||
let nxt = nxt.to_fun();
|
||||
fun::Term::Let { pat: Box::new(pat), val: Box::new(val), nxt: Box::new(nxt) }
|
||||
}
|
||||
Stmt::InPlace { op, var: nam, val, nxt } => fun::Term::Let {
|
||||
pat: Box::new(fun::Pattern::Var(Some(nam.clone()))),
|
||||
val: Box::new(fun::Term::Oper {
|
||||
opr: op.to_lang_op(),
|
||||
fst: Box::new(fun::Term::Var { nam }),
|
||||
snd: Box::new(val.to_fun()),
|
||||
}),
|
||||
nxt: Box::new(nxt.to_fun()),
|
||||
},
|
||||
Stmt::If { cond, then, otherwise } => {
|
||||
let arms = vec![otherwise.to_fun(), then.to_fun()];
|
||||
fun::Term::Swt { arg: Box::new(cond.to_fun()), bnd: None, with: Vec::new(), pred: None, arms }
|
||||
}
|
||||
Stmt::Match { arg, bind, arms } => {
|
||||
let arg = arg.to_fun();
|
||||
let arms = arms.into_iter().map(|arm| (arm.lft, Vec::new(), arm.rgt.to_fun())).collect();
|
||||
fun::Term::Mat { arg: Box::new(arg), bnd: bind, with: Vec::new(), arms }
|
||||
}
|
||||
Stmt::Switch { arg, bind, arms } => {
|
||||
let arg = arg.to_fun();
|
||||
let arms = arms.into_iter().map(Stmt::to_fun).collect();
|
||||
fun::Term::Swt { arg: Box::new(arg), bnd: bind, with: Vec::new(), pred: None, arms }
|
||||
}
|
||||
Stmt::Fold { arg, bind, arms } => {
|
||||
let arg = arg.to_fun();
|
||||
let arms = arms.into_iter().map(|arm| (arm.lft, Vec::new(), arm.rgt.to_fun())).collect();
|
||||
fun::Term::Fold { arg: Box::new(arg), bnd: bind, with: Vec::new(), arms }
|
||||
}
|
||||
Stmt::Bend { bind, init, cond, step, base } => {
|
||||
let init = init.into_iter().map(Expr::to_fun).collect();
|
||||
let cond = cond.to_fun();
|
||||
let step = step.to_fun();
|
||||
let base = base.to_fun();
|
||||
fun::Term::Bend { bind, init, cond: Box::new(cond), step: Box::new(step), base: Box::new(base) }
|
||||
}
|
||||
Stmt::Do { .. } => todo!(),
|
||||
Stmt::Return { term } => term.to_fun(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Expr {
|
||||
pub fn to_fun(self) -> fun::Term {
|
||||
match self {
|
||||
Expr::None => fun::Term::Era,
|
||||
Expr::Var { nam } => fun::Term::Var { nam },
|
||||
Expr::Num { val } => fun::Term::Num { val: fun::Num::U24(val) },
|
||||
Expr::Call { fun, args, kwargs } => {
|
||||
assert!(kwargs.is_empty());
|
||||
let args = args.into_iter().map(Self::to_fun);
|
||||
fun::Term::call(fun.to_fun(), args)
|
||||
}
|
||||
Expr::Lam { names, bod } => names.into_iter().rfold(bod.to_fun(), |acc, nxt| fun::Term::Lam {
|
||||
tag: fun::Tag::Static,
|
||||
pat: Box::new(fun::Pattern::Var(Some(nxt))),
|
||||
bod: Box::new(acc),
|
||||
}),
|
||||
Expr::Bin { op, lhs, rhs } => {
|
||||
fun::Term::Oper { opr: op, fst: Box::new(lhs.to_fun()), snd: Box::new(rhs.to_fun()) }
|
||||
}
|
||||
Expr::Str { val } => fun::Term::Str { val },
|
||||
Expr::Lst { els } => fun::Term::List { els: els.into_iter().map(Self::to_fun).collect() },
|
||||
Expr::Tup { els } => fun::Term::Fan {
|
||||
fan: fun::FanKind::Tup,
|
||||
tag: fun::Tag::Static,
|
||||
els: els.into_iter().map(Self::to_fun).collect(),
|
||||
},
|
||||
Expr::Comprehension { .. } => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
use crate::fun::{self as lang};
|
||||
|
||||
use super::{AssignPattern, Definition, Program, Stmt, Term};
|
||||
|
||||
impl Program {
|
||||
pub fn to_lang(self, mut book: lang::Book) -> lang::Book {
|
||||
for (def_name, def) in self.defs {
|
||||
book.defs.insert(def_name, def.to_lang());
|
||||
}
|
||||
|
||||
for (enum_name, r#enum) in self.enums {
|
||||
for variant in r#enum.variants.iter() {
|
||||
book.ctrs.insert(variant.0.clone(), enum_name.clone());
|
||||
}
|
||||
let ctrs = r#enum.variants.into_iter().map(|(k, v)| (k, v.fields)).collect();
|
||||
let adt = lang::Adt { ctrs, builtin: false };
|
||||
book.adts.insert(enum_name, adt);
|
||||
}
|
||||
|
||||
book
|
||||
}
|
||||
}
|
||||
|
||||
impl Definition {
|
||||
pub fn to_lang(self) -> lang::Definition {
|
||||
let rule = lang::Rule {
|
||||
pats: self.params.into_iter().map(|param| lang::Pattern::Var(Some(param))).collect(),
|
||||
body: self.body.to_lang(),
|
||||
};
|
||||
|
||||
lang::Definition { name: self.name, rules: vec![rule], builtin: false }
|
||||
}
|
||||
}
|
||||
|
||||
impl AssignPattern {
|
||||
pub fn to_lang(self) -> lang::Pattern {
|
||||
match self {
|
||||
AssignPattern::Var(name) => lang::Pattern::Var(Some(name)),
|
||||
AssignPattern::Tup(names) => lang::Pattern::Fan(
|
||||
lang::FanKind::Tup,
|
||||
lang::Tag::Static,
|
||||
names.into_iter().map(|name| lang::Pattern::Var(Some(name))).collect(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Stmt {
|
||||
pub fn to_lang(self) -> lang::Term {
|
||||
match self {
|
||||
Stmt::Assign { pat, val, nxt } => {
|
||||
let pat = pat.to_lang();
|
||||
let val = val.to_lang();
|
||||
let nxt = nxt.to_lang();
|
||||
lang::Term::Let { pat: Box::new(pat), val: Box::new(val), nxt: Box::new(nxt) }
|
||||
}
|
||||
Stmt::InPlace { op, var: nam, val, nxt } => lang::Term::Let {
|
||||
pat: Box::new(lang::Pattern::Var(Some(nam.clone()))),
|
||||
val: Box::new(lang::Term::Oper {
|
||||
opr: op.to_lang_op(),
|
||||
fst: Box::new(lang::Term::Var { nam }),
|
||||
snd: Box::new(val.to_lang()),
|
||||
}),
|
||||
nxt: Box::new(nxt.to_lang()),
|
||||
},
|
||||
Stmt::If { cond, then, otherwise } => {
|
||||
let arms = vec![otherwise.to_lang(), then.to_lang()];
|
||||
lang::Term::Swt { arg: Box::new(cond.to_lang()), bnd: None, with: Vec::new(), pred: None, arms }
|
||||
}
|
||||
Stmt::Match { arg, bind, arms: old_arms } => {
|
||||
let arg = arg.to_lang();
|
||||
let mut arms = Vec::with_capacity(old_arms.len());
|
||||
for arm in old_arms {
|
||||
arms.push((arm.lft, Vec::new(), arm.rgt.to_lang()))
|
||||
}
|
||||
lang::Term::Mat { arg: Box::new(arg), bnd: bind, with: Vec::new(), arms }
|
||||
}
|
||||
Stmt::Switch { .. } => unimplemented!(),
|
||||
Stmt::Fold { .. } => unimplemented!(),
|
||||
Stmt::Do { .. } => unimplemented!(),
|
||||
Stmt::Return { term } => term.to_lang(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Term {
|
||||
pub fn to_lang(self) -> lang::Term {
|
||||
match self {
|
||||
Term::None => lang::Term::Era,
|
||||
Term::Var { nam } => lang::Term::Var { nam },
|
||||
Term::Num { val } => lang::Term::Num { val: lang::Num::U24(val) },
|
||||
Term::Call { fun, args, kwargs } => {
|
||||
assert!(kwargs.is_empty());
|
||||
let args = args.into_iter().map(Self::to_lang);
|
||||
lang::Term::call(fun.to_lang(), args)
|
||||
}
|
||||
Term::Lam { names, bod } => names.into_iter().rfold(bod.to_lang(), |acc, nxt| lang::Term::Lam {
|
||||
tag: lang::Tag::Static,
|
||||
pat: Box::new(lang::Pattern::Var(Some(nxt))),
|
||||
bod: Box::new(acc),
|
||||
}),
|
||||
Term::Bin { op, lhs, rhs } => {
|
||||
lang::Term::Oper { opr: op, fst: Box::new(lhs.to_lang()), snd: Box::new(rhs.to_lang()) }
|
||||
}
|
||||
Term::Str { val } => lang::Term::Str { val },
|
||||
Term::Lst { els } => lang::Term::List { els: els.into_iter().map(Self::to_lang).collect() },
|
||||
Term::Tup { els } => lang::Term::Fan {
|
||||
fan: lang::FanKind::Tup,
|
||||
tag: lang::Tag::Static,
|
||||
els: els.into_iter().map(Self::to_lang).collect(),
|
||||
},
|
||||
Term::Comprehension { .. } => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
@ -6,3 +6,4 @@ Errors:
|
||||
In tests/golden_tests/compile_file/just_a_name.hvm :
|
||||
[1m- expected:[0m pattern-matching pattern
|
||||
[1m- detected:[0m end of input
|
||||
[0m 1 | asdf[4m[31m [0m
|
||||
|
@ -6,3 +6,4 @@ Errors:
|
||||
In tests/golden_tests/compile_file/just_data.hvm :
|
||||
[1m- expected:[0m datatype name
|
||||
[1m- detected:[0m end of input
|
||||
[0m 1 | data[4m[31m [0m
|
||||
|
@ -6,3 +6,4 @@ Errors:
|
||||
In tests/golden_tests/compile_file/just_paren.hvm :
|
||||
[1m- expected:[0m function name
|
||||
[1m- detected:[0m end of input
|
||||
[0m 2 | ([4m[31m [0m
|
||||
|
@ -6,3 +6,4 @@ Errors:
|
||||
In tests/golden_tests/compile_file/just_rule_paren.hvm :
|
||||
[1m- expected:[0m '='
|
||||
[1m- detected:[0m end of input
|
||||
[0m 1 | (rule)[4m[31m [0m
|
||||
|
@ -6,3 +6,4 @@ Errors:
|
||||
In tests/golden_tests/compile_file/missing_adt_eq.hvm :
|
||||
[1m- expected:[0m '='
|
||||
[1m- detected:[0m end of input
|
||||
[0m 1 | data Adt[4m[31m [0m
|
||||
|
@ -6,3 +6,4 @@ Errors:
|
||||
In tests/golden_tests/compile_file/missing_ctrs.hvm :
|
||||
[1m- expected:[0m datatype constructor name
|
||||
[1m- detected:[0m end of input
|
||||
[0m 1 | data Adt = [4m[31m [0m
|
||||
|
Loading…
Reference in New Issue
Block a user