// Copyright ⓒ 2017 David Li. // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. use std::str::FromStr; use ast::{self, WithLocation}; grammar; pub Program: ast::Program = { Expression => ast::Program(<>), }; FieldList: Vec<WithLocation<ast::RecordField>> = { <l: @L> <n:Name> ":" <v:Ty> <r: @R> "," <rest: FieldList> => { let mut rest = rest; rest.push(WithLocation::new(ast::RecordField::new(n, v), l, r)); rest }, <l: @L> <n:Name> ":" <v:Ty> <r: @R> "," => vec![WithLocation::new(ast::RecordField::new(n, v), l, r)], <l: @L> <n:Name> ":" <v:Ty> <r: @R> => vec![WithLocation::new(ast::RecordField::new(n, v), l, r)], }; RecordFields: WithLocation<Vec<WithLocation<ast::RecordField>>> = { <l: @L> "{" <v: FieldList> "}" <r: @R> => WithLocation::new(v, l, r), <l: @L> "{" "}" <r: @R> => WithLocation::new(vec![], l, r), }; Parameters: WithLocation<Vec<WithLocation<ast::RecordField>>> = { <l: @L> "(" <v: FieldList> ")" <r: @R> => WithLocation::new(v, l, r), <l: @L> "(" ")" <r: @R> => WithLocation::new(vec![], l, r), }; Ty: WithLocation<ast::Ty> = { <Name> => <>.map(|v| ast::Ty::Name(v)), <l: @L> "array" "of" <n: Name> <r: @R> => WithLocation::new(ast::Ty::Array(Box::new(n.map(|v| ast::Ty::Name(v)))), l, r), <RecordFields> => <>.map(|v| ast::Ty::Record(v)), }; Declaration: Box<WithLocation<ast::Declaration>> = { <l: @L> "var" <name: Name> ":" <ty: Ty> "=" <exp: Expression> <r: @R> => Box::new(WithLocation::new(ast::Declaration::new_var(name, Some(ty), exp), l, r)), <l: @L> "var" <name: Name> "=" <exp: Expression> <r: @R> => Box::new(WithLocation::new(ast::Declaration::new_var(name, None, exp), l, r)), <l: @L> "type" <name: Name> "=" <ty: Ty> <r: @R> => Box::new(WithLocation::new(ast::Declaration::new_ty(name, ty), l, r)), <l: @L> "function" <name: Name> <params: Parameters> "=" <body: Expression> <r: @R> => Box::new(WithLocation::new(ast::Declaration::new_fun(name, params.value, None, body), l, r)), <l: @L> "function" <name: Name> <params: Parameters> ":" <ty: Ty> "=" <body: Expression> <r: @R> => Box::new(WithLocation::new(ast::Declaration::new_fun(name, params.value, Some(ty), body), l, r)), }; DeclarationsList: Vec<WithLocation<ast::Declaration>> = { <h: DeclarationsList> <t: Declaration> => { let mut h = h; h.push(*t); h }, <Declaration> => vec![*<>], }; Expression: Box<ast::WithLocation<ast::Expression>> = { <e1:Expression> "&&" <e2:ExpressionEq> => Box::new(e1.join_map(*e2, |v1, v2| { ast::Expression::BinOp(ast::BinOp::And, Box::new(v1), Box::new(v2)) })), <e1:Expression> "||" <e2:ExpressionEq> => Box::new(e1.join_map(*e2, |v1, v2| { ast::Expression::BinOp(ast::BinOp::Or, Box::new(v1), Box::new(v2)) })), <l: @L> "!" <e: ExpressionEq> <r: @R> => Box::new(WithLocation::new( ast::Expression::UnaryOp(ast::UnaryOp::Not, e), l, r )), <ExpressionEq> => <>, }; ExpressionEq: Box<ast::WithLocation<ast::Expression>> = { <e1:ExpressionEq> "=" <e2:ExpressionCmp> => Box::new(e1.join_map(*e2, |v1, v2| { ast::Expression::BinOp(ast::BinOp::Eq, Box::new(v1), Box::new(v2)) })), <e1:ExpressionEq> "!=" <e2:ExpressionCmp> => Box::new(e1.join_map(*e2, |v1, v2| { ast::Expression::BinOp(ast::BinOp::Neq, Box::new(v1), Box::new(v2)) })), <ExpressionCmp> => <>, }; ExpressionCmp: Box<ast::WithLocation<ast::Expression>> = { <e1:ExpressionCmp> ">" <e2:ExpressionAdd> => Box::new(e1.join_map(*e2, |v1, v2| { ast::Expression::BinOp(ast::BinOp::Gt, Box::new(v1), Box::new(v2)) })), <e1:ExpressionCmp> "<" <e2:ExpressionAdd> => Box::new(e1.join_map(*e2, |v1, v2| { ast::Expression::BinOp(ast::BinOp::Lt, Box::new(v1), Box::new(v2)) })), <e1:ExpressionCmp> ">=" <e2:ExpressionAdd> => Box::new(e1.join_map(*e2, |v1, v2| { ast::Expression::BinOp(ast::BinOp::Ge, Box::new(v1), Box::new(v2)) })), <e1:ExpressionCmp> "<=" <e2:ExpressionAdd> => Box::new(e1.join_map(*e2, |v1, v2| { ast::Expression::BinOp(ast::BinOp::Le, Box::new(v1), Box::new(v2)) })), <ExpressionAdd> => <>, }; ExpressionAdd: Box<ast::WithLocation<ast::Expression>> = { <e1:ExpressionAdd> "+" <e2:ExpressionMul> => Box::new(e1.join_map(*e2, |v1, v2| { ast::Expression::BinOp(ast::BinOp::Add, Box::new(v1), Box::new(v2)) })), <e1:ExpressionAdd> "-" <e2:ExpressionMul> => Box::new(e1.join_map(*e2, |v1, v2| { ast::Expression::BinOp(ast::BinOp::Sub, Box::new(v1), Box::new(v2)) })), <ExpressionMul> => <>, }; ExpressionMul: Box<ast::WithLocation<ast::Expression>> = { <e1:ExpressionMul> "*" <e2:ExpressionSign> => Box::new(e1.join_map(*e2, |v1, v2| { ast::Expression::BinOp(ast::BinOp::Mul, Box::new(v1), Box::new(v2)) })), <e1:ExpressionMul> "/" <e2:ExpressionSign> => Box::new(e1.join_map(*e2, |v1, v2| { ast::Expression::BinOp(ast::BinOp::Div, Box::new(v1), Box::new(v2)) })), <e1:ExpressionMul> "//" <e2:ExpressionSign> => Box::new(e1.join_map(*e2, |v1, v2| { ast::Expression::BinOp(ast::BinOp::FloorDiv, Box::new(v1), Box::new(v2)) })), <ExpressionSign> => <>, }; ExpressionSign: Box<ast::WithLocation<ast::Expression>> = { <l: @L> "+" <e: ExpressionBase> <r: @R> => Box::new(WithLocation::new( ast::Expression::UnaryOp(ast::UnaryOp::Pos, e), l, r )), <l: @L> "-" <e: ExpressionBase> <r: @R> => Box::new(WithLocation::new( ast::Expression::UnaryOp(ast::UnaryOp::Neg, e), l, r )), <ExpressionBase> => <>, }; CallArgsList: Vec<WithLocation<ast::Expression>> = { <exp: Expression> "," <rest: CallArgsList> => { let mut rest = rest; rest.push(*exp); rest }, <exp: Expression> "," => vec![*exp], <exp: Expression> => vec![*exp], }; CallArgs: Vec<WithLocation<ast::Expression>> = { "(" <v: CallArgsList> ")" => v, "(" ")" => vec![], }; ExpressionBase: Box<ast::WithLocation<ast::Expression>> = { Num => Box::new(<>.map(|v| ast::Expression::Number(v))), String => Box::new(<>.map(|v| ast::Expression::String(v))), Name => Box::new(<>.map(|v| ast::Expression::Name(v))), <l: @L> <func: Name> <args: CallArgs> <r: @R> => Box::new(WithLocation::new(ast::Expression::Call(func.value, args), l, r)), Spanned<r"nil"> => Box::new(<>.map(|v| ast::Expression::Nil)), <l: @L> "let" <d: DeclarationsList> "in" <e: Expression> "end" <r: @R> => Box::new(WithLocation::new(ast::Expression::Let(d, e), l, r)), "(" <Expression> ")" => <>, }; Name: WithLocation<String> = <e: Spanned<r"[[:alpha:]_][[:alpha:]_0-9]*">> => e.map(|v| v.to_owned()); Num: WithLocation<u64> = <e: Spanned<r"[0-9]+">> => e.map(|v| u64::from_str(v).unwrap()); String: WithLocation<String> = <l: @L> <v:r##""(?:[^"\\]|\\\\)*""##> <r: @R> => { WithLocation::new(v[1..v.len() - 1].to_owned(), l, r) }; Spanned<T>: WithLocation<T> = { <l: @L> <v: T> <r: @R> => WithLocation::new(v, l, r) }; match { "nil", "var", "let", "in", "end", "type", "function", } else { r"[[:alpha:]_][[:alpha:]_0-9]*", } else { _, }