This commit is contained in:
Logan 2024-12-22 05:49:49 -06:00
parent c5eed8dcf8
commit fabb66148d
5 changed files with 130 additions and 68 deletions

View file

@ -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<Item = Token> {
@ -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(())
}

View file

@ -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<I: Iterator<Item = Token>> Parser<I> {
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<I: Iterator<Item = Token>> Parser<I> {
// 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<I: Iterator<Item = Token>> Parser<I> {
Ok(a) => {
span = span + a.span;
args.push(a)
}
},
Err(_) => break,
};
if !self.eat(t::Comma).is_ok() {
@ -263,7 +296,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
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<I: Iterator<Item = Token>> Parser<I> {
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<I: Iterator<Item = Token>> Parser<I> {
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<I: Iterator<Item = Token>> Parser<I> {
returns_str,
body,
}
}
},
// Struct definition
t::Struct => {
self.skip(1);
@ -425,7 +460,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
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<I: Iterator<Item = Token>> Parser<I> {
}
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<I: Iterator<Item = Token>> Parser<I> {
.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<I: Iterator<Item = Token>> Parser<I> {
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),

View file

@ -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;

View file

@ -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<Symbol> },
Push { name: String },
Symbol { name: String, prev: Symbol },
None,
}
@ -23,28 +24,49 @@ struct Symbol {
#[derive(Debug, Clone)]
struct SymbolTable {
types: HashMap<TID, Type>,
table: HashMap<String, Vec<Symbol>>,
type_table: Vec<Type>,
sym_table: HashMap<String, Symbol>,
undo_stack: Vec<Undo>,
path: Vec<String>,
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()
}
}

View file

@ -30,6 +30,7 @@ pub enum TokenKind {
Slash,
Star,
Percent,
Tilda,
Arrow,
FatArrow,
PlusEqual,
@ -278,6 +279,7 @@ impl<I: Iterator<Item = char>> Tokenizer<I> {
'&' => Ampersand,
'^' => Carrot,
'#' => Hash,
'~' => Tilda,
'.' if not_next('.') => Dot,
'+' if not_next('=') => Plus,
'-' if not_next('=') && not_next('>') => Minus,