Add string support to the frontend
This commit is contained in:
parent
32a5c0ff0f
commit
39656a3801
10 changed files with 95 additions and 11 deletions
|
@ -26,7 +26,7 @@ impl<'a, T> Binding<'a, T> {
|
|||
pub enum Expr<'a, T> {
|
||||
Ident(Ident<'a>, T),
|
||||
|
||||
Literal(Literal, T),
|
||||
Literal(Literal<'a>, T),
|
||||
|
||||
UnaryOp {
|
||||
op: UnaryOperator,
|
||||
|
@ -158,7 +158,7 @@ impl<'a, T> Expr<'a, T> {
|
|||
{
|
||||
match self {
|
||||
Expr::Ident(id, t) => Expr::Ident(id.to_owned(), t.clone()),
|
||||
Expr::Literal(lit, t) => Expr::Literal(lit.clone(), t.clone()),
|
||||
Expr::Literal(lit, t) => Expr::Literal(lit.to_owned(), t.clone()),
|
||||
Expr::UnaryOp { op, rhs, type_ } => Expr::UnaryOp {
|
||||
op: *op,
|
||||
rhs: Box::new((**rhs).to_owned()),
|
||||
|
|
|
@ -107,9 +107,20 @@ pub enum UnaryOperator {
|
|||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum Literal {
|
||||
pub enum Literal<'a> {
|
||||
Int(u64),
|
||||
Bool(bool),
|
||||
String(Cow<'a, str>),
|
||||
}
|
||||
|
||||
impl<'a> Literal<'a> {
|
||||
pub fn to_owned(&self) -> Literal<'static> {
|
||||
match self {
|
||||
Literal::Int(i) => Literal::Int(*i),
|
||||
Literal::Bool(b) => Literal::Bool(*b),
|
||||
Literal::String(s) => Literal::String(Cow::Owned(s.clone().into_owned())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
|
@ -133,7 +144,7 @@ impl<'a> Binding<'a> {
|
|||
pub enum Expr<'a> {
|
||||
Ident(Ident<'a>),
|
||||
|
||||
Literal(Literal),
|
||||
Literal(Literal<'a>),
|
||||
|
||||
UnaryOp {
|
||||
op: UnaryOperator,
|
||||
|
@ -174,7 +185,7 @@ impl<'a> Expr<'a> {
|
|||
pub fn to_owned(&self) -> Expr<'static> {
|
||||
match self {
|
||||
Expr::Ident(ref id) => Expr::Ident(id.to_owned()),
|
||||
Expr::Literal(ref lit) => Expr::Literal(lit.clone()),
|
||||
Expr::Literal(ref lit) => Expr::Literal(lit.to_owned()),
|
||||
Expr::UnaryOp { op, rhs } => Expr::UnaryOp {
|
||||
op: *op,
|
||||
rhs: Box::new((**rhs).to_owned()),
|
||||
|
@ -247,6 +258,7 @@ pub enum Type {
|
|||
Int,
|
||||
Float,
|
||||
Bool,
|
||||
CString,
|
||||
Function(FunctionType),
|
||||
}
|
||||
|
||||
|
@ -256,6 +268,7 @@ impl Display for Type {
|
|||
Type::Int => f.write_str("int"),
|
||||
Type::Float => f.write_str("float"),
|
||||
Type::Bool => f.write_str("bool"),
|
||||
Type::CString => f.write_str("cstring"),
|
||||
Type::Function(ft) => ft.fmt(f),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,6 +92,7 @@ impl<'ctx, 'ast> Codegen<'ctx, 'ast> {
|
|||
Literal::Bool(b) => Ok(AnyValueEnum::IntValue(
|
||||
ty.const_int(if *b { 1 } else { 0 }, false),
|
||||
)),
|
||||
Literal::String(_) => todo!(),
|
||||
}
|
||||
}
|
||||
Expr::UnaryOp { op, rhs, .. } => {
|
||||
|
|
|
@ -29,6 +29,7 @@ impl<'a> Interpreter<'a> {
|
|||
Expr::Ident(id, _) => self.resolve(id),
|
||||
Expr::Literal(Literal::Int(i), _) => Ok((*i).into()),
|
||||
Expr::Literal(Literal::Bool(b), _) => Ok((*b).into()),
|
||||
Expr::Literal(Literal::String(s), _) => Ok(s.clone().into()),
|
||||
Expr::UnaryOp { op, rhs, .. } => {
|
||||
let rhs = self.eval(rhs)?;
|
||||
match op {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use std::borrow::Cow;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::{self, Display};
|
||||
use std::ops::{Add, Div, Mul, Neg, Sub};
|
||||
use std::rc::Rc;
|
||||
use std::result;
|
||||
|
||||
use derive_more::{Deref, From, TryInto};
|
||||
|
||||
|
@ -22,15 +24,28 @@ pub enum Val<'a> {
|
|||
Int(i64),
|
||||
Float(f64),
|
||||
Bool(bool),
|
||||
String(Cow<'a, str>),
|
||||
Function(Function<'a>),
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<Val<'a>> for String {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: Val<'a>) -> result::Result<Self, Self::Error> {
|
||||
match value {
|
||||
Val::String(s) => Ok(s.into_owned()),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for Val<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Val::Int(x) => f.debug_tuple("Int").field(x).finish(),
|
||||
Val::Float(x) => f.debug_tuple("Float").field(x).finish(),
|
||||
Val::Bool(x) => f.debug_tuple("Bool").field(x).finish(),
|
||||
Val::String(s) => f.debug_tuple("String").field(s).finish(),
|
||||
Val::Function(Function { type_, .. }) => {
|
||||
f.debug_struct("Function").field("type_", type_).finish()
|
||||
}
|
||||
|
@ -62,6 +77,7 @@ impl<'a> Display for Val<'a> {
|
|||
Val::Int(x) => x.fmt(f),
|
||||
Val::Float(x) => x.fmt(f),
|
||||
Val::Bool(x) => x.fmt(f),
|
||||
Val::String(s) => write!(f, "{:?}", s),
|
||||
Val::Function(Function { type_, .. }) => write!(f, "<{}>", type_),
|
||||
}
|
||||
}
|
||||
|
@ -73,6 +89,7 @@ impl<'a> Val<'a> {
|
|||
Val::Int(_) => Type::Int,
|
||||
Val::Float(_) => Type::Float,
|
||||
Val::Bool(_) => Type::Bool,
|
||||
Val::String(_) => Type::CString,
|
||||
Val::Function(Function { type_, .. }) => Type::Function(type_.clone()),
|
||||
}
|
||||
}
|
||||
|
@ -178,3 +195,9 @@ impl TypeOf for f64 {
|
|||
Type::Float
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeOf for String {
|
||||
fn type_of() -> Type {
|
||||
Type::CString
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#![feature(str_split_once)]
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
pub mod ast;
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use nom::alt;
|
||||
use nom::character::complete::{digit1, multispace0, multispace1};
|
||||
use nom::{
|
||||
alt, call, char, complete, delimited, do_parse, flat_map, many0, map, named, opt, parse_to,
|
||||
call, char, complete, delimited, do_parse, flat_map, many0, map, named, opt, parse_to,
|
||||
preceded, separated_list0, separated_list1, tag, tuple,
|
||||
};
|
||||
use pratt::{Affix, Associativity, PrattParser, Precedence};
|
||||
|
||||
use crate::ast::{BinaryOperator, Binding, Expr, Fun, Ident, Literal, UnaryOperator};
|
||||
use crate::ast::{BinaryOperator, Binding, Expr, Fun, Literal, UnaryOperator};
|
||||
use crate::parser::{ident, type_};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -161,7 +164,22 @@ named!(bool_(&str) -> Literal, alt!(
|
|||
tag!("false") => { |_| Literal::Bool(false) }
|
||||
));
|
||||
|
||||
named!(literal(&str) -> Literal, alt!(int | bool_));
|
||||
fn string_internal(i: &str) -> nom::IResult<&str, Cow<'_, str>, nom::error::Error<&str>> {
|
||||
let (s, rem) = i
|
||||
.split_once('"')
|
||||
.ok_or_else(|| nom::Err::Error(nom::error::Error::new(i, nom::error::ErrorKind::Tag)))?;
|
||||
Ok((rem, Cow::Borrowed(s)))
|
||||
}
|
||||
|
||||
named!(string(&str) -> Literal, preceded!(
|
||||
char!('"'),
|
||||
map!(
|
||||
string_internal,
|
||||
|s| Literal::String(s)
|
||||
)
|
||||
));
|
||||
|
||||
named!(literal(&str) -> Literal, alt!(int | bool_ | string));
|
||||
|
||||
named!(literal_expr(&str) -> Expr, map!(literal, Expr::Literal));
|
||||
|
||||
|
@ -308,7 +326,7 @@ named!(pub expr(&str) -> Expr, alt!(
|
|||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::ast::Type;
|
||||
use crate::ast::{Ident, Type};
|
||||
use std::convert::TryFrom;
|
||||
use BinaryOperator::*;
|
||||
use Expr::{BinaryOp, If, Let, UnaryOp};
|
||||
|
@ -418,6 +436,14 @@ pub(crate) mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_string_lit() {
|
||||
assert_eq!(
|
||||
test_parse!(expr, "\"foobar\""),
|
||||
Expr::Literal(Literal::String(Cow::Borrowed("foobar")))
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn let_complex() {
|
||||
let res = test_parse!(expr, "let x = 1; y = x * 7 in (x + y) * 4");
|
||||
|
|
|
@ -16,7 +16,17 @@ pub type Error = nom::Err<nom::error::Error<String>>;
|
|||
pub(crate) fn is_reserved(s: &str) -> bool {
|
||||
matches!(
|
||||
s,
|
||||
"if" | "then" | "else" | "let" | "in" | "fn" | "int" | "float" | "bool" | "true" | "false"
|
||||
"if" | "then"
|
||||
| "else"
|
||||
| "let"
|
||||
| "in"
|
||||
| "fn"
|
||||
| "int"
|
||||
| "float"
|
||||
| "bool"
|
||||
| "true"
|
||||
| "false"
|
||||
| "cstring"
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ named!(pub type_(&str) -> Type, alt!(
|
|||
tag!("int") => { |_| Type::Int } |
|
||||
tag!("float") => { |_| Type::Float } |
|
||||
tag!("bool") => { |_| Type::Bool } |
|
||||
tag!("cstring") => { |_| Type::CString } |
|
||||
function_type |
|
||||
delimited!(
|
||||
tuple!(tag!("("), multispace0),
|
||||
|
@ -44,6 +45,7 @@ mod tests {
|
|||
assert_eq!(test_parse!(type_, "int"), Type::Int);
|
||||
assert_eq!(test_parse!(type_, "float"), Type::Float);
|
||||
assert_eq!(test_parse!(type_, "bool"), Type::Bool);
|
||||
assert_eq!(test_parse!(type_, "cstring"), Type::CString);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -49,6 +49,7 @@ pub enum PrimType {
|
|||
Int,
|
||||
Float,
|
||||
Bool,
|
||||
CString,
|
||||
}
|
||||
|
||||
impl From<PrimType> for ast::Type {
|
||||
|
@ -57,6 +58,7 @@ impl From<PrimType> for ast::Type {
|
|||
PrimType::Int => ast::Type::Int,
|
||||
PrimType::Float => ast::Type::Float,
|
||||
PrimType::Bool => ast::Type::Bool,
|
||||
PrimType::CString => ast::Type::CString,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,6 +69,7 @@ impl Display for PrimType {
|
|||
PrimType::Int => f.write_str("int"),
|
||||
PrimType::Float => f.write_str("float"),
|
||||
PrimType::Bool => f.write_str("bool"),
|
||||
PrimType::CString => f.write_str("cstring"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -125,6 +128,7 @@ impl TryFrom<Type> for ast::Type {
|
|||
const INT: Type = Type::Prim(PrimType::Int);
|
||||
const FLOAT: Type = Type::Prim(PrimType::Float);
|
||||
const BOOL: Type = Type::Prim(PrimType::Bool);
|
||||
const CSTRING: Type = Type::Prim(PrimType::CString);
|
||||
|
||||
impl Display for Type {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
|
@ -144,6 +148,7 @@ impl From<ast::Type> for Type {
|
|||
ast::Type::Int => INT,
|
||||
ast::Type::Float => FLOAT,
|
||||
ast::Type::Bool => BOOL,
|
||||
ast::Type::CString => CSTRING,
|
||||
ast::Type::Function(ast::FunctionType { args, ret }) => Type::Fun {
|
||||
args: args.into_iter().map(Self::from).collect(),
|
||||
ret: Box::new(Self::from(*ret)),
|
||||
|
@ -181,8 +186,9 @@ impl<'ast> Typechecker<'ast> {
|
|||
let type_ = match lit {
|
||||
Literal::Int(_) => Type::Prim(PrimType::Int),
|
||||
Literal::Bool(_) => Type::Prim(PrimType::Bool),
|
||||
Literal::String(_) => Type::Prim(PrimType::CString),
|
||||
};
|
||||
Ok(hir::Expr::Literal(lit, type_))
|
||||
Ok(hir::Expr::Literal(lit.to_owned(), type_))
|
||||
}
|
||||
ast::Expr::UnaryOp { op, rhs } => todo!(),
|
||||
ast::Expr::BinaryOp { lhs, op, rhs } => {
|
||||
|
|
Loading…
Reference in a new issue