From 351157566988ce0e7cd9ba460b73eed862792185 Mon Sep 17 00:00:00 2001 From: John Date: Sat, 20 Jul 2024 18:22:50 -0500 Subject: [PATCH] conlang: Add array and slice type syntax --- compiler/cl-ast/src/ast.rs | 15 ++++++++++ compiler/cl-ast/src/ast_impl.rs | 16 +++++++++++ compiler/cl-ast/src/ast_visitor/fold.rs | 10 +++++++ compiler/cl-ast/src/ast_visitor/visit.rs | 10 +++++++ compiler/cl-parser/src/error.rs | 6 +++- compiler/cl-parser/src/parser.rs | 27 ++++++++++++++++++ compiler/cl-repl/examples/yaml.rs | 14 +++++++++ compiler/cl-typeck/src/project.rs | 36 +++++++++++++++++++++++- grammar.ebnf | 4 ++- 9 files changed, 135 insertions(+), 3 deletions(-) diff --git a/compiler/cl-ast/src/ast.rs b/compiler/cl-ast/src/ast.rs index b9e9fff..9c4f4a0 100644 --- a/compiler/cl-ast/src/ast.rs +++ b/compiler/cl-ast/src/ast.rs @@ -253,12 +253,27 @@ pub enum TyKind { Never, Empty, Path(Path), + Array(TyArray), + Slice(TySlice), Tuple(TyTuple), Ref(TyRef), Fn(TyFn), // TODO: slice, array types } +/// An array of [`T`](Ty) +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct TyArray { + pub ty: Box, + pub count: usize, +} + +/// A [Ty]pe slice expression: `[T]` +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct TySlice { + pub ty: Box, +} + /// A tuple of [Ty]pes #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct TyTuple { diff --git a/compiler/cl-ast/src/ast_impl.rs b/compiler/cl-ast/src/ast_impl.rs index 0006f4b..22213d6 100644 --- a/compiler/cl-ast/src/ast_impl.rs +++ b/compiler/cl-ast/src/ast_impl.rs @@ -307,6 +307,8 @@ mod display { TyKind::Never => "!".fmt(f), TyKind::Empty => "()".fmt(f), TyKind::Path(v) => v.fmt(f), + TyKind::Array(v) => v.fmt(f), + TyKind::Slice(v) => v.fmt(f), TyKind::Tuple(v) => v.fmt(f), TyKind::Ref(v) => v.fmt(f), TyKind::Fn(v) => v.fmt(f), @@ -314,6 +316,20 @@ mod display { } } + impl Display for TyArray { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self { ty, count } = self; + write!(f, "[{ty}; {count}]") + } + } + + impl Display for TySlice { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self { ty } = self; + write!(f, "[{ty}]") + } + } + impl Display for TyTuple { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { separate(&self.types, ", ")(f.delimit(INLINE_PARENS)) diff --git a/compiler/cl-ast/src/ast_visitor/fold.rs b/compiler/cl-ast/src/ast_visitor/fold.rs index c2613ee..1835a62 100644 --- a/compiler/cl-ast/src/ast_visitor/fold.rs +++ b/compiler/cl-ast/src/ast_visitor/fold.rs @@ -172,6 +172,14 @@ pub trait Fold { fn fold_ty_kind(&mut self, kind: TyKind) -> TyKind { or_fold_ty_kind(self, kind) } + fn fold_ty_array(&mut self, a: TyArray) -> TyArray { + let TyArray { ty, count } = a; + TyArray { ty: Box::new(self.fold_ty_kind(*ty)), count } + } + fn fold_ty_slice(&mut self, s: TySlice) -> TySlice { + let TySlice { ty } = s; + TySlice { ty: Box::new(self.fold_ty_kind(*ty)) } + } fn fold_ty_tuple(&mut self, t: TyTuple) -> TyTuple { let TyTuple { types } = t; TyTuple { @@ -500,6 +508,8 @@ pub fn or_fold_ty_kind(folder: &mut F, kind: TyKind) -> TyKind TyKind::Never => TyKind::Never, TyKind::Empty => TyKind::Empty, TyKind::Path(p) => TyKind::Path(folder.fold_path(p)), + TyKind::Array(a) => TyKind::Array(folder.fold_ty_array(a)), + TyKind::Slice(s) => TyKind::Slice(folder.fold_ty_slice(s)), TyKind::Tuple(t) => TyKind::Tuple(folder.fold_ty_tuple(t)), TyKind::Ref(t) => TyKind::Ref(folder.fold_ty_ref(t)), TyKind::Fn(t) => TyKind::Fn(folder.fold_ty_fn(t)), diff --git a/compiler/cl-ast/src/ast_visitor/visit.rs b/compiler/cl-ast/src/ast_visitor/visit.rs index 0efbc3b..3a49b42 100644 --- a/compiler/cl-ast/src/ast_visitor/visit.rs +++ b/compiler/cl-ast/src/ast_visitor/visit.rs @@ -145,6 +145,14 @@ pub trait Visit<'a>: Sized { fn visit_ty_kind(&mut self, kind: &'a TyKind) { or_visit_ty_kind(self, kind) } + fn visit_ty_array(&mut self, a: &'a TyArray) { + let TyArray { ty, count: _ } = a; + self.visit_ty_kind(ty); + } + fn visit_ty_slice(&mut self, s: &'a TySlice) { + let TySlice { ty } = s; + self.visit_ty_kind(ty) + } fn visit_ty_tuple(&mut self, t: &'a TyTuple) { let TyTuple { types } = t; types.iter().for_each(|kind| self.visit_ty_kind(kind)) @@ -424,6 +432,8 @@ pub fn or_visit_ty_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a TyKind) { TyKind::Never => {} TyKind::Empty => {} TyKind::Path(p) => visitor.visit_path(p), + TyKind::Array(t) => visitor.visit_ty_array(t), + TyKind::Slice(t) => visitor.visit_ty_slice(t), TyKind::Tuple(t) => visitor.visit_ty_tuple(t), TyKind::Ref(t) => visitor.visit_ty_ref(t), TyKind::Fn(t) => visitor.visit_ty_fn(t), diff --git a/compiler/cl-parser/src/error.rs b/compiler/cl-parser/src/error.rs index 0661ad5..1335cab 100644 --- a/compiler/cl-parser/src/error.rs +++ b/compiler/cl-parser/src/error.rs @@ -78,6 +78,8 @@ pub enum Parsing { Ty, TyKind, + TySlice, + TyArray, TyTuple, TyRef, TyFn, @@ -181,13 +183,15 @@ impl Display for Parsing { Parsing::Ty => "a type", Parsing::TyKind => "a type", + Parsing::TySlice => "a slice type", + Parsing::TyArray => "an array type", Parsing::TyTuple => "a tuple of types", Parsing::TyRef => "a reference type", Parsing::TyFn => "a function pointer type", Parsing::Path => "a path", Parsing::PathPart => "a path component", - + Parsing::Stmt => "a statement", Parsing::StmtKind => "a statement", Parsing::Let => "a local variable declaration", diff --git a/compiler/cl-parser/src/parser.rs b/compiler/cl-parser/src/parser.rs index 7072667..02db1f1 100644 --- a/compiler/cl-parser/src/parser.rs +++ b/compiler/cl-parser/src/parser.rs @@ -673,6 +673,7 @@ impl<'t> Parser<'t> { TyKind::Never } TokenKind::Punct(Punct::Amp) | TokenKind::Punct(Punct::AmpAmp) => self.tyref()?.into(), + TokenKind::Punct(Punct::LBrack) => self.tyslice_or_array()?, TokenKind::Punct(Punct::LParen) => { let out = self.tytuple()?; match out.types.is_empty() { @@ -687,6 +688,32 @@ impl<'t> Parser<'t> { Ok(out) } + + /// [`TySlice`] = `[` [Ty] `]` \ + /// [`TyArray`] = `[` [Ty] `;` [usize] `]` + pub fn tyslice_or_array(&mut self) -> PResult { + self.match_op(BRACKETS.0, Parsing::TySlice)?; + let ty = self.tykind()?; + let (out, kind) = match self.match_op(Punct::Semi, Parsing::TyArray).is_ok() { + true => { + let literal = self.match_type(TokenKind::Literal, Parsing::TyArray)?; + let &TokenData::Integer(count) = literal.data() else { + Err(self.error(Unexpected(TokenKind::Literal), Parsing::TyArray))? + }; + ( + TyKind::Array(TyArray { ty: Box::new(ty), count: count as _ }), + Parsing::TyArray, + ) + } + false => ( + TyKind::Slice(TySlice { ty: Box::new(ty) }), + Parsing::TySlice, + ), + }; + self.match_op(BRACKETS.1, kind)?; + Ok(out) + } + /// [TyTuple] = `(` ([Ty] `,`)* [Ty]? `)` pub fn tytuple(&mut self) -> PResult { const PARSING: Parsing = Parsing::TyTuple; diff --git a/compiler/cl-repl/examples/yaml.rs b/compiler/cl-repl/examples/yaml.rs index af5e5e0..03b3495 100644 --- a/compiler/cl-repl/examples/yaml.rs +++ b/compiler/cl-repl/examples/yaml.rs @@ -614,6 +614,8 @@ pub mod yamlify { TyKind::Tuple(t) => y.yaml(t), TyKind::Ref(t) => y.yaml(t), TyKind::Fn(t) => y.yaml(t), + TyKind::Slice(_) => todo!(), + TyKind::Array(_) => todo!(), }; } } @@ -639,6 +641,18 @@ pub mod yamlify { }; } } + impl Yamlify for TyArray { + fn yaml(&self, y: &mut Yamler) { + let Self { ty, count } = self; + y.key("TyArray").pair("ty", ty).pair("count", count); + } + } + impl Yamlify for TySlice { + fn yaml(&self, y: &mut Yamler) { + let Self { ty } = self; + y.key("TyArray").pair("ty", ty); + } + } impl Yamlify for TyTuple { fn yaml(&self, y: &mut Yamler) { let Self { types } = self; diff --git a/compiler/cl-typeck/src/project.rs b/compiler/cl-typeck/src/project.rs index e55892f..58cdc08 100644 --- a/compiler/cl-typeck/src/project.rs +++ b/compiler/cl-typeck/src/project.rs @@ -172,7 +172,7 @@ pub mod evaluate { use super::*; use crate::module; - use cl_ast::{Sym, Ty, TyFn, TyKind, TyRef, TyTuple}; + use cl_ast::{Sym, Ty, TyArray, TyFn, TyKind, TyRef, TySlice, TyTuple}; /// Things that can be evaluated as a type expression pub trait EvaluableTypeExpression { @@ -197,6 +197,8 @@ pub mod evaluate { TyKind::Empty => prj.anon_types[&TypeKind::Empty], // TyKind::Path must be looked up explicitly TyKind::Path(path) => path.evaluate(prj, parent)?, + TyKind::Slice(slice) => slice.evaluate(prj, parent)?, + TyKind::Array(array) => array.evaluate(prj, parent)?, TyKind::Tuple(tup) => tup.evaluate(prj, parent)?, TyKind::Ref(tyref) => tyref.evaluate(prj, parent)?, TyKind::Fn(tyfn) => tyfn.evaluate(prj, parent)?, @@ -220,6 +222,38 @@ pub mod evaluate { } } + impl EvaluableTypeExpression for TySlice { + type Out = DefID; + + fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result { + let ty = self.ty.evaluate(prj, parent)?; + let root = prj.root; + let id = prj.insert_anonymous_type(TypeKind::Slice(ty), move || Def { + module: module::Module::new(root), + node: Node::new(Default::default(), None), + kind: DefKind::Type(TypeKind::Slice(ty)), + }); + + Ok(id) + } + } + + impl EvaluableTypeExpression for TyArray { + type Out = DefID; + + fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result { + let kind = TypeKind::Array(self.ty.evaluate(prj, parent)?, self.count); + let root = prj.root; + let id = prj.insert_anonymous_type(kind.clone(), move || Def { + module: module::Module::new(root), + node: Node::new(Default::default(), None), + kind: DefKind::Type(kind), + }); + + Ok(id) + } + } + impl EvaluableTypeExpression for TyTuple { type Out = DefID; fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result { diff --git a/grammar.ebnf b/grammar.ebnf index 0b02668..fbaf21e 100644 --- a/grammar.ebnf +++ b/grammar.ebnf @@ -50,10 +50,12 @@ UseTree = '*' | '{' (UseTree ',')* UseTree? '}' | PathPart ('::' UseTree | "as" Identifier)? ; (* type *) -Ty = Never | Empty | Path | TyTuple | TyRef | TyFn ; +Ty = Never | Empty | Path | TyArray | TySlice | TyTuple | TyRef | TyFn ; Never = '!' ; Empty = '(' ')' ; TyTuple = '(' (Ty ',')* Ty? ')' ; +TyArray = '[' Ty ';' INTEGER ']' ; +TySlice = '[' Ty ']' ; TyRef = Amps* Path ; Amps = '&' | '&&' ; TyFn = "fn" TyTuple ('->' Ty)? ;