Skip to content
Snippets Groups Projects
forge_grammar.lalrpop 9.1 KiB
Newer Older
Louis's avatar
Louis committed
use crate::lexer::ScriptTokenType;
use crate::parser::ast::*;
use crate::runtime::numbers::Number;

grammar<'a>;

pub Program: Program = {
    <ExpressionList> => Program(<>),
}

ExpressionBlock: ExpressionList = {
    "{" "}" => ExpressionList::empty(),
    "{" <ExpressionList> "}" => <>,
}

ExpressionList: ExpressionList = {
    <Expression> => ExpressionList::production(vec![<>]),
    <mut list:ExpressionList> ";" <expr:Expression> => {
        list.expressions.push(expr);
        list
    },
    <mut opt:ExpressionList> ";" => {
        opt.is_void = true;
        opt
    },
}

FunctionDeclParamList = CommaSeperatedList<FunctionDeclParam>;

FunctionDeclParam: DeclareIdent = {
    Identifier => DeclareIdent::WithoutValue(<>),
    <id:Identifier> "=" <def:ValueExpression> => DeclareIdent::WithValue(Assignment {
        ident: id,
        value: Box::new(def),
    }),
}

ParameterList: Vec<ValueExpression> = {
    "(" <CommaSeperatedValueExpressionList> ")" => <>,
    "(" ")" => Vec::new(),
}

CommaSeperatedValueExpressionList = CommaSeperatedList<ValueExpression>;
Louis's avatar
Louis committed
pub Expression: Expression = {
    ValueExpression => Expression::Value(<>),
    VoidExpression => Expression::Void(<>),
}

ValueExpression: ValueExpression = {
    "let" <Identifier> => DeclareIdent::WithoutValue(<>).into(),
    "let" <id:Identifier> "=" <expr:FirstClassDeclareExpression> => DeclareIdent::WithValue(
        Assignment { ident: id, value :Box::new(expr) }
    ).into(),
Louis's avatar
Louis committed
}
Louis's avatar
Louis committed

VoidExpression: VoidExpression = {
    UnaryStatement => <>
Louis's avatar
Louis committed
}
FirstClassDeclareExpression: ValueExpression = {
    BoolOrExpression => <>,
    DeclareFunction => <>.into(),
}

BoolOrExpression: ValueExpression = {
    <lhs:BoolOrExpression> "||" <rhs:BoolAndExpression> => ValueExpression::bool_or(lhs, rhs),
Louis's avatar
Louis committed
}
BoolAndExpression: ValueExpression = {
    <lhs:BoolAndExpression> "&&" <rhs:EqualityExpression> => ValueExpression::bool_and(lhs, rhs),
EqualityExpression: ValueExpression = {
    <lhs:EqualityExpression> "==" <rhs:ComparisonExpression> => ValueExpression::equals(lhs, rhs),
    <lhs:EqualityExpression> "!=" <rhs:ComparisonExpression> => ValueExpression::not_equals(lhs, rhs),
Louis's avatar
Louis committed
}

ComparisonExpression: ValueExpression = {
    <lhs:ComparisonExpression> "<" <rhs:AdditiveExpression> => ValueExpression::less_than(lhs, rhs),
    <lhs:ComparisonExpression> ">" <rhs:AdditiveExpression> => ValueExpression::greater_than(lhs, rhs),
    <lhs:ComparisonExpression> "<=" <rhs:AdditiveExpression> => ValueExpression::greater_than_equal(lhs, rhs),
    <lhs:ComparisonExpression> ">=" <rhs:AdditiveExpression> => ValueExpression::greater_than_equal(lhs, rhs),
Louis's avatar
Louis committed
}

AdditiveExpression: ValueExpression = {
    <lhs:AdditiveExpression> "+" <rhs:MultiplicativeExpression> => ValueExpression::add(lhs, rhs),
    <lhs:AdditiveExpression> "-" <rhs:MultiplicativeExpression> => ValueExpression::sub(lhs, rhs),
Louis's avatar
Louis committed
}
MultiplicativeExpression: ValueExpression = {
    <lhs:MultiplicativeExpression> "*" <rhs:UnaryExpression> => ValueExpression::mul(lhs, rhs),
    <lhs:MultiplicativeExpression> "/" <rhs:UnaryExpression> => ValueExpression::div(lhs, rhs),
    <lhs:MultiplicativeExpression> "%" <rhs:UnaryExpression> => ValueExpression::modulo(lhs, rhs),
}
    "print" <FirstClassDeclareExpression> => Print { expr: Box::new(<>) }.into(),
    "return" <FirstClassDeclareExpression> => Return { expr: Box::new(<>) }.into(),
UnaryExpression: ValueExpression = {
    "typeof" <UnaryExpression> => TypeofValue(Box::new(<>)).into(),
    "!" <UnaryExpression> => ValueExpression::Unary { operand: Box::new(<>), operator: UnaryOp::Not },
    "-" <UnaryExpression> => ValueExpression::Unary { operand: Box::new(<>), operator: UnaryOp::Negate },
}
IndexedExpression: ValueExpression = {
    CallExpression => <>,
    <init:CallExpression> "." <acc:AccessorChainAnyEnding> => AccessorPath {
        initialiser: Box::new(init),
        path: acc,
    }.into(),
}

CallExpression: ValueExpression = {
    BaseExpression => <>,
    FunctionCall => <>.into(),
BaseExpression: ValueExpression = {
    Literal => <>.into(),
    Identifier => <>.into(),
    Conditional => <>,
    "(" <ValueExpression> ")" => GroupedExpression { inner: Box::new(<>) }.into(),
}

DeclareFunction: DeclareFunction = {
    "fn" <ident:Identifier> "(" <params:FunctionDeclParamList?> ")" <body:ExpressionBlock> => DeclareFunction {
        ident,
        params: params.unwrap_or_default(),
        body,
    }
}

AccessorChainAnyEnding: Vec<AccessorType> = {
    AccessorChainEndingWithProperty => <>,
    FunctionCall => vec![AccessorType::FunctionCall(<>)],
    <mut ls:AccessorChainEndingWithProperty> "." <func:FunctionCall> => {
        ls.push(AccessorType::FunctionCall(func));
        ls
    },
}

AccessorChainEndingWithProperty: Vec<AccessorType> = {
    Identifier => vec![AccessorType::Identifier(<>)],
    <func:FunctionCall> "." <id:Identifier> => vec![
        AccessorType::FunctionCall(func),
        AccessorType::Identifier(id),
    ],
    <mut first:AccessorChainEndingWithProperty> "." <id:Identifier> => {
        first.push(AccessorType::Identifier(id));
        first
    },
    <mut first:AccessorChainEndingWithProperty> "." <func:FunctionCall> "." <id:Identifier> => {
        first.push(AccessorType::FunctionCall(func));
        first.push(AccessorType::Identifier(id));
        first
    },
}

Conditional: ValueExpression = {
    GuardedBlockList => Conditional { blocks: <>, fallback: None }.into(),
    <ls:GuardedBlockList> "else" <fb:ExpressionBlock> => Conditional { blocks: ls, fallback: Some(fb) }.into(),
}

GuardedBlockList: Vec<GuardedBlock> = {
    GuardedBlock => vec![<>],
    <mut ls:GuardedBlockList> "else" <gb:GuardedBlock> => {
        ls.push(gb);
        ls
    },
}

GuardedBlock: GuardedBlock = {
    "if" <guard:ValueExpression> <block:ExpressionBlock> => GuardedBlock {
        guard: Box::new(guard),
        block,
    },
}

FunctionCall: FunctionCall = {
    <id:Identifier> <params:ParameterList> => FunctionCall {
        name: id,
        params,
    }
IdentifierAsAlias: IdentifierAlias = {
    <base:Identifier> "as" <alias:Identifier> => IdentifierAlias(base.0, alias.0),
};

Identifier: Identifier = {
    "identifier" => Identifier(String::from(<>))
};
Louis's avatar
Louis committed

Literal: LiteralNode = {
    "boolean" => LiteralNode::Boolean(<>),
    "null" => LiteralNode::Null,
    "float" => LiteralNode::Number(Number::Float(<>)),
    "integer" => LiteralNode::Number(Number::Integer(<>)),
    "string" => LiteralNode::String(String::from(<>)),
    "owned_string" => LiteralNode::String(<>),
Louis's avatar
Louis committed

StringValue: String = {
    "string" => String::from(<>),
    "owned_string" => <>,
CommaSeperatedList<Type>: Vec<Type> = {
    Type => vec![<>],
    <mut ls:CommaSeperatedList<Type>> "," <ty:Type> => {
        ls.push(ty);
        ls
    }
}

Louis's avatar
Louis committed
extern {
    type Location = usize;
Louis's avatar
Louis committed
    type Error = String;
Louis's avatar
Louis committed

Louis's avatar
Louis committed
    enum ScriptTokenType {
Louis's avatar
Louis committed
        "(" => ScriptTokenType::LeftParen,
        ")" => ScriptTokenType::RightParen,
        "{" => ScriptTokenType::LeftBrace,
        "}" => ScriptTokenType::RightBrace,
        "," => ScriptTokenType::Comma,
        "." => ScriptTokenType::Dot,
        "-" => ScriptTokenType::Minus,
        "+" => ScriptTokenType::Plus,
        ";" => ScriptTokenType::Semicolon,
Louis's avatar
Louis committed
        "/" => ScriptTokenType::Slash,
        "*" => ScriptTokenType::Asterisk,
        "!" => ScriptTokenType::Bang,
        "!=" => ScriptTokenType::BangEqual,
        "=" => ScriptTokenType::Equal,
        "==" => ScriptTokenType::EqualEqual,
        ">" => ScriptTokenType::Greater,
        ">=" => ScriptTokenType::GreaterEqual,
        "<" => ScriptTokenType::Less,
        "<=" => ScriptTokenType::LessEqual,
        "||" => ScriptTokenType::DoublePipe,
        "&&" => ScriptTokenType::DoubleAmpersand,
        "%" => ScriptTokenType::Modulo,
        "^" => ScriptTokenType::Caret,
        "struct" => ScriptTokenType::Class,
        "else" => ScriptTokenType::Else,
        "fn" => ScriptTokenType::Function,
        "for" => ScriptTokenType::For,
        "if" => ScriptTokenType::If,
        "null" => ScriptTokenType::Null,
        "print" => ScriptTokenType::Print,
        "return" => ScriptTokenType::Return,
        "super" => ScriptTokenType::Super,
        "this" => ScriptTokenType::This,
        "let" => ScriptTokenType::Let,
        "while" => ScriptTokenType::While,
        "export" => ScriptTokenType::Export,
        "import" => ScriptTokenType::Import,
        "as" => ScriptTokenType::Alias,
        "from" => ScriptTokenType::From,
        "typeof" => ScriptTokenType::Typeof,
        "finally" => ScriptTokenType::Finally,
        "boolean" => ScriptTokenType::Boolean(<bool>),
        "float" => ScriptTokenType::Float(<f64>),
        "integer" => ScriptTokenType::Integer(<i64>),
        "owned_string" => ScriptTokenType::OwnedString(<String>),
Louis's avatar
Louis committed
        "string" => ScriptTokenType::String(<String>),
        "identifier" => ScriptTokenType::Identifier(<String>),
Louis's avatar
Louis committed
    }
}