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> {
|
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()),
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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, .. } => {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![feature(str_split_once)]
|
||||||
|
|
||||||
use clap::Clap;
|
use clap::Clap;
|
||||||
|
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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 } => {
|
||||||
|
|
Loading…
Reference in a new issue