Added print statement, various other fixes

This commit is contained in:
2026-07-10 02:06:03 -05:00
parent 725c6aa835
commit cf3e14c9e7
10 changed files with 261 additions and 87 deletions
+12
View File
@@ -0,0 +1,12 @@
# TODO
+ function calling
+ functions
+ mutable bindings
+ arrays
+ type syntax
+ error handling
+ pattern matching
+ closures
+ structs & tuples
+ loops
+ proper vm
+38 -10
View File
@@ -1,12 +1,12 @@
use crate::lex::{ TeaValue, Operator }; use crate::lex::{ TeaValue, Operator };
use crate::parse::{ ASTNode, Value }; use crate::parse::{ ASTNode, Value, KeywordFunction };
use std::collections::HashMap; use std::collections::HashMap;
#[derive(Debug)] #[derive(Debug)]
enum VarType { pub enum VarType {
Const(TeaValue), Const(TeaValue),
//Mut,
} }
enum BlockType { enum BlockType {
@@ -16,7 +16,7 @@ enum BlockType {
#[derive(Debug)] #[derive(Debug)]
pub struct InterpreterError { pub struct InterpreterError {
description: String, pub description: String,
} }
impl InterpreterError { impl InterpreterError {
@@ -47,6 +47,7 @@ fn get_value(v: Value, scopes: &mut [Scope]) -> Result<TeaValue, InterpreterErro
}, },
} }
}, },
Value::KwFn(_) => Err(InterpreterError::new("not yet implemented"))
} }
} }
@@ -59,24 +60,49 @@ macro_rules! apply_op {
(TeaValue::Int(aa), TeaValue::Int(bb)) => { (TeaValue::Int(aa), TeaValue::Int(bb)) => {
TeaValue::Int(aa $op bb) TeaValue::Int(aa $op bb)
}, },
//(TeaValue::Uint(aa), TeaValue::Uint(bb)) => { (TeaValue::Uint(aa), TeaValue::Uint(bb)) => {
// TeaValue::Uint(aa $op bb) TeaValue::Uint(aa $op bb)
//}, },
_ => panic!("Invalid types"), _ => panic!("Invalid types"),
} }
} }
} }
type Scope = HashMap<String, VarType>; pub type Scope = HashMap<String, VarType>;
fn interpret_block(ast: Vec<ASTNode>, scopes: &mut Vec<Scope>, block_type: BlockType) -> Result<Vec<Value>, InterpreterError> { fn interpret_block(ast: Vec<ASTNode>, scopes: &mut Vec<Scope>, block_type: BlockType) -> Result<Vec<Value>, InterpreterError> {
//println!("{ast:?}\n");
let mut ast = ast.clone(); let mut ast = ast.clone();
ast.reverse(); ast.reverse();
let mut stack = vec![]; let mut stack = vec![];
//let mut scope = Scope::new();
loop { loop {
match ast.pop() { match ast.pop() {
None => return Ok(stack), None => return Ok(stack),
Some(ASTNode::ArgColl(count)) => {
let start = stack.len() - count;
if stack.len() > count {
let call_stack: Vec<Value> = stack.drain(start..).collect();
match stack.pop() {
None => return Err(InterpreterError::new("invalid function call")),
Some(Value::KwFn(KeywordFunction::Print)) => {
let mut iter = call_stack.into_iter();
if let Some(i) = iter.next() {
let val = get_value(i, scopes)?;
print!("{val}");
}
for i in iter {
let val = get_value(i, scopes)?;
print!(", {val}");
}
println!();
}
Some(Value::Lit(_)) => return Err(InterpreterError::new("cannot call literal")),
Some(Value::Id(_)) => return Err(InterpreterError::new("not yet implemented")),
}
} else {
return Err(InterpreterError::new("invalid call"));
}
},
Some(ASTNode::Block(tokens)) => { Some(ASTNode::Block(tokens)) => {
scopes.push(HashMap::new()); scopes.push(HashMap::new());
let mut result = interpret_block(tokens, scopes, BlockType::Block)?; let mut result = interpret_block(tokens, scopes, BlockType::Block)?;
@@ -125,7 +151,6 @@ fn interpret_block(ast: Vec<ASTNode>, scopes: &mut Vec<Scope>, block_type: Block
let rhs = some_or_error!(stack.pop(), InterpreterError::new("rhs value needed")); let rhs = some_or_error!(stack.pop(), InterpreterError::new("rhs value needed"));
let lhs = some_or_error!(stack.pop(), InterpreterError::new("lhs value needed")); let lhs = some_or_error!(stack.pop(), InterpreterError::new("lhs value needed"));
let rhs: TeaValue = get_value(rhs, scopes)?; let rhs: TeaValue = get_value(rhs, scopes)?;
//let current_scope = .unwrap();
match lhs { match lhs {
Value::Lit(_) => return Err(InterpreterError::new("invalid left hand side")), Value::Lit(_) => return Err(InterpreterError::new("invalid left hand side")),
Value::Id(s) => { Value::Id(s) => {
@@ -135,6 +160,9 @@ fn interpret_block(ast: Vec<ASTNode>, scopes: &mut Vec<Scope>, block_type: Block
scopes.last_mut().unwrap().insert(s, VarType::Const(rhs)); scopes.last_mut().unwrap().insert(s, VarType::Const(rhs));
} }
} }
Value::KwFn(s) => {
return Err(InterpreterError::new(format!("unable to assign to keyword {s:?}").as_str()))
}
} }
}, },
Some(ASTNode::StatementEnd) => { Some(ASTNode::StatementEnd) => {
-10
View File
@@ -1,10 +0,0 @@
use crate::parse::ASTNode;
fn get_domain(ast: &mut Vec<ASTNode>) -> Vec<ASTNode> {
ast.iter().take_while(|x| !matches!(x, ASTNode::StatementEnd)).cloned().collect()
}
pub fn operator_chain(ast: &mut Vec<ASTNode>) {
let tree = get_domain(ast);
}
+47
View File
@@ -26,6 +26,18 @@ pub enum TeaValue {
String(String), String(String),
} }
use std::fmt;
impl fmt::Display for TeaValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TeaValue::Int(i) => write!(f, "{i}"),
TeaValue::Uint(u) => write!(f, "{u}u"),
TeaValue::Float(l) => write!(f, "{l}"),
TeaValue::String(s) => write!(f, "{s}"),
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub enum Token { pub enum Token {
Lit(TeaValue), Lit(TeaValue),
@@ -52,6 +64,33 @@ fn skip_whitespace(input: &str) -> &str {
&input[whitespace.len()..] &input[whitespace.len()..]
} }
fn skip_singleline_comment(input: &str) -> Option<usize> {
if input.get(0..2) != Some("//") {
return None;
}
let mut index = 2;
loop {
if matches!(input.chars().nth(index), None | Some('\n')) {
return Some(index);
}
index += 1;
}
}
fn skip_multiline_comment(input: &str) -> Option<usize> {
if input.get(0..2) != Some("/*") {
return None;
}
let mut index = 2;
loop {
let s = input.get(index..(index + 2))?;
if s == "*/" {
return Some(index + 2);
}
index += 1;
}
}
macro_rules! try_parse { macro_rules! try_parse {
($e:expr, $arr:ident, $str:ident) => { ($e:expr, $arr:ident, $str:ident) => {
if let Some((v, s)) = $e { if let Some((v, s)) = $e {
@@ -69,6 +108,14 @@ pub fn lex_tokens(s: &str) -> Result<Vec<Token>, (Vec<Token>, &str)> {
if input.is_empty() { if input.is_empty() {
return Ok(tokens); return Ok(tokens);
} }
if let Some(count) = skip_singleline_comment(input) {
input = &input[count..];
continue;
}
if let Some(count) = skip_multiline_comment(input) {
input = &input[count..];
continue;
}
try_parse!(parse_literal(input), tokens, input); try_parse!(parse_literal(input), tokens, input);
try_parse!(parse_operator(input), tokens, input); try_parse!(parse_operator(input), tokens, input);
try_parse!(parse_symbol(input), tokens, input); try_parse!(parse_symbol(input), tokens, input);
+2
View File
@@ -4,6 +4,7 @@ use super::{ whitespace_chars, Token };
pub enum Keyword { pub enum Keyword {
Let, Let,
Fn, Fn,
Print, // print is a keyword for... reasons
} }
macro_rules! match_keyword { macro_rules! match_keyword {
@@ -17,5 +18,6 @@ macro_rules! match_keyword {
pub fn parse_keyword(input: &str) -> Option<(Token, &str)> { pub fn parse_keyword(input: &str) -> Option<(Token, &str)> {
match_keyword!("let", Token::Kw(Keyword::Let), input); match_keyword!("let", Token::Kw(Keyword::Let), input);
match_keyword!("fn", Token::Kw(Keyword::Fn), input); match_keyword!("fn", Token::Kw(Keyword::Fn), input);
match_keyword!("print", Token::Kw(Keyword::Print), input);
None None
} }
+45
View File
@@ -86,16 +86,61 @@ fn parse_dec_int(input: &str) -> Option<(i32, &str)> {
Some((sign * s.parse::<i32>().unwrap(), &input[s.len()..])) Some((sign * s.parse::<i32>().unwrap(), &input[s.len()..]))
} }
fn parse_uint(input: &str) -> Option<(u32, &str)> {
if input.starts_with("-") {
return None;
}
input.strip_prefix("0x").map(|input| -> String {
input.chars().take_while(|c| matches!(c, hex_chars!())).collect()
}).filter(|s| !s.is_empty() && input.chars().nth(s.len()) == Some('u')).map(|s|
(u32::from_str_radix(&s, 16).unwrap(), &input[(s.len() + 1)..])
).or({
let s: String = input.chars().take_while(|c| matches!(c, dec_chars!())).collect();
if s.is_empty() || input.chars().nth(s.len()) != Some('u') {
None
} else {
Some ((s.parse::<u32>().unwrap(), &input[(s.len() + 1)..]))
}
})
}
fn parse_str(input: &str) -> Option<(String, &str)> {
let x = input.chars().next()?;
if x != '"' {
return None;
}
let mut index = 1;
let mut terminated = false;
while index < input.len() {
// this is definitely stupid but idk how to do this properly
if Some('"') == input.chars().nth(index) && Some('\'') != input.chars().nth(index - 1) {
terminated = true;
break;
}
index += 1;
}
if !terminated {
return None;
}
Some((input.get(1..index).unwrap().to_owned().replace("\\n", "\n").replace("\\r", "\r").replace("\\0", "\0"), &input[(index + 1)..]))
}
pub fn parse_literal(input: &str) -> Option<(Token, &str)> { pub fn parse_literal(input: &str) -> Option<(Token, &str)> {
if let Some((val, s)) = parse_float(input) { if let Some((val, s)) = parse_float(input) {
return Some((Token::Lit(TeaValue::Float(val)), s)) return Some((Token::Lit(TeaValue::Float(val)), s))
} }
if let Some((val, s)) = parse_uint(input) {
return Some((Token::Lit(TeaValue::Uint(val)), s))
}
if let Some((val, s)) = parse_hex_int(input) { if let Some((val, s)) = parse_hex_int(input) {
return Some((Token::Lit(TeaValue::Int(val)), s)) return Some((Token::Lit(TeaValue::Int(val)), s))
} }
if let Some((val, s)) = parse_dec_int(input) { if let Some((val, s)) = parse_dec_int(input) {
return Some((Token::Lit(TeaValue::Int(val)), s)) return Some((Token::Lit(TeaValue::Int(val)), s))
} }
if let Some((val, s)) = parse_str(input) {
return Some((Token::Lit(TeaValue::String(val)), s))
}
// TODO: other values // TODO: other values
None None
} }
+6 -4
View File
@@ -8,17 +8,19 @@ pub enum Symbol {
CloseBrace, CloseBrace,
OpenParen, OpenParen,
CloseParen, CloseParen,
Comma,
} }
pub fn parse_symbol(input: &str) -> Option<(Token, &str)> { pub fn parse_symbol(input: &str) -> Option<(Token, &str)> {
let c: char = input.chars().next()?; let c: char = input.chars().next()?;
let input = &input[1..]; let input = &input[1..];
match c { match c {
'=' => Some((Token::Sym(Symbol::EqualSign), input)), '=' => Some((Token::Sym(Symbol::EqualSign), input)),
';' => Some((Token::Sym(Symbol::Semicolon), input)), ';' => Some((Token::Sym(Symbol::Semicolon), input)),
'{' => Some((Token::Sym(Symbol::OpenBrace), input)), ',' => Some((Token::Sym(Symbol::Comma), input)),
'{' => Some((Token::Sym(Symbol::OpenBrace), input)),
'}' => Some((Token::Sym(Symbol::CloseBrace), input)), '}' => Some((Token::Sym(Symbol::CloseBrace), input)),
'(' => Some((Token::Sym(Symbol::OpenParen), input)), '(' => Some((Token::Sym(Symbol::OpenParen), input)),
')' => Some((Token::Sym(Symbol::CloseParen), input)), ')' => Some((Token::Sym(Symbol::CloseParen), input)),
_ => None _ => None
} }
+14 -4
View File
@@ -10,9 +10,19 @@ mod interpret;
use interpret::interpret; use interpret::interpret;
fn main() { fn main() {
println!("\n"); // makes output easier to visually parse when run with cargo run
let args: String = args().skip(1).collect::<Vec<_>>().join(" "); let args: String = args().skip(1).collect::<Vec<_>>().join(" ");
let k = lex_tokens(&args).unwrap(); let k = match lex_tokens(&args) {
let p = parse(k).unwrap(); Ok(a) => a,
let i = interpret(p); Err((_, a)) => panic!("{a:?}"),
println!("{i:?}"); };
let p = match parse(k) {
Ok(a) => a,
Err(a) => panic!("{:?}", a.description),
};
let stack = match interpret(p) {
Ok(a) => a,
Err(a) => panic!("{:?}", a.description),
};
println!("\nreturn: {stack:?}\n");
} }
+96 -58
View File
@@ -11,6 +11,12 @@ use crate::lex::{
pub enum Value { pub enum Value {
Lit(TeaValue), Lit(TeaValue),
Id(String), Id(String),
KwFn(KeywordFunction),
}
#[derive(Debug, Clone)]
pub enum KeywordFunction {
Print,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -22,11 +28,12 @@ pub enum ASTNode {
StatementEnd, StatementEnd,
Block(Vec<ASTNode>), Block(Vec<ASTNode>),
Parenthetical(Vec<ASTNode>), Parenthetical(Vec<ASTNode>),
ArgColl(usize),
} }
#[derive(Debug)] #[derive(Debug)]
pub struct ParseError { pub struct ParseError {
description: String, pub description: String,
} }
impl ParseError { impl ParseError {
@@ -37,6 +44,11 @@ impl ParseError {
} }
} }
pub enum ExprType {
Block,
Arg,
}
macro_rules! match_or_return { macro_rules! match_or_return {
($matchee:expr, $pattern:pat, $extract:expr, $ret:expr) => { ($matchee:expr, $pattern:pat, $extract:expr, $ret:expr) => {
match $matchee { match $matchee {
@@ -46,15 +58,6 @@ macro_rules! match_or_return {
} }
} }
/*macro_rules! assert_match {
($matchee:expr, $pattern:pat, $ret:expr) => {
match $matchee {
Some($pattern) => (),
_ => return Err($ret),
}
}
}*/
fn next_expr_end(tokens: &[Token]) -> usize { fn next_expr_end(tokens: &[Token]) -> usize {
let mut index = 0; let mut index = 0;
let mut block_count = 0usize; let mut block_count = 0usize;
@@ -100,7 +103,51 @@ fn next_paren_end(tokens: &[Token]) -> Result<usize, ParseError> {
} }
} }
fn parse_expr(tokens: &[Token]) -> Result<(Vec<ASTNode>, usize), ParseError> { fn next_arg_end(tokens: &[Token]) -> Result<(bool, usize), ParseError> {
let mut index = 0;
let mut paren_count = 0usize;
let mut block_count = 0usize;
loop {
match tokens.get(index) {
Some(Token::Sym(Symbol::OpenParen)) => paren_count += 1,
Some(Token::Sym(Symbol::CloseParen)) => paren_count -= 1,
Some(Token::Sym(Symbol::OpenBrace)) => block_count += 1,
Some(Token::Sym(Symbol::CloseBrace)) => block_count -= 1,
Some(Token::Sym(Symbol::Comma)) if paren_count == 0 && block_count == 0 => return Ok((false, index)),
Some(Token::Sym(Symbol::Semicolon)) if paren_count == 0 && block_count == 0 => return Ok((true, index)),
None => {
if paren_count > 0 || block_count > 0 {
return Err(ParseError::new("mismatched ( or {"));
}
return Ok((true, index))
}
_ => (),
}
index += 1;
}
}
macro_rules! check_if_call {
($tree:ident, $tkn:expr, $i:ident, $o:ident) => {
if matches!($tree.last(), Some(ASTNode::Val(_))) && $o.is_empty() {
let mut arg_count = 0;
loop {
let (finished, count) = next_arg_end(&$tkn[$i..])?;
let (mut stack, _) = parse_expr(&$tkn[$i..($i + count)], ExprType::Arg)?;
$tree.append(&mut stack);
$i += count + 1;
arg_count += 1;
if finished {
break;
}
}
$tree.push(ASTNode::ArgColl(arg_count));
continue;
}
}
}
fn parse_expr(tokens: &[Token], expr_type: ExprType) -> Result<(Vec<ASTNode>, usize), ParseError> {
let mut ast = vec![]; let mut ast = vec![];
let mut index = 0; let mut index = 0;
let mut op_stack: Vec<Operator> = vec![]; let mut op_stack: Vec<Operator> = vec![];
@@ -108,43 +155,47 @@ fn parse_expr(tokens: &[Token]) -> Result<(Vec<ASTNode>, usize), ParseError> {
loop { loop {
match tokens.get(index) { match tokens.get(index) {
None => { None => {
while !op_stack.is_empty() {
ast.push(ASTNode::BinOp(op_stack.pop().unwrap()));
}
if unary_negate && !matches!(tokens.get(index), Some(Token::Sym(Symbol::Semicolon))) { if unary_negate && !matches!(tokens.get(index), Some(Token::Sym(Symbol::Semicolon))) {
ast.push(ASTNode::UnaryMinus); ast.push(ASTNode::UnaryMinus);
} }
while !op_stack.is_empty() {
ast.push(ASTNode::BinOp(op_stack.pop().unwrap()));
}
return Ok((ast, index)); return Ok((ast, index));
}, },
Some(Token::Lit(v)) => ast.push(ASTNode::Val(Value::Lit(v.clone()))), Some(Token::Lit(v)) => {
Some(Token::Id(v)) => ast.push(ASTNode::Val(Value::Id(v.clone()))), check_if_call!(ast, tokens, index, op_stack);
ast.push(ASTNode::Val(Value::Lit(v.clone())));
},
Some(Token::Id(v)) => {
check_if_call!(ast, tokens, index, op_stack);
ast.push(ASTNode::Val(Value::Id(v.clone())));
},
Some(Token::Kw(Keyword::Let)) => { Some(Token::Kw(Keyword::Let)) => {
if matches!(expr_type, ExprType::Arg) {
return Err(ParseError::new("let not allowed in argument list"));
}
let id = match_or_return!(tokens.get(index + 1), Token::Id(a), a, ParseError::new("identifier expected after 'let' keyword")); let id = match_or_return!(tokens.get(index + 1), Token::Id(a), a, ParseError::new("identifier expected after 'let' keyword"));
match_or_return!(tokens.get(index + 2), Token::Sym(Symbol::EqualSign), (), ParseError::new("= expected after identifier")); match_or_return!(tokens.get(index + 2), Token::Sym(Symbol::EqualSign), (), ParseError::new("= expected after identifier"));
let stokens = &tokens[(3 + index)..]; let stokens = &tokens[(3 + index)..];
let next_end = next_expr_end(stokens); let next_end = next_expr_end(stokens);
let (mut expr, count) = parse_expr(&stokens[..next_end])?; let (mut expr, count) = parse_expr(&stokens[..next_end], ExprType::Block)?;
ast.push(ASTNode::Val(Value::Id(id.to_owned()))); ast.push(ASTNode::Val(Value::Id(id.to_owned())));
ast.append(&mut expr); ast.append(&mut expr);
ast.push(ASTNode::ConstDecl); ast.push(ASTNode::ConstDecl);
index += count + 2;// 4 + count - 1 index += count + 2;
}, },
Some(Token::Kw(Keyword::Fn)) => { Some(Token::Kw(Keyword::Fn)) => {
/*let id = match_or_return!(tokens.get(index + 1), Token::Id(a), a, ParseError::new("identifier expected after 'let' keyword"));
match_or_return!(tokens.get(index + 2), Token::Sym(Symbol::EqualSign), (), ParseError::new("= expected after identifier"));
let tokens = &tokens[(3 + index)..];
let next_end = next_expr_end(tokens);
let (mut expr, count) = parse_expr(&tokens[..next_end])?;
ast.push(ASTNode::Val(Value::Id(id.to_owned())));
ast.append(&mut expr);
ast.push(ASTNode::ConstDecl);
index += count + 3; // 4 + count - 1*/
todo!() todo!()
}, },
Some(Token::Kw(Keyword::Print)) => {
ast.push(ASTNode::Val(Value::KwFn(KeywordFunction::Print)));
},
Some(Token::Sym(Symbol::EqualSign)) => todo!(), Some(Token::Sym(Symbol::EqualSign)) => todo!(),
Some(Token::Sym(Symbol::OpenBrace)) => { Some(Token::Sym(Symbol::OpenBrace)) => {
check_if_call!(ast, tokens, index, op_stack);
let end = next_block_end(&tokens[(index + 1)..])?; let end = next_block_end(&tokens[(index + 1)..])?;
let (expr, count) = parse_expr(&tokens[(index + 1)..(index + end + 1)])?; let (expr, count) = parse_expr(&tokens[(index + 1)..(index + end + 1)], ExprType::Block)?;
ast.push(ASTNode::Block(expr)); ast.push(ASTNode::Block(expr));
index += count + 1; index += count + 1;
}, },
@@ -152,8 +203,9 @@ fn parse_expr(tokens: &[Token]) -> Result<(Vec<ASTNode>, usize), ParseError> {
return Err(ParseError::new("unexpected }")); return Err(ParseError::new("unexpected }"));
}, },
Some(Token::Sym(Symbol::OpenParen)) => { Some(Token::Sym(Symbol::OpenParen)) => {
check_if_call!(ast, tokens, index, op_stack);
let end = next_paren_end(&tokens[(index + 1)..])?; let end = next_paren_end(&tokens[(index + 1)..])?;
let (expr, count) = parse_expr(&tokens[(index + 1)..(index + end + 1)])?; let (expr, count) = parse_expr(&tokens[(index + 1)..(index + end + 1)], ExprType::Block)?;
ast.push(ASTNode::Parenthetical(expr)); ast.push(ASTNode::Parenthetical(expr));
index += count + 1; index += count + 1;
}, },
@@ -161,56 +213,42 @@ fn parse_expr(tokens: &[Token]) -> Result<(Vec<ASTNode>, usize), ParseError> {
return Err(ParseError::new("unexpected )")); return Err(ParseError::new("unexpected )"));
}, },
Some(Token::Sym(Symbol::Semicolon)) => { Some(Token::Sym(Symbol::Semicolon)) => {
if matches!(expr_type, ExprType::Arg) {
return Err(ParseError::new("semicolon within argument"));
}
if unary_negate && !matches!(tokens.get(index), Some(Token::Sym(Symbol::Semicolon))) {
ast.push(ASTNode::UnaryMinus);
unary_negate = false;
}
while !op_stack.is_empty() { while !op_stack.is_empty() {
ast.push(ASTNode::BinOp(op_stack.pop().unwrap())); ast.push(ASTNode::BinOp(op_stack.pop().unwrap()));
} }
// I'll macro this later // I'll macro this later
if unary_negate && !matches!(tokens.get(index), Some(Token::Sym(Symbol::Semicolon))) {
ast.push(ASTNode::UnaryMinus);
unary_negate = false;
}
ast.push(ASTNode::StatementEnd); ast.push(ASTNode::StatementEnd);
}, },
Some(Token::Sym(Symbol::Comma)) => {
if matches!(expr_type, ExprType::Arg) {
return Err(ParseError::new("comma within argument"));
}
},
Some(Token::Op(op)) => { Some(Token::Op(op)) => {
if (matches!(ast.last(), Some(ASTNode::StatementEnd) | None) || !op_stack.is_empty()) && matches!(op, Operator::Minus) { if (ast.is_empty() || matches!(tokens.get(index - 1), Some(Token::Sym(Symbol::Semicolon) | Token::Op(_)))) && matches!(op, Operator::Minus) {
unary_negate = true; unary_negate = true;
index += 1; index += 1;
continue; continue;
} }
if unary_negate && !matches!(tokens.get(index), Some(Token::Sym(Symbol::Semicolon))) {
ast.push(ASTNode::UnaryMinus);
unary_negate = false;
}
match op_stack.last() { match op_stack.last() {
None => op_stack.push(op.clone()), None => op_stack.push(op.clone()),
Some(last_op) if last_op.get_precedence() < op.get_precedence() => { Some(last_op) if last_op.get_precedence() < op.get_precedence() => {
op_stack.push(op.clone()) op_stack.push(op.clone())
}, },
Some(_) => { Some(_) => {
while op_stack.last().is_some_and(|l| l.get_precedence() < op.get_precedence()) { while op_stack.last().is_some_and(|l| l.get_precedence() > op.get_precedence()) {
ast.push(ASTNode::BinOp(op_stack.pop().unwrap())); ast.push(ASTNode::BinOp(op_stack.pop().unwrap()));
} }
op_stack.push(op.clone()) op_stack.push(op.clone())
} }
} }
/*if ast.is_empty() && matches!(op, Operator::Minus) {
ast.push(match tokens.get(index + 1) {
Some(Token::Id(v)) => ASTNode::Val(Value::Id(v.clone())),
Some(Token::Lit(v)) => ASTNode::Val(Value::Lit(v.clone())),
_ => return Err(ParseError::new("literal or identifier expected after operator")),
});
ast.push(ASTNode::UnaryMinus);
index += 1;
} else {
assert_match!(ast.last(), ASTNode::Val(_), ParseError::new("literal or identifier expected before operator"));
ast.push(match tokens.get(index + 1) {
Some(Token::Id(v)) => ASTNode::Val(Value::Id(v.clone())),
Some(Token::Lit(v)) => ASTNode::Val(Value::Lit(v.clone())),
_ => return Err(ParseError::new("literal or identifier expected after operator")),
});
ast.push(ASTNode::BinOp(op.clone()));
index += 1;
}*/
}, },
} }
@@ -223,7 +261,7 @@ pub fn parse(tokens: Vec<Token>) -> Result<Vec<ASTNode>, ParseError> {
let tokens: &[Token] = &tokens; let tokens: &[Token] = &tokens;
let mut next = 0; let mut next = 0;
loop { loop {
let (mut nodes, count) = parse_expr(&tokens[next..])?; let (mut nodes, count) = parse_expr(&tokens[next..], ExprType::Block)?;
ast.append(&mut nodes); ast.append(&mut nodes);
next += count; next += count;
if tokens.get(next).is_none() { if tokens.get(next).is_none() {
+1 -1
View File
@@ -1,2 +1,2 @@
(5 + 6) * 4 print "Hello, world!";