oakc 0.6.1

A portable programming language with a compact backend
Documentation

use crate::{Identifier, tir::{TirProgram, TirDeclaration, TirStructure, TirFunction, TirExpression, TirConstant, TirStatement, TirType}};

grammar;

pub Program: TirProgram = <(Declaration)*> => TirProgram::new(<>, 512);

Declaration: TirDeclaration = {
    "#" "[" "header" "(" <Str> ")" "]" => TirDeclaration::DocumentHeader(<>),
    "#" "[" "std" "]" => TirDeclaration::RequireStd,
    "#" "[" "no_std" "]" => TirDeclaration::NoStd,
    "#" "[" "assert" "(" <Constant> ")" "]" => TirDeclaration::Assert(<>),
    "#" "[" "extern" "(" <Str> ")" "]" => TirDeclaration::Extern(<>),
    "#" "[" "include" "(" <Str> ")" "]" => TirDeclaration::Include(<>),
    "#" "[" "memory" "(" <Num> ")" "]" => TirDeclaration::Memory(<> as i32),
    "#" "[" "error" "(" <Str> ")" "]" => TirDeclaration::Error(<>),
    "#" "[" "if" "(" <cond:Constant> ")" "{" <code:Program> "}" "]" => TirDeclaration::If(cond, code),
    "#" "[" "if" "(" <cond:Constant> ")" "{" <then_code:Program> "}" "else" "{" <else_code:Program> "}" "]" => TirDeclaration::IfElse(cond, then_code, else_code),
    "#" "[" "define" "(" <name:Str> "," <constant:Constant> ")" "]" => TirDeclaration::Constant(None, name, constant),
    "#" "[" "doc" "(" <doc:Str> ")" "]" "const" <name:Ident> "=" <constant:Constant> ";" => TirDeclaration::Constant(Some(doc), name, constant),
    "const" <name:Ident> "=" <constant:Constant> ";" => TirDeclaration::Constant(None, name, constant),
    <Function> => TirDeclaration::Function(<>),
    <Structure> => TirDeclaration::Structure(<>),
}

Str: String = <s:r#""(\\.|[^"])*""#> => String::from(&s[1..s.len()-1]).replace("\\\"", "\"").replace("\\n", "\n").replace("\\r", "\r").replace("\\t", "\t").replace("\\0", "\0");
Char: char = <s:r#"'(\\.|[^'])'"#> => s.replace("\\'", "'").replace("\\n", "\n").replace("\\r", "\r").replace("\\t", "\t").replace("\\0", "\0").chars().nth(1).unwrap() as char;

Num: f64 = {
    // r"([0-9]+([.][0-9]*)?|[.][0-9]+)" => <>.to_string().parse::<f64>().unwrap(),
    r"([1-9][0-9]*|[0])([.][0-9]+)?" => <>.to_string().parse::<f64>().unwrap(),
}

Ident: Identifier = {
    <head:(r"[a-zA-Z_][a-zA-Z0-9_]*" "::")*> <tail:r"[a-zA-Z_][a-zA-Z0-9_]*"> => {
        let mut result = String::new();
        for (a, b) in head {
            result += a;
            result += b;
        }
        result += tail;
        result
    }
    
}

List<Begin, T, Sep, End>: Vec<T> = {
    <first:Begin> <list: (<T> <Sep>)*> <end:T?> <last:End> => {
        match end {
            None => list.iter().map(|(v, s)| v.clone()).collect(),
            Some(val) => {
                let mut list: Vec<_> = list.iter().map(|(v, s)| v.clone()).collect();
                list.push(val);
                list
            }
        }
    }
}

Constant: TirConstant = {
    <cond:ConstantMathBottom> "?" <then:Constant> ":" <otherwise:Constant> => TirConstant::Conditional(Box::new(cond), Box::new(then), Box::new(otherwise)),
    <ConstantMathBottom> => <>
}

ConstantAtom: TirConstant = {
    "is_movable" "(" <Type> ")" => TirConstant::IsMovable(<>),
    "sizeof" "(" <Type> ")" => TirConstant::SizeOf(<>),
    "is_defined" "(" <Str> ")" => TirConstant::IsDefined(<>),
    "true" => TirConstant::True,
    "false" => TirConstant::False,
    <Ident> => TirConstant::Constant(<>),
    <Num> => TirConstant::Float(<>),
    <Char> => TirConstant::Character(<>),
    "-" <ConstantAtom> => TirConstant::Subtract(Box::new(TirConstant::Float(0.0)), Box::new(<>)),
    "!" <ConstantAtom> => TirConstant::Not(Box::new(<>))
}

ConstantMathBottom: TirConstant = {
    <l:ConstantMathLow> "&&" <r:ConstantMathLow> => TirConstant::And(Box::new(l), Box::new(r)),
    <l:ConstantMathLow> "||" <r:ConstantMathLow> => TirConstant::Or(Box::new(l), Box::new(r)),
    <ConstantMathLow> => <>
}

ConstantMathLow: TirConstant = {
    <l:ConstantMathMiddle> "==" <r:ConstantMathMiddle> => TirConstant::Equal(Box::new(l), Box::new(r)),
    <l:ConstantMathMiddle> "!=" <r:ConstantMathMiddle> => TirConstant::NotEqual(Box::new(l), Box::new(r)),
    <l:ConstantMathMiddle> ">=" <r:ConstantMathMiddle> => TirConstant::GreaterEqual(Box::new(l), Box::new(r)),
    <l:ConstantMathMiddle> ">" <r:ConstantMathMiddle>  => TirConstant::Greater(Box::new(l), Box::new(r)),
    <l:ConstantMathMiddle> "<=" <r:ConstantMathMiddle> => TirConstant::LessEqual(Box::new(l), Box::new(r)),
    <l:ConstantMathMiddle> "<" <r:ConstantMathMiddle>  => TirConstant::Less(Box::new(l), Box::new(r)),
    <ConstantMathMiddle> => <>
}

ConstantMathMiddle: TirConstant = {
    <l:ConstantMathHigh> "+" <r:ConstantMathHigh> => TirConstant::Add(Box::new(l), Box::new(r)),
    <l:ConstantMathHigh> "-" <r:ConstantMathHigh> => TirConstant::Subtract(Box::new(l), Box::new(r)),
    <ConstantMathHigh> => <>
}

ConstantMathHigh: TirConstant = {
    <l:ConstantAtom> "*" <r:ConstantAtom> => TirConstant::Multiply(Box::new(l), Box::new(r)),
    <l:ConstantAtom> "/" <r:ConstantAtom> => TirConstant::Divide(Box::new(l), Box::new(r)),
    <ConstantAtom> => <>
}

Function: TirFunction = {
    "#" "[" "doc" "(" <doc:Str> ")" "]" "fn" <name:Ident> <args:List<"(", (Ident ":" Type), ",", ")">> <body:Body> => TirFunction::new(Some(doc), name, args.iter().map(|(a, _, t)| (a.clone(), t.clone())).collect(), TirType::Void, body),
    "#" "[" "doc" "(" <doc:Str> ")" "]" "fn" <name:Ident> <args:List<"(", (Ident ":" Type), ",", ")">> "->" <return_type:Type> <body:Body> => TirFunction::new(Some(doc), name, args.iter().map(|(a, _, t)| (a.clone(), t.clone())).collect(), return_type, body),
    "fn" <name:Ident> <args:List<"(", (Ident ":" Type), ",", ")">> <body:Body> => TirFunction::new(None, name, args.iter().map(|(a, _, t)| (a.clone(), t.clone())).collect(), TirType::Void, body),
    "fn" <name:Ident> <args:List<"(", (Ident ":" Type), ",", ")">> "->" <return_type:Type> <body:Body> => TirFunction::new(None, name, args.iter().map(|(a, _, t)| (a.clone(), t.clone())).collect(), return_type, body),
}

Structure: TirStructure = {
    "#" "[" "doc" "(" <doc:Str> ")" "]" "struct" <name:Ident> "{" <members: List<"let", (Ident ":" Type), ",", ";">> <methods:Function*> "}" => TirStructure::new(Some(doc), name, members.iter().map(|(a, _, t)| (a.clone(), t.clone())).collect(), methods),
    "struct" <name:Ident> "{" <members: List<"let", (Ident ":" Type), ",", ";">> <methods:Function*> "}" => TirStructure::new(None, name, members.iter().map(|(a, _, t)| (a.clone(), t.clone())).collect(), methods),
}

Body: Vec<TirStatement> = "{" <head: Statement*> <tail: SmallStatement?> "}" => {
    let mut result = Vec::new();
    for stmt in head { result.push(stmt) }
    if let Some(stmt) = tail { result.push(stmt) }
    result
};

Type: TirType = {
    "&" <Type> => TirType::Pointer(Box::new(<>)),
    "&&" <Type> => TirType::Pointer(Box::new(TirType::Pointer(Box::new(<>)))),
    "void" => TirType::Void,
    "num"  => TirType::Float,
    "bool" => TirType::Boolean,
    "char" => TirType::Character,
    <Ident> => TirType::Structure(<>)
}

Statement: TirStatement = {
    <BodyStatement> => <>,
    <SmallStatement> ";" => <>
}

BodyStatement: TirStatement = {
    "for" "(" <pre:SmallStatement> ";" <cond:Expression> ";" <post:SmallStatement> ")" <body:Body> => TirStatement::For(Box::new(pre), cond, Box::new(post), body),
    "for" <var:Ident> "in" <from:Expression> ".." <to:Expression> <body:Body> => TirStatement::ForRange(var, from, to, body),
    "while" <cond:Expression> <body:Body> => TirStatement::While(cond, body),
    "if" <cond:Expression> <body:Body> => TirStatement::If(cond, body),
    "if" <cond:Expression> <then_body:Body> "else" <else_body:Body> => TirStatement::IfElse(cond, then_body, else_body),
    "if" <cond:Expression>  <then_body:Body> <elifs:("else" "if" Expression Body)+> "else" <else_body:Body> => {
        TirStatement::IfElifElse(cond, then_body, elifs.iter().map(|(_, _, cond, body)| (cond.clone(), body.clone())).collect(), else_body)
    },
}

SmallStatement: TirStatement = {
    "return" <exprs:List<"[", Expression, ",", "]">> => TirStatement::Return(exprs),
    "return" <expr:Expression> => TirStatement::Return(vec![expr]),
    "free" <addr:Expression> ":" <size:Expression> => TirStatement::Free(addr, size),
    "let" <name:Ident> "=" <expr:Expression> => TirStatement::AutoDefine(name, expr),
    "let" <name:Ident> ":" <t:Type> "=" <expr:Expression> => TirStatement::Define(name, t, expr),

    <name:Ident> "=" <expr:Expression> => TirStatement::AssignVariable(name, expr),
    <name:Ident> "+=" <expr:Expression> => TirStatement::AddAssignVariable(name, expr),
    <name:Ident> "-=" <expr:Expression> => TirStatement::SubtractAssignVariable(name, expr),
    <name:Ident> "*=" <expr:Expression> => TirStatement::MultiplyAssignVariable(name, expr),
    <name:Ident> "/=" <expr:Expression> => TirStatement::DivideAssignVariable(name, expr),

    "*" <lhs:Expression> "=" <rhs:Expression> => TirStatement::AssignAddress(lhs, rhs),
    "*" <lhs:Expression> "+=" <rhs:Expression> => TirStatement::AddAssignAddress(lhs, rhs),
    "*" <lhs:Expression> "-=" <rhs:Expression> => TirStatement::SubtractAssignAddress(lhs, rhs),
    "*" <lhs:Expression> "*=" <rhs:Expression> => TirStatement::MultiplyAssignAddress(lhs, rhs),
    "*" <lhs:Expression> "/=" <rhs:Expression> => TirStatement::DivideAssignAddress(lhs, rhs),

    <ptr:ExpressionAtom> "[" <idx:Expression> "]" "=" <rhs:Expression> => TirStatement::AssignAddress(TirExpression::Index(Box::new(ptr), Box::new(idx)), rhs),
    <ptr:ExpressionAtom> "[" <idx:Expression> "]" "+=" <rhs:Expression> => TirStatement::AddAssignAddress(TirExpression::Index(Box::new(ptr), Box::new(idx)), rhs),
    <ptr:ExpressionAtom> "[" <idx:Expression> "]" "-=" <rhs:Expression> => TirStatement::SubtractAssignAddress(TirExpression::Index(Box::new(ptr), Box::new(idx)), rhs),
    <ptr:ExpressionAtom> "[" <idx:Expression> "]" "*=" <rhs:Expression> => TirStatement::MultiplyAssignAddress(TirExpression::Index(Box::new(ptr), Box::new(idx)), rhs),
    <ptr:ExpressionAtom> "[" <idx:Expression> "]" "/=" <rhs:Expression> => TirStatement::DivideAssignAddress(TirExpression::Index(Box::new(ptr), Box::new(idx)), rhs),

    <instance:ExpressionAtom> "->" <name:Ident> "=" <rhs:Expression> => TirStatement::AssignAddress(TirExpression::Method(Box::new(instance), name, vec![]), rhs),
    <instance:ExpressionAtom> "->" <name:Ident> "+=" <rhs:Expression> => TirStatement::AddAssignAddress(TirExpression::Method(Box::new(instance), name, vec![]), rhs),
    <instance:ExpressionAtom> "->" <name:Ident> "-=" <rhs:Expression> => TirStatement::SubtractAssignAddress(TirExpression::Method(Box::new(instance), name, vec![]), rhs),
    <instance:ExpressionAtom> "->" <name:Ident> "*=" <rhs:Expression> => TirStatement::MultiplyAssignAddress(TirExpression::Method(Box::new(instance), name, vec![]), rhs),
    <instance:ExpressionAtom> "->" <name:Ident> "/=" <rhs:Expression> => TirStatement::DivideAssignAddress(TirExpression::Method(Box::new(instance), name, vec![]), rhs),

    <Expression> => TirStatement::Expression(<>)
}

Expression: TirExpression = {
    <cond:ExpressionBottom> "?" <then:Expression> ":" <otherwise:Expression> => TirExpression::Conditional(Box::new(cond), Box::new(then), Box::new(otherwise)),
    <expr:ExpressionAtom> "as" <t:Type> => TirExpression::TypeCast(Box::new(expr), t),
    "*" <ptr:ExpressionBottom> => TirExpression::Deref(Box::new(ptr)),
    <ExpressionBottom> => <>,
}

ExpressionAtom: TirExpression = {
    "is_movable" "(" <Type> ")" => TirExpression::IsMovable(<>),
    "is_defined" "(" <Str> ")" => TirExpression::Constant(TirConstant::IsDefined(<>)),
    "move" "(" <val:Expression> ")" => TirExpression::Move(Box::new(val)),
    "sizeof" "(" <Type> ")" => TirExpression::SizeOf(<>),
    "alloc" "(" <size:Expression> ")" => TirExpression::Alloc(Box::new(size)),
    <name:Ident> <args:List<"(", Expression, ",", ")">> => TirExpression::Call(name, args),
    <name:Ident> "!" <args:List<"(", Expression, ",", ")">> => TirExpression::ForeignCall(name, args),

    "true" => TirExpression::True,
    "false" => TirExpression::False,
    <Ident> => TirExpression::Variable(<>),
    <Str> => TirExpression::String(<>),

    "@" => TirExpression::Void,
    <Num> => TirExpression::Constant(TirConstant::Float(<>)),
    <Char> => TirExpression::Character(<>),

    "!" <ExpressionAtom> => TirExpression::Not(Box::new(<>)),
    "(" <Expression> ")" => <>,
    "-" <ExpressionAtom> => TirExpression::Subtract(Box::new(TirExpression::Constant(TirConstant::Float(0.0))), Box::new(<>)),
}


ExpressionBottom: TirExpression = {
    <l:ExpressionLow> "&&" <r:ExpressionLow> => TirExpression::And(Box::new(l), Box::new(r)),
    <l:ExpressionLow> "||" <r:ExpressionLow> => TirExpression::Or(Box::new(l), Box::new(r)),
    <ExpressionLow> => <>
}

ExpressionLow: TirExpression = {
    <l:ExpressionMiddle> "==" <r:ExpressionMiddle> => TirExpression::Equal(Box::new(l), Box::new(r)),
    <l:ExpressionMiddle> "!=" <r:ExpressionMiddle> => TirExpression::NotEqual(Box::new(l), Box::new(r)),
    <l:ExpressionMiddle> ">=" <r:ExpressionMiddle> => TirExpression::GreaterEqual(Box::new(l), Box::new(r)),
    <l:ExpressionMiddle> ">" <r:ExpressionMiddle>  => TirExpression::Greater(Box::new(l), Box::new(r)),
    <l:ExpressionMiddle> "<=" <r:ExpressionMiddle> => TirExpression::LessEqual(Box::new(l), Box::new(r)),
    <l:ExpressionMiddle> "<" <r:ExpressionMiddle>  => TirExpression::Less(Box::new(l), Box::new(r)),
    <ExpressionMiddle> => <>
}

ExpressionMiddle: TirExpression = {
    <l:ExpressionHigh> "+" <r:ExpressionHigh> => TirExpression::Add(Box::new(l), Box::new(r)),
    <l:ExpressionHigh> "-" <r:ExpressionHigh> => TirExpression::Subtract(Box::new(l), Box::new(r)),
    <ExpressionHigh> => <>
}

ExpressionHigh: TirExpression = {
    "&" <ptr:ExpressionAtom> "[" <idx:Expression> "]" => TirExpression::Index(Box::new(ptr), Box::new(idx)),
    "&" <instance:ExpressionAtom> "->" <name:Ident> <args:List<"(", Expression, ",", ")">> => TirExpression::Method(Box::new(instance), name, args),
    "&" <instance:ExpressionAtom> "->" <name:Ident> => TirExpression::Method(Box::new(instance), name, vec![]),
    "&" <name:Ident> => TirExpression::Refer(name),
    <ptr:ExpressionAtom> "[" <idx:Expression> "]" => TirExpression::Deref(Box::new(TirExpression::Index(Box::new(ptr), Box::new(idx)))),
    <instance:ExpressionAtom> "." <name:Ident> <args:List<"(", Expression, ",", ")">> => TirExpression::Method(Box::new(instance), name, args),
    <instance:ExpressionAtom> "." <name:Ident> => TirExpression::Method(Box::new(instance), name, vec![]),
    <instance:ExpressionAtom> "->" <name:Ident> <args:List<"(", Expression, ",", ")">> => TirExpression::Deref(Box::new(TirExpression::Method(Box::new(instance), name, args))),
    <instance:ExpressionAtom> "->" <name:Ident> => TirExpression::Deref(Box::new(TirExpression::Method(Box::new(instance), name, vec![]))),
    <l:ExpressionAtom> "*" <r:ExpressionAtom> => TirExpression::Multiply(Box::new(l), Box::new(r)),
    <l:ExpressionAtom> "/" <r:ExpressionAtom> => TirExpression::Divide(Box::new(l), Box::new(r)),
    <ExpressionAtom> => <>
}