mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
cargo fmt pass with the new rustfmt-preview
This commit is contained in:
parent
f105d93c14
commit
bafe5b3c30
184
src/ast.rs
184
src/ast.rs
@ -25,7 +25,7 @@ use std::hash::Hasher;
|
||||
use std::hash::Hash;
|
||||
|
||||
/// Encodes a parsing error with position information and a helpful description.
|
||||
#[derive(Debug,PartialEq)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ParseError {
|
||||
pub pos: Position,
|
||||
pub description: String,
|
||||
@ -33,11 +33,11 @@ pub struct ParseError {
|
||||
|
||||
impl std::fmt::Display for ParseError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||
write!(f,
|
||||
"Parsing Error {} at line: {} column: {}",
|
||||
self.description,
|
||||
self.pos.line,
|
||||
self.pos.column)
|
||||
write!(
|
||||
f,
|
||||
"Parsing Error {} at line: {} column: {}",
|
||||
self.description, self.pos.line, self.pos.column
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,7 +47,6 @@ impl std::error::Error for ParseError {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
macro_rules! enum_type_equality {
|
||||
( $slf:ident, $r:expr, $( $l:pat ),* ) => {
|
||||
match $slf {
|
||||
@ -65,10 +64,10 @@ macro_rules! enum_type_equality {
|
||||
}
|
||||
|
||||
/// Represents a line and a column position in UCG code.
|
||||
///
|
||||
///
|
||||
/// It is used for generating error messages mostly. Most all
|
||||
/// parts of the UCG AST have a positioned associated with them.
|
||||
#[derive(Debug,PartialEq,Eq,Clone,PartialOrd,Ord,Hash)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
|
||||
pub struct Position {
|
||||
pub line: usize,
|
||||
pub column: usize,
|
||||
@ -85,7 +84,7 @@ impl Position {
|
||||
}
|
||||
|
||||
/// Defines the types of tokens in UCG syntax.
|
||||
#[derive(Debug,PartialEq,Eq,Clone,PartialOrd,Ord,Hash)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
|
||||
pub enum TokenType {
|
||||
END,
|
||||
WS,
|
||||
@ -99,9 +98,9 @@ pub enum TokenType {
|
||||
// FIXME(jwall): We should probably implement copy for this.
|
||||
|
||||
/// Defines a Token representing a building block of UCG syntax.
|
||||
///
|
||||
///
|
||||
/// Token's are passed to the parser stage to be parsed into an AST.
|
||||
#[derive(Debug,PartialEq,Eq,Clone,PartialOrd,Ord,Hash)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
|
||||
pub struct Token {
|
||||
pub typ: TokenType,
|
||||
pub fragment: String,
|
||||
@ -146,27 +145,27 @@ macro_rules! make_tok {
|
||||
( EOF => $l:expr, $c:expr ) => {
|
||||
Token::new("", TokenType::END, $l, $c)
|
||||
};
|
||||
|
||||
|
||||
( WS => $l:expr, $c:expr ) => {
|
||||
Token::new("", TokenType::WS, $l, $c)
|
||||
};
|
||||
|
||||
|
||||
( CMT => $e:expr, $l:expr, $c:expr ) => {
|
||||
Token::new($e, TokenType::COMMENT, $l, $c)
|
||||
};
|
||||
|
||||
|
||||
( QUOT => $e:expr, $l:expr, $c:expr ) => {
|
||||
Token::new($e, TokenType::QUOTED, $l, $c)
|
||||
};
|
||||
|
||||
|
||||
( PUNCT => $e:expr, $l:expr, $c:expr ) => {
|
||||
Token::new($e, TokenType::PUNCT, $l, $c)
|
||||
};
|
||||
|
||||
|
||||
( DIGIT => $e:expr, $l:expr, $c:expr ) => {
|
||||
Token::new($e, TokenType::DIGIT, $l, $c)
|
||||
};
|
||||
|
||||
|
||||
( $e:expr, $l:expr, $c:expr ) => {
|
||||
Token::new($e, TokenType::BAREWORD, $l, $c)
|
||||
};
|
||||
@ -182,7 +181,7 @@ macro_rules! make_expr {
|
||||
( $e:expr, $l:expr, $c:expr ) => {
|
||||
Expression::Simple(Value::Symbol(Positioned::new($e.to_string(), $l, $c)))
|
||||
};
|
||||
|
||||
|
||||
( $e:expr => int, $l:expr, $c:expr ) => {
|
||||
Expression::Simple(Value::Int(Positioned::new($e, $l, $c)))
|
||||
};
|
||||
@ -223,15 +222,15 @@ macro_rules! make_selector {
|
||||
make_selector!($h => [ $( $item, )* ] => 1, 1)
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
( $h:expr => [ $( $item:expr ),* ] => $l:expr, $c:expr ) => {
|
||||
{
|
||||
let mut list: Vec<Token> = Vec::new();
|
||||
|
||||
|
||||
$(
|
||||
list.push($item);
|
||||
)*
|
||||
|
||||
|
||||
make_selector!($h, list, $l, $c)
|
||||
}
|
||||
};
|
||||
@ -242,25 +241,25 @@ macro_rules! make_selector {
|
||||
|
||||
let mut col = 1;
|
||||
let mut list: Vec<Token> = Vec::new();
|
||||
|
||||
|
||||
$(
|
||||
list.push(make_tok!($item, 1, col));
|
||||
col += $item.len() + 1;
|
||||
)*
|
||||
|
||||
|
||||
// Shut up the lint about unused code;
|
||||
assert!(col != 0);
|
||||
|
||||
make_selector!($h, list, 1, 1)
|
||||
make_selector!($h, list, 1, 1)
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
( $h:expr => $( $item:expr ),* => $l:expr, $c:expr ) => {
|
||||
{
|
||||
let mut col = $c;
|
||||
let mut list: Vec<Token> = Vec::new();
|
||||
|
||||
|
||||
$(
|
||||
list.push(make_tok!($item, $l, col));
|
||||
col += $item.len() + 1;
|
||||
@ -269,7 +268,7 @@ macro_rules! make_selector {
|
||||
// Shut up the linter about unused code;
|
||||
assert!(col != 0);
|
||||
|
||||
make_selector!($h, list, $l, $c)
|
||||
make_selector!($h, list, $l, $c)
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -290,7 +289,7 @@ macro_rules! make_selector {
|
||||
/// let berry = {best = "strawberry", unique = "acai"}.best;
|
||||
/// let third = ["uno", "dos", "tres"].1;
|
||||
/// '''
|
||||
#[derive(Debug,PartialEq,Clone)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct SelectorList {
|
||||
pub head: Box<Expression>,
|
||||
pub tail: Option<Vec<Token>>,
|
||||
@ -304,12 +303,12 @@ impl SelectorList {
|
||||
}
|
||||
|
||||
/// An ordered list of Name = Value pairs.
|
||||
///
|
||||
///
|
||||
/// This is usually used as the body of a tuple in the UCG AST.
|
||||
pub type FieldList = Vec<(Token, Expression)>; // Token is expected to be a symbol
|
||||
|
||||
/// Encodes a selector expression in the UCG AST.
|
||||
#[derive(Debug,PartialEq,Clone)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct SelectorDef {
|
||||
pub pos: Position,
|
||||
pub sel: SelectorList,
|
||||
@ -326,7 +325,7 @@ impl SelectorDef {
|
||||
}
|
||||
|
||||
/// Represents a Value in the UCG parsed AST.
|
||||
#[derive(Debug,PartialEq,Clone)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Value {
|
||||
// Constant Values
|
||||
Int(Positioned<i64>),
|
||||
@ -397,19 +396,23 @@ impl Value {
|
||||
|
||||
/// Returns true if called on a Value that is the same type as itself.
|
||||
pub fn type_equal(&self, target: &Self) -> bool {
|
||||
enum_type_equality!(self, target, &Value::Int(_),
|
||||
&Value::Float(_),
|
||||
&Value::String(_),
|
||||
&Value::Symbol(_),
|
||||
&Value::Tuple(_),
|
||||
&Value::List(_),
|
||||
&Value::Selector(_))
|
||||
enum_type_equality!(
|
||||
self,
|
||||
target,
|
||||
&Value::Int(_),
|
||||
&Value::Float(_),
|
||||
&Value::String(_),
|
||||
&Value::Symbol(_),
|
||||
&Value::Tuple(_),
|
||||
&Value::List(_),
|
||||
&Value::Selector(_)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an expansion of a Macro that is expected to already have been
|
||||
/// defined.
|
||||
#[derive(PartialEq,Debug,Clone)]
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct CallDef {
|
||||
pub macroref: SelectorDef,
|
||||
pub arglist: Vec<Expression>,
|
||||
@ -417,7 +420,7 @@ pub struct CallDef {
|
||||
}
|
||||
|
||||
/// Encodes a select expression in the UCG AST.
|
||||
#[derive(PartialEq,Debug,Clone)]
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct SelectDef {
|
||||
pub val: Box<Expression>,
|
||||
pub default: Box<Expression>,
|
||||
@ -428,7 +431,7 @@ pub struct SelectDef {
|
||||
// TODO(jwall): This should have a way of rendering with position information.
|
||||
|
||||
/// Adds position information to any type `T`.
|
||||
#[derive(Debug,Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Positioned<T> {
|
||||
pub pos: Position,
|
||||
pub val: T,
|
||||
@ -495,7 +498,7 @@ impl<'a> From<&'a Positioned<String>> for Positioned<String> {
|
||||
/// A macro is a pure function over a tuple.
|
||||
/// MacroDefs are not closures. They can not reference
|
||||
/// any values except what is defined in their arguments.
|
||||
#[derive(PartialEq,Debug,Clone)]
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct MacroDef {
|
||||
pub argdefs: Vec<Positioned<String>>,
|
||||
pub fields: FieldList,
|
||||
@ -512,10 +515,11 @@ impl MacroDef {
|
||||
return false;
|
||||
}
|
||||
|
||||
fn validate_value_symbols<'a>(&self,
|
||||
stack: &mut Vec<&'a Expression>,
|
||||
val: &'a Value)
|
||||
-> HashSet<String> {
|
||||
fn validate_value_symbols<'a>(
|
||||
&self,
|
||||
stack: &mut Vec<&'a Expression>,
|
||||
val: &'a Value,
|
||||
) -> HashSet<String> {
|
||||
let mut bad_symbols = HashSet::new();
|
||||
if let &Value::Symbol(ref name) = val {
|
||||
if !self.symbol_is_in_args(&name.val) {
|
||||
@ -572,11 +576,9 @@ impl MacroDef {
|
||||
stack.push(expr);
|
||||
}
|
||||
}
|
||||
&Expression::Call(ref def) => {
|
||||
for expr in def.arglist.iter() {
|
||||
stack.push(expr);
|
||||
}
|
||||
}
|
||||
&Expression::Call(ref def) => for expr in def.arglist.iter() {
|
||||
stack.push(expr);
|
||||
},
|
||||
&Expression::Simple(ref val) => {
|
||||
let mut syms_set = self.validate_value_symbols(&mut stack, val);
|
||||
bad_symbols.extend(syms_set.drain());
|
||||
@ -597,7 +599,7 @@ impl MacroDef {
|
||||
|
||||
/// Specifies the types of binary operations supported in
|
||||
/// UCG expression.
|
||||
#[derive(Debug,PartialEq,Clone)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum BinaryExprType {
|
||||
Add,
|
||||
Sub,
|
||||
@ -606,7 +608,7 @@ pub enum BinaryExprType {
|
||||
}
|
||||
|
||||
/// Represents an expression with a left and a right side.
|
||||
#[derive(Debug,PartialEq,Clone)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct BinaryOpDef {
|
||||
pub kind: BinaryExprType,
|
||||
pub left: Value,
|
||||
@ -615,7 +617,7 @@ pub struct BinaryOpDef {
|
||||
}
|
||||
|
||||
/// Encodes a tuple Copy expression in the UCG AST.
|
||||
#[derive(Debug,PartialEq,Clone)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct CopyDef {
|
||||
pub selector: SelectorDef,
|
||||
pub fields: FieldList,
|
||||
@ -623,7 +625,7 @@ pub struct CopyDef {
|
||||
}
|
||||
|
||||
/// Encodes a format expression in the UCG AST.
|
||||
#[derive(Debug,PartialEq,Clone)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct FormatDef {
|
||||
pub template: String,
|
||||
pub args: Vec<Expression>,
|
||||
@ -631,14 +633,14 @@ pub struct FormatDef {
|
||||
}
|
||||
|
||||
/// Encodes a list expression in the UCG AST.
|
||||
#[derive(Debug,PartialEq,Clone)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct ListDef {
|
||||
pub elems: Vec<Expression>,
|
||||
pub pos: Position,
|
||||
}
|
||||
|
||||
/// Encodes a ucg expression. Expressions compute a value from.
|
||||
#[derive(Debug,PartialEq,Clone)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Expression {
|
||||
// Base Expression
|
||||
Simple(Value),
|
||||
@ -672,21 +674,21 @@ impl Expression {
|
||||
}
|
||||
|
||||
/// Encodes a let statement in the UCG AST.
|
||||
#[derive(Debug,PartialEq)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct LetDef {
|
||||
pub name: Token,
|
||||
pub value: Expression,
|
||||
}
|
||||
|
||||
/// Encodes an import statement in the UCG AST.
|
||||
#[derive(Debug,PartialEq)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ImportDef {
|
||||
pub path: Token,
|
||||
pub name: Token,
|
||||
}
|
||||
|
||||
/// Encodes a parsed statement in the UCG AST.
|
||||
#[derive(Debug,PartialEq)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Statement {
|
||||
// simple expression
|
||||
Expression(Expression),
|
||||
@ -707,12 +709,15 @@ mod ast_test {
|
||||
let def = MacroDef {
|
||||
argdefs: vec![value_node!("foo".to_string(), 1, 0)],
|
||||
fields: vec![
|
||||
(make_tok!("f1", 1, 1), Expression::Binary(BinaryOpDef{
|
||||
kind: BinaryExprType::Add,
|
||||
left: Value::Symbol(value_node!("foo".to_string(), 1, 1)),
|
||||
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))),
|
||||
pos: Position::new(1, 0),
|
||||
})),
|
||||
(
|
||||
make_tok!("f1", 1, 1),
|
||||
Expression::Binary(BinaryOpDef {
|
||||
kind: BinaryExprType::Add,
|
||||
left: Value::Symbol(value_node!("foo".to_string(), 1, 1)),
|
||||
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))),
|
||||
pos: Position::new(1, 0),
|
||||
}),
|
||||
),
|
||||
],
|
||||
pos: Position::new(1, 0),
|
||||
};
|
||||
@ -724,12 +729,15 @@ mod ast_test {
|
||||
let def = MacroDef {
|
||||
argdefs: vec![value_node!("foo".to_string(), 1, 0)],
|
||||
fields: vec![
|
||||
(make_tok!("f1", 1, 1), Expression::Binary(BinaryOpDef{
|
||||
kind: BinaryExprType::Add,
|
||||
left: Value::Symbol(value_node!("bar".to_string(), 1, 1)),
|
||||
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))),
|
||||
pos: Position::new(1, 0),
|
||||
})),
|
||||
(
|
||||
make_tok!("f1", 1, 1),
|
||||
Expression::Binary(BinaryOpDef {
|
||||
kind: BinaryExprType::Add,
|
||||
left: Value::Symbol(value_node!("bar".to_string(), 1, 1)),
|
||||
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))),
|
||||
pos: Position::new(1, 0),
|
||||
}),
|
||||
),
|
||||
],
|
||||
pos: Position::new(1, 0),
|
||||
};
|
||||
@ -743,13 +751,16 @@ mod ast_test {
|
||||
let def = MacroDef {
|
||||
argdefs: vec![value_node!("foo".to_string(), 1, 0)],
|
||||
fields: vec![
|
||||
(make_tok!("f1", 1, 1), Expression::Binary(BinaryOpDef{
|
||||
kind: BinaryExprType::Add,
|
||||
left: Value::Selector(make_selector!(make_expr!("foo", 1, 1) => [
|
||||
(
|
||||
make_tok!("f1", 1, 1),
|
||||
Expression::Binary(BinaryOpDef {
|
||||
kind: BinaryExprType::Add,
|
||||
left: Value::Selector(make_selector!(make_expr!("foo", 1, 1) => [
|
||||
make_tok!("quux", 1, 1) ] => 1, 1)),
|
||||
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))),
|
||||
pos: Position::new(1, 0),
|
||||
})),
|
||||
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))),
|
||||
pos: Position::new(1, 0),
|
||||
}),
|
||||
),
|
||||
],
|
||||
pos: Position::new(1, 0),
|
||||
};
|
||||
@ -761,13 +772,16 @@ mod ast_test {
|
||||
let def = MacroDef {
|
||||
argdefs: vec![value_node!("foo".to_string(), 1, 0)],
|
||||
fields: vec![
|
||||
(make_tok!("f1", 1, 1), Expression::Binary(BinaryOpDef{
|
||||
kind: BinaryExprType::Add,
|
||||
left: Value::Selector(make_selector!(make_expr!("bar", 1, 1) => [
|
||||
(
|
||||
make_tok!("f1", 1, 1),
|
||||
Expression::Binary(BinaryOpDef {
|
||||
kind: BinaryExprType::Add,
|
||||
left: Value::Selector(make_selector!(make_expr!("bar", 1, 1) => [
|
||||
make_tok!("quux", 1, 1) ] => 1, 1)),
|
||||
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))),
|
||||
pos: Position::new(1, 0),
|
||||
})),
|
||||
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))),
|
||||
pos: Position::new(1, 0),
|
||||
}),
|
||||
),
|
||||
],
|
||||
pos: Position::new(1, 0),
|
||||
};
|
||||
|
@ -34,12 +34,14 @@ impl JsonConverter {
|
||||
Ok(serde_json::Value::Array(v))
|
||||
}
|
||||
|
||||
fn convert_tuple(&self,
|
||||
items: &Vec<(ast::Positioned<String>, Rc<Val>)>)
|
||||
-> Result<serde_json::Value> {
|
||||
fn convert_tuple(
|
||||
&self,
|
||||
items: &Vec<(ast::Positioned<String>, Rc<Val>)>,
|
||||
) -> Result<serde_json::Value> {
|
||||
let mut mp = serde_json::Map::new();
|
||||
for &(ref k, ref v) in items.iter() {
|
||||
mp.entry(k.val.clone()).or_insert(try!(self.convert_value(v)));
|
||||
mp.entry(k.val.clone())
|
||||
.or_insert(try!(self.convert_value(v)));
|
||||
}
|
||||
Ok(serde_json::Value::Object(mp))
|
||||
}
|
||||
|
10
src/error.rs
10
src/error.rs
@ -75,10 +75,7 @@ impl fmt::Debug for Error {
|
||||
write!(
|
||||
w,
|
||||
"{}: \"{}\" {}:{}",
|
||||
self.err_type,
|
||||
self.msg,
|
||||
self.pos.line,
|
||||
self.pos.column
|
||||
self.err_type, self.msg, self.pos.line, self.pos.column
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -88,10 +85,7 @@ impl fmt::Display for Error {
|
||||
write!(
|
||||
w,
|
||||
"{}: \"{}\" {}:{}",
|
||||
self.err_type,
|
||||
self.msg,
|
||||
self.pos.line,
|
||||
self.pos.column
|
||||
self.err_type, self.msg, self.pos.line, self.pos.column
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ impl<V: Into<String> + Clone> Formatter<V> {
|
||||
}
|
||||
|
||||
/// Renders a formatter to a string or returns an error.
|
||||
///
|
||||
///
|
||||
/// If the formatter has the wrong number of arguments for the number of replacements
|
||||
/// it will return an error. Otherwise it will return the formatted string.
|
||||
pub fn render(&self, pos: &Position) -> Result<String, Box<Error>> {
|
||||
@ -45,10 +45,12 @@ impl<V: Into<String> + Clone> Formatter<V> {
|
||||
for c in self.tmpl.chars() {
|
||||
if c == '@' && !should_escape {
|
||||
if count == self.args.len() {
|
||||
return Err(Box::new(error::Error::new("Too few arguments to string \
|
||||
formatter.",
|
||||
error::ErrorType::FormatError,
|
||||
pos.clone())));
|
||||
return Err(Box::new(error::Error::new(
|
||||
"Too few arguments to string \
|
||||
formatter.",
|
||||
error::ErrorType::FormatError,
|
||||
pos.clone(),
|
||||
)));
|
||||
}
|
||||
let arg = self.args[count].clone();
|
||||
let strval = arg.into();
|
||||
@ -61,10 +63,12 @@ impl<V: Into<String> + Clone> Formatter<V> {
|
||||
}
|
||||
}
|
||||
if self.args.len() != count {
|
||||
return Err(Box::new(error::Error::new("Too many arguments to string \
|
||||
formatter.",
|
||||
error::ErrorType::FormatError,
|
||||
pos.clone())));
|
||||
return Err(Box::new(error::Error::new(
|
||||
"Too many arguments to string \
|
||||
formatter.",
|
||||
error::ErrorType::FormatError,
|
||||
pos.clone(),
|
||||
)));
|
||||
}
|
||||
return Ok(buf);
|
||||
}
|
||||
@ -78,30 +82,21 @@ mod test {
|
||||
#[test]
|
||||
fn test_format_happy_path() {
|
||||
let formatter = Formatter::new("foo @ @ \\@", vec!["bar", "quux"]);
|
||||
let pos = Position {
|
||||
line: 0,
|
||||
column: 0,
|
||||
};
|
||||
let pos = Position { line: 0, column: 0 };
|
||||
assert_eq!(formatter.render(&pos).unwrap(), "foo bar quux @");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_happy_wrong_too_few_args() {
|
||||
let formatter = Formatter::new("foo @ @ \\@", vec!["bar"]);
|
||||
let pos = Position {
|
||||
line: 0,
|
||||
column: 0,
|
||||
};
|
||||
let pos = Position { line: 0, column: 0 };
|
||||
assert!(formatter.render(&pos).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_happy_wrong_too_many_args() {
|
||||
let formatter = Formatter::new("foo @ @ \\@", vec!["bar", "quux", "baz"]);
|
||||
let pos = Position {
|
||||
line: 0,
|
||||
column: 0,
|
||||
};
|
||||
let pos = Position { line: 0, column: 0 };
|
||||
assert!(formatter.render(&pos).is_err());
|
||||
}
|
||||
}
|
||||
|
22
src/lib.rs
22
src/lib.rs
@ -151,39 +151,39 @@
|
||||
//! ```
|
||||
//!
|
||||
//! #### Copy expressions
|
||||
//!
|
||||
//!
|
||||
//! ucg Tuples support a form of reuse with copy on write semantics. You can copy a tuple and selectively overwrite fields or add new
|
||||
//! fields to it with the copy expression. To perform a copy first reference the tuple by a bound name and then use `{ field = value, ... }`
|
||||
//! syntax to copy with overridden fields or add completely new fields. When replacing a preexisting field with a new value you cannot
|
||||
//! change the type of the field. This allows you to define a typed shape for a tuple with default values and then provide new values for
|
||||
//! some or all of the fields while still enforcing the same types for those fields. Adding completely new fields has no such restriction.
|
||||
//!
|
||||
//!
|
||||
//! ```ucg
|
||||
//! let base = {
|
||||
//! field1 = "value1",
|
||||
//! field2 = 100,
|
||||
//! field3 = 5.6,
|
||||
//! };
|
||||
//!
|
||||
//!
|
||||
//! let overridden = base{
|
||||
//! field1 = "new value"
|
||||
//! };
|
||||
//!
|
||||
//!
|
||||
//! let expanded = base{
|
||||
//! field2 = 200,
|
||||
//! field3 = "look ma a new field",
|
||||
//! };
|
||||
//! ```
|
||||
//!
|
||||
//!
|
||||
//! The following will cause an error because the overriden field's value does not match the original.
|
||||
//!
|
||||
//!
|
||||
//! ```ucg
|
||||
//! let bad = base{
|
||||
//! field1 = 300, // Error!!! must be a string.
|
||||
//! };
|
||||
//!
|
||||
//!
|
||||
//! ```
|
||||
//!
|
||||
//!
|
||||
//! #### Conditional data
|
||||
//!
|
||||
//! ucg supports a limited form of conditional data selection of using the select expression. A select expression starts with the `select`
|
||||
@ -232,7 +232,7 @@
|
||||
//! All ucg statements must be terminated by a semicolon.
|
||||
//!
|
||||
//! * expression statements
|
||||
//!
|
||||
//!
|
||||
//! The simplest and least useful is the expression statement. It is any valid expression followed by a semicolon.
|
||||
//!
|
||||
//! ```ucg
|
||||
@ -246,7 +246,7 @@
|
||||
//! ever create a repl for ucg statements they may prove more useful.
|
||||
//!
|
||||
//! * Let statements
|
||||
//!
|
||||
//!
|
||||
//! The let expression binds the result of any valid expression to a name. It starts with the `let` keyword and is followed by
|
||||
//! the name of the binding, an `=`, and a valid ucg expression.
|
||||
//!
|
||||
@ -255,7 +255,7 @@
|
||||
//! ```
|
||||
//!
|
||||
//! * Import statement
|
||||
//!
|
||||
//!
|
||||
//! The import statement imports the contents of another ucg file into the current file with a name. The imported files bound
|
||||
//! values are exposed as a tuple in the referencing file. It starts with the `import` keyword and is followed by a quoted path
|
||||
//! to the ucg file, the keyword `as`, and a name for the imported values.
|
||||
|
1337
src/parse.rs
1337
src/parse.rs
File diff suppressed because it is too large
Load Diff
190
src/tokenizer.rs
190
src/tokenizer.rs
@ -15,8 +15,8 @@
|
||||
//! The tokenization stage of the ucg compiler.
|
||||
use nom_locate::LocatedSpan;
|
||||
use nom;
|
||||
use nom::{alpha, is_alphanumeric, digit, multispace};
|
||||
use nom::{InputLength, InputIter, Slice};
|
||||
use nom::{alpha, digit, is_alphanumeric, multispace};
|
||||
use nom::{InputIter, InputLength, Slice};
|
||||
use ast::*;
|
||||
use std;
|
||||
use std::result::Result;
|
||||
@ -204,9 +204,11 @@ named!(astok( Span ) -> Token,
|
||||
fn end_of_input(input: Span) -> nom::IResult<Span, Token> {
|
||||
match eof!(input,) {
|
||||
nom::IResult::Done(_, _) => {
|
||||
return nom::IResult::Done(input,
|
||||
make_tok!(EOF => input.line as usize,
|
||||
input.get_column() as usize));
|
||||
return nom::IResult::Done(
|
||||
input,
|
||||
make_tok!(EOF => input.line as usize,
|
||||
input.get_column() as usize),
|
||||
);
|
||||
}
|
||||
nom::IResult::Incomplete(_) => {
|
||||
return nom::IResult::Incomplete(nom::Needed::Unknown);
|
||||
@ -220,22 +222,29 @@ fn end_of_input(input: Span) -> nom::IResult<Span, Token> {
|
||||
fn comment(input: Span) -> nom::IResult<Span, Token> {
|
||||
match tag!(input, "//") {
|
||||
nom::IResult::Done(rest, _) => {
|
||||
match alt!(rest, take_until_and_consume!("\r\n") | take_until_and_consume!("\n")) {
|
||||
match alt!(
|
||||
rest,
|
||||
take_until_and_consume!("\r\n") | take_until_and_consume!("\n")
|
||||
) {
|
||||
nom::IResult::Done(rest, cmt) => {
|
||||
return nom::IResult::Done(rest,
|
||||
make_tok!(CMT => cmt.fragment.to_string(),
|
||||
return nom::IResult::Done(
|
||||
rest,
|
||||
make_tok!(CMT => cmt.fragment.to_string(),
|
||||
input.line as usize,
|
||||
input.get_column() as usize));
|
||||
input.get_column() as usize),
|
||||
);
|
||||
}
|
||||
// If we didn't find a new line then we just grab everything.
|
||||
_ => {
|
||||
let blen = rest.input_len();
|
||||
let next = rest.slice(blen..);
|
||||
let tok = rest.slice(..blen);
|
||||
return nom::IResult::Done(next,
|
||||
make_tok!(CMT => tok.fragment.to_string(),
|
||||
return nom::IResult::Done(
|
||||
next,
|
||||
make_tok!(CMT => tok.fragment.to_string(),
|
||||
input.line as usize, input.get_column() as usize
|
||||
));
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -461,11 +470,13 @@ pub fn pos(i: TokenIter) -> nom::IResult<TokenIter, Position, ParseError> {
|
||||
let tok = &i[0];
|
||||
let line = tok.pos.line;
|
||||
let column = tok.pos.column;
|
||||
nom::IResult::Done(i.clone(),
|
||||
Position {
|
||||
line: line,
|
||||
column: column,
|
||||
})
|
||||
nom::IResult::Done(
|
||||
i.clone(),
|
||||
Position {
|
||||
line: line,
|
||||
column: column,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// TokenIter wraps a slice of Tokens and implements the various necessary
|
||||
@ -528,7 +539,8 @@ impl<'a> InputIter for TokenIter<'a> {
|
||||
}
|
||||
|
||||
fn position<P>(&self, predicate: P) -> Option<usize>
|
||||
where P: Fn(Self::RawItem) -> bool
|
||||
where
|
||||
P: Fn(Self::RawItem) -> bool,
|
||||
{
|
||||
for (o, v) in self.iter_indices() {
|
||||
if predicate(v.clone()) {
|
||||
@ -580,10 +592,10 @@ mod tokenizer_test {
|
||||
|
||||
#[test]
|
||||
fn test_tokenize_one_of_each() {
|
||||
// 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2
|
||||
// 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7
|
||||
let result = tokenize(LocatedSpan::new("let import macro select as => [ ] { } ; = % / * \
|
||||
+ - . ( ) , 1 . foo \"bar\" // comment\n ;"));
|
||||
let result = tokenize(LocatedSpan::new(
|
||||
"let import macro select as => [ ] { } ; = % / * \
|
||||
+ - . ( ) , 1 . foo \"bar\" // comment\n ;",
|
||||
));
|
||||
assert!(result.is_ok(), format!("result {:?} is not ok", result));
|
||||
let v = result.unwrap();
|
||||
for (i, t) in v.iter().enumerate() {
|
||||
@ -606,40 +618,71 @@ mod tokenizer_test {
|
||||
fn test_parse_comment() {
|
||||
assert!(comment(LocatedSpan::new("// comment\n")).is_done());
|
||||
assert!(comment(LocatedSpan::new("// comment")).is_done());
|
||||
assert_eq!(comment(LocatedSpan::new("// comment\n")),
|
||||
nom::IResult::Done(LocatedSpan{fragment: "", offset: 11, line: 2},
|
||||
Token{
|
||||
typ: TokenType::COMMENT,
|
||||
fragment: " comment".to_string(),
|
||||
pos: Position{line: 1, column: 1},
|
||||
}));
|
||||
assert_eq!(
|
||||
comment(LocatedSpan::new("// comment\n")),
|
||||
nom::IResult::Done(
|
||||
LocatedSpan {
|
||||
fragment: "",
|
||||
offset: 11,
|
||||
line: 2,
|
||||
},
|
||||
Token {
|
||||
typ: TokenType::COMMENT,
|
||||
fragment: " comment".to_string(),
|
||||
pos: Position { line: 1, column: 1 },
|
||||
}
|
||||
)
|
||||
);
|
||||
assert!(comment(LocatedSpan::new("// comment\r\n")).is_done());
|
||||
assert_eq!(comment(LocatedSpan::new("// comment\r\n")),
|
||||
nom::IResult::Done(LocatedSpan{fragment: "", offset: 12, line: 2},
|
||||
Token{
|
||||
assert_eq!(
|
||||
comment(LocatedSpan::new("// comment\r\n")),
|
||||
nom::IResult::Done(
|
||||
LocatedSpan {
|
||||
fragment: "",
|
||||
offset: 12,
|
||||
line: 2,
|
||||
},
|
||||
Token {
|
||||
typ: TokenType::COMMENT,
|
||||
fragment: " comment".to_string(),
|
||||
pos: Position{column: 1, line: 1}
|
||||
}));
|
||||
pos: Position { column: 1, line: 1 },
|
||||
}
|
||||
)
|
||||
);
|
||||
assert!(comment(LocatedSpan::new("// comment\r\n ")).is_done());
|
||||
assert_eq!(comment(LocatedSpan::new("// comment\r\n ")),
|
||||
nom::IResult::Done(LocatedSpan{fragment: " ", offset: 12, line: 2},
|
||||
Token{
|
||||
assert_eq!(
|
||||
comment(LocatedSpan::new("// comment\r\n ")),
|
||||
nom::IResult::Done(
|
||||
LocatedSpan {
|
||||
fragment: " ",
|
||||
offset: 12,
|
||||
line: 2,
|
||||
},
|
||||
Token {
|
||||
typ: TokenType::COMMENT,
|
||||
fragment: " comment".to_string(),
|
||||
pos: Position{column: 1, line: 1},
|
||||
}));
|
||||
pos: Position { column: 1, line: 1 },
|
||||
}
|
||||
)
|
||||
);
|
||||
assert!(comment(LocatedSpan::new("// comment")).is_done());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_match_word() {
|
||||
let input = vec![Token{
|
||||
fragment: "foo".to_string(),
|
||||
typ: TokenType::BAREWORD,
|
||||
pos: Position{line: 1, column: 1}
|
||||
}];
|
||||
let result = word!(TokenIter{source: input.as_slice()}, "foo");
|
||||
let input = vec![
|
||||
Token {
|
||||
fragment: "foo".to_string(),
|
||||
typ: TokenType::BAREWORD,
|
||||
pos: Position { line: 1, column: 1 },
|
||||
},
|
||||
];
|
||||
let result = word!(
|
||||
TokenIter {
|
||||
source: input.as_slice(),
|
||||
},
|
||||
"foo"
|
||||
);
|
||||
match result {
|
||||
nom::IResult::Done(_, tok) => assert_eq!(tok, input[0]),
|
||||
res => assert!(false, format!("Fail: {:?}", res)),
|
||||
@ -648,12 +691,19 @@ mod tokenizer_test {
|
||||
|
||||
#[test]
|
||||
fn test_match_word_empty_input() {
|
||||
let input = vec![Token{
|
||||
fragment: "".to_string(),
|
||||
typ: TokenType::END,
|
||||
pos: Position{line: 1, column: 1},
|
||||
}];
|
||||
let result = word!(TokenIter{source: input.as_slice()}, "foo");
|
||||
let input = vec![
|
||||
Token {
|
||||
fragment: "".to_string(),
|
||||
typ: TokenType::END,
|
||||
pos: Position { line: 1, column: 1 },
|
||||
},
|
||||
];
|
||||
let result = word!(
|
||||
TokenIter {
|
||||
source: input.as_slice(),
|
||||
},
|
||||
"foo"
|
||||
);
|
||||
match result {
|
||||
nom::IResult::Done(_, _) => assert!(false, "Should have been an error but was Done"),
|
||||
nom::IResult::Incomplete(_) => {
|
||||
@ -667,12 +717,19 @@ mod tokenizer_test {
|
||||
|
||||
#[test]
|
||||
fn test_match_punct() {
|
||||
let input = vec![Token{
|
||||
fragment: "!".to_string(),
|
||||
typ: TokenType::PUNCT,
|
||||
pos: Position{line: 1, column: 1}
|
||||
}];
|
||||
let result = punct!(TokenIter{source: input.as_slice()}, "!");
|
||||
let input = vec![
|
||||
Token {
|
||||
fragment: "!".to_string(),
|
||||
typ: TokenType::PUNCT,
|
||||
pos: Position { line: 1, column: 1 },
|
||||
},
|
||||
];
|
||||
let result = punct!(
|
||||
TokenIter {
|
||||
source: input.as_slice(),
|
||||
},
|
||||
"!"
|
||||
);
|
||||
match result {
|
||||
nom::IResult::Done(_, tok) => assert_eq!(tok, input[0]),
|
||||
res => assert!(false, format!("Fail: {:?}", res)),
|
||||
@ -681,12 +738,19 @@ mod tokenizer_test {
|
||||
|
||||
#[test]
|
||||
fn test_match_type() {
|
||||
let input = vec![Token{
|
||||
fragment: "foo".to_string(),
|
||||
typ: TokenType::BAREWORD,
|
||||
pos: Position{line: 1, column: 1}
|
||||
}];
|
||||
let result = match_type!(TokenIter{source: input.as_slice()}, BAREWORD);
|
||||
let input = vec![
|
||||
Token {
|
||||
fragment: "foo".to_string(),
|
||||
typ: TokenType::BAREWORD,
|
||||
pos: Position { line: 1, column: 1 },
|
||||
},
|
||||
];
|
||||
let result = match_type!(
|
||||
TokenIter {
|
||||
source: input.as_slice(),
|
||||
},
|
||||
BAREWORD
|
||||
);
|
||||
match result {
|
||||
nom::IResult::Done(_, tok) => assert_eq!(tok, input[0]),
|
||||
res => assert!(false, format!("Fail: {:?}", res)),
|
||||
|
Loading…
x
Reference in New Issue
Block a user