diff --git a/src/parse_error.rs b/src/parse_error.rs index c61f01d851..d242f870d7 100644 --- a/src/parse_error.rs +++ b/src/parse_error.rs @@ -6,4 +6,5 @@ pub enum ParseError { UnexpectedEof(String, Span), UnknownStatement(Span), Mismatch(String, Span), + VariableNotFound(Span), } diff --git a/src/parser.rs b/src/parser.rs index dea68accd4..a77a912aff 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -121,6 +121,22 @@ fn garbage(span: Span) -> Expression { Expression::garbage(span) } +fn is_identifier_byte(b: u8) -> bool { + b != b'.' && b != b'[' && b != b'(' && b != b'{' +} + +fn is_identifier(bytes: &[u8]) -> bool { + bytes.iter().all(|x| is_identifier_byte(*x)) +} + +fn is_variable(bytes: &[u8]) -> bool { + if bytes.len() > 1 && bytes[0] == b'$' { + is_identifier(&bytes[1..]) + } else { + is_identifier(bytes) + } +} + fn span(spans: &[Span]) -> Span { let length = spans.len(); @@ -254,9 +270,25 @@ impl ParserWorkingSet { span: Span, shape: SyntaxShape, ) -> (Expression, Option) { + let bytes = self.get_span_contents(span); + if !bytes.is_empty() && bytes[0] == b'$' { + if let Some((var_id, _, ty)) = self.find_variable(bytes) { + return ( + Expression { + expr: Expr::Var(var_id), + ty, + span, + }, + None, + ); + } else { + return (garbage(span), Some(ParseError::VariableNotFound(span))); + } + } + match shape { SyntaxShape::Number => { - if let Ok(token) = String::from_utf8(self.get_span_contents(span).into()) { + if let Ok(token) = String::from_utf8(bytes.into()) { self.parse_number(&token, span) } else { ( @@ -280,13 +312,17 @@ impl ParserWorkingSet { self.parse_math_expression(spans) } - pub fn parse_variable(&mut self, span: Span) -> Option { - let contents = self.get_span_contents(span); + pub fn parse_variable(&mut self, span: Span) -> (Option, Option) { + let bytes = self.get_span_contents(span); - if !contents.is_empty() && contents[0] == b'$' { - None + if is_variable(bytes) { + if let Some((var_id, _, _)) = self.find_variable(bytes) { + (Some(var_id), None) + } else { + (None, None) + } } else { - Some(ParseError::Mismatch("variable".into(), span)) + (None, Some(ParseError::Mismatch("variable".into(), span))) } } @@ -304,7 +340,7 @@ impl ParserWorkingSet { pub fn parse_let(&mut self, spans: &[Span]) -> (Statement, Option) { let mut error = None; if spans.len() >= 4 && self.parse_keyword(spans[0], b"let").is_none() { - let err = self.parse_variable(spans[1]); + let (_, err) = self.parse_variable(spans[1]); error = error.or(err); let err = self.parse_keyword(spans[2], b"="); @@ -369,8 +405,6 @@ impl ParserWorkingSet { let (output, err) = lite_parse(&output); error = error.or(err); - println!("{:?}", output); - let (output, err) = self.parse_block(&output); error = error.or(err); diff --git a/src/parser_state.rs b/src/parser_state.rs index 955da059fb..b7cdf2a35e 100644 --- a/src/parser_state.rs +++ b/src/parser_state.rs @@ -122,15 +122,15 @@ impl ParserWorkingSet { self.scope.push(ScopeFrame::new()); } - pub fn find_variable(&self, name: &[u8]) -> Option<(VarLocation, Type)> { + pub fn find_variable(&self, name: &[u8]) -> Option<(VarId, VarLocation, Type)> { for scope in self.scope.iter().rev().enumerate() { - if let Some(result) = scope.1.vars.get(name) { - if let Some(result) = self.vars.get(result) { + if let Some(var_id) = scope.1.vars.get(name) { + if let Some(result) = self.vars.get(var_id) { if scope.0 == 0 { // Top level - return Some((VarLocation::CurrentScope, result.clone())); + return Some((*var_id, VarLocation::CurrentScope, *result)); } else { - return Some((VarLocation::OuterScope, result.clone())); + return Some((*var_id, VarLocation::OuterScope, *result)); } } }