// Instructions.
//
// Shrubbery hands over a flat term list per group with no precedence applied,
// so this is a precedence-climbing parser over terms. The table below is Wax's
// own, transcribed from `lib/syntax/parser/parser.mbty`: comparisons bind
// LOOSER than the bitwise operators, which is the opposite of C and is the one
// thing a reader coming from C will get wrong.
///|
/// Binding powers, loosest first. Wax's `%left` declarations, in order.
fn infix_bp(name : String) -> (Int, Int)? {
match name {
"=="
| "!="
| "<"
| "<="
| ">"
| ">="
| "<~"
| "<=~"
| ">~"
| ">=~"
| "<$"
| "<=$"
| ">$"
| ">=$" => Some((3, 4))
"||" => Some((5, 6))
"^" => Some((7, 8))
"&" => Some((9, 10))
"<<" | ">>~" | ">>$" => Some((11, 12))
"+" | "-" => Some((13, 14))
"*" | "/" | "/~" | "/$" | "%~" | "%$" => Some((15, 16))
"as" | "is" | "on" => Some((17, 18))
_ => None
}
}
///|
/// The Wax operator a was operator is.
///
/// `~` is signed and `$` is unsigned, and a bare operator is the one Wax
/// leaves unannotated -- float division, float comparison, reference identity.
/// No letter can appear in a shrubbery operator, which is the whole reason
/// `/s` and `/u` had to become something else.
fn binop_of(name : String) -> @ast.BinOp? {
match name {
"+" => Some(Add)
"-" => Some(Sub)
"*" => Some(Mul)
"/" => Some(Div(None))
"/~" => Some(Div(Some(Signed)))
"/$" => Some(Div(Some(Unsigned)))
"%~" => Some(Rem(Signed))
"%$" => Some(Rem(Unsigned))
"<<" => Some(Shl)
">>~" => Some(Shr(Signed))
">>$" => Some(Shr(Unsigned))
"&" => Some(And)
"||" => Some(Or)
"^" => Some(Xor)
"==" => Some(Eq)
"!=" => Some(Ne)
"<" => Some(Lt(None))
"<~" => Some(Lt(Some(Signed)))
"<$" => Some(Lt(Some(Unsigned)))
">" => Some(Gt(None))
">~" => Some(Gt(Some(Signed)))
">$" => Some(Gt(Some(Unsigned)))
"<=" => Some(Le(None))
"<=~" => Some(Le(Some(Signed)))
"<=$" => Some(Le(Some(Unsigned)))
">=" => Some(Ge(None))
">=~" => Some(Ge(Some(Signed)))
">=$" => Some(Ge(Some(Unsigned)))
_ => None
}
}
///|
/// The compound assignments, and the operation each folds in.
fn assign_binop(name : String) -> @ast.BinOp? {
match name {
"+=" => Some(Add)
"-=" => Some(Sub)
"*=" => Some(Mul)
"/=" => Some(Div(None))
"/~=" => Some(Div(Some(Signed)))
"/$=" => Some(Div(Some(Unsigned)))
"%~=" => Some(Rem(Signed))
"%$=" => Some(Rem(Unsigned))
"<<=" => Some(Shl)
">>~=" => Some(Shr(Signed))
">>$=" => Some(Shr(Unsigned))
"&=" => Some(And)
"||=" => Some(Or)
"^=" => Some(Xor)
_ => None
}
}
///|
/// The name an infix term carries. `as`, `is` and `on` are identifiers to
/// shrubbery and operators to Wax.
fn infix_name(n : @sh.Node) -> String? {
match n.it {
Op(s) => Some(s)
Id(s) => if s == "as" || s == "is" || s == "on" { Some(s) } else { None }
_ => None
}
}
// -------------------------------------------------------------------- bodies
///|
/// A sequence of groups, as the instructions they are.
fn Reader::body(
self : Reader,
groups : Array[@sh.Node],
) -> Array[@ast.Instr[@basic.Location]] raise ReadError {
let out = []
for g in groups {
out.push(self.group(g))
}
out
}
///|
/// One group.
fn Reader::group(
self : Reader,
g : @sh.Node,
) -> @ast.Instr[@basic.Location] raise ReadError {
let p = split(g)
match self.form(p) {
Some(i) => i
None => {
if p.block is Some(_) {
fail_at(
"this line ends with a block, but nothing here takes one",
p.span,
source=self.src,
)
}
self.assign_or_expr(p.head, p.span)
}
}
}
///|
/// A leading `~label`, and the terms after it.
fn label_of(ts : Array[@sh.Node]) -> (String?, Array[@sh.Node]) {
if ts.length() > 0 && as_kw(ts[0]) is Some(k) {
(Some(k), ts[1:].to_owned())
} else {
(None, ts)
}
}
///|
/// The block a form's `:` introduced, or an error naming the form.
fn Reader::need_block(
self : Reader,
p : Parts,
what : String,
) -> Array[@ast.Instr[@basic.Location]] raise ReadError {
match p.block {
Some(gs) => self.body(gs)
None =>
fail_at(
"expected `:` and a body after `" + what + "`",
p.span,
source=self.src,
)
}
}