Skip to content

Instantly share code, notes, and snippets.

@jorendorff
Created November 12, 2018 21:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jorendorff/9a4bb1cc4d6085a8ee5776c244a39b06 to your computer and use it in GitHub Desktop.
Save jorendorff/9a4bb1cc4d6085a8ee5776c244a39b06 to your computer and use it in GitHub Desktop.
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