use super::*;
use lalrpop_util::ParseError;
grammar;
extern {
type Error = AstError;
}
match {
r";[^@][^\n\r]*" => {},
r"\s+" => {},
r"create-ordered-[\p{alpha}.?=*!<>:#+/%_^'&-][\p{alpha}\p{digit}.?=*!<>:#+/%_^'&-]*" => "CREATE-ORDERED-IDENT",
r"-?[0-9]+(\.[0-9]*)?([eE][+-]?[0-9]+)?" => "NUMBER",
} else {
r"create-[\p{alpha}.?=*!<>:#+/%_^'&-][\p{alpha}\p{digit}.?=*!<>:#+/%_^'&-]*" => "CREATE-IDENT",
} else {
r"[\p{alpha}.?=*!<>:#+/%_^'&-][\p{alpha}\p{digit}.?=*!<>:#+/%_^'&-]*-own" => "IDENT-OWN",
} else { _ }
pub Program = Item*;
RawUnaryFunc = {
"forward", "fd", "right", "rt", "left", "lt",
"random", "random-float",
"any?", "one-of", "other",
"distance", "towards",
"min", "max", "sum", "mean",
"abs", "sin", "cos", "tan", "asin", "acos", "sqrt", "ln", "log", "floor", "ceiling",
"user-input",
};
UnaryFunc: Ident = <l:@L> <name:RawUnaryFunc> <r:@R> => Ident { id: clean_ident(name), raw_span: Span(l, r) };
RawBinaryFunc = {
"setxy", "distancexy", "towardsxy",
"subtract-headings",
"atan",
};
BinaryFunc: Ident = <l:@L> <name:RawBinaryFunc> <r:@R> => Ident { id: clean_ident(name), raw_span: Span(l, r) };
Item: Item = {
Globals => Item::Globals(<>),
Breed => Item::Breed(<>),
Own => Item::Own(<>),
Function => Item::Function(<>),
};
Globals: Globals = <annotations:Annotation*> <l:@L> "globals" "[" <idents:Ident*> "]" <r:@R> => Globals { annotations, idents, raw_span: Span(l, r) };
Breed: Breed = <l:@L> "breed" "[" <plural:Ident> <singular:Ident> "]" <r:@R> => Breed { plural, singular, raw_span: Span(l, r) };
Own: Own = {
<l:@L> <owner:"IDENT-OWN"> "[" <props:Ident*> "]" <r:@R> => {
Own { plural_owner: Ident { id: owner[..owner.len()-4].to_lowercase(), raw_span: Span(l, l + owner.len() - 4) }, props, raw_span: Span(l, r) }
},
};
Function: Function = {
<annotations:Annotation*> <l:@L> "to" <name:Ident> <stmts:Stmt*> "end" <r:@R> => {
Function { annotations, name, params: vec![], reports: false, stmts, raw_span: Span(l, r) }
},
<annotations:Annotation*> <l:@L> "to" <name:Ident> "[" <params:Ident*> "]" <stmts:Stmt*> "end" <r:@R> => {
Function { annotations, name, params, reports: false, stmts, raw_span: Span(l, r) }
},
<annotations:Annotation*> <l:@L> "to-report" <name:Ident> <stmts:Stmt*> "end" <r:@R> => {
Function { annotations, name, params: vec![], reports: true, stmts, raw_span: Span(l, r) }
},
<annotations:Annotation*> <l:@L> "to-report" <name:Ident> "[" <params:Ident*> "]" <stmts:Stmt*> "end" <r:@R> => {
Function { annotations, name, params, reports: true, stmts, raw_span: Span(l, r) }
},
};
Stmt: Stmt = {
Report => Stmt::Report(<>),
IfElse => Stmt::IfElse(<>),
FnCall => Stmt::FnCall(<>),
Ident => Stmt::FnCall(FnCall { name: <>, args: vec![] }),
VarDecl => Stmt::VarDecl(<>),
Assign => Stmt::Assign(<>),
Loop => Stmt::Loop(<>),
Repeat => Stmt::Repeat(<>),
While => Stmt::While(<>),
Create => Stmt::Create(<>),
Ask => Stmt::Ask(<>),
Hatch => Stmt::Hatch(<>),
"(" <Stmt> ")" => <>,
};
Report: Report = <l:@L> "report" <value:Expr> => Report { value, lspan: l };
IfElse: IfElse = {
<l:@L> "if" <condition:Expr> "[" <then:Stmt*> "]" <r:@R> => IfElse { condition, then, otherwise: None, raw_span: Span(l, r) },
<l:@L> "ifelse" <condition:Expr> "[" <then:Stmt*> "]" "[" <otherwise:Stmt*> "]" <r:@R> => IfElse { condition, then, otherwise: Some(otherwise), raw_span: Span(l, r) },
};
VarDecl: VarDecl = <l:@L> "let" <name:Ident> <value:Expr> => VarDecl { name, value, lspan: l };
Assign: Assign = <l:@L> "set" <name:Ident> <value:Expr> => Assign { name, value, lspan: l };
Loop: Loop = <l:@L> "loop" "[" <stmts:Stmt*> "]" <r:@R> => Loop { stmts, raw_span: Span(l, r) };
Repeat: Repeat = <l:@L> "repeat" <count:Expr> "[" <stmts:Stmt*> "]" <r:@R> => Repeat { count, stmts, raw_span: Span(l, r) };
While: While = <l:@L> "while" "[" <condition:Expr> "]" "[" <stmts:Stmt*> "]" <r:@R> => While { condition, stmts, raw_span: Span(l, r) };
Create: Create = <l:@L> <h:CreateHelper> <count:Expr> "[" <stmts:Stmt*> "]" <r:@R> => {
Create { breed_plural: h.0, ordered: h.1, count, stmts, raw_span: Span(l, r) }
};
Ask: Ask = <l:@L> "ask" <agents:Expr> "[" <stmts:Stmt*> "]" <r:@R> => Ask { agents, stmts, raw_span: Span(l, r) };
Hatch: Hatch = <l:@L> "hatch" <count:Expr> "[" <stmts:Stmt*> "]" <r:@R> => Hatch { count, stmts, raw_span: Span(l, r) };
CreateHelper: (Ident, bool) = {
<l:@L> <raw:"CREATE-ORDERED-IDENT"> => (Ident { id: raw[15..].to_lowercase(), raw_span: Span(l + 15, l + raw.len()) }, true),
<l:@L> <raw:"CREATE-IDENT"> => (Ident { id: raw[7..].to_lowercase(), raw_span: Span(l + 7, l + raw.len()) }, false),
};
Expr: Expr = {
<l:@L>"ifelse-value" <cond:Logic> "[" <a:Expr> "]" "[" <b:Expr> "]" <r:@R> => {
Expr::Choice { condition: Box::new(cond), a: Box::new(a), b: Box::new(b), raw_span: Span(l, r) }
},
Logic,
};
Logic: Expr = { // and/or/xor have the same precedence in netlogo - this is not a mistake
<a:Logic> "and" <b:CmpEq> => Expr::And { a: Box::new(a), b: Box::new(b) },
<a:Logic> "or" <b:CmpEq> => Expr::Or { a: Box::new(a), b: Box::new(b) },
<a:Logic> "xor" <b:CmpEq> => Expr::Xor { a: Box::new(a), b: Box::new(b) },
CmpEq,
};
CmpEq: Expr = {
<a:CmpEq> "=" <b:Cmp> => Expr::Equ { a: Box::new(a), b: Box::new(b) },
<a:CmpEq> "!=" <b:Cmp> => Expr::Neq { a: Box::new(a), b: Box::new(b) },
Cmp,
};
Cmp: Expr = {
<a:Cmp> "<" <b:Sum> => Expr::Less { a: Box::new(a), b: Box::new(b) },
<a:Cmp> "<=" <b:Sum> => Expr::LessEq { a: Box::new(a), b: Box::new(b) },
<a:Cmp> ">" <b:Sum> => Expr::Great { a: Box::new(a), b: Box::new(b) },
<a:Cmp> ">=" <b:Sum> => Expr::GreatEq { a: Box::new(a), b: Box::new(b) },
Sum,
};
Sum: Expr = {
<a:Sum> "+" <b:Product> => Expr::Add { a: Box::new(a), b: Box::new(b) },
<a:Sum> "-" <b:Product> => Expr::Sub { a: Box::new(a), b: Box::new(b) },
Product,
};
Product: Expr = {
<a:Product> "*" <b:Power> => Expr::Mul { a: Box::new(a), b: Box::new(b) },
<a:Product> "/" <b:Power> => Expr::Div { a: Box::new(a), b: Box::new(b) },
<a:Product> "mod" <b:Power> => Expr::Mod { a: Box::new(a), b: Box::new(b) },
Power,
}
Power: Expr = {
<a:Power> "^" <b:Term> => Expr::Pow { a: Box::new(a), b: Box::new(b) },
Term,
};
Term: Expr = {
<l:@L> "[" <expr:Expr> "]" "of" <target:Molecule> => Expr::Fetch { target: Box::new(target), expr: Box::new(expr), lspan: l },
<l:@L> "min-one-of" <agents:Expr> "[" <expr:Expr> "]" <r:@R> => Expr::MinMaxOneOf { agents: Box::new(agents), expr: Box::new(expr), is_max: false, raw_span: Span(l, r) },
<l:@L> "max-one-of" <agents:Expr> "[" <expr:Expr> "]" <r:@R> => Expr::MinMaxOneOf { agents: Box::new(agents), expr: Box::new(expr), is_max: true, raw_span: Span(l, r) },
<l:@L> "not" <val:Molecule> => Expr::Not { val: Box::new(val), lspan: l },
"(" <l:@L> "-" <val:Molecule> ")" => Expr::Neg { val: Box::new(val), lspan: l },
FnCall => Expr::FnCall(<>),
Molecule,
};
Molecule: Expr = {
<agents:Molecule> "in-radius" <radius:Atom> => Expr::InRadius { agents: Box::new(agents), radius: Box::new(radius) },
Atom
};
Atom: Expr = {
Value => Expr::Value(<>),
"(" <Expr> ")" => <>,
};
FnCall: FnCall = {
"(" <name:Ident> <args:Term+> ")" => FnCall { name, args },
<name:UnaryFunc> <arg:Term> => FnCall { name, args: vec![arg] },
<name:BinaryFunc> <arg1:Term> <arg2:Term> => FnCall { name, args: vec![arg1, arg2] },
};
Value: Value = {
Ident => Value::Ident(<>),
Number => Value::Number(<>),
Text => Value::Text(<>),
List => Value::List(<>),
};
Ident: Ident = <l:@L> <v:r"[\p{alpha}.?=*!<>:#+/%_^'&-][\p{alpha}\p{digit}.?=*!<>:#+/%_^'&-]*"> <r:@R> => Ident { id: clean_ident(v), raw_span: Span(l, r) };
Number: Number = <l:@L> <v:"NUMBER"> <r:@R> => Number { value: String::from(v), raw_span: Span(l, r) };
Text: Text = <l:@L> <v:r#""(\\[\\nrt"']|[^\\"\r\n])*""#> <r:@R> => Text { content: clean_string(v), raw_span: Span(l, r) };
List: List = <l:@L> "(" "list" <values:Expr*> ")" <r:@R> => List { values, raw_span: Span(l, r) };
// -------------------------------------- //
// -- annotation stuff (meta-langauge) -- //
// -------------------------------------- //
Annotation: Annotation = {
<l:@L> ";@guivar" <ident:Ident> <value:Expr> => Annotation::GuiVar(GuiVar { ident, value, lspan: l }),
<l:@L> ";@placein" <sprite:Ident> <x:Number> <y:Number> <comment:Text?> <r:@R> =>? {
let x = match x.value.parse() { Ok(v) => v, Err(_) => return Err(ParseError::User { error: AstError::ParseFloat { problem_span: x.span() } }) };
let y = match y.value.parse() { Ok(v) => v, Err(_) => return Err(ParseError::User { error: AstError::ParseFloat { problem_span: y.span() } }) };
Ok(Annotation::PlaceIn(PlaceIn { sprite, x, y, comment, raw_span: Span(l, r) }))
},
};