stufffffffffff
This commit is contained in:
parent
ddd65e8c4d
commit
00359a306c
70
src/ast.rs
70
src/ast.rs
|
@ -6,7 +6,7 @@ use crate::string_table::{self, ImmOrIndex};
|
||||||
|
|
||||||
pub type Node = NonZero<u32>;
|
pub type Node = NonZero<u32>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum Tag {
|
pub enum Tag {
|
||||||
Undefined,
|
Undefined,
|
||||||
Root,
|
Root,
|
||||||
|
@ -46,14 +46,6 @@ pub enum Tag {
|
||||||
Ident {
|
Ident {
|
||||||
name: string_table::Index,
|
name: string_table::Index,
|
||||||
},
|
},
|
||||||
IntegralConstant {
|
|
||||||
bits: string_table::Index,
|
|
||||||
ty: Option<IntegralType>,
|
|
||||||
},
|
|
||||||
FloatingConstant {
|
|
||||||
bits: u64,
|
|
||||||
ty: FloatingType,
|
|
||||||
},
|
|
||||||
Constant {
|
Constant {
|
||||||
bytes: ImmOrIndex,
|
bytes: ImmOrIndex,
|
||||||
ty: Type,
|
ty: Type,
|
||||||
|
@ -197,7 +189,7 @@ pub enum Tag {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum LetOrVar {
|
pub enum LetOrVar {
|
||||||
Let,
|
Let,
|
||||||
Var,
|
Var,
|
||||||
|
@ -298,14 +290,12 @@ impl PartialEq for Type {
|
||||||
(Self::Floating(l0), Self::Floating(r0)) => l0 == r0,
|
(Self::Floating(l0), Self::Floating(r0)) => l0 == r0,
|
||||||
(
|
(
|
||||||
Self::Pointer {
|
Self::Pointer {
|
||||||
constness: l_constness,
|
pointee: l_pointee, ..
|
||||||
pointee: l_pointee,
|
|
||||||
},
|
},
|
||||||
Self::Pointer {
|
Self::Pointer {
|
||||||
constness: r_constness,
|
pointee: r_pointee, ..
|
||||||
pointee: r_pointee,
|
|
||||||
},
|
},
|
||||||
) => l_constness == r_constness && l_pointee == r_pointee,
|
) => l_pointee == r_pointee,
|
||||||
(
|
(
|
||||||
Self::Fn {
|
Self::Fn {
|
||||||
parameter_types: l_parameter_types,
|
parameter_types: l_parameter_types,
|
||||||
|
@ -322,12 +312,41 @@ impl PartialEq for Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Type {
|
impl Type {
|
||||||
|
pub fn as_primitive_type(&self) -> Option<PrimitiveType> {
|
||||||
|
match self {
|
||||||
|
Type::Void => Some(PrimitiveType::Void),
|
||||||
|
Type::Bool => Some(PrimitiveType::Bool),
|
||||||
|
Type::Integer(t) => Some(PrimitiveType::IntegralType(*t)),
|
||||||
|
Type::Floating(t) => Some(PrimitiveType::FloatingType(*t)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn equal_type(&self, rhs: &Self) -> Option<Type> {
|
pub fn equal_type(&self, rhs: &Self) -> Option<Type> {
|
||||||
match (self, rhs) {
|
match (self, rhs) {
|
||||||
(Self::ComptimeNumber, Self::Floating(_))
|
(Self::ComptimeNumber, Self::Floating(_))
|
||||||
| (Self::ComptimeNumber, Self::Integer(_)) => Some(rhs.clone()),
|
| (Self::ComptimeNumber, Self::Integer(_)) => Some(rhs.clone()),
|
||||||
(Self::Integer(_), Self::ComptimeNumber)
|
(Self::Integer(_), Self::ComptimeNumber)
|
||||||
| (Self::Floating(_), Self::ComptimeNumber) => Some(self.clone()),
|
| (Self::Floating(_), Self::ComptimeNumber) => Some(self.clone()),
|
||||||
|
(
|
||||||
|
Self::Pointer {
|
||||||
|
constness: a_const,
|
||||||
|
pointee: a_ptr,
|
||||||
|
},
|
||||||
|
Self::Pointer {
|
||||||
|
constness: b_const,
|
||||||
|
pointee: b_ptr,
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
if a_ptr == b_ptr {
|
||||||
|
Some(Self::Pointer {
|
||||||
|
constness: *a_const || *b_const,
|
||||||
|
pointee: a_ptr.clone(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if self.eq(rhs) {
|
if self.eq(rhs) {
|
||||||
Some(self.clone())
|
Some(self.clone())
|
||||||
|
@ -343,6 +362,9 @@ impl Type {
|
||||||
pub fn bool() -> Type {
|
pub fn bool() -> Type {
|
||||||
Self::Void
|
Self::Void
|
||||||
}
|
}
|
||||||
|
pub fn comptime_number() -> Type {
|
||||||
|
Self::ComptimeNumber
|
||||||
|
}
|
||||||
pub fn any() -> Type {
|
pub fn any() -> Type {
|
||||||
Self::Any
|
Self::Any
|
||||||
}
|
}
|
||||||
|
@ -353,6 +375,22 @@ impl Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn bit_width(&self) -> u16 {
|
||||||
|
match self {
|
||||||
|
Type::Any => 0,
|
||||||
|
Type::Void => 0,
|
||||||
|
Type::Bool => 1,
|
||||||
|
Type::ComptimeNumber => u16::MAX,
|
||||||
|
Type::Integer(i) => i.bits,
|
||||||
|
Type::Floating(f) => match f {
|
||||||
|
FloatingType::Binary32 => 32,
|
||||||
|
FloatingType::Binary64 => 64,
|
||||||
|
},
|
||||||
|
Type::Pointer { .. } => 64,
|
||||||
|
Type::Fn { .. } => 64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn can_logical_and_or(&self) -> bool {
|
pub fn can_logical_and_or(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Type::Bool => true,
|
Type::Bool => true,
|
||||||
|
@ -452,7 +490,7 @@ impl Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum PrimitiveType {
|
pub enum PrimitiveType {
|
||||||
FloatingType(FloatingType),
|
FloatingType(FloatingType),
|
||||||
IntegralType(IntegralType),
|
IntegralType(IntegralType),
|
||||||
|
|
26
src/error.rs
Normal file
26
src/error.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
use crate::ast::Type;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||||
|
pub enum AnalysisErrorTag {
|
||||||
|
#[error("Mismatching types in function return.")]
|
||||||
|
MismatchingTypesFunctionReturn,
|
||||||
|
#[error("Insufficient bits in type {1} for constant with {0} bits")]
|
||||||
|
InsufficientBitsInTypeForConstant(u32, Type),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||||
|
pub struct AnalysisError {
|
||||||
|
inner: AnalysisErrorTag,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnalysisError {
|
||||||
|
pub fn new(inner: AnalysisErrorTag) -> Self {
|
||||||
|
Self { inner }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Display for AnalysisError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
core::fmt::Debug::fmt(&self.inner, f)
|
||||||
|
}
|
||||||
|
}
|
40
src/lexer.rs
40
src/lexer.rs
|
@ -593,18 +593,7 @@ pub mod bigint {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bit_width(&self) -> u32 {
|
pub fn bit_width(&self) -> u32 {
|
||||||
let mut bits = self.0.len() as u32;
|
count_bits(&self.0)
|
||||||
|
|
||||||
for d in self.0.iter().rev() {
|
|
||||||
if *d == 0 {
|
|
||||||
bits -= u32::BITS;
|
|
||||||
} else {
|
|
||||||
bits -= d.leading_zeros();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bits
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bytes_le(bytes: &[u8]) -> BigInt {
|
pub fn from_bytes_le(bytes: &[u8]) -> BigInt {
|
||||||
|
@ -677,6 +666,33 @@ pub mod bigint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// counts used bits in a u32 slice, discards leading zeros in MSB.
|
||||||
|
/// `[0xff,0xff,0x00,0x00]` -> 16
|
||||||
|
/// `[0xff,0xff,0x00]` -> 16
|
||||||
|
/// `[0xff,0xff,0x0f]` -> 20
|
||||||
|
pub fn count_bits(bytes: &[u32]) -> u32 {
|
||||||
|
let mut bits = bytes.len() as u32;
|
||||||
|
|
||||||
|
for d in bytes.iter().rev() {
|
||||||
|
if *d == 0 {
|
||||||
|
bits -= u32::BITS;
|
||||||
|
} else {
|
||||||
|
bits -= d.leading_zeros();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bits
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_count_bits() {
|
||||||
|
assert_eq!(count_bits(&[0xffffffff, 0x00, 0x00]), 32);
|
||||||
|
assert_eq!(count_bits(&[0xffffffff, 0xff, 0x00]), 40);
|
||||||
|
assert_eq!(count_bits(&[0xffffffff, 0xff]), 40);
|
||||||
|
assert_eq!(count_bits(&[0xffffffff, 0xff, 0xffff]), 64 + 16);
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
/// lhs must be bigger than rhs
|
/// lhs must be bigger than rhs
|
||||||
fn sub_bigint(lhs: &mut [u32], rhs: &[u32]) {
|
fn sub_bigint(lhs: &mut [u32], rhs: &[u32]) {
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
pub mod codegen;
|
pub mod codegen;
|
||||||
pub mod common;
|
pub mod common;
|
||||||
|
pub mod error;
|
||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod string_table;
|
pub mod string_table;
|
||||||
|
|
225
src/parser.rs
225
src/parser.rs
|
@ -5,8 +5,9 @@ use itertools::Itertools;
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{FloatingType, IntegralType, LetOrVar, Node, PrimitiveType, Tag, Type},
|
ast::{FloatingType, IntegralType, LetOrVar, Node, PrimitiveType, Tag, Type},
|
||||||
common::NextIf,
|
common::NextIf,
|
||||||
|
error::{AnalysisError, AnalysisErrorTag},
|
||||||
lexer::{bigint::BigInt, Radix, TokenIterator},
|
lexer::{bigint::BigInt, Radix, TokenIterator},
|
||||||
string_table::{Index, StringTable},
|
string_table::{ImmOrIndex, Index, StringTable},
|
||||||
symbol_table::{SymbolKind, SymbolTable},
|
symbol_table::{SymbolKind, SymbolTable},
|
||||||
tokens::Token,
|
tokens::Token,
|
||||||
};
|
};
|
||||||
|
@ -41,6 +42,11 @@ impl core::ops::Index<Node> for Nodes {
|
||||||
&self.inner[index.get() as usize]
|
&self.inner[index.get() as usize]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl core::ops::IndexMut<Node> for Nodes {
|
||||||
|
fn index_mut(&mut self, index: Node) -> &mut Self::Output {
|
||||||
|
&mut self.inner[index.get() as usize]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Nodes {
|
impl Nodes {
|
||||||
fn new() -> Nodes {
|
fn new() -> Nodes {
|
||||||
|
@ -126,6 +132,25 @@ impl Tree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn global_decls(&self) -> impl Iterator<Item = (Node, String)> {
|
||||||
|
self.global_decls.iter().map(|decl| {
|
||||||
|
let name = match self.nodes.get_node(*decl) {
|
||||||
|
Tag::FunctionDecl { proto, body } => {
|
||||||
|
let Tag::FunctionProto { name, .. } = self.nodes.get_node(*proto) else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
self.get_ident_str(*name).unwrap().to_owned()
|
||||||
|
}
|
||||||
|
Tag::GlobalDecl { name, .. } => self.get_ident_str(*name).unwrap().to_owned(),
|
||||||
|
_ => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(*decl, name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn is_integral_type(lexeme: &str) -> Option<()> {
|
fn is_integral_type(lexeme: &str) -> Option<()> {
|
||||||
let mut iter = lexeme.chars();
|
let mut iter = lexeme.chars();
|
||||||
|
@ -822,16 +847,35 @@ impl Tree {
|
||||||
| Token::IntegerConstant => {
|
| Token::IntegerConstant => {
|
||||||
_ = tokens.next();
|
_ = tokens.next();
|
||||||
let (bits, ty) = Self::parse_integral_constant(token.token(), token.lexeme());
|
let (bits, ty) = Self::parse_integral_constant(token.token(), token.lexeme());
|
||||||
let index = self.strings.insert(bits.into_bytes_le());
|
let bytes = bits.into_bytes_le();
|
||||||
|
|
||||||
|
const BUF_SIZE: usize = core::mem::size_of::<u64>();
|
||||||
|
let mut buf = [0u8; BUF_SIZE];
|
||||||
|
buf[..bytes.len().min(BUF_SIZE)]
|
||||||
|
.copy_from_slice(&bytes[..bytes.len().min(BUF_SIZE)]);
|
||||||
|
let bytes = match bytes.len() {
|
||||||
|
0..2 => {
|
||||||
|
let (buf, _) = buf.split_at(core::mem::size_of::<u32>());
|
||||||
|
let dw = u32::from_le_bytes(buf.try_into().unwrap());
|
||||||
|
ImmOrIndex::U32(dw)
|
||||||
|
}
|
||||||
|
0..4 => {
|
||||||
|
let (buf, _) = buf.split_at(core::mem::size_of::<u64>());
|
||||||
|
let qw = u64::from_le_bytes(buf.try_into().unwrap());
|
||||||
|
ImmOrIndex::U64(qw)
|
||||||
|
}
|
||||||
|
0.. => {
|
||||||
|
let idx = self.strings.insert(bytes);
|
||||||
|
ImmOrIndex::Index(idx)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let ty = match ty {
|
let ty = match ty {
|
||||||
Some(int) => Type::Integer(int),
|
Some(int) => Type::Integer(int),
|
||||||
None => Type::ComptimeNumber,
|
None => Type::ComptimeNumber,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(self.nodes.push_tag(Tag::Constant {
|
Ok(self.nodes.push_tag(Tag::Constant { bytes, ty }))
|
||||||
bytes: crate::string_table::ImmOrIndex::Index(index),
|
|
||||||
ty,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
Token::FloatingConstant
|
Token::FloatingConstant
|
||||||
| Token::FloatingExpConstant
|
| Token::FloatingExpConstant
|
||||||
|
@ -840,8 +884,13 @@ impl Tree {
|
||||||
_ = tokens.next();
|
_ = tokens.next();
|
||||||
let (bits, ty) = Self::parse_floating_constant(token.token(), token.lexeme());
|
let (bits, ty) = Self::parse_floating_constant(token.token(), token.lexeme());
|
||||||
|
|
||||||
|
let bytes = match ty {
|
||||||
|
FloatingType::Binary32 => ImmOrIndex::U32(bits as u32),
|
||||||
|
FloatingType::Binary64 => ImmOrIndex::U64(bits as u64),
|
||||||
|
};
|
||||||
|
|
||||||
Ok(self.nodes.push_tag(Tag::Constant {
|
Ok(self.nodes.push_tag(Tag::Constant {
|
||||||
bytes: crate::string_table::ImmOrIndex::U64(bits),
|
bytes,
|
||||||
ty: Type::Floating(ty),
|
ty: Type::Floating(ty),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -906,7 +955,9 @@ impl Tree {
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tree {
|
||||||
fn render_node<W: core::fmt::Write>(
|
fn render_node<W: core::fmt::Write>(
|
||||||
&mut self,
|
&mut self,
|
||||||
writer: &mut W,
|
writer: &mut W,
|
||||||
|
@ -1360,17 +1411,13 @@ impl Tree {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Tag::Constant { bytes, ty } => {
|
Tag::Constant { bytes, ty } => {
|
||||||
let bytes = match bytes {
|
|
||||||
crate::string_table::ImmOrIndex::U64(i) => &i.to_le_bytes()[..],
|
|
||||||
crate::string_table::ImmOrIndex::U32(i) => &i.to_le_bytes()[..],
|
|
||||||
crate::string_table::ImmOrIndex::Index(idx) => self.strings.get_bytes(idx),
|
|
||||||
};
|
|
||||||
writeln_indented!(
|
writeln_indented!(
|
||||||
indent,
|
indent,
|
||||||
writer,
|
writer,
|
||||||
"%{} = constant{{ ty: {}, bytes: {bytes:?}}}",
|
"%{} = constant{{ ty: {}, bytes: {}}}",
|
||||||
node.get(),
|
node.get(),
|
||||||
ty
|
ty,
|
||||||
|
self.strings.display_idx(bytes)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
|
@ -1487,6 +1534,146 @@ impl Tree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Tree {
|
||||||
|
/// type-checks and inserts appropriate explicit-cast nodes.
|
||||||
|
pub fn typecheck(&mut self) {
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
for decl in self.global_decls.clone() {
|
||||||
|
self.typecheck_node(&mut errors, decl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: inline types into the AST proper before tackling this.
|
||||||
|
// for now, comptime_number is not supported in IR gen, then.
|
||||||
|
fn typecheck_node(&mut self, errors: &mut Vec<AnalysisError>, node: Node) {
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
match self.nodes[node].clone() {
|
||||||
|
Tag::FunctionProto { .. } => {}
|
||||||
|
Tag::FunctionDecl { proto, body } => {
|
||||||
|
let Tag::FunctionProto { return_type, .. } = self.nodes[proto] else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
let body_t = self.type_of_node(body);
|
||||||
|
let ret_t = self.type_of_node(return_type);
|
||||||
|
|
||||||
|
if let Some(peer_t) = body_t.equal_type(&ret_t) {
|
||||||
|
if body_t == Type::comptime_number() {
|
||||||
|
let Tag::Block { trailing_expr, .. } = self.nodes[body] else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
if let Some(expr) = trailing_expr {
|
||||||
|
let ty = self.nodes.push_tag(Tag::PrimitiveType(
|
||||||
|
peer_t
|
||||||
|
.as_primitive_type()
|
||||||
|
.expect("comptime cannot be cast into a non-primitive type"),
|
||||||
|
));
|
||||||
|
let expr = self.nodes.push_tag(Tag::ExplicitCast {
|
||||||
|
lhs: expr,
|
||||||
|
typename: ty,
|
||||||
|
});
|
||||||
|
|
||||||
|
let Tag::Block { trailing_expr, .. } = &mut self.nodes[body] else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
*trailing_expr = Some(expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errors.push(AnalysisError::new(
|
||||||
|
AnalysisErrorTag::MismatchingTypesFunctionReturn,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Tag::Constant { bytes, ty } => {
|
||||||
|
let bits = self.strings.count_bits(bytes);
|
||||||
|
if bits < ty.bit_width() as u32 {
|
||||||
|
errors.push(AnalysisError::new(
|
||||||
|
AnalysisErrorTag::InsufficientBitsInTypeForConstant(bits, ty.clone()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Tag::Block {
|
||||||
|
statements,
|
||||||
|
trailing_expr,
|
||||||
|
} => {
|
||||||
|
for statement in statements {
|
||||||
|
self.typecheck_node(errors, statement);
|
||||||
|
}
|
||||||
|
if let Some(expr) = trailing_expr {
|
||||||
|
self.typecheck_node(errors, expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Tag::ReturnStmt { expr } => {
|
||||||
|
if let Some(expr) = expr {
|
||||||
|
self.typecheck_node(errors, expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Tag::ExprStmt { expr } => {
|
||||||
|
self.typecheck_node(errors, expr);
|
||||||
|
}
|
||||||
|
Tag::VarDecl {
|
||||||
|
explicit_type,
|
||||||
|
assignment,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
assignment.map(|t| self.typecheck_node(errors, t));
|
||||||
|
|
||||||
|
let explicit_t = explicit_type.map(|t| self.type_of_node(t));
|
||||||
|
let assignment_t = assignment.map(|t| self.type_of_node(t));
|
||||||
|
|
||||||
|
match (explicit_t, assignment_t) {
|
||||||
|
(None, None) => unreachable!(),
|
||||||
|
(Some(explicit_t), None) => {}
|
||||||
|
(Some(explicit_t), Some(assignment_t)) => {
|
||||||
|
// TODO: ensure types match, explicit-cast comptime_number
|
||||||
|
}
|
||||||
|
(None, Some(assignment_t)) => {
|
||||||
|
// TODO: set explicit_type to assignment_t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Tag::GlobalDecl {
|
||||||
|
name,
|
||||||
|
explicit_type,
|
||||||
|
assignment,
|
||||||
|
} => todo!(),
|
||||||
|
Tag::DeclRef(_) => todo!(),
|
||||||
|
Tag::GlobalRef(_) => todo!(),
|
||||||
|
Tag::CallExpr { lhs, rhs } => todo!(),
|
||||||
|
Tag::ArgumentList { parameters } => todo!(),
|
||||||
|
Tag::Argument { name, expr } => todo!(),
|
||||||
|
Tag::ExplicitCast { lhs, typename } => todo!(),
|
||||||
|
Tag::Deref { lhs } => todo!(),
|
||||||
|
Tag::Ref { lhs } => todo!(),
|
||||||
|
Tag::Not { lhs } => todo!(),
|
||||||
|
Tag::Negate { lhs } => todo!(),
|
||||||
|
Tag::Or { lhs, rhs } => todo!(),
|
||||||
|
Tag::And { lhs, rhs } => todo!(),
|
||||||
|
Tag::BitOr { lhs, rhs } => todo!(),
|
||||||
|
Tag::BitAnd { lhs, rhs } => todo!(),
|
||||||
|
Tag::BitXOr { lhs, rhs } => todo!(),
|
||||||
|
Tag::Eq { lhs, rhs } => todo!(),
|
||||||
|
Tag::NEq { lhs, rhs } => todo!(),
|
||||||
|
Tag::Lt { lhs, rhs } => todo!(),
|
||||||
|
Tag::Gt { lhs, rhs } => todo!(),
|
||||||
|
Tag::Le { lhs, rhs } => todo!(),
|
||||||
|
Tag::Ge { lhs, rhs } => todo!(),
|
||||||
|
Tag::Shl { lhs, rhs } => todo!(),
|
||||||
|
Tag::Shr { lhs, rhs } => todo!(),
|
||||||
|
Tag::Add { lhs, rhs } => todo!(),
|
||||||
|
Tag::Sub { lhs, rhs } => todo!(),
|
||||||
|
Tag::Mul { lhs, rhs } => todo!(),
|
||||||
|
Tag::Rem { lhs, rhs } => todo!(),
|
||||||
|
Tag::Div { lhs, rhs } => todo!(),
|
||||||
|
Tag::Assign { lhs, rhs } => todo!(),
|
||||||
|
_ => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static PRECEDENCE_MAP: std::sync::LazyLock<HashMap<Token, u32>> = std::sync::LazyLock::new(|| {
|
static PRECEDENCE_MAP: std::sync::LazyLock<HashMap<Token, u32>> = std::sync::LazyLock::new(|| {
|
||||||
HashMap::from([
|
HashMap::from([
|
||||||
(Token::PipePipe, 10),
|
(Token::PipePipe, 10),
|
||||||
|
@ -1518,7 +1705,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn render_ast() {
|
fn render_ast() {
|
||||||
let src = "let a: u21 = 3;";
|
let src = "let a: u21 = 3u32;";
|
||||||
let tokens = Tokenizer::new(src.as_bytes()).unwrap();
|
let tokens = Tokenizer::new(src.as_bytes()).unwrap();
|
||||||
|
|
||||||
let mut tree = Tree::new();
|
let mut tree = Tree::new();
|
||||||
|
@ -1532,8 +1719,8 @@ mod tests {
|
||||||
fn render_ast2() {
|
fn render_ast2() {
|
||||||
let src = "
|
let src = "
|
||||||
fn main() -> void {
|
fn main() -> void {
|
||||||
let a: u32 = 0;
|
let a: u32 = 0u32;
|
||||||
a == 1
|
a == 1u32
|
||||||
}
|
}
|
||||||
fn square(x: u32) -> u32 {
|
fn square(x: u32) -> u32 {
|
||||||
x * x
|
x * x
|
||||||
|
@ -1553,10 +1740,10 @@ x * x
|
||||||
fn render_ast3() {
|
fn render_ast3() {
|
||||||
let src = "
|
let src = "
|
||||||
fn main() -> void {
|
fn main() -> void {
|
||||||
let a: u32 = 0;
|
let a: u32 = 0u32;
|
||||||
a == global
|
a == global
|
||||||
}
|
}
|
||||||
const global: u32 = 42;
|
const global: u32 = 42u32;
|
||||||
";
|
";
|
||||||
let tokens = Tokenizer::new(src.as_bytes()).unwrap();
|
let tokens = Tokenizer::new(src.as_bytes()).unwrap();
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use std::{collections::BTreeMap, hash::Hasher};
|
use std::{collections::BTreeMap, hash::Hasher};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct Index {
|
pub struct Index {
|
||||||
pub start: u32,
|
pub start: u32,
|
||||||
pub end: u32,
|
pub end: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum ImmOrIndex {
|
pub enum ImmOrIndex {
|
||||||
U64(u64),
|
U64(u64),
|
||||||
U32(u32),
|
U32(u32),
|
||||||
|
@ -19,7 +19,6 @@ impl Index {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct StringTable {
|
pub struct StringTable {
|
||||||
bytes: Vec<u8>,
|
bytes: Vec<u8>,
|
||||||
indices: BTreeMap<u64, Index>,
|
indices: BTreeMap<u64, Index>,
|
||||||
|
@ -41,6 +40,24 @@ impl StringTable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn display_idx(&self, idx: ImmOrIndex) -> ImmOrIndexDisplay {
|
||||||
|
ImmOrIndexDisplay::new(self, idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn count_bits(&self, idx: ImmOrIndex) -> u32 {
|
||||||
|
match idx {
|
||||||
|
ImmOrIndex::U64(v) => u64::BITS - v.leading_zeros(),
|
||||||
|
ImmOrIndex::U32(v) => u32::BITS - v.leading_zeros(),
|
||||||
|
ImmOrIndex::Index(idx) => {
|
||||||
|
let bytes = self.get_bytes(idx);
|
||||||
|
let ints = unsafe {
|
||||||
|
core::slice::from_raw_parts(bytes.as_ptr().cast::<u32>(), bytes.len() / 4)
|
||||||
|
};
|
||||||
|
crate::lexer::bigint::count_bits(ints)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_str(&self, idx: Index) -> &str {
|
pub fn get_str(&self, idx: Index) -> &str {
|
||||||
unsafe { core::str::from_utf8_unchecked(&self[idx]) }
|
unsafe { core::str::from_utf8_unchecked(&self[idx]) }
|
||||||
}
|
}
|
||||||
|
@ -73,3 +90,60 @@ impl StringTable {
|
||||||
index
|
index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod display {
|
||||||
|
use core::{fmt::Debug, str};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
impl Debug for StringTable {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_list()
|
||||||
|
.entries(self.indices.iter().map(|(_, idx)| {
|
||||||
|
struct Test<'a> {
|
||||||
|
bytes: &'a [u8],
|
||||||
|
str: Option<&'a str>,
|
||||||
|
}
|
||||||
|
impl<'a> Debug for Test<'a> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{{ bytes: {:x?}", self.bytes)?;
|
||||||
|
if let Some(str) = self.str {
|
||||||
|
write!(f, ", str: {}", str)?;
|
||||||
|
}
|
||||||
|
write!(f, " }}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let bytes = self.get_bytes(*idx);
|
||||||
|
let str = str::from_utf8(bytes).ok();
|
||||||
|
Test { bytes, str }
|
||||||
|
}))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ImmOrIndexDisplay<'table> {
|
||||||
|
table: &'table StringTable,
|
||||||
|
idx: ImmOrIndex,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'table> ImmOrIndexDisplay<'table> {
|
||||||
|
pub fn new(table: &'table StringTable, idx: ImmOrIndex) -> Self {
|
||||||
|
Self { table, idx }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'table> core::fmt::Display for ImmOrIndexDisplay<'table> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self.idx {
|
||||||
|
ImmOrIndex::U64(i) => write!(f, "0x{i:0>16x}"),
|
||||||
|
ImmOrIndex::U32(i) => write!(f, "0x{i:0>8x}"),
|
||||||
|
ImmOrIndex::Index(idx) => {
|
||||||
|
let bytes = self.table.get_bytes(idx);
|
||||||
|
write!(f, "{bytes:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use display::ImmOrIndexDisplay;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use std::collections::{hash_map::Entry, HashMap};
|
use std::collections::{hash_map::Entry, HashMap};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{FloatingType, IntegralType, Node as AstNode, Tag, Type},
|
ast::{Node as AstNode, Tag, Type},
|
||||||
parser::Tree,
|
parser::Tree,
|
||||||
string_table::{ImmOrIndex, Index as StringsIndex},
|
string_table::{ImmOrIndex, Index as StringsIndex},
|
||||||
writeln_indented,
|
writeln_indented,
|
||||||
|
@ -533,7 +533,7 @@ mod tests {
|
||||||
fn ir() {
|
fn ir() {
|
||||||
let src = "
|
let src = "
|
||||||
fn main() -> u32 {
|
fn main() -> u32 {
|
||||||
let a: u32 = 0 + 3u32;
|
let a: u32 = 0u32 + 3u32;
|
||||||
let ptr_a = &a;
|
let ptr_a = &a;
|
||||||
return *ptr_a * global;
|
return *ptr_a * global;
|
||||||
}
|
}
|
||||||
|
@ -542,7 +542,7 @@ fn square(x: u32) -> u32 {
|
||||||
x * x
|
x * x
|
||||||
}
|
}
|
||||||
|
|
||||||
const global: u32 = 42;
|
const global: u32 = 42u32;
|
||||||
";
|
";
|
||||||
let tokens = Tokenizer::new(src.as_bytes()).unwrap();
|
let tokens = Tokenizer::new(src.as_bytes()).unwrap();
|
||||||
|
|
||||||
|
@ -552,6 +552,7 @@ const global: u32 = 42;
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
tree.render(&mut buf).unwrap();
|
tree.render(&mut buf).unwrap();
|
||||||
println!("{buf}");
|
println!("{buf}");
|
||||||
|
println!("{:#?}", tree.strings);
|
||||||
|
|
||||||
let mut ir = IR::new();
|
let mut ir = IR::new();
|
||||||
ir.build(&mut tree);
|
ir.build(&mut tree);
|
||||||
|
|
Loading…
Reference in a new issue