From e43847bbd4a59232c401d74c32a3b78175cc2604 Mon Sep 17 00:00:00 2001 From: John Date: Fri, 26 Jul 2024 05:26:08 -0500 Subject: [PATCH] conlang: Introduce `as` casting Arbitrary primitive type conversion Currently implemented as jankily as possible in the interpreter, but it works alright:tm: --- compiler/cl-ast/src/ast.rs | 8 +++++ compiler/cl-ast/src/ast_impl.rs | 9 +++++ compiler/cl-ast/src/ast_visitor/fold.rs | 5 +++ compiler/cl-ast/src/ast_visitor/visit.rs | 6 ++++ compiler/cl-interpret/src/interpret.rs | 43 ++++++++++++++++++++++++ compiler/cl-parser/src/error.rs | 2 ++ compiler/cl-parser/src/parser.rs | 15 ++++++++- compiler/cl-repl/examples/yaml.rs | 7 ++++ compiler/cl-token/src/token_type.rs | 6 ++-- grammar.ebnf | 4 ++- 10 files changed, 100 insertions(+), 5 deletions(-) diff --git a/compiler/cl-ast/src/ast.rs b/compiler/cl-ast/src/ast.rs index 9c4f4a0..1e01033 100644 --- a/compiler/cl-ast/src/ast.rs +++ b/compiler/cl-ast/src/ast.rs @@ -365,6 +365,8 @@ pub enum ExprKind { Binary(Binary), /// A [Unary] expression: [`UnaryKind`]\* [`Expr`] Unary(Unary), + /// A [Cast] expression: [`Expr`] `as` [`Ty`] + Cast(Cast), /// A [Member] access expression: [`Expr`] [`MemberKind`]\* Member(Member), /// An Array [Index] expression: a[10, 20, 30] @@ -484,6 +486,12 @@ pub enum UnaryKind { Tilde, } +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Cast { + pub head: Box, + pub ty: Ty, +} + /// A [Member] access expression: [`Expr`] [`MemberKind`]\* #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Member { diff --git a/compiler/cl-ast/src/ast_impl.rs b/compiler/cl-ast/src/ast_impl.rs index 22213d6..35d83ad 100644 --- a/compiler/cl-ast/src/ast_impl.rs +++ b/compiler/cl-ast/src/ast_impl.rs @@ -433,6 +433,7 @@ mod display { ExprKind::Modify(v) => v.fmt(f), ExprKind::Binary(v) => v.fmt(f), ExprKind::Unary(v) => v.fmt(f), + ExprKind::Cast(v) => v.fmt(f), ExprKind::Member(v) => v.fmt(f), ExprKind::Index(v) => v.fmt(f), ExprKind::Structor(v) => v.fmt(f), @@ -548,6 +549,13 @@ mod display { } } + impl Display for Cast { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self { head, ty } = self; + write!(f, "{head} as {ty}") + } + } + impl Display for Member { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self { head, kind } = self; @@ -764,6 +772,7 @@ mod convert { Modify => ExprKind::Modify, Binary => ExprKind::Binary, Unary => ExprKind::Unary, + Cast => ExprKind::Cast, Member => ExprKind::Member, Index => ExprKind::Index, Path => ExprKind::Path, diff --git a/compiler/cl-ast/src/ast_visitor/fold.rs b/compiler/cl-ast/src/ast_visitor/fold.rs index 1835a62..b8a6159 100644 --- a/compiler/cl-ast/src/ast_visitor/fold.rs +++ b/compiler/cl-ast/src/ast_visitor/fold.rs @@ -276,6 +276,10 @@ pub trait Fold { fn fold_unary_kind(&mut self, kind: UnaryKind) -> UnaryKind { kind } + fn fold_cast(&mut self, cast: Cast) -> Cast { + let Cast { head, ty } = cast; + Cast { head: Box::new(self.fold_expr_kind(*head)), ty: self.fold_ty(ty) } + } fn fold_member(&mut self, m: Member) -> Member { let Member { head, kind } = m; Member { head: Box::new(self.fold_expr_kind(*head)), kind: self.fold_member_kind(kind) } @@ -535,6 +539,7 @@ pub fn or_fold_expr_kind(folder: &mut F, kind: ExprKind) -> Ex ExprKind::Modify(m) => ExprKind::Modify(folder.fold_modify(m)), ExprKind::Binary(b) => ExprKind::Binary(folder.fold_binary(b)), ExprKind::Unary(u) => ExprKind::Unary(folder.fold_unary(u)), + ExprKind::Cast(c) => ExprKind::Cast(folder.fold_cast(c)), ExprKind::Member(m) => ExprKind::Member(folder.fold_member(m)), ExprKind::Index(i) => ExprKind::Index(folder.fold_index(i)), ExprKind::Structor(s) => ExprKind::Structor(folder.fold_structor(s)), diff --git a/compiler/cl-ast/src/ast_visitor/visit.rs b/compiler/cl-ast/src/ast_visitor/visit.rs index 3a49b42..cfbd410 100644 --- a/compiler/cl-ast/src/ast_visitor/visit.rs +++ b/compiler/cl-ast/src/ast_visitor/visit.rs @@ -238,6 +238,11 @@ pub trait Visit<'a>: Sized { self.visit_expr_kind(tail); } fn visit_unary_kind(&mut self, _kind: &'a UnaryKind) {} + fn visit_cast(&mut self, cast: &'a Cast) { + let Cast { head, ty } = cast; + self.visit_expr_kind(head); + self.visit_ty(ty); + } fn visit_member(&mut self, m: &'a Member) { let Member { head, kind } = m; self.visit_expr_kind(head); @@ -456,6 +461,7 @@ pub fn or_visit_expr_kind<'a, V: Visit<'a>>(visitor: &mut V, e: &'a ExprKind) { ExprKind::Modify(m) => visitor.visit_modify(m), ExprKind::Binary(b) => visitor.visit_binary(b), ExprKind::Unary(u) => visitor.visit_unary(u), + ExprKind::Cast(c) => visitor.visit_cast(c), ExprKind::Member(m) => visitor.visit_member(m), ExprKind::Index(i) => visitor.visit_index(i), ExprKind::Structor(s) => visitor.visit_structor(s), diff --git a/compiler/cl-interpret/src/interpret.rs b/compiler/cl-interpret/src/interpret.rs index 48f3aa9..1293c2c 100644 --- a/compiler/cl-interpret/src/interpret.rs +++ b/compiler/cl-interpret/src/interpret.rs @@ -138,6 +138,7 @@ impl Interpret for ExprKind { ExprKind::Modify(v) => v.interpret(env), ExprKind::Binary(v) => v.interpret(env), ExprKind::Unary(v) => v.interpret(env), + ExprKind::Cast(v) => v.interpret(env), ExprKind::Member(v) => v.interpret(env), ExprKind::Index(v) => v.interpret(env), ExprKind::Structor(v) => v.interpret(env), @@ -334,6 +335,48 @@ impl Interpret for Unary { } } } + +fn cast(value: ConValue, ty: Sym) -> IResult { + let value = match value { + ConValue::Empty => 0, + ConValue::Int(i) => i as _, + ConValue::Bool(b) => b as _, + ConValue::Char(c) => c as _, + ConValue::Ref(v) => return cast((*v).clone(), ty), + _ => Err(Error::TypeError)?, + }; + Ok(match &*ty { + "u8" => ConValue::Int(value as u8 as _), + "i8" => ConValue::Int(value as i8 as _), + "u16" => ConValue::Int(value as u16 as _), + "i16" => ConValue::Int(value as i16 as _), + "u32" => ConValue::Int(value as u32 as _), + "i32" => ConValue::Int(value as i32 as _), + "u64" => ConValue::Int(value), + "i64" => ConValue::Int(value), + "char" => ConValue::Char(char::from_u32(value as _).unwrap_or('\u{fffd}')), + "bool" => ConValue::Bool(value < 0), + _ => Err(Error::NotDefined(ty))?, + }) +} + +impl Interpret for Cast { + fn interpret(&self, env: &mut Environment) -> IResult { + let Cast { head, ty } = self; + let value = head.interpret(env)?; + if TyKind::Empty == ty.kind { + return Ok(ConValue::Empty); + }; + let TyKind::Path(Path { absolute: false, parts }) = &ty.kind else { + Err(Error::TypeError)? + }; + match parts.as_slice() { + [PathPart::Ident(ty)] => cast(value, *ty), + _ => Err(Error::TypeError), + } + } +} + impl Interpret for Member { fn interpret(&self, env: &mut Environment) -> IResult { let Member { head, kind } = self; diff --git a/compiler/cl-parser/src/error.rs b/compiler/cl-parser/src/error.rs index 1335cab..bd46c12 100644 --- a/compiler/cl-parser/src/error.rs +++ b/compiler/cl-parser/src/error.rs @@ -99,6 +99,7 @@ pub enum Parsing { BinaryKind, Unary, UnaryKind, + Cast, Index, Structor, Fielder, @@ -204,6 +205,7 @@ impl Display for Parsing { Parsing::BinaryKind => "a binary operator", Parsing::Unary => "a unary expression", Parsing::UnaryKind => "a unary operator", + Parsing::Cast => "an `as`-casting expression", Parsing::Index => "an indexing expression", Parsing::Structor => "a struct constructor expression", Parsing::Fielder => "a struct field expression", diff --git a/compiler/cl-parser/src/parser.rs b/compiler/cl-parser/src/parser.rs index 02db1f1..310e6b5 100644 --- a/compiler/cl-parser/src/parser.rs +++ b/compiler/cl-parser/src/parser.rs @@ -948,6 +948,17 @@ impl<'t> Parser<'t> { continue; } + if let Punct::As = op { + let before = Precedence::Cast.level(); + if before < power { + break; + } + self.consume_peeked(); + let ty = self.ty()?; + head = Cast { head: head.into(), ty }.into(); + continue; + } + if let Punct::Eq = op { let (before, after) = Precedence::Assign .infix() @@ -961,6 +972,7 @@ impl<'t> Parser<'t> { } break; } + Ok(head) } @@ -1213,13 +1225,14 @@ pub enum Precedence { Factor, Term, Unary, + Cast, Member, // left-associative Call, } impl Precedence { #[inline] - pub fn level(self) -> u8 { + pub const fn level(self) -> u8 { (self as u8) << 1 } pub fn prefix(self) -> Option<((), u8)> { diff --git a/compiler/cl-repl/examples/yaml.rs b/compiler/cl-repl/examples/yaml.rs index 03b3495..c03d29d 100644 --- a/compiler/cl-repl/examples/yaml.rs +++ b/compiler/cl-repl/examples/yaml.rs @@ -392,6 +392,7 @@ pub mod yamlify { ExprKind::Modify(k) => k.yaml(y), ExprKind::Binary(k) => k.yaml(y), ExprKind::Unary(k) => k.yaml(y), + ExprKind::Cast(k) => k.yaml(y), ExprKind::Member(k) => k.yaml(y), ExprKind::Index(k) => k.yaml(y), ExprKind::Structor(k) => k.yaml(y), @@ -461,6 +462,12 @@ pub mod yamlify { y.value(self); } } + impl Yamlify for Cast { + fn yaml(&self, y: &mut Yamler) { + let Self { head, ty } = self; + y.key("Cast").pair("head", head).pair("ty", ty); + } + } impl Yamlify for Member { fn yaml(&self, y: &mut Yamler) { let Self { head, kind } = self; diff --git a/compiler/cl-token/src/token_type.rs b/compiler/cl-token/src/token_type.rs index b24ec62..7283fd6 100644 --- a/compiler/cl-token/src/token_type.rs +++ b/compiler/cl-token/src/token_type.rs @@ -13,7 +13,6 @@ pub enum TokenKind { /// A non-keyword identifier Identifier, // A keyword - As, Break, Cl, Const, @@ -48,6 +47,7 @@ pub enum TokenKind { /// An operator character (delimiter, punctuation) #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum Punct { + As, // as LCurly, // { RCurly, // } LBrack, // [ @@ -112,7 +112,6 @@ impl Display for TokenKind { TokenKind::Literal => "literal".fmt(f), TokenKind::Identifier => "identifier".fmt(f), - TokenKind::As => "as".fmt(f), TokenKind::Break => "break".fmt(f), TokenKind::Cl => "cl".fmt(f), TokenKind::Const => "const".fmt(f), @@ -151,7 +150,7 @@ impl FromStr for TokenKind { /// Parses a string s to return a Keyword fn from_str(s: &str) -> Result { Ok(match s { - "as" => Self::As, + "as" => Self::Punct(Punct::As), "break" => Self::Break, "cl" => Self::Cl, "const" => Self::Const, @@ -187,6 +186,7 @@ impl FromStr for TokenKind { impl Display for Punct { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + Punct::As => "as".fmt(f), Punct::LCurly => "{".fmt(f), Punct::RCurly => "}".fmt(f), Punct::LBrack => "[".fmt(f), diff --git a/grammar.ebnf b/grammar.ebnf index fbaf21e..928b70e 100644 --- a/grammar.ebnf +++ b/grammar.ebnf @@ -96,7 +96,9 @@ Shift = Factor (ShiftOp Factor )* ; Factor = Term (FactorOp Term )* ; Term = Unary (TermOp Unary )* ; -Unary = (UnaryKind)* Member ; +Unary = (UnaryKind)* Cast ; + +Cast = Member ("as" Ty)? ; Member = Call (Access)* ; Access = '.' (Identifier ('(' Tuple? ')')? | Literal) ;