summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGreg Brown <gmb60@cam.ac.uk>2021-01-09 14:31:02 +0000
committerGreg Brown <gmb60@cam.ac.uk>2021-01-09 14:31:02 +0000
commit0d01692c97ea8ca6fc4b229e5b9678cb252bceda (patch)
tree6c5ed07740b814f50dddbc6afaefc21c11dc3440
parent487ce4fe0fa081f58d790d7d6417bf7d2659197c (diff)
Introduce chomp as a procedural macro.
Add a bunch of tests. Fix chomp and chewed so autochomp compiles.
-rw-r--r--Cargo.toml2
-rw-r--r--autochomp/Cargo.toml4
-rw-r--r--autochomp/build.rs78
-rw-r--r--autochomp/src/main.rs5
-rw-r--r--autochomp/src/nibble.nb67
-rw-r--r--autochomp/src/nibble.rs70
-rw-r--r--chewed/src/parse.rs10
-rw-r--r--chomp-macro/Cargo.toml18
-rw-r--r--chomp-macro/src/lib.rs42
-rw-r--r--chomp-macro/tests/nibble_exp.rs81
-rw-r--r--chomp-macro/tests/ratata.rs15
-rw-r--r--chomp-macro/tests/regex.rs27
-rw-r--r--chomp-macro/tests/regex_fix.rs11
-rw-r--r--chomp-macro/tests/sheep.rs15
-rw-r--r--src/chomp/check/inline.rs119
-rw-r--r--src/chomp/error.rs17
-rw-r--r--src/lower/rust.rs5
17 files changed, 427 insertions, 159 deletions
diff --git a/Cargo.toml b/Cargo.toml
index ca41e36..62fcc65 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,7 +5,7 @@ authors = ["Greg Brown <gmb60@cam.ac.uk>"]
edition = "2018"
[workspace]
-members = ["autochomp", "chewed"]
+members = ["autochomp", "chewed", "chomp-macro"]
[dependencies]
quote = "1"
diff --git a/autochomp/Cargo.toml b/autochomp/Cargo.toml
index 4e20c49..6dfd491 100644
--- a/autochomp/Cargo.toml
+++ b/autochomp/Cargo.toml
@@ -4,12 +4,10 @@ version = "0.1.0"
authors = ["Greg Brown <gmb60@cam.ac.uk>"]
edition = "2018"
-[build-dependencies]
-chomp = {path = ".."}
-
[build-dependencies.syn]
version = "1"
features = ["extra-traits"]
[dependencies]
chewed = {path = "../chewed"}
+chomp-macro = {path = "../chomp-macro"}
diff --git a/autochomp/build.rs b/autochomp/build.rs
deleted file mode 100644
index 58c12a1..0000000
--- a/autochomp/build.rs
+++ /dev/null
@@ -1,78 +0,0 @@
-use std::{
- env,
- error::Error,
- fmt::Display,
- fs,
- io::{Read, Write},
- path::Path,
- process::Command,
-};
-
-use chomp::{
- chomp::{
- check::{InlineCall, TypeCheck},
- context::Context,
- visit::Visitable,
- },
- lower::{rust::RustBackend, Backend, GenerateCode},
- nibble::cst::File,
-};
-
-const PATH: &str = "src/nibble.nb";
-
-#[derive(Debug)]
-struct UndecVar;
-
-impl Display for UndecVar {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "Undeclared variable somewhere.")
- }
-}
-impl Error for UndecVar {}
-
-fn main() {
- println!("cargo:rerun-if-changed={}", PATH);
-
- let out_dir = env::var("OUT_DIR").unwrap();
-
- let mut input = String::new();
-
- fs::File::open(PATH)
- .map_err(|e| Box::new(e) as Box<dyn Error>)
- .and_then(|mut file| {
- file.read_to_string(&mut input)
- .map_err(|e| Box::new(e) as Box<dyn Error>)
- })
- .and_then(|_| syn::parse_str(&input).map_err(|e| Box::new(e) as Box<dyn Error>))
- .and_then(|nibble: File| nibble.convert().ok_or(Box::new(UndecVar) as Box<dyn Error>))
- .and_then(|(funs, goal)| {
- funs.into_iter()
- .try_rfold(goal, |goal, function| {
- goal.fold(&mut InlineCall::new(function))
- })
- .map_err(|e| Box::new(e) as Box<dyn Error>)
- })
- .and_then(|term| {
- let mut context = Context::default();
- term.fold(&mut TypeCheck {
- context: &mut context,
- })
- .map_err(|e| Box::new(e) as Box<dyn Error>)
- })
- .map(|typed| {
- let mut backend = RustBackend::default();
- let id = typed.gen(&mut backend);
- backend.emit_code(id)
- })
- .and_then(|code| {
- fs::File::create(Path::new(&out_dir).join("nibble.rs"))
- .and_then(|mut f| write!(f, "{}", code))
- .map_err(|e| Box::new(e) as Box<dyn Error>)
- })
- .unwrap();
-
- Command::new("rustfmt")
- .arg(&format!("{}/nibble.rs", out_dir))
- .status()
- .unwrap();
-}
diff --git a/autochomp/src/main.rs b/autochomp/src/main.rs
index 9b3b521..493204d 100644
--- a/autochomp/src/main.rs
+++ b/autochomp/src/main.rs
@@ -1,15 +1,12 @@
use std::{
error::Error,
- fs,
io::{self, Read, Write},
process::exit,
};
use chewed::{IterWrapper, Parser};
-mod nibble {
- include!(concat!(env!("OUT_DIR"), "/nibble.rs"));
-}
+mod nibble;
fn main() {
let mut input = String::new();
diff --git a/autochomp/src/nibble.nb b/autochomp/src/nibble.nb
deleted file mode 100644
index d454adf..0000000
--- a/autochomp/src/nibble.nb
+++ /dev/null
@@ -1,67 +0,0 @@
-let opt(x) = _ | x;
-let plus(x) = [plus](x . opt(plus));
-let star(x) = opt(plus(x));
-let star_(base, step) = [rec](base | step . rec);
-
-let Pattern_Whitespace = "\t"|"\n"|"\x0B"|"\x0c"|"\r"|" "|"\u{85}"|"\u{200e}"|"\u{200f}"|"\u{2028}"|"\u{2029}";
-
-let oct_digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" ;
-let digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
-let hex_digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" |
- "a" | "b" | "c" | "d" | "e" | "f" |
- "A" | "B" | "C" | "D" | "E" | "F" ;
-
-let XID_Start =
- "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" |
- "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" |
- "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" |
- "y" | "z" |
- "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" |
- "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" |
- "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" |
- "Y" | "Z" ;
-let XID_Continue =
- XID_Start | "_" | "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
-
-let literal_char =
- " " | "!" | "#" | "$" | "%" | "&" | "'" |
- "(" | ")" | "*" | "+" | "," | "-" | "." | "/" |
- "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
- "8" | "9" | ":" | ";" | "<" | "=" | ">" | "?" |
- "@" | "A" | "B" | "C" | "D" | "E" | "F" | "G" |
- "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" |
- "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" |
- "X" | "Y" | "Z" | "[" | "]" | "^" | "_" |
- "`" | "a" | "b" | "c" | "d" | "e" | "f" | "g" |
- "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" |
- "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" |
- "x" | "y" | "z" | "{" | "|" | "}" | "~" |
- "\\" . ("\"" | "'" | "n" | "r" | "t" | "\\" | "0" |
- "x" . oct_digit . hex_digit |
- "u{" . hex_digit . opt(hex_digit . opt(hex_digit . opt(hex_digit . opt(hex_digit . opt(hex_digit))))) . "}");
-
-let ws = plus(Pattern_Whitespace);
-
-let punctuated(x, p) = [rec](x . opt(p . opt(ws) . rec));
-let list(x) = "(" . opt(ws) . [rec](x . opt("," . opt(ws) . opt(rec))) . ")";
-
-let epsilon = "_";
-let ident = XID_Start . star(XID_Continue);
-let literal = "\"" . plus(literal_char) . "\"";
-let parens(expr) = "(" . opt(ws) . expr . ")";
-let fix(expr) = "[" . opt(ws) . ident . opt(ws) . "]" . opt(ws) . parens(expr);
-
-let term(expr) =
- epsilon . opt(ws)
- | literal . opt(ws)
- | parens(expr) . opt(ws)
- | fix(expr) . opt(ws)
- | ident . opt(ws) . opt(list(expr) . opt(ws))
- ;
-
-let seq(expr) = punctuated(term(expr), ".");
-let alt(expr) = punctuated(seq(expr), "|");
-let expr = [expr](alt(expr));
-let let = "let" . ws . ident . opt(ws) . opt(list(ident . opt(ws)) . opt(ws)) . "=" . opt(ws) . expr . ";";
-let goal = "match" . ws . expr . ";";
-match star_(goal, let);
diff --git a/autochomp/src/nibble.rs b/autochomp/src/nibble.rs
new file mode 100644
index 0000000..f6629f2
--- /dev/null
+++ b/autochomp/src/nibble.rs
@@ -0,0 +1,70 @@
+chomp_macro::nibble! {
+ let opt(x) = _ | x;
+ let plus(x) = [plus](x . opt(plus));
+ let star(x) = opt(plus(x));
+ let star_(base, step) = [rec](base | step . rec);
+
+ let Pattern_Whitespace = "\t"|"\n"|"\x0B"|"\x0c"|"\r"|" "|"\u{85}"|"\u{200e}"|"\u{200f}"|"\u{2028}"|"\u{2029}";
+
+ let oct_digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" ;
+ let digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
+ let hex_digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" |
+ "a" | "b" | "c" | "d" | "e" | "f" |
+ "A" | "B" | "C" | "D" | "E" | "F" ;
+
+ let XID_Start =
+ "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" |
+ "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" |
+ "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" |
+ "y" | "z" |
+ "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" |
+ "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" |
+ "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" |
+ "Y" | "Z" ;
+ let XID_Continue =
+ XID_Start | "_" | "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
+
+ let literal_char =
+ " " | "!" | "#" | "$" | "%" | "&" | "'" |
+ "(" | ")" | "*" | "+" | "," | "-" | "." | "/" |
+ "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
+ "8" | "9" | ":" | ";" | "<" | "=" | ">" | "?" |
+ "@" | "A" | "B" | "C" | "D" | "E" | "F" | "G" |
+ "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" |
+ "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" |
+ "X" | "Y" | "Z" | "[" | "]" | "^" | "_" |
+ "`" | "a" | "b" | "c" | "d" | "e" | "f" | "g" |
+ "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" |
+ "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" |
+ "x" | "y" | "z" | "{" | "|" | "}" | "~" |
+ "\\" . ("\"" | "'" | "n" | "r" | "t" | "\\" | "0" |
+ "x" . oct_digit . hex_digit |
+ "u{" . hex_digit . opt(hex_digit . opt(hex_digit . opt(hex_digit . opt(hex_digit . opt(hex_digit))))) . "}");
+
+ let ws = plus(Pattern_Whitespace);
+
+ let punctuated(x, p) = [rec](x . opt(p . opt(ws) . rec));
+ let list(x) = "(" . opt(ws) . [rec](x . opt("," . opt(ws) . opt(rec))) . ")";
+
+ let epsilon = "_";
+ let ident = XID_Start . star(XID_Continue);
+ let literal = "\"" . plus(literal_char) . "\"";
+ let parens(expr) = "(" . opt(ws) . expr . ")";
+ let fix(expr) = "[" . opt(ws) . ident . opt(ws) . "]" . opt(ws) . parens(expr);
+
+ let term(expr) =
+ epsilon . opt(ws)
+ | literal . opt(ws)
+ | parens(expr) . opt(ws)
+ | fix(expr) . opt(ws)
+ | ident . opt(ws) . opt(list(expr) . opt(ws))
+ ;
+
+ let cat(expr) = punctuated(term(expr), ".");
+ let alt(expr) = punctuated(cat(expr), "|");
+ let expr = [expr](alt(expr));
+ let let = "let" . ws . ident . opt(ws) . opt(list(ident . opt(ws)) . opt(ws)) . "=" . opt(ws) . expr . ";" . opt(ws);
+ let goal = "match" . ws . expr . ";" . opt(ws);
+
+ match star_(star_(goal, let), Pattern_Whitespace);
+}
diff --git a/chewed/src/parse.rs b/chewed/src/parse.rs
index 58bb94c..65e9272 100644
--- a/chewed/src/parse.rs
+++ b/chewed/src/parse.rs
@@ -21,7 +21,7 @@ pub trait Parser {
P::parse(self)
}
- fn take_str(&mut self, s: &'static str) -> Result<(), TakeError> {
+ fn consume_str(&mut self, s: &'static str) -> Result<(), TakeError> {
let mut count = 0;
for exp in s.chars() {
@@ -114,6 +114,14 @@ pub trait Parse: Sized {
Ok(res)
}
}
+
+ fn take_str(input: &str) -> Result<Self, TakeError> {
+ Self::take(&mut IterWrapper::new(input.chars()))
+ }
+
+ fn parse_str(input: &str) -> Result<Self, ParseError> {
+ Self::parse(IterWrapper::new(input.chars()))
+ }
}
impl Parse for () {
diff --git a/chomp-macro/Cargo.toml b/chomp-macro/Cargo.toml
new file mode 100644
index 0000000..bc8dce0
--- /dev/null
+++ b/chomp-macro/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "chomp-macro"
+version = "0.1.0"
+authors = ["Greg Brown <gmb60@cam.ac.uk>"]
+edition = "2018"
+
+[dependencies]
+chomp = {path = ".."}
+
+[dependencies.syn]
+version = "1"
+features = ["parsing", "proc-macro"]
+
+[dev-dependencies]
+chewed = {path = "../chewed"}
+
+[lib]
+proc_macro = true
diff --git a/chomp-macro/src/lib.rs b/chomp-macro/src/lib.rs
new file mode 100644
index 0000000..d58bbdc
--- /dev/null
+++ b/chomp-macro/src/lib.rs
@@ -0,0 +1,42 @@
+use chomp::{
+ chomp::{
+ check::{InlineCalls, TypeCheck},
+ context::Context,
+ visit::Visitable,
+ },
+ lower::{rust::RustBackend, Backend, GenerateCode},
+ nibble::cst::File,
+};
+use proc_macro::{Span, TokenStream};
+use syn::Error;
+
+#[proc_macro]
+pub fn nibble(item: TokenStream) -> TokenStream {
+ syn::parse(item)
+ .and_then(|nibble: File| {
+ nibble
+ .convert()
+ .ok_or_else(|| todo!())
+ })
+ .and_then(|(funs, goal)| {
+ funs.into_iter()
+ .try_rfold(goal, |goal, function| {
+ goal.fold(&mut InlineCalls::new(function))
+ })
+ .map_err(Error::from)
+ })
+ .and_then(|expr| {
+ let mut context = Context::default();
+ expr.fold(&mut TypeCheck {
+ context: &mut context,
+ })
+ .map_err(Error::from)
+ })
+ .map(|typed| {
+ let mut backend = RustBackend::default();
+ let id = typed.gen(&mut backend);
+ backend.emit_code(id)
+ })
+ .unwrap_or_else(Error::into_compile_error)
+ .into()
+}
diff --git a/chomp-macro/tests/nibble_exp.rs b/chomp-macro/tests/nibble_exp.rs
new file mode 100644
index 0000000..7e31e6a
--- /dev/null
+++ b/chomp-macro/tests/nibble_exp.rs
@@ -0,0 +1,81 @@
+chomp_macro::nibble! {
+ let opt(x) = _ | x;
+ let plus(x) = [plus](x . opt(plus));
+ let star(x) = [star](opt(x . star));
+
+ let Pattern_Whitespace = "\n"|" ";
+
+ let XID_Start =
+ "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" |
+ "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" |
+ "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" |
+ "y" | "z" |
+ "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" |
+ "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" |
+ "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" |
+ "Y" | "Z" ;
+ let XID_Continue =
+ XID_Start | "_" | "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
+
+ let literal_char = XID_Continue;
+
+ let ws = star(Pattern_Whitespace);
+ let must_ws = plus(Pattern_Whitespace);
+
+ let punctuated(x, p) = [rec](x . opt(p . ws . rec));
+ let list(x) = "(" . ws . [rec](x . opt("," . ws . opt(rec))) . ")";
+
+ let epsilon = "_";
+ let ident = XID_Start . star(XID_Continue);
+ let literal = "\"" . plus(literal_char) . "\"";
+ let parens(expr) = "(" . ws . expr . ")";
+ let fix(expr) = "[" . ws . ident . ws . "]" . ws . parens(expr);
+
+ let term(expr) =
+ epsilon . ws
+ | literal . ws
+ | parens(expr) . ws
+ | fix(expr) . ws
+ | ident . ws . opt(list(expr) . ws)
+ ;
+
+ let cat(expr) = punctuated(term(expr), ".");
+ let alt(expr) = punctuated(cat(expr), "|");
+ let expr = [expr](alt(expr));
+ match expr;
+}
+
+#[test]
+fn exp_epsilon() {
+ Ast::parse_str("_").unwrap();
+}
+
+#[test]
+fn exp_literal() {
+ Ast::parse_str(r#""foo""#).unwrap();
+}
+
+#[test]
+fn exp_cat() {
+ Ast::parse_str(r#""a" . "b""#).unwrap();
+}
+
+#[test]
+fn exp_alt() {
+ Ast::parse_str(r#""a" | "b""#).unwrap();
+}
+
+#[test]
+fn exp_ident() {
+ Ast::parse_str("foo").unwrap();
+}
+
+#[test]
+fn exp_call() {
+ Ast::parse_str(r#"opt("foo")"#).unwrap();
+}
+
+#[test]
+fn exp_fix() {
+ Ast::parse_str(r#"[rec](_ | "a" . rec)"#).unwrap();
+}
diff --git a/chomp-macro/tests/ratata.rs b/chomp-macro/tests/ratata.rs
new file mode 100644
index 0000000..869ad61
--- /dev/null
+++ b/chomp-macro/tests/ratata.rs
@@ -0,0 +1,15 @@
+chomp_macro::nibble! {
+ let opt(x) = _ | x;
+ let plus(x) = [rec](x . opt(rec));
+ match plus(("r" | "t") . "a");
+}
+
+#[test]
+fn ratata_ratata() {
+ Ast::parse_str("ratata").unwrap();
+}
+
+#[test]
+fn ratata_ratarataratatata() {
+ Ast::parse_str("ratarataratatata").unwrap();
+}
diff --git a/chomp-macro/tests/regex.rs b/chomp-macro/tests/regex.rs
new file mode 100644
index 0000000..1e831b4
--- /dev/null
+++ b/chomp-macro/tests/regex.rs
@@ -0,0 +1,27 @@
+chomp_macro::nibble! {
+ let opt(x) = _ | x;
+ let plus(x) = [plus](x . opt(plus));
+ let star(x) = [star](opt(x . star));
+
+ match plus("a") . star("b");
+}
+
+#[test]
+fn regex_a() {
+ Ast::parse_str("a").unwrap();
+}
+
+#[test]
+fn regex_ab() {
+ Ast::parse_str("ab").unwrap();
+}
+
+#[test]
+fn regex_aa() {
+ Ast::parse_str("aa").unwrap();
+}
+
+#[test]
+fn regex_aaaabbb() {
+ Ast::parse_str("aaaabbb").unwrap();
+}
diff --git a/chomp-macro/tests/regex_fix.rs b/chomp-macro/tests/regex_fix.rs
new file mode 100644
index 0000000..9842484
--- /dev/null
+++ b/chomp-macro/tests/regex_fix.rs
@@ -0,0 +1,11 @@
+chomp_macro::nibble! {
+ let opt(x) = _ | x;
+ let ws = [star](opt(" " . star));
+
+ match [rec]("a" . opt("." . ws . rec));
+}
+
+#[test]
+fn regex_fix_cat() {
+ Ast::parse_str(r#"a. a"#).unwrap();
+}
diff --git a/chomp-macro/tests/sheep.rs b/chomp-macro/tests/sheep.rs
new file mode 100644
index 0000000..862e810
--- /dev/null
+++ b/chomp-macro/tests/sheep.rs
@@ -0,0 +1,15 @@
+chomp_macro::nibble! {
+ let opt(x) = _ | x;
+ let plus(x) = [rec](x . opt(rec));
+ match "ba" . plus("a");
+}
+
+#[test]
+fn baa() {
+ Ast::parse_str("baa").unwrap();
+}
+
+#[test]
+fn baaaaaaaaaaaaaaa() {
+ Ast::parse_str("baaaaaaaaaaaaaaa").unwrap();
+}
diff --git a/src/chomp/check/inline.rs b/src/chomp/check/inline.rs
index 43a2eb8..da501f1 100644
--- a/src/chomp/check/inline.rs
+++ b/src/chomp/check/inline.rs
@@ -158,7 +158,7 @@ mod tests {
None,
)
.into(),
- None
+ None,
);
let inlined = expr.fold(&mut InlineCalls::new(function));
assert_eq!(
@@ -208,7 +208,7 @@ mod tests {
None,
)
.into(),
- None
+ None,
);
let inlined = expr.fold(&mut InlineCalls::new(function));
assert_eq!(
@@ -231,4 +231,119 @@ mod tests {
.into())
)
}
+
+ #[test]
+ fn test_inline_double_subst() {
+ let expr = Call::new(
+ Name::Spanless("opt".to_string()),
+ vec![Call::new(
+ Name::Spanless("opt".to_string()),
+ vec![Literal::Spanless("x".to_string()).into()],
+ None,
+ )
+ .into()],
+ None,
+ );
+ let inlined = expr.fold(&mut InlineCalls::new(opt()));
+ assert_eq!(
+ inlined,
+ Ok(Alt::new(
+ Epsilon::default().into(),
+ None,
+ Alt::new(
+ Epsilon::default().into(),
+ None,
+ Literal::Spanless("x".to_string()).into()
+ )
+ .into()
+ )
+ .into())
+ )
+ }
+
+ #[test]
+ fn test_inline_call_args() {
+ let expr = Fix::new(
+ Name::Spanless("rec".to_string()),
+ Cat::new(
+ Literal::Spanless("a".to_string()).into(),
+ None,
+ Call::new(
+ Name::Spanless("opt".to_string()),
+ vec![Cat::new(
+ Cat::new(
+ Literal::Spanless("a".to_string()).into(),
+ None,
+ Fix::new(
+ Name::Spanless("star".to_string()),
+ Call::new(
+ Name::Spanless("opt".to_string()),
+ vec![Cat::new(
+ Literal::Spanless(" ".to_string()).into(),
+ None,
+ Variable::new(Name::Spanless("star".to_string()), 0).into(),
+ )
+ .into()],
+ None,
+ )
+ .into(),
+ None,
+ )
+ .into(),
+ )
+ .into(),
+ None,
+ Variable::new(Name::Spanless("rec".to_string()), 0).into(),
+ )
+ .into()],
+ None,
+ )
+ .into(),
+ )
+ .into(),
+ None,
+ );
+ let inlined = expr.fold(&mut InlineCalls::new(opt()));
+ assert_eq!(inlined,
+ Ok(Fix::new(
+ Name::Spanless("rec".to_string()),
+ Cat::new(
+ Literal::Spanless("a".to_string()).into(),
+ None,
+ Alt::new(
+ Epsilon::default().into(),
+ None,
+ Cat::new(
+ Cat::new(
+ Literal::Spanless("a".to_string()).into(),
+ None,
+ Fix::new(
+ Name::Spanless("star".to_string()),
+ Alt::new(
+ Epsilon::default().into(),
+ None,
+ Cat::new(
+ Literal::Spanless(" ".to_string()).into(),
+ None,
+ Variable::new(Name::Spanless("star".to_string()), 0).into(),
+ )
+ .into()
+ )
+ .into(),
+ None,
+ )
+ .into(),
+ )
+ .into(),
+ None,
+ Variable::new(Name::Spanless("rec".to_string()), 0).into(),
+ )
+ .into(),
+ )
+ .into(),
+ )
+ .into(),
+ None,
+ ).into()))
+ }
}
diff --git a/src/chomp/error.rs b/src/chomp/error.rs
index ffcbdd0..e7e4660 100644
--- a/src/chomp/error.rs
+++ b/src/chomp/error.rs
@@ -358,6 +358,19 @@ pub enum SubstituteError {
WrongArgCount { call: Call, expected: usize },
}
+impl From<SubstituteError> for syn::Error {
+ fn from(e: SubstituteError) -> Self {
+ match e {
+ SubstituteError::FreeParameter(param) => {
+ Self::new(param.name().span().unwrap_or_else(Span::call_site), format!("undeclared variable `{}'", param.name()))
+ }
+ SubstituteError::WrongArgCount { call, expected } => {
+ Self::new(call.span().unwrap_or_else(Span::call_site), format!("wrong number of arguments. Expected {}, got {}", expected, call.args().len()))
+ }
+ }
+ }
+}
+
impl Display for SubstituteError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
@@ -378,8 +391,8 @@ impl Display for SubstituteError {
"{}:{}: wrong number of arguments. Expected {}, got {}",
start.line,
start.column,
- call.args().len(),
- expected
+ expected,
+ call.args().len()
)
}
}
diff --git a/src/lower/rust.rs b/src/lower/rust.rs
index 74deb73..7931306 100644
--- a/src/lower/rust.rs
+++ b/src/lower/rust.rs
@@ -75,7 +75,7 @@ impl Backend for RustBackend {
impl Parse for #name {
fn take<P: Parser + ?Sized>(input: &mut P) -> Result<Self, TakeError> {
- input.take_str(#lit).map(|()| #name)
+ input.consume_str(#lit).map(|()| #name)
}
}
};
@@ -206,8 +206,11 @@ impl Backend for RustBackend {
TokenStream::new(),
BTreeSet::new(),
));
+
self.context.push(id);
let inner = inner.gen(self);
+ self.context.pop();
+
let inner_ty = self.data[inner].0.clone();
let tokens = quote! {
#[derive(Clone, Debug, Eq, Hash, PartialEq)]