Added print statement, various other fixes
This commit is contained in:
@@ -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
@@ -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) => {
|
||||||
|
|||||||
@@ -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
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -1,2 +1,2 @@
|
|||||||
|
|
||||||
(5 + 6) * 4
|
print "Hello, world!";
|
||||||
|
|||||||
Reference in New Issue
Block a user