conlang: Introduce as
casting
Arbitrary primitive type conversion
Currently implemented as jankily as possible in the interpreter, but it works alright™️
This commit is contained in:
parent
a8b8a91c79
commit
e43847bbd4
@ -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<ExprKind>,
|
||||
pub ty: Ty,
|
||||
}
|
||||
|
||||
/// A [Member] access expression: [`Expr`] [`MemberKind`]\*
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Member {
|
||||
|
@ -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,
|
||||
|
@ -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<F: Fold + ?Sized>(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)),
|
||||
|
@ -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),
|
||||
|
@ -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<ConValue> {
|
||||
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<ConValue> {
|
||||
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<ConValue> {
|
||||
let Member { head, kind } = self;
|
||||
|
@ -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",
|
||||
|
@ -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)> {
|
||||
|
@ -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;
|
||||
|
@ -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<Self, Self::Err> {
|
||||
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),
|
||||
|
@ -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) ;
|
||||
|
Loading…
Reference in New Issue
Block a user