#[macro_use]
extern crate pest_derive;
use anyhow::{anyhow, Result};
use pest::iterators::Pairs;
use pest::Parser;
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use indexmap::IndexMap;
#[derive(Parser)]
#[grammar = "src/grammar.pest"]
struct Tokenizer;
#[derive(Default)]
pub struct ParseConfig {
pub use_64_bit_numbers: bool,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct ASTFunctionCall {
pub identifier: Identifier,
pub args: Vec<Element>,
pub scope: Option<Scope>,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum Definition {
Object(Object),
List(List),
}
impl PartialEq for Object {
fn eq(&self, other: &Self) -> bool {
if self.0.len() != other.0.len() {
return false;
}
for (k, v) in &self.0 {
let compare = other.0.get(k);
if compare.is_none() {
return false;
}
if !compare.unwrap().eq(v) {
return false;
}
}
true
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Object(pub IndexMap<Identifier, Element>);
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct List(pub Vec<Element>);
pub type Identifier = String;
pub type Symbol = String;
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum Value {
Int(i32),
Long(i64),
Float(f32),
Double(f64),
Bool(bool),
String(String),
Object(Object),
List(List),
FunctionCall(ASTFunctionCall),
Symbol(Symbol),
None,
}
impl Display for Value {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Value::Int(i) => write!(f, "{}", i),
Value::Long(l) => write!(f, "{}", l),
Value::Float(d) => write!(f, "{}", d),
Value::Double(d) => write!(f, "{}", d),
Value::Bool(b) => write!(f, "{}", b),
Value::String(s) => write!(f, "{}", s),
Value::Object(o) => write!(f, "{:?}", o),
Value::List(l) => write!(f, "{:?}", l),
Value::FunctionCall(fc) => write!(f, "{:?}", fc),
Value::Symbol(s) => write!(f, "{}", s),
Value::None => write!(f, "none"),
}
}
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum Element {
FunctionCall(ASTFunctionCall),
Identifier(Identifier),
Args(Vec<Element>),
Value(Value),
Object(Object),
List(List),
Int(i32),
Long(i64),
Float(f32),
Double(f64),
Bool(bool),
String(String),
NamedArg(Identifier, Value),
Symbol(Symbol),
Assignment(Mutability, Identifier, Value),
Scope(Scope),
ScopeArgs(Vec<Identifier>),
IdentifierAssignment(Identifier, Value),
Mutability(Mutability),
FunctionDefinition(Identifier, Vec<Element>, Scope),
IfElse(Value, Scope, Option<Scope>),
Import(Import),
Export(Export),
None,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct Import {
source: String,
import_definition: ExternalDefinition
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct Export {
import_definition: ExternalDefinition,
as_identifier: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum ExternalDefinition {
Identifier(String),
Object(Object),
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct Scope {
pub args: Vec<Identifier>,
pub elements: Vec<Element>,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum Mutability {
Let,
Mut
}
impl Element {
pub fn as_string(&self) -> Result<String> {
match self {
Element::String(v) => Ok(v.to_string()),
_ => Err(anyhow!("Unsupported type for as_string {}", self)),
}
}
pub fn to_list(self) -> Result<Vec<Element>> {
match self {
Element::List(v) => Ok(v.0),
_ => Err(anyhow!("Unsupported type for to_list {}", self)),
}
}
}
impl Display for Element {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Element::FunctionCall(fc) => write!(f, "{:?}", fc),
Element::Identifier(i) => write!(f, "{}", i),
Element::Args(a) => write!(f, "{:?}", a),
Element::Value(v) => write!(f, "{}", v),
Element::Object(o) => write!(f, "{:?}", o),
Element::List(l) => write!(f, "{:?}", l),
Element::Int(i) => write!(f, "{}", i),
Element::Long(l) => write!(f, "{}", l),
Element::Float(d) => write!(f, "{}", d),
Element::Double(d) => write!(f, "{}", d),
Element::Bool(b) => write!(f, "{}", b),
Element::String(s) => write!(f, "{}", s),
Element::None => write!(f, "none"),
Element::NamedArg(key, value) => write!(f, "{}: {}", key, value),
Element::Assignment(m, a, value) => write!(f, "{:?} {:?} = {:?}", m, a, value),
Element::Symbol(s) => write!(f, "{}", s),
Element::Scope(s) => write!(f, "{:?}", s),
Element::IdentifierAssignment(i, v) => write!(f, "{} = {}", i, v),
Element::Mutability(m) => write!(f, "{:?}", m),
Element::FunctionDefinition(i, a, s) => write!(f, "fn {} {:?} {:?}", i, a, s),
Element::IfElse(value, if_elements, else_elements) => write!(f, "if {} {:?} else {:?}", value, if_elements, else_elements),
Element::ScopeArgs(args) => write!(f, "({}) ->", args.join(", ")),
Element::Import(i) => write!(f, "{:?}", i),
Element::Export(e) => write!(f, "{:?}", e),
}
}
}
#[derive(Debug, PartialEq)]
pub struct AST {
pub elements: Vec<Element>,
}
impl AST {
pub fn init() -> AST {
AST { elements: vec![] }
}
pub fn merge(&mut self, other: AST) {
let mut to_append = other.elements;
self.elements.append(to_append.as_mut())
}
}
pub fn parse(input: String, config: &ParseConfig) -> Result<AST> {
let tokens = Tokenizer::parse(Rule::program, input.as_str())?;
let elements = parse_pairs(tokens, config)?;
Ok(AST { elements })
}
fn parse_pairs(pairs: Pairs<Rule>, config: &ParseConfig) -> Result<Vec<Element>> {
let mut results = Vec::new();
for pair in pairs {
match pair.as_rule() {
Rule::program => results.append(parse_pairs(pair.into_inner(), config)?.as_mut()),
Rule::rules => results.append(parse_pairs(pair.into_inner(), config)?.as_mut()),
Rule::function_body => results.append(parse_pairs(pair.into_inner(), config)?.as_mut()),
Rule::scope_args => {
let mut args = Vec::new();
for element in parse_pairs(pair.into_inner(), config)? {
match element {
Element::Identifier(i) => args.push(i),
_ => {
return Err(anyhow!("Unexpected Element in `scope_args`: {:?}", element));
}
}
}
results.push(Element::ScopeArgs(args));
}
Rule::scope => {
let inner = parse_pairs(pair.into_inner(), config)?;
let mut scope_args = None;
let mut scope = Vec::new();
for element in inner {
match element {
Element::ScopeArgs(args) => {
scope_args = Some(args)
}
_ => {
scope.push(element)
}
}
}
results.push(Element::Scope(Scope {
args: scope_args.unwrap_or(Vec::new()),
elements: scope,
}));
},
Rule::function_call => {
let inner = parse_pairs(pair.into_inner(), config)?;
let mut identifier = None;
let mut scope = None;
let mut args = Vec::new();
for element in inner {
match element {
Element::Identifier(i) => {
identifier = Some(i);
}
Element::Scope(s) => {
scope = Some(s);
}
Element::Args(mut a) => args.append(a.as_mut()),
_ => {
return Err(anyhow!(
"Unexpected Element in `function_call`: {:?}",
element
));
}
}
}
results.push(Element::FunctionCall(ASTFunctionCall {
identifier: identifier.expect("`identifier` not set for function_call"),
args,
scope,
}))
}
Rule::identifier => {
let identifier = pair.as_str().trim();
results.push(Element::Identifier(identifier.into()));
}
Rule::named_arg => {
let mut identifier = None;
let mut value = None;
for element in parse_pairs(pair.into_inner(), config)? {
match element {
Element::Identifier(id) => {
identifier = Some(id);
}
Element::Value(v) => {
value = Some(v);
}
_ => {
return Err(anyhow!("Unexpected Element in `named_arg`: {:?}", element));
}
}
}
let identifier = match identifier {
Some(i) => i,
None => {
return Err(anyhow!("`identifier` not set for named_arg"));
}
};
let value = match value {
Some(v) => v,
None => {
return Err(anyhow!("`value` not set for named_arg"));
}
};
results.push(Element::NamedArg(identifier, value));
}
Rule::args => results.push(Element::Args(parse_pairs(pair.into_inner(), config)?)),
Rule::value => {
let value = parse_pairs(pair.into_inner(), config)?;
for element in value {
let next = match element {
Element::Object(object) => Value::Object(object),
Element::List(list) => Value::List(list),
Element::Int(int) => Value::Int(int),
Element::Long(long) => Value::Long(long),
Element::Float(float) => Value::Float(float),
Element::Double(double) => Value::Double(double),
Element::Bool(bool) => Value::Bool(bool),
Element::String(string) => Value::String(string),
Element::FunctionCall(fc) => Value::FunctionCall(fc),
Element::Symbol(s) => Value::Symbol(s),
Element::None => Value::None,
_ => {
return Err(anyhow!("Unexpected Element in `value`: {:?}", element));
}
};
results.push(Element::Value(next));
}
}
Rule::object => {
let mut definition = IndexMap::new();
let mut last = None;
for element in parse_pairs(pair.into_inner(), config)? {
match element {
Element::Identifier(i) => {
if last.is_some() {
definition.insert(
last.expect("Missing Identifier for Object"),
Element::Identifier(i),
);
last = None;
} else {
last = Some(i);
}
}
Element::FunctionCall(f) => {
definition.insert(f.identifier.clone(), Element::FunctionCall(f));
}
Element::Value(v) => {
definition.insert(
last.expect("Missing Identifier for Object"),
Element::Value(v),
);
last = None;
}
_ => {
return Err(anyhow!("Unexpected Element in `object`: {:?}", element));
}
}
}
results.push(Element::Object(Object(definition)))
}
Rule::attribute => results.append(parse_pairs(pair.into_inner(), config)?.as_mut()),
Rule::list => {
results.push(Element::List(List(parse_pairs(pair.into_inner(), config)?)))
}
Rule::bool => {
let value = pair.as_str().trim();
let b = match value {
"true" => true,
"false" => false,
_ => {
return Err(anyhow!("Unsupported `bool`: {}", value));
}
};
results.push(Element::Bool(b));
}
Rule::number => {
let value = pair.as_str().trim();
let num = if value.contains('.') {
if config.use_64_bit_numbers {
Element::Float(value.parse()?)
} else {
Element::Double(value.parse()?)
}
} else if config.use_64_bit_numbers {
Element::Long(value.parse()?)
} else {
Element::Int(value.parse()?)
};
results.push(num);
}
Rule::string => {
let raw = pair.as_str().trim();
results.push(Element::String(raw[1..raw.len() - 1].to_string()));
}
Rule::VALID_CHARS => {
return Err(anyhow!(
"`VALID_CHARS` called directly, it should be handled in parent"
))
}
Rule::none => {
results.push(Element::None);
}
Rule::assignment => {
let mut mutability = Mutability::Let;
let mut assignment = None;
for element in parse_pairs(pair.into_inner(), config)? {
match element {
Element::Mutability(m) => {
mutability = m;
}
Element::IdentifierAssignment(i, v) => {
assignment = Some((i, v));
}
_ => {
return Err(anyhow!("Unexpected Element in `assignment`: {:?}", element));
}
}
}
let (identifier, value) = match assignment {
Some(a) => a,
None => {
return Err(anyhow!("`assignment` not set for assignment"));
}
};
results.push(Element::Assignment(mutability, identifier, value));
}
Rule::identifier_assignment => {
let mut identifier = None;
let mut value = None;
for element in parse_pairs(pair.into_inner(), config)? {
match element {
Element::Identifier(id) => {
identifier = Some(id);
}
Element::Value(v) => {
value = Some(v);
}
_ => {
return Err(anyhow!("Unexpected Element in `identifier_assignment`: {:?}", element));
}
}
}
let identifier = match identifier {
Some(i) => i,
None => {
return Err(anyhow!("`identifier` not set for identifier_assignment"));
}
};
let value = match value {
Some(v) => v,
None => {
return Err(anyhow!("`value` not set for identifier_assignment"));
}
};
results.push(Element::IdentifierAssignment(identifier, value));
}
Rule::function_definition => {
let mut function_name = None;
let mut args = None;
let mut scope = None;
for element in parse_pairs(pair.into_inner(), config)? {
match element {
Element::Identifier(i) => {
function_name = Some(i);
}
Element::Args(a) => {
args = Some(a);
}
Element::Scope(s) => {
scope = Some(s);
}
_ => {
return Err(anyhow!("Unexpected Element in `function_definition`: {:?}", element));
}
}
}
let function_name = match function_name {
Some(i) => i,
None => {
return Err(anyhow!("`function_name` not set for function_definition"));
}
};
let args = args.unwrap_or_else(|| Vec::new());
let scope = match scope {
Some(i) => i,
None => {
return Err(anyhow!("`scope` not set for function_definition"));
}
};
results.push(Element::FunctionDefinition(function_name, args, scope));
}
Rule::symbol => {
let raw = pair.as_str().trim();
results.push(Element::Symbol(raw.to_string()));
}
Rule::field => {
let raw = pair.as_str().trim().strip_suffix(":").unwrap();
results.push(Element::Identifier(raw.to_string()));
}
Rule::import => {
let mut source = None;
let mut import_definition = None;
for element in parse_pairs(pair.into_inner(), config)? {
match element {
Element::String(s) => {
source = Some(s);
}
Element::Object(o) => {
import_definition = Some(ExternalDefinition::Object(o));
}
Element::Identifier(i) => {
if import_definition.is_some() {
source = Some(i);
} else {
import_definition = Some(ExternalDefinition::Identifier(i));
}
}
_ => {
return Err(anyhow!("Unexpected Element in `import`: {:?}", element));
}
}
}
let source = match source {
Some(s) => s,
None => {
return Err(anyhow!("`source` not set for import"));
}
};
let import_definition = match import_definition {
Some(i) => i,
None => {
return Err(anyhow!("`import_definition` not set for import"));
}
};
results.push(Element::Import(Import {
source,
import_definition,
}));
}
Rule::import_source => {
for element in parse_pairs(pair.into_inner(), config)? {
match element {
Element::String(s) => {
results.push(Element::String(s));
}
Element::Identifier(i) => {
results.push(Element::Identifier(i));
}
_ => {
return Err(anyhow!("Unexpected Element in `import_source`: {:?}", element));
}
}
}
}
Rule::export => {
let mut import_definition = None;
let mut as_identifier = None;
for element in parse_pairs(pair.into_inner(), config)? {
match element {
Element::Object(o) => {
import_definition = Some(ExternalDefinition::Object(o));
}
Element::Identifier(i) => {
if import_definition.is_some() {
as_identifier = Some(i);
} else {
import_definition = Some(ExternalDefinition::Identifier(i));
}
}
_ => {
return Err(anyhow!("Unexpected Element in `export`: {:?}", element));
}
}
}
let import_definition = match import_definition {
Some(i) => i,
None => {
return Err(anyhow!("`import_definition` not set for export"));
}
};
results.push(Element::Export(Export {
import_definition,
as_identifier,
}));
}
Rule::if_else => {
let mut condition = None;
let mut if_elements = None;
let mut else_elements = None;
for element in parse_pairs(pair.into_inner(), config)? {
match element {
Element::Value(v) => {
condition = Some(v);
}
Element::Scope(s) => {
if if_elements.is_none() {
if_elements = Some(s);
} else {
else_elements = Some(s);
}
}
_ => {
return Err(anyhow!("Unexpected Element in `if_else`: {:?}", element));
}
}
}
let condition = match condition {
Some(v) => v,
None => {
return Err(anyhow!("`condition` not set for if_else"));
}
};
let if_elements = match if_elements {
Some(v) => v,
None => {
return Err(anyhow!("`if_elements` not set for if_else"));
}
};
results.push(Element::IfElse(condition, if_elements, else_elements));
}
Rule::assignment_operator => {
let raw = pair.as_str().trim();
let mutability = match raw {
"let" => Mutability::Let,
"mut" => Mutability::Mut,
_ => {
return Err(anyhow!("Unsupported `assignment_operator`: {}", raw));
}
};
results.push(Element::Mutability(mutability));
}
Rule::EOI => break,
Rule::COMMENT => continue,
Rule::single_line_comment => continue,
Rule::multi_line_comment => continue,
Rule::end => continue,
Rule::WHITESPACE => continue,
};
}
Ok(results)
}
#[cfg(test)]
macro_rules! syntax_test_pass {
($($name:ident: $input:expr,)*) => {
$(
#[test]
fn $name() {
let input = $input.to_string();
let result = parse(input, &ParseConfig::default());
assert!(result.is_ok(), "Failed to parse: {:?}", result);
}
)*
};
}
#[cfg(test)]
macro_rules! syntax_test_fail {
($($name:ident: $input:expr,)*) => {
$(
#[test]
fn $name() {
let input = $input.to_string();
let result = parse(input, &ParseConfig::default());
assert!(result.is_err(), "Expected to fail: {:?}", result);
}
)*
}
}
#[cfg(test)]
mod valid_syntax_tests {
use super::*;
syntax_test_pass! {
puts: "puts \"hi\"",
single_line: "# single line comment",
multi_line: r#"/**
* one
* two
*/"#,
define_empty_function: "fn test {}",
}
}
#[cfg(test)]
mod invalid_syntax_tests {
use super::*;
syntax_test_fail! {
missing_quote: "puts \"hi",
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn puts_works() {
let mut elements = Vec::new();
let mut args = Vec::new();
args.push(Element::Value(Value::String("Hello World".to_string())));
elements.push(Element::FunctionCall(ASTFunctionCall {
identifier: "puts".into(),
args,
scope: None,
}));
let result = parse("puts 'Hello World'".to_string(), &ParseConfig::default()).unwrap();
assert_eq!(result, AST { elements });
}
#[test]
fn let_works() {
let mut elements = Vec::new();
let accounts = [1, 2, 3]
.iter()
.map(|int| Element::Value(Value::Int(*int)))
.collect();
elements.push(Element::Assignment(Mutability::Let, "accounts".into(), Value::List(List(accounts))));
let input = r#"
let accounts = [1, 2, 3]
"#
.to_string();
let result = parse(input, &ParseConfig::default()).unwrap();
assert_eq!(result, AST { elements });
}
#[test]
fn symbols_work() {
let mut elements = Vec::new();
let mut details = Vec::new();
details.push(
Element::Assignment(Mutability::Let, "account".into(), Value::Symbol(":valid_account".to_string())),
);
let scope = Some(Scope {
args: Vec::new(),
elements: details,
});
elements.push(Element::FunctionCall(ASTFunctionCall {
identifier: "allow".to_string(),
args: Vec::new(),
scope,
}));
let input = r#"
allow {
account = :valid_account
}
"#
.to_string();
let result = parse(input, &ParseConfig::default()).unwrap();
assert_eq!(result, AST { elements });
}
#[test]
fn function_call_in_object_allowed() {
let mut elements = Vec::new();
let fc = Value::FunctionCall(ASTFunctionCall {
identifier: "one_of".to_string(),
args: vec![
Element::Value(Value::List(List(vec![
Element::Value(Value::Int(1)),
Element::Value(Value::Int(2)),
Element::Value(Value::Int(3)),
]))),
],
scope: None,
});
let inner = Scope {
args: Vec::new(),
elements: vec![
Element::Assignment(Mutability::Let, "account".into(), fc),
],
};
elements.push(Element::FunctionCall(ASTFunctionCall {
identifier: "allow".to_string(),
args: Vec::new(),
scope: Some(Scope {
args: Vec::new(),
elements: vec![
Element::FunctionCall(ASTFunctionCall {
identifier: "variables".to_string(),
args: vec![],
scope: Some(inner)
})
]})
}));
let input = r#"
allow {
variables {
account = one_of [1, 2, 3]
}
}
"#
.to_string();
let result = parse(input, &ParseConfig::default()).unwrap();
assert_eq!(result, AST { elements });
}
#[test]
fn named_args_new_line_separator() {
let input = r#"
change {
table :todo {
string :name, null: false
text :description
timestamps
}
}
"#;
let mut todo = Vec::new();
todo.push(
Element::FunctionCall(ASTFunctionCall {
identifier: "string".into(),
args: vec![
Element::Value(Value::Symbol(":name".into())),
Element::NamedArg("null".into(), Value::Bool(false)),
],
scope: None,
}),
);
todo.push(
Element::FunctionCall(ASTFunctionCall {
identifier: "text".into(),
args: vec![
Element::Value(Value::Symbol(":description".to_string())),
],
scope: None,
}),
);
todo.push(
Element::FunctionCall(ASTFunctionCall {
identifier: "timestamps".into(),
args: vec![],
scope: None,
}),
);
let expected: AST = AST {
elements: vec![Element::FunctionCall(ASTFunctionCall {
identifier: "change".into(),
args: vec![],
scope: Some(Scope {
args: Vec::new(),
elements: vec![Element::FunctionCall(ASTFunctionCall {
identifier: "table".into(),
args: vec![
Element::Value(Value::Symbol(":todo".to_string())),
],
scope: Some(Scope {
args: vec![],
elements: todo,
}),
})]}),
})],
};
assert_eq!(expected, parse(input.to_string(), &ParseConfig::default()).unwrap());
}
}