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:
Nicolas Abril 2024-05-09 18:18:21 +02:00 committed by GitHub
commit d41ddfa363
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 379 additions and 351 deletions

View File

@ -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())
}
}
}

View File

@ -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> },
}

View File

@ -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() {

View File

@ -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 {
return num_spaces;
}
}
}
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;
}
}
Ok(())
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
View 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!(),
}
}
}

View File

@ -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!(),
}
}
}

View File

@ -6,3 +6,4 @@ Errors:
In tests/golden_tests/compile_file/just_a_name.hvm :
- expected: pattern-matching pattern
- detected: end of input
 1 | asdf 

View File

@ -6,3 +6,4 @@ Errors:
In tests/golden_tests/compile_file/just_data.hvm :
- expected: datatype name
- detected: end of input
 1 | data 

View File

@ -6,3 +6,4 @@ Errors:
In tests/golden_tests/compile_file/just_paren.hvm :
- expected: function name
- detected: end of input
 2 | ( 

View File

@ -6,3 +6,4 @@ Errors:
In tests/golden_tests/compile_file/just_rule_paren.hvm :
- expected: '='
- detected: end of input
 1 | (rule) 

View File

@ -6,3 +6,4 @@ Errors:
In tests/golden_tests/compile_file/missing_adt_eq.hvm :
- expected: '='
- detected: end of input
 1 | data Adt 

View File

@ -6,3 +6,4 @@ Errors:
In tests/golden_tests/compile_file/missing_ctrs.hvm :
- expected: datatype constructor name
- detected: end of input
 1 | data Adt =