wip -- dot notation
This commit is contained in:
@@ -6,7 +6,8 @@ use parser::{
|
|||||||
tree_node::{
|
tree_node::{
|
||||||
AssignmentExpression, BinaryExpression, BlockExpression, DeviceDeclarationExpression,
|
AssignmentExpression, BinaryExpression, BlockExpression, DeviceDeclarationExpression,
|
||||||
Expression, FunctionExpression, IfExpression, InvocationExpression, Literal,
|
Expression, FunctionExpression, IfExpression, InvocationExpression, Literal,
|
||||||
LiteralOrVariable, LogicalExpression, LoopExpression, Span, Spanned, WhileExpression,
|
LiteralOrVariable, LogicalExpression, LoopExpression, MethodCallExpression, Span, Spanned,
|
||||||
|
WhileExpression,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use quick_error::quick_error;
|
use quick_error::quick_error;
|
||||||
@@ -351,6 +352,9 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
temp_name: Some(result_name),
|
temp_name: Some(result_name),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
Expression::MethodCall(method_expr) => {
|
||||||
|
self.expression_method_call(method_expr.node, scope, method_expr.span)
|
||||||
|
}
|
||||||
_ => Err(Error::Unknown(
|
_ => Err(Error::Unknown(
|
||||||
format!(
|
format!(
|
||||||
"Expression type not yet supported in general expression context: {:?}",
|
"Expression type not yet supported in general expression context: {:?}",
|
||||||
@@ -540,6 +544,28 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
scope,
|
scope,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Expression::MethodCall(method_expr) => {
|
||||||
|
if let Some(result) =
|
||||||
|
self.expression_method_call(method_expr.node, scope, method_expr.span)?
|
||||||
|
{
|
||||||
|
let var_loc = scope.add_variable(&name_str, LocationRequest::Persist)?;
|
||||||
|
|
||||||
|
// Move result from temp to new persistent variable
|
||||||
|
let result_reg = self.resolve_register(&result.location)?;
|
||||||
|
self.emit_variable_assignment(&name_str, &var_loc, result_reg)?;
|
||||||
|
|
||||||
|
// Free the temp result
|
||||||
|
if let Some(name) = result.temp_name {
|
||||||
|
scope.free_temp(name)?;
|
||||||
|
}
|
||||||
|
(var_loc, None)
|
||||||
|
} else {
|
||||||
|
return Err(Error::Unknown(
|
||||||
|
"Method call did not return a value".into(),
|
||||||
|
Some(method_expr.span),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Error::Unknown(
|
return Err(Error::Unknown(
|
||||||
format!("`{name_str}` declaration of this type is not supported/implemented."),
|
format!("`{name_str}` declaration of this type is not supported/implemented."),
|
||||||
@@ -1252,6 +1278,25 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
scope.free_temp(name)?;
|
scope.free_temp(name)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Expression::Invocation(invoke_expr) => {
|
||||||
|
self.expression_function_invocation(invoke_expr, scope)?;
|
||||||
|
// The result is already in RETURN_REGISTER from the call
|
||||||
|
}
|
||||||
|
Expression::MethodCall(method_expr) => {
|
||||||
|
if let Some(result) =
|
||||||
|
self.expression_method_call(method_expr.node, scope, method_expr.span)?
|
||||||
|
{
|
||||||
|
let result_reg = self.resolve_register(&result.location)?;
|
||||||
|
self.write_output(format!(
|
||||||
|
"move r{} {}",
|
||||||
|
VariableScope::RETURN_REGISTER,
|
||||||
|
result_reg
|
||||||
|
))?;
|
||||||
|
if let Some(name) = result.temp_name {
|
||||||
|
scope.free_temp(name)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Error::Unknown(
|
return Err(Error::Unknown(
|
||||||
format!("Unsupported `return` statement: {:?}", expr),
|
format!("Unsupported `return` statement: {:?}", expr),
|
||||||
@@ -1546,4 +1591,117 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
self.write_output("j ra")?;
|
self.write_output("j ra")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expression_method_call<'v>(
|
||||||
|
&mut self,
|
||||||
|
expr: MethodCallExpression,
|
||||||
|
scope: &mut VariableScope<'v>,
|
||||||
|
span: Span,
|
||||||
|
) -> Result<Option<CompilationResult>, Error> {
|
||||||
|
let object_name = expr.object.node;
|
||||||
|
|
||||||
|
if let Some(device_val) = self.devices.get(&object_name).cloned() {
|
||||||
|
match expr.method.node.as_str() {
|
||||||
|
"load" => {
|
||||||
|
if expr.arguments.len() != 1 {
|
||||||
|
return Err(Error::AgrumentMismatch(
|
||||||
|
"load expects 1 argument".into(),
|
||||||
|
span,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
let arg = expr.arguments.into_iter().next().unwrap();
|
||||||
|
let logic_type = match arg.node {
|
||||||
|
Expression::Literal(l) => match l.node {
|
||||||
|
Literal::String(s) => s,
|
||||||
|
_ => {
|
||||||
|
return Err(Error::AgrumentMismatch(
|
||||||
|
"load argument must be a string literal".into(),
|
||||||
|
arg.span,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
return Err(Error::AgrumentMismatch(
|
||||||
|
"load argument must be a string literal".into(),
|
||||||
|
arg.span,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.write_output(format!(
|
||||||
|
"l r{} {} {}",
|
||||||
|
VariableScope::RETURN_REGISTER,
|
||||||
|
device_val,
|
||||||
|
logic_type
|
||||||
|
))?;
|
||||||
|
|
||||||
|
// Move result to a temp register to be safe for use in expressions
|
||||||
|
let temp_name = self.next_temp_name();
|
||||||
|
let temp_loc = scope.add_variable(&temp_name, LocationRequest::Temp)?;
|
||||||
|
self.emit_variable_assignment(
|
||||||
|
&temp_name,
|
||||||
|
&temp_loc,
|
||||||
|
format!("r{}", VariableScope::RETURN_REGISTER),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
return Ok(Some(CompilationResult {
|
||||||
|
location: temp_loc,
|
||||||
|
temp_name: Some(temp_name),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
"set" => {
|
||||||
|
if expr.arguments.len() != 2 {
|
||||||
|
return Err(Error::AgrumentMismatch(
|
||||||
|
"set expects 2 arguments".into(),
|
||||||
|
span,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut args_iter = expr.arguments.into_iter();
|
||||||
|
let logic_type_arg = args_iter.next().unwrap();
|
||||||
|
let value_arg = args_iter.next().unwrap();
|
||||||
|
|
||||||
|
let logic_type = match logic_type_arg.node {
|
||||||
|
Expression::Literal(l) => match l.node {
|
||||||
|
Literal::String(s) => s,
|
||||||
|
_ => {
|
||||||
|
return Err(Error::AgrumentMismatch(
|
||||||
|
"set expects a string literal as first argument".into(),
|
||||||
|
logic_type_arg.span,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
return Err(Error::AgrumentMismatch(
|
||||||
|
"set expects a string literal as first argument".into(),
|
||||||
|
logic_type_arg.span,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let (val_str, cleanup) = self.compile_operand(value_arg, scope)?;
|
||||||
|
|
||||||
|
self.write_output(format!("s {} {} {}", device_val, logic_type, val_str))?;
|
||||||
|
|
||||||
|
if let Some(name) = cleanup {
|
||||||
|
scope.free_temp(name)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(Error::Unknown(
|
||||||
|
format!(
|
||||||
|
"Unknown method '{}' on device '{}'",
|
||||||
|
expr.method.node, object_name
|
||||||
|
),
|
||||||
|
Some(span),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(Error::UnknownIdentifier(object_name, expr.object.span))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,7 +121,6 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates a Span from a given Token reference.
|
/// Calculates a Span from a given Token reference.
|
||||||
/// This is a static helper to avoid borrowing `self` when we already have a token ref.
|
|
||||||
fn token_to_span(t: &Token) -> Span {
|
fn token_to_span(t: &Token) -> Span {
|
||||||
let len = t.original_string.as_ref().map(|s| s.len()).unwrap_or(0);
|
let len = t.original_string.as_ref().map(|s| s.len()).unwrap_or(0);
|
||||||
Span {
|
Span {
|
||||||
@@ -149,7 +148,6 @@ impl<'a> Parser<'a> {
|
|||||||
where
|
where
|
||||||
F: FnOnce(&mut Self) -> Result<T, Error>,
|
F: FnOnce(&mut Self) -> Result<T, Error>,
|
||||||
{
|
{
|
||||||
// Peek at the start token. If no current token (parsing hasn't started), peek the buffer.
|
|
||||||
let start_token = if self.current_token.is_some() {
|
let start_token = if self.current_token.is_some() {
|
||||||
self.current_token.clone()
|
self.current_token.clone()
|
||||||
} else {
|
} else {
|
||||||
@@ -163,7 +161,6 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
let node = parser(self)?;
|
let node = parser(self)?;
|
||||||
|
|
||||||
// The end token is the current_token after parsing.
|
|
||||||
let end_token = self.current_token.as_ref();
|
let end_token = self.current_token.as_ref();
|
||||||
|
|
||||||
let (end_line, end_col) = end_token
|
let (end_line, end_col) = end_token
|
||||||
@@ -184,26 +181,15 @@ impl<'a> Parser<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Skips tokens until a statement boundary is found to recover from errors.
|
|
||||||
fn synchronize(&mut self) -> Result<(), Error> {
|
fn synchronize(&mut self) -> Result<(), Error> {
|
||||||
// We advance once to consume the error-causing token if we haven't already
|
|
||||||
// But often the error happens after we consumed something.
|
|
||||||
// Safe bet: consume current, then look.
|
|
||||||
|
|
||||||
// If we assign next, we might be skipping the very token we want to sync on if the error didn't consume it?
|
|
||||||
// Usually, in recursive descent, the error is raised when `current` is unexpected.
|
|
||||||
// We want to discard `current` and move on.
|
|
||||||
self.assign_next()?;
|
self.assign_next()?;
|
||||||
|
|
||||||
while let Some(token) = &self.current_token {
|
while let Some(token) = &self.current_token {
|
||||||
if token.token_type == TokenType::Symbol(Symbol::Semicolon) {
|
if token.token_type == TokenType::Symbol(Symbol::Semicolon) {
|
||||||
// Consuming the semicolon is a good place to stop and resume parsing next statement
|
|
||||||
self.assign_next()?;
|
self.assign_next()?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the token looks like the start of a statement.
|
|
||||||
// If so, we don't consume it; we return so the loop in parse_all can try to parse it.
|
|
||||||
match token.token_type {
|
match token.token_type {
|
||||||
TokenType::Keyword(Keyword::Fn)
|
TokenType::Keyword(Keyword::Fn)
|
||||||
| TokenType::Keyword(Keyword::Let)
|
| TokenType::Keyword(Keyword::Let)
|
||||||
@@ -231,7 +217,6 @@ impl<'a> Parser<'a> {
|
|||||||
let mut expressions = Vec::<Spanned<Expression>>::new();
|
let mut expressions = Vec::<Spanned<Expression>>::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Check EOF without unwrapping error
|
|
||||||
match self.tokenizer.peek() {
|
match self.tokenizer.peek() {
|
||||||
Ok(None) => break,
|
Ok(None) => break,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -248,19 +233,13 @@ impl<'a> Parser<'a> {
|
|||||||
Ok(None) => break,
|
Ok(None) => break,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
self.errors.push(e);
|
self.errors.push(e);
|
||||||
// Recover
|
|
||||||
if self.synchronize().is_err() {
|
if self.synchronize().is_err() {
|
||||||
// If sync failed (e.g. EOF during sync), break
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Even if we had errors, we return whatever partial AST we managed to build.
|
|
||||||
// If expressions is empty and we had errors, it's a failed parse, but we return a block.
|
|
||||||
|
|
||||||
// Use the last token position for end span, or start if nothing parsed
|
|
||||||
let end_token_opt = self.tokenizer.peek().unwrap_or(None);
|
let end_token_opt = self.tokenizer.peek().unwrap_or(None);
|
||||||
let (end_line, end_col) = end_token_opt
|
let (end_line, end_col) = end_token_opt
|
||||||
.map(|tok| {
|
.map(|tok| {
|
||||||
@@ -285,7 +264,6 @@ impl<'a> Parser<'a> {
|
|||||||
pub fn parse(&mut self) -> Result<Option<Spanned<tree_node::Expression>>, Error> {
|
pub fn parse(&mut self) -> Result<Option<Spanned<tree_node::Expression>>, Error> {
|
||||||
self.assign_next()?;
|
self.assign_next()?;
|
||||||
|
|
||||||
// If assign_next hit EOF or error?
|
|
||||||
if self.current_token.is_none() {
|
if self.current_token.is_none() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
@@ -317,15 +295,18 @@ impl<'a> Parser<'a> {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
// check if the next or current token is an operator, comparison, or logical symbol
|
// Handle Postfix operators (Member Access, Method Call) immediately after unary
|
||||||
|
let lhs = self.parse_postfix(lhs)?;
|
||||||
|
|
||||||
|
// Handle Infix operators (Binary, Logical, Assignment)
|
||||||
if self_matches_peek!(
|
if self_matches_peek!(
|
||||||
self,
|
self,
|
||||||
TokenType::Symbol(s) if s.is_operator() || s.is_comparison() || s.is_logical()
|
TokenType::Symbol(s) if s.is_operator() || s.is_comparison() || s.is_logical() || matches!(s, Symbol::Assign)
|
||||||
) {
|
) {
|
||||||
return Ok(Some(self.infix(lhs)?));
|
return Ok(Some(self.infix(lhs)?));
|
||||||
} else if self_matches_current!(
|
} else if self_matches_current!(
|
||||||
self,
|
self,
|
||||||
TokenType::Symbol(s) if s.is_operator() || s.is_comparison() || s.is_logical()
|
TokenType::Symbol(s) if s.is_operator() || s.is_comparison() || s.is_logical() || matches!(s, Symbol::Assign)
|
||||||
) {
|
) {
|
||||||
self.tokenizer.seek(SeekFrom::Current(-1))?;
|
self.tokenizer.seek(SeekFrom::Current(-1))?;
|
||||||
return Ok(Some(self.infix(lhs)?));
|
return Ok(Some(self.infix(lhs)?));
|
||||||
@@ -334,6 +315,116 @@ impl<'a> Parser<'a> {
|
|||||||
Ok(Some(lhs))
|
Ok(Some(lhs))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handles dot notation chains: x.y.z()
|
||||||
|
fn parse_postfix(
|
||||||
|
&mut self,
|
||||||
|
mut lhs: Spanned<Expression>,
|
||||||
|
) -> Result<Spanned<Expression>, Error> {
|
||||||
|
loop {
|
||||||
|
if self_matches_peek!(self, TokenType::Symbol(Symbol::Dot)) {
|
||||||
|
self.assign_next()?; // consume Dot
|
||||||
|
|
||||||
|
let identifier_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
|
||||||
|
let identifier_span = Self::token_to_span(identifier_token);
|
||||||
|
let identifier = match identifier_token.token_type {
|
||||||
|
TokenType::Identifier(ref id) => id.clone(),
|
||||||
|
_ => {
|
||||||
|
return Err(Error::UnexpectedToken(
|
||||||
|
identifier_span,
|
||||||
|
identifier_token.clone(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check for Method Call '()'
|
||||||
|
if self_matches_peek!(self, TokenType::Symbol(Symbol::LParen)) {
|
||||||
|
// Method Call
|
||||||
|
self.assign_next()?; // consume '('
|
||||||
|
let mut arguments = Vec::<Spanned<Expression>>::new();
|
||||||
|
|
||||||
|
while !token_matches!(
|
||||||
|
self.get_next()?.ok_or(Error::UnexpectedEOF)?,
|
||||||
|
TokenType::Symbol(Symbol::RParen)
|
||||||
|
) {
|
||||||
|
let expression = self.expression()?.ok_or(Error::UnexpectedEOF)?;
|
||||||
|
|
||||||
|
// Block expressions not allowed in args
|
||||||
|
if let Expression::Block(_) = expression.node {
|
||||||
|
return Err(Error::InvalidSyntax(
|
||||||
|
self.current_span(),
|
||||||
|
String::from("Block expressions are not allowed in method calls"),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
arguments.push(expression);
|
||||||
|
|
||||||
|
if !self_matches_peek!(self, TokenType::Symbol(Symbol::Comma))
|
||||||
|
&& !self_matches_peek!(self, TokenType::Symbol(Symbol::RParen))
|
||||||
|
{
|
||||||
|
let next_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
|
||||||
|
return Err(Error::UnexpectedToken(
|
||||||
|
Self::token_to_span(next_token),
|
||||||
|
next_token.clone(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self_matches_peek!(self, TokenType::Symbol(Symbol::RParen)) {
|
||||||
|
self.assign_next()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// End span is the ')'
|
||||||
|
let end_span = self.current_span();
|
||||||
|
let combined_span = Span {
|
||||||
|
start_line: lhs.span.start_line,
|
||||||
|
start_col: lhs.span.start_col,
|
||||||
|
end_line: end_span.end_line,
|
||||||
|
end_col: end_span.end_col,
|
||||||
|
};
|
||||||
|
|
||||||
|
lhs = Spanned {
|
||||||
|
span: combined_span,
|
||||||
|
node: Expression::MethodCall(Spanned {
|
||||||
|
span: combined_span,
|
||||||
|
node: MethodCallExpression {
|
||||||
|
object: boxed!(lhs),
|
||||||
|
method: Spanned {
|
||||||
|
span: identifier_span,
|
||||||
|
node: identifier,
|
||||||
|
},
|
||||||
|
arguments,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Member Access
|
||||||
|
let combined_span = Span {
|
||||||
|
start_line: lhs.span.start_line,
|
||||||
|
start_col: lhs.span.start_col,
|
||||||
|
end_line: identifier_span.end_line,
|
||||||
|
end_col: identifier_span.end_col,
|
||||||
|
};
|
||||||
|
|
||||||
|
lhs = Spanned {
|
||||||
|
span: combined_span,
|
||||||
|
node: Expression::MemberAccess(Spanned {
|
||||||
|
span: combined_span,
|
||||||
|
node: MemberAccessExpression {
|
||||||
|
object: boxed!(lhs),
|
||||||
|
member: Spanned {
|
||||||
|
span: identifier_span,
|
||||||
|
node: identifier,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(lhs)
|
||||||
|
}
|
||||||
|
|
||||||
fn unary(&mut self) -> Result<Option<Spanned<tree_node::Expression>>, Error> {
|
fn unary(&mut self) -> Result<Option<Spanned<tree_node::Expression>>, Error> {
|
||||||
macro_rules! matches_keyword {
|
macro_rules! matches_keyword {
|
||||||
($keyword:expr, $($pattern:pat),+) => {
|
($keyword:expr, $($pattern:pat),+) => {
|
||||||
@@ -357,10 +448,7 @@ impl<'a> Parser<'a> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
TokenType::Keyword(Keyword::Let) => {
|
TokenType::Keyword(Keyword::Let) => Some(self.spanned(|p| p.declaration())?),
|
||||||
// declaration is wrapped in spanned inside the function, but expects 'let' to be current
|
|
||||||
Some(self.spanned(|p| p.declaration())?)
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenType::Keyword(Keyword::Device) => {
|
TokenType::Keyword(Keyword::Device) => {
|
||||||
let spanned_dev = self.spanned(|p| p.device())?;
|
let spanned_dev = self.spanned(|p| p.device())?;
|
||||||
@@ -404,7 +492,6 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
TokenType::Keyword(Keyword::Break) => {
|
TokenType::Keyword(Keyword::Break) => {
|
||||||
let span = self.current_span();
|
let span = self.current_span();
|
||||||
// make sure the next token is a semi-colon
|
|
||||||
let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
|
let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
|
||||||
if !token_matches!(next, TokenType::Symbol(Symbol::Semicolon)) {
|
if !token_matches!(next, TokenType::Symbol(Symbol::Semicolon)) {
|
||||||
return Err(Error::UnexpectedToken(
|
return Err(Error::UnexpectedToken(
|
||||||
@@ -451,16 +538,6 @@ impl<'a> Parser<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
TokenType::Identifier(_)
|
|
||||||
if self_matches_peek!(self, TokenType::Symbol(Symbol::Assign)) =>
|
|
||||||
{
|
|
||||||
let spanned_assign = self.spanned(|p| p.assignment())?;
|
|
||||||
Some(Spanned {
|
|
||||||
span: spanned_assign.span,
|
|
||||||
node: Expression::Assignment(spanned_assign),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenType::Identifier(ref id) => {
|
TokenType::Identifier(ref id) => {
|
||||||
let span = self.current_span();
|
let span = self.current_span();
|
||||||
Some(Spanned {
|
Some(Spanned {
|
||||||
@@ -489,24 +566,36 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TokenType::Symbol(Symbol::LParen) => {
|
TokenType::Symbol(Symbol::LParen) => {
|
||||||
// Priority handles its own spanning
|
|
||||||
self.spanned(|p| p.priority())?.node.map(|node| *node)
|
self.spanned(|p| p.priority())?.node.map(|node| *node)
|
||||||
}
|
}
|
||||||
|
|
||||||
TokenType::Symbol(Symbol::Minus) => {
|
TokenType::Symbol(Symbol::Minus) => {
|
||||||
// Need to handle span manually because unary call is next
|
|
||||||
let start_span = self.current_span();
|
let start_span = self.current_span();
|
||||||
self.assign_next()?;
|
self.assign_next()?;
|
||||||
let inner_expr = self.unary()?.ok_or(Error::UnexpectedEOF)?;
|
let inner_expr = self.unary()?.ok_or(Error::UnexpectedEOF)?;
|
||||||
|
// NOTE: Unary negation can also have postfix applied to the inner expression
|
||||||
|
// But generally -a.b parses as -(a.b), which is what parse_postfix ensures if called here.
|
||||||
|
// However, we call parse_postfix on the RESULT of unary in expression(), so
|
||||||
|
// `expression` sees `Negation`. `parse_postfix` doesn't apply to Negation node unless we allow it?
|
||||||
|
// Actually, `x.y` binds tighter than `-`. `postfix` logic belongs inside `unary` logic or
|
||||||
|
// `expression` logic.
|
||||||
|
// If I have `-x.y`, standard precedence says `-(x.y)`.
|
||||||
|
// `unary` returns `Negation(x)`. Then `expression` calls `postfix` on `Negation(x)`.
|
||||||
|
// `postfix` loop runs on `Negation`. This implies `(-x).y`. This is usually WRONG.
|
||||||
|
// `.` binds tighter than `-`.
|
||||||
|
// So `unary` must call `postfix` on the *operand* of the negation.
|
||||||
|
|
||||||
|
let inner_with_postfix = self.parse_postfix(inner_expr)?;
|
||||||
|
|
||||||
let combined_span = Span {
|
let combined_span = Span {
|
||||||
start_line: start_span.start_line,
|
start_line: start_span.start_line,
|
||||||
start_col: start_span.start_col,
|
start_col: start_span.start_col,
|
||||||
end_line: inner_expr.span.end_line,
|
end_line: inner_with_postfix.span.end_line,
|
||||||
end_col: inner_expr.span.end_col,
|
end_col: inner_with_postfix.span.end_col,
|
||||||
};
|
};
|
||||||
Some(Spanned {
|
Some(Spanned {
|
||||||
span: combined_span,
|
span: combined_span,
|
||||||
node: Expression::Negation(boxed!(inner_expr)),
|
node: Expression::Negation(boxed!(inner_with_postfix)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -514,17 +603,18 @@ impl<'a> Parser<'a> {
|
|||||||
let start_span = self.current_span();
|
let start_span = self.current_span();
|
||||||
self.assign_next()?;
|
self.assign_next()?;
|
||||||
let inner_expr = self.unary()?.ok_or(Error::UnexpectedEOF)?;
|
let inner_expr = self.unary()?.ok_or(Error::UnexpectedEOF)?;
|
||||||
|
let inner_with_postfix = self.parse_postfix(inner_expr)?;
|
||||||
let combined_span = Span {
|
let combined_span = Span {
|
||||||
start_line: start_span.start_line,
|
start_line: start_span.start_line,
|
||||||
start_col: start_span.start_col,
|
start_col: start_span.start_col,
|
||||||
end_line: inner_expr.span.end_line,
|
end_line: inner_with_postfix.span.end_line,
|
||||||
end_col: inner_expr.span.end_col,
|
end_col: inner_with_postfix.span.end_col,
|
||||||
};
|
};
|
||||||
Some(Spanned {
|
Some(Spanned {
|
||||||
span: combined_span,
|
span: combined_span,
|
||||||
node: Expression::Logical(Spanned {
|
node: Expression::Logical(Spanned {
|
||||||
span: combined_span,
|
span: combined_span,
|
||||||
node: LogicalExpression::Not(boxed!(inner_expr)),
|
node: LogicalExpression::Not(boxed!(inner_with_postfix)),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -543,41 +633,44 @@ impl<'a> Parser<'a> {
|
|||||||
fn get_infix_child_node(&mut self) -> Result<Spanned<tree_node::Expression>, Error> {
|
fn get_infix_child_node(&mut self) -> Result<Spanned<tree_node::Expression>, Error> {
|
||||||
let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?;
|
let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?;
|
||||||
|
|
||||||
match current_token.token_type {
|
let start_span = self.current_span();
|
||||||
|
|
||||||
|
let expr = match current_token.token_type {
|
||||||
TokenType::Number(_) | TokenType::Boolean(_) => {
|
TokenType::Number(_) | TokenType::Boolean(_) => {
|
||||||
let lit = self.spanned(|p| p.literal())?;
|
let lit = self.spanned(|p| p.literal())?;
|
||||||
Ok(Spanned {
|
Spanned {
|
||||||
span: lit.span,
|
span: lit.span,
|
||||||
node: Expression::Literal(lit),
|
node: Expression::Literal(lit),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
TokenType::Identifier(ref ident)
|
TokenType::Identifier(ref ident)
|
||||||
if !self_matches_peek!(self, TokenType::Symbol(Symbol::LParen)) =>
|
if !self_matches_peek!(self, TokenType::Symbol(Symbol::LParen)) =>
|
||||||
{
|
{
|
||||||
|
// This is a Variable. We need to check for Postfix operations on it.
|
||||||
let span = self.current_span();
|
let span = self.current_span();
|
||||||
Ok(Spanned {
|
Spanned {
|
||||||
span,
|
span,
|
||||||
node: Expression::Variable(Spanned {
|
node: Expression::Variable(Spanned {
|
||||||
span,
|
span,
|
||||||
node: ident.clone(),
|
node: ident.clone(),
|
||||||
}),
|
}),
|
||||||
})
|
|
||||||
}
|
}
|
||||||
TokenType::Symbol(Symbol::LParen) => Ok(*self
|
}
|
||||||
|
TokenType::Symbol(Symbol::LParen) => *self
|
||||||
.spanned(|p| p.priority())?
|
.spanned(|p| p.priority())?
|
||||||
.node
|
.node
|
||||||
.ok_or(Error::UnexpectedEOF)?),
|
.ok_or(Error::UnexpectedEOF)?,
|
||||||
|
|
||||||
TokenType::Identifier(_)
|
TokenType::Identifier(_)
|
||||||
if self_matches_peek!(self, TokenType::Symbol(Symbol::LParen)) =>
|
if self_matches_peek!(self, TokenType::Symbol(Symbol::LParen)) =>
|
||||||
{
|
{
|
||||||
let inv = self.spanned(|p| p.invocation())?;
|
let inv = self.spanned(|p| p.invocation())?;
|
||||||
Ok(Spanned {
|
Spanned {
|
||||||
span: inv.span,
|
span: inv.span,
|
||||||
node: Expression::Invocation(inv),
|
node: Expression::Invocation(inv),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
TokenType::Symbol(Symbol::Minus) => {
|
TokenType::Symbol(Symbol::Minus) => {
|
||||||
let start_span = self.current_span();
|
|
||||||
self.assign_next()?;
|
self.assign_next()?;
|
||||||
let inner = self.get_infix_child_node()?;
|
let inner = self.get_infix_child_node()?;
|
||||||
let span = Span {
|
let span = Span {
|
||||||
@@ -586,13 +679,12 @@ impl<'a> Parser<'a> {
|
|||||||
end_line: inner.span.end_line,
|
end_line: inner.span.end_line,
|
||||||
end_col: inner.span.end_col,
|
end_col: inner.span.end_col,
|
||||||
};
|
};
|
||||||
Ok(Spanned {
|
Spanned {
|
||||||
span,
|
span,
|
||||||
node: Expression::Negation(boxed!(inner)),
|
node: Expression::Negation(boxed!(inner)),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
TokenType::Symbol(Symbol::LogicalNot) => {
|
TokenType::Symbol(Symbol::LogicalNot) => {
|
||||||
let start_span = self.current_span();
|
|
||||||
self.assign_next()?;
|
self.assign_next()?;
|
||||||
let inner = self.get_infix_child_node()?;
|
let inner = self.get_infix_child_node()?;
|
||||||
let span = Span {
|
let span = Span {
|
||||||
@@ -601,19 +693,25 @@ impl<'a> Parser<'a> {
|
|||||||
end_line: inner.span.end_line,
|
end_line: inner.span.end_line,
|
||||||
end_col: inner.span.end_col,
|
end_col: inner.span.end_col,
|
||||||
};
|
};
|
||||||
Ok(Spanned {
|
Spanned {
|
||||||
span,
|
span,
|
||||||
node: Expression::Logical(Spanned {
|
node: Expression::Logical(Spanned {
|
||||||
span,
|
span,
|
||||||
node: LogicalExpression::Not(boxed!(inner)),
|
node: LogicalExpression::Not(boxed!(inner)),
|
||||||
}),
|
}),
|
||||||
})
|
|
||||||
}
|
}
|
||||||
_ => Err(Error::UnexpectedToken(
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(Error::UnexpectedToken(
|
||||||
self.current_span(),
|
self.current_span(),
|
||||||
current_token.clone(),
|
current_token.clone(),
|
||||||
)),
|
));
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Important: We must check for postfix operations here too
|
||||||
|
// e.g. a + b.c
|
||||||
|
self.parse_postfix(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn device(&mut self) -> Result<DeviceDeclarationExpression, Error> {
|
fn device(&mut self) -> Result<DeviceDeclarationExpression, Error> {
|
||||||
@@ -665,39 +763,6 @@ impl<'a> Parser<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assignment(&mut self) -> Result<AssignmentExpression, Error> {
|
|
||||||
let identifier_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?;
|
|
||||||
let identifier_span = Self::token_to_span(identifier_token);
|
|
||||||
let identifier = match identifier_token.token_type {
|
|
||||||
TokenType::Identifier(ref id) => id.clone(),
|
|
||||||
_ => {
|
|
||||||
return Err(Error::UnexpectedToken(
|
|
||||||
self.current_span(),
|
|
||||||
self.current_token.clone().ok_or(Error::UnexpectedEOF)?,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?.clone();
|
|
||||||
if !token_matches!(current_token, TokenType::Symbol(Symbol::Assign)) {
|
|
||||||
return Err(Error::UnexpectedToken(
|
|
||||||
Self::token_to_span(¤t_token),
|
|
||||||
current_token.clone(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
self.assign_next()?;
|
|
||||||
|
|
||||||
let expression = self.expression()?.ok_or(Error::UnexpectedEOF)?;
|
|
||||||
|
|
||||||
Ok(AssignmentExpression {
|
|
||||||
identifier: Spanned {
|
|
||||||
span: identifier_span,
|
|
||||||
node: identifier,
|
|
||||||
},
|
|
||||||
expression: boxed!(expression),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn infix(&mut self, previous: Spanned<Expression>) -> Result<Spanned<Expression>, Error> {
|
fn infix(&mut self, previous: Spanned<Expression>) -> Result<Spanned<Expression>, Error> {
|
||||||
let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?.clone();
|
let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?.clone();
|
||||||
|
|
||||||
@@ -708,7 +773,9 @@ impl<'a> Parser<'a> {
|
|||||||
| Expression::Priority(_)
|
| Expression::Priority(_)
|
||||||
| Expression::Literal(_)
|
| Expression::Literal(_)
|
||||||
| Expression::Variable(_)
|
| Expression::Variable(_)
|
||||||
| Expression::Negation(_) => {}
|
| Expression::Negation(_)
|
||||||
|
| Expression::MemberAccess(_)
|
||||||
|
| Expression::MethodCall(_) => {}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Error::InvalidSyntax(
|
return Err(Error::InvalidSyntax(
|
||||||
self.current_span(),
|
self.current_span(),
|
||||||
@@ -722,9 +789,10 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
let mut temp_token = current_token.clone();
|
let mut temp_token = current_token.clone();
|
||||||
|
|
||||||
|
// Include Assign in the operator loop
|
||||||
while token_matches!(
|
while token_matches!(
|
||||||
temp_token,
|
temp_token,
|
||||||
TokenType::Symbol(s) if s.is_operator() || s.is_comparison() || s.is_logical()
|
TokenType::Symbol(s) if s.is_operator() || s.is_comparison() || s.is_logical() || matches!(s, Symbol::Assign)
|
||||||
) {
|
) {
|
||||||
let operator = match temp_token.token_type {
|
let operator = match temp_token.token_type {
|
||||||
TokenType::Symbol(s) => s,
|
TokenType::Symbol(s) => s,
|
||||||
@@ -955,6 +1023,37 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
operators.retain(|symbol| !matches!(symbol, Symbol::LogicalOr));
|
operators.retain(|symbol| !matches!(symbol, Symbol::LogicalOr));
|
||||||
|
|
||||||
|
// --- PRECEDENCE LEVEL 8: Assignment (=) ---
|
||||||
|
// Assignment is Right Associative: a = b = c => a = (b = c)
|
||||||
|
// We iterate Right to Left
|
||||||
|
for (i, operator) in operators.iter().enumerate().rev() {
|
||||||
|
if matches!(operator, Symbol::Assign) {
|
||||||
|
let right = expressions.remove(i + 1);
|
||||||
|
let left = expressions.remove(i);
|
||||||
|
let span = Span {
|
||||||
|
start_line: left.span.start_line,
|
||||||
|
start_col: left.span.start_col,
|
||||||
|
end_line: right.span.end_line,
|
||||||
|
end_col: right.span.end_col,
|
||||||
|
};
|
||||||
|
|
||||||
|
expressions.insert(
|
||||||
|
i,
|
||||||
|
Spanned {
|
||||||
|
span,
|
||||||
|
node: Expression::Assignment(Spanned {
|
||||||
|
span,
|
||||||
|
node: AssignmentExpression {
|
||||||
|
assignee: boxed!(left),
|
||||||
|
expression: boxed!(right),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
operators.retain(|symbol| !matches!(symbol, Symbol::Assign));
|
||||||
|
|
||||||
if expressions.len() != 1 || !operators.is_empty() {
|
if expressions.len() != 1 || !operators.is_empty() {
|
||||||
return Err(Error::InvalidSyntax(
|
return Err(Error::InvalidSyntax(
|
||||||
self.current_span(),
|
self.current_span(),
|
||||||
@@ -1506,7 +1605,6 @@ impl<'a> Parser<'a> {
|
|||||||
Literal::String(variable),
|
Literal::String(variable),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
// ... (implementing other syscalls similarly using patterns above)
|
|
||||||
"setOnDevice" => {
|
"setOnDevice" => {
|
||||||
check_length(self, &invocation.arguments, 3)?;
|
check_length(self, &invocation.arguments, 3)?;
|
||||||
let mut args = invocation.arguments.into_iter();
|
let mut args = invocation.arguments.into_iter();
|
||||||
@@ -1531,23 +1629,10 @@ impl<'a> Parser<'a> {
|
|||||||
boxed!(variable),
|
boxed!(variable),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => Err(Error::UnsupportedKeyword(
|
||||||
// For Math functions or unknown functions
|
|
||||||
if SysCall::is_syscall(&invocation.name.node) {
|
|
||||||
// Attempt to parse as math if applicable, or error if strict
|
|
||||||
// Here we are falling back to simple handling or error.
|
|
||||||
// Since Math isn't fully expanded in this snippet, we return Unsupported.
|
|
||||||
Err(Error::UnsupportedKeyword(
|
|
||||||
self.current_span(),
|
self.current_span(),
|
||||||
self.current_token.clone().ok_or(Error::UnexpectedEOF)?,
|
self.current_token.clone().ok_or(Error::UnexpectedEOF)?,
|
||||||
))
|
)),
|
||||||
} else {
|
|
||||||
Err(Error::UnsupportedKeyword(
|
|
||||||
self.current_span(),
|
|
||||||
self.current_token.clone().ok_or(Error::UnexpectedEOF)?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,13 +74,13 @@ impl std::fmt::Display for LogicalExpression {
|
|||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub struct AssignmentExpression {
|
pub struct AssignmentExpression {
|
||||||
pub identifier: Spanned<String>,
|
pub assignee: Box<Spanned<Expression>>,
|
||||||
pub expression: Box<Spanned<Expression>>,
|
pub expression: Box<Spanned<Expression>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for AssignmentExpression {
|
impl std::fmt::Display for AssignmentExpression {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "({} = {})", self.identifier, self.expression)
|
write!(f, "({} = {})", self.assignee, self.expression)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,6 +145,41 @@ impl std::fmt::Display for InvocationExpression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct MemberAccessExpression {
|
||||||
|
pub object: Box<Spanned<Expression>>,
|
||||||
|
pub member: Spanned<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for MemberAccessExpression {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}.{}", self.object, self.member)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct MethodCallExpression {
|
||||||
|
pub object: Box<Spanned<Expression>>,
|
||||||
|
pub method: Spanned<String>,
|
||||||
|
pub arguments: Vec<Spanned<Expression>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for MethodCallExpression {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}.{}({})",
|
||||||
|
self.object,
|
||||||
|
self.method,
|
||||||
|
self.arguments
|
||||||
|
.iter()
|
||||||
|
.map(|e| e.to_string())
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(", ")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum LiteralOrVariable {
|
pub enum LiteralOrVariable {
|
||||||
Literal(Literal),
|
Literal(Literal),
|
||||||
@@ -290,6 +325,8 @@ pub enum Expression {
|
|||||||
Literal(Spanned<Literal>),
|
Literal(Spanned<Literal>),
|
||||||
Logical(Spanned<LogicalExpression>),
|
Logical(Spanned<LogicalExpression>),
|
||||||
Loop(Spanned<LoopExpression>),
|
Loop(Spanned<LoopExpression>),
|
||||||
|
MemberAccess(Spanned<MemberAccessExpression>),
|
||||||
|
MethodCall(Spanned<MethodCallExpression>),
|
||||||
Negation(Box<Spanned<Expression>>),
|
Negation(Box<Spanned<Expression>>),
|
||||||
Priority(Box<Spanned<Expression>>),
|
Priority(Box<Spanned<Expression>>),
|
||||||
Return(Box<Spanned<Expression>>),
|
Return(Box<Spanned<Expression>>),
|
||||||
@@ -314,6 +351,8 @@ impl std::fmt::Display for Expression {
|
|||||||
Expression::Literal(l) => write!(f, "{}", l),
|
Expression::Literal(l) => write!(f, "{}", l),
|
||||||
Expression::Logical(e) => write!(f, "{}", e),
|
Expression::Logical(e) => write!(f, "{}", e),
|
||||||
Expression::Loop(e) => write!(f, "{}", e),
|
Expression::Loop(e) => write!(f, "{}", e),
|
||||||
|
Expression::MemberAccess(e) => write!(f, "{}", e),
|
||||||
|
Expression::MethodCall(e) => write!(f, "{}", e),
|
||||||
Expression::Negation(e) => write!(f, "(-{})", e),
|
Expression::Negation(e) => write!(f, "(-{})", e),
|
||||||
Expression::Priority(e) => write!(f, "({})", e),
|
Expression::Priority(e) => write!(f, "({})", e),
|
||||||
Expression::Return(e) => write!(f, "(return {})", e),
|
Expression::Return(e) => write!(f, "(return {})", e),
|
||||||
@@ -323,3 +362,4 @@ impl std::fmt::Display for Expression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user