use crate::lexer::bundler::Bundle;
use crate::lexer::token::*;
use moore_common::errors::*;
use moore_common::grind::{Grinder, Lookahead};
use moore_common::name::*;
use moore_common::source::*;
pub struct Tokenizer<T: Grinder> {
inner: Lookahead<T>,
}
impl<T: Grinder> Tokenizer<T>
where
T: Grinder<Item = Option<Spanned<Bundle>>, Error = DiagBuilder2>,
{
pub fn new<I>(inner: I) -> Tokenizer<T>
where
I: Into<Lookahead<T>>,
{
Tokenizer {
inner: inner.into(),
}
}
fn next_significant(&mut self) -> Option<Spanned<Bundle>> {
while let Some(v) = self.inner.next() {
if v.value.is_significant() {
return Some(v);
}
}
None
}
fn parse_bit_string_literal(
&mut self,
int: Option<Spanned<Name>>,
base: Spanned<String>,
mut value: Spanned<String>,
) -> Spanned<Token> {
let (int, mut span) = match int {
Some(Spanned { value, span }) => (Some(value), span),
None => (None, base.span),
};
span.end = value.span.end;
let base = match base.value.to_uppercase().as_str() {
"B" => BitStringBase::B,
"O" => BitStringBase::O,
"X" => BitStringBase::X,
"D" => BitStringBase::D,
"UB" => BitStringBase::UB,
"UO" => BitStringBase::UO,
"UX" => BitStringBase::UX,
"SB" => BitStringBase::SB,
"SO" => BitStringBase::SO,
"SX" => BitStringBase::SX,
_ => {
self.emit(
DiagBuilder2::error(format!(
"`{}` is not a valid base for a bit string literal",
base.value
))
.span(base.span)
.add_note("Valid bases are B, O, X, UB, UO, UX, SB, SO, SX, D"),
);
BitStringBase::B
}
};
let mut parsed_value = String::new();
for c in value.value.drain(..) {
if !c.is_whitespace() {
if c != '_' {
parsed_value.push(c);
}
} else {
self.emit(
DiagBuilder2::error(format!(
"Character `{}` may not appear in a bit string literal",
c
))
.span(value.span),
);
}
}
let value = get_name_table().intern(&parsed_value, true);
Spanned::new(Lit(Literal::BitString(int, base, value)), span)
}
fn parse_integer(&mut self, mut s: String, mut sp: Span) -> Spanned<Name> {
loop {
match self.inner.next() {
Some(Spanned {
value: Bundle::Digits(n),
span,
}) => {
s.push_str(&n);
sp.end = span.end;
}
Some(Spanned {
value: Bundle::Special('_'),
..
}) => (),
n => {
self.inner.undo(n);
break;
}
}
}
Spanned::new(get_name_table().intern(&s, true), sp)
}
fn parse_based_integer(&mut self) -> Spanned<Name> {
let (mut s, mut sp) = match self.inner.next() {
Some(Spanned {
value: Bundle::Letters(n),
span,
})
| Some(Spanned {
value: Bundle::Digits(n),
span,
}) => (n, span),
Some(n) => {
let sp = n.span.begin().into();
self.emit(DiagBuilder2::error("Expected digits or letters").span(sp));
self.inner.undo(Some(n));
return Spanned::new(get_name_table().intern("", true), sp);
}
None => {
self.emit(DiagBuilder2::error("Expected digits or letters"));
self.inner.undo(None);
return Spanned::new(get_name_table().intern("", true), INVALID_SPAN);
}
};
loop {
match self.inner.next() {
Some(Spanned {
value: Bundle::Letters(n),
span,
})
| Some(Spanned {
value: Bundle::Digits(n),
span,
}) => {
s.push_str(&n);
sp.end = span.end;
}
Some(Spanned {
value: Bundle::Special('_'),
..
}) => (),
n => {
self.inner.undo(n);
break;
}
}
}
Spanned::new(get_name_table().intern(&s, true), sp)
}
fn try_exponent(&mut self) -> Option<Spanned<Exponent>> {
match self.inner.next() {
Some(Spanned {
value: Bundle::Letters(ref l),
span: mut sp,
}) if l == "e" || l == "E" => {
let mut n = self.inner.next();
let sign = match n {
Some(Spanned {
value: Bundle::Special('+'),
..
}) => {
n = self.inner.next();
ExponentSign::Positive
}
Some(Spanned {
value: Bundle::Special('-'),
..
}) => {
n = self.inner.next();
ExponentSign::Negative
}
_ => ExponentSign::Positive,
};
match n {
Some(Spanned {
value: Bundle::Digits(s),
span,
}) => {
let int = self.parse_integer(s, span);
sp.end = int.span.end;
Some(Spanned::new(Exponent(sign, int.value), sp))
}
n => {
self.emit(
DiagBuilder2::error(format!("Expected exponent after `{}`", l))
.span(sp),
);
self.inner.undo(n);
None
}
}
}
n => {
self.inner.undo(n);
None
}
}
}
fn parse_symbol(&mut self, c0: char, mut span: Span) -> Option<Spanned<Token>> {
let n1 = self.inner.next();
let n2 = self.inner.next();
if let (
&Some(Spanned {
value: Bundle::Special(c1),
..
}),
&Some(Spanned {
value: Bundle::Special(c2),
span: sp,
}),
) = (&n1, &n2)
{
if let Some(tkn) = match (c0, c1, c2) {
('?', '/', '=') => Some(MatchNeq),
('?', '<', '=') => Some(MatchLeq),
('?', '>', '=') => Some(MatchGeq),
_ => None,
} {
span.expand(sp);
return Some(Spanned::new(tkn, span));
}
}
self.inner.undo(n2);
if let &Some(Spanned {
value: Bundle::Special(c1),
span: sp,
}) = &n1
{
if let Some(tkn) = match (c0, c1) {
('=', '>') => Some(Arrow),
('?', '?') => Some(Condition),
('<', '>') => Some(LtGt),
(':', '=') => Some(VarAssign),
('<', '<') => Some(Lshift),
('>', '>') => Some(Rshift),
('/', '=') => Some(Neq),
('<', '=') => Some(Leq),
('>', '=') => Some(Geq),
('?', '=') => Some(MatchEq),
('?', '<') => Some(MatchLt),
('?', '>') => Some(MatchGt),
('*', '*') => Some(Pow),
_ => None,
} {
span.expand(sp);
return Some(Spanned::new(tkn, span));
}
}
self.inner.undo(n1);
if let Some(tkn) = match c0 {
'(' => Some(OpenDelim(Paren)),
')' => Some(CloseDelim(Paren)),
'[' => Some(OpenDelim(Brack)),
']' => Some(CloseDelim(Brack)),
'.' => Some(Period),
',' => Some(Comma),
':' => Some(Colon),
';' => Some(Semicolon),
'\'' => Some(Apostrophe),
'&' => Some(Ampersand),
'=' => Some(Eq),
'<' => Some(Lt),
'>' => Some(Gt),
'+' => Some(Add),
'-' => Some(Sub),
'*' => Some(Mul),
'/' => Some(Div),
'|' => Some(Pipe),
'?' => Some(Qmark),
_ => None,
} {
return Some(Spanned::new(tkn, span));
}
self.emit(DiagBuilder2::error(format!("`{}` is not a valid symbol", c0)).span(span));
None
}
}
impl<T> Grinder for Tokenizer<T>
where
T: Grinder<Item = Option<Spanned<Bundle>>, Error = DiagBuilder2>,
{
type Item = Option<Spanned<Token>>;
type Error = DiagBuilder2;
fn emit(&mut self, err: Self::Error) {
self.inner.emit(err);
}
fn next(&mut self) -> Self::Item {
let b = match self.next_significant() {
Some(v) => v,
None => return None,
};
match b.value {
Bundle::Letters(mut s) => {
let mut m = self.inner.next();
if let Some(Spanned {
value: Bundle::StringLiteral(v),
span,
}) = m
{
Some(self.parse_bit_string_literal(
None,
Spanned::new(s, b.span),
Spanned::new(v, span),
))
} else {
let mut sp = b.span;
loop {
match m {
Some(Spanned {
value: Bundle::Letters(n),
span,
})
| Some(Spanned {
value: Bundle::Digits(n),
span,
}) => {
s.push_str(&n);
sp.end = span.end;
m = self.inner.next();
}
Some(Spanned {
value: Bundle::Special('_'),
span,
}) => {
s.push('_');
sp.end = span.end;
m = self.inner.next();
}
n => {
self.inner.undo(n);
break;
}
}
}
Some(Spanned::new(
if let Some(kw) = find_keyword(&s) {
Keyword(kw)
} else {
Ident(get_name_table().intern(&s, false))
},
sp,
))
}
}
Bundle::ExtendedIdent(s) => Some(Spanned::new(
Ident(get_name_table().intern(&s, true)),
b.span,
)),
Bundle::Digits(s) => {
let int = self.parse_integer(s, b.span);
match (self.inner.next(), self.inner.next()) {
(
Some(Spanned {
value: Bundle::Letters(b),
span: sp1,
}),
Some(Spanned {
value: Bundle::StringLiteral(s),
span: sp2,
}),
) => Some(self.parse_bit_string_literal(
Some(int),
Spanned::new(b, sp1),
Spanned::new(s, sp2),
)),
(
Some(Spanned {
value: Bundle::Special('.'),
..
}),
Some(Spanned {
value: Bundle::Digits(s),
span,
}),
) => {
let frac = self.parse_integer(s, span);
let exp = self.try_exponent();
let mut sp = int.span;
let exp = match exp {
Some(Spanned { value, span }) => {
sp.end = span.end;
Some(value)
}
_ => {
sp.end = frac.span.end;
None
}
};
Some(Spanned::new(
Lit(Literal::Abstract(None, int.value, Some(frac.value), exp)),
sp,
))
}
(
Some(Spanned {
value: Bundle::Special('#'),
..
}),
n,
) => {
self.inner.undo(n);
let base = int;
let int = self.parse_based_integer();
let mut sp = Span::union(base.span, int.span);
let n = self.inner.next();
let frac = if let Some(Spanned {
value: Bundle::Special('.'),
..
}) = n
{
let f = self.parse_based_integer();
sp.expand(f.span);
Some(f.value)
} else {
self.inner.undo(n);
None
};
match self.inner.next() {
Some(Spanned {
value: Bundle::Special('#'),
span,
}) => {
sp.expand(span);
}
n => {
self.emit(
DiagBuilder2::error(
"Expected `#` after digits of based literal",
)
.span(sp.end()),
);
self.inner.undo(n);
}
}
let exp = match self.try_exponent() {
Some(Spanned { value, span }) => {
sp.end = span.end;
Some(value)
}
_ => None,
};
Some(Spanned::new(
Lit(Literal::Abstract(Some(base.value), int.value, frac, exp)),
sp,
))
}
(n, m) => {
self.inner.undo(m);
self.inner.undo(n);
let exp = self.try_exponent();
let mut sp = int.span;
let exp = match exp {
Some(Spanned { value, span }) => {
sp.end = span.end;
Some(value)
}
_ => {
sp.end = int.span.end;
None
}
};
Some(Spanned::new(
Lit(Literal::Abstract(None, int.value, None, exp)),
sp,
))
}
}
}
Bundle::StringLiteral(s) => Some(Spanned::new(
Lit(Literal::String(get_name_table().intern(&s, true))),
b.span,
)),
Bundle::BitLiteral(c) => Some(Spanned::new(Lit(Literal::Char(c)), b.span)),
Bundle::Special(c0) => self.parse_symbol(c0, b.span),
Bundle::Space | Bundle::Comment => unreachable!(),
}
}
}