use ast;
use std::sync::Arc;
use std::path::PathBuf;
use ast::BlockLevelNode;
use ast::block_statements as stm;
use ast::{Filter, Comment, AstLocation};
use ast::block::{Block, BlockType};
use ast::mixin::*;
use tok::Location as TokenLocation;
use tok::Tok;
use ast::import::ImportStatement;
use ast::var::{VarDefinition,VarReference};
use ast::expression::{ExpressionValue, ExpressionNode, ExpressionOperation};
grammar<'a>(file: &'a Arc<PathBuf>);
extern {
type Location = TokenLocation;
type Error = char;
enum Tok {
"(" => Tok::LParen,
")" => Tok::RParen,
"-" => Tok::Minus,
"+" => Tok::Plus,
"*" => Tok::Times,
"/" => Tok::Div,
"," => Tok::Comma,
"\n" => Tok::NewLine,
"Show" => Tok::Show,
"Hide" => Tok::Hide,
"Mixin" => Tok::Mixin,
"Import" => Tok::Import,
">=" => Tok::Gte,
">" => Tok::Gt,
"<=" => Tok::Lte,
"<" => Tok::Lt,
"=" => Tok::Eql,
"if" => Tok::If,
"True" => Tok::True,
"False" => Tok::False,
Comment => Tok::Comment(<String>),
BlockComment => Tok::BlockComment(<String>),
Num => Tok::Num(<i64>),
Float => Tok::Float(<f64>),
QuotedStrLiteral => Tok::StrLiteral(<String>),
Constant => Tok::Constant(<String>),
VarIdentifier => Tok::VarIdentifier(<String>)
}
}
pub Filter: Filter = {
<l:@L> <defs:DefinitionBlock*> <first:InstructionBlock> <rest:AnyBlock*> <r:@R> => {
let mut nodes = defs;
nodes.push(first);
nodes.extend(rest);
Filter {
nodes: nodes,
location: AstLocation::new(l, r, file.clone())
}
},
<l:@L> <defs:DefinitionBlock*> <r:@R> =>
Filter {
nodes: defs,
location: AstLocation::new(l, r, file.clone())
},
};
LineEndOrComment: Option<Comment> = {
<s_option:Comment?> "\n" => {
s_option.and_then(|comment_str| Some(Comment { content: comment_str, inline: true }))
}
};
CommentLine: Comment = {
<Comment> "\n" => Comment { content: <>, inline: false }
};
BlockCommentLine: Comment = {
<BlockComment> "\n" => Comment { content: <>, inline: false }
};
ImportStmt: ImportStatement = {
<l:@L> "Import" <path:RawStrLiteral> <r:@R> <comment:LineEndOrComment> =>
ImportStatement {
path: path,
location: AstLocation::new(l, r, file.clone()),
comment
}
};
AnyBlock: BlockLevelNode = {
<ImportStmt> => BlockLevelNode::Import(<>),
InstructionBlock
};
DefinitionBlock: BlockLevelNode = {
<VarDefinition> => BlockLevelNode::VarDef(<>),
<ImportStmt> => BlockLevelNode::Import(<>),
<CommentLine> => BlockLevelNode::Comment(<>),
};
InstructionBlock: BlockLevelNode = {
<block_comments:BlockCommentLine*> <l:@L> <t:BlockType> <condition:("if" <ExpressionRoot>)?> <inline_comment:LineEndOrComment> <stmts:BlockStatement+> <r:@R> => BlockLevelNode::Block(
Block {
nodes: stmts,
variant: t,
location: AstLocation::new(l, r, file.clone()),
condition: condition.and_then( |inner| Some(inner)),
block_comments,
inline_comment
}
),
<block_comments:BlockCommentLine*> <l:@L> "Mixin" <name:Constant> <args:("(" <Comma<VarIdentifier>> ")")?> <inline_comment:LineEndOrComment> <instructions:BlockStatement+> <r:@R> => {
let params = args
.unwrap_or(vec![])
.iter()
.map(|param_name| ast::mixin::Param { name: param_name.clone(), default: None })
.collect();
BlockLevelNode::Mixin(
ast::mixin::Mixin {
name: name,
parameters: params,
statements: instructions,
location: AstLocation::new(l, r, file.clone()),
block_comments,
inline_comment
}
)
}
};
BlockType: BlockType = {
"Show" => BlockType::Show,
"Hide" => BlockType::Hide
};
Comma<T>: Vec<T> = {
<v:(<T> ",")*> <e:T?> => match e {
None => v,
Some(e) => {
let mut v = v;
v.push(e);
v
}
}
};
VarDefinition: VarDefinition = {
<l:@L> <id:VarIdentifier> "=" <v:Value> <r:@R> <comment:LineEndOrComment> =>
VarDefinition {
identifier: id,
values: v,
location: AstLocation::new(l, r, file.clone()),
comment
}
};
VarReference: ExpressionValue = {
<l:@L> <ident:VarIdentifier> <r:@R> => ExpressionValue::Var(
VarReference {
identifier: ident,
location: AstLocation::new(l, r, file.clone())
}
)
};
SetValueStmt: stm::SetValueStatement = {
<l:@L> <n:Constant> <val:Value> <r:@R> <comment:LineEndOrComment> =>
stm::SetValueStatement {
name: n,
values: val,
location: AstLocation::new(l, r, file.clone()),
comment
}
};
ConditionStmt: stm::ConditionStatement = {
<l:@L> <n: Constant> <cond: StmCondition> <r:@R> <comment:LineEndOrComment> =>
stm::ConditionStatement {
name: n,
condition: cond,
location: AstLocation::new(l, r, file.clone()),
comment
}
};
StmCondition: stm::Condition = {
<op:ComparisonOperator> <v:ExpressionRoot> => stm::Condition { value: v, operator: op }
};
MixinCall: MixinCall = {
<l:@L> "+" <n:Constant> <params:("(" <Comma<Value>> ")")?> <r:@R> <comment:LineEndOrComment> =>
MixinCall {
name: n,
parameters: params.unwrap_or(vec![]),
location: AstLocation::new(l, r, file.clone()),
comment
}
};
#[inline]
BlockStatement: stm::BlockStatement = {
<SetValueStmt> => stm::BlockStatement::SetValue(<>),
<ConditionStmt> => stm::BlockStatement::Condition(<>),
<MixinCall> => stm::BlockStatement::MixinCall(<>),
<VarDefinition> => stm::BlockStatement::VarDef(<>),
<CommentLine> => stm::BlockStatement::Comment(<>)
};
RawStrLiteral: String = {
QuotedStrLiteral => String::from(<>),
Constant => String::from(<>),
};
StrLiteral: ExpressionValue = {
<l:@L> <s:RawStrLiteral> <r:@R> => ExpressionValue::String(s)
};
ComparisonOperator: stm::ComparisonOperator = {
">=" => stm::ComparisonOperator::Gte,
">" => stm::ComparisonOperator::Gt,
"<=" => stm::ComparisonOperator::Lte,
"<" => stm::ComparisonOperator::Lt,
"=" => stm::ComparisonOperator::Eql
};
NumberLiteral: ExpressionValue = {
<num:Num> => ExpressionValue::Int(num),
<num:Float> => ExpressionValue::Decimal(num),
};
Bool: ExpressionValue = {
"True" => ExpressionValue::Bool(true),
"False" => ExpressionValue::Bool(false)
};
Value: Box<ExpressionNode> = {
<l:@L> <values:ExpressionRoot+> <r:@R> => {
if values.len() == 1 {
values[0].clone()
} else {
Box::new(
ExpressionNode::Val(ExpressionValue::List(values), AstLocation::new(l, r, file.clone()))
)
}
}
};
ExprComparisonOp: ExpressionOperation = {
">=" => ExpressionOperation::Gte,
">" => ExpressionOperation::Gt,
"<=" => ExpressionOperation::Lte,
"<" => ExpressionOperation::Lt,
"=" => ExpressionOperation::Eql
};
ExprSumOp: ExpressionOperation = {
"+" => ExpressionOperation::Add,
"-" => ExpressionOperation::Sub
};
ExprFactorOp: ExpressionOperation = {
"*" => ExpressionOperation::Mul,
"/" => ExpressionOperation::Div
};
Tier<Op,NextTier>: Box<ExpressionNode> = {
Tier<Op,NextTier> Op NextTier => Box::new(ExpressionNode::Op(<>)),
NextTier
};
ExpressionRoot = ComparisonTierExpression;
ComparisonTierExpression = Tier<ExprComparisonOp, SumTierExpression>;
SumTierExpression = Tier<ExprSumOp, FactorTierExpression>;
FactorTierExpression = Tier<ExprFactorOp, ExpressionTerm>;
ExpressionTerm: Box<ExpressionNode> = {
<l:@L> <lit:ExpressionLiteral> <r:@R> => Box::new(ExpressionNode::Val(lit, AstLocation::new(l, r, file.clone()))),
"(" <ExpressionRoot> ")" => <>
};
ExpressionLiteral: ExpressionValue = {
VarReference,
NumberLiteral,
StrLiteral,
Bool,
};
Color: ast::color::Color = {
<r:ExpressionTerm> <g:ExpressionTerm> <b:ExpressionTerm> <a:ExpressionTerm> => ast::color::Color {
r, g, b, a
},
<r:ExpressionTerm> <g:ExpressionTerm> <b:ExpressionTerm> <right:@R> => ast::color::Color {
r,
g,
b,
a: Box::new(
ExpressionNode::Val(ExpressionValue::Int(255), AstLocation::new(right, right, file.clone())
))
}
};