Add string support to the frontend

This commit is contained in:
Griffin Smith 2021-03-14 11:53:13 -04:00
parent 32a5c0ff0f
commit 39656a3801
10 changed files with 95 additions and 11 deletions

View file

@ -26,7 +26,7 @@ impl<'a, T> Binding<'a, T> {
pub enum Expr<'a, T> { pub enum Expr<'a, T> {
Ident(Ident<'a>, T), Ident(Ident<'a>, T),
Literal(Literal, T), Literal(Literal<'a>, T),
UnaryOp { UnaryOp {
op: UnaryOperator, op: UnaryOperator,
@ -158,7 +158,7 @@ impl<'a, T> Expr<'a, T> {
{ {
match self { match self {
Expr::Ident(id, t) => Expr::Ident(id.to_owned(), t.clone()), 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 { Expr::UnaryOp { op, rhs, type_ } => Expr::UnaryOp {
op: *op, op: *op,
rhs: Box::new((**rhs).to_owned()), rhs: Box::new((**rhs).to_owned()),

View file

@ -107,9 +107,20 @@ pub enum UnaryOperator {
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
pub enum Literal { pub enum Literal<'a> {
Int(u64), Int(u64),
Bool(bool), 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)] #[derive(Debug, PartialEq, Eq, Clone)]
@ -133,7 +144,7 @@ impl<'a> Binding<'a> {
pub enum Expr<'a> { pub enum Expr<'a> {
Ident(Ident<'a>), Ident(Ident<'a>),
Literal(Literal), Literal(Literal<'a>),
UnaryOp { UnaryOp {
op: UnaryOperator, op: UnaryOperator,
@ -174,7 +185,7 @@ impl<'a> Expr<'a> {
pub fn to_owned(&self) -> Expr<'static> { pub fn to_owned(&self) -> Expr<'static> {
match self { match self {
Expr::Ident(ref id) => Expr::Ident(id.to_owned()), 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 { Expr::UnaryOp { op, rhs } => Expr::UnaryOp {
op: *op, op: *op,
rhs: Box::new((**rhs).to_owned()), rhs: Box::new((**rhs).to_owned()),
@ -247,6 +258,7 @@ pub enum Type {
Int, Int,
Float, Float,
Bool, Bool,
CString,
Function(FunctionType), Function(FunctionType),
} }
@ -256,6 +268,7 @@ impl Display for Type {
Type::Int => f.write_str("int"), Type::Int => f.write_str("int"),
Type::Float => f.write_str("float"), Type::Float => f.write_str("float"),
Type::Bool => f.write_str("bool"), Type::Bool => f.write_str("bool"),
Type::CString => f.write_str("cstring"),
Type::Function(ft) => ft.fmt(f), Type::Function(ft) => ft.fmt(f),
} }
} }

View file

@ -92,6 +92,7 @@ impl<'ctx, 'ast> Codegen<'ctx, 'ast> {
Literal::Bool(b) => Ok(AnyValueEnum::IntValue( Literal::Bool(b) => Ok(AnyValueEnum::IntValue(
ty.const_int(if *b { 1 } else { 0 }, false), ty.const_int(if *b { 1 } else { 0 }, false),
)), )),
Literal::String(_) => todo!(),
} }
} }
Expr::UnaryOp { op, rhs, .. } => { Expr::UnaryOp { op, rhs, .. } => {

View file

@ -29,6 +29,7 @@ impl<'a> Interpreter<'a> {
Expr::Ident(id, _) => self.resolve(id), Expr::Ident(id, _) => self.resolve(id),
Expr::Literal(Literal::Int(i), _) => Ok((*i).into()), Expr::Literal(Literal::Int(i), _) => Ok((*i).into()),
Expr::Literal(Literal::Bool(b), _) => Ok((*b).into()), Expr::Literal(Literal::Bool(b), _) => Ok((*b).into()),
Expr::Literal(Literal::String(s), _) => Ok(s.clone().into()),
Expr::UnaryOp { op, rhs, .. } => { Expr::UnaryOp { op, rhs, .. } => {
let rhs = self.eval(rhs)?; let rhs = self.eval(rhs)?;
match op { match op {

View file

@ -1,7 +1,9 @@
use std::borrow::Cow;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::fmt::{self, Display}; use std::fmt::{self, Display};
use std::ops::{Add, Div, Mul, Neg, Sub}; use std::ops::{Add, Div, Mul, Neg, Sub};
use std::rc::Rc; use std::rc::Rc;
use std::result;
use derive_more::{Deref, From, TryInto}; use derive_more::{Deref, From, TryInto};
@ -22,15 +24,28 @@ pub enum Val<'a> {
Int(i64), Int(i64),
Float(f64), Float(f64),
Bool(bool), Bool(bool),
String(Cow<'a, str>),
Function(Function<'a>), 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> { impl<'a> fmt::Debug for Val<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Val::Int(x) => f.debug_tuple("Int").field(x).finish(), Val::Int(x) => f.debug_tuple("Int").field(x).finish(),
Val::Float(x) => f.debug_tuple("Float").field(x).finish(), Val::Float(x) => f.debug_tuple("Float").field(x).finish(),
Val::Bool(x) => f.debug_tuple("Bool").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_, .. }) => { Val::Function(Function { type_, .. }) => {
f.debug_struct("Function").field("type_", type_).finish() 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::Int(x) => x.fmt(f),
Val::Float(x) => x.fmt(f), Val::Float(x) => x.fmt(f),
Val::Bool(x) => x.fmt(f), Val::Bool(x) => x.fmt(f),
Val::String(s) => write!(f, "{:?}", s),
Val::Function(Function { type_, .. }) => write!(f, "<{}>", type_), Val::Function(Function { type_, .. }) => write!(f, "<{}>", type_),
} }
} }
@ -73,6 +89,7 @@ impl<'a> Val<'a> {
Val::Int(_) => Type::Int, Val::Int(_) => Type::Int,
Val::Float(_) => Type::Float, Val::Float(_) => Type::Float,
Val::Bool(_) => Type::Bool, Val::Bool(_) => Type::Bool,
Val::String(_) => Type::CString,
Val::Function(Function { type_, .. }) => Type::Function(type_.clone()), Val::Function(Function { type_, .. }) => Type::Function(type_.clone()),
} }
} }
@ -178,3 +195,9 @@ impl TypeOf for f64 {
Type::Float Type::Float
} }
} }
impl TypeOf for String {
fn type_of() -> Type {
Type::CString
}
}

View file

@ -1,3 +1,5 @@
#![feature(str_split_once)]
use clap::Clap; use clap::Clap;
pub mod ast; pub mod ast;

View file

@ -1,11 +1,14 @@
use std::borrow::Cow;
use nom::alt;
use nom::character::complete::{digit1, multispace0, multispace1}; use nom::character::complete::{digit1, multispace0, multispace1};
use nom::{ 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, preceded, separated_list0, separated_list1, tag, tuple,
}; };
use pratt::{Affix, Associativity, PrattParser, Precedence}; 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_}; use crate::parser::{ident, type_};
#[derive(Debug)] #[derive(Debug)]
@ -161,7 +164,22 @@ named!(bool_(&str) -> Literal, alt!(
tag!("false") => { |_| Literal::Bool(false) } 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)); named!(literal_expr(&str) -> Expr, map!(literal, Expr::Literal));
@ -308,7 +326,7 @@ named!(pub expr(&str) -> Expr, alt!(
#[cfg(test)] #[cfg(test)]
pub(crate) mod tests { pub(crate) mod tests {
use super::*; use super::*;
use crate::ast::Type; use crate::ast::{Ident, Type};
use std::convert::TryFrom; use std::convert::TryFrom;
use BinaryOperator::*; use BinaryOperator::*;
use Expr::{BinaryOp, If, Let, UnaryOp}; 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] #[test]
fn let_complex() { fn let_complex() {
let res = test_parse!(expr, "let x = 1; y = x * 7 in (x + y) * 4"); let res = test_parse!(expr, "let x = 1; y = x * 7 in (x + y) * 4");

View file

@ -16,7 +16,17 @@ pub type Error = nom::Err<nom::error::Error<String>>;
pub(crate) fn is_reserved(s: &str) -> bool { pub(crate) fn is_reserved(s: &str) -> bool {
matches!( matches!(
s, s,
"if" | "then" | "else" | "let" | "in" | "fn" | "int" | "float" | "bool" | "true" | "false" "if" | "then"
| "else"
| "let"
| "in"
| "fn"
| "int"
| "float"
| "bool"
| "true"
| "false"
| "cstring"
) )
} }

View file

@ -27,6 +27,7 @@ named!(pub type_(&str) -> Type, alt!(
tag!("int") => { |_| Type::Int } | tag!("int") => { |_| Type::Int } |
tag!("float") => { |_| Type::Float } | tag!("float") => { |_| Type::Float } |
tag!("bool") => { |_| Type::Bool } | tag!("bool") => { |_| Type::Bool } |
tag!("cstring") => { |_| Type::CString } |
function_type | function_type |
delimited!( delimited!(
tuple!(tag!("("), multispace0), tuple!(tag!("("), multispace0),
@ -44,6 +45,7 @@ mod tests {
assert_eq!(test_parse!(type_, "int"), Type::Int); assert_eq!(test_parse!(type_, "int"), Type::Int);
assert_eq!(test_parse!(type_, "float"), Type::Float); assert_eq!(test_parse!(type_, "float"), Type::Float);
assert_eq!(test_parse!(type_, "bool"), Type::Bool); assert_eq!(test_parse!(type_, "bool"), Type::Bool);
assert_eq!(test_parse!(type_, "cstring"), Type::CString);
} }
#[test] #[test]

View file

@ -49,6 +49,7 @@ pub enum PrimType {
Int, Int,
Float, Float,
Bool, Bool,
CString,
} }
impl From<PrimType> for ast::Type { impl From<PrimType> for ast::Type {
@ -57,6 +58,7 @@ impl From<PrimType> for ast::Type {
PrimType::Int => ast::Type::Int, PrimType::Int => ast::Type::Int,
PrimType::Float => ast::Type::Float, PrimType::Float => ast::Type::Float,
PrimType::Bool => ast::Type::Bool, 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::Int => f.write_str("int"),
PrimType::Float => f.write_str("float"), PrimType::Float => f.write_str("float"),
PrimType::Bool => f.write_str("bool"), 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 INT: Type = Type::Prim(PrimType::Int);
const FLOAT: Type = Type::Prim(PrimType::Float); const FLOAT: Type = Type::Prim(PrimType::Float);
const BOOL: Type = Type::Prim(PrimType::Bool); const BOOL: Type = Type::Prim(PrimType::Bool);
const CSTRING: Type = Type::Prim(PrimType::CString);
impl Display for Type { impl Display for Type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 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::Int => INT,
ast::Type::Float => FLOAT, ast::Type::Float => FLOAT,
ast::Type::Bool => BOOL, ast::Type::Bool => BOOL,
ast::Type::CString => CSTRING,
ast::Type::Function(ast::FunctionType { args, ret }) => Type::Fun { ast::Type::Function(ast::FunctionType { args, ret }) => Type::Fun {
args: args.into_iter().map(Self::from).collect(), args: args.into_iter().map(Self::from).collect(),
ret: Box::new(Self::from(*ret)), ret: Box::new(Self::from(*ret)),
@ -181,8 +186,9 @@ impl<'ast> Typechecker<'ast> {
let type_ = match lit { let type_ = match lit {
Literal::Int(_) => Type::Prim(PrimType::Int), Literal::Int(_) => Type::Prim(PrimType::Int),
Literal::Bool(_) => Type::Prim(PrimType::Bool), 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::UnaryOp { op, rhs } => todo!(),
ast::Expr::BinaryOp { lhs, op, rhs } => { ast::Expr::BinaryOp { lhs, op, rhs } => {