Created
November 12, 2018 21:49
-
-
Save jorendorff/9a4bb1cc4d6085a8ee5776c244a39b06 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::str::FromStr; | |
use crate::ast::{self, ArithmeticOp, CompareOp}; | |
use crate::location; | |
use crate::token; | |
#[LALR] | |
grammar; | |
match { | |
"{", "}", "(", ")", ":", "@", ",", | |
"+", "-", "*", "/", "~", | |
"<", "=", "<=", | |
".", ";", | |
"=>", | |
"<-", | |
r"(?i)class" => CLASS, | |
r"(?i)case" => CASE, | |
r"(?i)else" => ELSE, | |
r"(?i)fi" => FI, | |
r"(?i)if" => IF, | |
r"(?i)in" => IN, | |
r"(?i)inherits" => INHERITS, | |
r"(?i)isvoid" => ISVOID, | |
r"(?i)let" => LET, | |
r"(?i)loop" => LOOP, | |
r"(?i)pool" => POOL, | |
r"(?i)then" => THEN, | |
r"(?i)while" => WHILE, | |
r"(?i)esac" => ESAC, | |
r"(?i)new" => NEW, | |
r"(?i)of" => OF, | |
r"(?i)not" => NOT, | |
r"(?i)end" => END, | |
r"t[Rr][Uu][Ee]" => TRUE, | |
r"f[Aa][Ll][Ss][Ee]" => FALSE, | |
r"[0-9]+" => Num, | |
} else { | |
r"[A-Z][0-9A-Z_a-z]*" => TypeIdToken, | |
r"[a-z][0-9A-Z_a-z]*" => ObjectIdToken, | |
} | |
ObjectId: token::ObjectId = { | |
<s:ObjectIdToken> => | |
token::to_object_id(s), | |
}; | |
TypeId: token::TypeId = { | |
<s:TypeIdToken> => | |
token::to_type_id(s), | |
}; | |
pub Program: ast::Program = { | |
<classes:ClassList> => | |
ast::Program { classes }, | |
}; | |
ClassList: Vec<ast::Class> = { | |
<class:Class> => | |
vec![class], | |
<classes:ClassList> <class:Class> => { | |
let mut classes = classes; | |
classes.push(class); | |
classes | |
}, | |
}; | |
Class: ast::Class = { | |
<l:@L> CLASS <name:TypeId> <parent:Inherits?> <features:ClassBody> <r:@R> => | |
ast::Class { | |
loc: location::span(l, r), | |
name, | |
parent, | |
attributes: features.0, | |
methods: features.1, | |
}, | |
<l:@L> CLASS <name:TypeId> ! <features:ClassBody> <r:@R> => | |
ast::Class { | |
loc: location::span(l, r), | |
name, | |
parent: None, | |
attributes: features.0, | |
methods: features.1, | |
}, | |
<l:@L> CLASS <name:TypeId> ! ";" <r:@R> => | |
ast::Class { | |
loc: location::span(l, r), | |
name, | |
parent: None, | |
attributes: Vec::new(), | |
methods: Vec::new(), | |
}, | |
}; | |
Inherits: token::TypeId = { | |
INHERITS <TypeId>, | |
} | |
ClassBody: (Vec<Box<ast::Attribute>>, Vec<Box<ast::Method>>) = { | |
"{" <FeatureList> "}" ";" => | |
<>, | |
}; | |
FeatureList: (Vec<Box<ast::Attribute>>, Vec<Box<ast::Method>>) = { | |
END => | |
(Vec::new(), Vec::new()), | |
<features:FeatureList> <attribute:Attribute> => { | |
let mut features = features; | |
features.0.push(attribute); | |
features | |
}, | |
<features:FeatureList> <method:Method> => { | |
let mut features = features; | |
features.1.push(method); | |
features | |
}, | |
<features:FeatureList> ! ";" => | |
features, | |
}; | |
Attribute: Box<ast::Attribute> = { | |
<l:@L> <name:ObjectId> ":" <type_:TypeId> <initializer:Initializer?> ";" <r:@R> => | |
Box::new( | |
ast::Attribute { | |
loc: location::span(l, r), | |
name, | |
type_, | |
initializer, | |
}, | |
), | |
}; | |
Method: Box<ast::Method> = { | |
<l:@L> <name:ObjectId> <parameters:FormalsParenthesized> ":" <return_type:TypeId> <sigr:@R> | |
<body:MethodBody> ";" <r:@R> => | |
Box::new( | |
ast::Method { | |
loc: location::span(l, r), | |
signature_loc: location::span(l, sigr), | |
name, | |
parameters, | |
return_type, | |
body, | |
}, | |
), | |
}; | |
MethodBody: ast::Expression = { | |
"{" <Expression<Assignment<"yes">>> "}", | |
<l:@L> "{" ! "}" <r:@R> => | |
ast::Expression { | |
loc: location::span(l, r), | |
type_: "", | |
e: ast::ExpressionDetails::Error, | |
}, | |
}; | |
FormalsParenthesized: Vec<ast::Parameter> = { | |
"(" ")" => | |
Vec::new(), | |
"(" <Formals> ")", | |
"(" ! ")" => | |
Vec::new(), | |
}; | |
Formals: Vec<ast::Parameter> = { | |
<param:Formal> => | |
vec![param], | |
<params:Formals> "," <param:Formal> => { | |
let mut params = params; | |
params.push(param); | |
params | |
}, | |
}; | |
Formal: ast::Parameter = { | |
<l:@L> <name:ObjectId> ":" <type_:TypeId> <r:@R> => | |
ast::Parameter { | |
loc: location::span(l, r), | |
name, | |
type_, | |
}, | |
}; | |
Expression<T>: ast::Expression = { | |
<l:@L> <e:T> <r:@R> => | |
ast::Expression { | |
loc: location::span(l, r), | |
type_: "", | |
e, | |
}, | |
}; | |
Assignment<AllowLet>: ast::ExpressionDetails = { | |
<l:ObjectId> "<-" <r:Expression<Assignment<AllowLet>>> => | |
ast::ExpressionDetails::Assign(l, Box::new(r)), | |
<Not<AllowLet>>, | |
}; | |
Not<AllowLet>: ast::ExpressionDetails = { | |
NOT <operand:Expression<Comparison<AllowLet>>> => | |
ast::ExpressionDetails::Not(Box::new(operand)), | |
<Comparison<AllowLet>>, | |
}; | |
Comparison<AllowLet>: ast::ExpressionDetails = { | |
<l:Expression<Addition<"no">>> "<" <r:Expression<Addition<AllowLet>>> => | |
ast::ExpressionDetails::Compare(CompareOp::Lt, Box::new(l), Box::new(r)), | |
<l:Expression<Addition<"no">>> "<=" <r:Expression<Addition<AllowLet>>> => | |
ast::ExpressionDetails::Compare(CompareOp::Le, Box::new(l), Box::new(r)), | |
<l:Expression<Addition<"no">>> "=" <r:Expression<Addition<AllowLet>>> => | |
ast::ExpressionDetails::Eq(Box::new(l), Box::new(r)), | |
<Addition<AllowLet>>, | |
}; | |
Addition<AllowLet>: ast::ExpressionDetails = { | |
<l:Expression<Addition<"no">>> "+" <r:Expression<Multiplication<AllowLet>>> => | |
ast::ExpressionDetails::Arithmetic(ArithmeticOp::Add, Box::new(l), Box::new(r)), | |
<l:Expression<Addition<"no">>> "-" <r:Expression<Multiplication<AllowLet>>> => | |
ast::ExpressionDetails::Arithmetic(ArithmeticOp::Sub, Box::new(l), Box::new(r)), | |
<Multiplication<AllowLet>>, | |
}; | |
Multiplication<AllowLet>: ast::ExpressionDetails = { | |
<l:Expression<Multiplication<"no">>> "*" <r:Expression<IsVoid<AllowLet>>> => | |
ast::ExpressionDetails::Arithmetic(ArithmeticOp::Mul, Box::new(l), Box::new(r)), | |
<l:Expression<Multiplication<"no">>> "/" <r:Expression<IsVoid<AllowLet>>> => | |
ast::ExpressionDetails::Arithmetic(ArithmeticOp::Div, Box::new(l), Box::new(r)), | |
<IsVoid<AllowLet>>, | |
}; | |
IsVoid<AllowLet>: ast::ExpressionDetails = { | |
ISVOID <expr:Expression<IsVoid<AllowLet>>> => | |
ast::ExpressionDetails::IsVoid(Box::new(expr)), | |
<Negation<AllowLet>>, | |
}; | |
Negation<AllowLet>: ast::ExpressionDetails = { | |
"~" <operand:Expression<Negation<AllowLet>>> => | |
ast::ExpressionDetails::Negate(Box::new(operand)), | |
<Dispatch<AllowLet>>, | |
}; | |
Dispatch<AllowLet>: ast::ExpressionDetails = { | |
<recipient:Boxed<Expression<Dispatch<"no">>>> | |
<static_type:StaticType?> | |
"." <method_name:ObjectId> <arguments:Arguments> => | |
ast::ExpressionDetails::Dispatch { | |
recipient: Some(recipient), | |
static_type: static_type, | |
method_name, | |
arguments, | |
}, | |
<method_name:ObjectId> <arguments:Arguments> => | |
ast::ExpressionDetails::Dispatch { | |
recipient: None, | |
static_type: None, | |
method_name, | |
arguments, | |
}, | |
<Primary<AllowLet>>, | |
}; | |
StaticType: (location::Location, token::TypeId) = { | |
"@" <l:@L> <type_:TypeId> <r:@R> => | |
(location::span(l, r), type_), | |
}; | |
Arguments: Vec<ast::Expression> = { | |
"(" ")" => | |
Vec::new(), | |
"(" <NonEmptyArguments> ")" => | |
<>, | |
}; | |
NonEmptyArguments: Vec<ast::Expression> = { | |
<arg:Expression<Assignment<"yes">>> => | |
vec![arg], | |
<args:NonEmptyArguments> "," <arg:Expression<Assignment<"yes">>> => { | |
let mut args = args; | |
args.push(arg); | |
args | |
}, | |
}; | |
Primary<AllowLet>: ast::ExpressionDetails = { | |
IF <condition:Boxed<Expression<Assignment<"yes">>>> | |
THEN <then_expr:Boxed<Expression<Assignment<"yes">>>> | |
ELSE <else_expr:Boxed<Expression<Assignment<"yes">>>> | |
FI => | |
ast::ExpressionDetails::If(condition, then_expr, else_expr), | |
WHILE <condition:Boxed<Expression<Assignment<"yes">>>> | |
LOOP <body:Boxed<Expression<Assignment<"yes">>>> | |
POOL => | |
ast::ExpressionDetails::While(condition, body), | |
"{" <be:BlockExprs> "}" => | |
ast::ExpressionDetails::Block(be.0, Box::new(be.1)), | |
"{" ! "}" => | |
ast::ExpressionDetails::Error, | |
LET <LetTail> if AllowLet == "yes" => | |
<>, | |
CASE <discriminator:Boxed<Expression<Assignment<"yes">>>> | |
OF <branches:Cases> | |
ESAC => | |
ast::ExpressionDetails::Case { discriminator, branches }, | |
NEW <type_:TypeId> => | |
ast::ExpressionDetails::New(type_), | |
"(" <Assignment<"yes">> ")" => | |
<>, | |
"(" ! ")" => | |
ast::ExpressionDetails::Error, | |
<id:ObjectId> => | |
ast::ExpressionDetails::Var(id), | |
TRUE => | |
ast::ExpressionDetails::BoolConst(true), | |
FALSE => | |
ast::ExpressionDetails::BoolConst(false), | |
<s:Num> => | |
ast::ExpressionDetails::IntConst(u32::from_str(s).unwrap()), | |
}; | |
BlockExprs: (Vec<ast::Expression>, ast::Expression) = { | |
<expr:Expression<Assignment<"yes">>> ";" => (Vec::new(), expr), | |
<block_exprs:BlockExprs> <expr:Expression<Assignment<"yes">>> ";" => { | |
let (mut v, e) = block_exprs; | |
v.push(e); | |
(v, expr) | |
}, | |
}; | |
Boxed<T>: Box<T> = { | |
<T> => | |
Box::new(<>), | |
}; | |
Initializer: Box<ast::Expression> = { | |
"<-" <Boxed<Expression<Assignment<"yes">>>>, | |
} | |
LetTail: ast::ExpressionDetails = { | |
<l:@L> <binding_name:ObjectId> ":" <binding_type:TypeId> <r:@R> | |
<initializer:Initializer?> | |
IN <body:Expression<Assignment<"yes">>> => | |
ast::ExpressionDetails::Let { | |
binding_loc: location::span(l, r), | |
binding_name, | |
binding_type, | |
initializer, | |
body: Box::new(body), | |
}, | |
<l:@L> <binding_name:ObjectId> ":" <binding_type:TypeId> <r:@R> | |
<initializer:Initializer?> | |
"," <etc:Expression<LetTail>> => | |
ast::ExpressionDetails::Let { | |
binding_loc: location::span(l, r), | |
binding_name, | |
binding_type, | |
initializer, | |
body: Box::new(etc), | |
}, | |
}; | |
Cases: Vec<ast::Branch> = { | |
<case:Case> => | |
vec![case], | |
<cases:Cases> <case:Case> => { | |
let mut cases = cases; | |
cases.push(case); | |
cases | |
}, | |
}; | |
Case: ast::Branch = { | |
<l:@L> <binding_name:ObjectId> ":" <binding_type:TypeId> <r:@R> | |
"=>" <body:Expression<Assignment<"yes">>> ";" => | |
ast::Branch { | |
binding_loc: location::span(l, r), | |
binding_name, | |
binding_type, | |
body: Box::new(body) | |
}, | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment