TEST_zap 0.6.1

A blazingly fast networking solution for Roblox.
Documentation
use crate::parser::{reports::Report, syntax_tree::*};
use crate::config::{EvCall, EvSource, EvType, FnCall, NumTy};

use lalrpop_util::ParseError;

grammar;

extern {
    type Error = Report<'input>;
}

match {
    r"\s*" => { },
    r"(--\[\[[^(\]\])]*\]\])|(--.*)" => { },
} else {
	_
}

pub Config: SyntaxConfig<'input> = {
    <start:@L> <opts:Opt*> <decls:Decl*> <end:@R> => SyntaxConfig { start, opts, decls, end },
}

Opt: SyntaxOpt<'input> = {
    <start:@L> "opt" <name:Identifier> "=" <value:OptValue> ";"? <end:@R> => SyntaxOpt { start, name, value, end },
}

OptValue: SyntaxOptValue<'input> = {
    <start:@L> <kind:OptValueKind> <end:@R> => SyntaxOptValue { start, kind, end },
}

OptValueKind: SyntaxOptValueKind<'input> = {
    StrLit => SyntaxOptValueKind::Str(<>),
    NumLit => SyntaxOptValueKind::Num(<>),
    BoolLit => SyntaxOptValueKind::Bool(<>),
}

Decl: SyntaxDecl<'input> = {
    <decl:TyDecl> => SyntaxDecl::Ty(decl),
    <decl:EvDecl> => SyntaxDecl::Ev(decl),
    <decl:FnDecl> => SyntaxDecl::Fn(decl),
}

FnDecl: SyntaxFnDecl<'input> = {
    <start:@L> "funct" <name:Identifier> "=" "{"
        "call" ":" <call:FnCall>
        <args:("," "args" ":" <Ty>)?>
        <rets:("," "rets" ":" <Ty>)?>
        ","?
    "}" ";"? <end:@R> => SyntaxFnDecl { start, name, call, args, rets, end },
}

FnCall: FnCall = {
    "Async" => FnCall::Async,
    "Sync" => FnCall::Sync,
}

EvDecl: SyntaxEvDecl<'input> = {
    <start:@L> "event" <name:Identifier> "=" "{"
        "from" ":" <from:EvSource>
        "," "type" ":" <evty:EvTy>
        "," "call" ":" <call:EvCall> 
        <data:("," "data" ":" <Ty>)?>
        ","?
    "}" ";"? <end:@R> => SyntaxEvDecl { start, name, from, evty, call, data, end },
}

EvCall: EvCall = {
    "SingleSync" => EvCall::SingleSync,
    "SingleAsync" => EvCall::SingleAsync,
    "ManySync" => EvCall::ManySync,
    "ManyAsync" => EvCall::ManyAsync,
}

EvTy: EvType = {
    "Reliable" => EvType::Reliable,
    "Unreliable" => EvType::Unreliable,
}

EvSource: EvSource = {
    "Server" => EvSource::Server,
    "Client" => EvSource::Client,
}

TyDecl: SyntaxTyDecl<'input> = {
    <start:@L> "type" <name:Identifier> "=" <ty:Ty> ";"? <end:@R> => SyntaxTyDecl { start, name, ty, end },
}

Ty: SyntaxTy<'input> = {
    <start:@L> <kind:TyKind> <end:@R> => SyntaxTy { start, kind, end },
}

TyKind: SyntaxTyKind<'input> = {
    "f32" <r:("(" <NumRange> ")")?> => SyntaxTyKind::Num(NumTy::F32, r),
    "f64" <r:("(" <NumRange> ")")?> => SyntaxTyKind::Num(NumTy::F64, r),

    "i8" <r:("(" <IntRange> ")")?> => SyntaxTyKind::Num(NumTy::I8, r),
    "i16" <r:("(" <IntRange> ")")?> => SyntaxTyKind::Num(NumTy::I16, r),
    "i32" <r:("(" <IntRange> ")")?> => SyntaxTyKind::Num(NumTy::I32, r),

    "u8" <r:("(" <IntRange> ")")?> => SyntaxTyKind::Num(NumTy::U8, r),
    "u16" <r:("(" <IntRange> ")")?> => SyntaxTyKind::Num(NumTy::U16, r),
    "u32" <r:("(" <IntRange> ")")?> => SyntaxTyKind::Num(NumTy::U32, r),

    "string" <r:("(" <IntRange> ")")?> => SyntaxTyKind::Str(r),
    "buffer" <r:("(" <IntRange> ")")?> => SyntaxTyKind::Buf(r),

    <ty:Ty> "[" <r:IntRange?> "]" => SyntaxTyKind::Arr(Box::new(ty), r),
    "map" "{" "[" <k:Ty> "]" ":" <v:Ty> "}" => SyntaxTyKind::Map(Box::new(k), Box::new(v)),

    <ty:Ty> "?" => SyntaxTyKind::Opt(Box::new(ty)),
    <name:Identifier> => SyntaxTyKind::Ref(name),

    "enum" <e:Enum> => SyntaxTyKind::Enum(e),
    "struct" <s:Struct> => SyntaxTyKind::Struct(s),
    "Instance" <c:("(" <Identifier> ")")?> => SyntaxTyKind::Instance(c),
}

Enum: SyntaxEnum<'input> = {
    <start:@L> <kind:EnumKind> <end:@R> => SyntaxEnum { start, kind, end },
}

EnumKind: SyntaxEnumKind<'input> = {
    "{" <enumerators:Comma<Identifier>> "}" => SyntaxEnumKind::Unit(enumerators),

    <tag:StrLit> "{" <mut variants:(<Identifier> <Struct> ",")*> <lv:(<Identifier> <Struct>)?> "}" =>
        SyntaxEnumKind::Tagged {
            tag,
            variants: match lv {
                Some(lv) => { variants.push(lv); variants },
                None => variants,
            },
            catch_all: None
        },
    
    <tag:StrLit> "{" <variants:(<Identifier> <Struct> ",")*> <ca:("..." <Struct> ","?)> "}" =>
        SyntaxEnumKind::Tagged {
            tag,
            variants,
            catch_all: Some(ca),
        },
}

Struct: SyntaxStruct<'input> = {
    <start:@L> "{" <fields:Comma<(<Identifier> ":" <Ty>)>> "}" <end:@R> => SyntaxStruct { start, fields, end },
}

IntRange: SyntaxRange<'input> = {
    <start:@L> <kind:IntRangeKind> <end:@R> => SyntaxRange { start, kind, end },
}

IntRangeKind: SyntaxRangeKind<'input> = {
    ".." => SyntaxRangeKind::None,
    <n:IntLit> => SyntaxRangeKind::Exact(n),
    <min:IntLit> ".." => SyntaxRangeKind::WithMin(min),
    ".." <max:IntLit> => SyntaxRangeKind::WithMax(max),
    <min:IntLit> ".." <max:IntLit> => SyntaxRangeKind::WithMinMax(min, max),
}

NumRange: SyntaxRange<'input> = {
    <start:@L> <kind:NumRangeKind> <end:@R> => SyntaxRange { start, kind, end },
}

NumRangeKind: SyntaxRangeKind<'input> = {
    ".." => SyntaxRangeKind::None,
    <n:NumLit> => SyntaxRangeKind::Exact(n),
    <min:NumLit> ".." => SyntaxRangeKind::WithMin(min),
    ".." <max:NumLit> => SyntaxRangeKind::WithMax(max),
    <min:NumLit> ".." <max:NumLit> => SyntaxRangeKind::WithMinMax(min, max),
}

StrLit: SyntaxStrLit<'input> = {
    <start:@L> <value:r#""[^"]*""#> <end:@R> => SyntaxStrLit { start, value, end },
    <start:@L> <value:r"'[^']*'"> <end:@R> => SyntaxStrLit { start, value, end },
}

IntLit: SyntaxNumLit<'input> = {
    <n:NumLit> =>? {
        if n.value.contains('.') {
            Err(ParseError::User {
                error: Report::ParserExpectedInt { span: n.span() }
            })
        } else {
            Ok(n)
        }
    }
}

NumLit: SyntaxNumLit<'input> = {
    <start:@L> <value:r"-?\d+(\.\d+)?"> <end:@R> => SyntaxNumLit { start, value, end },
}

BoolLit: SyntaxBoolLit = {
    <start:@L> "true" <end:@R> => SyntaxBoolLit { start, value: true, end },
    <start:@L> "false" <end:@R> => SyntaxBoolLit { start, value: false, end },
}

Identifier: SyntaxIdentifier<'input> = {
    <start:@L> <name:r"[a-zA-Z_][a-zA-Z0-9_]*"> <end:@R> => SyntaxIdentifier { start, name, end },
    <start:@L> "data" <end:@R> => SyntaxIdentifier { start, name: "data", end },
    <start:@L> "event" <end:@R> => SyntaxIdentifier { start, name: "event", end },
    <start:@L> "type" <end:@R> => SyntaxIdentifier { start, name: "type", end },
    <start:@L> "opt" <end:@R> => SyntaxIdentifier { start, name: "opt", end },
    <start:@L> "from" <end:@R> => SyntaxIdentifier { start, name: "from", end },
    <start:@L> "call" <end:@R> => SyntaxIdentifier { start, name: "call", end },
    <start:@L> "Async" <end:@R> => SyntaxIdentifier { start, name: "Async", end },
    <start:@L> "Sync" <end:@R> => SyntaxIdentifier { start, name: "Sync", end },
    <start:@L> "Reliable" <end:@R> => SyntaxIdentifier { start, name: "Reliable", end },
    <start:@L> "Unreliable" <end:@R> => SyntaxIdentifier { start, name: "Unreliable", end },
    <start:@L> "Server" <end:@R> => SyntaxIdentifier { start, name: "Server", end },
    <start:@L> "Client" <end:@R> => SyntaxIdentifier { start, name: "Client", end },
    <start:@L> "SingleSync" <end:@R> => SyntaxIdentifier { start, name: "SingleSync", end },
    <start:@L> "SingleAsync" <end:@R> => SyntaxIdentifier { start, name: "SingleAsync", end },
    <start:@L> "ManySync" <end:@R> => SyntaxIdentifier { start, name: "ManySync", end },
    <start:@L> "ManyAsync" <end:@R> => SyntaxIdentifier { start, name: "ManyAsync", end },
    <start:@L> "enum" <end:@R> => SyntaxIdentifier { start, name: "enum", end },
    <start:@L> "struct" <end:@R> => SyntaxIdentifier { start, name: "struct", end },
    <start:@L> "map" <end:@R> => SyntaxIdentifier { start, name: "map", end },
    <start:@L> "true" <end:@R> => SyntaxIdentifier { start, name: "true", end },
    <start:@L> "false" <end:@R> => SyntaxIdentifier { start, name: "false", end },
}

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