diff --git a/src/main.rs b/src/main.rs index 8add9f8..7b0746a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +#![feature(let_chains)] mod err; mod ir; mod lookahead; @@ -58,7 +59,7 @@ fn test_expression(expr: &str) { let source = expr.to_string(); let tokens = Tokenizer::new(source.chars()).filter(|t| t.0.is_meaningful()); let mut parser = Parser::new(tokens); - println!("{:?}", parser.expression(0).unwrap()); + println!("{:#?}", parser.expression(0).unwrap()); } fn tokenize(input: &'static str) -> impl Iterator { @@ -78,7 +79,6 @@ fn main() -> Result<()> { */ //let module = frontend::Module::from_file("./demo.hal")?; //module.write_to("test"); - let p = punycode::encode("-_").unwrap(); - println!("{p}"); + test_expression("asdf~+ + 3"); Ok(()) } diff --git a/src/parse/expression.rs b/src/parse/expression.rs index 76da257..8652fd9 100644 --- a/src/parse/expression.rs +++ b/src/parse/expression.rs @@ -105,27 +105,27 @@ impl std::fmt::Display for ExpressionKind { right, } => { write!(f, "({left} {token} {right})") - } + }, e::Parenthesis(inner) => write!(f, "{inner}"), e::Unary { op: token, child } => { write!(f, "({token} {child})") - } + }, e::Identifier { name, .. } => write!(f, "{name}"), e::FunctionCall { callee, args, .. } => { write!(f, "({callee} call {args:?})") - } + }, e::Field { namespace, field, .. } => { write!(f, "({namespace} . {field})") - } + }, e::FunctionDef { params, returns_str, .. } => { write!(f, "(fn({params:?}) -> {returns_str:?})") - } + }, e::StructDef(params) => write!(f, "struct {{ {params:?} }}"), e::StructLiteral { name, args, .. } => write!(f, "{name} {{ {args:?} }}"), e::Block(block) => { @@ -134,7 +134,7 @@ impl std::fmt::Display for ExpressionKind { write!(f, "{:#?}", s)?; } write!(f, "}}") - } + }, e::If { block, else_, .. } => { write!(f, "{{\n")?; for s in block { @@ -145,7 +145,7 @@ impl std::fmt::Display for ExpressionKind { write!(f, "{else_}")?; } Ok(()) - } + }, } } } @@ -163,13 +163,13 @@ impl> Parser { let next = self.peek(0)?; // Unary prefix expression let mut current = if let Ok(operator) = UnaryOp::try_from(&next.0) { - self.skip(1); let span = next.1; if operator.assoc() == RIGHT_ASSOC { return error() .reason(format!("The {} operator must come after a value", operator)) .span(&span); } + self.skip(1); let child = self .expression(operator.precedence()) .trace(format!("while parsing unary {}", operator)) @@ -190,29 +190,62 @@ impl> Parser { // Precedence climbing loop while let Ok(next) = self.peek(0) { - // Binary infix + // Binary or mixed if let Ok(operator) = BinaryOp::try_from(&next.0) { - let new_precedence = operator.precedence(); - if ((operator.assoc() == LEFT_ASSOC) && new_precedence <= precedence) - || (new_precedence < precedence) - { - return Ok(current); + 'binop: { + // Operator may be postfix unary + if let Ok(operator) = UnaryOp::try_from(&next.0) + && operator.assoc() == RIGHT_ASSOC + { + let is_unop = if let Err(_) = self.peek(1) { + true // Reached past end of input, must be unop + } else if let Ok(Token(t::EOF, _)) = self.peek(1) { + true // Reached end of input, must be unop + } else if let Ok(next2) = self.peek(1) + && (BinaryOp::try_from(&next2.0).is_ok() + || UnaryOp::try_from(&next2.0) + .is_ok_and(|u| u.assoc() == RIGHT_ASSOC)) + { + true // Next is op + } else { + false + }; + // Op is postfix unary + if is_unop { + let span = next.1; + self.skip(1); + current = Expression::new( + e::Unary { + op: operator, + child: current.into(), + }, + span, + ); + break 'binop; + } + } + let new_precedence = operator.precedence(); + if ((operator.assoc() == LEFT_ASSOC) && new_precedence <= precedence) + || (new_precedence < precedence) + { + return Ok(current); + } + self.skip(1); + let span = next.1; + let rhs = self + .expression(new_precedence) + .trace(format!("while parsing binary {}", operator)) + .span(&span)?; + let span = next.1 + rhs.span; + current = Expression::new( + e::Binary { + op: operator, + left: current.into(), + right: rhs.into(), + }, + span, + ); } - self.skip(1); - let span = next.1; - let rhs = self - .expression(new_precedence) - .trace(format!("while parsing binary {}", operator)) - .span(&span)?; - let span = next.1 + rhs.span; - current = Expression::new( - e::Binary { - op: operator, - left: current.into(), - right: rhs.into(), - }, - span, - ); } // Field else if let Token(t::Dot, span) = next { @@ -243,7 +276,7 @@ impl> Parser { Ok(a) => { span = span + a.span; args.push(a) - } + }, Err(_) => break, }; if !self.eat(t::Comma).is_ok() { @@ -263,7 +296,7 @@ impl> Parser { else if let Ok(operator) = UnaryOp::try_from(&next.0) { self.skip(1); let span = next.1; - if operator.assoc() == RIGHT_ASSOC { + if operator.assoc() == LEFT_ASSOC { return error() .reason(format!( "The {} operator must come before a value", @@ -302,9 +335,10 @@ impl> Parser { names.push(name.clone()); // Param type (optional) if self.eat(t::Colon).is_ok() { - let (type_name, span2) = self - .identifier() - .trace_span(span + span2, format!("While parsing type of '{}'", name))?; + let (type_name, span2) = self.identifier().trace_span( + span + span2, + format!("While parsing type of '{}'", name), + )?; strongly_typed = true; span = span + span2; type_names.push(type_name); @@ -358,37 +392,38 @@ impl> Parser { t::IntegerLiteral(i, b) => { self.skip(1); e::Immediate(im::Integer(i, b)) - } + }, t::FloatLiteral(f) => { self.skip(1); e::Immediate(im::Real(f)) - } + }, t::StringLiteral(s) => { self.skip(1); e::Immediate(im::String(s)) - } + }, t::GlyphLiteral(c) => { self.skip(1); e::Immediate(im::Glyph(c)) - } + }, t::True => { self.skip(1); e::Immediate(im::Boolean(true)) - } + }, t::False => { self.skip(1); e::Immediate(im::Boolean(false)) - } + }, t::If => return self.if_else(), t::LeftBrace => { let (block, span1) = self.block()?; span = span + span1; e::Block(block) - } + }, // Function definition t::LeftParen if (self.look(1, t::Identifier("".into())).is_ok() - && (self.look(2, t::Colon).is_ok() || self.look(2, t::Comma).is_ok())) + && (self.look(2, t::Colon).is_ok() + || self.look(2, t::Comma).is_ok())) || self.look(1, t::RightParen).is_ok() => { self.skip(1); @@ -417,7 +452,7 @@ impl> Parser { returns_str, body, } - } + }, // Struct definition t::Struct => { self.skip(1); @@ -425,7 +460,7 @@ impl> Parser { let params = self.parameters(span)?; self.eat(t::RightBrace)?; e::StructDef(params) - } + }, // Struct literal t::Identifier(name) if self.look(1, t::LeftBrace).is_ok() => { self.skip(2); @@ -450,11 +485,11 @@ impl> Parser { } self.eat(t::RightBrace)?; e::StructLiteral { name, args } - } + }, t::Identifier(i) => { self.skip(1); e::Identifier { name: i } - } + }, // Parenthetical t::LeftParen => { self.skip(1); @@ -466,12 +501,12 @@ impl> Parser { .reason("Unclosed '('") .span(&expr.span)?; e::Parenthesis(expr.into()) - } + }, _ => { return error() .span(&span) .reason(format!("Expected expression, found {}", next.0)); - } + }, }; Ok(Expression::new(kind, span)) } @@ -512,7 +547,7 @@ impl> Parser { Ok(Token(t::Identifier(i), span)) => { self.skip(1); Ok((i, span)) - } + }, Ok(t) => error() .reason(format!("Expected identifier, found {}", t.0)) .span(&t.1), diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 0ee6a47..cf79289 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -74,13 +74,16 @@ op! { op! { UnaryOp; - Bang, 12, RIGHT_ASSOC; - Question, 12, RIGHT_ASSOC; + Plus, 11, RIGHT_ASSOC; + Tilda, 11, RIGHT_ASSOC; Minus, 11, LEFT_ASSOC; - Plus, 11, LEFT_ASSOC; Not, 11, LEFT_ASSOC; } +pub fn is_mixed_op(t: &TokenKind) -> bool { + BinaryOp::try_from(t).is_ok() && UnaryOp::try_from(t).is_ok() +} + const FIELD_PREC: Precedence = 13; const CALL_PREC: Precedence = 12; diff --git a/src/semantic/analyzer.rs b/src/semantic/analyzer.rs index f530e93..34e2609 100644 --- a/src/semantic/analyzer.rs +++ b/src/semantic/analyzer.rs @@ -1,6 +1,8 @@ use std::collections::HashMap; -use crate::{err::*, Expression, ExpressionKind, Statement, StatementKind}; +use crate::{ + Expression, ExpressionKind, Span, Statement, StatementKind, err::*, +}; use super::*; use ir::*; @@ -9,8 +11,7 @@ use ir::*; enum Undo { FuncGuard, BlockGuard, - Symbol { name: String, prev: Vec }, - Push { name: String }, + Symbol { name: String, prev: Symbol }, None, } @@ -23,28 +24,49 @@ struct Symbol { #[derive(Debug, Clone)] struct SymbolTable { - types: HashMap, - table: HashMap>, + type_table: Vec, + sym_table: HashMap, undo_stack: Vec, path: Vec, salt: usize, + depth: usize, } impl SymbolTable { - fn define(&mut self, name: &str, type_: TID, life: Lifetime) { - if !self.table.contains_key(name) { - self.table.insert(name.to_string(), vec![]); - } - let symbols = self.table.get_mut(name).unwrap(); + fn define_symbol(&mut self, name: &str, type_: TID) { let mut path = self.path.clone(); path.push(name.to_string()); - let mangle = names::mangle(path, &format!("{:#x}", self.salt)); + let undo = match self.sym_table.get(name) { + Some(prev) => Undo::Symbol { + name: name.to_string(), + prev: prev.clone(), + }, + None => Undo::None, + }; + self.undo_stack.push(undo); + let mangle = names::mangle(path, &self.salt.to_string()); let symbol = Symbol { mangle, type_, - life, + life: if self.depth == 0 { + Lifetime::Static + } else { + Lifetime::Dynamic + }, }; - let undo = if + self.sym_table.insert(name.to_string(), symbol); + } + + fn query_symbol(&mut self, name: &str) -> Result<&Symbol> { + self.sym_table.get(name).ok_or(Diagnostic::new( + format!("The name {name} is not declared in this scope",), + None, + )) + } + + fn define_type(&mut self, type_: Type) -> TID { + self.type_table.push(type_); + self.type_table.len() } } diff --git a/src/token.rs b/src/token.rs index 5280ea7..65e5507 100644 --- a/src/token.rs +++ b/src/token.rs @@ -30,6 +30,7 @@ pub enum TokenKind { Slash, Star, Percent, + Tilda, Arrow, FatArrow, PlusEqual, @@ -278,6 +279,7 @@ impl> Tokenizer { '&' => Ampersand, '^' => Carrot, '#' => Hash, + '~' => Tilda, '.' if not_next('.') => Dot, '+' if not_next('=') => Plus, '-' if not_next('=') && not_next('>') => Minus,