use crate::ast::*;
use crate::error::FluidError;
use crate::lexer::Lexer;
use crate::token::{Token, TokenType};
pub struct Parser<'a> {
lexer: Lexer<'a>,
pub i: usize,
pub tokens: Vec<Token<'a>>,
}
impl<'a> Parser<'a> {
pub fn new(mut lexer: Lexer<'a>) -> Self {
let mut t = lexer.next();
let mut tokens = vec![t];
while t.typ != TokenType::Eof {
t = lexer.next();
tokens.push(t);
}
Self {
lexer,
i: 0,
tokens,
}
}
pub fn parse(&mut self) -> Result<Ast, FluidError> {
let mut a = Ast::default();
while self.i < self.tokens.len() {
if self.tokens[self.i].typ == TokenType::Eof {
break;
}
let curr = self.tokens[self.i];
match curr.typ {
TokenType::Eof => break,
TokenType::Word => match curr.word {
"i18n_type" => {
a.i18n_type = Some(true);
self.next_token()?;
self.next_token()?;
}
"class" => {
let c = self.consume_class()?;
a.classes.push(c);
}
"Function" => {
let f = self.consume_func()?;
a.functions.push(f);
}
"comment" => {
let c = self.consume_comment()?;
a.comments.push(c);
}
"decl" => {
let d = self.consume_decl()?;
a.decls.push(d);
}
"widget_class" => {
let w = self.consume_widget()?;
a.widget_classes.push(w);
}
_ => (),
},
_ => (),
}
self.next_token()?;
}
Ok(a)
}
fn next_token(&mut self) -> Result<Token, FluidError> {
self.i += 1;
if self.i >= self.tokens.len() {
let loc = if self.tokens.is_empty() {
crate::error::Location::default()
} else {
self.tokens[self.tokens.len() - 1].loc
};
return Err(FluidError::UnexpectedEof(loc));
}
Ok(self.tokens[self.i])
}
fn consume_func(&mut self) -> Result<Function, FluidError> {
let mut f = Function::default();
self.next_token()?;
f.name = self.consume_braced_string()?;
self.next_token()?; while self.tokens[self.i].typ != TokenType::Eof {
self.next_token()?;
if self.tokens[self.i].typ == TokenType::CloseBrace {
break;
}
match self.tokens[self.i].word {
"open" => f.props.open = Some(true),
"C" => f.props.c = Some(true),
"protected" => f.props.visibility = Some(Visibility::PROTECTED),
"private" => f.props.visibility = Some(Visibility::PRIVATE),
"comment" => {
self.next_token()?;
if self.tokens[self.i].typ == TokenType::OpenBrace {
f.props.comment = Some(self.consume_braced_string()?);
} else {
f.props.comment = Some(self.tokens[self.i].word.to_string());
}
}
"return_type" => {
self.next_token()?;
if self.tokens[self.i].typ == TokenType::OpenBrace {
f.props.return_type = Some(self.consume_braced_string()?);
} else {
f.props.return_type = Some(self.tokens[self.i].word.to_string());
}
}
_ => (),
}
}
self.next_token()?; if self.tokens[self.i].typ == TokenType::OpenBrace {
while self.tokens[self.i].typ != TokenType::CloseBrace {
self.next_token()?;
if self.tokens[self.i].word == "code" {
self.next_token()?;
f.code = Some(self.consume_code()?);
self.next_token()?;
}
if self.tokens[self.i].word.starts_with("Fl_")
|| self.tokens[self.i].word == "MenuItem"
|| self.tokens[self.i].word == "Submenu"
{
let w = self.consume_widget()?;
f.widgets.push(w);
self.next_token()?;
}
}
}
Ok(f)
}
fn consume_widget(&mut self) -> Result<Widget, FluidError> {
let mut w = Widget {
typ: self.tokens[self.i].word.to_string(),
..Default::default()
};
self.next_token()?;
if self.tokens[self.i].typ == TokenType::OpenBrace {
self.next_token()?;
}
if !self.tokens[self.i].word.is_empty() {
w.name = self.tokens[self.i].word.to_string();
} else {
w.name = String::new();
}
while self.tokens[self.i].typ != TokenType::Eof {
self.next_token()?;
if self.tokens[self.i].typ == TokenType::CloseBrace {
break;
}
match self.tokens[self.i].word {
"open" => w.props.open = Some(true),
"hide" => w.props.hide = Some(true),
"deactivate" => w.props.deactivate = Some(true),
"divider" => w.props.divider = Some(true),
"resizable" => w.props.resizable = Some(true),
"visible" => w.props.visible = Some(true),
"hotspot" => w.props.hotspot = Some(true),
"modal" => w.props.modal = Some(true),
"non_modal" => w.props.non_modal = Some(true),
"noborder" => w.props.noborder = Some(true),
"xywh" => {
self.next_token()?;
w.props.xywh = self.consume_braced_string()?;
}
"size_range" => {
self.next_token()?;
w.props.size_range = Some(self.consume_braced_string()?);
}
"color" => {
self.next_token()?;
w.props.color =
Some(self.tokens[self.i].word.to_string().parse().map_err(|_| {
FluidError::Parse(
self.tokens[self.i].word.to_string(),
self.tokens[self.i].loc,
)
})?);
}
"selection_color" => {
self.next_token()?;
w.props.selection_color =
Some(self.tokens[self.i].word.to_string().parse().map_err(|_| {
FluidError::Parse(
self.tokens[self.i].word.to_string(),
self.tokens[self.i].loc,
)
})?);
}
"labelcolor" => {
self.next_token()?;
w.props.labelcolor =
Some(self.tokens[self.i].word.to_string().parse().map_err(|_| {
FluidError::Parse(
self.tokens[self.i].word.to_string(),
self.tokens[self.i].loc,
)
})?);
}
"textcolor" => {
self.next_token()?;
w.props.textcolor =
Some(self.tokens[self.i].word.to_string().parse().map_err(|_| {
FluidError::Parse(
self.tokens[self.i].word.to_string(),
self.tokens[self.i].loc,
)
})?);
}
"type" => {
self.next_token()?;
if self.tokens[self.i].typ == TokenType::OpenBrace {
w.props.typ = Some(self.consume_braced_string()?);
} else {
w.props.typ = Some(self.tokens[self.i].word.to_string());
}
}
"labeltype" => {
self.next_token()?;
w.props.labeltype = Some(self.tokens[self.i].word.to_string());
}
"labelfont" => {
self.next_token()?;
w.props.labelfont =
Some(self.tokens[self.i].word.to_string().parse().map_err(|_| {
FluidError::Parse(
self.tokens[self.i].word.to_string(),
self.tokens[self.i].loc,
)
})?);
}
"textfont" => {
self.next_token()?;
w.props.textfont =
Some(self.tokens[self.i].word.to_string().parse().map_err(|_| {
FluidError::Parse(
self.tokens[self.i].word.to_string(),
self.tokens[self.i].loc,
)
})?);
}
"labelsize" => {
self.next_token()?;
w.props.labelsize =
Some(self.tokens[self.i].word.to_string().parse().map_err(|_| {
FluidError::Parse(
self.tokens[self.i].word.to_string(),
self.tokens[self.i].loc,
)
})?);
}
"textsize" => {
self.next_token()?;
w.props.textsize =
Some(self.tokens[self.i].word.to_string().parse().map_err(|_| {
FluidError::Parse(
self.tokens[self.i].word.to_string(),
self.tokens[self.i].loc,
)
})?);
}
"box" => {
self.next_token()?;
w.props.r#box = Some(self.tokens[self.i].word.to_string());
}
"down_box" => {
self.next_token()?;
w.props.down_box = Some(self.tokens[self.i].word.to_string());
}
"align" => {
self.next_token()?;
w.props.align =
Some(self.tokens[self.i].word.to_string().parse().map_err(|_| {
FluidError::Parse(
self.tokens[self.i].word.to_string(),
self.tokens[self.i].loc,
)
})?);
}
"when" => {
self.next_token()?;
w.props.when =
Some(self.tokens[self.i].word.to_string().parse().map_err(|_| {
FluidError::Parse(
self.tokens[self.i].word.to_string(),
self.tokens[self.i].loc,
)
})?);
}
"shortcut" => {
self.next_token()?;
w.props.shortcut = Some(self.tokens[self.i].word.to_string());
}
"gap" => {
self.next_token()?;
if self.tokens[self.i].typ == TokenType::OpenBrace {
w.props.gap = Some(self.consume_braced_string()?);
} else {
w.props.gap = Some(self.tokens[self.i].word.to_string());
}
}
"minimum" => {
self.next_token()?;
w.props.minimum =
Some(self.tokens[self.i].word.to_string().parse().map_err(|_| {
FluidError::Parse(
self.tokens[self.i].word.to_string(),
self.tokens[self.i].loc,
)
})?);
}
"maximum" => {
self.next_token()?;
w.props.maximum =
Some(self.tokens[self.i].word.to_string().parse().map_err(|_| {
FluidError::Parse(
self.tokens[self.i].word.to_string(),
self.tokens[self.i].loc,
)
})?);
}
"step" => {
self.next_token()?;
w.props.step =
Some(self.tokens[self.i].word.to_string().parse().map_err(|_| {
FluidError::Parse(
self.tokens[self.i].word.to_string(),
self.tokens[self.i].loc,
)
})?);
}
"slider_size" => {
self.next_token()?;
w.props.slider_size =
Some(self.tokens[self.i].word.to_string().parse().map_err(|_| {
FluidError::Parse(
self.tokens[self.i].word.to_string(),
self.tokens[self.i].loc,
)
})?);
}
"size" => {
self.next_token()?;
w.props.size =
Some(self.tokens[self.i].word.to_string().parse().map_err(|_| {
FluidError::Parse(
self.tokens[self.i].word.to_string(),
self.tokens[self.i].loc,
)
})?);
}
"label" => {
self.next_token()?;
if self.tokens[self.i].typ == TokenType::OpenBrace {
w.props.label = Some(self.consume_braced_string()?);
} else {
w.props.label = Some(self.tokens[self.i].word.to_string());
}
}
"xclass" => {
self.next_token()?;
if self.tokens[self.i].typ == TokenType::OpenBrace {
w.props.xclass = Some(self.consume_braced_string()?);
} else {
w.props.xclass = Some(self.tokens[self.i].word.to_string());
}
}
"class" => {
self.next_token()?;
if self.tokens[self.i].typ == TokenType::OpenBrace {
w.props.class = Some(self.consume_braced_string()?);
} else {
w.props.class = Some(self.tokens[self.i].word.to_string());
}
}
"tooltip" => {
self.next_token()?;
if self.tokens[self.i].typ == TokenType::OpenBrace {
w.props.tooltip = Some(self.consume_braced_string()?);
} else {
w.props.tooltip = Some(self.tokens[self.i].word.to_string());
}
}
"image" => {
self.next_token()?;
if self.tokens[self.i].typ == TokenType::OpenBrace {
w.props.image = Some(self.consume_braced_string()?);
} else {
w.props.image = Some(self.tokens[self.i].word.to_string());
}
}
"deimage" => {
self.next_token()?;
if self.tokens[self.i].typ == TokenType::OpenBrace {
w.props.deimage = Some(self.consume_braced_string()?);
} else {
w.props.deimage = Some(self.tokens[self.i].word.to_string());
}
}
"value" => {
self.next_token()?;
if self.tokens[self.i].typ == TokenType::OpenBrace {
w.props.value = Some(self.consume_braced_string()?);
} else {
w.props.value = Some(self.tokens[self.i].word.to_string());
}
}
"set_size_tuples" => {
self.next_token()?;
w.props.size_tuple = Some(self.consume_braced_string()?);
}
"margins" => {
self.next_token()?;
w.props.margins = Some(self.consume_braced_string()?);
}
"dimensions" => {
self.next_token()?;
w.props.dimensions = Some(self.consume_braced_string()?);
}
"margin" => {
self.next_token()?;
w.props.margin = Some(self.consume_braced_string()?);
}
"fixed_size_tuples" => {
self.next_token()?;
w.props.size_tuple = Some(self.consume_braced_string()?);
}
"code0" => {
self.next_token()?;
w.props.code0 = Some(self.consume_braced_string()?);
}
"code1" => {
self.next_token()?;
w.props.code1 = Some(self.consume_braced_string()?);
}
"code2" => {
self.next_token()?;
w.props.code2 = Some(self.consume_braced_string()?);
}
"code3" => {
self.next_token()?;
w.props.code3 = Some(self.consume_braced_string()?);
}
"extra_code" => {
self.next_token()?;
w.props.extra_code = Some(self.consume_braced_string()?);
}
"callback" => {
self.next_token()?;
if self.tokens[self.i].typ == TokenType::OpenBrace {
w.props.callback = Some(self.consume_braced_string()?);
} else {
w.props.callback = Some(self.tokens[self.i].word.to_string());
}
}
"user_data" => {
self.next_token()?;
if self.tokens[self.i].typ == TokenType::OpenBrace {
w.props.user_data = Some(self.consume_braced_string()?);
} else {
w.props.user_data = Some(self.tokens[self.i].word.to_string());
}
}
"user_data_type" => {
self.next_token()?;
if self.tokens[self.i].typ == TokenType::OpenBrace {
w.props.user_data_type = Some(self.consume_braced_string()?);
} else {
w.props.user_data_type = Some(self.tokens[self.i].word.to_string());
}
}
"comment" => {
self.next_token()?;
if self.tokens[self.i].typ == TokenType::OpenBrace {
w.props.comment = Some(self.consume_braced_string()?);
} else {
w.props.comment = Some(self.tokens[self.i].word.to_string());
}
}
"parent_properties" => {
while self.tokens[self.i].typ != TokenType::CloseBrace {
self.next_token()?;
w.props.parent_properties = Some(self.consume_parent_props()?);
}
}
_ => (),
}
}
if self
.tokens
.get(self.i + 1)
.is_some_and(|t| t.typ == TokenType::OpenBrace)
{
self.next_token()?;
while self.tokens[self.i].typ != TokenType::CloseBrace {
self.next_token()?;
while self.tokens[self.i].word.starts_with("Fl_")
|| self.tokens[self.i].word == "MenuItem"
|| self.tokens[self.i].word == "Submenu"
{
let c = self.consume_widget()?;
w.children.push(c);
self.next_token()?;
}
}
}
Ok(w)
}
fn consume_class(&mut self) -> Result<Class, FluidError> {
let mut c = Class::default();
self.next_token()?;
if self.tokens[self.i].typ == TokenType::OpenBrace {
self.next_token()?;
self.next_token()?;
}
c.name = self.tokens[self.i].word.to_string();
self.next_token()?;
while self.tokens[self.i].typ != TokenType::CloseBrace {
self.next_token()?;
match self.tokens[self.i].word {
"open" => c.props.open = Some(true),
"protected" => c.props.visibility = Some(Visibility::PROTECTED),
"private" => c.props.visibility = Some(Visibility::PRIVATE),
"comment" => {
self.next_token()?;
if self.tokens[self.i].typ == TokenType::OpenBrace {
c.props.comment = Some(self.consume_braced_string()?);
} else {
c.props.comment = Some(self.tokens[self.i].word.to_string());
}
}
_ => (),
}
}
self.next_token()?;
if self.tokens[self.i].typ == TokenType::OpenBrace {
while self.tokens[self.i].typ != TokenType::CloseBrace {
self.next_token()?;
while self.tokens[self.i].word == "Function" {
let f = self.consume_func()?;
c.functions.push(f);
}
if self.tokens[self.i].word == "comment" {
self.next_token()?;
if self.tokens[self.i].typ == TokenType::OpenBrace {
c.props.comment = Some(self.consume_braced_string()?);
} else {
c.props.comment = Some(self.tokens[self.i].word.to_string());
}
}
}
}
Ok(c)
}
fn consume_comment(&mut self) -> Result<Comment, FluidError> {
let mut c = Comment::default();
self.next_token()?;
c.comment = self.consume_braced_string()?;
while self.tokens[self.i].typ != TokenType::Eof {
self.next_token()?;
if self.tokens[self.i].typ == TokenType::CloseBrace {
break;
}
match self.tokens[self.i].word {
"in_source" => c.props.in_source = Some(true),
"in_header" => c.props.in_header = Some(true),
_ => (),
}
}
Ok(c)
}
fn consume_decl(&mut self) -> Result<Decl, FluidError> {
let mut d = Decl::default();
self.next_token()?;
d.decl = self.consume_braced_string()?;
while self.tokens[self.i].typ != TokenType::Eof {
self.next_token()?;
if self.tokens[self.i].typ == TokenType::CloseBrace {
break;
}
match self.tokens[self.i].word {
"private" => d.props.visibility = Visibility::PRIVATE,
"public" => d.props.visibility = Visibility::PUBLIC,
"global" => d.props.global = Some(true),
"local" => d.props.local = Some(true),
_ => (),
}
}
Ok(d)
}
fn consume_code(&mut self) -> Result<String, FluidError> {
let s = self.consume_braced_string()?;
self.next_token()?;
self.next_token()?;
Ok(s)
}
fn consume_braced_string(&mut self) -> Result<String, FluidError> {
self.next_token()?;
let mut t = self.tokens[self.i];
let start = t.start;
let mut openbrace = 1;
while t.typ != TokenType::Eof {
self.next_token()?;
t = self.tokens[self.i];
if t.typ == TokenType::OpenBrace {
openbrace += 1;
}
if t.typ == TokenType::CloseBrace {
openbrace -= 1;
}
if openbrace == 0 {
break;
}
}
if t.typ == TokenType::Eof {
return Err(FluidError::UnexpectedEof(self.tokens[self.i].loc));
}
let end = self.tokens[self.i].end - 1;
Ok(self.lexer.s[start..end].to_string())
}
fn consume_parent_props(&mut self) -> Result<ParentProps, FluidError> {
let mut p = ParentProps::default();
while self.tokens[self.i].typ != TokenType::Eof {
self.next_token()?;
if self.tokens[self.i].typ == TokenType::CloseBrace {
break;
}
if self.tokens[self.i].word == "location" {
self.next_token()?;
p.location = Some(self.consume_braced_string()?);
}
}
Ok(p)
}
}