blr-lang 0.1.0

A language implementation that provides type safe dataframes
Documentation
use std::str::FromStr;
use super::*;

grammar;

pub Module: Module = {
    <imports:Import*> <items:Item*> => Module {
        imports,
        items,
    },
    <AnonymousItem> => Module {
        imports: Default::default(),
        items: vec![<>],
    }
}

Import: Import = {
    imp <path:Path> => Import {
        path
    }
};

Path: String = {
    <start:@L> (identifier "::")+ identifier <end:@R> => input[start..end].to_string(),
};

Item: Item = {
    <NativeItem> => Item::Native(<>),
    <ExternalItem> => Item::External(<>),
}

ExternalItems: Vec<ExternalItem> = {
    ext "{" <items:ExternalItem*> "}" => <>,
}

ExternalItem: ExternalItem = {
    pub_ ext fn_ <field:Identifier> "(" <parameters:ParameterNameTypes> ")" "->" <ret:TypeExpr> ";" => {
        let (parameter_names, parameter_typs) : (Vec<_>, Vec<_>)= if parameters.is_empty() {
            (vec!["_".to_string()],vec![TypeExpr::Unit])
        } else {
           parameters.into_iter().unzip()
        };
        ExternalItem {
            symbol: Symbol {
                module: "".to_string(),
                field,
            },
            typ: FunctionType {
                parameter_names,
                parameter_typs,
                ret: Box::new(ret),
            },
        }
    },
};

NativeItem: NativeItem = {
     pub_ fn_ <name:Identifier> "(" <parameters:ParameterNameTypes> ")" "->" <ret:TypeExpr> "{" <body:Expr> "}" => {
        let (names, types) : (Vec<_>, Vec<_>)= if parameters.is_empty() {
            (vec!["_".to_string()],vec![TypeExpr::Unit])
        } else {
           parameters.into_iter().unzip()
        };
        NativeItem {
            symbol: Symbol {
                module: "".to_string(),
                field: name,
            },
            function: Function {
                parameters: names.clone(),
                body: Box::new(body),
            },
            typ: FunctionType {
                parameter_names: names,
                parameter_typs: types,
                ret: Box::new(ret),
            },
        }
    },
};

AnonymousItem: Item = {
    //TODO make this unparse correctly
    <body:Expr> => Item::Native(NativeItem {
        symbol: Symbol{ module:"".to_string(), field: "main".to_string() },
        function: Function {
            parameters: vec!["_".to_string()],
            body: Box::new(body),
        },
        typ: FunctionType {
            parameter_names: vec!["_".to_string()],
            parameter_typs: vec![TypeExpr::Unit],
            ret: Box::new(TypeExpr::Var("_".to_string())),
        },
    }),
};

pub Expr: Expr = {
    let_ <pattern:Pattern> "=" <init:Expr> ";" <body:Expr> => Expr::Let{
        pattern,
        init: Box::new(init),
        body: Box::new(body),
    },
    fn_ "(" <parameters:ParameterNames> ")"  "=>" <body:Expr> => Expr::Function (Function { 
        parameters,
        body: Box::new(body),
    }),
   ForwardExpr,
};

ForwardExpr = {
    <parameter:ForwardExpr> ">>" <call:ApplicationExpr> => Expr::Forward{
        parameter: Box::new(parameter),
        call: Box::new(call),
    },
    ArithExpr,
};


// Left associative binary operator rule
BinaryTier<Op,NextTier>: Expr = {
    <left:BinaryTier<Op,NextTier>> <op:Op> <right:NextTier> => Expr::Binary {
        left: Box::new(left),
        op,
        right: Box::new(right),
    },
    NextTier,
};

ArithExpr = BinaryTier<ArithOp, FactorExpr>;
FactorExpr = BinaryTier<FactorOp, ConcatExpr>;
ConcatExpr = BinaryTier<ConcatOp, ApplicationExpr>;

ArithOp: BinaryOperator = {
    "+" => BinaryOperator::Addition,
    "-" => BinaryOperator::Subtraction,
};

FactorOp: BinaryOperator = {
    "*" => BinaryOperator::Multiplication,
    "/" => BinaryOperator::Division,
};
ConcatOp: BinaryOperator = {
    ".." => BinaryOperator::Concat,
};


ApplicationExpr = {
    <function:ApplicationExpr> "(" <parameters:Exprs> ")" => Expr::Application {
        function: Box::new(function),
        parameters,
    },
    Term,
};

Field = {
    <Identifier> ":" <Expr>,
};

Fields = Comma<Field>;

Exprs = Comma<Expr>;

Term: Expr = {
    Int,
    Float,
    ItemExpr,
    Variable,
    StringLiteral,
    Variant,
    Match,
    <record:Term> "." <label:Identifier> => Expr::RecordSelect {
        record: Box::new(record),
        label,
    },
    //"[" <Exprs> "]" => Expr::List(List{values:<>}),
    "{" <Fields> "}" => Expr::Record{fields:<>},
    "(" <Expr> ")" => Expr::Parenthesized(Box::new(<>)),
};

Int: Expr = {
    number => Expr::Integer(i64::from_str(<>).unwrap())
};
Float: Expr = {
    decimal_number => Expr::Float(f64::from_str(<>).unwrap())
};
ItemExpr: Expr = {
    <path:Path> => {
        let (module, field) = path.rsplit_once("::").unwrap();
        Expr::Item(Symbol{
            module: module.to_string(),
            field: field.to_string(),
        })
    }
};
Variable: Expr = {
    Identifier => Expr::Variable(<>)
};
Identifier: String = {
    identifier => <>.to_string(),
};

StringLiteral: Expr = {
    string => Expr::String(<>[1..<>.len()-1].to_string()),
};
Variant: Expr = {
    <lbl:Label> "(" <expr:Expr> ")" => Expr::Variant(lbl, Box::new(expr)),
};
Match: Expr = {
    match_ <expr:Expr> "{" <cases:Comma<MatchCase>> "}"  => Expr::Match(Box::new(expr), cases),
};
MatchCase = {
    <Pattern> "=>" <Expr>,
};

Pattern: Pattern ={
    <ident:Identifier> => Pattern::Identifier(ident),
    <lbl:Label> "(" <p:Pattern> ")" => Pattern::Variant(lbl, Box::new(p)),
};
Label: String = {
    label => <>.to_string(),
};

Delimited<T,D>: Vec<T> = {
    <mut v:(<T> D)*> <e:T?> => match e {
        None => v,
        Some(e) => {
            v.push(e);
            v
        }
    }
};
Comma<T> = Delimited<T, ",">;

ParameterNames = Comma<Identifier>;
ParameterNameTypes = Comma<ParameterWithType>;


ParameterWithType: (String,TypeExpr)  = {
    <name:Identifier> ":" <type_expr:TypeExpr> => (
        name,
        type_expr,
    ),
};


TypeExpr: TypeExpr = {
    // TODO Remove these keywords for named types
    "unit" => TypeExpr::Unit,
    "i64" => TypeExpr::Int,
    "f64" => TypeExpr::Float,
    "string" => TypeExpr::String,
    "DataFrame" => TypeExpr::DataFrame,
    "'" <identifier> => TypeExpr::Var(<>.to_string()),
    "{" <ProdType> "}" => TypeExpr::Prod(Row{ fields: <> }),
    "[" <SumType> "]" => TypeExpr::Sum(Row{ fields: <> }),
    <name:Identifier> "=" <typ:TypeExpr> => TypeExpr::Nominal(name, Box::new(typ)),
};

ProdType = Comma<FielType>;
FielType = {
    <Identifier> ":" <TypeExpr>,
};


SumType = Delimited<VariantType, "|">;
VariantType: (String, TypeExpr) = {
    <lbl:label> <typ:TypeExpr> => (lbl.to_string(), typ)
};

String: String = {
    string => <>.to_string()
}

match {
    "extern" => ext,
    "pub" => pub_,
    "import" => imp,
    "fn" => fn_,
    "let" => let_,
    "match" => match_,
    r"[0-9]+" => number,
    r"[0-9]+\.[0-9]+" => decimal_number,
    _
} else {
    r"`[a-zA-Z0-9_]+" => label,
} else {
    r"[a-zA-Z0-9_]+" => identifier,
} else {
    r#""[^"]*""# => string,
}