Skip to content

Commit

Permalink
Introduce variables
Browse files Browse the repository at this point in the history
Close #1
  • Loading branch information
rexim committed Mar 27, 2022
1 parent dea86e3 commit 5c5fc44
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 27 deletions.
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ The Main Idea is being able to define transformation rules of symbolic algebraic
Current expression syntax:

```
<expression> ::= <symbol> | <functor>
<symbol> ::= [a-zA-Z0-9]+
<functor> ::= <symbol> ( [<expression>],* )
<expression> ::= <symbol> | <functor> | <var>
<var> ::= [A-Z][a-zA-Z0-9]*
<symbol> ::= [a-z0-9][a-zA-Z0-9]*
<functor> ::= <expression> ( [<expression>],* )
```

## Rules and Shapes
Expand All @@ -33,7 +34,7 @@ rule <name:symbol> <head:expression> = <body:expression>
Here is an example of a rule that swaps elements of a pair:

```
rule swap swap(pair(a, b)) = pair(b, a)
rule swap swap(pair(A, B)) = pair(B, A)
```

Shaping is a process of sequential applying of rules to an expression transforming it into a different expression. Shaping has the following syntax:
Expand All @@ -60,7 +61,7 @@ You don't have to define a rule to use it in shaping. You can directly describe

```
shape swap(pair(f(a), g(b)))
apply rule swap(pair(a, b)) = pair(b, a)
apply rule swap(pair(A, B)) = pair(B, A)
done
```

Expand Down
4 changes: 2 additions & 2 deletions examples/add.noq
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Adding two Peano numbers

rule add0 add(0, a) = a # base
rule add add(s(a), b) = s(add(a, b)) # recursion
rule add0 add(0, A) = A # base
rule add add(s(A), B) = s(add(A, B)) # recursion

shape add(s(s(0)), s(s(s(0)))) # 2 + 3
# TODO: it would be nice to have a "strategy" that applies a particular rule
Expand Down
6 changes: 3 additions & 3 deletions examples/swap.noq
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
rule swap swap(pair(a, b)) = pair(b, a)
rule rot rot(triple(a, b, c)) = triple(c, a, b)
rule swap swap(pair(A, B)) = pair(B, A)
rule rot rot(triple(A, B, C)) = triple(C, A, B)

shape swap(pair(f(a), g(b)))
apply swap
apply rule pair(a, b) = rot(triple(a, b, c))
apply rule pair(A, B) = rot(triple(A, B, c))
apply rot
done
45 changes: 28 additions & 17 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ use lexer::*;
#[derive(Debug, Clone, PartialEq)]
enum Expr {
Sym(String),
Fun(String, Vec<Expr>)
Var(String),
Fun(Box<Expr>, Vec<Expr>),
}

#[derive(Debug)]
Expand All @@ -26,6 +27,14 @@ enum Error {
}

impl Expr {
fn var_or_sym_from_name(name: &str) -> Expr {
if name.chars().next().expect("Empty names are not allowed").is_uppercase() {
Expr::Var(name.to_string())
} else {
Expr::Sym(name.to_string())
}
}

fn parse(lexer: &mut Peekable<impl Iterator<Item=Token>>) -> Result<Self, Error> {
use TokenKind::*;
let name = lexer.next().expect("Completely exhausted lexer");
Expand All @@ -34,20 +43,20 @@ impl Expr {
if let Some(_) = lexer.next_if(|t| t.kind == OpenParen) {
let mut args = Vec::new();
if let Some(_) = lexer.next_if(|t| t.kind == CloseParen) {
return Ok(Expr::Fun(name.text, args))
return Ok(Expr::Fun(Box::new(Self::var_or_sym_from_name(&name.text)), args))
}
args.push(Self::parse(lexer)?);
while let Some(_) = lexer.next_if(|t| t.kind == Comma) {
args.push(Self::parse(lexer)?);
}
let close_paren = lexer.next().expect("Completely exhausted lexer");
if close_paren.kind == CloseParen {
Ok(Expr::Fun(name.text, args))
Ok(Expr::Fun(Box::new(Self::var_or_sym_from_name(&name.text)), args))
} else {
Err(Error::UnexpectedToken(TokenKindSet::single(CloseParen), close_paren))
}
} else {
Ok(Expr::Sym(name.text))
Ok(Self::var_or_sym_from_name(&name.text))
}
},
_ => Err(Error::UnexpectedToken(TokenKindSet::single(Sym), name))
Expand All @@ -58,7 +67,7 @@ impl Expr {
impl fmt::Display for Expr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Expr::Sym(name) => write!(f, "{}", name),
Expr::Sym(name) | Expr::Var(name) => write!(f, "{}", name),
Expr::Fun(name, args) => {
write!(f, "{}(", name)?;
for (i, arg) in args.iter().enumerate() {
Expand All @@ -81,25 +90,23 @@ struct Rule {
fn substitute_bindings(bindings: &Bindings, expr: &Expr) -> Expr {
use Expr::*;
match expr {
Sym(name) => {
Sym(_) => expr.clone(),

Var(name) => {
if let Some(value) = bindings.get(name) {
value.clone()
} else {
expr.clone()
}
},

Fun(name, args) => {
let new_name = match bindings.get(name) {
Some(Sym(new_name)) => new_name.clone(),
None => name.clone(),
Some(_) => todo!("Report expected symbol in the place of the functor name"),
};
Fun(head, args) => {
let new_head = substitute_bindings(bindings, head);
let mut new_args = Vec::new();
for arg in args {
new_args.push(substitute_bindings(bindings, &arg))
}
Fun(new_name, new_args)
Fun(Box::new(new_head), new_args)
}
}
}
Expand All @@ -120,13 +127,14 @@ impl Rule {
} else {
use Expr::*;
match expr {
Sym(_) => expr.clone(),
Fun(name, args) => {
Sym(_) | Var(_) => expr.clone(),
Fun(head, args) => {
let new_head = self.apply_all(head);
let mut new_args = Vec::new();
for arg in args {
new_args.push(self.apply_all(arg))
}
Fun(name.clone(), new_args)
Fun(Box::new(new_head), new_args)
}
}
}
Expand All @@ -145,7 +153,10 @@ fn pattern_match(pattern: &Expr, value: &Expr) -> Option<Bindings> {
fn pattern_match_impl(pattern: &Expr, value: &Expr, bindings: &mut Bindings) -> bool {
use Expr::*;
match (pattern, value) {
(Sym(name), _) => {
(Sym(name1), Sym(name2)) => {
name1 == name2
}
(Var(name), _) => {
if let Some(bound_value) = bindings.get(name) {
bound_value == value
} else {
Expand Down

0 comments on commit 5c5fc44

Please sign in to comment.