initial structure impl

This commit is contained in:
2025-03-26 21:39:24 -04:00
parent 0614d48fcc
commit 021434d2f1
23 changed files with 390 additions and 84 deletions

View File

@@ -37,7 +37,10 @@ impl PType {
output: &mut CompilerOutput,
span: FileSpan,
) -> Type {
match namespace.get(&self.name).and_then(|ids| ids.ty) {
let Some(name) = self.name.as_ref() else {
return Type::Error;
};
match namespace.get(&name).and_then(|ids| ids.ty) {
Some(id) => {
if self.args.is_empty() {
Type::Concrete(id)
@@ -51,10 +54,10 @@ impl PType {
}
}
None => {
if let Ok(num) = self.name.parse::<u32>() {
if let Ok(num) = name.parse::<u32>() {
Type::Bits(num)
} else {
match self.name.as_str() {
match name.as_str() {
"slice" => {
let inner = self.args[0].lower(namespace, output);
Type::Slice(Box::new(inner))

View File

@@ -1,5 +1,8 @@
use super::{func::FnLowerCtx, FnLowerable, PExpr, UnaryOp};
use crate::ir::{DataDef, IRUInstruction, Origin, Type, VarInst};
use crate::{
ir::{DataDef, IRUInstruction, Origin, Type, VarInst},
parser::PInfixOp,
};
impl FnLowerable for PExpr {
type Output = VarInst;
@@ -13,7 +16,7 @@ impl FnLowerable for PExpr {
DataDef {
ty: Type::Bits(8).arr(data.len() as u32),
origin: Origin::File(l.span),
label: format!("string \"{}\"", s.replace("\n", "\\n"))
label: format!("string \"{}\"", s.replace("\n", "\\n")),
},
data,
);
@@ -42,7 +45,7 @@ impl FnLowerable for PExpr {
DataDef {
ty,
origin: Origin::File(l.span),
label: format!("num {n:?}")
label: format!("num {n:?}"),
},
n.whole.parse::<i64>().unwrap().to_le_bytes().to_vec(),
);
@@ -56,15 +59,39 @@ impl FnLowerable for PExpr {
PExpr::Ident(i) => ctx.get_var(i)?,
PExpr::BinaryOp(op, e1, e2) => {
let res1 = e1.lower(ctx)?;
let res2 = e2.lower(ctx)?;
op.traitt();
todo!();
if *op == PInfixOp::Access {
let sty = &ctx.map.get_var(res1.id).ty;
let Type::Concrete(tid) = sty else {
ctx.err(format!("Type {:?} has no fields", ctx.map.type_name(sty)));
return None;
};
let struc = ctx.map.get_struct(*tid);
let Some(box PExpr::Ident(ident)) = &e2.inner else {
ctx.err(format!("Field accesses must be identifiers",));
return None;
};
let fname = &ident.as_ref()?.0;
let Some(field) = struc.fields.get(fname) else {
ctx.err(format!("Field '{fname}' not in struct"));
return None;
};
let temp = ctx.temp(field.ty.clone());
ctx.push(IRUInstruction::Access {
dest: temp,
src: res1,
field: fname.to_string(),
});
temp
} else {
let res2 = e2.lower(ctx)?;
todo!()
}
}
PExpr::UnaryOp(op, e) => {
let res = e.lower(ctx)?;
match op {
UnaryOp::Ref => {
let temp = ctx.temp(ctx.map.get_var(res.id).ty.clone());
let temp = ctx.temp(ctx.map.get_var(res.id).ty.clone().rf());
ctx.push(IRUInstruction::Ref {
dest: temp,
src: res,
@@ -120,7 +147,7 @@ impl FnLowerable for PExpr {
temp
}
PExpr::Group(e) => e.lower(ctx)?,
PExpr::Construct(c) => todo!(),
PExpr::Construct(c) => c.lower(ctx)?,
})
}
}

View File

@@ -2,7 +2,7 @@ use super::{CompilerMsg, CompilerOutput, FileSpan, FnLowerable, Node, PFunction}
use crate::{
ir::{
FnDef, FnID, IRInstructions, IRUFunction, IRUInstruction, Idents, NamespaceGuard, Origin,
Type, VarDef, VarID, VarInst,
Type, VarDef, VarInst,
},
parser,
};

View File

@@ -1,10 +1,11 @@
mod arch;
mod asm;
mod block;
mod def;
mod expr;
mod func;
mod module;
mod arch;
mod struc;
use super::*;

View File

@@ -4,6 +4,9 @@ use super::{PModule, CompilerOutput};
impl PModule {
pub fn lower(&self, map: &mut NamespaceGuard, output: &mut CompilerOutput) {
for s in &self.structs {
s.lower(map, output);
}
let mut fns = Vec::new();
for f in &self.functions {
if let Some(id) = f.lower_header(map, output) {

View File

@@ -0,0 +1,96 @@
use std::collections::HashMap;
use crate::{
common::{CompilerMsg, CompilerOutput, FileSpan},
ir::{IRUInstruction, NamespaceGuard, Origin, StructDef, StructField, VarInst},
parser::{Node, PConstruct, PConstructFields, PStruct, PStructFields},
};
use super::{FnLowerCtx, FnLowerable};
impl FnLowerable for PConstruct {
type Output = VarInst;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<VarInst> {
let ty = self.name.lower(ctx.map, ctx.output);
let fields = match &self.fields {
PConstructFields::Named(nodes) => nodes
.iter()
.flat_map(|n| {
let def = n.as_ref()?;
let name = def.name.as_ref()?.to_string();
let expr = def.val.as_ref()?.lower(ctx)?;
Some((name, expr))
})
.collect(),
PConstructFields::Tuple(nodes) => nodes
.iter()
.enumerate()
.flat_map(|(i, n)| {
let expr = n.as_ref()?.lower(ctx)?;
Some((format!("{i}"), expr))
})
.collect(),
PConstructFields::None => HashMap::new(),
};
let id = ctx.temp(ty);
ctx.push(IRUInstruction::Construct { dest: id, fields });
Some(id)
}
}
impl PStruct {
pub fn lower(
&self,
map: &mut NamespaceGuard,
output: &mut CompilerOutput,
span: FileSpan,
) -> Option<()> {
let mut offset = 0;
let fields = match &self.fields {
PStructFields::Named(nodes) => nodes
.iter()
.flat_map(|n| {
let def = n.as_ref()?;
let name = def.name.as_ref()?.to_string();
let tynode = def.ty.as_ref()?;
let ty = tynode.lower(map, output);
let size = map.size_of_type(&ty).unwrap_or_else(|| {
output.err(CompilerMsg {
msg: format!("Size of type '{}' unknown", map.type_name(&ty)),
spans: vec![tynode.span],
});
0
});
let res = Some((name, StructField { ty, offset }));
offset += size;
res
})
.collect(),
PStructFields::Tuple(nodes) => nodes
.iter()
.enumerate()
.flat_map(|(i, n)| {
let ty = n.as_ref()?.lower(map, output, span);
let size = map.size_of_type(&ty)?;
let res = Some((format!("{i}"), StructField { ty, offset }));
offset += size;
res
})
.collect(),
PStructFields::None => HashMap::new(),
};
map.def_type(StructDef {
name: self.name.as_ref()?.to_string(),
origin: Origin::File(span),
size: offset,
fields,
});
Some(())
}
}
impl Node<PStruct> {
pub fn lower(&self, map: &mut NamespaceGuard, output: &mut CompilerOutput) {
self.as_ref().map(|i| i.lower(map, output, self.span));
}
}

View File

@@ -14,3 +14,6 @@ pub use node::*;
pub use nodes::*;
pub use parse::*;
pub use token::*;
// idea: create generic "map" and "tuple" types which are used for function calls, tuples, struct
// creation, etc. instead of specializing at the parsing level

View File

@@ -23,6 +23,12 @@ impl<T> Node<T> {
span: self.span,
}
}
pub fn map<T2, F: Fn(T) -> T2>(self, f: F) -> Node<T2> {
Node {
inner: self.inner.map(f),
span: self.span,
}
}
}
impl<T> Deref for Node<T> {

View File

@@ -1,8 +1,8 @@
use std::fmt::Debug;
use super::{
MaybeParsable, Node, PExpr, PIdent, PType, Parsable, ParseResult, ParserCtx, Symbol,
Token, CompilerMsg
CompilerMsg, MaybeParsable, Node, PExpr, PIdent, PType, Parsable, ParseResult, ParserCtx,
Symbol, Token,
};
pub struct PVarDef {
@@ -18,27 +18,30 @@ pub struct PFieldDef {
impl Parsable for PVarDef {
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
let name = ctx.parse()?;
if ctx.peek().is_some_and(|n| n.is_symbol(Symbol::Colon)) {
ParseResult::Ok(if ctx.peek().is_some_and(|n| n.is_symbol(Symbol::Colon)) {
ctx.next();
ctx.parse().map(|ty| Self { name, ty: Some(ty) })
Self {
name,
ty: Some(ctx.parse()?),
}
} else {
ParseResult::Ok(Self { name, ty: None })
}
Self { name, ty: None }
})
}
}
impl Parsable for PFieldDef {
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
let name = ctx.parse()?;
if ctx.peek().is_some_and(|n| n.is_symbol(Symbol::Colon)) {
ParseResult::Ok(if ctx.peek().is_some_and(|n| n.is_symbol(Symbol::Colon)) {
ctx.next();
ctx.parse().map(|ty| Self {
Self {
name,
val: Some(ty),
})
val: Some(ctx.parse()?),
}
} else {
ParseResult::Ok(Self { name, val: None })
}
Self { name, val: None }
})
}
}

View File

@@ -1,7 +1,10 @@
use std::fmt::{Debug, Write};
use super::{
op::{PInfixOp, UnaryOp}, util::parse_list, Keyword, Node, NodeParsable, PAsmBlock, PBlock, PConstruct, PIdent, PLiteral, Parsable, ParseResult, ParserCtx, Symbol, CompilerMsg
op::{PInfixOp, UnaryOp},
util::parse_list,
CompilerMsg, Keyword, Node, NodeParsable, PAsmBlock, PBlock, PConstruct, PIdent, PLiteral,
Parsable, ParseResult, ParserCtx, Symbol,
};
type BoxNode = Node<Box<PExpr>>;
@@ -53,12 +56,21 @@ impl Parsable for PExpr {
Self::UnaryOp(op, n)
}
});
} else if let Some(val) = Node::maybe_parse(ctx) {
} else if let Some(val) = ctx.maybe_parse() {
Self::Lit(val)
} else {
let res = ctx.parse();
if res.node.is_some() {
Self::Ident(res.node)
// TODO: this is extremely limiting
// maybe parse generically and then during lowering figure out what's a function vs
// struct vs etc like mentioned in main.rs
if let Some(next) = ctx.peek()
&& next.is_symbol(Symbol::OpenCurly)
{
Self::Construct(ctx.parse_with(res.node)?)
} else {
Self::Ident(res.node)
}
} else {
let next = ctx.expect_peek()?;
return ParseResult::Err(CompilerMsg::unexpected_token(next, "an expression"));

View File

@@ -5,7 +5,7 @@ use std::{
};
#[derive(Clone)]
pub struct PIdent(String);
pub struct PIdent(pub String);
impl Parsable for PIdent {
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {

View File

@@ -1,8 +1,10 @@
use std::fmt::Debug;
use crate::parser::ParsableWith;
use super::{
util::parse_list, Keyword, Node, PExpr, PFieldDef, PIdent, PType, PVarDef, Parsable,
ParseResult, ParserCtx, CompilerMsg, Symbol,
util::parse_list, CompilerMsg, Keyword, Node, PExpr, PFieldDef, PIdent, PType, PVarDef,
Parsable, ParseResult, ParserCtx, Symbol,
};
#[derive(Debug)]
@@ -13,7 +15,7 @@ pub struct PStruct {
#[derive(Debug)]
pub struct PConstruct {
pub name: Node<PIdent>,
pub name: Node<PType>,
pub fields: PConstructFields,
}
@@ -57,11 +59,20 @@ impl Parsable for PStruct {
}
}
impl Parsable for PConstruct {
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
ctx.expect_kw(Keyword::Struct)?;
let name = ctx.parse()?;
impl ParsableWith for PConstruct {
type Data = Node<PIdent>;
fn parse(ctx: &mut ParserCtx, name_node: Self::Data) -> ParseResult<Self> {
let next = ctx.expect_peek()?;
// TODO: this is not correct span; type should also span generics, which aren't even in
// here yet
let span = name_node.span;
let name = Node::new(
PType {
name: name_node,
args: Vec::new(),
},
span,
);
let fields = if next.is_symbol(Symbol::Semicolon) {
ctx.next();
PConstructFields::None

View File

@@ -1,9 +1,11 @@
use std::fmt::Debug;
use super::{util::parse_list, Node, Parsable, ParseResult, ParserCtx, CompilerMsg, Symbol, Token};
use super::{
util::parse_list, CompilerMsg, Node, PIdent, Parsable, ParseResult, ParserCtx, Symbol, Token,
};
pub struct PType {
pub name: String,
pub name: Node<PIdent>,
pub args: Vec<Node<PType>>,
}
@@ -11,18 +13,15 @@ impl Parsable for PType {
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
let next = ctx.expect_peek()?;
let res = if next.is_symbol(Symbol::Ampersand) {
let name = Node::new(PIdent("&".to_string()), next.span);
ctx.next();
let arg = ctx.parse()?;
Self {
name: "&".to_string(),
name,
args: vec![arg],
}
} else {
let Token::Word(name) = &next.token else {
return ParseResult::Err(CompilerMsg::unexpected_token(next, "a type identifier"));
};
let n = name.to_string();
ctx.next();
let n = ctx.parse()?;
let mut args = Vec::new();
if let Some(next) = ctx.peek() {
if next.is_symbol(Symbol::OpenAngle) {
@@ -38,8 +37,8 @@ impl Parsable for PType {
impl Debug for PType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name)?;
if self.name == "&" {
write!(f, "{:?}", self.name)?;
if self.name.as_ref().is_some_and(|n| n.0 == "&") {
write!(f, "{:?}", self.args[0])?;
} else if !self.args.is_empty() {
write!(f, "<{:?}>", self.args)?;