about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGriffin Smith <root@gws.fyi>2021-03-14T15·53-0400
committerGriffin Smith <root@gws.fyi>2021-03-14T15·53-0400
commit39656a3801bb311edd9ebb65e92a24fc48f69ec7 (patch)
treed408937901c7789c033373019a94e014a03522a8
parent32a5c0ff0fc58aa6721c1e0ad41950bde2d66744 (diff)
Add string support to the frontend
-rw-r--r--src/ast/hir.rs4
-rw-r--r--src/ast/mod.rs19
-rw-r--r--src/codegen/llvm.rs1
-rw-r--r--src/interpreter/mod.rs1
-rw-r--r--src/interpreter/value.rs23
-rw-r--r--src/main.rs2
-rw-r--r--src/parser/expr.rs34
-rw-r--r--src/parser/mod.rs12
-rw-r--r--src/parser/type_.rs2
-rw-r--r--src/tc/mod.rs8
10 files changed, 95 insertions, 11 deletions
diff --git a/src/ast/hir.rs b/src/ast/hir.rs
index 151ddd529872..9db6919f6f53 100644
--- a/src/ast/hir.rs
+++ b/src/ast/hir.rs
@@ -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()),
diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index cef366d16e04..5526c5348350 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -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),
         }
     }
diff --git a/src/codegen/llvm.rs b/src/codegen/llvm.rs
index 5b5db90a1ad8..ee087845b640 100644
--- a/src/codegen/llvm.rs
+++ b/src/codegen/llvm.rs
@@ -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, .. } => {
diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs
index 85a8928cbf9a..d414dedf8560 100644
--- a/src/interpreter/mod.rs
+++ b/src/interpreter/mod.rs
@@ -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 {
diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs
index 5e55825160cd..a1a579aec8db 100644
--- a/src/interpreter/value.rs
+++ b/src/interpreter/value.rs
@@ -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
+    }
+}
diff --git a/src/main.rs b/src/main.rs
index d5b00d6b6c46..d476b96ed634 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,5 @@
+#![feature(str_split_once)]
+
 use clap::Clap;
 
 pub mod ast;
diff --git a/src/parser/expr.rs b/src/parser/expr.rs
index 73c873b5b304..2ec6d60cd04f 100644
--- a/src/parser/expr.rs
+++ b/src/parser/expr.rs
@@ -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};
@@ -419,6 +437,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");
         assert_eq!(
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 0251d02df464..9ac590cee86c 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -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"
     )
 }
 
diff --git a/src/parser/type_.rs b/src/parser/type_.rs
index 076df7d6bd55..66b4f9f72c23 100644
--- a/src/parser/type_.rs
+++ b/src/parser/type_.rs
@@ -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]
diff --git a/src/tc/mod.rs b/src/tc/mod.rs
index b5acfac2b426..2c40a02bf7c6 100644
--- a/src/tc/mod.rs
+++ b/src/tc/mod.rs
@@ -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 } => {